Vue项目的搭建
前言:为公司前端项目搭建整理的一份文档
一、项目环境
1、node安装
下载地址:http://nodejs.cn/download/
安装检查:
命令: node -v npm -v
注:若需更改node模块(node_modules)的安装目录自行百度,由于创建的Vue项目中node模块是安装在项目文件夹下就不做更改了。
若使用淘宝npm镜像(cnpm)通过以下命令安装:
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
2、Vue-Cli安装
1)、vue-cli@2.X版本
官方文档:https://github.com/vuejs/vue-cli/tree/v2#vue-cli–
注:使用的一些UI组件不适配vue-cli@3.x,所以需要安装vue-cli@2.x
比如VUX
安装命令:
$ npm install -g vue-cli
查看版本:
$ vue –version
使用: $ vue init <template-name> <project-name> 例如: $ vue init webpack my-project
将从vuejs-templates/webpack中提取模板信息,根据此模板信息生成项目(./my-project/)
2)、@vue/cli 3.x 版本
官方文档:https://cli.vuejs.org/zh/guide/
安装命令:
$ npm install -g @vue/cli
查看版本:
$ vue --version
使用:
$ vue create <project-name>
二、项目创建
本项目使用的vue-cli版本:2.9.6
1、创建本地项目
在本地/d/Projects目录下创建名为project-demo的项目
Vue build
第一个选项可以不基于.vue文件开发,在运行时会进行编译
第二个选项基于.vue文件开发,比第一个选项小6KB
使用vue-router
使用ESLint进行代码检测(ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。)
ESLint代码检测规则(本项目使用Standard),官方文档连接:
Standard:https://github.com/standard/standard/blob/master/docs/README-zhcn.md
Airbnb:https://github.com/airbnb/javascript
安装完成后运行上述命令:
$ cd project-demo
$ npm run dev
根据运行成功后的地址在浏览器进行访问
2、目录结构如下:
. |-- README.md |-- build | |-- build.js | |-- check-versions.js | |-- logo.png | |-- utils.js | |-- vue-loader.conf.js | |-- webpack.base.conf.js | |-- webpack.dev.conf.js | `-- webpack.prod.conf.js |-- config | |-- dev.env.js | |-- index.js | `-- prod.env.js |-- index.html |-- package-lock.json |-- package.json |-- src | |-- App.vue | |-- assets | | `-- logo.png | |-- components | | `-- HelloWorld.vue | |-- main.js | `-- router | `-- index.js `-- static
3、将本地仓库与线上仓库关联:
$ cd /project-demo $ git init $ git remote add origin <线上git仓库地址(SHH)> $ git add . $ git commit $ git push -u origin master
三、项目初始化配置
附:使用Visual Studio Code推荐安装以下扩展程序
1) Vetur 2) ESLint 3) Beautify 4) GitLens — Git supercharged 5) Git History 6) HTML CSS Support 7) HTML Snippets 8) Vue 2 Snippets 9) Debugger for Chrome
附:目录结构
项目结构多采用vue-element-admin项目结构:https://panjiachen.gitee.io/vue-element-admin-site/zh/guide/
|-- README.md |-- build # 项目打包配置 |-- config # 项目配置 |-- index.html # html模板 |-- package-lock.json # postcss 配置 |-- package.json # package.json |-- src # 源代码 | |-- App.vue # 入口页面 | |-- api # 所有请求 | |-- assets # 主题 字体等静态资源 | |-- axios # axios通用配置 | |-- components # 全局公用组件 | |-- directive # 全局指令 | |-- filters # 全局 filter | |-- layout # 全局 layout | |-- main.js # 入口文件 加载组件 初始化等 | |-- router # 路由 | |-- store # vuex配置,全局 store管理 | |-- styles # 全局样式、自定义主题等 | |-- utils # 全局公用方法 | `-- views # 所有页面 `-- static
1、ESLint规则配置
修改.eslintrc.js文件配置,在rules对象中添加以下配置(其他配置默认):
// 在函数括号之前不允许空格 'space-before-function-paren': ['error', 'never']
修改package.json文件配置:
添加后运行以下命令可以快速修复ESLint报错:
$ npm run lint
2、UI组件安装
1)、PC端(element-ui)
官方文档:https://element.eleme.cn/#/zh-CN/component/installation
npm安装:
$ npm i element-ui -S
Element完整引入
在 main.js 中写入以下内容:
import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import App from './App' import router from './router' Vue.config.productionTip = false Vue.use(ElementUI) /* eslint-disable no-new */ new Vue({ el: '#app', router, render: h => h(App) })
2)、移动端(VUX/Mint UI)
VUX文档:https://doc.vux.li/zh-CN/
Mint UI文档:https://mint-ui.github.io/docs/#/zh-cn2
3、css预处理器安装(less/sass/stylus)
本项目安装sass预处理器
Sass文档:https://www.sass.hk/docs/
安装:
$ npm install node-sass --save-dev
$ npm install sass-loader --save-dev
4、axios配置
文档:https://www.kancloud.cn/yunye/axios/234845
1)、npm安装:
$ npm install axios
2)、请求拦截配置
于src目录下创建axios文件夹,并于其中创建index.js文件,内容如下(PC端配置了element-ui的一个加载组件):
import axios from 'axios' import { Notification, Loading } from 'element-ui' // 根据环境设置请求baseURL axios.defaults.baseURL = process.env.API_ROOT // 请求超时时间 axios.defaults.timeout = 6000 // post请求头 axios.defaults.headers.post['Content-Type'] = 'application/json;charset=utf-8' let loading // 请求拦截器 axios.interceptors.request.use( config => { // 加载提示 loading = Loading.service({ lock: true, text: '拼命加载中', spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' // background: 'rgba(255, 255, 255, 0)' }) return config }, error => { return Promise.reject(error) } ) // 响应拦截器 axios.interceptors.response.use( response => { // 关闭加载提示 loading.close() return response }, error => { // 关闭加载提示 loading.close() if (error && error.response) { switch (error.response.status) { case 400: Notification.error({ message: '请求错误' }) break case 403: Notification.error({ message: '拒绝访问' }) break case 404: Notification.error({ message: '请求地址出错' }) break case 408: Notification.error({ message: '请求超时' }) break case 500: Notification.error({ message: '服务出错' }) break case 501: Notification.error({ message: '网络未实现' }) break case 502: Notification.error({ message: '网络错误' }) break case 503: Notification.error({ message: '服务不可用' }) break case 504: Notification.error({ message: '网络超时' }) break default: } } else if (error.message === 'Network Error') { Notification.error({ message: '网络错误' }) } else if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) { Notification.error({ message: '请求超时,请重试' }) } return Promise.reject(error) } ) export default axios
3)、API请求管理
于src目录下新建api文件夹,文件夹中根据API类型新建对应的js文件模块以便管理。
例如用户信息请求API新建userApi.js文件
文件内容格式如下:
import request from '@/axios' /** * 用户登录 * @param {Object} data 用户账号信息对象 */ export function login(data) { return request({ url: '/user/login', method: 'post', data }) } /** * 获取用户信息 * @param {String} token token */ export function getInfo(token) { return request({ url: '/user/info', method: 'get', params: { token } }) }
4)、js中调用
//script标签内进行导入 import * as userApi from '@/api/userApi' //使用: userApi.login({username: '张三', password: '123456'}) .then(res => { const { data } = res console.log(data) })
5、Vue Router配置
1)、router/index.js配置文件修改
文档:https://router.vuejs.org/zh/installation.html
|-- src … | |-- layout | | `-- index.vue … | |-- router | | |-- index.js | | `-- modules | | `-- userCenter.js
import Vue from 'vue' import Router from 'vue-router' import Layout from '@/layout' /* Router Modules */ import userRouter from './modules/userCenter' Vue.use(Router) const router = new Router({ // 路由跳转后重新定位到顶部 scrollBehavior: () => ({ y: 0 }), routes: [ userRouter, { path: '/', name: 'Layout', component: Layout, meta: {title: '主页'} }, { path: '*', component: () => import('@/components/404') } ] }) export default router
可以根据项目需要可配置导航守卫、路由模块等相关配置
2)、全局layout
在src目录下新建layout文件夹,其中新建index.vue文件
<template> <div> Layout <router-view/> </div> </template> <script> export default { name: 'Layout' } </script> <style lang="scss" scoped> </style>
6、Vuex配置
1)、Vuex安装
$ npm install vuex --save
2)、vuex-persistedstate安装
文档:https://github.com/robinvdvleuten/vuex-persistedstate
vuex-persistedstate是一个将vuex持久化的插件
$ npm install --save vuex-persistedstate
3)、配置
于src目录下新建store文件夹,其下新建index.js、mutation-types.js文件和modules文件夹
-- store | |-- index.js | |-- modules | | `-- user.js | `-- mutation-types.js
将 store 分割成模块(module),每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
使用常量替代 mutation 事件类型(使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名),把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然。
新建mutation-types.js文件存放常量,例如:
// mutation-types.js // user modules export const SET_USER_NAME = 'SET_USER_NAME'
// user.js import * as types from '../mutation-types' const state = { userName: '' } const mutations = { [types.SET_USER_NAME](state, str) { state.userName = str } } const actions = { saveUserName({ commit }, str) { commit(types.SET_USER_NAME, str) } } export default { namespaced: true, state, mutations, actions }
index.js配置如下:
vuex-persistedstate插件仅将需要持久化的状态值进行持久化。
import Vue from 'vue' import Vuex from 'vuex' import CreatePersistedState from 'vuex-persistedstate' Vue.use(Vuex) // webpack v4.35.2 依赖管理 // https://webpack.js.org/guides/dependency-management/#requirecontext const modulesFiles = require.context('./modules', false, /\.js$/) // 自动请求modules目录下的模块文件 const modules = modulesFiles.keys().reduce((modules, modulePath) => { // 模块名为modules目录下的js文件名 const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1') // value 为请求到的模块文件内容 {default:{state:{...},mutations:{...},actions:{...}}, __esModule: true} const value = modulesFiles(modulePath) modules[moduleName] = value.default return modules }, {}) const vuexPersisted = new CreatePersistedState({ key: 'VuexPersisted', storage: window.localStorage, reducer: state => ({ user: { userName: state.user.userName } }) }) const store = new Vuex.Store({ modules, plugins: [vuexPersisted] }) export default store
4)、组件中使用
使用方式就不一一列举了,以下仅列举其中一种,其他的可以去看官方文档。
访问state值时,使用对象展开运算符将此对象混入到局部计算属性中。
import { mapState } from 'vuex' export default { computed: { ...mapState('user', ['userName']) }, mounted() { console.log(this.userName) } }
访问actions,使用对象展开运算符将此对象混入到局部 methods 中。
import { mapActions } from 'vuex' export default { methods: { ...mapActions('user', ['saveUserName']), // 登录时存储用户名 login() { this.saveUserName('80xxxxx06@qq.com') } } }
7、自定义样式/主题配置
于src目录下新建styles文件夹
|-- src … | |-- styles | | |-- border.css #app一像素边框解决 | | |-- element-ui.scss #自定义的element-ui组件样式 | | |-- element-variables.scss #通过element-ui样式变量修改通用主题 | | |-- index.scss #全局通用样式引入/定义 | | |-- mixin.scss #自定义的scss混合指令 | | |-- reset.css #css样式重置文件 | | |-- transition.scss #自定义过渡样式 | | `-- variables.scss #自定义样式变量
在main.js中引入
import '@/styles/index.scss' // 全局 css 样式
8、打包相关配置修改
1)、config/index.js文件修改
将文件中build对象中assetsPublicPath值改为’./’
assetsPublicPath: './',
2)、build/utils.js文件修改
if (options.extract) { return ExtractTextPlugin.extract({ use: loaders, publicPath: '../../', fallback: 'vue-style-loader' }) } else { return ['vue-style-loader'].concat(loaders) }
以上配置中添加了 publicPath 配置 ’../../’
此配置解决打包后图片读取路径错误问题
9、多环境打包配置
一)、配置方式一:
https://www.cnblogs.com/VoiceOfDreams/p/11052447.html
二)、配置方式二:
注:由于我之前写文档一的配置方式稍显繁琐,现将文档一的配置进行简化。此次文档就只配置开发、测试、生产三个环境,添加其他环境的配置方式一样。
1、安装依赖:cross-env
$ npm i --save-dev cross-env
2、修改项目 package.json 文件
"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "lint": "eslint --fix --ext .js,.vue src", "build:dev": "cross-env NODE_ENV=production ENV_CONFIG=dev node build/build.js", "build:test": "cross-env NODE_ENV=production ENV_CONFIG=test node build/build.js", "build:prod": "cross-env NODE_ENV=production ENV_CONFIG=prod node build/build.js" },
3、修改项目 config 配置文件
目录结构 |-- config | |-- dev.env.js | |-- index.js | |-- prod.env.js | `-- test.env.js
1)、修改 dev.env.js 文件
'use strict' const merge = require('webpack-merge') const prodEnv = require('./prod.env') const env = process.env.NODE_ENV module.exports = merge(prodEnv, { NODE_ENV: '"development"', ENV_CONFIG: '"dev"', API_ROOT: env === 'production' ? '"http://(线上开发环境请求地址)"' : '"/apis"' })
2)、添加 test.env.js 文件
'use strict' const merge = require('webpack-merge') const prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"testing"', ENV_CONFIG: '"test"', API_ROOT: '"http://(线上测试环境请求地址)"' })
3)、修改 prod.env.js 文件
'use strict' module.exports = { NODE_ENV: '"production"', ENV_CONFIG: '"prod"', API_ROOT: '"http://(线上生产环境请求地址)"' }
4)、修改 index.js 文件
解决本地开发启动后浏览器跨域问题,在此处配置服务代理。
dev对象参数下修改如下配置:
proxyTable: { '/apis': { target: 'http://(本地开发环境请求地址)', changeOrigin: true, // 是否允许跨域 pathRewrite: { '^/apis': '' // 重写 } }, },
build 对象参数下添加如下参数
devEnv: require('./dev.env'),
testEnv: require('./test.env'),
prodEnv: require('./prod.env'),
4、修改 build.js 文件
// process.env.NODE_ENV = 'production' // 将此行代码注释 // const spinner = ora('building for production...') const spinner = ora('building for ' + process.env.NODE_ENV + ' of ' + process.env.ENV_CONFIG + ' production...')
5、修改 webpack.prod.conf.js 文件
原代码 const env = require('../config/prod.env') 修改后 const env = config.build[process.env.ENV_CONFIG+'Env']
三)、项目中 HTTP 请求设置
配置文档一中是在 API 文件中获取配置的请求地址,也可以在 axios 配置中设置统一的默认请求地址
详见 axios 配置中,axios 文件夹下的 index.js 配置文件
// 根据环境设置请求baseURL axios.defaults.baseURL = process.env.API_ROOT