Webpack相关
1.前端构建(打包)工具有哪些?区别?
grunt、gulp为前端构建工具
主要讲webpack和gulp的区别
webpack专注于实现模块化,高效地管理和维护项目中的资源。而gulp强调的是自动化构建流程,自定义配置一系列任务按照顺序执行,后端开发的思维。webpack适合单页面开发,将资源模块化打包,适配各种模块系统,减少请求资源的数量、程序等待时间。gulp适合多页面应用开发,通用,更多适用于轻量化中。webpack使用起来是基于入口文件的,根据入口自动解析需要加载的资源文件,然后使用loader来处理不同的文件,使用plugin来扩展功能;gulp是基于任务流的,一整个链式操作构成一个任务。webpack使用起来相对复杂,但是功能强大,众多的plugins资源可供webpage拓展;gulp相对简单一点,只需要定义一些任务让他们顺序执行就好。
Webpack、vite、Rollup、Parcel、Snowpack为前端打包工具
webpack适用于大型复杂的前端项目的打包构建,plugins和loader实现强大的功能。rollup适用于基础库的打包,如vue、react,和webpack类似,但是更轻量化;但是加载其他资源时或者实现其他功能需要引入各种模块、插件;不支持HMR,开发效率低;只打包js库时可用他。parcel适用于简单的实验性项目,他可以满足低门槛的快速看到效果Vite是直接开启一个服务器,没有webpack那样的打包过程,所以启动很快;HMR效率高;不够成熟。
2.webpack的loader和plugins区别?常见的loader和plugins?
webpack本身只能打包commonjs规范的js的文件,因此引入Loader,Loader可以加载不同格式的资源文件,并对这些文件进行一些处理包括编译、压缩等,如将scss转换为css,或者typescript转化为js,loader运行在Nodejs中,仅仅为了打包各种资源文件。
如:
file-loader:文件加载
url-loader:文件加载,可以设置阈值,小于时把文件base64编码
image-loader:加载并压缩图片
babel-loader:ES6+转成ES5
ts-loader:将ts转成js
css-loader:处理@import和url这样的外部资源
style-loader:在head创建style标签把样式插入s
eslint-loader:进行代码eslint检查
cache-loader:性能开销大的loader前添加,将结果缓存到磁盘
等
Plugin目的在于解决loader无法实现的其他事,也是对webpack的功能进行拓展,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理构建过程中各种各样的任务。
如:
ignore-plugin:忽略文件terser-webpack-plugin:支持压缩ES6(webpack4)webpack-arallel-uglify-plugin: 多进程执行代码压缩,提升构建速度mini-css-extract-plugin: 分离样式文件,css提取为独立文件,支持按需加载serviceworker-webpack-plugin: 为网页应用增加离线缓存功能clean-webpack-plugin: 目录清理- 为什么不用vite
ProviderPlugin: 自动加载模块,代替require和import html-webpack-plugin可以根据模板自动生成html代码,并自动引用css和js文件extract-text-webpack-plugin将js文件中引用的样式单独抽离成css文件compression-webpack-plugin生产环境可以采用gzip压缩js和csshappypack: 通过多进程模式,来加速代码构建
Loader运行在打包文件之前;
Plugins在整个编译周期都起作用
3.如何自己写一个loader或者plugin?
loader就是对源码进行编译转换,如ES6编译为ES5,一个loader就是一个function,每一个function接收一个源码作为source参数,进行一些处理后返回。
实现一个简单的loader:删除调试代码阶段写的一些console.log()代码
// 目录结构
loader-demo
├─index.js
└package.json
-
npm init之后在package.json同目录下新建一个js文件,内容为:// index.js内容 module.exports = function(source) { console.log('----------- loader -----------'); return source.replace(/console.log(.*?)/, ''); } -
然后要使用这个
loader的话需要先发布该loader;- 通过
npm publish命令将loader-demo发布到npm仓库;这样就可以在另外一个工程使用了; - 为了方便测试,也可以使用
npm link命令,进入loader-demo目录,执行npm link, - 在一个工程项目中执行
npm link loader-demo
- 通过
-
在配置文件中添加规则使用
loader-demo// webpack.config.js内容 const path = require('path'); module.exports = { entry: './src/index', output: { path: path.resolve(__dirname, 'dist'), filename: 'app.bundle.js' }, module: { rules: [ { test: /.js$/, use: 'loader-demo' } ] } }
实现一个简单的plugin:在打包后的每个js文件头部加入版权信息。
// 目录结构
yo-plugin-demo
├─index.js
└package.json
-
npm init之后在package.json同目录下新建一个js文件,内容为:// index.js内容 module.exports = class YoPloginDemo { // 构造器,传入版权信息 constructor(crInfo) { this.crInfo = crInfo; } // 必须,webpack运行时调用 apply(compiler) { // webpack生命周期,生成资源到output目录之前执行 // 具体可以查看 https://www.webpackjs.com/api/compiler-hooks compiler.hooks.emit.tap('YoPluginDemo', compilation => { console.log('----------- plugin -----------'); // 遍历所有资源,以js结尾的文件,在头部加上版权信息 for(const fileName in compilation.assets) { if(/.js$/.test(fileName)) { const asset = compilation.assets[fileName]; asset.source = () => { return `/** Copyright © ${this.crInfo} */\r\n${asset._value}` } } } }); } } -
然后要使用这个
plugin的话需要先发布该plugin;- 通过
npm publish命令将yo-plugin-demo发布到npm仓库;这样就可以在另外一个工程使用了; - 为了方便测试,也可以使用
npm link命令,进入yo-plugin-demo目录,执行npm link, - 在一个工程项目中执行
npm link yo-plugin-demo
- 通过
-
在配置文件中
// webpack.config.js内容
const path = require('path');
const YoPluginDemo = require('yo-plugin-demo');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'app.bundle.js'
},
module: {
rules: [
{
test: /.js$/,
use: 'loader-demo'
}
]
},
plugins: [
new HtmlWebpackPlugin(),
new YoPluginDemo('卢本伟牛逼出品'),
]
}
4.webpack的构建流程?
随着前端开发越来越复杂,规模越来越大,开始走向了工程化的独立开发,日常写的代码包括.js.html.css.json.png.scss等需要我们进行打包合并到一个文件里面,简单来说就是把这些文件压缩、打包等一系列操作自动化,包括分析项目结构、配合loader处理ES6语法等特殊资源的加载和解析、通过plugin实现自动化操作。
整个过程包括两方面:
- 通过 Loader 处理特殊类型资源的加载,例如加载样式、图片;
- 通过 Plugin 实现各种自动化的构建任务,例如自动压缩、自动发布。
具体的流程:
- 初始化:从配置文件开始读取和合并参数,对参数进行整合,得到整合后的参数
- 开始编译:用上一步整合后的参数,初始化Complier对象,加载所有配置的插件(调用插件中的apply方法),通过
complie.run开始执行编译 - 确定入口:根据配置文件的entry找到入口文件
- 编译模块:从入口文件出发,调用配置的loader,对模块进行转换。具体的操作是根据入口模块开始依次递归找出所有依赖,形成依赖关系树,然后将递归结果交给各自的loader进行编译处理,得到chunk。
- 输出:模块转换完成后得到一个个的chunk代码块,并转换成文件输出。
5.用webpack来优化前端性能?
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
- 压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用
webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件 - 利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用
webpack对于output参数和各loader的publicPath参数来修改资源路径 - 删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动
webpack时追加参数optimize-minimize来实现 - 提取公共代码
- 分离代码,按需加载或者并行加载分离出来的
bundle文件
6.如何提高webpack的构建速度?
npm run serve/build时速度慢,项目越来越复杂。
主要可以从优化搜索时间、缩小文件搜索范围、减少不必要的编译等方面入手
- 优化 loader 配置
可以通过配置include、exclude、test属性来匹配文件
- 合理使用
resolve.extensions
module.exports = {
...
extensions:[".warm",".mjs",".js",".json"]
}
配置的时候,则不要随便把所有后缀都写在里面,这会调用多次文件的查找,这样就会减慢打包速度
- 优化
resolve.modules
resolve.modules 用于配置 webpack 去哪些目录下寻找第三方模块,可以使用绝对路径把第三方模块放在项目根目录。
- 优化
resolve.alias
resolve:{
alias:{
"@":path.resolve(__dirname,'./src')
}
}
alias给常用路径起一个别名,如根目录用@表示
- 使用
DLLPlugin插件
把那些不常用的代码抽成一个共享的库,再需要的时候直接引入即可。
- 使用
cache-loader
在一些性能开销较大的 loader之前添加 cache-loader,以将结果缓存到磁盘里,显著提升二次构建速度
保存和读取这些缓存文件会有一些时间开销,所以请只对性能开销较大的 loader 使用此loader
module: {
rules: [
{
test: /.ext$/,
use: ['cache-loader', ...loaders],
include: path.resolve('src'),
},
],
},
terser启动多线程
optimization: {
minimizer: [
new TerserPlugin({
parallel: true,
}),
],
},
- 合理使用
sourceMap
7.webpack的热更新是如何做到的?原理是什么?
HMR全称 Hot Module Replacement,热模块更新/替换,指在应用程序运行过程中,替换、添加、删除模块,而无需重新刷新整个应用。
Webpack Compile:将 JS 源代码编译成bundle.jsHMR Server:用来将热更新的文件输出给HMR RuntimeBundle Server:静态资源文件服务器,提供文件访问路径HMR Runtime:socket服务器,会被注入到浏览器,更新文件的变化bundle.js:构建输出的文件- 在
HMR Runtime和HMR Server之间建立websocket,即图上4号线,用于实时更新文件变化
上面图中,可以分成两个阶段:
- 启动阶段为上图 1 - 2 - A - B
在编写未经过webpack打包的源代码后,Webpack Compile 将源代码和 HMR Runtime 一起编译成 bundle文件,传输给Bundle Server 静态资源服务器
-
更新阶段为上图 1 - 2 - 3 - 4
-
当某一个文件或者模块发生变化时,
webpack监听到文件变化对文件重新编译打包,编译生成唯一的hash值,这个hash值用来作为下一次热更新的标识 根据变化的内容生成两个补丁文件:manifest(包含了hash和chundId,用来说明变化的内容)和chunk.js模块 -
由于
socket服务器在HMR Runtime和HMR Server之间建立websocket链接,当文件发生改动的时候,服务端会向浏览器推送一条消息,消息包含文件改动后生成的hash值 -
在浏览器接受到这条消息之前,浏览器已经在上一次
socket消息中已经记住了此时的hash标识,这时候我们会创建一个ajax去服务端请求获取到变化内容的manifest文件mainfest文件包含重新build生成的hash值,以及变化的模块 -
浏览器根据
manifest文件获取模块变化的内容,从而触发render流程,实现局部模块更新
-
8.常用的库有哪些?作用?
"cross-env": "^7.0.3", 设置跨操作系统的环境变量
"css-loader": "^6.7.1", 解析css文件
"scss-loader": "^0.0.1", 解析scss文件
"style-loader": "^3.3.1", 将解析的css文件打包到bundle.js文件中
"html-webpack-plugin": "^5.5.0", 输入一个html模板文件,然后生成一个自动引入打包后生成的css文件的html文件
"mini-css-extract-plugin": "^2.6.1", 该插件将单独把css文件提取出来,用于生产环境中
"webpack-merge": "^5.8.0", 将开发环境和生产环境分开后,分别在两个配置文件里用于合并公共配置文件,类似Object.assign({},'a')
"source-map": "^0.7.4", 可以通过开发者工具看到源码,方便调试,主要用于开发环境,生产环境非要用就用hidden-source-map这个类型或者白名单策略
基础:
"webpack": "^5.74.0", 核心包
"webpack-cli": "^4.10.0", 命令行工具包
"webpack-dev-server": "^4.9.3", 开启一个本地服务器访问打包代码,处理网络请求,不用每次都重新打包,用于开发环境中
预处理器loader:
"babel-loader": ES6->ES5
"file-loader": 处理css、js文件的导入语句并替换成访问地址,同时把文件输出到相应位置
"url-loader": file-loader的升级版,除了上述功能还有base64编码能力,可以减少小于8KB的文件的一次网络请求
插件plugin:
"clean-webpack-plugin": 重复打包后,删除本地上次打包的文件
"copy-webpack-plugin": 用来复制文件,如一些图片和音频等资源,打包过程没有用到,但需要输出到输出目录下
开发环境和生产环境的需求区别:
-
开发环境的需求:
- 模块热更新(本地开启服务,页面实时更新,不用每次都刷新一下页面,只需要把webpack-dev-server里的hot设置为true)
- sourceMap(方便打包调试)
- 接口代理(配置proxyTable解决开发环境中的跨域问题)
- 代码规范检查
-
生产环境的需求:
- 压缩混淆代码,清楚代码空格、注释
- 文件压缩/图片使用Base64编码
- 去除无用代码
开发环境配置:
"webpack": 核心包
"webpack-cli": 命令行工具包
"webpack-dev-server": 开启一个本地服务器访问打包代码
"source-map": "^0.7.4", 可以通过开发者工具看到源码,方便调试,主要用于开发环境,生产环境非要用就用hidden-source-map这个类型或者白名单策略
"cross-env": 设置跨操作系统的环境变量
"css-loader": 解析css文件
"scss-loader": 解析scss文件
"style-loader": 将解析的css文件打包到bundle.js文件中
"mini-css-extract-plugin": "^2.6.1", 该插件将单独把css文件提取出来,用于生产环境中
"html-webpack-plugin": "^5.5.0", 输入一个html模板文件,然后生成一个自动引入打包后生成的css文件的html文件
"file-loader"、"url-loader": 解析文件导入地址并转换为访问地址,同时把文件输出到相应位置,未来被Assets Modules代替
"webpack-merge": "^5.8.0", 将开发环境和生产环境分开后,分别在两个配置文件里用于合并公共配置文件,类似Object.assign({},'a')
生产环境配置:
"webpack": 核心包
"webpack-cli": 命令行工具包
"webpack-dev-server": 开启一个本地服务器访问打包代码
"cross-env": 设置跨操作系统的环境变量
"css-loader": 解析css文件
"scss-loader": 解析scss文件
"style-loader": 将解析的css文件打包到bundle.js文件中
"mini-css-extract-plugin": "^2.6.1", 该插件将单独把css文件提取出来,用于生产环境中
"html-webpack-plugin": "^5.5.0", 输入一个html模板文件,然后生成一个自动引入打包后生成的css文件的html文件
"webpack-merge": "^5.8.0", 将开发环境和生产环境分开后,分别在两个配置文件里用于合并公共配置文件,类似Object.assign({},'a')
\