webpack源码解析(80行代码写一个Webpack插件并发布到npm)
webpack源码解析
80行代码写一个Webpack插件并发布到npm1. 前言最近在学习 Webpack
相关的原理,以前只知道 Webpack 的配置方法,但并不知道其内部流程,经过一轮的学习,感觉获益良多,为了巩固学习的内容,我决定尝试自己动手写一个插件。
这个插件实现的功能比较简单:
- 默认清除
js
代码中的console.log
的打印输出; - 可通过传入配置,实现移除
console
的其它方法,如console.warn
、console.error
等;
2.1 Webpack 构建流程
Webpack
的主要构建流程,可以分为三个阶段:
- 初始化阶段:启动构建,读取与合并配置参数,加载
Plugin
,实例化Compiler
。 - 编译阶段:从
Entry
发出,针对每个Module
串行调用对应的Loader
去翻译文件内容,再找到该Module
依赖的Module
,递归地进行编译处理。 - 生成阶段:对编译后的
Module
组合成Chunk
,把Chunk
转换成文件,输出到文件系统。
如果 Webpack
打包生产环境文件时,只会执行一次构建,以上阶段会按顺序执行一遍。但是在开启监听模式时,如开发环境,Webpack 会持续的进行构建。
2.2 plugin 原理
Webpack
插件通常是一个带有 apply
函数的类,其中 constructor
可以接收传入的配置项。插件被安装时,apply
函数会被调用一次,并接收 Compiler
对象,然后我们可以在 Compiler
对象上监听不同的事件钩子,从而进行插件功能的开发。
// 定义一个插件 class MyPlugin { // 构造函数,接收插件的配置项 options constructor(options) { // 获取配置项,初始化插件 } // 插件安装时会调用 apply,并传入 compiler apply(compiler) { // 获取 comolier 独享,可以监听事件钩子 // 功能开发 ... } }
2.3 compiler 和 compilation 对象
在开发 Plugin
过程中最常用的两个对象就是 Compiler
和 Compilation
:
Compiler
对象在Webpack
启动时被实例化,该对象包含了Webpack
环境所有的配置信息,包括options
、loaders
、plugins
等。在整个Webpack
构建过程中,Compiler
对象是全局唯一的, 它提供了很多事件钩子回调供插件使用。Compilation
对象包含了当前的模块资源、编译生成资源、变化的文件等。Compilation
对象在Webpack
构建过程中并不是唯一的,如果在开发模式下Webpack
开启了文件检测功能,每当文件变化时,Webpack
会重新构建,此时会生成一个新的Compilation
对象。Compilation
对象也提供了很多事件回调供插件做扩展。
3.1 项目目录
该插件实现的功能比较简单,文件目录也不复杂。首先新建一个空文件夹 remove-console-Webpack-plugin
,并在该文件夹目录下运行 npm init
,根据提示来填写 package.json
相关信息。然后再新建一个 src
文件夹,插件主要代码就放在 src/index.js
里面。如果你需要把项目放到 github
上,最好也添加一下 .gitignore
、README.md
等文件。
// remove-console-Webpack-plugin ├─src │ └─index.js ├─.gitignore ├─package.json └─README.md
3.2 插件代码
插件代码逻辑也并不复杂,主要有几点:
- 在构造函数中接收配置参数,并对参数进行合并,得到需要清除的
console
函数, 存放在removed
数组中; - 在
apply
函数中监听compiler.hook.compilation
钩子,该钩子触发后,拿到compilation
后进一步监听它的钩子,这里Webpack4
和Webpack5
的钩子不一样,需要做兼容; - 定义
assetsHandler
方法来处理js
文件,利用正则表达式清除removed
中包括的console
函数;
class RemoveConsoleWebpackPlugin { // 构造函数接受配置参数 constructor(options) { let include = options && options.include; let removed = ['log']; // 默认清除的方法 if (include) { if (!Array.isArray(include)) { console.error('options.include must be an Array.'); } else if (include.includes('*')) { // 传入 * 表示清除所有 console 的方法 removed = Object.keys(console).filter(fn => { return typeof console[fn] === 'function'; }) } else { removed = include; // 根据传入配置覆盖 } } this.removed = removed; } // Webpack 会调用插件实例的 apply 方法,并传入compiler 对象 apply(compiler) { // js 资源代码处理函数 let assetsHandler = (assets, compilation) => { let removedStr = this.removed.reduce((a, b) => (a + '|' + b)); let reDict = { 1: [RegExp(`\\.console\\.(${removedStr})\\(\\)`, 'g'), ''], 2: [RegExp(`\\.console\\.(${removedStr})\\(`, 'g'), ';('], 3: [RegExp(`console\\.(${removedStr})\\(\\)`, 'g'), ''], 4: [RegExp(`console\\.(${removedStr})\\(`, 'g'), '('] } Object.entries(assets).forEach(([filename, source]) => { // 匹配js文件 if (/\.js$/.test(filename)) { // 处理前文件内容 let outputContent = source.source(); Object.keys(reDict).forEach(i => { let [re, s] = reDict[i]; outputContent = outputContent.replace(re, s); }) compilation.assets[filename] = { // 返回文件内容 source: () => { return outputContent }, // 返回文件大小 size: () => { return Buffer.byteLength(outputContent, 'utf8') } } } }) } /** * 通过 compiler.hooks.compilation.tap 监听事件 * 在回调方法中获取到 compilation 对象 */ compiler.hooks.compilation.tap('RemoveConsoleWebpackPlugin', compilation => { // Webpack 5 if (compilation.hooks.processAssets) { compilation.hooks.processAssets.tap( { name: 'RemoveConsoleWebpackPlugin' }, assets => assetsHandler(assets, compilation) ); } else if (compilation.hooks.optimizeAssets) { // Webpack 4 compilation.hooks.optimizeAssets.tap( 'RemoveConsoleWebpackPlugin', assets => assetsHandler(assets, compilation) ); } }) } } // export Plugin module.exports = RemoveConsoleWebpackPlugin;
希望别人能使用到你的插件,就需要把插件发布到 npm
上,发布的主要流程:
首先在 npm
官网上注册账号,然后打开命令行工具,在任意目录下输入 npm login
并按提示登录;
登录后可用 npm whoami
查看是否登录成功;
发布前检查一下根目录下的 package.json
文件信息是否填写正确,主要字段:
- name:决定用户下载你的插件时用的名称,不可与
npm
上已有的第三方包重名,否则无法发布; - main:插件主文件入口,
Webpack
引入插件时,就从该目录导入; - version:每次更新发布时,需要与上一版本的版本号不一样,否则上传不成功;
- repository:如果你的插件代码放在
github
、gitee
等网站,可以填一下; - private:不能设置为
true
,否则无法发布;
一切准备就绪后,切换到插件所在的目录下,运行 npm publish
即可上传插件;
上传成功后,到 npm
官网上搜索,看看是否能搜到插件;
到此这篇关于80行代码写一个Webpack插件并发布到npm的文章就介绍到这了,更多相关Webpack插件发布到npm内容请搜索开心学习网以前的文章或继续浏览下面的相关文章希望大家以后多多支持开心学习网!
- webpack打包后的代码(webpack几种手动实现HMR的方式)
- webpack打包后css路径(Webpack 中 css import 使用 alias 相对路径的方法)
- typescript开发后端(教你使用webpack打包编译TypeScript代码)
- webpack源码解析(80行代码写一个Webpack插件并发布到npm)
- webpack打包公共文件(webpack文件打包错误异常)
- 如何在webpack中搭建项目(如何在webpack项目中调试loader插件)
- webpack配置项流程先后顺序(Webpack中publicPath使用详解)
- vuecli打包项目(使用vue-cli创建项目并webpack打包的操作方法)
- webpack打包分析(webpack代码分片的实现)
- webpack常用的配置(webpack-dev-server搭建本地服务器的实现)
- 前端webpack方面的优化(webpack的移动端适配方案小结)
- vscode报错提示框(关于在vscode使用webpack指令显示"因为在此系统中禁止运行脚本"问题)
- 这里输入关键词(请手动输入关键词)
- 小说 顾瑾岚拿出一套飞行棋,别说你连飞行棋都不会哦(顾瑾岚拿出一套飞行棋)
- 金品公司 界界乐中秋限定飞行棋礼盒 露营藤篮礼盒全新上市(界界乐中秋限定飞行棋礼盒)
- 必看 8月,相比七夕,更需要注意的是这些事(必看8月相比七夕)
- 8月23日11时16分将迎处暑,逐渐进入气象意义上的秋天(8月23日11时16分将迎处暑)
- 花不语 下 如果重来一次的话,你还会这么选择吗(花不语下如果重来一次的话)
热门推荐
- laravel框架的业务流程顺序(Laravel+Dingo/Api 自定义响应的实现)
- socket和tcp什么关系(TCP socket SYN队列和Accept队列区别原理解析)
- js绘制平滑路径(如何利用Javascript生成平滑曲线详解)
- nginx常见错误码(Nginx常见的错误配置举例)
- docker离线安装步骤(Docker安装FastDFS的方法步骤)
- 私有云需要企业自己买服务器吗(企业如何对私有云主机进行管理?)
- dedecms分页修改(dedecms 自动审核会员发表的最新文章的修改方法)
- mysqlreplace用法(细说mysql replace into用法)
- jquery中unbind、bind
- 简简单单教你用python写个游戏(python3 pygame实现接小球游戏)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9