[发财UI] 开发日志
Vue3中的props和attributes
| 1
 | <Button @click="click">按钮</Button>
 | 
Vue3中默认会将外部的@click="click"传给组件的最外层元素,也就是<button></button>上
| 12
 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可以选择是否继承组件外部的属性
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | <template><button>按钮</button>
 </template>
 
 <script lang="ts">
 export default {
 inheritAttrs:false
 }
 </script>
 
 
 | 
通过$attrs绑定外部属性
| 12
 3
 4
 5
 
 | <template><div>  //默认属性会继承到div上
 <button v-bind="$attrs">按钮</button>  //手动将属性绑定到button上
 </div>
 </template>
 
 | 
部分绑定属性
| 12
 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动画
| 12
 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对应的内容
| 12
 3
 4
 5
 6
 7
 
 | <header><slot name="title"/>
 <span class="rich-dialog-close" @click="close"></span>
 </header>
 <main>
 <slot name="content"/>
 </main>
 
 | 
| 12
 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>中的内容会被挂载到目的地下
| 12
 3
 4
 5
 
 | <template><Teleport to="body">
 ...
 </Teleport>
 </template>
 
 | 
Tabs
用JS获取插槽内容
| 1
 | const defaults = context.slots.default()
 | 
在运行时确认子组件的类型
| 12
 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()
 | 
异步加载
| 12
 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
| 12
 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>
| 12
 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
| 12
 3
 
 | const Title = component.__sourceCodeTitleconst soucreCode = component.__sourceCode
 
 
 | 
动态加载bug
yarn build之后不能加载.md文件,浏览器提示404
原因是使用了动态加载,rollup不支持import()时拼字符串
| 12
 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},
 ]
 },
 ]
 });
 
 | 
调整过后:
| 12
 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},
 ]
 },
 ]
 });
 
 |