Vue3自制UI框架的技术总结

本项目的结构所有UI组件及其依赖都放到lib文件中,起来用于搭建官网,正式版只需要导出lib文件中的各组件。
常用控件的实现--数据结构设计
参照Element UI,笔者发现为了提供组件的自定义能力,我们需要定义一套高可用的数据结构,这样才能实现因组件需求更变而带来的维护性的优势。 不同的组件都对应不同的组件属性.我们需要设计一套统一的标准的配置来约定它, 这样后期扩展非常方便, 只需要往组件添加属性即可。
以Button为例,我按照下面的属性表实现组件,功能间互不影响。
常用控件的实现--vue3的新特性
笔者制作框架中学习认识了Vue3的新特性。
- 属性和事件继承
和Vue2相比Vue3默认为子组件继承了属性和事件 ,你可以选择关闭它,而且props所声明的属性也不再帮你继承:
// 子组件
inheritAttr=false
v-model
Vue3摒弃了
.sycn
修饰符,如果符合要求就使用v-model
,否则就手动通信吧。v-model:
// 父 <Component v-model:value="xxx"/> // 子 this.emit('update:xxx',yyy)
手动:
// 父 <Component :value="xxx" v-on:update:xxx="(yyy)=>xxx=yyy" /> // 子 this.emit('update:xxx',yyy)
新的模板语法--class的绑定
绑定对象
<div :class="{ active: isActive }"></div>
上面的语法表示 active 是否存在取决于数据属性 isActive 的真假值。
- Teleport
Dialog对话框组件当被打开是应该覆盖整个页面,使用Teleport
就可以脱离当前的层叠上下文去你想去的地方。
<Teleport to="body"><Teleport>
component元组件和context
制作Tabs和Tab时,构想用户可以这样使用:
<Tabs v-model:selected="selected">
<Tab title="三国演义">“汝死后,汝妻子我养之,汝勿虑也。”</Tab>
<Tab title="西游记">“大师兄,师傅被妖精抓走了。”</Tab>
<Tab title="水浒传">“大郎,该吃药了。”</Tab>
<Tab title="红楼梦">“这个妹妹我曾见过的。”</Tab>
</Tabs>
第一步:需要检查Tabs子组件的类型,如果不是tab直接报错
setup(props,context){
const defaults = context.slots.default()
defaults.forEach((tag) => {
if (tag.type !== Tab) {
throw new Error('Tabs 子标签必须是 Tab')
}
}
第二步: 导航条遍历所有Tab的title
const titles = defaults.map((tab) => {
return tab.props.title
})
第三步: 展示选中的Tab组件
<component class="friday-tabs-content-item" :is="current" :key="selected"/>
const current =computed(()=>{
return defaults.find(tab=>tab.props.title===props.selected)
})
可以发现深入认识context和component元组件可以灵活的实现功能。
rollup打包文件
Rollup是一个 JavaScript模块打包器,可以将小块代码编译成大块复杂的代码
安装
npm install --global rollup
- 配置
// rollup.config.js
// 编译ts
import esbuild from 'rollup-plugin-esbuild'
// 编译vue
import vue from 'rollup-plugin-vue'
// 编译scss
import scss from 'rollup-plugin-scss'
import dartSass from 'sass';
// 打乱代码
import { terser } from "rollup-plugin-terser"
export default {
// 入口
input: 'src/lib/index.ts',
// 输出
output: [{
globals: {
vue: 'Vue'
},
name: 'Friday',
file: 'dist/lib/friday.js',
format: 'umd',
plugins: [terser()]
}, {
name: 'Friday',
file: 'dist/lib/friday.esm.js',
format: 'es',
plugins: [terser()]
}],
plugins: [
scss({ include: /\.scss$/, sass: dartSass }),
esbuild({
include: /\.[jt]s$/,
minify: process.env.NODE_ENV === 'production',
target: 'es2015'
}),
vue({
include: /\.vue$/,
})
],
}
好用的API--Element.getBoundingClientRect()
Element.getBoundingClientRect()
返回一个 DOMRect 对象,是包含整个元素的最小矩形(包括 padding 和 border-width)。该对象使用 left、top、right、bottom、x、y、width 和 height 这几个以像素为单位的只读属性描述整个矩形的位置和大小。
其他插件--引入marked和prismjs
- marked转换.md的文件
- 引入
$ npm add marked
- 配置 vite.config.ts ,建议单独放到一个md.ts文件中调用
// vite.config.ts
// @ts-nocheck
import path from 'path'
import fs from 'fs'
import marked from 'marked'
const mdToJs = str => {
const content = JSON.stringify(marked(str))
return `export default ${content}`
}
export default {
plugins: [
{
configureServer: [ // 用于开发
async ({ app }) => {
app.use(async (ctx, next) => { // koa
if (ctx.path.endsWith('.md')) {
ctx.type = 'js'
const filePath = path.join(process.cwd(), ctx.path)
ctx.body = mdToJs(fs.readFileSync(filePath).toString())
} else {
await next()
}
})
},
],
transforms: [{ // 用于 rollup // 插件
test: context => context.path.endsWith('.md'),
transform: ({ code }) => mdToJs(code)
}]
}
],
};
- prismjs
形成一个代码块并且带有语法高亮
- 引入
$ npm install prismjs
- 使用
prismjs默认提供了多个主题,可以去node_modules\prismjs\themes
下找一下
<template>
<pre class="language-html" v-html="html" />
</template>
<script>
const Prism = require('prismjs');
import 'prismjs/themes/prism-okaidia.css'
export default{
setup(){
const code = '<p>hello World</p>';
const html = Prism.highlight(code,'html');
return {html}
}
}
<script>
利用Custom Block(自定义块)和vite实现代码渲染器
待完成。。。