webpack相关-webpack性能优化及webpack5展望(面试相关)

2023-08-26 0 4,763 百度已收录

世界上没有免费的早餐。

前言

波斯有一位国王,他勤政爱民,仁慈又聪明。 在他的领导下,全国人民过上了安居乐业的生活。

但随着国王一天天老去,他开始害怕一件事,那就是他死后,人民将无法再像现在一样过上幸福的生活。 我们应该做什么? 有一天,他召集全省所有有识之士,要求他们找出一条能够保证人民生活幸福的永恒法则。

三个月后,学者们向国王献上三本六寸厚的帛书,并说道:“这三本书,涵盖了天下的知识,只要老百姓能读懂它们,就可以世世代代无忧无虑。” 。 向上”。

国王看着这三本厚厚的书,不以为然,因为他知道人民不会花那么多时间去读那些书。 他让那些学者继续研究。

两个月后,学者们向国王呈上一张纸。 国王读完纸条后,非常满意,说道:“很好,我就带着这句话死去。按照纸条上写的去做,我相信他们一定能够过上幸福健康的生活。”

纸上写的是什么?

以上内容是一个简单的鱼汤故事,与本文内容无关,但蕴含着人生哲理。

今天的话题是关于webpack性能优化,以及webpack5正式版的新更新。

Webpack性能优化

之前在拉勾教育买了一课《webpack原理与实践》。 本课程从独特的角度探讨了webpack的工作原理和运行机制,传达了一种“带着疑问看源码”的思想,明确“复习”而不是“死扣”。

文章提到了生产环境中的一个优化插件DefinePlugin。 DefinePlugin 用于将全局成员注入到我们的代码中。

// ./webpack.config.js
const webpack = require('webpack')
module.exports = {
  // ... 其他配置
  plugins: [
    new webpack.DefinePlugin({
      // 值要求的是一个代码片段
      API_BASE_URL: JSON.stringify('https://api.example.com')
    })
  ]
}

本文将解释一些插件的一些技巧和常见陷阱。

比较有趣的是我问了一个问题:“程序如何识别rc类型文件,webpack如何用.webpackrc.js替换webpack.config.js?”

老师的回复是一个库:“cosmiconfig”这个库会手动搜索配置文件。

除了拉勾教育之外,我还在凯科吧买了一个小课程《Web后端架构师中级高级技能》。 这门课有很多东西。 其中一课是《webpack原理分析》,从源码讲解webpack如何打包编译,自动实现一个简单的webpack。

除了上面这些之外,我还去B站找到了一个比较清晰的“weebpack实用教程”。 本课程来自尚硅谷。 从使用层面出发,讲解了webpack可以解决哪些问题,在项目中如何优化,如何配置。

本文下面的内容大部分是上述课程的笔记。 如果你说的不明白,可以自行查看实战,或者留言说明。

关于webpack的性能优化,我们从两个方面入手:开发环境和生产环境。

开发环境性能优化

在开发环境中,程序员会关注两点,一是实时编译,二是方便调试。

说到热更新,大家就会想到HMR功能。 在webpack配置中开启devServer的hot属性,开启HMR功能。 HMR将大大提高构建速度,并且如果某个模块发生变化,则只会重新打包一个模块而不是所有模块。

与样式文件相比,style-loader内部已经实现了对HMR的支持。

与HTML文件相比,更改不会手动更新(默认不支持HMR),我们需要做一些配置。 (引文html很少变动,一般我们不做处理)

// 修改entry入口,将html文件引入
module.exports = {
   entry: ["./src/index.js""./src/index.html"],

   // ... 其他配置
}

与JS文件相比,每次更改都会全局刷新。 我们想要的效果是每次更改模块时都更新该模块,而无需刷新浏览器。以下示例

//a.js
 function add(x,y){
    console.log("add加载了")
    return x+y;
 }
 export default add

//index.js
import add from "./a.js"

console.log("index.js被加载了")
if(module.hot){
    module.hot.accept("./a.js",function(){
        //监听a.js的变化,一旦发生变化,其他模块不会重新打包构建
        add()
    })
}

使用module.hot判断是否开启HMR功能,使用accept监听模块是否发生变化。

source-map是一种在源代码创建后提供代码映射的技术。 如果创建的代码有问题,可以通过映射追踪源代码的错误位置。

源映射有两种类型:内联和外部。 内联是直接在打包的js内部生成的,内联速度比较快。 external是在外部生成source-map文件。

//[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
//source-map:外部
//错误代码的准确位置

//inline-source-map:内联  只生成一个内联的source-map
//错误代码的准确位置

//hidden-source-map:外部  在外部生成一个source-map文件
//错误代码的错误原因,不能追踪到源代码的错误位置,只能提示构建后的代码错误位置。

//eval-source-map:内联  每一个文件对应的source-map都在eval里
//错误代码的准确位置

//nosource-source-map:外部
//错误代码的准确位置,没有任何源代码信息

//cheap-source-map
//错误代码的准确位置,只精确到行

//cheap-module-source-map
//module会将loader的source-map加入

//开发环境:速度快,调试友好
    //速度 eval>inline>cheap>...
     //eval-cheap-source-map会变得更快

    //调试更友好的是
    //source-map>cheap-module-source-map>cheap-source-map

    //折中化方案(脚手架默认配置方案)
    //eval-source-map

//生产环境:源代码要不要隐藏?
    //内联会让体积非常大,所以一般不考虑内联
    //如果需要隐藏源代码可以考虑
    //nosource-source-map  全部隐藏
    //hidden-source-map   只隐藏源代码

生产环境性能优化

通常,一个文件只能由一个加载器处理。 当一个文件要被多个加载器处理时,需要指定加载器的执行顺序。

oneOf是让一个加载器处理一类文件,这会让加载器执行得更好。 需要注意的是,两个配置不能处理同一个文件,比如eslint-loader,所以我们需要把eslint-loader放在外面。

  module: {
    rules: [
      {
        test/.js$/,
        exclude:/node_modules/,
        enforce:"pre",
        loader:"eslint-loader",
        options:{
          fix:true,
        }
      },
      {
        oneOf: [
          {
            test/.css$/,
            use: ["style-loader""css-loader"],
          },
          {
            test/.(png|jpg|gif|svg)$/,
            use: ["file-loader"],
          },
          //...
        ]
      }
    ]
  }

除了oneOf等优化速度的解决方案之外,我们还可以从缓存入手。 babel-loader 提供了一个 cacheDirectory 属性来帮助我们实现 babel 缓存,并且在第二次创建时会读取之前的缓存。

  module: {
    rules: [
      {
        test/.js$/,
        exclude:/node_modules/,
        loader:"babel-loader",
        options:{
          cacheDirection:true,
        }
      }
      //...
    ]
  }

除了babel缓存之外,还有一种缓存就是文件资源缓存,它是通过hash来实现的。

webpack中有3种hash,每次创建webpack时,都会生成一个唯一的hash值。

//因为css和js同时使用一个hash值
//如果重新打包,会导致所有缓存失效。
output: {
  filename"bundle.[hash:10].js",
  path: path.resolve(__dirname, "dist"),
},
plugins: [
  new MiniCssExtractPlugin({
    filename:"css/main.[hash:10].css"
  }),
  new HtmlWebpackPlugin({
    template"./index.html",
  }),
],

chunkhash是根据chunk生成的hash值。 如果包装来自同一个块,则哈希值将相同。

另一种哈希是contenthash,它根据文件的内容生成哈希值。 不同文件的hash值一定是不同的。

所以我们经常使用contenthash作为文件资源缓存webpack相关,这样可以在代码在线运行时更好的利用缓存。

接下来我们来说说tree-shaking,它是用来消除无用代码的。 它的前提是必须使用ES6模块化并且环境是生产环境。

举个反例,比如,untils.js 中导入的方式有很多种,但我们只使用其中一种。 那么untils.js中其他无用的代码将不会被webpack打包编译。

//untils.js
export const add=()=>{
  //...
}
export const muilt=()=>{
  //...
}

//index.js
import { add } from "../../untils"
//tree-shaking 就是通过import将没用使用过的代码去除掉,比如muilt方法

但是如果代码中引入了css模块呢? css模块也会被tree-shaking处理吗?

这里我们需要在package.json中设置:

//...
//"sideEffects":false,//所有代码都没有副作用(都可以进行tree-shaking)
"sideEffects":["*.css"],//css等文件不会进行tree-shaking。

如果我们的生产环境中需要第三方依赖,并且我们想将它们单独打包,我们该怎么做呢?

这里会用到优化:

//...
optimization:{
  splitChunks:{
     chunk:"all"
  }
}

他会手动分析多入口chunk,看看是否有共同的文件,如果有,就会打包成单独的chunk。

另一种方式是在动态导出的时候添加,这在路由中比较常见。

//...
component:()=>import(/*webpackChunkName: "text"*/ "./../views/text.vue")

import可以动态导出句型,这个文件可以单独打包。

如果我们想要按需加载,即当风暴触发时,就会加载需要的文件,那么如何设置呢?

这就是典型的懒加载,用的时候就加载。 除了延迟加载和预加载之外,看起来效果和延迟加载是一样的,但毕竟已经偷偷加载了,点击的时候就会从显存中获取。

//如果把import放在头部是正常加载,也是并行加载。
//把import语法放在异步的回掉中处理。
document.getElementById("btn").onclick=function(){
   //懒加载
   import(/*webpackChunkName:'test'*/"./test").then(
     (res)=>{
     //....
     }
   )
   //预加载:会提前加载(空闲的时候偷偷加载,不是并行加载)
   //等其他资源加载完毕的时候,偷偷加载,兼容行不是很好
   import(/*webpackPreFetch:true */"./test").then(
     (res)=>{
     //....
     }
   )
}

如果项目比较大,js文件较多,另外一个优化打包率的方案就是多进程打包,通过thread-loader来实现。 比如babel-loader需要编译转换大量的js。

{
  test/.js$/,
  exclude/node_modules/,
  use: [
    {
      //多进程打包
      loader: "thread-loader",
      options: {
        workers2,
      },
    },
    {
      loader"babel-loader",
      options: {
        cacheDirection:true,
      }
    }
  ]
}

在一些项目中webpack相关,我们可能会通过CDN引入一些第三方库或者资源。 我们不希望 webpack 在编译时将它们打包。 如何设置它们?

只有这个时候才会用到externals,它的主要作用是排除运行和编译时的外部依赖。 官网给出了一个非常恰当的反例:

//index.html
<script
  src="https://code.jquery.com/jquery-3.1.0.js"
  integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  crossorigin="anonymous">
</script>

//webpack.config.js
module.exports = {
  //...
  externals: {
    jquery'jQuery'
  }
};

//打包编译后仍然工作
import $ from 'jquery';

$('.my-element').animate(/* ... */);

外部根本不需要打包,但是如果我们不想使用CDN,想在本地打包,又不想重复打包,怎么办?

这里用到了dll技术,需要分为几个步骤。 第一步:将第三方库单独打包到node_modules中。 首先,我们创建一个webpack.dll.js(名称可以任意,这里我们使用dll)文件。

//webpack.dll.js
const path = require("path");
const webpack = require("webpack");

module.exports = {
  mode"production",
  entry: {
    //打包输出的名字为键,值中的jquery为要打包的库
    jquery: ["jquery"],
  },
  output: {
    path: path.resolve(__dirname, "dll"),
    filename"[name].js",
    //打包的库里面向外暴露出去的内容叫什么
    library: "[name]_[hash]",
  },
  plugins: [
    new webpack.DllPlugin({
      //映射库暴露的内容名字
      name: "[name]_[hash]",
      path: path.resolve(__dirname, "dll/manifest.json"),
    }),
  ],
};

通过 webpack --config webpack.dll.js 命令打包后,会生成一个dll文件目录,该目录下有两个文件,一个是jquery,另一个是manifest.json映射文件。

第二步是告诉webpack哪些库不需要打包,并将之前打包的dll库映射到项目中(使用时名称会改变)。

第三步,借助add-asset-html-webpack-plugin插件将其引入到html中。

//webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin");

module.exports = {
  mode"production",
  entry"./src/index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename"bundle.js",
  },
  plugins: [
    new HtmlWebpackPlugin({
      title"Hello",
      template"src/index.html",
      // minify: {
      //   //移除空格
      //   collapseWhitespace: true,
      //   //移除注释
      //   removeComments: true,
      // },
      // filename: "html/admin.html",
    }),
    //告诉webpack哪些库不需要打包,同时使用时名称也的变。
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, "dll/manifest.json"),
    }),
    //将某个文件打包输出去,并在html中自动引入该资源
    new AddAssetHtmlWebpackPlugin({
      filepath: path.resolve(__dirname, "dll/jquery.js"),
    }),
  ],
}

输出HTML:



<body>
  <div id="title">Hello World</div>
  <p>say you hello-</p>
  <script src="jquery.js"></script>
  <script src="bundle.js"></script>
</body>

性能优化总结

上述性能优化从开发环境和生产环境两个方面入手。

主要优化点是代码重构率、代码调试性能、代码运行效率等。

webpack5展望

webpack 5 尚未发布,但提供了一个实验版本。 webpack5的github的文档地址是:

文章顶部的原文链接是github地址,可以点击查看webpack5的详细文档。

我们还可以下载 webpack5 的实验版本并尝试新的更新。

npm i webpack@next webpack-cli -D

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 webpack webpack相关-webpack性能优化及webpack5展望(面试相关) https://www.wkzy.net/game/164560.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务