需求背景
最近隔壁组的运营同学新上线了一个产品,想在咱们组这边已有的产品中导流给他们。我们这边的产品大概有4-5个导流给他们,需要在这几个产品中同时导流,就必须优先考虑代码复用的问题。经过和组长的一番讨论,如何以最低的迭代成本完成,又不影响原来的业务,最优的解决方案就是,做一个导流的广告弹窗,打包成一个js文件,然后在需要导流的产品中引入,添加几行控制弹窗显示、隐藏的逻辑即可。
需求分析
- 一处打包,多处使用
- 因为通过script标签引入,所以体积需要尽可能的小
- 产品各自有其原来的业务逻辑,弹窗组件的逻辑需要与原有业务完全解耦
- 考虑到各个应用场景可能会有一些不同的逻辑,所以使用原型链的方式进行函数传参
原始人方式
用最原始的方式,当然就是纯手写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的参数
从 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…