1、Webpack 的构建流程主要有哪些环节?如果可以请尽可能详尽的描述 Webpack 打包的整个过程。
根据配置文件里entry找到打包入口文件
顺着入口文件代码里的import或require之类的语句,解析推断文件所依赖的资源模块
分别去解析每个资源模块对应的依赖,最后形成一颗依赖树,
递归依赖树,找到每个节点对应的资源文件,
根据配置文件 rules 属性,找到资源模块所对应的加载器,
交给对应的加载器加载对应的资源模块,
最后将加载以后的结果放入到bundle.js打包结果里,
实现整个项目的打包。
2、Loader 和 Plugin 有哪些不同?请描述一下开发 Loader 和 Plugin 的思路。
Loader 和 Plugin 的不同
- loader是资源加载器,实现资源模块的转换和加载,编译转换类 css-loader,文件操作类 file-loader、url-loader,代码检查类 eslint-loader,负责资源文件从输入到输出的资源转换,对于同一个资源可以使用多个loader,类似于管道概念
- Plugin 解决除了资源加载之外其他自动化工作(打包之前清除 dist 目录、拷贝静态文件、压缩代码等等)
开发loader的思路
每个webpack的loader需要导出一个函数 这个函数是对加载到的资源的处理过程 输入是加载到的资源文件的内容 输出是加工之后的结果
const marked = require('marked')
module.exports = source => {//接收到的资源文件
// console.log(source)
// return 'console.log("hello ~")'
const html = marked(source)
//方式一:第一,输出标准的 JS 代码,让打包结果的代码能正常执行;
return html
return `export default ${JSON.stringify(html)}`
//方式二: 输出处理结果,交给下一个 loader 进一步处理成 JS 代码
//返回 html 字符串交给下一个 loader 处理
return html
}
在 webpack.config.js 中使用 loader,配置 module.rules ,其中 use 除了可以使用模块名称,也可以使用模块路径
const path = require('path')
module.exports = {
mode: 'none',
entry: './src/main.js',
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist'),
publicPath: 'dist/'
},
module: {
rules: [
{
test: /.md$/,
use: [//输出处理结果交给下一个laoder处理
'html-loader',
'./markdown-loader'
]
}
]
}
}
开发plugin的思路
plugin 是通过在生命周期的钩子中挂载函数实现扩展,我们可以在不同的事件节点上挂载不同的任务,就可以扩展一个插件,插件必须是一个函数或者是一个包含 apply 方法的对象,一般可以把插件定义为一个类型,在类型中定义一个 apply 方法
apply 方法接收一个 compiler对象 参数,包含了这次构建的所有配置信息,
通过这个对象注册钩子函数
通过 compiler.hooks.emit.tap 注册钩子函数(emit也可以为其他事件),
钩子函数第一个参数为插件名称,
第二个参数 compilation函数 为此次打包的上下文,
根据 compilation.assets[name].source() 就可以拿到此次打包的资源的内容,
然后做一些相应的逻辑处理
class MyPlugin {
apply (compiler) {
console.log('MyPlugin 启动')
compiler.hooks.emit.tap('MyPlugin', compilation => {
// compilation => 可以理解为此次打包的上下文
for (const name in compilation.assets) {
// console.log(name)
// console.log(compilation.assets[name].source())
if (name.endsWith('.js')) {
const contents = compilation.assets[name].source()//文件内容
const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
compilation.assets[name] = {
source: () => withoutComments, //返回新的内容
size: () => withoutComments.length //返回新的内容的长度
}
}
}
})
}
}