vue-cli3项目加载优化记录全过程

由于cli3配置简化,cli2中原来存在的很多配置出现了一些改变,现在就以下遇到的问题进行记录

一、 实现vue-router懒加载功能

使用初衷是为了减少首页http请求过多,默认预加载了全部的js文件

官网文档提出 PreloadPrefetch,vue-cli3默认使用俩大功能

vue.config.js增加如下配置,取消prefetchpreload,这样就是实现加载当前所需

chainWebpack(config) {
    config.plugins.delete('preload') 
    config.plugins.delete('prefetch') 
  }

1、Preload用来指定页面加载后很快会被用到的资源,所以在页面加载的过程中,我们希望在浏览器开始主体渲染之前尽早 preload
2、Prefetch用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。

// webpackPrefetch 开启home页的预加载 ,未设置的默认执行路由懒加载
// webpackChunkName 指定about页面在当前home加载完提前加载
component: () => import(/* webpackChunkName: "about",webpackPrefetch: true */ "../views/Home.vue"),

二、webpack 外包扩展 (externals

使用externals减小打包的后js体积大小,cdn加速来提升访问速度

bootcdn官网地址

初衷为了减小打包后的文件大小,下图为浏览器使用externals 前后 请求文件总数量,时间,文件大小的对比图

externals使用前后对比图

1.在vue.config.js文件中增加如下配置

// 对应的版本可以看package.json
const cdnMap = {
    css: [
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/theme-chalk/index.css'
    ],
    js: [
        'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js',
        'https://cdn.bootcdn.net/ajax/libs/vuex/3.1.3/vuex.min.js',
        'https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.6/vue-router.min.js',
        'https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js',
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js',
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/locale/zh-CN.min.js',
    ]
}
// 需要扩展的资源
const externals = {
    vue: "Vue",
    vuex: "Vuex",
    "vue-router": "VueRouter",
    "element-ui":"element-ui",
    "axios":"axios",
}
const regDev = RegExp(/development/);
module.exports = {
  chainWebpack(config) {
        // 外部扩展
        config.externals((regDev.test(process.env.NODE_ENV))? {} : externals)
        config.plugin('html').tap(args => {
            args[0].cdn = (regDev.test(process.env.NODE_ENV))? {} : cdnMap
            return args
        })
    }
}

2.通过 bootcdn官网可以查询到vue,vue-router,vuex,axios,element-ui的cdn外网地址,查询示例如下

vue的cdn地址

为了区分开发环境和生产环境进行如下配置

html文件中引入cdn地址 如下

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>cdn外部扩展</title>
  <% if (process.env.NODE_ENV !== 'development') { %>
    <% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
      <link href="<%=css%>" rel="preload" as="style" />
      <link rel="stylesheet" href="<%=css%>" as="style" />
    <% } %>
    <% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
      <link href="<%=js%>" rel="preload" as="script" />
      <script type="text/javascript" src="<%=js%>"></script>
    <% } %>
  <% } %>
</head>
<body>
  <div id="app"></div>
</body>

你以为当前就结束了?太天真了
当前配置完启动会报错 element is not defined

element引用报错

明明我已经在html里边引用了,开发模式怎么还报错呢?
what are you 弄撒呢

经过层层排除问题,找到所有有关联关系的文件,最终锁定main.js入口文件

// 判断在开发模式下才是用引入  
const regDev = RegExp(/development/); // 为多个开发环境做的预留
if(regDev.test(process.env.NODE_ENV)){ //开发模式
  const ElementUI = require("element-ui"); // 不能使用import形式引入
  require("element-ui/lib/theme-chalk/index.css");
  const locale = require("element-ui/lib/locale/lang/zh-CN");
  Vue.use(ElementUI, {
    locale,
  });
}

现在有个问题,为什么vuex,vue-router,axios不需要注释掉也不报错呢?vue.use(Vuex)externals之间有什么关联呢?

实际上使用了externals之后依赖,里边所有的use都可以去掉了,因为外部差距都会先判断有没有vue实例,直接挂载上去,element-ui出现问题是因为本身与vue有强依赖关系,所以element-ui使用externals的基础上同时去掉main.js里的use

关于element的单模块按需引用也可以有效的减少js体积,比如我只用到了 按钮,分页,表格等等

import {Button,ButtonGroup,Input,Pagination,Table,TableColumn} from 'element-ui';
Vue.use(Button);
Vue.use(ButtonGroup);
 Vue.use(Input);
 Vue.use(Pagination);
 Vue.use(Table);
Vue.use(TableColumn);

这类情况不需要使用cdn外扩加载,可以直接打包进js文件

三、压缩代码配置 提升效率,保证安全 uglifyjs-webpack-plugin

UglifyJS中文文档

// cli3 默认不包含此依赖,需要下载引入
npm i uglifyjs-webpack-plugin --save

vue.config.js添加以下代码

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
........此次省略其他配置
    configureWebpack(config){
        //压缩代码
        config.plugins.push(
            new UglifyJsPlugin({ // 更多配置请参考 uglifyJs官方文档
                uglifyOptions: {
                      compress: {
                        drop_debugger: true, //生产环境自动删除debugger
                        drop_console: true,  //生产环境自动删除console
                      },
                      warnings: false,
                },
                sourceMap: false,//是否生成map文件
                parallel: true,//使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
            })
        )
    }
}

四、代码分割配置优化 splitChunks

1.node_modules全部打包成chunk-libs并设置优先级为10
2.将vantUI单独拆包,设置优先级为20
3.将自己的comnponents打包成common设置优先级为5
vue.config.js 添加以下代码

module.exports = {
........此次省略其他配置
      chainWebpack(config) {
            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
                    },
                    // 如果想爸vantUI单独打包出来 可以通过当前方式设置  其他后引入的依赖也可以通过这种方式单独打包成一个js文件
                    vantUI: {
                        name: 'chunk-vantUI', // split vantUI into a single package
                        priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
                        test: /[\\/]node_modules[\\/]_?vant(.*)/ // in order to adapt to cnpm
                    },
                    commons: {
                        name: 'chunk-commons',
                        test: resolve('src/components'), // can customize your rules
                        minChunks: 3, //  minimum common number
                        priority: 5,
                        reuseExistingChunk: true
                    }
                }
            })
      }
}

启动报错:resolve is not defind
莫慌这是因为引用失败,vue.config.js 文件上方加入引用

const path = require('path');
function resolve(dir){
    return path.join(__dirname, dir)
}

ok了,当前启动成功

五、合并css文件(经过测试没发现明显优化)

主要是借助ExtractTextPlugin插件
优化思路是减少css文件的请求数量

css: {
        // 是否将组件中的 CSS 提取至一个独立的 CSS 文件中,当作为一个库构建时,你也可以将其设置为 false 免得用户自己导入 CSS
        // 默认生产环境下是 true,开发环境下是 false
        extract: !(regDev.test(process.env.NODE_ENV))? true : false,
        // 当为true时,css文件名可省略 module 默认为 false
        modules: false,
        // 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能
        sourceMap: false,
        //向 CSS 相关的 loader 传递选项(支持 css-loader postcss-loader sass-loader less-loader stylus-loader)
        loaderOptions: { css: {}, less: {} }
    }

为什么要CSS分离,开启CSS分离之后每个组件的css会单独打包,造成页面上有大量请求,所以在正式环境中将CSS分离关闭

启动项目发现提示warning警告
WARN "css.modules" option in vue.config.js is deprecated now, please use "css.requireModuleExtension" instead.
css-loader升级v3后使用css.requireModuleExtension代替css.modules

css: {
        ........
        // 当为true时,css文件名可省略 requireModuleExtension默认为 false
        requireModuleExtension: false,
        .......
    }

六、使用gzip的形式进行访问优化(正在测试中,稍后进行更新)

利用 compression-webpack-plugin 开启gzip

// 安装依赖
npm i compression-webpack-plugin --save-dev

vue.config.js文件添加如下配置

const CompressionWebpackPlugin = require('compression-webpack-plugin')//开启gzip
module.exports = {
......此处省略一万行
    chainWebpack(config) {
            config.plugin('compression').use(CompressionWebpackPlugin).tap(() => [
                    {
                        test: /\.js$|\.html$|\.css/, // 匹配文件名
                        threshold: 1024, // 超过1k进行压缩
                        deleteOriginalAssets: false // 是否删除源文件
                    }
            ])
    }
}


gzip压缩率还是很高的,下方是对比图

gzip压缩率对比

经过一系列配置,发现vue.config.js文件变量太多了,看上去不美观,现进行文件抽离
注意:不能使用es6import引入,配置需要使用commonjsrequire形式引入,es6commonjs的关系自行百度,这里就不在复述了

抽离文件config.base.js文件内容如下,放在项目src>config>config.base.js

'use strict';
const path = require('path');
//cdn外链地址 对应的版本可以看package.json
exports.cdnMap = {
    css: [
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/theme-chalk/index.css'
    ],
    js: [
        'https://cdn.bootcdn.net/ajax/libs/vue/2.6.11/vue.min.js',
        'https://cdn.bootcdn.net/ajax/libs/vuex/3.1.3/vuex.min.js',
        'https://cdn.bootcdn.net/ajax/libs/vue-router/3.1.6/vue-router.min.js',
        'https://cdn.bootcdn.net/ajax/libs/axios/0.19.2/axios.min.js',
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/index.js',
        'https://cdn.bootcdn.net/ajax/libs/element-ui/2.13.2/locale/zh-CN.min.js',
    ]
}

// 需要扩展的资源
exports.externals = {
    vue: "Vue",
    vuex: "Vuex",
    "vue-router": "VueRouter",
    "element-ui":"element-ui",
    "axios":"axios",
}

//正则检测当前是否为开发环境
exports.regDev = RegExp(/development/);
//路径指定函数
function resolve(dir){
    return path.join(__dirname, dir)
}

//压缩代码配置
exports.UglifyJsPluginOptions={
    uglifyOptions: {
        compress: {
            drop_debugger: true, //生产环境自动删除debugger
            drop_console: true,  //生产环境自动删除console
        },
        warnings: false,
    },
    sourceMap: false,//是否生成map文件
    parallel: true,//使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
}

// 代码分割配置
exports.splitChunksOptions={
    chunks: 'all',
    cacheGroups: {
        libs: {
            name: 'chunk-libs',
            test: /[\\/]node_modules[\\/]/,
            priority: 10,
            chunks: 'initial' // only package third parties that are initially dependent
        },
        commons: {
            name: 'chunk-commons',
            test: resolve('src/components'), // can customize your rules
            minChunks: 3, //  minimum common number
            priority: 5,
            reuseExistingChunk: true
        }
    }
}
//gzip配置
exports.CompressionWebpackPluginOptions ={
    test: /\.js$|\.html$|\.css/, // 匹配文件名
    threshold: 10240, // 超过10k进行压缩
    deleteOriginalAssets: false // 是否删除源文件
}

vue.config.js文件修改成如下形式
为区分开发环境,生产环境使用regDev进行如下区分,开发环境不需要使用访问类优化,只有生产环境才需要

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const {regDev,cdnMap,externals,UglifyJsPluginOptions,splitChunksOptions,CompressionWebpackPluginOptions} = require("./src/config/config.base")

module.exports = {
    outputDir: 'dist',
    assetsDir: "static",
    publicPath: (regDev.test(process.env.NODE_ENV))? "/" : "./",
    productionSourceMap: false,
    devServer: {
        port: 8093,
        host: '0.0.0.0',
        open: true,
        proxy: {
            '/API': {
                target: 'http://0.0.0.0:8088',
                changeOrigin: true,
                pathRewrite: {
                    '^/API': ''
                },
                ws: false
            }
        }
    },

    // 在exports中添加,这里很关键,不配置不行
    transpileDependencies: ['element-ui'],
    chainWebpack(config) {
        // 在chainWebpack中添加下面的代码
        config.entry('main').add('babel-polyfill') // main是入口js文件

        // 移除 prefetch 插件
        config.plugins.delete('prefetch')
        // 开发环境不执行
        if(!regDev.test(process.env.NODE_ENV)){
            // 外部扩展
            config.externals(externals)
            config.plugin('html').tap(args => {
                args[0].cdn = cdnMap
                return args
            })
            // 代码分割
            config.optimization.splitChunks(splitChunksOptions)
            //压缩代码
            config.optimization.minimizer = [
                new UglifyJsPlugin(UglifyJsPluginOptions)
            ]
            // 开启gzip 但是Nginx上也要做配置
            config.plugin('compression').use(CompressionWebpackPlugin).tap(() => [
                CompressionWebpackPluginOptions
            ])
        }
    },
    css: {
        // 是否将组件中的 CSS 提取至一个独立的 CSS 文件中,当作为一个库构建时,你也可以将其设置为 false 免得用户自己导入 CSS
        // 默认生产环境下是 true,开发环境下是 false
        extract: !(regDev.test(process.env.NODE_ENV))? true : false,
        // 当为true时,css文件名可省略 module 默认为 false
        modules: false,
        // 是否为 CSS 开启 source map。设置为 true 之后可能会影响构建的性能
        sourceMap: false,
        //向 CSS 相关的 loader 传递选项(支持 css-loader postcss-loader sass-loader less-loader stylus-loader)
        loaderOptions: { css: {}, less: {} }
    }

}

详细的vue.config.js配置说明

深层优化方案借助分析工具 webpack-bundle-analyzer(正在研究学习中,后续进行补充)

以上所有为个人实践总结和参考前辈经验,欢迎各位大佬指出问题,小弟感激不尽(性能优化持续进行中,后续继续总结)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351