前端性能优化十九:构建策略

1. 构建策略:

. 主要是webpack做相关处理,主要从时间层面和体积层面优化..- 时间层面:
    a. 目的是减少打包时间
    b. 方案:
       (1). 缩减范围、缓存副本、定向搜索、提前构建、并行构建、可视结构

③.  -  体积层面:
    a. 目的是减少打包体积
    b. 方案:
       (1). 分割代码、摇树优化、动态垫片、按需加载、作用提升、压缩资源

2. 时间层面:

(1). 缩减范围:

. 场景:
    a. 配置include、exclude缩小Loader对文件的搜索范围.. 目的:
    a. 避免不必要的转译.
    b. node_modules目录的体积很大,减少检索所有文件时间成本.
export default {
    // include/exclude通常在各大Loader里配置
    // src目录通常作为源码目录
    module: {
        rules: [{
            exclude: /node_modules/,
            include: /src/,
            test: /\.js$/,
            use: "babel-loader"
        }]
    }
}

(2). 缓存副本:

. 场景:
    a. 配置cache缓存Loader对文件的编译副本

②. 目的:
    a. 再次编译时只编译修改过的文件
    b. 大部分Loader/Plugin都会提供一个可使用编译缓存的选项,通常包含cache字眼

以babel-loader和eslint-webpack-plugin为例:

import EslintPlugin from "eslint-webpack-plugin";

export default {
    // ...
    module: {
        rules: [{
            // ...
            test: /\.js$/,
            use: [{
                loader: "babel-loader",
                options: { cacheDirectory: true }
            }]
        }]
    },
    plugins: [
        new EslintPlugin({ cache: true })
    ]
};

(3). 定向搜索:

. 场景:
    a. 配置resolve提高文件的搜索速度.. 目的:
    a. 定向指定必须文件路径.
    b. 若某些第三方库以常规形式引入可能报错,希望程序自动索引特定类型文件都可通过该方式解决.
    c. 配置项:
       (1). alias映射模块路径
       (2). extensions表明文件后缀
       (3). noParse过滤无依赖文件
export default {
    // ...
    resolve: {
        alias: {
            "#": AbsPath(""), // 根目录快捷方式
            "@": AbsPath("src"), // src目录快捷方式
            swiper: "swiper/js/swiper.min.js"
        }, // 模块导入快捷方式
        extensions: [".js", ".ts", ".jsx", ".tsx", ".json", ".vue"] // import路径时文件可省略后缀名
    }
};

(4). 提前构建:

. 场景:
    a. 将第三方依赖提前打包,配置DllPlugin

②. 目的:
    a.DLL与业务代码完全分离,且每次只构建业务代码
    b. DLL(动态链接库),指一个包含可由多个程序同时使用的代码库:
       (1). 把公共代码打包为DLL文件并存到硬盘,再次打包时,动态链接DLL文件就无需再次打包那些公共代码
    c. webpack v4+已不推荐,因为其版本迭代带来的性能提升足以忽略DllPlugin所带来的效益

③. 配置:
    a. 告知构建脚本哪些依赖做成DLL
    b. 生成DLL文件和DLL映射表文件
import { DefinePlugin, DllPlugin } from "webpack";
export default {
    // ...
    entry: {
        vendor: ["react", "react-dom", "react-router-dom"]
    },
    mode: "production",
    optimization: {
        splitChunks: {
            cacheGroups: {
                vendor: {
                    chunks: "all",
                    name: "vendor",
                    test: /node_modules/
                }
            }
        }
    },
    output: {
        filename: "[name].dll.js", // 输出路径和文件名称
        library: "[name]", // 全局变量名称:其他模块会从此变量上获取里面模块
        path: AbsPath("dist/static") // 输出目录路径
    },
    plugins: [
        new DefinePlugin({
            "process.env.NODE_ENV": JSON.stringify("development") // DLL模式下覆盖生产环境成开发环境(启动第三方依赖调试模式)
        }),
        new DllPlugin({
            name: "[name]", // 全局变量名称:减小搜索范围,与output.library结合使用
            path: AbsPath("dist/static/[name]-manifest.json") // 输出目录路径
        })
    ]
};

在package.json中配置执行脚本且每次构建前首先执行该脚本打包出DLL文件:

{
    "scripts": {
        "dll": "webpack --config webpack.dll.js"
    }
}

链接DLL文件并告知webpack可命中的DLL文件让其自行读取,使用html-webpack-tags-plugin在打包时自动插入DLL文件:

import { DllReferencePlugin } from "webpack";
import HtmlTagsPlugin from "html-webpack-tags-plugin";
export default {
    // ...
    plugins: [
        // ...
        new DllReferencePlugin({
            manifest: AbsPath("dist/static/vendor-manifest.json") // manifest文件路径
        }),
        new HtmlTagsPlugin({
            append: false, // 在生成资源后插入
            publicPath: "/", // 使用公共路径
            tags: ["static/vendor.dll.js"] // 资源路径
        })
    ]
};

也可使用autodll-webpack-plugin代替手动配置.

(5). 并行构建:

. 场景:
    a. 配置Thread将Loader单进程转换为多进程

②. 目的:
    a. 释放CPU多核并发的优势
    b. webpack构建时会有大量文件需解析和处理,构建过程是计算密集型的操作,随着文件增多会使构建过程变得越慢
    c. Node运行webpack是单线程模型,处理的任务需要一件件来处理,不能同一时刻处理多件任务
    d. thread-loader可以根据CPU个数开启线程,能让webpack同一时刻处理多个任务
import Os from "os";
export default {
    // ...
    module: {
        rules: [{
            // ...
            test: /\.js$/,
            use: [{
                loader: "thread-loader",
                options: { workers: Os.cpus().length }
            }, {
                loader: "babel-loader",
                options: { cacheDirectory: true }
            }]
        }]
    }
};

(6). 可视结构:

. 场景:
    a. 配置BundleAnalyzer分析打包文件结构

②. 目的:
    a. 找出导致体积过大的原因,分析原因得出优化方案减少构建时间
    b. webpack官方插件,可直观分析打包文件的模块组成部分、模块体积占比、模块包含关系、模块依赖关系、文件是否重复、压缩体积对比等可视化数据
    c. Node运行webpack是单线程模型,处理的任务需要一件件来处理,不能同一时刻处理多件任务
    d. thread-loader可以根据CPU个数开启线程,能让webpack同一时刻处理多个任务
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer";
export default {
    // ...
    plugins: [
        // ...
        BundleAnalyzerPlugin()
    ]
}

3. 体积层面:

(1). 分割代码:

. 场景:
    a. 分割各个模块代码,提取相同部分代码

②. 目的:
    a. 减少重复代码的出现频率
    b. webpack v4使用splitChunks替代CommonsChunksPlugin实现代码分割
export default {
    // ...
    optimization: {
        runtimeChunk: { name: "manifest" }, // 抽离WebpackRuntime函数
        splitChunks: {
            cacheGroups: {
                common: {
                    minChunks: 2,
                    name: "common",
                    priority: 5,
                    reuseExistingChunk: true, // 重用已存在代码块
                    test: AbsPath("src")
                },
                vendor: {
                    chunks: "initial", // 代码分割类型
                    name: "vendor", // 代码块名称
                    priority: 10, // 优先级
                    test: /node_modules/ // 校验文件正则表达式
                }
            }, // 缓存组
            chunks: "all" // 代码分割类型:all全部模块,async异步模块,initial入口模块
        } // 代码块分割
    }
};

(2). 摇树优化:

. 场景:
    a. 删除项目中未被引用代码

②. 目的:
    a. 好处是移除重复代码和未使用代码
    b. 摇树优化首次出现于rollup(核心概念),后来在webpack v2里借鉴过来使用
    c. 摇树优化只对ESM规范生效,对其它模块规范失效.
    d. 摇树优化针对静态结构分析,只有import/export才能提供静态的导入/导出功能.
    e. 因此在编写业务代码时,必须使用ESM规范才能让摇树优化移除重复代码和未使用代码.

在webpack里只需将打包环境设置成生产环境就能让摇树优化生效,同时业务代码使用ESM规范编写,使用import导入模块,使用export导出模块.

export default {
    // ...
    mode: "production"
};

(3). 动态垫片:

. 场景:
    a. 通过垫片服务根据UA返回当前浏览器代码垫片

②. 目的:
    a. 无需将繁重的代码垫片打包进去
    b. 每次构建都配置@babel/preset-env和core-js,根据某些需求将Polyfill打包进来
    c. @babel/preset-env提供的useBuiltIns可按需导入Polyfill

你可能感兴趣的:(前端,性能优化,spring)