基于vue脚手架的一些配置及优化
css
- 私有样式写在
<style lang="scss" scoped>
里面,一定要加scoped
-
覆盖element的样式写在styles里,在common.scss里导入(注意:这里是所有页面统一用的样式),另外强烈建议不要直接覆盖ui框架里面的样式,请用一个class别名进行覆盖。
-
css样式穿透:
- 单个页面或组件覆盖element的样式,请写在
<style lang="scss">
里,并且以文件名添加class,防止影响其他样式,如
文件名为 order.vue 在<template>最外层div上加上class="order" 需要覆盖的样式全写在.order里面 <style lang="scss"> .order { ... } </style> 注:有同名文件的话把父文件带上,比如/a/order,/b/order,请用a-order作为class名
scss
使用/deep/语法,css使用>>>语法:
.table /deep/ .el-XXXX { .... } or .table >>> .el-XXXX { .... } 补充 .table ::v-deep .el-XXXX { .... }
注:css用/deep/不会生效,scss用>>>不会生效。经过我的实验node-sass能解析/deep/和::v-deep,dart-sass不能解析/deep/,能解析::v-deep,由于vue-cli最新的全部用dart-sass,sass团队也从node-sass迁移至dart-sass,故建议用::v-deep
参考vue-loader说明链接 - 单个页面或组件覆盖element的样式,请写在
-
样式统一风格,我们开发一个东西时,整体的ui风格一定要一致,不然会感觉很怪异。开发人员都是不同的,那么怎么做到统一风格呢?开发ui原则:
- 能用ui框架做到的不要自己写,如
<el-button>
这个按钮,很多设计稿上是32px高度。但是直接写出来是40,这里我们很多同学就自己写样式把elment上的样式给覆盖了,但是<el-button>
这个组件是可以通过设置size属性来改变大小的,自己强行写由于padding的不同反而样式不好看了,所以仔细看ui框架的文档还是很重要的。 - 假如
<el-button>
这个按钮的高度在size属性里不能设置,如果只是几个按钮有这样的情况的话,建议在按钮所在的页面写样式覆盖,如果很多页面很常用的话,建议取一个特殊class名字,如btn-h32,在styles里全局引用。
- 能用ui框架做到的不要自己写,如
- css的一些建议,css布局还是挺重要的,大多数前端同学css基础真的有些差,建议多看看css的相关知识,一个熟练的布局可以让代码结构,样式看起来清晰明了,添加需求时也非常方便。
项目结构
-src
-api // 请求文件夹 请求请全部写在本文件下,最好和发起请求的页面同路径同名,请求当有接口的注释说明
-asset // 静态资源 图片等静态资源全部放在这个下面,最好区分路径,方便查找
-commons // 公共样式 公共样式,这个主要是统一样式,进行ui框架等样式覆盖的,或者写的scss函数等,覆盖样式在main.js导入,
-components // 公共组件
-config // 项目配置
-util // 工具函数
-view // 视图
-ruoter // 路由
axios:
- axios请求的参数不要写在请求里面,应该调用时传参到请求里,因为后续参数增加后,只需要对调用点进行修改,不然的话要修改两个地方,而且在不知情的情况下只会修改调用点,会因为参数少而困扰很久。
export const xxxx = (id) => {
return request({
url: 'xxxx?id=' + id, 这里如果id换名了,或者添加参数了,就会很难看
method: "get",
});
}
xxxx(111) 调用 单从调用来看,不知道111是什么东西,id?
----------------------
export const xxxx = (params) => {
return request({
url: 'xxxx,
method: "get",
params
});
}
xxxx({id: 1111})
- 为什么要对axios进行封装?
-
axios请求的post是用data,get是params,写的时候为了方便可以统一用data。
-
请求签名,你们以后可能会遇到。
-
配置不同环境请求(后面环境变量时一起说)。
export default function(options) {
let params = {
method: options.method,
url: options.url,
params: options.params,
data: options.params
}
if (options.method.toLowerCase() === 'get') {
delete params.data
} else if (options.method.toLowerCase() === 'post') {
delete params.params
}
return axios(params)
}
题外,进阶配置:
其实我们写的很多请求格式都一个样,可以集中处理,但是这样优缺点都有,优点是结构明确,代码少,缺点是不能用编辑器直接跳转引用的函数,自己取舍。
config与环境变量
confg主要用于环境区分,可以用作请求区分和功能区分。
环境变量主要是区分环境打包和配置环境变量参数。
环境变量
- 环境变量是什么?有什么用?
环境变量其实就是一个变量,vue-cli运行时会产生一个全局的变量,可以通过这个变量来区分环境和打包配置。
- 怎么配置环境变量
我们在打包是会运行对应的命令
开发环境 yarn(npm) run build:dev
测试环境 yarn(npm) run build:test
生产环境 yarn(npm) run build:prod
- 这三个命令实际上是package.json下的scripts里面的命令,比如
npm run build:dev
实际上是运行的是vue-cli-service build --mode development
。 -
vue-cli-service build
是vue-cli的打包命令,--mode development
是设置一个模式,vue-cli会在打包时进行对根目录下的.env.xxx文件对查找,读取里面对变量并进行全局设置。比如development对应.env.development
,prod对应.env.prod
。
看看里面有什么吧:
NODE_ENV=development
VUE_APP_TITLE=development
NODE_ENV
,这个是打包生成文件的配置,development
和production
的区别在于生成文件带不带hash值,一般来说打包带有hash值更好一点,所以一般除了本地环境都设置成production
,这里有个注意点,npm run serve
也是读取的.env.development
,所以本地运行时不要把.env.development
里面的NODE_ENV
设置成production
,不然修改后的热更新会非常慢。-
VUE_APP_TITLE
,这个就是我们所设置的环境变量了,在打包时我们可以通过console打印process.env.VUE_APP_TITLE
发现这个就是development,所以能通过这个区分环境变量了。
config
简单解释一下
const env = process.env
let config = {
host: "", // 请求地址
port: "", // 端口号
baseUrl: "", // 带不带baseUrl,比如/api,一般用作nginx代理匹配
type: "development" // 什么环境
}
config.type = env.VUE_APP_TITLE
if (config.type === 'development') {
config.baseUrl = '/api'
// config.port = ':8084'
} else if (config.type === 'production') {
config.host = 'xxx.xxx.xxx'
config.port = ':8084'
config.baseUrl = '/api'
} else if (config.type === 'test') {
config.baseUrl = '/api'
}
export default config
使用,以axios为例
import cfg from '@/config'
axios.defaults.baseURL = `${cfg.host}${cfg.port}${cfg.baseUrl}`
production环境请求调用aaaa("/abc/efg")
那么我们请求的完整路径则是xxx.xxx.xxx:8084/api/abc/efg
注意事项:
- 若development的baseUrl设置了/api,且没设置host和port,vue.config.js的proxy也有/api,那么请求会被vue.config.js的proxy拦截代理到设置的target,简单说就是development的请求如果是完整的,那么不会被代理
xx.xxx.xxx:8084/api/abc/efg
,如果是/api/abc/efg
,就会代理。 -
代理只会发生在本地运行,打包后是没有代理的,要代理的话前端是只设置/api的baseURl,用nginx做/api的匹配代理转发。
打包优化
打包优化的话有什么gzip压缩,移除console呀,这个自行百度吧,这里主要是cdn打包优化。
1. 为什么要用cdn,有什么好处?
其实我们写的代码引入的包在不升级的情况都是不变的,只有我们自己写的业务代码是变化的,所以每次打包没有必要把不变的代码也进行打包,这些包用cdn进行加载,可以走304缓存,让页面加载速度更快,打包体积更小。
- 当然业务代码也可以走cdn缓存,因为打包是带有hash值的,代码变化了hash也会变,但是入口的html千万不要缓存,因为一切加载的js,css都是html加载的,html被缓存了的话,js,css再怎么变也没效果。
-
免费cdn:
- 首先,我们要明确那些包是需要抽离的,把要抽离的用一个对象装起来:
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'vue-baidu-map': 'VueBaiduMap',
'nprogress': 'NProgress',
'ant-design-vue': 'antd',
'vue-cropper': 'vue-cropper'
}
这里要注意,对象的key是包的名字,对象的value是包导出的名字,比如Vue也就是window.Vue可以直接调用,不是在main.js里import的xxx
- 在chainWebpack里添加config.externals(externals),当然可以用环境变量做判断,到底是什么环境要添加cdn,config.externals是在打包时不会把这些打进包里,调用用对象的value代替。
-
写个cdn的json,注意包的顺序,需要依赖的在下面,比如vue-router依赖于vue,所以vue要在vue-router上面,在chainWebpack里添加html要引入的链接
const cdn = {
dev: {
css: [
'https://cdn.jsdelivr.net/npm/ant-design-vue@1.3.13/dist/antd.min.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
],
js: [
'https://cdn.jsdelivr.net/npm/vue-cropper@0.4.9/dist/index.min.js'
]
},
prod: {
css: [
'https://cdn.jsdelivr.net/npm/ant-design-vue@1.3.13/dist/antd.min.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
],
js: [
'https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js',
'https://cdn.jsdelivr.net/npm/vuex@3.1.1/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/axios@0.19.0/dist/axios.min.js',
'https://cdn.jsdelivr.net/npm/vue-baidu-map',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js',
'https://cdn.jsdelivr.net/npm/ant-design-vue@1.3.13/dist/antd.min.js',
'https://cdn.jsdelivr.net/npm/vue-cropper@0.4.9/dist/index.min.js'
]
}
}
config.plugin('html').tap(args => {
if (~['analyz', 'production'].indexOf(process.env.VUE_APP_TITLE)) {
args[0].cdn = cdn.prod
} else {
args[0].cdn = cdn.dev
}
return args
})
- 在html里用模版添加cdn的链接
<head>
....
<!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
<% } %>
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
<% } %>
...
</head>
<body>
<noscript>
<strong>We're sorry but vue-test doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
<% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<!-- built files will be auto injected -->
</body>
- 私有cdn的话,没有那么麻烦,把
publicPath
,设置成私有cdn的地址,这样html的引入全是cdn的链接,文件打包后把文件上传至cdn空间就行了。
近期评论