准备
node & npm
1
$ brew install node
检查 node & npm
1
2$ node -v
$ npm -vvue-cli
1
$ npm install -g vue-cli
或
1
$ npm install -g @vue/cli @vue/cli-init @vue/cli-service-global
初始化
1 | $ vue init <template-name> <project-name> |
举例
1 | vue init webpack vue-step-by-step |
根据提示依次输入相关信息 ↓
最后出现finished
安装完成 ↓
在终端中运行 ↓
1 | cd vue-step-by-step |
即可查看初始化完成的效果
添加依赖
项目初始化完成后添加项目常用依赖包
1 | npm install --save vuex axios qs |
包含vuex
、axios
、qs
、sass
、pug
等,其他依赖包根据项目需求自己选择vue-router
在脚手架 init 的时候会提示是否选择安装
完善项目结构
添加views
文件夹
src 下添加 views 文件夹主要存放页面级的 vue 组件
src 下的 components 文件夹主要用于存放通用的组件
在 views 文件夹中创建Home.vue
作为主页
删除App.vue
中无用的内容,只保留router-view
1 | <template> |
如果是移动端项目用 rem 作为单位,可以在src/main.js
中添加如下代码做自适应 ↓
1 | if (window.addEventListener) { |
调整router
配置
更多路由相关使用方法请访问:https://router.vuejs.org/zh-cn/
目录结构 ↓
1 | router |
修改路由主文件router/index.js
使用require.context
实现路由去中心化
1 | import Vue from 'vue' |
在 router 文件夹下添加 modules
文件夹
在 modules 文件夹下添加 home.js
,这个 home.js 对应首页业务模块,首页相关的路由页面都可以写到 home.js 文件里。
如果以后添加其他业务模块,只需要在 modules 文件夹添加相对应的业务模块文件,并在其中添加业务相关的路由页面。这样所有不同业务线的开发人员就可以互不干扰 ↓
1 | import Home from '../../views/Home' |
对于不需要即时加载的非一级页面可以使用异步路由组件
1 | // region 异步组件 - 路由地址demo |
添加store
文件夹
src 下的 store 文件夹主要是存放 vuex 相关信息的
更多 vuex 相关使用方法请访问:https://vuex.vuejs.org/zh-cn/
在 store 文件夹下创建目录结构 ↓
1 | store |
下面开始改造 store 文件夹 ↓
在
mutation-types.js
中添加一个常量1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95export const BASE = {
setUserInfo: 'base/setUserInfo',
}
export function mutations (states) {
addFirstUpperCaseToPrototype()
addFlatToPrototype()
return {
// 单个state赋值 https://forum.vuejs.org/t/vuex-state/39459/5
...Object.keys(states).reduce(
(obj, key) => ({
...obj,
[`set${key.firstUpperCase()}`]: (state, payload) => (state[key] = payload)
}),
{}
),
// 多个state批量赋值
setData (state, payload) {
// state = { ...state, ...payload } // eslint-disable-line
Object.assign(state, payload)
},
// 深度合并赋值
setDataDeep: mergeJSON,
// 表格页码改变
pageChange (state, payload) {
const { list, total } = payload
state.total = total
list.forEach((el, index) => {
el.key = index
})
state.list.splice(0, state.list.length, ...list)
}
}
}
// #region addFirstUpperCaseToPrototype - String原型链方法firstUpperCase
/**
* String原型链方法firstUpperCase
* @export
*/
export function addFirstUpperCaseToPrototype () {
/* eslint-disable */
String.prototype.firstUpperCase = function() {
return (([first, ...rest]) => first.toUpperCase() + rest.join(''))(this) // return this.replace(/^\S/, s => s.toUpperCase())
}
/* eslint-enable */
}
// #endregion
// #region addFlatToPrototype - Array原型链方法flat
/**
* Array原型链方法flat
* @export
*/
export function addFlatToPrototype () {
/* eslint-disable */
if (!Array.prototype.flat) {
Array.prototype.flat = function(num = 1) {
if (!Number(num) || Number(num) < 0) {
return this
}
let arr = []
this.forEach(item => {
if (Array.isArray(item)) {
arr = arr.concat(item.flat(--num))
} else {
arr.push(item)
}
})
return arr
}
}
/* eslint-enable */
}
// #endregion
// #region mergeJSON - 合并JSON
/**
* 直接修改 main 将 minor 合并到 main
* @export
* @param {*} main
* @param {*} minor
*/
export function mergeJSON (main = {}, minor = {}) {
Object.keys(minor).forEach((key) => {
const type = Object.prototype.toString.call(minor[key])
if (type === '[object Object]') {
mergeJSON(main[key] || {}, minor[key] || {})
} else {
main[key] = type === '[object Null]' || type === '[object Undefined]' ? main[key] : minor[key]
}
})
}
// #endregion在
action-types.js
中添加一个常量1
2
3export const BASE = {
login: 'base/login',
}修改
modules/base.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44import Vue from 'vue'
import { BASE, mutations } from '../mutation-types'
import axios from 'axios'
import qs from 'qs'
const states = {
version: '',
token: null,
user: {
userID: '',
userName: '',
name: '',
tel: '',
email: '',
head: '',
},
}
export default {
namespaced: true, // https://vuex.vuejs.org/zh/guide/modules.html#命名空间
state: states,
getters: {
versionGetter(state, getters) {
return state.version
},
},
mutations: {
...mutations(states),
setUserInfo(state, userInfo) {
userInfo.userID && (state.user.userID = userInfo.userID)
userInfo.USERNAME && (state.user.userName = userInfo.USERNAME)
userInfo.NAME && (state.user.name = userInfo.NAME)
userInfo.TEL && (state.user.tel = userInfo.TEL)
userInfo.EMAIL && (state.user.email = userInfo.EMAIL)
userInfo.HEAD && (state.user.head = userInfo.HEAD)
},
},
actions: {
async login({ commit, dispatch, state }, { userName, password }) {
let userInfo = await axios.post('/api/login', qs.stringify({ userName, password }))
commit(BASE.SET_USER_INFO, userInfo)
},
},
}
修改 vuex 主文件
index.js
,组合所有状态模块1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import Vue from 'vue'
import Vuex from 'vuex'
import root from './root'
import base from './modules/base'
// import createLogger from 'vuex/dist/logger' //vuex内置的Logger日志插件
const debug = process.env.NODE_ENV !== 'production' // 发布品种时需要用 Webpack 的 DefinePlugin 来转换 process.env.NODE_ENV !== 'production' 的值为 false
Vue.use(Vuex)
const state = {}
export default new Vuex.Store({
...root
modules: {
base,
// https://vuex.vuejs.org/zh/guide/modules.html#模块动态注册
},
strict: debug, // 开发阶段使用
// plugins: debug ? [createLogger()] : []//vuex插件,https://vuex.vuejs.org/zh/guide/plugins.html
})修改
main.js
,引入 vuex1
2
3
4
5
6
7
8
9
10
11
12//...
import store from './store/index'
//...
new Vue({
el: '#app',
router,
store,
// components: { App },
// template: '<App/>',
render: h => h(App), // https://cn.vuejs.org/v2/guide/render-function.html#JSX
})
https://juejin.im/post/5bcd967b6fb9a05d07197b1e
Vuex 实战:如何在大规模 Vue 应用中组织 Vuex 代码
super-vuex
添加mixins
文件夹
目录结构 ↓
1 | mixins |
添加filters
文件夹
目录结构 ↓
1 | filters |
添加utils
文件夹
目录结构 ↓
1 | utils |
src/main.js
中添加全局引用 ↓
1 | import * as filters from './utils/filters' |
封装 axios
1 | import Vue from 'vue' |
config 配置
build 生成的文件路径使用相对路径
修改config/index.js
文件中build
节点的assetsPublicPath
值
1 | module.exports = { |
开发的的时候需要使用代理(proxy)跨域访问服务器接口
修改config/index.js
文件中dev
节点的proxyTable
值
1 | module.exports = { |
分离线上环境和本地环境的配置信息
修改config/dev.env.js
与config/prod.env.js
,为不同的环境配置文件添加与NODE_ENV
同级的环境变量
1 | module.exports = { |
通用样式(SCSS)
目录结构 ↓
1 | assets |
base.scss
1 | @charset "utf-8"; |
common.scss
1 | @charset "UTF-8"; |
fun.scss
1 | @charset "UTF-8"; |
mixin.scss
1 | @charset "UTF-8"; |
variable.scss
1 | @charset "UTF-8"; |
demo 地址:https://github.com/MrLeo/wedive
查缺补漏
我用了 axios
, 为什么 IE 浏览器不识别(IE9+)
那是因为 IE 整个家族都不支持 promise, 解决方案:
1 | npm install es6-promise |
1 | // 在 main.js 引入即可 |