使用preact+webpack写一个对业务的组件
背景是之前使用vite+vue3+windiCss做过一个后台管理,为了达成技术闭环,我们对业务提供了H5和小程序的解决方案,今天提供一下H5的技术方案
技术选型
为什么使用preact,而不是使用vue、react?
- 因为之前在大量的vue开发项目中发现vue对外导出库的能力不够强
- 不使用react是因为preact已经能满足我的需求,且代码体积preact要小的多
目标产物
- 发版到npm库,可import/require的方式模块化按需引入
- 上传到cdn,输出cdn链接直接使用
实现过程
1. webpack配置
webpack.base.config.js如下:
const path = require('path')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, './dist/js'),
filename: '[name].min.js',
libraryTarget: 'umd',
},
resolve: {
alias: {
'~': path.resolve(__dirname, 'src'),
'react': 'preact-compat',
'react-dom': 'preact-compat'
}
},
module: {
rules: [
{
test: /\.less/,
use: [
{
loader: 'style-loader'
}, {
loader: 'css-loader',
options: {
modules: true,
}
}, {
loader: 'less-loader'
}]
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
},
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.js?$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}]
}
]
},
plugins: [
new UglifyJsPlugin()
]
}
webpack.config.js如下:
const path = require('path')
const baseConfig = require('./webpack.base.config')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...baseConfig,
mode: 'development',
devServer: {
static: {
directory: path.join(__dirname, './dist')
},
compress: true,
port: 9000,
host: '0.0.0.0',
},
plugins: [
...baseConfig.plugins,
new HtmlWebpackPlugin({
title: '测试页面',
filename: 'index.html',
template: 'index.html'
})
]
}
webpack.build.config.js如下:
const baseConfig = require('./webpack.base.config')
module.exports = {
...baseConfig,
mode: 'production'
}
2. 入口文件
/** @jsx h */
import { h, render } from 'preact'
// NOTE: 对应的业务组件
import Banner from './components/banner'
import Float from './components/float'
import Popup from './components/popup'
import Tips from './components/tips'
import CouponPerceive from './components/coupon-perceive'
// NOTE: export 导出不能是export default
export class TaxiOperation {
constructor (option) {
this.optiop = option
this.requireCommon = {
params: '1',
params: '2'
}
}
setRequireConfig (config) {
this.requireCommon = {
...this.requireCommon,
...config
}
}
init () {
return new Promise ((resolve, reject) => {
// NOTE: 网络请求返回,并在成功的时候去做相关的UI展示
this._show()
})
}
_show () {
const actions = {
banner: (props) => <Banner OnClick={(obj) => {this._OnItemClick(obj)}} {...props} />,
tips: (props) => <Tips OnClick={(obj) => {this._OnItemClick(obj)}} {...props} />,
float: (props) => <Float OnClick={(obj) => this._OnItemClick(obj)} OnClose={(obj) => this._OnItemClose(obj)} {...props} />,
popup: (props) => <Popup OnClick={(obj) => {this._OnItemClick(obj)}} OnClose={(obj) => this._OnItemClose(obj)} {...props}/>,
coupon: (props) => <CouponPerceive OnClick={(obj) => {this._OnItemClick(obj)}} OnClose={(obj) => this._OnItemClose(obj)} {...props}/>
}
this.option.forEach(item => {
if (!item.el) {
const createDiv = document.createElement('div')
document.body.appendChild(createDiv)
item.el = createDiv
}
actions[item.type] && render(actions[item.type](item), item.el)
})
}
}
使用说明
使用npm
npm i taxi-operation-h5 -S
import TaxiOperation from 'TaxiOperation'
const toh = new TaxiOperation([
{
id: '',
el: document.querySelector('.banner'),
type: 'banner',
show: true,
omg: {
a: {
id: '',
label: ''
},
attrs: {
}
}
}
])
toh.setRequireConfig({
token: ''
})
toh.init().then(res => {
console.log(res)
})
引入cdn模式
<script src="//cdnurl/operation/1.0.0/main.min.js"></script>
const toh = new TaxiOperation([
{
id: '',
el: document.querySelector('.banner'),
type: 'banner',
show: true,
omg: {
a: {
id: '',
label: ''
},
attrs: {
}
}
}
])
toh.setRequireConfig({
token: ''
})
toh.init().then(res => {
console.log(res)
})