项目优化-针对vue
优化
1. 代码层面
-
部分代码使用懒加载方式,比如点击事件,回调函数内进行
import('xxx').then()
-
vue路由使用懒加载:
component: ()=> import('@/views/about.vue')
-
v-if/v-show: ① 尽量多的使用v-if,减少渲染dom数量,加快首屏加载速度。② 权限控制使用v-if
-
v-for遍历时,必须加上key,加上key是为了① 准确找到该条数据;② 在虚拟dom执行diff时,尽可能的复用dom节点
-
v-for遍历避免同时使用v-if。可以先判断好条件,再进行v-for。比如在computed中先处理好数据
computed:{ activedUsers(){return this.users.filter(user => user.isActived)}}
,在template中使用activedUsers进行遍历<li v-for="(user,i) in activedUsers">{{user.name}}</li>
-
防抖
-
computed、watch:
① 需要数值计算,并需要依赖其他数据,使用computed,因为可以利用computed的缓存特性,避免每次获取值时,都需要重新计算;
② 当我们在数据变化时,需要执行异步或者开销比较大的操作时,比如 图标渲染,使用watch,允许我们执行异步操作(访问一个api),限制我们执行该操作的频率(比如,只有在数据发生改变的时候,才可以做什么) -
vue长列表新能优化:
① vue的数据响应式是通过 Object.defineProperty()进行数据劫持,实现视图响应数据变化。但一些长列表仅仅只是展示作用,不会有任何改变,我们就不需要vue来劫持我们的数据,在大量数据展示的情况下,可以明显减少组件初始化的时间
② 使用Object.freeze()
方法来冻结一个对象,一旦被冻结,这个对象就不可以进行修改了,对于data或vuex里使用freeze冻结了的对象,vue不会做getter和setter的转换。export default{ data(){return{ users:[] }}, async created(){ const { users } = await axios.get('/api/users') // vue 不对 this.users存储的 users 进行响应式拦截 this.users = Object.freeze(users) } }
-
在beforeDestory/destroyed中进行 ① 事件注销解绑(事件有在mounted中进行注册) ② 清除定时器 ③ 定义的全局变量 设为
null
后者undefined
④ 在beforeDestory中进行注销eventBusmounted(){ let self = this; //监听窗口变化,重绘echarts window.onresize = () => { return (() => { if (!!self.fansCome) { self.fansCome.resize(); } })() }; eventBus.$on('vipUpdate', () => { //声明一个vipUpdate的eventBus事件 this.openDialog(this.curTab); }) }, beforeDestory(){ eventBus.$off('vipUpdate'); //页面销毁前注销该事件,避免vipUpdate事件的多次触发 }, destroyed() { //页面销毁时需要清除全局的onresize事件,便面其他页面触发onresize事件中的方法 window.onresize = null; },
-
图片资源懒加载:
vue-lazyload
图片过多的页面,使用图片懒加载。等图片到可是区域内,在进行加载图片
npm i vue-lazyload --save-dev
import VueLazyload from 'vue-lazyload' vue.use(VueLazyload)
-
第三方插件按需引入
-
无线长列表优化: 使用虚拟屏幕滚动,只渲染窗口处+上线部分数据
-
服务端渲染 ssr
-
减少或合并dom操作,或使用虚拟dom操作(非框架,原生html)
// 不好的方式 var elem = $('#elem'); for (var i = 0; i < 100; i++) { elem.append('<li>element '+i+'</li>'); } // 好的方式 var elem = $('#elem' ), arr = []; for (var i = 0; i < 100; i++) { arr.push('<li>element ' +i+'</li>' ); } elem.append(arr. join(''));
-
对大量数据计算,使用缓存
// ❌ for(var i = 0;i < data.length;i++){ // do something... } // ✅ for(var i = 0,len = data.length;i < len;i++){ // do something... }
2. webpack配置
-
图片loader,小于10k的图片转变为
base64
、或者使用svg
来代替小图标 -
图片 image-webpack-loader, 对大图进行压缩
-
js配置babel时,使用corejs的usage来实现按需兼容:
useBuiltIns: 'usage',corejs:{version: 3}
-
HMR
-
source-map:
- vue.config.js中
productionSourceMap:false
, 去掉.map文件; - webpack.config.js
① 开发环境:eval-source-map
② source-map 或者 hidden-source-map(隐藏源代码,但会提示构建后代码的错误信息)
- vue.config.js中
-
oneOf:loader加载时,用oneOf来保证一个文件只被一个rule进行处理,当然如果确实需要多个rule进行处理,要注意顺序
-
缓存:
① babel的配置项中添加cacheDirectory:options:{cacheDirectory: true}
,开启babel缓存,第二次构建时就不需要再次打包一些公共文件,构建速度更快。 (或者使用 babel-plugin-transform-runtime插件)
② 服务器资源缓存,使用hash、contenthash,配合http的缓存策略,设置过期时间为一年,只有文件改变(hash值改变)文件才会改变,否则使用缓存 -
多进程打包:thread-loader
当js代码比较多时,开启多进程打包: 在匹配js的rule中设置。 多进程打包,开启进程是需要消耗时间的600ms左右,进程通信也是需要时间的,所以文件不大,不需要开启多进程打包{ loader: 'thread-loader', options: { workers: 2 // 进程数为2 } },
-
code split:
将 第三方依赖包、js中动态导入 import('xxx')文件单独打包
webpack.config.jsoptimization: { /* splitChunks根据不同策略来分割bundle */ splitChunks: { chunks: 'all' } // 默认值:async ; all: 同时分割同步和异步代码 }
-
externals: 在webpack.config.js中配置externals,忽略依赖包打包,手动在html中加入cdn引入 => 减少文件体积,加快首屏速度
index.html<script src="https://cdn.bootcss.com/jquery/1.11.0/jquery.min.js"></script> <script src="//cdn.bootcss.com/vue/2.2.2/vue.min.js"></script> <script src='//cdn.bootcss.com/mint-ui/2.2.3/index.js'></script>
index.js
import $ from 'jquery'
webpack.config.js
module.exports = { // ... externals: { 'jquery': 'jQuery', 'vue': 'Vue', 'mint-ui': 'MINT' } }
-
main.js去中引入的第三方包的样式表去除,该在index.html中使用cdn引入
减小打包体积,加快首屏加载速度 -
dll分包,针对第三方模块:dll是webpack内置模块,webpack只处理一次第三方模块,后续打包告诉webpack不需要处理这些依赖
- dll.config.js: 使用dll将指定第三方包单独打包出来,并生成对应的manifest.json(映射关系表)
- 在package.json中配置dll命令,并运行 dll命令,将第三方依赖单独打包出来
- npm i add-asset-html-webpack-plugin --save-dev
- webpack.config.js:
① 告诉webpack哪些包不参与打包,同时使用的名字也得变,manifest.json
② 在html中自动引入这些单独打包出来的文件
dll.config.js
/* webpack.dll.js 运行命令: webpack --config webpack.dll.js 使用dll技术,将指定第三方依赖包单独打包成相应的单独文件, ① 减小打包后文件体积; ② 减少第三方包的打包次数,加快构建速度(因为某些指定库已经打包过,再次构建不需要打包这些库,要构建的文件变少,速度变快) 使用插件: 生成一个manifest.json,提供与第三方依赖包的映射关系 new webpack.DllPlugin({ name: '[name]_[hash]', // 映射库的暴露名称 path: resolve(__dirname, 'dll/manifest.json') // 输出文件路径 }) */ const { resolve } = require('path') const webpack = require('webpack') module.exports = { entry:{ // vue: ['vue/dist/vue.esm.js','vue-router'], vue: ['vue/dist/vue.esm.js'], vendors: ['jquery', 'lodash', 'axios'] }, output: { filename: '[name].js', path: resolve(__dirname, 'dll'), library: '[name]_[hash]' // 打包的库里面向外暴露的内容的名称叫什么名字 }, plugins: [ new webpack.DllPlugin({ name: '[name]_[hash]', // 映射库向外暴露的内容名称 path: resolve(__dirname, 'dll', '[name].manifest.json') // 输出文件路径 }) ], mode: 'production' }
package.json
"scripts":{ // ... "dll": "webpack --config dll.config.js" }
webpack.config.js
const { resolve } = require('path') const plugins = [ // 插件 ... ] /* 利用fs读取dll目录中,单独打包出来的js文件,并自动添加插件到plugins中 */ fs.readdirSync(resolve(__dirname, 'dll')).forEach(file => { if(/.*\.manifest.json$/.test(file)){ plugins.push(new webpack.DllReferencePlugin({ manifest: resolve(__dirname, `dll/${file}`)})) } if(/\.js$/.test(file)){ plugins.push(new AddAssetHtmlWebpackPlugin({filepath: resolve(__dirname, `dll/${file}`), publicPath: './'})) } }) module.exports = { entry: './src/index.js', output:{ filename: './static/js/[name][contenthash:6].js', path: resolve(__dirname, 'dist')}, module:{ rules: [ { oneOf: [ // ... ] } ] }, plugins: [...plugins], optimization:{ splitChunks:{ chunks: 'all'} }, devtool: 'source-map', mode: 'production' }
-
vue服务器渲染 ssr(实际项目中并未使用过,具体的不太了解)
3. 基础web技术层面
- nginx开启gzip压缩
- 对静态资源进行缓存,强缓存的expire、协商缓存etag、last-modify
- 合理使用cdn请求静态资源(要综合平衡首屏加载时间问题)
- 加快响应速度,可以是请求总时间降低
- 加大浏览器的同时请求数量,因为使用了不同域名,一个域名可以并发4-8个请求(为什么图片要分多个域名?)
- 减少cookie
- 使用骨架屏
- 优化http请求
- 资源缓存:合理使用cache control来代替http请求
- 强缓存expire时间设为1年
- 优化响应:配合 etags,浏览器请求的文件与服务器一至,返回304, 浏览器使用 缓存
- 资源文件名加上hash,只有打包的文件hash值改变,才会请求新的资源,否则一至使用缓存
- 优化响应:nginx开启gzip压缩
- 合理合并js、css文件,使用webpack构建工具进行打包boundle,同时要平衡首屏加载速度
- 合理使用浏览器的并发请求数,一个域名下可同时并发多个请求,ie可以同时请求下载4个,谷歌可以同时请求下载8个:所以可以适当使用cdn请求一些静态资源,图片使用懒加载,
- 小图使用inline image,即base64减小cookie体积,每个请求都会携带cookie,不滥用cookie
- 大图可以使用webpack进行大图压缩
- 资源缓存:合理使用cache control来代替http请求
- 图片
- 懒加载
- webpack进行配置
- 小图 base64
- 大图 图片压缩
- 图片尽量使用webp格式(有更优化的算法,体积更小)
- 重要图片,写上alt属性
- app加上过渡动画,loading...优化用户体验