Webpack概述
Webpack这样的打包工具进行打包,将vue、jsx、ts等代码转为浏览器认识的代码。Webpack使用的是js代码进行开发,基于Node平台运行。
webpack打包流程:
根据entry生成各个模块之间的依赖关系图,遍历关系图对不同文件进行打包。默认只支持js文件的打包,其他类型文件要使用对应的loader进行打包。
webpack配置有那些?
entry:指定打包的入口文件,单个或多个js文件。 output:指定打包的输出文件、包括path,filename,publicpath等。 module:配置不同的loader处理不同的模块。可以根据文件扩展名或者正则表示来绑定不同类型文件的的解析loader。如: css-loader,style-loader、file-loader、url-loader,babel-loader、eslint-loader。
module.exports = {
module: {
rules: [
{
test: /.txt$/,
use: 'raw-loader'
}
]
}
}
resolve:设置webpack如何解析模块依赖,包括别名,扩展名等。
resolve:{
alias: {
'@': path.resolve(__dirname, 'src')
},
plugins:使用不同的插件增强webpack的功能。如:
- html-webpack-plugin生成html并自动引入打包后的js文件;
- clean-webpack-plugin打包前清除之前生成的文件;
- copy-webpack-plugin拷贝不需要打包的资源。
devSever:web服务器和热重载。可以在这里配置devServer.contentBase、devServer.proxy、devServer.port。
optimization:optimization.splitChunks&optimization.runtimeChunk配置代码拆分和运行时代码提取等优化策略。 设置chunk的具体划分格式条件等。
splitChunks:
module.exports = {
//...
optimization: {
splitChunks: {
//chunks: 表示显示块的范围,有三个可选值:initial(初始块)、async(按需加载块)、all(全部块),默认为all;
chunks: 'async',
minSize: 30000, // 表示在压缩前的最小模块大小,默认是30kb;
minRemainingSize: 0, //
maxSize: 0,
minChunks: 1, // 表示被引用次数,默认为1;
maxAsyncRequests: 6, //所有异步请求不得超过6个
maxInitialRequests: 4, //初始话并行请求不得超过4个
automaticNameDelimiter: '~', //名称分隔符,默认是~
automaticNameMaxLength: 30,
// cacheGroups: 缓存组
cacheGroups: {
common: {
name: 'common', //抽取的chunk的名字
chunks(chunk) { //同外层的参数配置,覆盖外层的chunks,以chunk为维度进行抽取
},
test(module, chunks) { //可以为字符串,正则表达式,函数,以module为维度进行抽取,只要是满足条件的module都会被抽取到该common的chunk中,为函数时第一个参数是遍历到的每一个模块,第二个参数是每一个引用到该模块的chunks数组。自己尝试过程中发现不能提取出css,待进一步验证。
},
priority: 10, //优先级,一个chunk很可能满足多个缓存组,会被抽取到优先级高的缓存组中
minChunks: 2, //最少被几个chunk引用
reuseExistingChunk: true,// 如果该chunk中引用了已经被抽取的chunk,直接引用该chunk,不会重复打包代码
enforce: true // 如果cacheGroup中没有设置minSize,则据此判断是否使用上层的minSize,true:则使用0,false:使用上层minSize
}
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
runtimeChunk:
module.exports = {
//...
optimization: {
runtimeChunk: {
name: entrypoint => `runtime~${entrypoint.name}`
}
}
};
externals:配置排除打包的模块。
devtool:配置source-map类型。
context:webpack使用的根目录,string类型必须是绝对路径。
target:指定webpack编译的目标环境。
performance:输出文件的性能检查配置。
noParse:不用解析和处理的模块。
stats:控制台输出日志控制。
Loader和plugin的区别:
loader本质是一个函数,是一个转换器。把相应的文件进行转换。
plugin是插件,用于增强webpack功能。webpack在运行的生命周期中会广播出许多事件,plguin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。
用法不同:
- loader的配置是在module.rules下进行。类型为object数组。每个obj描述了test(扩展名或者正则表达式的文件),loader(使用什么加载)和options(参数)。
- plugin的配置在plugins下,类型为数组,每一项都是plugin的示例,参数都通过构造函数传入。
##webpack构建流程
- 初始化参数:解析webpack配置参数,合并shell传入webpack.config.js文件配置的参数形成最终的配置结果。
- 开始编译:使用上一步得到的参数初始化compiler对象,注册所有配置的插件,插件监听webpack构建生命周期的事件节点,做出相应的反应,执行对象的run方法开始执行编译。
- 确定入口:从entry入口,开始解析文件构建AST语法树,找出依赖进行递归。
- 编译模块:递归中根据文件类型和loader配置,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到每个依赖的文件都经过了编译处理。
- 完成模块编译:在经过上一步使用loader翻译完所有模块后,得到了每个模块被翻译后的最终内容以及互相的依赖关系。
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,再把每个chunk转换成单独的文件加入到输出列表,这一步是可以修改输出内容的最后机会。
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
这个流程是一个串行的过程,webpack的运行流程是一个串行的过程,它的工作流程就是将各个插件串联起来。在运行过程中会广播事件,插件值需要监听它所关心的事件,就能加入到这条webpack机制中,去改变webpack的运作。
webpack热更新(Hot Module Replacement)
在不刷新页面的前提下,将新代码替换掉旧代码。 HRM的实际原理是webpack-dev-server(WDS)和浏览器之间维护了一个websocket服务。当本地资源发生变化后,webpack会先将打包生成新的模块代码放入内存中,然后WDS向浏览器推送更新,并附带上构建时的hash,让客户端和上一次资源进行对比。
code splitting
optimization.splitChunks中可以配置具体的chunk分割条件。
source map
source map是一种文件,它简历了构建后的代码与原始源代码之间的映射关系。主要用于开发阶段调试代码。在配置文件中的devtool选项中指定devtoll:'source-map'来开启。
Tree Shaking
Tree Shaking是一个利用ES6模块静态结构特性来去除生产花鸟卷下不必要代码的优化过程。原理在于:
- 当webpack分析代码时,它会标记出所有的import和export语句。
- 当webpack确定哪个某个模块没有被导入时候,它会在生成的bundle中排除这个模块的代码。
- 同时,webpack还会进行递归的标记清理,以确保所有未使用的依赖项都不会出现在最终的bundle中。 启动Tree Shaking,需要在配置文件中添加如下配置:
javascriptmodule.exports = {
optimization: {
usedExports: true,
concatenateModules: true,
minimize: true
}
}
确保使用的是ES6模块语法(import和export)Tree Shaking才会发挥作用。
提高webpack的打包速度
- 利用缓存:利用webpack的持久缓存功能,避免重复构建没有变化的代码。
- 使用多进程/多线程构建:thread-loader、happypack等插件可以将构建过程分解为多个进程或线程
- 使用Tree Shaking:配置webpack的Tree Shaking机制,去除未使用的代码,减小生成的文件体积。
- 移除不必要的插件:移除不必要的插件和配置,移除不必要的复杂性和性能开销
减少打包后的代码体积
- 代码分割(Code Splitting):将应用程序的代码划分为多个代码块,按需加载。
- Tree Shaking:配置Tree Shaking机制,去除未使用的代码
- 压缩代码:使用工具如uglifyJS或Terseer来压缩js代码
- 使用生产模式:通过设置mode:'production'来启用优化
- 利用CDN加速:将项目中引用的静态资源路径修改未CDN路劲,减少图片、字体等静态资源的打包。
vite比webpack快在哪里
一、开发模式的差异
在开发环境中,webpack先打包再启动开发服务器,而vite是直接启动服务器再按需编辑依赖文件。
所以当使用webpack时,所有的模块都需要在开发前进行打包,增加了启动时间和构建时间。
vite则采用了不同的策略,它会在请求模块时再进行实时编译,这种按需动态编译的模式极大的缩短了编译时间,尤其是在大型项目中,文件数量众多,vite优势更为明显。
二、对ES Modules的支持 现代浏览器支持ES Modules,会主动发起请求去获取所需文件。Vite充分利用了这一点,将开发环境下的模块文件直接作为浏览器要执行的文件,而不是像webpack那样先打包,再交给浏览器执行。这种方式减少了中间环节,提高了效率。
什么是ES Modules? 通过使用export和import语句,ES Modules允许在浏览器端导入和导出模块。
当使用ES Modules进行开发时,开发者实际上是在构建一个依赖关系图,不同依赖项之间通过导入语句进行关联。
主流浏览器(除IE)均支持ES Modules,并且可以通过在script标签中设置 type='module'来加载模块。默认情况下,模块会延迟加载,执行时机在文档解析之后,触发DOMContentLoaded事件前。
三、底层语言的差异 webpack是基于nodejs构建的,而vite则是基于esbuild进行预构建依赖。esbuild是采用go语言写的go语言是纳秒级别,而nodejs是毫秒级别的。
因此vite在打包速度上相比webpakc有10-100倍的提升。
什么是预构建依赖? 预构建依赖通常指的是在项目启动或构建之前,对项目中所需的依赖项进行预先的处理或构建。这样做的好处在于,当项目实际运行时,可以直接使用这些已经预构建好的依赖,而无需再进行实时的编译或构建,从而提高了应用程序的运行速度和效率。
四、热更新的处理 在webpack中,当一个模块或其依赖的模块内容改变时候,需要重新百衲衣这些模块。
vite中,当某个模块内容改变时,只需要让浏览器重新请求该模块即可,大大的减少了热更新的时间。