前言
最近的工作基本上在第三方低代码平台展开,使用过之后才发现低代码其实并不低代码,反而有种束手束脚的别扭感,很多时候业务设计的思路平台无法实现或者实现的体验感不佳。想写低代码文章的目的有两个,一个是熟悉低代码的逻辑和业务,二是想思考什么样的低代码平台才更被人想用。

参考文章
技术栈
- vue3 组件的开发语言,同时通过vue3的异步组件defineAsyncComponent来加载远程组件
- vite 脚手架,用于调试组件
- webpack 把组件编译打包成esm格式,并输出压缩文件
组件的目录结构

- index.js 组件的入口文件
- panel/index.js 组件配置面板的入口文件
- card.json 组件配置项
card.json
{
// 版本号
"version": "1.0.2",
// 默认显示的大小
"config": {
"width": 200,
"height": 50
},
// 配置项
"initProp": {
"text": "我是一个文本组件",
"textStyle": {
"font": "",
"fontSize": "12px"
}
}
...
}
后续随着功能的丰富,增加各种配置项。
使用vite初始化工程
yarn create vite 按照提示操作即可。
这一步就不赘述了,可查阅vite官网
webpack配置
安装webpack
依赖之间可能有版本要求,所以一开始最好跟我的版本一致。
yarn add webpack@5.89.0 webpack-cli@5.1.4 -D
完整配置
import { dirname} from "node:path"
import { fileURLToPath } from "node:url"
import { VueLoaderPlugin } from "vue-loader"
import FileManagerPlugin from 'filemanager-webpack-plugin'
import path from 'path'
import AutoImport from 'unplugin-auto-import/webpack'
import Components from 'unplugin-vue-components/webpack'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import CopyPlugin from 'copy-webpack-plugin'
// 打包命令
// npm run build:card --card=text
const getCurrentDir = () => {
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
return __dirname
}
const __dirname = getCurrentDir()
const cardName = process.env.npm_config_card
const cardJson = await import(`../src/cards/${cardName}/card.json`,{ assert: {type: 'json'} })
const version = cardJson.default.version || '1.0.0'
const outputDir = `${cardName}-${version}`
export default {
mode: "production",
entry: {
index: path.resolve(__dirname, `../src/cards/${cardName}/index.js`),
panel: path.resolve(__dirname, `../src/cards/${cardName}/panel/index.js`)
},
// 兼容浏览器引入vue
// 与加载组件的项目保持一致,使得远程组件和运行项目的vue上下文相同
externals: {
"vue": 'https://6c6f-lowcode-2gxh0baw4d19d595-1256670128.tcb.qcloud.la/assets/vue.esm.js'
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, `./dist`),
library: { type: 'module' }
// library: "MyPlugin",
// libraryTarget: "umd",
},
experiments: { outputModule: true },
plugins: [
// 引入插件
new VueLoaderPlugin(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
new CopyPlugin({
patterns: [
{ from: path.resolve(__dirname, `../src/cards/${cardName}/card.json`), to: path.resolve(__dirname, `./dist`) },
],
}),
new FileManagerPlugin({
events: {
onEnd: {
delete: [
path.resolve(__dirname, `./.zip/${outputDir}.zip`),
],
archive: [
{source: path.resolve(__dirname, `./dist`), destination: path.resolve(__dirname, `../.zip/${outputDir}.zip`)},
]
}
}
})
],
optimization: {
minimize: true,
},
module: {
rules: [
// vue
{
test: /\.vue/,
use: ["vue-loader"],
},
// JS
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: [
// 开始
[
"@babel/preset-env",
{
useBuiltIns: "usage",
corejs: {
version: 3,
}
},
],
// 结束
],
},
},
},
// CSS
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
{
test: /\.less$/,
use: ["style-loader", "css-loader", "less-loader"],
},
// Images
{
test: /\.(jpg|png|gif|svg)$/,
use: [
{
loader: "file-loader",
options: {
outputPath: "assets/images/",
},
},
],
},
// Fonts
{
test: /\.(ttf|eot|woff|woff2)$/,
use: [
{
loader: "file-loader",
options: {
outputPath: "assets/fonts/",
},
},
]
}
]
}
}
打包命令
npm run build --card=组件名