如何输出属于自己的组件方案

412 阅读2分钟

使用preact+webpack写一个对业务的组件

背景是之前使用vite+vue3+windiCss做过一个后台管理,为了达成技术闭环,我们对业务提供了H5和小程序的解决方案,今天提供一下H5的技术方案

技术选型

为什么使用preact,而不是使用vue、react?

  1. 因为之前在大量的vue开发项目中发现vue对外导出库的能力不够强
  2. 不使用react是因为preact已经能满足我的需求,且代码体积preact要小的多

目标产物

  1. 发版到npm库,可import/require的方式模块化按需引入
  2. 上传到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)
})