首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >webpack高级配置_2023-03-01

webpack高级配置_2023-03-01

原创
作者头像
gogo2027
发布于 2023-03-01 13:10:29
发布于 2023-03-01 13:10:29
94500
代码可运行
举报
运行总次数:0
代码可运行

摇树(tree shaking)

我主要是想说摇树失败的原因(tree shaking 失败的原因),先讲下摇树本身效果

什么是摇树?

举个例子

首先 webpack.config.js配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const webpack = require("webpack");

/**
 * @type {webpack.Configuration}
 */
module.exports = {
  mode: "production"
};

在固定 a.js 用esm导出,b.js用commonjs导出不变动

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// a.js
export function f1() {
  console.log("11111");
}
export function f2() {
  console.log("22222");
}

// b.js
exports.f3 = function () {
  console.log("33333");
};
exports.f4 = function () {
  console.log("44444");
};

例子1:import a.js 和 require b.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// index.js
import { f1 } from "./a";
import { f3 } from "./b";
console.log(f1);
console.log(f3);

打包结果:a.j 和 b.js 都摇树了,只输出了 f1 和 f3。所以导入用import,导出esm和commonjs都可以

image.png
image.png

例子2:import a.js 和 import b.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// index.js
import { f1 } from "./a";
const { f3 } = require("./b");
console.log(f1);
console.log(f3);

打包结果:a.js 摇,b.js 没摇,输出了 f1 、f3、f4。所以导入用require不成功

image.png
image.png

结论:

摇树只能import,导出用esm和commonjs都可以

因为摇树发生在编译阶段,只支持esm的import,不支持commonjs的require,因为esm是编译时,commonjs是运行时

摇树失败的原因

三方面可能导致失败:

1、代码没用import引入

2、webpack配置没开启摇树

3、副作用(sideEffects)

4、babel配置preset-env没写 module:false 参数

代码没用import引入

这一点上面已经说明,必须用 import 导入,导出用 esm 或者 commonjs 都行

webpack配置没开启摇树

开启摇树两步:

1、usedExports设置true,标记无用代码,esm导出的没使用到的导出函数标记为unused harmony export f2,commonjs导出的没使用的导出函数赋值为__webpack_unused_export__

2、terser-webpack-plugin插件做代码压缩去除无用代码,根据一步两种标记,压缩代码会去除

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const webpack = require("webpack");

/**
 * @type {webpack.Configuration}
 */
module.exports = {
  mode: "none",
  optimization:{
    usedExports:true
  }
};
  • mode: production模式下,默认开启摇树,不用做任何配置,由源码看出nonedevelopment不会开启摇树,需要手动加这两步,注意要设置minimize:true,或者放到plugins中

看webpack源码默认配置,参考 前端进阶面试题详细解答

image.png
image.png

副作用(sideEffects)

先来解释下什么是副作用:修改当前作用域之外的行为都叫副作用,比如在函数内部,修改dom,修改全局对象等等

这条主要是针对引入三方包,三方包package.json的sideEffects字段默认true表示有副作用,可以设置为false表示没有副作用,设置为数组列出有副作用的文件

webpack.config.js设置sideEffects:true表示检查三方包的sideEffects字段,webpack在用userExports标记无用代码时,如果判断不出库中代码是否有副作用,就不会标记,则压缩的时候也没法清除,如果判断有副作用,则更不会标记清除

  • mode: production模式下,默认开启摇树,不用做任何配置,usedExports: true
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const webpack = require("webpack");
const TerserPlugin = require("terser-webpack-plugin");

/**
 * @type {webpack.Configuration}
 */
module.exports = {
  mode: "none",
  optimization:{
    sideEffects:true,
    usedExports:true
  },
  plugins:[    new TerserPlugin()  ]
};

babel配置preset-env没写 module:false 参数

在文章 我掌握的Babel配置 中详细讲解了 module: false 参数,简单说不设置false时,只针对babel相关的runtime包的引入会使用require,设置了false引入会使用import,就能让webpack去摇树,回到第一点上

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module.exports = {
  presets: [    [      "@babel/preset-env",      {        modules: false      },    ],
  ]
};

拆包(splitChunks)

splitChunks是webpack配置下optimization下的配置,即优化。看单词理解意思就是拆分多个chunk。

什么是chunk

webpack的本质是把多个js模块合并到一个js中,即一个入口得到一个输出js文件(bundle.js)。

但是导致的问题是,如果这个bundle.js文件很大,那么浏览器请求的时候,导致请求时间很长,首屏长时间白屏。

所以优化手段就是把bundle.js文件拆分成多个小的js文件,同时请求,首屏当然就更快渲染显示。

所以入口文件,chunk文件,输出文件三者的关系从原来的一个入口文件对应一个chunk最后输出一个bundle文件改变为一个入口文件对应多个chunk最后输出多个bundle文件

三种方式获得chunk

  • 1、入口文件可以生成chunk,入口文件即webpack配置的entry选项;
  • 2、异步请求 import函数调用 或者 require.ensure 可以生成chunk; 如:import函数即我们在写vue-router时写的异步请求路由方式,这里webpackChunkName可以魔法定义chunk名,也可不写
代码语言:go
AI代码解释
复制
import(/* webpackChunkName: "AboutPage" */'./view/about.vue')
  • 3、webpack配置splitChunks手动拆分生成chunk,最后独立输出到js文件

splitChunks 配置

简单配置,把react相关包都单独提到一个文件

代码语言:css
AI代码解释
复制
{
   optimization: {
    splitChunks: {
      chunks: "all", // initial、async和all
      cacheGroups: {
        react: {
          name: "react",
          test: /[\\/]react(\w)*[\\/]/i,
          priority: 10
        },
        lodash: {
          name: "lodash",
          test: /[\\/]lodash(\w)*[\\/]/i,
          priority: 20,
          minChunks:3
        },
      },
    },
  },
}

先来看下webpack默认的splitChunks参数

image.png
image.png

看图production非production模式下有参数不一样,下面这些参数表示自动拆包的条件:

  • chunks

重要:拆包的范围,默认async,只针对异步请求的,即上面第二条的import函数调用的chunk里面;initial表示只针对初始化入口entry的;all表示最大包含async + entry

  • cacheGroups

重要:自定义拆包规则,name是chunk名,test正则包名,priority优先级(因为同一个包可能符合多个拆包规则,会处理给优先级高的);看图可知,默认会有两个包规则,defaultVendors规则表示node_modules会拆到一个chunk包,default规则表示只有被两个即以上chunk引用就要拆到一个chunk包

  • minChunks

拆分前必须共享模块的最小 chunks 数,可以不用修改

  • maxAsyncRequests

浏览器发送异步请求时,最大不超过30个请求,即上面第二条的import函数调用,可以不用修改

  • maxInitialRequests

浏览器请求入口entry时,最大不超过30个,可以不用修改

热更新

我们主要是说明热更新的 module.hot.accept()

先来了解一下热更新怎么配置的?

热更新配置

装包

代码语言:css
AI代码解释
复制
npm i -D webpack-dev-server html-webpack-plugin

webpack.config.js

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const webpack = require("webpack");
const HtmlWebpackPlugin = require('html-webpack-plugin');

/**
 * @type {webpack.Configuration}
 */
module.exports = {
  mode: "development",
  devServer: {
    port: 3000,
    open: true,
    hot: true,
  },
  plugins: [    new HtmlWebpackPlugin(),  ]
};

package.json

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
"scripts": {
    "serve": "webpack serve",
},

结论

到此热更新配置完成,正常写代码,但是发现问题了,此时更新页面是整个刷新页面的,并不是局部刷新,怎么回事呢,原来需要在每个文件中最后加上module.hot.accept()才会触发局部更新,accept可以接受两个参数,依赖和回调

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
exports.f3 = function () {
  console.log("33333");
};
exports.f4 = function () {
  console.log("44444");
};
if (module.hot) {
  module.hot.accept();
}

随即产生了另一个疑问,这太麻烦了吧,每个文件文件都需要去加module.hot.accept(),但是我们在实际写下项目的时候怎么没有写这句呢?

原因是不论css、vue、react的loader都帮我们自动加了这句。

css有style-loader,react有react-hot-loader,vue有vue-loader。

对于jsx文件,有vue-jsx-hot-loader

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    test:/\.jsx?$/,
    use:['babel-loader','vue-jsx-hot-loader']
}

按需加载

一段时间以来,我一直把tree shaking和按需加载混为一谈,其实应该分开理解,这里我主要是想说第三方包的按需加载,比如使用element-ui、lodash、vant

tree shaking的前提是使用import导入,但是按需加载并不需要

还有一个点需要注意:如果是我们封装的库,如组件库,导出格式根据文件类型不同,如是js文件可以为 commonjs + es5、esm + es5;如是vue或react文件,esm/commonjs + es6/es5 任意都行,因为我们用babel-loader时会排除node_modules目录不编译,vue-loader等会去编译vue文件

使用babel插件

代码语言:txt
AI代码解释
复制
npm install babel-plugin-component -D

babel.config.js

代码语言:ruby
AI代码解释
复制
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        useBuiltIns: "usage",
        corejs: 2,
        modules: false,
      },
    ],
  ],
  plugins: [
    ["@babel/plugin-transform-runtime"],
    [
      "babel-plugin-import",
      {
        libraryName: "vant",
        libraryDirectory: "es",
        style: true,
      },
      "vant",
    ],
    [
      "babel-plugin-import",
      {
        libraryName: "antd",
        style: true, // or 'css'
      },
    ],
    [
      "babel-plugin-import",
      {
        "libraryName": "lodash",
        "libraryDirectory": "",
        "camel2DashComponentName": false,  // default: true
      },
      "lodash",
    ],
    [
      "babel-plugin-component",
      {
        libraryName: "element-ui",
        styleLibraryName: "theme-chalk",
      },
      "element-ui",
    ],
  ],
};

完毕!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
万能的BeanPostProcessor是如何让spring无限扩展的?
很多朋友一提到spring,首先想到的肯定是IOC(控制反转)和 AOP (面向切面编程),没错,这两个是spring的核心功能。但是什么技术让spring拥有如此强大的扩展能力,
苏三说技术
2020/10/15
2.3K0
万能的BeanPostProcessor是如何让spring无限扩展的?
spring源码解析之ConfigurationClassPostProcessor分析
ConfigurationClassPostProcessor是spring框架中非常重要的一个PostProcessor,尤其是现在的项目基本上都是使用springboot和springcloud,那么更加离不开它。我们先来看看它实现了哪些功能
全栈程序员站长
2022/07/04
4320
spring源码解析之ConfigurationClassPostProcessor分析
Spring IOC 源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器。
黎明大大
2020/12/07
4110
Spring IOC 源码分析
Spring之Bean容器源码理解,Spring4.3.12.RELEASE版本
1、Spring容器创建以及初始化过程。Spring容器的refresh(),是容器的创建以及刷新功能。整个容器就是通过refresh()方法运行完成以后,实现容器创建、以及将所有的bean创建对象、初始化完成。this();是做预处理操作,register(annotatedClasses);是做解析操作。
别先生
2020/01/02
6380
Spring读源码系列07----容器扩展功能--中
在AbstractAutowireCapableBeanFactory#doCreateBean方法中会调用initializeBean方法来初始化bean:
大忽悠爱学习
2022/05/10
7010
Spring读源码系列07----容器扩展功能--中
Spring IOC——源码分析
【3】prepareBeanFactory 对刚创建的bean工厂进行设置,源码分析:
Java架构师必看
2021/05/14
4530
spring源码篇(五)启动过程
前面我们了解了bean的实例化过程,依赖注入,大体上对一个bean的创建有了认知,那么现在从spring启动来看它的一个架构,本来是想把配置类扫描的也加载本章,但两个主题并不相同,就拆开了。
用针戳左手中指指头
2021/09/10
7690
Spring容器的启动流程
因为是基于 java-config 技术分析源码,所以这里的入口是 AnnotationConfigApplicationContext ,如果是使用 xml 分析,那么入口即为 ClassPathXmlApplicationContext ,它们俩的共同特征便是都继承了 AbstractApplicationContext 类,而大名鼎鼎的 refresh()便是在这个类中定义的。我们接着分析 AnnotationConfigApplicationContext 类,源码如下:
全栈程序员站长
2022/06/29
6740
Spring容器的启动流程
IOC容器加载-深度解析(全集)
this();调用AnnotationConfigApplicationContext 的无参构造方法
Java微观世界
2025/01/20
760
IOC容器加载-深度解析(全集)
2.3 spring5源码---spring ioc 加载配置类的源码
看源码的心得: 抓住主脉络, 明确分析的目标对象. 抛开枝枝叶叶, 不要去扣细枝末节, 尤其是spring源码, 因为里面的东西实在是太多太多了 这次分析的对象: spring 如何加载配置类的?
用户7798898
2020/10/10
4400
2.3 spring5源码---spring ioc 加载配置类的源码
Spring官网阅读系列(五):BeanDefinition(下)
在上篇文章中,我们学习了BeanDefinition的一些属性,其中有以下几个属性:
秃顶的Java程序员
2020/03/26
6600
深入理解EnableAutoConfiguration原理
源码分析@EnableAutoConfiguration在SpringBoot中的加载和实例化过程
石奈子
2020/07/06
3.3K0
深入理解EnableAutoConfiguration原理
【07】Spring源码-分析篇-refresh方法
  在obtainFreshBeanFactory方法会完成BeanFactory对象的创建。
用户4919348
2022/09/29
1.2K0
【07】Spring源码-分析篇-refresh方法
2.3 spring5源码系列---内置的后置处理器PostProcess加载源码
本文涉及主题 1. BeanFactoryPostProcessor调用过程源码剖析 2. 配置类的解析过程源码 3. 配置类@Configuration加与不加的区别 4. 重复beanName的覆
用户7798898
2020/11/04
5690
2.3 spring5源码系列---内置的后置处理器PostProcess加载源码
你知道Spring中BeanFactoryPostProcessors是如何执行的吗?
了解了BeanDefinition以及生命周期的大概概念之后,我们可以试着看一下源码!我们上一章也说到,BeanFactoryPostProcessors的执行时机是:在扫描完成之后,实例化之前!那么我们看一下Spring是如何去回调BeanFactoryPostProcessors的呢?
止术
2020/09/15
1K0
你知道Spring中BeanFactoryPostProcessors是如何执行的吗?
Spring启动invokeBeanFactoryPostProcessors方法解释
本方法(invokeBeanFactoryPostProcessors)会实例化和调用所有 BeanFactoryPostProcessor(包括其子类BeanDefinitionRegistryPostProcessor)。
chengcheng222e
2021/11/04
4580
Spring | Bean 的生命周期
Spring Bean的生命周期是Spring面试热点问题。这个问题即考察对Spring的微观了解,又考察对Spring的宏观认识,想要答好并不容易!本文希望能够从源码角度入手,帮助面试者彻底搞定Spring Bean的生命周期。
JavaFish
2019/10/17
1.8K0
Spring | Bean 的生命周期
Spring源码解析(三):bean容器刷新
因为 AnnotationConfigApplicationContext 实现了 ApplicationContext , 而 ApplicationContext 实现了 ApplicationEventPublisher,所以这块可以传入当前 context。
Java微观世界
2025/01/21
1430
Spring源码解析(三):bean容器刷新
Spring BeanFactoryPostProcessor 的作用时机详解
在前面的文章中,松哥已经和大家分享了 BeanPostProcessor 和 BeanFactoryPostProcessor 的基本用法以及差别,但是没有去分析其原理,今天我们就来聊一聊这两个后置处理器的工作原理。
江南一点雨
2023/09/20
6060
Spring BeanFactoryPostProcessor 的作用时机详解
Spring5源码 - 05 invokeBeanFactoryPostProcessors 源码解读_2
接上文 Spring5源码 - 04 invokeBeanFactoryPostProcessors 源码解读_1
小小工匠
2021/08/17
2990
推荐阅读
相关推荐
万能的BeanPostProcessor是如何让spring无限扩展的?
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 摇树(tree shaking)
    • 什么是摇树?
      • 例子1:import a.js 和 require b.js
      • 例子2:import a.js 和 import b.js
    • 摇树失败的原因
      • 代码没用import引入
      • webpack配置没开启摇树
      • 副作用(sideEffects)
      • babel配置preset-env没写 module:false 参数
  • 拆包(splitChunks)
    • 什么是chunk
    • 三种方式获得chunk
      • splitChunks 配置
  • 热更新
    • 热更新配置
    • 结论
  • 按需加载
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档