webpack自定义plugin实现编译时自动插入js和css

有一些场景下需要这么个需求,在webpack编译运行时自动插入js或css代码到html模板中。例如要插入一段埋点插件或监控插件的js代码,当然你可以直接写在html模板中,但是对于一个项目中存在多个html模板的情况就不适合一个个手写添加了;

本文讨论通过自定义封装一个webpack plugin的形式来实现自动插入js或css的功能。

  • 你可以直接使用我封装好的npm包:insert-html-webpack-plugin(欢迎star)
  • 也可以按以下教程自己手写一个。

一、分析

  • 本身webpack在运行时就会自动处理html模板,把项目引用的代码经过编译、打包、压缩等一系列处理后通过js和css插入到html相应的位置,而这个功能是通过html-webpack-plugin插件完成的。
  • 该插件在webpack编译时会有几个events事件,以v3.2.0版本为例,会有以下几个异步events:
    html-webpack-plugin-before-html-generation
    html-webpack-plugin-before-html-processing
    html-webpack-plugin-alter-asset-tags
    html-webpack-plugin-after-html-processing
    html-webpack-plugin-after-emit
    ----以上events分别对应不同的编译时期。
  • 而我们要插入自定义的js和css,为了能精确控制插入位置,我们最好是在html-webpack-plugin插件插入js和css之后再做我们的插入动作,顾名思义,也就是在 html-webpack-plugin-after-html-processing 事件里来处理。

二、实现

基本思路:在webpack hook的编译阶段,拿到html页面字符串,根据标签字符串分割,然后插入我们自己的js或css代码,再拼接起来。

1、webpack plugin

  • webpack plugin的主要作用就是在webpack某些钩子时期做一些处理来实现想要的功能,每一个plugin插件使用时都是通过new的方式创建一个实例,webpack在内部接收到该实例后会调用实例的apply方法来执行plugin的逻辑。
  • 所以,自定义编写plugin时有两种方式:
    一是编写一个构造函数,在原型上添加一个apply方法;
    二是编写一个class类,类里添加一个apply方法。

2、插入js示例代码

这里就以class类的形式编写,实现插入自定义js,

  • 以script标签外联方式引入js文件:
class MyPlugin {
  constructor (options) {
    this.options = options || ''
  }

  apply (compiler) {
    // 获取要插入的js的地址
    const path = this.options.path
    // 定义要插入的script字符串
    const scriptCode = `<script src="${path}"></script>`

    // 编译时注入
    compiler.hooks.compilation.tap(
      'compilation',
      compilation => {
        compilation.hooks.htmlWebpackPluginAfterHtmlProcessing.tap(
          'htmlWebpackPluginAfterHtmlProcessing',
          htmlPluginData => {
            // 获取html文件字符串
            const htmlStr = htmlPluginData.html.toString()
            // 字符串替换,在<body>字符串后追加script
            htmlPluginData.html = htmlStr.replace(/<body>/, '<body>' + scriptCode)
          })
      })
  }
}

module.exports = MyPlugin

3、插入css示例代码

和js类似,可以在</title>之后插入:

const linkCode = `<link rel="stylesheet" href="${path}"/>`

htmlPluginData.html = htmlStr.replace(/</title>/, '</title>' + linkCode)

4、使用方式

以插入js为例:

const MyPlugin = require('./MyPlugin')

new MyPlugin({
  path: 'https://www.a.com/bb.js'
})

5、内联方式

如果要以内联方式引用代码插入,可以考虑使用node的fs模块读取js或css文件字符串后拼接,
以插入js为例:

const fs = require('fs')
const path = require('path')

// 获取要插入的js的路径,这里的路径是相对于webpack运行命令目录(一般是项目根目录)的路径
const path = this.options.path
// 定义要插入的script字符串
let scriptCode = ''

// 读取js内容
let str = ''
try {
  str = fs.readFileSync(path.join(process.cwd(), path), 'utf-8').toString()
} catch (err) {
  console.log('js readFileSync error:', err)
}
scriptCode = `<script>${str}</script>`
  • 如果是插入css,只需要把上述代码的script标签换成style标签即可。

三、注意点

  • 插入的js或css不会再经过webpack的任何处理,所以在插入之前请检查待插入的代码兼容性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值