[发财UI] 开发日志
Vue3中的props和attributes
1
| <Button @click="click">按钮</Button>
|
Vue3中默认会将外部的@click="click"
传给组件的最外层元素,也就是<button></button>
上
1 2 3 4 5 6
| <template> <button :class="classes" :disabled="disabled" class="rich-button"> <span v-if="loading" class="rich-loadingIndicator"></span> <slot/> </button> </template>
|
属性绑定
通过inheritAttrs
可以选择是否继承组件外部的属性
1 2 3 4 5 6 7 8 9 10
| <template> <button>按钮</button> </template>
<script lang="ts"> export default { inheritAttrs:false } </script>
|
通过$attrs
绑定外部属性
1 2 3 4 5
| <template> <div> //默认属性会继承到div上 <button v-bind="$attrs">按钮</button> //手动将属性绑定到button上 </div> </template>
|
部分绑定属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <Button @click="click" size="" xxx="xxx">按钮</Button>
<template> <div :size="size"> //将size绑定到div上 <button v-bind="rest">按钮</button> //将rest绑定到button上 </div> //rest包含了click和xxx属性 </template>
<script lang="ts"> export default { setup(props, context){ const {size, ...rest} = context.attrs return {size, rest} } } </script>
|
props VS attrs
1、props 要先声明才能取值,attrs 不用先声明
2、props 声明过的属性,attrs 里不会再出现
3、props 不包含事件,attrs 包含
4、props 支持 string 以外的类型,attrs 只有 string 类型
项目开发中的CSS原则
-
组件的CSS不能用scoped
-
每个CSS的类最好加上前缀,防止相互覆盖
-
CSS最小影响原则
如何做loading动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| .rich-loadingIndicator { width: 14px; height: 14px; display: inline-block; margin-right: 4px; border-radius: 8px; border-color: $button-blue $button-blue $button-blue transparent; border-style: solid; border-width: 2px; animation: rich-spin 1s infinite linear; } @keyframes rich-spin { 0% { transform: rotate(0deg) } 100% { transform: rotate(360deg) }
|
具名插槽
给slot
一个name
,就可以精准的确定slot
对应的内容
1 2 3 4 5 6 7
| <header> <slot name="title"/> <span class="rich-dialog-close" @click="close"></span> </header> <main> <slot name="content"/> </main>
|
1 2 3 4 5 6 7 8
| <template v-slot:content> <strong>hi</strong> <div>hi2</div> </template> <template v-slot:title> <strong>加粗的标题</strong> </template>
|
Teleport 传送标签
在<Teleport to="目的地"></Teleport>
中的内容会被挂载到目的地下
1 2 3 4 5
| <template> <Teleport to="body"> ... </Teleport> </template>
|
Tabs
用JS获取插槽内容
1
| const defaults = context.slots.default()
|
在运行时确认子组件的类型
1 2 3 4 5
| defaults.forEach(tag => { if (tag.type !== Tab) { throw new Error('Tabs的子组件必须是Tab'); } });
|
getBoundingClientRect()
Element.getBoundingClientRect()
方法返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。
1
| const {width,height,top,left} = el.getBoundingClientRect()
|
异步加载
1 2 3 4 5 6 7
| setup(props) { const content = ref<string>('') import(props.path).then(result => { content.value = result.default }) return {content} }
|
Custom Blocks
首先需要先配置好vite
vite.config.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import * as fs from 'fs' import {baseParse} from '@vue/compiler-core'
export default { vueCustomBlockTransforms: { demo: (options) => { const {code, path} = options const file = fs.readFileSync(path).toString() const parsed = baseParse(file).children.find(n => n.tag === 'demo') const title = parsed.children[0].content const main = file.split(parsed.loc.source).join('').trim() return `export default function (Component) { Component.__sourceCode = ${ JSON.stringify(main) } Component.__sourceCodeTitle = ${JSON.stringify(title)} }`.trim() } } };
|
在需要变为demo的文件开头加上<demo>Title</demo>
1 2 3 4 5 6 7 8 9 10
| <demo> 常规使用 </demo> <template> <div> <Button>你好</Button> <Button theme="link">你好</Button> <Button theme="text">你好</Button> </div> </template>
|
就可以在引用该demo的组件中使用相关的api
1 2 3
| const Title = component.__sourceCodeTitle const soucreCode = component.__sourceCode
|
动态加载bug
yarn build
之后不能加载.md
文件,浏览器提示404
原因是使用了动态加载,rollup
不支持import()
时拼字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const history = createWebHashHistory(); const md = filename => h(Markdown, {path: `../markdown/${filename}.md`, key: filename}) export const router = createRouter({ history, routes: [ {path: '/', component: Home}, { path: '/doc', component: Doc, children: [ {path: '', redirect: '/doc/intro'}, {path: 'intro', component: md('intro')}, {path: 'install', component: md('install')}, {path: 'get-started', component: md('get-started')}, {path: 'switch', component: SwitchDemo}, {path: 'button', component: ButtonDemo}, {path: 'dialog', component: DialogDemo}, {path: 'tabs', component: TabsDemo}, ] }, ] });
|
调整过后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import intro from './markdown/intro.md' import getStarted from './markdown/get-started.md' import install from './markdown/install.md'
const history = createWebHashHistory(); const md = string => h(Markdown, {content: string, key: string}) export const router = createRouter({ history, routes: [ {path: '/', component: Home}, { path: '/doc', component: Doc, children: [ {path: '', redirect: '/doc/intro'}, {path: 'intro', component: md(intro)}, {path: 'install', component: md(install)}, {path: 'get-started', component: md(getStarted)}, {path: 'switch', component: SwitchDemo}, {path: 'button', component: ButtonDemo}, {path: 'dialog', component: DialogDemo}, {path: 'tabs', component: TabsDemo}, ] }, ] });
|