1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > vue-cli3前端性能优化与首屏加载优化(-06-17)

vue-cli3前端性能优化与首屏加载优化(-06-17)

时间:2021-05-30 20:40:38

相关推荐

vue-cli3前端性能优化与首屏加载优化(-06-17)

优化结果:

js文件大小变化: 8.6 M --> 336 KB

首页加载时间变化:52.82 s --> 8.58 s (浏览器模拟3G网络状态下)

一、优化工作前准备

二、配置打包环境,使用webpack4自带的分包功能

三、路由懒加载,按需引入

四、CDN替换依赖包引入

五、查看首屏文件加载,细节分析

六、开启gzip压缩

七、开启图片压缩

--------------------------------------------------------------------------

一、优化工作前准备

首先,添加打包文件分析插件,后续相关信息都需要依赖于此进行对比查看

1.添加插件

npm i -D webpack-bundle-analyzer

2.修改vue.config.js(文末有项目完整vue.config.js文件)

// 引入js分析插件let BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin// 环境区分主要为开发环境与其他环境(其他:生产,uat,测试等等)const isNotDevelopMentEnv = process.env.NODE_ENV !== 'development'chainWebpack (config) {config.plugins.delete('preload') // TODO: need testconfig.plugins.delete('prefetch') // TODO: need test// 配置启用打包文件分析if (isNotDevelopMentEnv) {// js文件包分析if (process.env.npm_config_report) {config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin).end()}}}

3.使用命令(我本地打包命令是npm run build,这里看各自项目对应命令,重点是在打包命令后面加上--report),打包完会自动打开浏览器显示项目中js文件情况,后续所做的优化效果,都将通过该视图进行查看对比

npm run build --report

4.初始项目情况

1. js文件分析

2. 首屏加载情况(本地node服务器运行,模拟3G网络,后续一致)

二、配置打包环境,使用webpack4自带的分包功能

1.项目使用的是vue-element-admin,默认已经做了代码分包,在vue.config.js中可以看到配置。

chainWebpack (config) {config.when(isNotDevelopMentEnv,config => {config.plugin('ScriptExtHtmlWebpackPlugin').after('html').use('script-ext-html-webpack-plugin', [{// `runtime` must same as runtimeChunk name. default is `runtime`inline: /runtime\..*\.js$/}]).end()config.optimization.splitChunks({chunks: 'all',cacheGroups: {libs: {name: 'chunk-libs',test: /[\\/]node_modules[\\/]/,priority: 10,chunks: 'initial' // only package third parties that are initially dependent},elementUI: {name: 'chunk-elementUI', // split elementUI into a single packagepriority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or apptest: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm},commons: {name: 'chunk-commons',test: resolve('src/components'), // can customize your rulesminChunks: 3, // minimum common numberpriority: 5,reuseExistingChunk: true}}})config.optimization.runtimeChunk('single')})}}

但是因为本地新建了许多环境文件,其中没有设置这些环境文件的NODE_ENV为production,导致分包功能丢失(原有分包功能是判断NODE_ENV为production才会执行)

2.环境文件添加NODE_ENV,对应环境打包就会使用分包功能

# 在以下环境中添加该句:表示当以下环境打包时使用分包# .env.test (公司测试环境)# .env.prd (公司生产环境)# .env.pet (性能测试环境)# .env.uat (用户测试环境)NODE_ENV=production

3. 项目情况

1. js文件分析

2. 首屏加载情况

三、路由懒加载,按需引入

1. 路由引入:使用 () => import('文件名')进行懒加载

{path: '/default/iconTool/Business',component: () => import('@/views/default/iconTool/Business')}

2.需要额外考虑:项目会根据接口返回的菜单数据动态生成路由,使用此方式会根据页面路由各自生成对应文件,但在开发时候会因为热更新而频繁加载导致变慢,所以需要区分开发与生产环境分开

2.1 开发环境使用require一次性引入页面

2.2 生产环境使用import懒加载

const notFoundComponent = () => import('@/views/default/404.vue')try {if (process.env.NODE_ENV === 'development') {// 开发环境一次性加载,避免多路由热更新缓慢component = require(`@/views/${item.path}.vue`).default} else {// 打包环境,按照路由懒加载拆分页面component = () => import(`@/views/${item.path}.vue`)// 当上面路由找不到时会使用下面模块,这里不写到时404页面会出不来// 由于import是使用懒加载,只有在使用的时候才会加载该页面,// 因此import时会直接使用页面地址,即使页面地址不存在也不会报错,// 也无法使用404替换当真正使用的时候就会找不到页面而报错,因此需要// 再这里多添加404,找不到时会可以使用404页面component = notFoundComponent}} catch (e) {component = notFoundComponent}

3.注意:

3.1 按道理是全局的路由(包括已有的静态的路由 &&动态添加的路由)引入全部使用条件判断去决定require还是import引入,将这里的逻辑封转成函数,然后引入路由的地方统一使用为函数;但在实际测试中部分页面路由使用此种方式引入会异常,所以折中处理,静态路由import引入,动态路由根据环境对应引入。项目可自行测试,也许你们的可以直接使用此方式

3.2使用 () => import('filePath')时,要求filePath必须是显示字符串,不能使用变量,但这里可以使用【模版字符串 +部分已明确路径】实现动态路径

component = () => import(`@/views/${filePath}.vue`)

3.3这里根据环境对应引入会有一个问题,就是必须将所有动态路由都预先定义在路由文件中,不然就会出现本地用require正常,但是生产环境上面使用import就会报错找不到页面

3.4根据环境引入还有个问题,就是匹配不到404页面逻辑了。猜想:使用import是懒加载,只有在用到页面才会去加载,假如配置了某个路径页面是不存在的,因为懒加载所以不会执行检测到错误,也就不会跑进用404页面替换当前路径的逻辑,所以看第2小点中代码注释说明,我是在import后面还加了一句设置404页面,这样页面地址存在的时候就会使用页面,不存在就会展示404页面,也不会报错

4.项目情况

4.1js文件分析

4.2首屏加载情况

5.路由懒加载分组

可查看vue-router 懒加载分组 webpackChunkName相关知识

{path: '/login',component: () => import(/* webpackChunkName: "login" */ '@/views/default/Login')}

主要就是直接懒加载的话模块太多,请求数太多,可以考虑合并模块,减轻服务器压力,但在实际优化中发现速度没有多大变化,可能还没领悟到精髓,这里就不做过多描述,项目自行考虑是否采用此优化,贴两张图意思

四、CDN替换依赖包引入

1.项目情况

2.项目打包时会根据依赖关系自动打包压缩依赖文件,当依赖文件过大,会导致首屏加载变慢

3.使用CDN:简单来说就是可以使用CDN引入依赖库,减少依赖包体积,释放服务器压力, 它由距离最近的缓存服务器直接响应,提高加载速度

4.进一步考虑:当我们在开发环境下,使用CDN引入会比我们直接引入依赖要慢,所以配置CDN需要只在生产环境

5.配置步骤

5.1修改vue.config.js

5.1.1定义使用CDN的相关数据

//环境区分主要为开发环境与其他环境(其他:生产,uat,测试等等)constisNotDevelopMentEnv=process.env.NODE_ENV!=='development'constcdnData={css:['/ajax/libs/element-ui/2.13.0/theme-chalk/index.css'],js:['/ajax/libs/vue/2.6.10/vue.min.js','/ajax/libs/axios/0.19.2/axios.min.js','/ajax/libs/vuex/3.1.0/vuex.min.js','/ajax/libs/vue-router/3.0.6/vue-router.min.js','/ajax/libs/element-ui/2.13.0/index.js','/ajax/libs/jquery/1.12.1/jquery.min.js','/ajax/libs/vee-validate/2.0.0-rc.21/vee-validate.min.js','/ajax/libs/vee-validate/2.0.0-rc.21/locale/zh_CN.js'],externals:{'vue':'Vue','vuex':'Vuex','vue-router':'VueRouter','element-ui':'ELEMENT','vuex':'Vuex','axios':'axios','vee-validate':'VeeValidate','jQuery':"jquery",'jquery':'window.$'}}

5.1.2 在configureWebpack中添加externals

configureWebpack:{externals:isNotDevelopMentEnv?cdnData.externals:{}}

5.1.3在chainWepack中添加如下

if(isNotDevelopMentEnv){config.plugin('html').tap(args=>{args[0].cdn=cdnDatareturnargs})}

5.2修改 public/index.html(根据环境,开发环境不使用CDN,生产环境才开放)

<html><head><!-- 样式文件优先加载 --><% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %><link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet"><% } %></head><body><div id="app"></div><!-- js加载 --><% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %><script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script><% } %></body></html>

5.3 注意: 采用CDN引入后,不需要删除原有依赖引入,因为在本地还是使用这些依赖进行调试的,打包后因为有CDN所以不会把这些依赖引入所以不用担心,import引入的不需要变更。例如main.js中使用importElementUIfrom'element-ui', 以上的代码已经实现在开发环境会设置不适用CDN,会使用依赖包文件;当发布到生产环境,因为我们已经在vue.config.js的externals中指代了element-ui,所以这个语句也是有效的可以直接使用CDN elementUI

6. 项目情况

6.1 js文件分析

6.2 首屏加载情况

五、查看首屏文件加载,细节分析

1. elementUI css资源发现重复引入,因为项目的使用主题化的时候已经引入样式文件,所以main.js中可以不需要引入elementUI样式文件了

// 必须使用主题化,ui组件的样式的颜色才会同步,统一UI颜色风格$--color-primary: $primary;$--color-danger: $danger;/* 改变 icon 字体路径变量,必需 */$--font-path: '~element-ui/lib/theme-chalk/fonts';@import "~element-ui/packages/theme-chalk/src/index";

具体看各自项目,自行优化,我这只是提提这边的优化

2. 关于elementUI的按需加载优化:因为项目修改了elementUI的主题色,如上图代码

@import "~element-ui/packages/theme-chalk/src/index";

这一句在按需加载后会失效,不知该如何实现按需加载后后的主题修改,所以暂时放弃按需加载,还是CDN直接处理

3. 项目情况

六、开启gzip压缩

1. 安装插件

npm i -D compression-webpack-plugin

2. 配置vue.config.js

constCompressionPlugin=require('compression-webpack-plugin')//引入gzip压缩插件chainWebpack (config) {// 配置启用打包文件分析if (isNotDevelopMentEnv) {// gzip压缩config.plugin('compression').use(CompressionPlugin, [{algorithm: 'gzip',test: new RegExp('\\.(js|css)$'),threshold: 10240, //超过多少字节进行压缩minRatio: 0.8 //至少压缩到原来体积的0.8,才会进行压缩}])}}

3. 需要服务器配合开启gzip

3.1 服务器为nginx,修改nginx.conf文件

server {gzip on;gzip_buffers 4 16K;gzip_comp_level 5;gzip_min_length 100k;gzip_types text/plain application/x-javascript application/javascript application/json text/css application/xml text/javascript image/jpeg image/gif image/png;gzip_vary on;}// gzip on|off; 是否开启gzip// gzip_min_length 100k; 压缩的最小长度(再小就不要压缩了,意义不在)// gzip_buffers 4 16k; 缓冲(压缩在内存中缓冲几块? 每块多大?)// gzip_comp_level 5; 压缩级别(级别越高,压的越小,越浪费CPU计算资源)// gzip_types text/plain; 对哪些类型的文件用压缩 如txt,xml,html,css,js等// gzip_vary on|off; 是否传输gzip压缩标志

3.2 服务器为tomcat,修改server.xml文件

<Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"compression="on" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,application/javascript"useSendfile="false"/>// compression="on" 打开压缩功能// compressableMimeType="text/html,text/xml" 压缩类型// useSendfile="false" 设置该属性将会压缩所有文件,不限阙值,不然可能按照阙值部分压缩

注意:在网上查阅文章的时候,发现有文章把【useSendfile】这个单词写错了,一直没效果,注意单词不要写错

4. 项目情况

4.1 js文件分析(注意切换到Gzipped模式查看,这个就是到时使用gzip后的文件大小)

4.2 首屏加载情况

七、开启图片压缩(插件地址在国外,使用jenkins自动部署时可能会下载异常,自行考虑

1. 安装插件

npm install -D image-webpack-loader

2. vue.config.js

chainWebpack (config) {if (isNotDevelopMentEnv) {// 开启图片压缩-使用异常config.module.rule('images').test(/\.(png|jpe?g|gif|svg)(\?.*)?$/).use('image-webpack-loader').loader('image-webpack-loader').options({ bypassOnDebug: true })}}

3. 图片变化

压缩前

压缩后

七、优化结果视图分析

总结:在最后的打包分析看,还有很大优化空间,就是字体图标iconfont模块,初始element管理框架已经对图标做了处理,只不过当时年轻,不懂为什么他们要这么做就放弃了他们的图标方案,总感觉引一份iconfont.js比较方便,现在就需要优化处理了,等后面继续优化

参考文章:

/sixam/article/details/106058083?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2

/a/1190000021444697

/sinat_17775997/article/details/83023148

/Newbie___/article/details/104925587?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

https://juejin.im/post/5bd02f98e51d457a944b634f

最后附上vue.config.js代码

'use strict'const path = require('path')const defaultSettings = require('./src/settings.js')const webpack = require('webpack')// 引入js分析插件let BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPluginconst CompressionPlugin = require('compression-webpack-plugin') // 引入gzip压缩插件function resolve(dir) {return path.join(__dirname, dir)}const name = defaultSettings.title || 'vue Admin Template' // page title// If your port is set to 80,// use administrator privileges to execute the command line.// For example, Mac: sudo npm run// You can change the port by the following methods:// port = 9528 npm run dev OR npm run dev --port = 9528const port = process.env.port || process.env.npm_config_port || 8000 // dev port// 环境区分主要为开发环境与其他环境(其他:生产,uat,测试等等)const isNotDevelopMentEnv = process.env.NODE_ENV !== 'development'// 非正式环境:包括开发环境与测试环境const isNotRegularEnv = process.env.VUE_APP_ENV === 'development' ||process.env.VUE_APP_ENV === 'test'const cdnData = {css: ['/ajax/libs/element-ui/2.13.0/theme-chalk/index.css'],js: ['/ajax/libs/vue/2.6.10/vue.min.js','/ajax/libs/axios/0.19.2/axios.min.js','/ajax/libs/vuex/3.1.0/vuex.min.js','/ajax/libs/vue-router/3.0.6/vue-router.min.js','/ajax/libs/element-ui/2.13.0/index.js','/ajax/libs/jquery/1.12.1/jquery.min.js','/ajax/libs/vee-validate/2.0.0-rc.21/vee-validate.min.js','/ajax/libs/vee-validate/2.0.0-rc.21/locale/zh_CN.js'],externals: {'vue': 'Vue','vuex': 'Vuex','vue-router': 'VueRouter','element-ui': 'ELEMENT','vuex': 'Vuex','axios': 'axios','vee-validate': 'VeeValidate','jQuery':"jquery",'jquery': 'window.$'}}// All configuration item explanations can be find in /config/module.exports = {/*** You will need to set publicPath if you plan to deploy your site under a sub path,* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,* then publicPath should be set to "/bar/".* In most cases please use '/' !!!* Detail: /config/#publicpath*/publicPath: '/',outputDir: 'ROOT',assetsDir: 'static',lintOnSave: !isNotDevelopMentEnv,// TODO 是否启动问题源码追踪productionSourceMap: isNotRegularEnv,css: {loaderOptions: {sass: {data: `@import "@/styles/global.scss";`}}},// 兼容ie浏览器// entry: ['babel-polyfill', './app/js'],devServer: {port: port,open: false,compress: true,overlay: {warnings: false,errors: true},// proxy: {// // change xxx-api/login => mock/login// // detail: /config/#devserver-proxy// [process.env.VUE_APP_BASE_API]: {//target: `http://127.0.0.1:${port}/mock`,//changeOrigin: true,//pathRewrite: {// ['^' + process.env.VUE_APP_BASE_API]: ''//}// }// },// after: require('./mock/mock-server.js')},configureWebpack: {// provide the app's title in webpack's name field, so that// it can be accessed in index.html to inject the correct title.name: name,resolve: {alias: {'@': resolve('src')}},plugins: [new webpack.ProvidePlugin({$:"jquery",jQuery:"jquery","windows.jQuery":"jquery"}),],// 性能提醒performance: {// 提醒方式hints: "warning",// 文件大小峰值控制(单位:kb)maxAssetSize: 10000},externals: isNotDevelopMentEnv ? cdnData.externals : {}},chainWebpack (config) {config.plugins.delete('preload') // TODO: need testconfig.plugins.delete('prefetch') // TODO: need test// 配置启用打包文件分析if (isNotDevelopMentEnv) {// 开启图片压缩-使用异常,暂时注释// config.module.rule('images')//.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)//.use('image-webpack-loader')//.loader('image-webpack-loader')//.options({ bypassOnDebug: true })// js文件包分析if (process.env.npm_config_report) {config.plugin('webpack-bundle-analyzer').use(BundleAnalyzerPlugin).end()}// 配置html文件引入变量config.plugin('html').tap(args => {args[0].cdn = cdnDatareturn args})// gzip压缩config.plugin('compression').use(CompressionPlugin, [{algorithm: 'gzip',test: new RegExp('\\.(js|css)$'),threshold: 10240, //超过多少字节进行压缩minRatio: 0.8 //至少压缩到原来体积的0.8,才会进行压缩}])}// set svg-sprite-loaderconfig.module.rule('svg').exclude.add(resolve('src/icons')).end()config.module.rule('icons').test(/\.svg$/).include.add(resolve('src/icons')).end().use('svg-sprite-loader').loader('svg-sprite-loader').options({symbolId: 'icon-[name]'}).end()// set preserveWhitespaceconfig.module.rule('vue').use('vue-loader').loader('vue-loader').tap(options => {pilerOptions.preserveWhitespace = truereturn options}).end()config// /configuration/devtool/#development.when(!isNotDevelopMentEnv,config => config.devtool('cheap-source-map'))config.when(isNotDevelopMentEnv,config => {config.plugin('ScriptExtHtmlWebpackPlugin').after('html').use('script-ext-html-webpack-plugin', [{// `runtime` must same as runtimeChunk name. default is `runtime`inline: /runtime\..*\.js$/}]).end()config.optimization.splitChunks({chunks: 'all',cacheGroups: {libs: {name: 'chunk-libs',test: /[\\/]node_modules[\\/]/,priority: 10,chunks: 'initial' // only package third parties that are initially dependent},elementUI: {name: 'chunk-elementUI', // split elementUI into a single packagepriority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or apptest: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm},commons: {name: 'chunk-commons',test: resolve('src/components'), // can customize your rulesminChunks: 3, // minimum common numberpriority: 5,reuseExistingChunk: true}}})config.optimization.runtimeChunk('single')})}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。