05-手动实现一个Loader和Plugin
项目github地址:https://github.com/Trade-Offf/LoaderAndPlugin
上图为markdown Loader的文件结构:
- dist是打包后的内容;
- node_modules是文件依赖,git clone 项目之后记得 npm i 安装依赖;
-
src是核心功能,里面有一个.md文件,一个js文件。js负责console .md文件;
直接在JS里不能输出.md文件
但在node环境下不能通过js直接输出.md文件,所以要经过处理,
方法一:JS内部处理
JS内部处理
方法二:写个Loader加载器
Webpack 加载资源文件的过程类似于一个工作管道,你可以在这个过程中依次使用多个 Loader,但是最终这个管道结束过后的结果必须是一段标准的 JS 代码字符串。
所以我们还可以用多个Loader进行处理:
markdown-loader2 中直接返回 HTML 字符串,再安装一个处理 HTML 的 Loader,叫作 html-loader。在webpack.config.js里把module的 use 属性修改为一个数组,以便依次使用多个 Loader。
P.S. 此时注意执行顺序是从后往前,所以要先返回HTML,再处理HTML;
以上,我们用两种方式,实现了一个markdown Loader;
相比于 Loader,插件的能力范围更宽,因为 Loader 只是在模块的加载环节工作,而插件的作用范围几乎可以触及 Webpack 工作的每一个环节。
那么插件机制如何实现呢?很简单,类似软件开发中的钩子机制;
在Webpack整个工作过程中会有很多环节,每个环节都埋下了钩子,在开发时不同节点挂载不同任务,即可实现扩展Webpack的能力;
接下来我想开发一个自动清除Webpack打包结果中的注释,让bundle.js更易读;
image.pngWebpack 要求我们的插件必须是一个函数或者是一个包含 apply 方法的对象,一般我们都会定义一个类,在这个类中定义 apply 方法。然后在使用时,再通过这个类来创建一个实例对象去使用这个插件。
所以我们这里定义一个 RemoveCommentsPlugin 类型,然后在这个类型中定义一个 apply 方法,这个方法会在 Webpack 启动时被调用,它接收一个 compiler 对象参数,这个对象是 Webpack 工作过程中最核心的对象,里面包含了我们此次构建的所有配置信息,我们就是通过这个对象去注册钩子函数
image.png做完这些,我们还需要知道把任务挂载到哪个钩子上
我们的需求是删除 bundle.js 中的注释,也就是说只有当 Webpack 需要生成的 bundle.js 文件内容明确过后才可能实施。
image.png那根据 API 文档中的介绍,我们找到一个叫作 emit 的钩子,这个钩子会在 Webpack 即将向输出目录输出文件时执行,非常符合我们的需求。
打印出文件名称bundle.js我们回到代码中,通过 compiler 对象的 hooks 属性访问到 emit 钩子,再通过 tap 方法注册一个钩子函数,这个方法接收两个参数:
-
第一个是插件的名称,我们这里的插件名称是 RemoveCommentsPlugin;
-
第二个是要挂载到这个钩子上的函数;
根据 API 文档中的提示,这里我们在这个函数中接收一个 compilation 对象参数,这个对象可以理解为此次运行打包的上下文,所有打包过程中产生的结果,都会放到这个对象中。
我们可以使用这个对象中的 assets 属性获取即将写入输出目录的资源文件信息,它是一个对象,我们这里通过 for in 去遍历这个对象,其中键就是每个文件的名称,我们尝试把它打印出来;
能够拿到文件名后,我们回到代码中。这里需要先判断文件名是不是以 .js 结尾,因为 Webpack 打包还有可能输出别的文件,而我们的需求只需要处理 JS 文件。
那如果是 JS 文件,我们将文件内容得到,再通过正则替换的方式移除掉代码中的注释,最后覆盖掉 compilation.assets 中对应的对象,在覆盖的对象中,我们同样暴露一个 source 方法用来返回新的内容。另外还需要再暴露一个 size 方法,用来返回内容大小,这是 Webpack 内部要求的格式,具体代码如下:此时的bundle.js文件如图,实现需求:
bundle.js打包后无注释文件
以上就是我们实现一个移除注释插件的过程,通过这个过程我们了解了:插件都是通过往 Webpack 生命周期的钩子中挂载任务函数实现的。