写在开头的开头
目标搭建一个Vue Cli 中后台项目的基础。一步一步搭建,众多知识点已经有很多文字,这里会综合比较多的连接。接触的点会多,但是篇幅,不希望太大。
学习的时候,希望多翻阅文档,资料地址也会引入到原文
资料
- Vue CLI.
- iview-admin GitHub
- 在线 Demo
- vue-cli3 从搭建到优化
- 没有废话的vue高级进阶( 二 )8种组件通信详解
- 给axios做个挺靠谱的封装(报错,鉴权,跳转,拦截,提示)
- VUE 9个性能优化秘密?(vue-9-perf-secrets)
后台可能是这样的以 iview-admin 为栗子在线 Demo
拆解(结合将要做的做一个iView 拆解)
- 侧边栏菜单权限过滤,这部分实现需要配合路由,权限数据保存,跨组件通信(iview 使用了vuex)
- 标签页,这部分也需要吧打开的页面使用数据存起来,也搭配
keep-alive
附录中的 VUE 9个性能优化秘密?(vue-9-perf-secrets) 其中之一优化办法就是keep-alive
将要做什么
- 开发环境 @vue/cli 创建项目
- 字体库 & SVG使用
- 网络请求 axios封装api
- 数据管理 vuex
- vue router 做好路由守卫
- 附录
- vue.config.js
- webpack-bundle-analyzer 使用
- VUE 9个性能优化秘密?(vue-9-perf-secrets)
@vue/cli 创建项目
开发环境:
+ Win10 x64
+ node v10.15.3
+ npm v6.4.1
+ @vue/cli 3.6.3
复制代码
vue create project-name
复制代码
缩减篇幅,省略具体创建过程。
- step.1 选择设置,默认设置或者自定义Manually
- step.2 自定义配置,有 TypeScript支持,也有PWA。
- step.3 路由模式选择 history
- step.4 CSS 编译器选择
- step.5 ESLint 以及配置选择
- step.6 何时检查 Lint
- step.7 ESLint 配置文件写在哪里,可以单独文件,也可以在package.json
- step.8 是否保存配置信息,下次项目使用 N
- 自动安装依赖
目录规划
.
├── build 项目构建配置
├── public 打包所需静态资源
└── src
├── api AJAX请求
└── assets 项目静态资源
├── icons SVG 图标资源
├── fonts 字体图标资源
└── images 图片资源
├── components 业务组件
├── config 项目运行配置
├── directive 自定义指令
├── libs 封装工具函数
├── router 路由配置
├── store Vuex配置
└── views 页面文件
复制代码
字体库 && SVG
main.js 引入全局 阿里 Ant 图标字体库
SVG
图标字体,也可以用SVG代替,方案可以做一个SVG组件,通过名字,载入不同的 SVG图标,通过size,控制图标大小,color,控制颜色即可
网络请求 axios封装api
给axios做个挺靠谱的封装(报错,鉴权,跳转,拦截,提示) iview-admin
axios 用继承封装。当中亮点,对请求队列做了处理,多次请求同一个地址,会节流。
// 安装
npm i axios
复制代码
- 统一捕获接口报错 : 用的axios内置的拦截器
- 弹窗提示: 引入 Element UI的Message组件
- 报错重定向: 路由钩子
- 基础鉴权: 服务端过期时间戳和token,还有借助路由的钩子
- 客户端支持防止 CSRF/XSRF
- 表单序列化: 我这边直接用qs(npm模块),你有时间也可以自己写
- 号外,对请求队列优化,尤其是翻页时多次请求。(未完成)
请求拦截器 Axios.interceptors.request
带上自己需要的参数,比如CSRF/XSRF,基础鉴权 token,请求时间戳
对请求数据做处理,转表单,或转Json
对错误的情况统一处理
响应拦截器 Axios.interceptors.interceptors
对响应结果统一处理,响应结果状态status 判断解封装,错误处理等
axios可配置
iview-admin
libs/axios.js
import axios from 'axios'
import store from '@/store'
// import { Spin } from 'iview'
const addErrorLog = errorInfo => {
const { statusText, status, request: { responseURL } } = errorInfo
let info = {
type: 'ajax',
code: status,
mes: statusText,
url: responseURL
}
if (!responseURL.includes('save_error_logger')) store.dispatch('addErrorLog', info)
}
class HttpRequest {
constructor (baseUrl = baseURL) {
this.baseUrl = baseUrl
this.queue = {}
}
getInsideConfig () {
const config = {
baseURL: this.baseUrl,
headers: {
//
}
}
return config
}
destroy (url) {
delete this.queue[url]
if (!Object.keys(this.queue).length) {
// Spin.hide()
}
}
interceptors (instance, url) {
// 请求拦截
instance.interceptors.request.use(config => {
// 添加全局的loading...
if (!Object.keys(this.queue).length) {
// Spin.show() // 不建议开启,因为界面不友好
}
this.queue[url] = true
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截
instance.interceptors.response.use(res => {
this.destroy(url)
const { data, status } = res
return { data, status }
}, error => {
this.destroy(url)
let errorInfo = error.response
if (!errorInfo) {
const { request: { statusText, status }, config } = JSON.parse(JSON.stringify(error))
errorInfo = {
statusText,
status,
request: { responseURL: config.url }
}
}
addErrorLog(errorInfo)
return Promise.reject(error)
})
}
request (options) {
const instance = axios.create()
options = Object.assign(this.getInsideConfig(), options)
this.interceptors(instance, options.url)
return instance(options)
}
}
export default HttpRequest
复制代码
Vuex 数据管理
面试通常都会被问到,数据通信的问题,跨组件之间如何实现数据管理。
当然方法不止 vuex
,
- props
- provide / inject
- 本地存储Local Storage,Cookies 也可以实现。
- vuex,项目比较简单时,store 都在一个文件中,或者 getter,actions,mutations,mutations-types,state拆分。应用比较大的时候,可以按模块拆分。
扩展阅读
import Vue from 'vue'
import Vuex from 'vuex'
// 按模块拆分
import app from './module/app'
// 打开 vuex logs
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
// 按模块引入
modules: {
app
},
strict: debug,
plugins: debug ? [createLogger()] : []
})
复制代码
Vue Router. 做好路由守卫
包含的功能:
- 嵌套的路由/视图表
- 模块化的、基于组件的路由配置
- 路由参数、查询、通配符
- 基于 Vue.js 过渡系统的视图过渡效果
- 细粒度的导航控制
- 带有自动激活的 CSS class 的链接
- HTML5 历史模式或 hash 模式,在 IE9 中自动降级
- 自定义的滚动条行为
高级进阶
- 导航守卫
- 路由元信息
- 过度动效
- 数据获取
- 滚动行为
- 路由懒加载
我们可以做什么
- 嵌套路由/视图表,子路由
children
可以更好的组织页面 - 路由参数,跳转,传参,匹配
- 过渡效果,滚动行为
- 导航守卫,权限限定
- 路由懒加载,结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载
- 判断用户是否已经登录,控制进入权限页面。
- 定义路由的时候可以配置
meta
字段,个性化定制一些功能 - 等等
全局前置守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// todo 在路由守卫中,可以判断用户登录情况,鉴权,
})
复制代码
菜单栏权限过滤
iview-admin
中侧边菜单栏权限,是在 router meta增加一个字段,
流程
- vuex 搭配使用 vuex-persistedstate vuex 数据持久化
- 1.用户登录后,获取用户权限,权限列表(列表必须能和,router meta 字段对比)
- 2.提交用户数据,权限等到 Vuex(数据中心)
- 3.用户权限和 router meta 生成权限列表,提交到vuex
- 4.菜单栏页面,getter 到数据变化,更新菜单栏
- 5.注意:思考是否需要,数据需要本地化一份,在用户刷新页面,或者vuex(数据中心)数据丢失时,还要能拿到用户权限,token等数据。
vue.config.js
调整 webpack 配置最简单的方式就是在 vue.config.js 中的 configureWebpack 选项提供一个对象:
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
new MyAwesomeWebpackPlugin()
]
}
}
复制代码
该对象将会被 webpack-merge 合并入最终的 webpack 配置。
文档中,提及链式操作 (高级)、修改 Loader 选项、替换Loader、新建Loader、修改插件,等等。
环境变量和模式
NODE_ENV
如果在环境中有默认的 NODE_ENV,你应该移除它或在运行 vue-cli-service 命令的时候明确地设置 NODE_ENV。
模式
模式是 Vue CLI 项目中一个重要的概念。默认情况下,一个 Vue CLI 项目有三个模式:
development
模式用于vue-cli-service serve
production
模式用于vue-cli-service build
和vue-cli-service test:e2e
test
模式用于vue-cli-service test:unit
注意模式不同于 NODE_ENV
,一个模式可以包含多个环境变量。也就是说,每个模式都会将 NODE_ENV
的值设置为模式的名称——比如在 development 模式下 NODE_ENV
的值会被设置为 "development"
。
你可以通过为 .env
文件增加后缀来设置某个模式下特有的环境变量。比如,如果你在项目根目录创建一个名为 .env.development
的文件,那么在这个文件里声明过的变量就只会在 development
模式下被载入。
你可以通过传递 --mode
选项参数为命令行覆写默认的模式。例如,如果你想要在构建命令中使用开发环境变量,请在你的 package.json
脚本中加入:
"dev-build": "vue-cli-service build --mode development",
复制代码
在客户端侧代码中使用环境变量
只有以 VUE_APP_
开头的变量会被 webpack.DefinePlugin
静态嵌入到客户端侧的包中。你可以在应用的代码中这样访问它们:
console.log(process.env.VUE_APP_SECRET)
复制代码
vue.config.js
- 有了
NODE_ENV
可以对不同环境做出不同的配置 - 文件夹别名
resolve.alias
- 设置代理
devServer
,解决跨域开发问题
const path = require('path')
// Webpack包文件分析器
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
function resolve (dir) {
return path.join(__dirname, './', dir)
}
// 项目部署基础 (webpack 的 devServer 地址)
// process.env.NODE_ENV
// 正式环境 production
// 开发环境 development
// 默认情况下,我们假设你的应用将被部署在域的根目录下,
// 例如:https://www.my-app.com/
// 默认:'/'
// 如果您的应用程序部署在子路径中,则需要在这指定子路径
// 例如:https://www.foobar.com/my-app/
// 需要将它改为'/my-app/'
const BASE_URL = process.env.NODE_ENV === 'production'
? '/my-app/'
: '/'
module.exports = {
// 这里是对环境的配置,不同环境对应不同的BASE_API,以便 axios 的请求地址不同 baseUrl 从 Vue CLI 3.3 起已弃用,请使用publicPath
publicPath: BASE_URL,
// tweak internal webpack configuration.
// see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
// 如果你不需要使用eslint,把lintOnSave设为false即可
lintOnSave: false,
/**
* 对内部的 webpack 配置进行更细粒度的修改
* https://github.com/neutrinojs/webpack-chain see
* https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
* @param config
*/
chainWebpack: (config) => {
// key,value自行定义,比如.set('@@', resolve('src/components'))
config.resolve.alias
.set('@', resolve('src'))
.set('api', resolve('src/api'))
.set('common', resolve('src/common'))
.set('components', resolve('src/components'))
},
/**
* 调整 webpack 配置
* https://cli.vuejs.org/zh/guide/webpack.html#%E7%AE%80%E5%8D%95%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F
* @param config
*/
configureWebpack: config => {
// 生产and测试环境
let pluginsPro = [
// Webpack包文件分析器(https://github.com/webpack-contrib/webpack-bundle-analyzer)
// new BundleAnalyzerPlugin()
]
// 开发环境
let pluginsDev = []
if (process.env.NODE_ENV === 'production') {
// 为生产环境修改配置... process.env.NODE_ENV !== 'development'
config.plugins = [...config.plugins, ...pluginsPro]
} else {
// 为开发环境修改配置...
config.plugins = [...config.plugins, ...pluginsDev]
}
},
// 打包时不生成.map文件
productionSourceMap: false,
// webpack-dev-server 相关配置 https://webpack.js.org/configuration/dev-server/
// 这里写你调用接口的基础路径,来解决跨域,如果设置了代理,那你本地开发环境的axios的baseUrl要写为 '' ,即空字符串
// devServer: {
// proxy: 'localhost:3000'
// }
devServer: {
// host: 'localhost',
host: '0.0.0.0',
port: 8000, // 端口号
https: false, // https:{type:Boolean}
open: true, // 配置自动启动浏览器
hotOnly: true, // 热更新
// 配置跨域处理,只有一个代理
proxy: {
'/my-app/*': {
target: 'http://xxx.xxx.xxx.xxx:xxxx/',
changeOrigin: true,
pathRewrite: {
'^/my-app': ''
}
},
'/SocketWeb/*': {
target: 'http://xxx.xxx.xxx.xxx:xxxx',
changeOrigin: true,
// websocket支持
ws: true,
secure: false
}
}
}
}
复制代码
开发基础环境可以了,开发中遇到的文件夹别名,请求跨域的问题解决了,自定义配置,也可以更具自己公司的情况配置。还有比较自定义的部分可以参照文档配置。
webpack-bundle-analyzer
安装
$ npm intall webpack-bundle-analyzer --save-dev
复制代码
- 配置vue.config.js 打开注释
webpack-bundle-analyzer
的部分。 - 在script中添加
// 在运行 build 时,后台面添加 --report
npm run build --report
// 或者在script中添加新命令
"analyze": "npm_config_report=true npm run build"
复制代码