集成react

集成 ​​react​​ ,安装依赖。

npm i react react-dom -Snpm i @babel/preset-react -D复制代码

配置 ​​babel.config.js​​ 。

// babel.config.jsmodule.exports = {  presets: [    ["@babel/preset-react", {}]  ]}复制代码

集成vue

集成 ​​vue​​​ ,安装依赖。这个其实跟 ​​babel​​ 没啥关系,顺便在这写了。

要注意 ​​vue​​​ 和 ​​vue-template-compiler​​ 的版本要保持一致。

npm i vue vue-loader vue-template-compiler -D复制代码

配置 ​​webpack.config.js​​ 。

// webpack.config.jsconst { VueLoaderPlugin } = require('vue-loader')module.exports = {  module: {    rules: [      // ... 其它规则      {        test: /.vue$/,        loader: 'vue-loader'      }    ]  },  plugins: [    // 请确保引入这个插件!    new VueLoaderPlugin()  ]}复制代码

实现自定义plugin

plugin的结构

根目录下新建 ​​myPlugins/my-plugin.js​​ 。

​plugin​​​ 的实质就是一个类。通过构造函数接收 ​​options​​​ 配置。每个 ​​plugin​​​ 必须内置一个 ​​apply​​​ 方法,该方法接收一个参数 ​​compiler​​​ ,就是实例化的 ​​webpack​​ ,其中包含配置等信息。

// my-plugin.jsclass MyPlugin {  constructor(options) {    console.log('????????~ options:', options);  }  apply(compiler) {    // console.log('????????~ : my-plugin');  }}module.exports = MyPlugin复制代码

plugin的引用

新建实例的方式引用,可以在创建实例的时候传递 ​​options​​。

// webpack.config.jsconst MyPlugin = require("./myPlugins/my-plugin")module.exports = {  plugins: [    new MyPlugin({      name: '一尾流莺'    })  ]}复制代码

plugin的编写

怎么确定 ​​plugin​​ 的执行时机?

其实 ​​webpack​​ 也是有生命周期钩子的。我们可以来看一下都有哪些钩子。

// 跟目录下新建 webpack.js  然后通过 node webpack.js 启动// 引入 webpackconst webpack = require("webpack")// 引入配置文件const config = require("./webpack.config.js")// 根据配置文件 生产 webpack 实例const compiler = webpack(config)// 打印 webpack 的 生命周期钩子Object.keys(compiler.hooks).forEach((hookName) => {  compiler.hooks[hookName].tap("xxx", (compilation) => {    console.log('????????~ hookName:', hookName);  })})// 执行 编译compiler.run()复制代码

所有的生命周期钩子都在 ​​compiler.hooks​​ 属性上,所以我们遍历打印出来的结果如下:

【webpack】入门(8)_生命周期

可以看到 ​​webpack​​ 的生命周期钩子实在是太多了。

所以我们的 ​​plugin​​​ 的事件只需要注册在对应的生命周期钩子上就可以了,然后 ​​webpack​​​ 就会在恰当的时机通过我们自己的 ​​plugin​​ 了。

class MyPlugin {  constructor(options) {  }  // 接收一个参数 compiler 就是实例化的webpack 包含配置等信息  apply(compiler) {    // 注册事件  同步钩子用tap  异步钩子用tapAsync注册    // 事件名称可以为任意值,建议和插件名称保持语义一致    compiler.hooks.emit.tapAsync('xxx', (compilation, cb) => {      // compilation 半成品      console.log('????????~ compilation:', compilation.assets);      const content = 'hello,plugin  xxxxx'      // 添加静态资源      compilation.assets['warbler.txt'] = {        source: function() {          return content        },        // 只是用来查看的,并不会影响文件真实大小        size: function() {          return content.length        }      }      cb()    })  }}module.exports = MyPlugin复制代码

通过 ​​apply​​​ 方法的 ​​compiler​​ 参数进行事件的注册,可以注册在任何一个生命周期中,但是有的生命周期是同步的,有的是异步的,具体的还是要查看官网。

同步钩子用 ​​tap​​​ 注册事件, 异步钩子用 ​​tapAsync​​ 注册事件。

如果是异步钩子的话, 需要在事件的回调中传递一个​​ cb​​ 参数,并在最后调用一下。

事件名称可以为任意值,建议和插件名称保持语义一致,方便阅读源码,理解功能。

事件回调的 ​​compilation​​ 参数就是上一个钩子执行过后的半成品,属性有很多,可以阅读官网。