Webpack 2 中一些常见的优化措施

    选择打赏方式

作者:dwqs


Webpack 是一款强大的前端构建工具, 社区对其介绍的相关文章已经很多了, 本文不再赘述. 基于 Webpack 2, 本文是对我在搭建团队前端脚手架的过程中, 搜罗的 Webpack 2 常见的优化措施的一个总结.


如果你还不了解 Webpack 2, 可以先看下 Webpack 2 快速入门(https://github.com/dwqs/blog/issues/46)


1. 分离第三方依赖


在开发环境下, 通常会采取 HMR 模式来提高开发效率. 但一般情况下, 我们只会更改自身的业务文件, 不会去更改第三方的依赖, 但 webpack 在 rebuild 的时候, 依旧会 build 所有的依赖. 因而, 为减少 rebuild 的时间, 我们可以分离第三方依赖, 在项目启动之前, 将其单独打包和引入.


这要借助 DllPlugin 插件.


我们定义一份生成 dll 的配置文件:


## webpacl.dll.config.js

module.exports = {

    entry: {

        vendor: [

            'vue', 'vuex', 'vue-router', 'vuex-router-sync','babel-polyfill', '...'

        ]

    },

    output: {

        path: path.join(__dirname, './public/', 'dist'),

        filename: '[name].dll.js',

        library: '[name]_library'

    },

    plugins: [

        new webpack.DllPlugin({

          path: path.join(__dirname, './public/', 'dist','[name]-manifest.json'),

          name: '[name]_library'

      })

    ]

}


生成 dll 文件之后, 可以根据环境变量在页面的静态文件中引入:



这样, 在每次 rebuild 的时候, webpack 都不会去重新 build vendor, 能极大减少 rebuild 的时间, 提升开发效率.


仅在开发环境下使用


2. 多进程构建


Webpack的构建过程是单进程的, 利用 HappyPack 可让 loader 对文件进行多进程处理, 其原理图如下:



在业务文件依赖越多和复杂的情况下, HappyPack 对 Webpack 构建效率的提升会越明显. 下图是我在项目使用 HappyPack 前后的一张构建时间对比图:



HappyPack 会充分利用系统的资源来提升 Webpack 的构建效率, 所以系统本身的硬件配置会对 HappyPack 的使用有一定的影响.


HappyPack 不限于处理 js 文件, 也可以同时处理 css/vue 等其它类型文件. HappyPack 支持多个实例, 可以创建多个实例来分别处理不同的类型文件:


let HappyPack = require('happypack');

let os = require('os');

let happyThreadPool = HappyPack.ThreadPool({size: os.cpus().length });

 

module.exports = {

 

...

plugins: [

new HappyPack({

id: 'vue',

          threadPool: happyThreadPool,

          cache: true,

          verbose: true,

          loaders: ['vue-loader'],

}),

new HappyPack({

id: 'js',

          threadPool: happyThreadPool,

          cache: true,

          verbose: true,

          loaders: ['babel-loader'],

})

# others

]

...

 

}


此外, HappyPack 同时还利用缓存来使得 rebuild 更快.


开发环境和生产环境下均可使用. 关于其原理分析, 请看 HappyPack 原理解析


3. 提取公共的依赖模块


无论是单页还是多页应用, 在生产环境下, 通常都会利用 CommonsChunkPlugin 插件来提供公共的依赖模块:


new webpack.optimize.CommonsChunkPlugin({

    name: "vendor",

    minChunks: ({resource}) => {

     resource &&

     resource.indexOf('node_modules') &&

     resource.match(/.js$/)

    }

}),


上述的配置会提取 node_modules 下的所有模块, 打包出来的结果可能是这样的:



打包结果分析图由 webpack-bundle-analyzer 提供


这样提取了公共模块之后, 的确会减少业务包的大小, 但是, 这种方式会导致两个问题:


  • 业务越复杂, 三方依赖会越多, vendor 包会越大

  • 没有隔离业务路由组件, 所有的路由都有可能会去加载 vendor, 但并不是所有的路由组件都依赖 node_modules 下的所有模块


所以, 上述提取公共依赖的方式不可取. 我们应该去分析业务依赖和路由, 尽可能将所有路由组件的公共依赖提取出来:


entry: {

    app: path.resolve(__dirname,'../src/page/index.js'),

    vendor: [

        'vue', 'vuex', 'vue-router', 'vuex-router-sync','babel-polyfill',

        'axios', '....'

    ]

},

 

new webpack.optimize.CommonsChunkPlugin({

    name: "vendor",

    filename: "vendor.js"

}),


前后两种方式打包出来的 vendor 大小对比:



既要去提取公共依赖, 也要避免 vendor 包过于太大.


4. 文件分离


文件分离主要是将图片和 CSS 从 js 中分离. 图片和 CSS 都是 Webpack 需要构建的资源, 通过某种配置, 图片可以以 base64 的方式混淆在 js 文件中, 这会增加最终的 bundle 文件的大小. 在 生产环境下, 应该将图片和 CSS 从 js 中分离:


  • 在生产环境下, 通过自定义插件, 将图片的本地引用替换为 CDN 的链接

  • 在生产环境下, 通过 ExtractTextPlugin 来 提取 CSS.


5. 资源混淆和压缩


Webpack提供的 UglifyJS 插件由于采用单线程压缩, 速度比较慢,

可以使用 Parallel 插件进行优化:


let ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');

let os = require('os');

 

new ParallelUglifyPlugin({

    workerCount: os.cpus().length,

    cacheDir: '.cache/',

    uglifyJS: {

        compress: {

            warnings: false,

            drop_debugger: true,

            drop_console: true

        },

        comments: false,

        sourceMap: true,

        mangle: true

    }

})


6. Gzip 压缩


在生产环境下, 如果想进一步减小 bundle 文件的大小, 可以使用 Gzip 压缩.


let CompressionPlugin = require("compression-webpack-plugin");

 

module.exports = {

    plugins: [

        new CompressionPlugin({

            asset: "[path].gz[query]",

            algorithm: "gzip",

            test: /.(js|html)$/,

            threshold: 10240,

            minRatio: 0.8

        })

    ]

}


Gzip 压缩能有效减少 bundle 的文件大小:



部署上线时, 服务端也需要开启 gzip 压缩


7. 按需加载


在单页应用中, 一个应用可能会对应很多路由, 每个路由都会对应一个组件; 如果将这些组件全部全部放进一个 bundle, 会导致最终的 bundle 文件比较大(看上图的 app bundle 文件). 因而, 我们需要利用 Webpack 的 Code Splitting 功能, 将代码进行分割, 实现路由的按需加载.


在 Vue 中, 利用 vue-router 的懒加载功能, 是比较容易实现按需加载的:



当访问首页时, 会去加载 Index 组件, 此时并不会加载 Info 组件; 只有当路由切换为 /info 时, Info 组件才会被加载.


以上是个人的一些总结, 如有不足请指正, 如有遗漏, 欢迎补充.


vue-startup 是基于上述的一些优化措施写的一个 Vue 的脚手架, 欢迎 star.



版权声明:若无特殊注明,本文皆为《 周阿盈 》原创,转载请保留文章出处。
本文链接:Webpack 2 中一些常见的优化措施 https://95o9.com/wljs/84.html
正文到此结束

热门推荐

发表吐槽

你肿么看?

你还可以输入 250 / 250 个字

嘻嘻 大笑 可怜 吃惊 害羞 调皮 鄙视 示爱 大哭 开心 偷笑 嘘 奸笑 委屈 抱抱 愤怒 思考 日了狗 胜利 不高兴 阴险 乖 酷 滑稽

评论信息框

可使用QQ号实时获取昵称+头像

吃奶的力气提交吐槽中...

请勾选我再发表评论!

已有1条吐槽

非瞳博客

2017-07-14 11:40 河北省邯郸市电信
文章写的挺不错的,留言支持下,欢迎回访我的网站
 Windows 7 x64   Google Chrome 45.0.2454.101