Webpack是一个现代的JavaScript应用程序的静态模块打包工具。当webpack处理应用程序时,它会在内部构建一个依赖图,此依赖图对应映射到项目所需的每个模块,然后将所有这些模块打包成一个或多个bundle。Webpack的主要功能包括:
相同点:
不同点:
手写 loader 兼容 小程序迁移代码 实现 rpx 到px-loader
Plugin 在打包后生成当前dev版本号 和小程序版本号,
babel 为每个点击事件 添加日志 记录,(小程序无法实现顶部点击事件的监听)
支持自动识别颜色并转义成变量,新增变量
在Webpack中,Loader和Plugin是两个不同的概念,它们的作用和使用方式也有所不同。
总的来说,Loader主要用于文件的加载和转化,将非JS文件转化为Webpack可处理的模块;而Plugin则用于扩展Webpack的功能,对打包过程进行增强和优化。
Loader和Plugin有什么区别
Loader为专门解决某个子问题(eslint、scss)的加载器
Plugin 服务于整个webpack的生命周期
一文让你弄懂Webpack的Loader、Plugin - 掘金
Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。 因为 Webpack 只认识 JavaScript,所以 Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。
将接收内容转译预处理为 webpack能够识别的js
Plugin 就是插件,基于事件流框架 Tapable,插件可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
1、CleanWebpackPlugin是在项目打包时先将上一次所打包的文件夹进行一次清除后再去打包的一种插件
- * webpack.config.js文件中 */
-
- // 先在头部通过CommonJS方式引入
- const {CleanWebpackPlugin} = require('clean-webpack-plugin');
- // 在plugins数组中写入
- plugins:[
- new CleanWebpackPlugin()
- ]
HtmlWebpackPlugin插件能够在打包后帮助我们生成html文件,因为webpack打包是没有帮我们生成html文件的,所以我们需要这个插件帮助我们生成html文件,同时也可以对html文件进行一些另外的配置
- /* webpack.config.js文件中 */
-
- // 先在头部通过CommonJS方式引入,这里要注意,该插件通过默认暴露的类,所以无需解构
- const HtmlWebpackPlugin = require('html-webpack-plugin');
- // 在plugins数组中写入
- plugins:[
- new HtmlWebpackPlugin()
- ]
-
- // 该插件可额外配置:
- new HtmlWebpackPlugin({
- // template 可指定打包时根据哪个html模板去打包
- template:"./public/index.html",
- // title 可指定打包后的index的标题
- title:"Title"
- })
DefinePlugin是webpack内置的一个插件,它能够定义一些变量,使我们能够在编译后的html文件中通过**<%= 变量 %>**的方式(EJS模块填充数据)使用这些变量。
- /* webpack.config.js文件中 */
-
- const {DefinePlugin} = require('webpack');
-
- plugins:[
- new DefinePlugin({
- // BASE_URL 则是定义的变量
- BASE_URL:"./"
- })
- ]
CopyWebpackPlugin是能够让我们指定某些文件在编译后会被拷贝指编译后的指定目录下
- /* webpack.config.js文件中 */
-
- const CopyWebpackPlugin = require('copy-webpack-plugin')
-
- plugins:[
- new CopyWebpackPlugin({
- // patterns的配置指定哪些文件需要被copy
- // 该数组内是一个个的配置对象,一个对象代表一个copy
- // 一般我们只会用到一个对象,一种copy
- patterns:[
- {
- // from 指定哪个文件夹要被复制
- from:"public",
- // to 指定将要粘贴到哪个文件夹中
- to:"./",
- // globOptions 是哪些文件要被忽略
- globOptions:{
- ignore:[
- // 忽略上级以及上上级的所有文件夹中的所有index.html文件
- "**/index.html"
- ]
- }
- }
- ]
- })
- ]
2、有没有手写过
webpack实战之手写一个简易的loader和plugin - 掘金
loader 实现将css -
高版本、多进程并行压缩、高效压缩 loader 、有些不压缩、有些提前压缩缓存,分包压缩,充分利用缓存
更多优化请参考官网-构建性能
更多 Loader 请参考官网
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
简单说
对源码感兴趣的同学可以移步我的另一篇专栏从源码窥探Webpack4.x原理
source map 是将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试源码就需要 soucre map。
map文件只要不打开开发者工具,浏览器是不会加载的。
线上环境一般有三种处理方案:
注意:避免在生产中使用 inline- 和 eval-,因为它们会增加 bundle 体积大小,并降低整体性能。
原理:轮询判断文件的最后编辑时间是否变化,如果某个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等 aggregateTimeout 后再执行。
发现源码发生变化时,自动重新构建出新的输出文件。
Webpack开启监听模式,有两种方式:
缺点:每次需要手动刷新浏览器
Webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
HMR的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上 WDS 与浏览器之间维护了一个 Websocket,当本地资源发生变化时,WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比。客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该chunk的增量更新。
后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader 和 vue-loader 都是借助这些 API 实现 HMR。
细节请参考Webpack HMR 原理解析
VSCode 中有一个插件 Import Cost 可以帮助我们对引入模块的大小进行实时监测,还可以使用 webpack-bundle-analyzer 生成 bundle 的模块组成图,显示所占体积。
bundlesize 工具包可以进行自动化资源体积监控。
.webpack生态中存在多种计算hash的方式
hash代表每次webpack编译中生成的hash值,所有使用这种方式的文件hash都相同。每次构建都会使webpack计算新的hash。chunkhash基于入口文件及其关联的chunk形成,某个文件的改动只会影响与它有关联的chunk的hash值,不会影响其他文件contenthash根据文件内容创建。当文件内容发生变化时,contenthash发生变化
2.避免相同随机值
文件指纹是打包后输出的文件名的后缀。
JS的文件指纹设置
设置 output 的 filename,用 chunkhash。
- module.exports = {
- entry: {
- app: './scr/app.js',
- search: './src/search.js'
- },
- output: {
- filename: '[name][chunkhash:8].js',
- path:__dirname + '/dist'
- }
- }
CSS的文件指纹设置
设置 MiniCssExtractPlugin 的 filename,使用 contenthash。
- module.exports = {
- entry: {
- app: './scr/app.js',
- search: './src/search.js'
- },
- output: {
- filename: '[name][chunkhash:8].js',
- path:__dirname + '/dist'
- },
- plugins:[
- new MiniCssExtractPlugin({
- filename: `[name][contenthash:8].css`
- })
- ]
- }
图片的文件指纹设置
设置file-loader的name,使用hash。
占位符名称及含义
- const path = require('path');
-
- module.exports = {
- entry: './src/index.js',
- output: {
- filename:'bundle.js',
- path:path.resolve(__dirname, 'dist')
- },
- module:{
- rules:[{
- test:/\.(png|svg|jpg|gif)$/,
- use:[{
- loader:'file-loader',
- options:{
- name:'img/[name][hash:8].[ext]'
- }
- }]
- }]
- }
- }
可以使用 enforce 强制执行 loader 的作用顺序,pre 代表在所有正常 loader 之前执行,post 是所有 loader 之后执行。(inline 官方不推荐使用)
代码分割的本质其实就是在源代码直接上线和打包成唯一脚本main.bundle.js这两种极端方案之间的一种更适合实际场景的中间状态。
阿卡丽:荣耀剑下取,均衡乱中求
「用可接受的服务器性能压力增加来换取更好的用户体验。」
源代码直接上线:虽然过程可控,但是http请求多,性能开销大。
打包成唯一脚本:一把梭完自己爽,服务器压力小,但是页面空白期长,用户体验不好。
(Easy peezy right)
Loader 支持链式调用,所以开发上需要严格遵循“单一职责”,每个 Loader 只负责自己需要负责的事情。
Loader的API 可以去官网查阅