侧边栏壁纸
博主头像
前端自习室博主等级

折腾是进步的阶梯

  • 累计撰写 30 篇文章
  • 累计创建 0 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

简易的webpack5配置方法(一):核心配置和模块联邦和优化

lumozx
2022-08-08 / 0 评论 / 0 点赞 / 29 阅读 / 61272 字

注:本文webpack版本为5.74.0

经过vite的学习,我们了解到no-bundle工具正在异军突起,百花齐放,因此webpack的地位也是遇到威胁,同时,经过多年的发展,webpack上手成本非常之高,学习曲线非常陡峭,因此越来越复杂,虽然官方和社区有着不少优质文章,但依然缺少体系化和深度化。

那么,webpack还有学习必要吗?

答案是肯定的,一方面,我们业务正是基于webpack来构建的,我们接触到的社区代码、生态,也有很大部分是基于webpack构建的,因此,我们在维护项目的时候,遇到一些针对性功能,或者构建的疑难杂症,或者是性能优化,常常束手无策,一个小问题,会导致花费大量时间去解决,效率低下。

另一方面,根据State-of-JS 2021 统计数据,在2021年,webpack依然占据着使用排行榜榜首的位置。

也就是说,未来有可能是no-bundle的天下,但目前来看,近几年,webpack依然是主流中的主流。

同时,no-bundle工具目前依然是一个正在发展的阶段,生态并没有webpack一样强大,经过几年的发展,webapck功能覆盖小程序、桌面应用、微前端等场景,作为构建工具的主流,他的逻辑也被借鉴到各个构建工具中。

现在,webpack依然在迭代,同时推出了持久化缓存,懒编译等特性,虽然依然不及no-bundle,但还没有到达被立刻抛弃的程度。

因此,webpack依然值得学习。

webpack配置

webpack的配置项繁多,这么多的配置所带来的的,就是流程上的细化,而这流程,其实也就是绝大部分构建工具的流程,我们简化一下,就是以下的样子。

  • 入口文件:指的是构建工具需要知道从哪开始进行构建,通过这个入口文件,逐步查找到整个项目代码。

  • 模块处理:指的是针对模块转译,在webpack中,是使用loader,把模块转译为AST,然后分析依赖关系,然后进一步递归处理模块,在vite中,就是预构建和vite将业务代码处理成ESM

  • 后处理:所有模块解析完毕,在内存中生成了产物,这个时候需要对总体产物进行处理,比如注入运行时、模块合并与拆包等。

  • 打包成产物:就是将内存中的产物写入文件系统。

因此,webapck的配置项,大体分为两类:

  • 流程类

  • 工具类

流程类

  • 入口文件:

  • entry:定义项目入口。

  • context:项目执行的上下文路径。

  • 模块处理:

  • resolve:用于模块路径的解析,在vite中我们经常遇到的resolveIdonResolve属于这种。

  • module:模块加载规则,这个在vite中我们并没有遇到类似的,因为vite依赖Rollup,已经进行了内置。

  • externals:声明外部资源,webpack解析、打包的时候会忽略到这些资源。

  • 后处理:

  • optimization:控制产物的体积、分包、代码压缩、混淆,在vite那里,我们在类似的build.rollupOptions.output实现了自定义分包。

  • target:用于配置产物的运行环境,不同的环境产物会有所不同。

  • mode:编译模式,与vitemode类似。

  • plugin:插件。

  • 打包成为产物

  • output:配置产物的路径、名称,与vitebuild.rollupOptions.output类似

工具类

webpack并非只有上述配置,还有着开发环境、性能优化等配置。

  • 开发效率:

  • watch:用于监听文件的变化,持续构建。

  • devtool: 用于配置产物的Sourcemap

  • devServer:配置开发服务器。

  • 性能优化类:

  • cache:该选项用于控制如何缓存编译过程和编译结果。

  • performance:配置产物大小的阈值。

  • 日志类

  • stats: 控制编译过程的日志内容,在性能调试的时候使用。

  • infrastructureLogging:日志输出方式。

我们注意到。以上的配置选项,我们或多或少在vite中寻找到类似的痕迹,这并非是webpackvite,而是webpack建立、稳固了这种模式,并通过增加上百项配置,来细化控制各个情况下的表现。

配置详解

webpack的配置除了对象的方式外,还支持数组对象、函数的方式。

  • 对象是一种比较常见的配置方式,逻辑简单,因此适合大部分简单的项目。

  • 数组对象是针对某些特定情况才会使用的配置,但数组项都会创建多个构建实例,这些实例不会进行共享,因此不会复用之间的结果,所以不会带来任何性能上的优化。

  • 函数是根据环境,进行动态调整的配置对象。

这里需要注意的是,如果使用了数组对象作为配置项,同时使用了webpack --config-name="xxx"来进行构建,那么构建逻辑仅仅使用xxx的,而非整个数组。

如果我们遇到数组的情况,可以借助webpack-merge 来简化配置逻辑。这个工具是专门用来合并配置对象的工具,这个工具的逻辑与lodashmerge类似,但也支持更多的特性。

  • 支持数组的合并

  • 支持函数属性的合并

  • 支持合并策略

  • 支持自定义对象合并函数

因此,这个工具不光使用于整个webpack的配置项,还可用于rulesentryoutput的配置。

entry

正常来说,入口文件是多种多样的,而针对入口文件的处理也体现了整个构建工具的特性。

webpack中,入口文件可以是一个字符串,简单粗暴地指定入口文件的路径,也可以是对象。

这里需要注意的是,如果是一个对象,那么这个对象的key就是包名,而value值才是entry作为对象的时候的配置项。

这个配置项可以是一个字符串,也可以是一个对象,对象的配置项比较多。还可以是函数,可以动态生成entry的信息,而返回值也可以是字符串、对象或者数组。当然,也支持数组,数组每一项也可以是上面说的字符串、对象、函数。

对象的的配置最为复杂,包含以下属性。

  • import: 入口文件,如果是数组,说明是多入口。

  • dependOn:前置依赖bundle

  • runtime: 设置该入口的runtime chunk,如果不为空,webpack就会将该入口运行时的代码进行抽离。

  • filename:声明构建产物模块。

  • library:构建NPM Library使用。

  • publicPath:声明入口发布的URL。

  • chunkLoading:异步加载方技术方案,比如jsonp、require、import

  • asyncChunks:声明模块是否异步加载。

这些大概都能理解,不过其中dependOnruntime可能需要花一些时间。

我们来看看设置好dependOn,会发生什么。

我们这里设置了两个入口,其中一个入口设置了dependOn,指向第一个入口文件。

此时 Webpack 认为,项目在加载 foo 产物之前一定会加载 main,因此可以将重复的模块代码、运行时代码等都放到 main 产物,这样会减少不必要的重复,最终打包结果:

我们可以看到,main.js中的代码比较臃肿,是较为平常的bundle,而相比而言,foo.js中的代码较少。

dependOn 适用于那些有明确入口依赖的场景,比如我们构建了一个主框架 Bundle,其中包含了项目基本框架,还需要为每个页面单独构建 Bundle,这些业务代码也都依赖于主框架代码,此时可用 dependOn 属性优化产物内容,减少代码重复。

接下来,我们看一下runtime

为了支持产物在各种环境中运行,webpack会在产物中注入一系列代码,这些代码数量与使用wepack的特性数量相关,有的时候,这些注入代码会超过业务代码,因此,为了产物缓存,我们可以把这些代码抽离出来。

我们把runtime在各个入口设置为同一个名称。

然后进行打包,可以看到,每个入口的代码都相对清爽,而大部分代码都被提取到runtime中。

因此,runtime也是一个常用的性能优化手段。

output

output是输出构建结果,比如产物的位置,文件分包,名字是什么,其中,他还支持多个配置项。

  • path:产物的位置。

  • filename:产物的命名规则,支持[name]、[hash]的占位符。

  • publicPath:文件发布路径。这个会在cdn中使用。

  • clean:是否会清除path目录下的内容。

  • library:构建NPM Library使用。

  • chunkLoading:异步加载方技术方案,比如jsonp、require、import

这里我们注意到,output的配置项与entry大部分是重合的,如果outputentry的配置相同,那么优先级以entry为主。

target

大多数的时候,webpack打包web应用,但实际上还支持Node、electron、NW.js等形态,这个特性主要是target配置控制的。他主要支持以下数值:

  • node[[X].Y]:编译为Node应用,此时将使用Noderequire方法加载其他Chunk,还支持指定版本。

  • async-node[[X].Y]:编译为Node应用,但跟node的区别在于,async-node是以异步进行加载模块的。

  • nwjs[[X],Y]:编译为NW.js应用。

  • electron[[X].Y]-main: 构建Electron主进程。

  • electron[[X].Y]-renderer: 构建Electron渲染进程。

  • electron[[X].Y]-preload: 构建Electron Preload脚本。

  • web:构建web应用

  • esX:构建为特定ECMAScript兼容代码。

  • browserslist:使用browserslist语法。

不同构建目标在打包的时候会出现差异的结果。如下:

可以看到,在web版本跟node版本是不同的,node版本增加了exports相关的兼容。

mode

mode本身使用很简单,但同时也算一个非常关键的配置项,webpack内置了跟多构建优化策略,具体如下:

  • production:默认值,生产模式,使用该值,webpack会开启一系列优化措施,比如树摇、代码压缩,代码分割。

  • development: 开发模式,使用此值,会保留语义化的代码,更助于调试。

  • none: 关闭所有内置优化规则。

模块联邦

在使用vite的时候,我们提到模块联邦是webpack提出来的一个新特性,那么在webpack这里,我们来看一下,他们是如何进行操作的。

模块联邦最常见的应用是微前端。那么我们来稍微了解一下微前端。

在微前端中,会存在一个容器应用,他的任务就是加载各个微应用。

而微应用需要做到以下事情:

  • 提供两个方法,一个是挂载方法,容器应用会调用他来渲染应用,另一个个是卸载方法,用于卸载微应用,他们都以接口的形式提供给容器。

  • 远程入口文件的地址,容器应该在合适的时候动态记载文件,同时获得花在方法后,进行微应用的渲染。

  • 提给微应用的ID,用于标识自己。

  • 路由首地址,此地址决定微应用的视图展示。

而容器需要做到以下事情:

  • 加载远程的微应用,进行渲染。

  • 在合适的时机,卸载微应用。

而模块联邦,让打包的产物,也有了动态加载功能,我们注意到,当一个微应用有了动态加载功能,那么他就可以成为一个容器应用。如果我们确立了只有一个容器应用的结构后,上图会变成这样:

可以看到,使用了模块联邦,每个应用在架构上面是平等的,应用也会进行相互依赖和相互加载。换句话说,每一个都是容器应用,也就是说,在微前端上,并没有进行任何架构上的约束,但我们可以从业务上规划他,来规划处容器应用和微应用的区别,从而让整个架构是一个有价值的、可复用的模块,而不是脱缰的野马。

如何使用

那么,webpack是怎么实现模块联邦的呢?

webpack内置了一个ModuleFederationPlugin 插件,不管是导出模块还是导入模块,都需要使用此插件,针对生成方,插件的expose参数,确定了需要导出的列表,filename参数,定义了导出入口文件,针对模块使用方,插件的remotes参数确定了引入远程模块的位置。

针对导出方,我们可以这么使用

而针对使用方,我们可以这么使用

然后我们就可以在业务文件中使用了。

// 正常引入
import { sayHello } from "RemoteApp/utils"
// 异步引入
const { sayHello } = await import("RemoteApp/utils");

这里需要注意的是,webpack的模块联邦的remotesvite的有些许不同。

webpack需要定义的格式是LocalModuleName: "RemoteModuleName@Host",也就是说,这里并不能单纯填写入口文件,而是需要远程模块的名称,同时确定本地使用的时候的模块名称,在大部分情况下,远程模块和和被使用的时候的名称是相同的,但也不排除名称冲突的情况。

同时,remotes还接受[XXX]格式的占位符,XXX的值会从全局下取。也就是说[XXX] === window.XXX

共享模块

跟vite的模块联邦一样,webpack也支持模块的共享,通过shared字段进行控制。shared接受对象和数组,当是数组的时候,每一项都是包名,而是对象的的时候,key作为包名,而value是详细的配置项。

共享模块通常用来配置常服的基础依赖库,因此在引入的远程模块的产物中,并不会打包shared所指向的基础库的代码内容。而是让他们作为异步模块加载。同时,模块联邦对共享模块做了版本化管理,从防止共享模块的版本不同,而导致不必要的冲突和错误。

同时,如果本地启动项目的时候,使用共享模块,需要把eager置为true,否则会出现以下错误。

Uncaught Error: Shared module is not available for eager consumption

该选项会将模块打入容器文件中,作为同步模块加载并使用。或者在项目对应地方进行修改,使用异步加载共享依赖也可以进行解决。

我们还可以使用requiredVersion,来指定共享模块的版本,当接受字符串的时候,我们可以引入package.json的对应版本,来让我们的依赖保持一致。而如果是布尔值的时候,会根据此值来判断,是否开启模块联邦的版本号自动推断。此配置项限制依赖版本的上线下,因此极为实用。

shared还提供了其他配置:

  • shareScope:任意字符串,如果本地模块和引用模块使用了相同的依赖,但只想针对一定范围内的微应用共享依赖,那么就可以共同配置相同的字符串,从而使相同依赖在更精确的范围内进行共享。

  • version: 依赖包的版本,默认会从对应包的package.jsonversion获取。

  • packageName:包名,当无法从请求中自动确定包名的时候,才需要配置此选项。

  • import: 声明如何导入该模块,实用性不高。

优化性能

持久化缓存

缓存是一门提升性能的技术,不仅仅在前端,在计算机领域也无处不在。我们在vite中遇到的预构建和metafile.json,也是属于缓存的一种,而webpack中,也使用了各种各样的缓存,也就是说,缓存是一种牺牲空间来换取时间、提升效率的方法。

持久化缓存是webpack5提出来的,他能将首次构建的过程和结果数据保存在本地文件系统、或者内存中,在下次执行构建的时候跳过机解析、链接、编译一一些列耗费性能的操作,甚至可以直接复用chunk,迅速构建出最终产物。

而相关的配置项就是cache,他提供了几个相关的配置项,用来配置缓存效果和周期。

  • type:缓存类型,支持 memory filesystem,其中设置为filesystem即可开启持久化缓存。

  • cacheDirectory:缓存文件路径,默认是node_modules/.cache/webpack

  • buildDependencies: 额外的依赖文件,当这些文件内容发生变化时,缓存会完全失效而执行完整的编译构建,类似vite中,pacgeage-lock.json变动导致预构建失效。

  • managedPaths: 受控目录,使用缓存的时候,会进行新旧代码的哈希值和时间戳的对比,在此选项中的目录会跳过对比,直接使用缓存,默认值为 ['./node_modules']

  • profile:是否输出缓存处理过程的详细日志,默认为fasle

  • maxAge:缓存失效时间,默认为5184000000。也就是60天。

那么,这个持久化缓存,所节约的时间,大概有多少呢?

我们请出构建工具的劳模——Three.js来给大家现身说法。

three.js的dev分支上,目前有368个js文件,合计31653行代码。

顺带一提,Three.js使用的Rollup进行构建,因此我们需要把他改造成webpack,同时也增加了babel-loader@babel/preset-env来模拟构建环境。

然后执行webpack的构建,发现时间大概在3500ms左右。

然后,使用cache特性,进行构建,由于是第二次构建cahce才会起到作用,因此我们构建两次,发现第二次时间为360ms

从结果可以看出来,cache的的确确可以减少构建时间,提升十倍左右的性能。

那么,问题来了,cache如何提升这么大的性能呢?

那么我们就需要先从webpack的构建流程说起。

webpack的构建流程

我们前面提到过流程,但那个是webpack的配置的分类,现在这个流程是构建流程,我们知道webpack最核心功能,是通过loader将任意文件转译成为可识别代码,比如css转译为js字符串,图像转译为base64,然后对这些产物进行合并、打包、兼容。

这里需要提到的一点是,webpack是不识别css文件的,他通过css-loadercss转译为js,然后在打包的时候,如果是开发环境就通过style-loader注入到html中,如果是生产环境就使用mini-css-extract-plugin,生成单独的css文件,注入到html文件。

webpack的工作流程分为以下几个阶段:

  • 初始阶段:

  • 初始化参数,从配置文件,配置对象,命令行,获取最终的配置,在类似于vite中的configResolved钩子。

  • 创建编译器对象,以最终配置,创建编译器,如果最终配置是一个数组,那么就有多个编译器对象。

  • 初始化编译环境,包括注入内部插件、注册模块工厂、加载配置插件。

  • 开始编译:根据入口文件,调用compilition.addEntry,把入口文件转换为dependence对象。也就是记录依赖关系,类似vite的记录模块之间的依赖图。

  • 构建阶段:

  • 编译模块,从入口文件开始,使用loader把模块转译为标准js内容,然后再转译为AST,再从中找出依赖模块,然后递归操作,直到所有可到达的模块都经过了处理。

  • 完成模块编译,所有模块都经过处理后,我们也同时得到了每个模块被翻译后的内容,以及他们只见的依赖关系图。

  • 生成阶段:

  • 合并,根据入口和模块之间依赖关系,对每一个模块,进行代码转译,然后分析依赖,合并代码组成一个个chunk

  • 优化,对这些chunk进行优化,比如树摇、压缩等。

  • 写入文件系统,跟vite大多数打包工具一样,得出的chunk一开始都在内存中,写入磁盘才是最后的操作。

我们注意到,以上会有不少可能有性能问题的地方,比如构建阶段,这里涉及到多次文件的读取,也就是IO操作。还有是loader转译,这里涉及到密集的CPU操作,递归也会因为模块的数量较多,而消耗大量资源。在生成阶段,树摇、分包也涉及到AST和算法,因此也会消耗大量的CPU资源。

webpack的持久化缓存功能,就是将以上流程的部分结果,进行缓存,当下次构建的时候,尝试从这些缓存中进行恢复,如果哈希值和时间戳一致,从而跳过初识阶段和构建阶段,直接从生成阶段开始。

这个与vite的预构建类似,但区别在于,持久化缓存是构建完成、第二次构建才会生效,但vite的预构建是在第一次构建的时候,就开始进行了。

并行构建

我们都知道,Node.js是单线程,那么意味着webpack针对资源的解析、转译、合并可能会进行相互阻塞,所以导致CPU利用率极低,因此社区中出现了一些以多线程方式运行webpack,从而提升效率。这些方案核心都很类似,那就是针对某周计算任务创建子进程,然后通过IPC回传给主进程,最后把结果给Webpack

比如Thread-loader就是其中一个方案:

  • Thread-loader 提供了一个loader,只需要放入loader最开头即可。

  • Thread-loader是官方提供,因此维护、迭代稳定。

我们简单配置一下,使用three.js无缓存进行一次构建。

可以看到,使用了并行构建,webpack的构建时间少了30%左右。虽然这只是加载单一资源的场景。

如果想加载多种资源类型,只需要根据不同资源,在loader之前配置这个loader就可以,启动后,他会在加载文件时创建新的进程,在子进程运行之后的loader,执行完毕后再回传给webpack

不过有一点需要注意的是,Thread-loader不能调用emitAsset等接口,这意味着style-loader 这一类加载器无法正常工作,解决方案是将这类组件放置在 thread-loader 之前,如 ['style-loader', 'thread-loader', 'css-loader']

并且loader不能获取compilation、compiler 等实例对象,也无法获取 Webpack 配置。

当然除了这些缺点,他具有较高配置自由度,因为是一个loader,因此我们可以使用options进行配置:

  • workber:子进程总数,默认比cpu 少 1

  • workerParallelJobs: 单个进程并发执行的任务数

  • poolTimeout: 空闲进程关闭时间

  • poolRespawn:是否运行子进程关闭后重新创建子进程,一般情况是fasle

  • workerNodeArgs:启动子进程的额外参数。

虽然并行构建会节约时间,但由于频繁创建、销毁进程,会带来新的性能损耗,因此,Thread-loader提供了warmup接口,用来提前创建工作子进程,需要注意的是,子进程需要预加载对应的loader模块。

lazyCompilation

lazyCompilation是webpack5另一个新特性,用于实现入口或者异步引用模块的按需编译。

这个在vite中自带支持的,也正是这个原因,导致我们在vite中使用异步路由,产生了巨量的chunk,然后进行合包。

但不可否认,异步加载、异步编译是一个非常实用的特性,lazyCompilation正是webpack的解决方案。

我们只需要在配置中,将experiments.lazyCompilation设置为true,即可开启这个特性。

开启后,代码中的异步引用语句、异步导入模块,不会被立即编译,而是等到页面正式请求该模块,才开始构建,因此极大提升了冷启动的速度。

同时lazyCompilation还支持如下参数:

  • backend:设置后端服务器信息,这个一般是默认值即可。

  • entries:设置是否对entry启动进行按需编译。

  • imports:设置是否对异步模块进行按需编译。

  • test:支持正则表达式,用来声明哪些异步模块启用按需编译的新特性。

noParse

我们都了解,webpackloader是进行代码转换,但,有的代码是已经做好转换的,不需要进行二次编译就可以在浏览器进行运行了,因此如果再次转换,反而是一种浪费性能的方式,因此,module.noParse可以用来设置,哪些资源没必要做重复的代码解析、以来分析、转译功能。

这个配置项支持正则、函数、字符串、字符串数组等性能。

配置后,所有匹配到的文件都会跳过前置构建、分析动作,并把内容直接合并到Chunk,提升构建速度,比如vue.runtime.esm.js、lodsah.js

但这里需要注意的是:

  • 由于跳过了AST分析,因此无法在文件中发现可能的语法错误,直到压缩甚至运行的时候才发现问题,所以必须确保所匹配的文件的内容的正确性。

  • 由于跳过了依赖分析过程,所以文件的依赖无法进行分析,所以此文件不能依赖其他模块,否则会出现错误。

  • 并且跳过了内容分析过程,因此无法无法实现树摇,从而减小产物的体积。比如第三方库有通过判断环境,来加载min版本还是unbundle版本,如果使用此配置项,那么将会在chunk中打包进两份一样的代码。

所以使用此配置项,需要慎重考虑以上几点,需要使用的库是否符合,以及带来的性能提升和需要承担的风险是否一致。

设置resolve

同大多数构建工具一样,webpack提供了一套同时兼容 CMD、AMD、ESM 等模块化方案的资源搜索规则,enhanced-resolve,能将各种模块导入语句准确定位到模块对应的物理资源路径:

  • import 'lodash' 这一类引入 NPM 包的语句会被 enhanced-resolve 定位到对应包体文件路径 node_modules/lodash/index.js

  • import './a' 这类不带文件后缀名的语句,则可能被定位到 ./a.js 文件

  • import '@/a' 这类化名路径的引用,则可能被定位到 $PROJECT_ROOT/src/a.js 文件

但这里需要注意的是,如果导入的语句并没有携带后缀,webpack就会遍历resolve.extensions ,尝试在路径追加后缀名,搜索对应物理文件,同时,webpack5resolve.extensions 默认值为 ['.js', '.json', '.wasm'] ,那么如果是一个ts文件,针对extensions进行配置的时候,如果配置不当,可能会引起多次判断才能完成文件搜索。那么我们就需要进行优化了:

  • 修改extensions配置项,减少匹配次数

  • 代码中尽量补齐文件名称

  • 设置 resolve.enforceExtension = true,这种方式属于强制更改项目规则,针对与多人协同项目弊大于利,不推荐。

我们还记得,在import第三方包的规则中,如果当前项目并没有所需资源,会逐层尝试,如果还找不到,就在全局进行搜索,这个兜底逻辑是没有问题的,但在一个良好的业务中,第三方依赖一般出于当前项目中,因此我们可以通过修改resolve.modules配置,关闭逐层搜索功能。

在实际项目中,我们甚至可能遇到使用resolve.mainFiles的情况,这个配置属于自定义文件夹的默认文件名,比如resolve.mainFiles = ['index', 'home']import './a' 请求,会依次测试./a/index ./a/home 文件是否存在,所以这个配置项同resolve.extensions 类似,应该尽可能减少其中的数量。

善用exclude

loader组件一般需要处理对应资源文件,但有些资源文件,经过我们的确认,认为不需要这些loader进行加载,比如node_modules中的文件,因此,我们可以根据开发场景,通过rules中的includeexclude等配置,限定loader的执行范围,通常排除node_modules文件夹。

但是,如果node_modules中,的确有我们需要经过loader的模块怎么办呢?

此时includeexclude还支持类似MongoDB参数风格的值,也就是通过and、not、or等属性组合过滤逻辑。

比如我想把lodash加入loader逻辑中,但还要排除node_modules文件。

使用这种能力,我们可以适当将部分需要转译处理的 NPM 包(例如代码中包含 ES6 语法)纳入 Loader 处理范围中。

TS优化

JavaScript 本身是一门弱类型语言,这在多人协作项目中经常会引起一些不必要的类型错误,影响开发效率。随前端能力与职能范围的不断扩展,前端项目的复杂性与协作难度也在不断上升,TypeScript 所提供的静态类型检查能力也就被越来越多人所采纳。

不过,类型检查涉及 AST 解析、遍历以及其它非常消耗 CPU 的操作,会给工程化流程带来比较大的性能负担,因此我们可以选择关闭 ts-loader 的类型检查功能,将transpileOnly设置为true

但这样子带来了弊端,那就是ts的类型检查就没有用了,因此我们需要用其他方式实现ts的类型检查,而非启动的时候。

vue-clits插件,就依赖了fork-ts-checker-webpack-plugin插件,将类型检查剥离到子进程执行,这样即获得了ts的类型检查能力,有提升了整体编译速度。

小结

我们在这里了解了webpack的核心配置,然后了解到了webpack提出来的模块联邦、持久化缓存、懒构建,然后通过进行合理配置的方式,来优化webpack的打包流程和速度,不过,这并非webpack的全部,甚至,我们到目前为止,仅仅了解到了webpack的皮毛,我们将以这个作为新的基础,来拓展我们学习webpack的眼界,规划学习的路线。

这并非我们的终点,而是我们新的起点。

0
博主关闭了所有页面的评论