• webpack5 (三)


     webpack 高级配置

    其实就是对 webpack 进行优化,让代码在编译/运行时性能更好

    1. 提升开发体验
    2. 提升打包构建速度
    3. 减少代码体积
    4. 优化代码运行性能

    一、提升开发体验

    sourcemap

    在编译打包后所有的 css 和 js 都合并为了一个文件,并多了很多其他代码,如果此时代码运行出错,提示的错误位置很难定位。所以开发者需要更准确的错误提示来进行开发工作。

    SourceMap 源代码映射,是一个用来生成源代码与构建后代码一一映射的文件的方案。

    它会生成一个 xxx.map 文件,里面包含源代码和构建后代码每一行、每一列的映射关系。当构建后代码出错后,会通过 xxx.map 文件,从构建后代码的出错位置找到映射后源代码的出错位置,从而让浏览器提示源代码的错误位置。

    使用:

    Devtool | webpack 中文文档 | webpack 中文文档 | webpack 中文网

    实际开发中只需要关注两种情况:

    • 开发模式:cheap-module-source-map
      • 优点:打包编译速度快,只包含行映射
      • 缺点:没有列映射
    1. module.export = {
    2. //其他省略
    3. mode:'development',
    4. devtool:'cheap-module-source-map'
    5. };
    • 生产模式:source-map
      • 优点:包含行/列映射
      • 缺点:打包编译速度更慢
    1. module.export = {
    2. //其他省略
    3. mode:'production',
    4. devtool:'source-map'
    5. };

    生产模式下一定要关心列的位置,因为生产模式打完完成后只有一行代码,如果不看列位置,也就无法定位错误位置。

    二、提升打包构建速度

    HMR(HotModuleReplacement)热模块替换

    只在开发模式下有效,生产模式中是不存在的。

    开发时如果修改了其中某一个模块的代码,webpack 默认会将所有模块全部重新打包编译,速度比较慢。

    所以需要做到修改某个模块的代码时,就只有这个模块的代码需要重新打包编译,其他的模块不变,这样打包的速度也会更快。

    HMR:在程序运行中,替换、添加或删除模块,而无需重新加载整个页面。

    HotModuleReplacementPlugin | webpack 中文文档 | webpack 中文文档 | webpack 中文网

    css 经过 style-loader 处理已经具备 HMR 的功能了,但是 js 还不行,在实际开发中,一般使用其他 loader 来解决。比如 vue-loader 和 react-hot-loader 。 

    oneOf

    正常文件在进入配置文件时,是会查看所有的loader的,即使 css 文件在第一个 css-loader 已经进行处理后,也会将全部 loader 都查看一遍。

    oneOf 让文件只让一个 loader 进行处理。

    只需要将 rule 下的 loader 都用 oneOf 包裹起来就可以了。这样在执行时,如果第一个 loader 被命中,就不会再查看后续 loader。

    1. module.exports = {
    2. //...
    3. module: {
    4. rules: [
    5. {
    6. test: /\.css$/,
    7. oneOf: [
    8. {
    9. resourceQuery: /inline/, // foo.css?inline
    10. use: 'url-loader',
    11. },
    12. {
    13. resourceQuery: /external/, // foo.css?external
    14. use: 'file-loader',
    15. },
    16. ],
    17. },
    18. ],
    19. },
    20. };

    include/exclude 只针对 js 文件进行处理

    开发时需要使用第三方的库或者插件,所有文件都下载到 node_modules 文件夹中,而这些文件是不需要编译,可以直接使用的。

    所以在对 js 文件进行打包处理时,需要排除 node_modules 文件夹下目录。

    • include 包含,只处理 xxx 文件
    • exclude 排除,除了 xxx 文件以外的其他文件都被处理。

    只是用来处理 js 文件的,样式文件等都不可用

     只能使用一个,不能同时使用 

    cache

    每次打包时 js 文件都要经过 Eslint 检查和 babel 编译,速度比较慢。可以设置缓存之前的 Eslint 检查和 babel 编译结果,这样二次打包时速度就会更快。

    作用就是对 Eslint 检查和 babel 编译结果进行缓存。

    1. // 配置loader
    2. module: {
    3. rules: [
    4. // 处理 js
    5. {
    6. test:/\.jsx?$/,
    7. include:path.resolve(__dirname,'../src'),
    8. loader:'babel-loader',//babel 也需要有一个 babel 的配置文件
    9. options:{
    10. catchDirectory:true,
    11. catchCompression:false,
    12. plugins:[
    13. 'react-refresh/babel',//用来解决react脚手架下 js不能热更新的问题
    14. ]
    15. }
    16. }
    17. ]
    18. },
    19. // 配置plugin
    20. // 处理html
    21. plugin:[
    22. //eslint 插件也是需要有自己的配置文件
    23. new EslintWebpackPlugin({
    24. context:path.resolve(__dirname,'../src'),
    25. exclude:'node_modules',
    26. cache:true,
    27. cacheLocation:path.resolve(__dirname,'../node_modules/.catch/.eslintcache'),
    28. }),
    29. new HtmlWebpackPlugin({
    30. template:path.resolve(__dirname,'../public/index.html'),
    31. }),
    32. new ReactRefreshWebpackPlugin()//用来解决react脚手架下 js不能热更新的问题
    33. ],

     Thead

    当项目越来越大时,打包的速度会越来越慢,想要继续提升打包速度,其实就是要提升 js 的打包速度(因为 js 文件是量最多的),而对 js 文件处理主要是三个工具:eslint、babel、Terser 三个工具,所以需要提升工具的运行速度。

    可以开启多进程同时处理 js 文件,这样速度就会比之前的单进程打包更快。

    ⚠️注意:开启多线程仅需要在特别耗时的操作中使用,因为每个进程启动大概就会有 600 ms的开销。

    启动进程的数量就是 CPU 的核数,获取 cpu 核数:

    // node js 的核心模块,可以直接使用

    const os = require('os');

    // cpu 核数

    const threads = os.cpus().length;

    1. const TerserPlugin = require("terser-webpack-plugin");
    2. module.exports = {
    3. optimization: {
    4. minimize: true,
    5. minimizer: [new TerserPlugin({
    6. parallel: threads,//启用多进程并发运行并设置并发运行次数
    7. })],
    8. },
    9. };

     optimization 选项专门用来放置压缩文件插件的,css、js、图片等压缩插件都可以在里面 new。

    三、减少代码体积

    Tree Shaking

    开发时会定义一些工具函数库,或者引用第三方工具函数库或组件库。

    如果没有经过特殊处理,打包时会引入整个库,但是实际上可能业务中只用到了极小一部分功能,整个库打包进代码体积过大。

    Tree Shaking 是一个术语,描述为移除 js 中没有使用上的代码。依赖于 ES Module。

    webpack 已经默认开启了该功能,无需其他配置。

    babel

    babel-loader | webpack 中文文档 | webpack 中文文档 | webpack 中文网

    babel 为编译的每个文件都插入了辅助代码,使代码体积比较大,babel对一些公共方法使用了非常小的辅助代码,如_extend。默认情况下会被添加到每一个需要它的文件中。可以将这些辅助代码 Babel runtime 作为一个独立的模块,来避免重复引入。

    下面的配置禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。

    1. rules: [
    2. // 'transform-runtime' 插件告诉 Babel
    3. // 要引用 runtime 来代替注入。
    4. {
    5. test: /\.m?js$/,
    6. exclude: /(node_modules|bower_components)/,
    7. use: {
    8. loader: 'babel-loader',
    9. options: {
    10. presets: ['@babel/preset-env'],
    11. plugins: ['@babel/plugin-transform-runtime']
    12. }
    13. }
    14. }
    15. ]

    Image Minimizer

    ImageMinimizerWebpackPlugin | webpack 中文文档 | webpack 中文文档 | webpack 中文网

    开发的项目中如果引用了较多的图片,那么图片体积就会比较大,将来请求的速度比较慢,可以对图片进行压缩,减少图片体积。

    ⚠️注意:如果项目中图片为在线链接,就不用压缩,压缩的前提是必须为本地项目静态图片

    四、优化代码的运行性能

    Code Split

    打包代码时,会将所有的 js 文件打包到一个文件中,体积太大了。如果只渲染首页,就应该只加载首页的 js 文件,其他文件暂时不加载。

    所以需要对打包生成的文件进行代码分割,生成多个 js 文件,渲染哪个页面就只加载某个js文件,这样加载的资源少,速度更快。

    代码分割(Code Split)主要做了两件事:

    1. 分割文件:将打包生成的文件进行分割,生成多个 js 文件。
    2. 按需加载:需要哪个文件就加载哪个文件。​

    多入口

    1. entry: {
    2. //多个入口文件
    3. main: './src/main.js',
    4. app: './src/app.js',
    5. },
    6. output: {
    7. path: path.resolve(__dirname, '../dist'),//设置出口文件路径
    8. filename: 'static/js/[name].[contenthash:10].js',//加入哈希值
    9. chunkFilename: 'static/js/[name].[contenthash:10].chunk.js',//加入哈希值
    10. assetModuleFilename: 'static/media/[hash:10][etx][query]',
    11. clean: true,//将上次打包内容清空
    12. },

    多入口提取公共组件

    在多个入口文件存在着相同的模块时,将相同部分单独抽离出来,变成可复用的文件。在 optimization 模块下的设置 splitChunks 。

    1. optimization: {
    2. //代码分割配置
    3. splitChunks: {
    4. chunks: 'all'
    5. },
    6. }

    按需加载 动态引入

    import 动态导入,会将动态导入的文件进行代码分割,拆分成单独的模块,在需要的时候自动加载。

    单文件 单入口

    如果在进行动态导入时,eslint 不能识别,需要在 eslint 配置文件中进行单独配置,设置 plugins:["import"],用来解决动态导入语法报错。

    统一命名规则

    需要用到的地方:

    output 中入口文件打包输出的文件名 filename,动态加载打包输出的其他文件名 chunkFilename。loader 模块 module 中图片、字体图标等命名 filename。
    plugins 插件中打包样式的命名 filename 。

    1. output: {
    2. path: path.resolve(__dirname, '../dist'),//设置出口文件路径
    3. filename: 'static/js/[name].[contenthash:10].js',//加入哈希值
    4. chunkFilename: 'static/js/[name].[contenthash:10].chunk.js',//加入哈希值
    5. assetModuleFilename: 'static/media/[hash:10][etx][query]',
    6. clean: true,//将上次打包内容清空
    7. },

    Preload/ Prefetch

    前面使用的代码分割,同时会使用动态导入语法来进行按需加载(懒加载)。但是加载速度不够好,比如:

    当用户点击按钮时才加载这个资源,如果资源的体积很大,那么用户会感觉到卡顿。

    如果想在浏览器空闲时间,加载后续需要使用的资源,就用到 preload/prefetch 技术。

    • Preload:通知浏览器立即加载资源。
    • Prefetch:通知浏览器在空闲时间时才开始加载资源。

    共同点:

    • 都只会加载资源,并不执行。
    • 都有缓存。

    不同点:

    • preload 加载的优先级高;prefetch 加载优先级低。
    • preload只能加载当前页面需要使用的资源;prefetch可以加载当前页面资源,也可以加载下一个页面需要使用的资源。

    在当前页面优先级高的资源用 preload 加载,下一个页面需要使用的资源用 prefetch 加载。二者的兼容性都比较差,preload 相对于 prefetch 兼容性好一点,可以去Can I Use 网站查询 API 的兼容性问题。

    Network Cache

    如果 a 文件依赖于 b 文件,而 b 文件进行了修改,那么 a 文件也需要重新打包,为了解决这种问题,需要把文件依赖的哈希值抽成一个 runtime 文件,这样在依赖发生修改时,只有该依赖文件和 runtime 文件的缓存失效,缓存更持久化。

    CoreJs

    解决 js 兼容性问题

    过去使用 babel 对 js 代码进行兼容性处理,其中使用@babel/preset-env 智能预设来处理兼容性问题。它能将 ES6 的一些语法进行编译转换,比如箭头函数,扩展运算符等。但是如果是 async 函数、promise 对象、数组的一些方法等,就没有办法处理。

    所以此时代码中依然存在着 js 兼容性问题,一旦遇到低版本浏览器会直接报错。所以用 core-js 来解决。

    core-js 是专门用来做 ES6 及以上 API 的 polyfill。polyfill 翻译为垫片/补丁,就是用社区上提供的一段代码,让在不兼容某些新特性的浏览器上使用该新特性。​

    方式一:入口文件 main.js 直接引入

    import 'core-js';

    按需加载:import 'core-js/es/promise';(只使用promise),需要使用什么新语法就直接加载哪个包,但此时也会带来新的问题,在代码越写越多时,一旦用到新写法都要进行重新引入。

    方式二: babel 配置文件的智能预设选项

    1. module.exports = {
    2. presets: [
    3. [
    4. '@babel/preset-env',
    5. {
    6. useBuiltins:"usage"//按需加载 自动引入
    7. corejs:3 //corejs版本
    8. }
    9. ]
    10. ],
    11. };

    PWA

     

    ​ 

  • 相关阅读:
    最新版 Cesium(1.99.0) 构建封装开发环境以及遇到问题
    SchedulingConfigurer教程,怎么使用Spring自带的可扩展定时任务调度接口
    SpringBoot整合JavaMail---发送邮件
    ChatGPT 网站合集/NovelAI tag生成器/Novel资源大全
    4-19 算法思路总结
    阿里达摩院(研究型实习生)
    上云网关EasyNTS遇到IP冲突时,如何正确更改IP地址?
    2022 ICPC 杭州站
    STM32两种输入两种输出仿真设计合集
    七个合法学习黑客的网站,让你从萌新变为大佬!
  • 原文地址:https://blog.csdn.net/Mqyyy/article/details/132475452