IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Webpack 性能优化

    雨浣潇湘发表于 2018-11-01 13:55:04
    love 0

    随着项目的发展,项目目录会越来越大,各种库也会越来越多,会直接导致Webpack的构建效率极低,比如下面的例子:

    1
    2
    3
    4
    5
    6
    7
    $  webpack
    Hash: 6aa4a418e100b6563347
    Version: webpack 3.5.5
    Time: 20199ms
    Asset Size Chunks Chunk Names
    index.js 4.27 MB 0 [emitted] index
    + 395 hidden modules

    可以看到,在这个项目中有大量的模块,构建一次的时间长达20秒,这显然是不可接受的。

    慢在哪里

    有很多工具提供了可视化的分析,如Webpack-bundle-analyzer、webpack-chart、 webpack-analyse。
    以Webpack-bundle-analyzer为例,它提供了一个下图所示的图表,展示了引入的所有模块的大小、路径等信息,可以针对性的做出优化。

    Webpack-bundle-analyzer
    Webpack-bundle-analyzer

    使用上也很简单:

    1
    2
    // 全局安装:
    yarn global add webpack-bundle-analyzer
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // webpack.config.js 配置
    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

    plugins: [
    new BundleAnalyzerPlugin({
    analyzerMode: 'server',
    analyzerHost: '127.0.0.1',
    analyzerPort: 8888,
    reportFilename: 'report.html',
    defaultSizes: 'parsed',
    openAnalyzer: true,
    generateStatsFile: false,
    statsFilename: 'stats.json',
    logLevel: 'info'
    })
    ]

    运行webpack命令,会自动在浏览器中打开http://127.0.0.1:8888/页面,展示可视化图表。

    具体优化

    1. 使用DllPlugin拆分模块

    开发过程中,我们经常需要引入大量第三方库,这些库并不需要随时修改或调试,我们可以使用DllPlugin和DllReferencePlugin单独构建它们。
    具体使用如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    const HtmlWebpackPlugin = require('html-webpack-plugin');

    module.exports = {
    entry: {
    vendor: [
    'axios',
    'vue-i18n',
    'vue-router',
    'vuex'
    ]
    },
    output: {
    path: path.resolve(__dirname, '../static/'),
    filename: '[name].dll.js',
    library: '[name]_library'
    },
    plugins: [
    new webpack.DllPlugin({
    path: path.join(__dirname, 'build', '[name]-manifest.json'),
    name: '[name]_library'
    })
    ]
    }

    执行webpack命令,build目录下即可生成 dll.js 文件和对应的 manifest 文件,使用 DLLReferencePlugin 引入:

    1
    2
    3
    4
    5
    6
    plugins: [
    new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: require('./build/vendor-manifest.json')
    })
    ]

    2. 使用 externals 通过CDN引入第三方库

    Webpack提供了 externals 的方式来引入第三方库,我们可以在 HTML 文件中直接使用 script 标签的形式来引入:

    1
    2
    <script src="//cdn.bootcss.com/react.min.js"></script>
    <script src="//cdn.bootcss.com/react-dom.js"></script>

    在 Webpack 中如下配置即可:

    1
    2
    3
    4
    externals: {
    'react': 'React',
    'react-dom': 'ReactDOM'
    }

    和 dll 的打包方式相比,主要有以下几点区别:

    1. 并非所有依赖库都提供了打包好的生产环境的文件,对于这种只能通过npm来引入的库, externals 无能为力。
    2. 部分依赖库中会存在循环依赖的现象,在一些 React 相关的库中尤为明显,使用 externals 处理会造成路径混乱无法识别。
    3. 使用dll的方式打包好的静态文件在生产环境中需要额外处理,同步到build目录中。(可以使用 CopyWebpackPlugin 等插件)。

    3. 使用 happypack 开启多线程构建

    HappyPack可以将原有的 webpack 对 loader 的执行过程,从单一进程的形式扩展为多进程的模式,从而加速代码构建。使用方式如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const HappyPack = require('happypack');
    const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });

    module: {
    loaders: [{
    test: /\.less$/,
    loader: ExtractTextPlugin.extract(
    'style', path.resolve(__dirname, './node_modules', 'happypack/loader') + '?id=less'
    )
    }]
    },
    plugins: [
    new HappyPack({
    id: 'less',
    loaders: ['css!less'],
    threadPool: happyThreadPool,
    cache: true,
    verbose: true
    })
    ]

    更多配置可以参考文档:https://github.com/amireh/happypack

    4. 增强代码压缩工具

    Webpack默认提供的UglifyJS插件速度很慢,可以使用webpack-parallel-uglify-plugin替换。配置如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
    const os = require('os');

    new ParallelUglifyPlugin({
    workerCount: os.cpus().length,
    cacheDir: '.cache/',
    uglifyJS: {
    output: {
    comments: false
    },
    compress: {
    warnings: false,
    drop_debugger: true,
    drop_console: true
    },
    sourceMap: true,
    mangle: true
    }
    })

    5. 缩小文件搜索范围

    Node.js的模块的载入及缓存机制如下:

    1. 载入内置模块
    2. 载入文件模块
    3. 载入文件目录模块
    4. 载入node_modules里的模块
    5. 自动缓存已载入模块

    如果模块名不是路径,也不是内置模块,Node将试图去当前目录的node_modules文件夹里搜索。如果当前目录的node_modules里没有找到,Node会从父目录的node_modules里搜索,这样递归下去直到根目录。

    我们可以对搜索过程进行一些优化,比如可以直接指定node_modules的路径:

    1
    2
    3
    4
    5
    module.exports = {
    resolve: {
    modules: [path.resolve(__dirname, 'node_modules')]
    }
    };

    6. Webpack3 新功能: Scope Hoisting

    在Webpack3.0 版本中,提供了一个新的功能:Scope Hoisting,又译作“作用域提升”。在Webpack2中,打包后的文件里每个模块都会被包装在一个单独的闭包中,这些闭包会导致JS执行速度变慢,Scope Hoisting则可以将所有模块打包进一个大的闭包中。只需在配置文件中添加一个新的插件,就可以让 Webpack 打包出来的代码文件更小、运行的更快:

    1
    2
    3
    4
    5
    module.exports = {
    plugins: [
    new webpack.optimize.ModuleConcatenationPlugin()
    ]
    }

    7. 合理配置 babel-loader

    在balel的官方文档中有一句话:babel-loader is slow!。
    我们在babel的配置中,也要尽量注意这一点。
    比如,使用 /\.js$/来匹配文件的话,可能会将node_modules中的文件一起处理,我们需要使用exclude、include等尽可能准确的来指定需要转换的内容。
    我们还可以开启babel的缓存配置(cacheDirectory)来提升一倍以上的效率,配置如下:

    1
    2
    3
    test: /\.js$/,
    loader: 'babel-loader?cacheDirectory=true',
    exclude: /node_modules/


沪ICP备19023445号-2号
友情链接