美文网首页
webpack性能优化

webpack性能优化

作者: 风雅欢乐 | 来源:发表于2020-05-08 21:18 被阅读0次

    性能优化, 可以分为三个方面:

    • 构建性能
      主要指开发阶段的构建性能, 降低从打包开始到代码呈现效果所经过的时间
    • 传输性能
      指打包后的js代码传输到浏览器经过的时间. 优化传输性能要考虑到
      1. 总传输量
      2. 文件数量
      3. 浏览器缓存
    • 运行性能
      指js代码在浏览器端的运行速度, 主要取决于如何书写高性能代码

    构建性能

    1. 减少模块解析

    模块解析包括: 抽象语法树分析, 依赖分析, 替换依赖函数等.

    哪些模块不需要解析?

    • 模块中无其他依赖: 一些已经打包好的第三方库, 比如jquery

    如何让webpack不解析某些模块?

    • 配置noParse

    2. 优化loader性能

    • 进一步限制loader的应用范围
      使用exlucdeinclude排除或仅包含需要应用loader的场景(在每个module下, 每个rule配置对象内)
    • 缓存loader结果
      使用cache-loader缓存loader(它必须放在所有loader第一位)
      loader是倒序运行的, cache-loader放第一位却能控制其他loader是否执行, 是因为其实loader运行还包括一个过程, 即pitch. pitch是loader函数的静态属性, 它也是一个函数, 这个函数有一个参数是文件的路径, 如果这个函数返回文件内容, 那么直接交给到配置里它的前一个loader去执行, 如果pitch函数没返回, 那么把路径按照配置的顺序交到下一个loader的pitch函数. 直到最后一个loader, pitch函数都没返回, 才读取文件内容, 然后开始倒序交给一个一个loader处理.
    • 为loader的运行开启多线程
      thread-load可以开启一个线程池, 后续的loader会放到线程池中运行.但是后续的loader不能:
      - 使用webpack api生成文件
      - 使用自定义的plugin api
      - 访问webpack options

    3. 热替换

    热替换并不能降低构建时间, 但是能降低代码改动到效果呈现的时间.

    a) 更改配置

    module.exports = {
        devServer: {
            hot: true
        },
        plugins: [
            // 可选, webpack 4之后如果设置了hot: true, 默认使用这个插件
            new webpack.HotModuleReplacementPlugin()
        ]
    }
    

    b) 更改代码

    if (module.hot) {
        module.hot.accept();
    }
    

    样式热替换: 使用style-loader可以进行热替换. 但是如果用mini-css-extract-plugin, 由于它生成文件是在构建期间, 运行期间无法改动文件, 热替换对它无效

    传输性能

    1. 分包

    什么是分包?

    将一个整体的代码, 分布到不同的打包文件中

    为什么要分包?

    • 减少公共代码, 降低总体积(特别是一些大型的第三方库)
    • 充分利用浏览器缓存

    什么时候要分包?

    • 多个chunk引入了公共模块
    • 公共模块体积较大或较少变动

    如何分包?

    • 手动分包
      步骤:

      • 打包公共模块, 生成manifest.json资源清单
      • 使用公共模块, 此时代码中引入公共模块的时候, 就不再把公共模块代码引入打包

      打包公共模块是另外的过程, 需要单独使用另一个配置文件, 专门配置, 如:

    const webpack = require("webpack");
    const path = require("path");
    
    module.exports = {
        mode: "production",
        entry: {
            jquery: ["jquery"],
            lodash: ["lodash"]
        },
        output: {
            filename: "dll/[name].js",
            // 打包结果暴露的全局变量名
            library: "[name]"
        },
        plugins: [
            new webpack.DllPlugin({
                path: path.resolve(__dirname, "dll", "[name].manifest.json"),
                name: "[name]"
            })
        ]
    }
    

    然后要在源代码的配置文件中:

    • 重新设置clean-webpack-plugin, 避免它清除公共模块
    • 在模板html中手动引入公共模块
    • 使用DllReferencePlugin, 如
    plugins: [
            new CleanWebpackPlugin({
                cleanOnceBeforeBuildPatterns: ["**/*", "!dll", "!dll/*"]
            }),
            new HtmlWebpackPlugin({
                template: "./public/index.html"
            }),
            new webpack.DllReferencePlugin({
                manifest: require("./dll/jquery.manifest.json")
            }),
            new webpack.DllReferencePlugin({
                manifest: require("./dll/lodash.manifest.json")
            }),
        ]
    
    • 自动分包
      自动分包现在由webpack根据分包策略配置自动完成. webpack有默认的分包配置, 能够在绝大多数情况下都适用.
      详细配置见官网, 大致配置如下:
    module.exports = {
        optimization: {
            // 分包策略的配置
            splitChunks: {
                ...
            }
        }
    }
    

    2. 代码压缩

    webpack自动集成了terser(一个代码压缩工具)
    在生产环境下, webpack使用 terser自动完成压缩.

    3. tree shaking

    能够使得单模块体积优化. 可以移除模块之间的无效代码. (某些模块的导出的代码不一定用到, tree shaking用于移除掉不会用到的部分)

    webpack 2开始就支持了tree shaking, 只要是生产环境, tree shaking自动开启.

    原理

    webpack会从入口模块出发寻找依赖关系.

    当解析一个模块时, webpack会根据es6的模块导入语句来判断, 该模块依赖了另一个模块的哪个导出.
    webpack之所以选择es6的模块导入语句, 是因为es6模块有以下特点:

    1. 导入导出语句只能是顶层语句
    2. import的模块名只能是字符串常量
    3. import绑定的常量是不可变的

    这些特征都非常有利于分析出稳定的依赖.

    如果依赖的是一个导出的对象, 由于js语言动态的特性, 为了保证代码正常运行, 它不会移除对象中的任何信息.

    因此, 我们在编写代码的时候, 尽量:

    • 使用export xxx导出, 而不是export default {xxx}导出
    • 使用import {xxx} from "xxx"导入或import * as xxx from "xxx"导入, 而不使用import xxx from "xxx"导入

    依赖分析完毕后, webpack会根据每个模块每个导出是否被使用, 标记其他导出为dead code, 然后交给代码压缩工具移除dead code.

    4. 懒加载

    懒加载指在运行时再动态加载需要的文件.

    直接看示例代码

    const btn = document.querySelector("button");
    btn.onclick = async function () {
        
        // 动态加载
        // import(xxx)是es6的草案, 不是正式标准
        // 此处可以想象为浏览器使用jsonp的方式远程去读取一个js模块
        // 这里被import的内容会被单独打包成一个js
        // import()会返回一个promise, 结果类似于(* as obj)
        const _ = await import(/* webpackChunkName: "lodash */"lodash-es");
        const result = _.chunk([3, 5, 6, 89, 1, 31], 2);
        console.log(result);
    }
    

    5. gzip

    客户端 - -服务器传输之间, 有可能会对传输内容进行压缩. 浏览器发送请求时可以设置Accept-Encoding来告诉服务器支持哪些压缩方式, 服务器就可以返回压缩后的资源文件给客户端.

    webpack可以使用compression-webpack-plugin来对打包内容进行压缩(增加输出压缩后的文件, 而不是只输出压缩后的文件)

    相关文章

      网友评论

          本文标题:webpack性能优化

          本文链接:https://www.haomeiwen.com/subject/uquqnhtx.html