likes
comments
collection
share

前端这么卷,开发了三年你还不知道webpack的这些优化??

作者站长头像
站长
· 阅读数 13

大家好,我是前端小张同学,本章主讲webpack优化内容第,接下来我会将webpack一步一步深入,并分享给大家。

前端这么卷,开发了三年你还不知道webpack的这些优化?? 上篇文章中我们讲到了,webpack的一些进阶知识,配置常见的插件,根据环境执行不同的配置文件配置代理等等,进阶知识,那我们这篇文章就介绍一下 , webpack的优化篇 ,关于优化我想说的是,泰酷辣。

1:优化篇(构建性能优化 | 网页性能优化)

1.1 知识点目录

  1. webpack中的 treeshakingscoped-hoisting

  2. webpack 将css打包并抽取到单文件中

  3. 使用 CssMinimizerWebpackPlugin 对css进行压缩(优化)

  4. webpack处理css兼容性问题,自动加上前缀( --webkit -moz )等

  5. CodeSpliting 对JS进行代码分割优化

  6. 优化webpack构建性能-noparse

  7. 使用ignorePlugin插件进行构建性能优化

  8. webpack 使用 dllPugindllReference 进行构建优化

  9. 使用boundleAnalysis进行资源包占比分析

  10. 使用 prefetching 优化首屏加载速度

  11. 介绍 prefetchpreload的区别

1.2 步入正文

1:webpack中的 treeshakingscoped-hoisting

1.1 tree Shaking(摇树优化),什么是 treeShaking?

简单理解 : treeShaking 就是webpack打包过程中,将你导入了的包或者函数等等,导入了但未使用的webpack打包时会将这些代码全部删除,以缩小代码体积

webpack 会根据当前的环境,来决定打包完的代码是否需要压缩,当在开发环境下时,代码是不会进行压缩,

如果,你的代码需要进行手动配置 tree shaking ,那你可以在 package.json文件中,添加 side-effect-free 选项,如果打包的代码不包含副作用,那webpack会自动 删除 未使用并export 的代码

1.2: webpack中的 sideEffectsusedExports 的区别

sideEffects 会根据你设置的文件,去进行treeShking,如果你没有副作用文件,那webpack将会将所有的代码进行treeShaking。

useExports ,是依赖于 terser 去检测语句中的副作用,但terser 在每次解析的时候也无法确认哪些代码是需要的还是不需要的,因为js是一门动态语言,如果你想要指定哪些代码被 treeShking,你可以在代码 上添加 /*#__PURE__*/ 进行标志。

举个栗子

count.js

export const add (a , b) {
    return a + b 
}
export const sub (a , b) {
   return a - b 
}

main.js

import { add } from './count.js'
console.log(add(1,2))

上方有两个函数 , 一个是 add 一个是 sub 对于webpack treeShaking机制来说,sub函数 是未被使用的,那webpack打包时,就不会把 sub 函数 进行解析并打包。

1.3 scoped-hoisting 优化

作用: scoped-hoisting 可以在webpack打包构建的时候,进行分析,将一些函数运算结果计算完成后返回给你,以提高性能。

举个栗子

export const add (a , b) {
    return a + b 
}
export const sub (a , b) {
   return a - b 
}
const num1 = add (5 , 3)
const num2 = sub (5 , 3)

对于 webpack来说 这段代码会变成 这个样子,并且add | sub 函数将会消失不见。

  console.log("输出的结果add",5+3),console.log("输出的结果sub",function(e,t){return e-t}(5,3))

2:webpack 将 css 打包并抽取到单文件中

相信跟我学到这里的小伙伴都知道,原来我们是通过 style-loader 进行样式写入,插入到网页中,生成style 标签,今天在这里我们学习另一种方式,将 css 写入文件中

应用场景 : 打包完成后,将我们组件里的样式写入到一个或者多个css文件中,通过 link 链接进行引入 不需要在通过 style-loader 创建style 标签并插入内容的方式。

配置

我们可以借助 MiniCssExtractPlugin 插件,来进行对css进行管理,并且,如果你是将css单独打包到文件中去时,你需要将style.loader 更换为 MiniCssExtractPlugin.loader 这样css才能生效。

注意事项(warring)

对于 css source map 只在 source-map/nosources-source-map/hidden-nosources-source-map/hidden-source-map 值情况下起作用,因为 CSS 仅支持带有 sourceMappingURL 注释的 source map (例如 //# sourceMappingURL=style.css.map)。如果你需要将 devtool 设置为其他值,你可以使用 css-loader 中的 sourceMap: true 来启用提取并生成 CSS 的 source map。

   // 下载插件
   yarn add mini-css-extract-plugin -D 或者 npm i mini-css-extract-plugin --save-dev
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module : {
    rules : [
        {
            test : /\.css$/,
            use : [MiniCssExtractPlugin.loader,'css-loader' , 'postcss-loader'] // 将 style-loader 换为  MiniCssExtractPlugin.loader
        }
    ]
}
plugins : [
    new MiniCssExtractPlugin({
       filename : '[name].css', // 生成的 css 文件名称  [name].css placeholder 语法 能够以根据你的入口生成名称
       chunkFilename : '[name].css' // 将打包完成的css 进行 拆分 ,分为每一个小模块,并生成对应的css文件
    })
]

3:使用 CssMinimizerWebpackPlugin 对css进行压缩(优化)

作用 :压缩css,优化性能;

安装插件

yarn add css-minimizer-webpack-plugin -D  或者 npm i  css-minimizer-webpack-plugin --save-dev

编写配置

const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin')
//在生产上进行配置,在我们平时开发中打包,无需压缩
optimization : {
    minimizer : [new CssMinimizerWebpackPlugin()]
}
// 如果你需要在开发环境下也s进行压缩 ,你需要配置
module.exports = {
       optimization : {
            minimize : true,
           include // 包含哪些进行压缩
           exclude // 哪些不进行压缩
       }
}

注意(warring)

css压缩和js压缩在生产环境下是webpack自带的优化性能的配置选项,如果你手动去配置了css压缩,那将会覆盖原有的默认配置,js压缩也将会失效,你需要配置 terserPlugin对js进行压缩。

在 webpack4以前js压缩采用的是 ugliflyJSPlugin 进行压缩,在webpack5中 准备了一款开箱即用的插件terserPlugin

4:webpack处理css兼容性问题,自动加上前缀( --webkit -moz )等

作用 : 处理一些css新属性,兼容 chrom webkit-moz兼容火狐等等。

安装插件 postcss-loaderautoprefixerpostcss-preset-env

配置 postCss.config.js

// postCss.config,js 在根目录下创建 postCss.config,js 文件 写入 以下配置
module.exports = {
    plugins : [
        require('autoprefixer')
    ]   
    
}

配置 webpack.config.js

// webpack.config.js  中配置
{
   loader : 'postcss-loader',
   options : {
       postcssOptions : {
       plugins : [ ['postcss-preset-env'] ]
        },
  }
}

匹配规则

postcss-loader必须在 less-loader 或者 sass-loader 之前,因为,webpack打包借助 less | sass loader 解析完成后,再对css进行添加前缀。

注意事项(warring)

webpack5之后,如果你想要对css自动添加前缀,你还需要额外在package.json文件中,添加对应的浏览器版本控制,这样才能生效(巨坑)

 "browserslist": [
    "defaults",
    "not ie < 11",
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
  ]

5:使用CodeSpliting 对JS进行代码分割优化

作用 : 将业务代码,根据设置的大小进行文件拆分,不需要一次请求,请求很大的文件。

配置

方式一 : 通过多入口,多出口打包,进行代码分割

module.exports = {
    entry : {
        index : {
            import : './src/index.js',
            dependOn : 'shared', // 指定用提取出来的公共文件
        },
        other : {
            import : './src/other.js',
            dependOn : 'shared', // 指定用提取出来的公共文件
        },
        share : 'lodash' // 将公共的库抽取到一个 shared.bundle.js
    }
    output : {
        filename : '[name].boundle,js'
    }
}

方式二 : 使用 splitChunksPlugin 进行代码拆分

在webpack4之前采用的是 CommonsChunkPlugin,webpack4开始,已经移除了CommonsChunkPlugin,采用optimization.splitChunks `

触发条件

webpack 将根据以下条件自动拆分 chunks:

  • 新的 chunk 可以被共享,或者模块来自于 node_modules 文件夹
  • 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积)
  • 当按需加载 chunks 时,并行请求的最大数量小于或等于 30
  • 当加载初始化页面时,并发请求的最大数量小于或等于 30

当尝试满足最后两个条件时,最好使用较大的 chunks。

配置

module.exports = {
  //...
  optimization: {
    splitChunks: {  // all 全部进行分割 
     // async 对异步导入进行分割
     // initial 静态导入时会进行分割
      chunks: 'all', // 可选的值  all , async , initial
      minSize: 20000, // 模块大于 20 kb才进行拆分
      minRemainingSize: 0, // 在 webpack 5 中引入了 splitChunks.minRemainingSize 选项,通过确保拆
      // 分后剩余的最小 chunk 体积超过限制来避免大小为零的模块。 'development' 模式 中默认为 0。对于
      //其他情况,splitChunks.minRemainingSize 默认为 splitChunks.minSize 的值,因此除需要深度控制
      // 的极少数情况外,不需要手动指定它
      minChunks: 1, // 模块至少被引用一次才被拆分
      maxAsyncRequests: 30, // 最大的异步请求 不能超过30 个 超过30 个 不对代码进行拆分
      maxInitialRequests: 30, // 入口点的最大并行请求数,超过30个不拆分
      enforceSizeThreshold: 50000, // 强制执行拆分的体积阈值和其他限制,就是按照文件多少大小进行拆分
      cacheGroups: { // 缓存组
        defaultVendors: {
          test: /[\/]node_modules[\/]/,
          priority: -10, // 权重
          reuseExistingChunk: true, // 当模块之间相互引用同一个模块时,reuseExistingChunk会单独拆分
         // 出一个模块出来
        },
        default: {
          minChunks: 2,
          priority: -20, //权重等级 优先使用哪个 
          reuseExistingChunk: true,
        },
      },
    },
  },
};

异步导入自动分割(codeSpliting)

当我们在项目中,使用import函数进行异步导入时,webpack打包该文件会自动codeSpliting

const btn = document.querySeletor('button').addEventListener('click', () -> {
  import('lodash').then( (res,rej) => {
    console.log('res' , res);
  })
})

6:优化webpack构建性能 -noparse

webpack中,有些模块未使用第三方包,或者 import , require, define,其他导入机制,我们可以配置no-parse,进行优化提高构建性能

注意(warring)

如果你在模块中,运用了第三方资源或者导入,那我建议你不要编写该配置,否则会出现不可预知的错误。

配置

module.exports = {
  //...
  module: {
    noParse: /jquery|lodash/, // 不对 jqurey或者lodash 进行模块分析
  },
};

7:使用ignorePlugin插件进行构建性能优化

作用:可以将一些三方资源包中的未使用的包忽略,不对它进行构建

类型

webpack 内置插件,忽略匹配到的文件,不进行构建。

配置

const webpack = require('webpack')
module.exports = {
   new webpack.IgnorePlugin({
      contextRegExp :/^\.\/locale$/, // 忽略 moment.js包中的国际化 local
      resourceRegExp : /moment$/,
    })
}

8: 使用 dllPugindllReference 进行构建优化

作用 :将一些重量的框架库(Vue | React),固定的包单独打包到一个文件中去,通过 dllReferencePlugin进行引用。

配置

8.1 :创建 webpack.reactDll.js

const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry : {
    react : [
      'react',
      'react-dom/client'
    ]
  },
  output : {
    filename : '[name].dll.js', // 输出的文件名称
    path : path.resolve(__dirname,'../dist/react/'),//输出 生成的dll文件目录
    library : '[name]_dll' // 暴露全局对象 react_dll
  },
  plugins : [
    new webpack.DllPlugin({ // 
      name : '[name]_dll', // 创建的文件名称
      path : path.resolve(__dirname , '../dist/react/manifest.json'), // 创建引用json文件,并输出到指定目录
    })
  ],
  mode : 'production'// 环境选择
}

8.2:package.json中 添加 build:react script

    "build:react": "webpack --config ./build/webpack.reactDll.js" // 构建上方创建的文件

8.3 : 通过 DllReferencePlugin进行引用,创建的映射JSON文件

  new webpack.DllReferencePlugin({
      manifest : path.resolve(__dirname , '../dist/react/manifest.json'),
  }),

8.4: 通过 add-asset-html-webpack-plugin 将创建好的dll文件自动引入index.html

const AddAssteHtmlWebpackPlugin = require('add-asset-html-webpack-plugin')
plugins : [
    new AddAssteHtmlWebpackPlugin({
        // 指定添加那个文件
        filepath : path.resolve(__dirname , '../dist/react/react.dll.js'),
        // warning 注意 ,在webpack5之后,如果你只提供了文件路径,index.html 引入的 script
        //src 会变成 auto/reactDll.js
        //如果需要解决该问题 , 你需要指定 publicPath
        publicPath :'./react/'
    })
]

9: 使用boundleAnalysis进行打包分析

作用 : 能够快速构建出项目包之间的占比分析,生成可视化的图形界面

配置

下载插件

yarn add webpack-bundle-analyzer -D
const BoundAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
modul.expoets = {
    plugins : [
        new BoundAnalyzerPlugin()
    ]
}

前端这么卷,开发了三年你还不知道webpack的这些优化??

10: 使用 prefetching 优化首屏加载速度

作用:优化首屏加载速度,并提准备好一些资源包。

话术

采用webpack中的魔法注释 , 进行异步加载,它会将一些一些同步加载的文件先加载完成后再去加载一些需要的资源,如果是需要等待 主资源加载完成后,再去加载子资源,那你可以在异步导入的函数中 加上 模块注释并且将 webpackPrefetch: true指定为 true。

const getDate = () => {
  import(/* webpackPrefetch: true  */ 'dayjs').then(res => {
    console.log('dayjs' , res);
  })
}

11 : prefetchpreload的区别

prefetch(预获取):将来某些导航下可能需要的资源

preload(预加载):当前导航下可能需要资源

prefetch : 会在父级的 chunks 加载完成后 再去加载子级导入的chunks

preload : 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。

结束语

好了,最后到这次本章了解的知识就到此结束了,下一张为大家准备手写一个自己的脚手架,如果你觉得有用,请一键三连吧,谢谢。