基于 Vue 的 H5 首屏加载优化以及打包优化实践

偶尔有一天觉得公司的 H5 项目在 build 的时候特别慢,看了下时间大约在 60 - 80s,而且由于需要在微信中看真机效果,所以需要经常 build,于是去网上找找资料如何缩短这个加载时间。以下是我在优化加载过程中遇到的一些坑及优化方案。

vue-build.png
网络请求.png

从上图中我们发现编译耗时接近 80s,而且仔细看还发现 vendor.js 和 app.css 异常的大,这不仅导致我们在编译阶段很慢,而且首屏响应也会变慢,怎么去解决呢?通过查了些资料我们开始着手优化起来~

优化方案

  • 服务端开启 gzip 压缩
  • webpack 配置优化
  • 按需加载第三方组件

项目配置


该 H5 商城基于 vue-cli 脚手架构建,引入 vue-router 、vuex、axios 搭建 SPA 应用,UI 工具库选用的是 iView、mint-ui,以及不少第三方组件。

服务端 Nginx 开启 gzip

这个很重要,尽管我们现在已经做了,但是需要提醒的是服务端 Nginx 开启 gzip 压缩能大幅度提高页面加载速度,前端需要配置 compression-webpack-plugin 插件。这里注意根据 webpack 版本来选择 compression-webpack-plugin 插件版本,webpack4 使用 2.x ,webpack4 以下版本选择 1.x

  • config/index.js
module.exports = {
  build: {
        ...
        // Gzip off by default as many popular static hosts such as
        // Surge or Netlify already gzip all static assets for you.
        // Before setting to `true`, make sure to:
        // npm install --save-dev compression-webpack-plugin
        productionGzip: true,  // 这里开启 gzip,默认为false
        productionGzipExtensions: ['js', 'css']   // 只压缩 js css 不压缩图片 
  }
}
  • build/webpack.prod.conf.js
if (config.build.productionGzip) {
    const CompressionWebpackPlugin = require('compression-webpack-plugin')
    //增加浏览器CPU(需要解压缩), 减少网络传输量和带宽消耗 (需要衡量,一般小文件不需要压缩的)
    webpackConfig.plugins.push(
        new CompressionWebpackPlugin({
            asset: '[path].gz[query]',
            algorithm: 'gzip',
            test: new RegExp(
                '\\.(' +
                config.build.productionGzipExtensions.join('|') +
                ')$'
            ),
            threshold: 10240,               // 达到10kb的静态文件进行压缩 按字节计算
            minRatio: 0.8                   // 只有压缩率比这个值小的资源才会被处理
        })
    )
}
开启双端gzip后效果1.png

开启双端gzip后效果2.png

由图中可以看出,这里配置完后首屏加载速度提升是很明显的,vendor 由 176kb 压缩成61kb,app.css 由 1.22M 压缩成186kb,接下来我们可以看看如何通过配置 webpack 优化打包速度。

1、使用 webpack-parallel-uglify-plugin 插件来压缩代码

vue 默认使用 UglifyJsPlugin 插件进行代码压缩,由于是单线程,压缩效率较低;我们可以使用 webpack-parallel-uglify-plugin 插件,可以合理使用 CPU 资源,通过设置缓存能够大大减少非首次构建时间

const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
    ...
    // 注释 webpack 提供的 UglifyJS 插件
    //new UglifyJsPlugin({
    //  uglifyOptions: {
    //    compress: {
    //      warnings: false
    //    }
    //  },
    //  sourceMap: config.build.productionSourceMap,
    //  parallel: true
    //}),
    // 增加 webpack-parallel-uglify-plugin 来替换
    new ParallelUglifyPlugin({
      cacheDir: '.cache/',
      uglifyJS:{
        output: {
          comments: false
        },
        compress: {
          drop_debugger: true, // 去除生产环境的 debugger 和 console.log
          drop_console: true
        }
      }
    })
webpack-parallel-uglify-plugin.png

此时编译发现打包时间缩短了不少,不错,总算看到点希望,接下来我们来尝试下缩小打包后的文件大小。

2、减少打包文件数量

随着项目的迭代,引入的第三方库也会越来越多,每次 build 都会对所有文件进行打包,耗时也会越来越久,不利于平时开发。这个时候我们可以通过减少一些不经常更新的包来单独打包,我们有两种方式,第一种是通过 CDN 引入包然后配合 externals 配置的方式也可以,抽离第三方包,只打包业务代码。第二种方式就是引入 DLL 文件,dllplugin 是 webpack DllPlugin、webpack 的简称,思路也是讲一些第三方包抽离出来单独打包,后面就不需要重新打包了。

使用方法:

  • 1、在 build 文件夹下新建 webpack.dll.conf.js,内容如下:
const path = require('path')
const webpack = require('webpack')

module.exports = {
    entry: {
        //这地方写你想抽离的包,可以参考你的package.json文件下的 dependencies
        vendor: [
            'vue/dist/vue.common.js',
            'vue-router',
            'vuex'
        ]
    },
    output: {
        //这地方写你打包后生成文件的路径
        path: path.join(__dirname, "../dist/dll/"),
        filename: '[name].dll.js',
        library: '[name]_library'
    },
    plugins: [
        //这个插件是重点,用于打包上面entry里配置的包
        new webpack.DllPlugin({
            path: path.join(__dirname, '../dist/dll', '[name]-manifest.json'),
            name: '[name]_library'
        }),
        new webpack.optimize.UglifyJsPlugin()
    ]
}

这里我们先抽离出 vue,vue-router 和 vuex,然后在 package.json 中 scripts
下加入该命令 "build:dll": "webpack --config build/webpack.dll.conf.js",执行 yarn build:dll 命令将第三方依赖打包

yarn dll.png

可以看到在 dist 目录下生成了一个 dll 文件夹,里面有 vendor-manifest.json 和 vendor.dll.js 这两个文件,我们打开 build/webpack.base.conf.js 文件,编辑添加如下配置

const webpack = require('webpack');
module.exports = {
    ...
    plugins: [
        new webpack.DllReferencePlugin({
            manifest: require('../dist/vendor-manifest.json')
        })
    ]
    ...
}

接着我们只要在 index.html 中引入 vendor.dll.js 即可

<body>
    <div id="app"></div>
    <script src="./dist/dll/vendor.dll.js"></script>
</body>

接下来我们执行 yarn build 发现 vendor 文件由原来的 1.57M 变成了 931KB,看起来效果不错

步骤一.png

这里我们只是预编译了三个文件,当我们把其他的第三方库打包后看看效果如何

步骤二.png

vendor 文件现在只有 270 kb 了,缩小了不知道多少倍~ 到这里,dll 的配置基本完事了,我们还可以把 index.html 中的手动引入的脚步改成自动导入。

// webpack.base.conf.js
new webpack.DllReferencePlugin({
    context: path.resolve(__dirname, '..'),
    manifest: require('../dist/vendor-manifest.json')
}),
// 在htmlwebpack后插入一个 AddAssetHtmlPlugin 插件,用于将 vendor 插入打包后的页面
new AddAssetHtmlPlugin({
    filepath: require.resolve('../dist/dll/vendor.dll.js'),
    includeSourcemap: false
})

一开始踩了很多坑,配置 dll 一直配不对,还是要细心~

然后删除 index.html 引入的脚本即可。接下来我们可以继续优化一下 css ,从我们的 main.js 文件可以看到全局引入了很多 css,我们可以通过 CDN 方式引入。

多说一句,cdn 引入其实原理和 dll 一样,但是我们知道每一个 cdn 资源也是需要走请求的,如果很多 cdn 的话还是要权衡一下,同理,dll 文件如果太大,会影响首屏加载,不能光图打包爽。

CDN 引入全局样式和字体库

我们把 main.js 中一些可以从 cdn 上找到的样式和字体都通过 cdn 引入到 index.html 中,然后再打包

首页.png
app.css.png

我们发现 css 文件也响应缩小了一些,最终效果如下图所示:

end.png
end.png

总结

从首次打包花了 80s 到现在 47s,优化了接近一半的时间,在研究 webpack 时也遇到不少问题,基本都是自己 Google ,不断的翻阅文档和相关 issues。有的插件在 webpack3.x 和 webpack4.x 不尽相同,没有做向下兼容,需要不断地踩坑,比如这次的 add-asset-html-webpack-plugin 在 webpack3.x 下只能使用 2.x 版本,以及昨晚的 compression-webpack-plugin 插件在 webpack3.x 下只能使用 1.x 版本,这些东西其实劳心劳力但是又没有什么用处,真心不建议跟 webpack 较劲。

项目优化其实只是一个下策,更重要的是我们平时写代码需要多思考,提升效率和性能。我们写的时候需要注意引入第三方组件以及样式,不是多处用到的组件尽量按需加载,外部样式走 CDN,公用逻辑尽量提出来,不要写完就完事了。就我们目前的商城,移动端组件库就有俩,查看请求时发现有几张图片都是 1.5M(编辑同学上传时没有压缩),首页第一次加载请求有200多个,主要是图片请求,这块如何优化呢?

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

推荐阅读更多精彩内容