用webpack打包一个script标签引入使用的组件

1,411 阅读3分钟

需求背景

最近隔壁组的运营同学新上线了一个产品,想在咱们组这边已有的产品中导流给他们。我们这边的产品大概有4-5个导流给他们,需要在这几个产品中同时导流,就必须优先考虑代码复用的问题。经过和组长的一番讨论,如何以最低的迭代成本完成,又不影响原来的业务,最优的解决方案就是,做一个导流的广告弹窗,打包成一个js文件,然后在需要导流的产品中引入,添加几行控制弹窗显示、隐藏的逻辑即可。

需求分析

  1. 一处打包,多处使用
  2. 因为通过script标签引入,所以体积需要尽可能的小
  3. 产品各自有其原来的业务逻辑,弹窗组件的逻辑需要与原有业务完全解耦
  4. 考虑到各个应用场景可能会有一些不同的逻辑,所以使用原型链的方式进行函数传参

原始人方式

用最原始的方式,当然就是纯手写js,包括逻辑、字符串模板、style样式等等,于是乎就有了第一个雏形版本:

function Modal(opts) {
  this.title = opts.title
  this.channel = opts.channel
  this.dom = '<div id="modal" style="position: relative; width: 500px; height: 300px; margin: 150px auto; background: #00CCFF">' + 
             '<div id="modalCloseBtn" style="position: absolute; top: 0px; right: 10px;">close</div>' +
             '<div style="text-align: center">' + this.title + '</div>' +
             '</div>'
  this.onShow = function() {
    if (opts && opts.onShow && typeof(opts.onShow) == 'function') {
      return opts.onShow()
    }
  }
  this.onClose = function() {
    document.getElementById("modal").style.display = 'none'
    if (opts && opts.onClose && typeof(opts.onClose) == 'function') {
      return opts.onClose()
    }
  }
}

Modal.prototype.show = function() {
  document.body.insertAdjacentHTML("beforeend", this.dom)
  document.getElementById("modalCloseBtn").addEventListener("click", this.onClose);
  this.onShow()
}

export default Modal

只有最简单显示、隐藏逻辑(当中的样式请忽略,实际情况肯定没那么丑的)。

使用的时候只需要这样就可以了

<script type="text/javascript" src="./modal.js"></script>
<script type="text/javascript">
var m = new Modal({
  title: 'test-modal',
  channel: 'channel',
  onShow: function() {
    console.log('modal show')
  },
  onClose: function() {
    console.log('modal close')
  }
})
m.show()
</script>

行了,我知道你们要说什么,接着往下看吧...

使用webpack、rollup等工具

对于这类需求,最理想的方式就是利用webpack或者rollup等工具打包成一个库。由于webpack用得相对频繁,所以就直接上手webpack了。需求不算太复杂,所以不需要太多依赖,基本上babel和样式的loader就够了,详情请看github源码

但是,webpack配置有一个地方需要注意。由于需求是生成一个js文件,在webpack的配置中,可以理解为,把这个组件最终打包成一个库,这就需要关注一下webpack的output配置中library的参数

image.png

从 libraryTarget 这个参数中得知,值为var是切合我们的需求,但是这个参数已经准备放弃了,因而使用library.type,至于其他类型的作用,这里就不做解释了,可参阅其他文章。

关键是 output.library.export 参数,由于我们要使用原型链的方式使用,这里要设置成'default',默认是undefined,如果不设置这个参数,是没法使用 New 关键字,只能这样使用

<script type="text/javascript" src="./modal.js"></script>
<script type="text/javascript">
modal.show() // 这里的 modal 需要与 output.library.name 参数一致
</script>

完整的webpack配置也不复杂

const path = require('path');
const webpackBaseConfig = {
    mode: "production",
    entry: path.join(__dirname, './src/index.js'),
    output: {
        path: path.join(__dirname, './dist'),
        filename: 'bundle.js',
        library: {
            name: 'Modal',
            type: 'var',
            export: 'default'
        }
    },
    module: {
        rules: [
            {
                test: /\.js?$/,
                use: 'babel-loader',
                exclude: /node_modules/,
            },
            {
                test: /\.(sc|c)ss/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader',
                ],
            },
        ],
    },
};

module.exports = webpackBaseConfig;

有了webpack的加持,我们就可以脱离“刀耕火种”的年代了,例如可以使用ES6 的class语法写原型链,可以使用更加优雅的字符串模板,也可以单独写sass引入

完整的代码可看这里:github.com/Leungkingma…