上一章讲解了什么是web component, 这一章带大家从0-1开发一个web component, 我们先观摩市面上已有的web component组件库
下面跟着我的脚步从0-1开发一个web component
-
框架选用
我们使用vue3来开发web component, 你没听错, 就是vue3, vue支持将组件编译成web component
-
新建项目模版
npm i -g yarn yarn create vite web-component-ui --template vue-ts cd ./web-component-ui yarn -
新建组件文件index.ce.vue
web-component-ui/src/components/button/index.ce.vue
<template> <button class="fl-button" :class="[ `fl-button--${type}`, { 'is-disabled': disabled, 'is-loading': loading, 'is-round': round, } ]" :disabled="disabled || loading" @click="handleClick" > <!-- loading 图标 --> <span v-if="loading" class="loading-icon"> <svg viewBox="0 0 1024 1024" class="loading"> <path d="M512 64q14.016 0 23.008 8.992T544 96v192q0 14.016-8.992 23.008T512 320t-23.008-8.992T480 288V96q0-14.016 8.992-23.008T512 64zm0 640q14.016 0 23.008 8.992T544 736v192q0 14.016-8.992 23.008T512 960t-23.008-8.992T480 928V736q0-14.016 8.992-23.008T512 704zm448-192q0 14.016-8.992 23.008T928 544H736q-14.016 0-23.008-8.992T704 512t8.992-23.008T736 480h192q14.016 0 23.008 8.992T960 512zm-640 0q0 14.016-8.992 23.008T288 544H96q-14.016 0-23.008-8.992T64 512t8.992-23.008T96 480h192q14.016 0 23.008 8.992T320 512z" /> </svg> </span> <!-- 默认插槽 --> <slot></slot> </button> </template> <script setup lang="ts"> import { defineProps, defineEmits } from 'vue' // 定义属性 const props = defineProps({ type: { type: String, default: 'default', validator: (val: string) => { return ['default', 'primary', 'success', 'warning', 'danger'].includes(val) } }, disabled: { type: Boolean, default: false }, loading: { type: Boolean, default: false }, round: { type: Boolean, default: false } }) // 定义事件 const emit = defineEmits(['click']) // 点击处理 const handleClick = (event: MouseEvent) => { if (props.disabled || props.loading) return emit('click', event) } </script> <style scoped> .fl-button { display: inline-flex; justify-content: center; align-items: center; line-height: 1; height: 32px; white-space: nowrap; cursor: pointer; color: #606266; text-align: center; box-sizing: border-box; outline: none; transition: .1s; font-weight: 500; padding: 8px 15px; font-size: 14px; border-radius: 4px; border: 1px solid #dcdfe6; background-color: #ffffff; } .fl-button:hover { color: #409eff; border-color: #c6e2ff; background-color: #ecf5ff; } .fl-button:active { color: #3a8ee6; border-color: #3a8ee6; outline: none; } /* 禁用状态 */ .fl-button.is-disabled, .fl-button.is-disabled:hover { color: #c0c4cc; cursor: not-allowed; background-image: none; background-color: #fff; border-color: #ebeef5; } /* 加载状态 */ .fl-button.is-loading { position: relative; pointer-events: none; } /* 圆角按钮 */ .fl-button.is-round { border-radius: 20px; } /* 类型样式 */ .fl-button--primary { color: #fff; background-color: #409eff; border-color: #409eff; } .fl-button--primary:hover { background: #66b1ff; border-color: #66b1ff; color: #fff; } .fl-button--success { color: #fff; background-color: #67c23a; border-color: #67c23a; } .fl-button--success:hover { background: #85ce61; border-color: #85ce61; color: #fff; } .fl-button--warning { color: #fff; background-color: #e6a23c; border-color: #e6a23c; } .fl-button--warning:hover { background: #ebb563; border-color: #ebb563; color: #fff; } .fl-button--danger { color: #fff; background-color: #f56c6c; border-color: #f56c6c; } .fl-button--danger:hover { background: #f78989; border-color: #f78989; color: #fff; } /* Loading 图标样式 */ .loading-icon { margin-right: 5px; } .loading { width: 14px; height: 14px; animation: rotating 2s linear infinite; } @keyframes rotating { 0% { transform: rotateZ(0deg); } 100% { transform: rotateZ(360deg); } } </style> -
新建编译入口文件index.ts
web-component-ui/src/components/button/index.ts
import Button from './index.ce.vue'; import { defineCustomElement } from 'vue' /** 编译成webcomponent */ const vueWebComponent = defineCustomElement(Button) /** 注册组件 */ if (!customElements.get('fl-button')) customElements.define('fl-button', vueWebComponent); export { Button }; -
修改编译配置
web-component-ui/vite.config.ts
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vite.dev/config/ export default defineConfig({ plugins: [ vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.includes('-') } } }), ], build: { rollupOptions: { input: { "components/button": './src/components/button/index.ts', }, output: { format: 'esm', dir: 'dist', entryFileNames: "js/[name].js", chunkFileNames: "js/deps/[name].[hash].js", assetFileNames: 'js/assets/[name]-[hash][extname]', /** 分包配置 */ manualChunks: { 'vue': ['vue'], }, } } } }) -
打包
yarn build打包成功后, 你就可以看到dist目录, 只要引入
dist/js/components/button.js文件, 就可以使用对应的fl-button组件了, 就是那么简单 -
测试
方案1. 新建一个.html文件, 引入button.js文件, 看看fl-button是否能正常使用;
web-component-ui/dist/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script type="module" src="./js/components/button.js"></script> </head> <body> <fl-button type="primary">基本按钮</fl-button> <fl-button type="success">成功样式</fl-button> <fl-button type="warning">警告样式</fl-button> <fl-button type="danger">危险样式</fl-button> <fl-button type="primary" disabled>禁用状态</fl-button> </body> </html>方案2: 直接在当前的vue项目中测试, 修改App.vue(yarn vite启动项目即可预览)
web-component-ui/src/App.vue
<template> <div> <fl-button type="primary">基本按钮</fl-button> <fl-button type="success">成功样式</fl-button> <fl-button type="warning">警告样式</fl-button> <fl-button type="danger">危险样式</fl-button> <fl-button type="primary" disabled>禁用状态</fl-button> </div> </template> <script setup lang="ts"> // 导入即注册 import "../dist/js/components/button.js"; </script> <style scoped> </style>
示例代码已经放到gitee上了: gitee.com/LAMMUpro/ar…
就这么简单? no,no,no, 这只是一个开始, 后续章节会讲解出现的问题及解决方案