# vite+ts+vue3 **Repository Path**: CQLeiTao/vite-ts-vue3 ## Basic Information - **Project Name**: vite+ts+vue3 - **Description**: vite+ts+vue3 的项目demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-03-01 - **Last Updated**: 2023-03-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 相关知识点以及示例说明 ## 1. computed的使用 示例: [vite-demo\src\views\HomeView.vue]() ## 2. 动态组件 动态加载组件示例见 vite-demo\src\views\dynamicComponent ## 3. 异步加载组件Suspense 在需要使用的时候,才加载需要的组件。避免一开始就全部加载完,浪费性能。 示例:[vite-demo\src\views\syncComponent]() ## 4. 使用插槽 示例: [vite-demo\src\views\slotUse]() ## 5. 传送组件teleport 如果想让某个组件的Dom节点加载到另一个指定的节点下。可以使用vue内置传送组件 示例: [vite-demo\src\views\teltportDemo]() ## 6. 多层组件传参provide 当父子组件**嵌套过深**,且需要层层传递数据时,使用prop就会显得很麻烦。 **provide**可以在祖先组件中指定要提供给后代组件的方法或数据,而在后代组件中,可以使用inject来接受provide提供的数据方法。 子组件中可以直接修改爷爷组件中的值。如果不想被修改,可以使用vue的readOnly方法。`provide('word', readOnly(word))` 示例:[vite-demo\src\views\provideDemo]() ## 7. 兄弟组件传参 **方式一**:通过在两个组件中配合使用emit 和 props 实现数据传递。原理比较简单,但写起来很麻烦。就不写示例了 **方式二**:通过发布订阅模式实现。 实现一个发布订阅的类,命名为Bus,然后在兄弟组件中,一个订阅,另一个发布。 示例: [vite-demo\src\Bus.ts]() 注意这种模式含有on方法的执行要在emit方法的前面,因为要先订阅后,才能够进行发布。 兄弟组件中的使用示例: [vite-demo\src\views\brothers]() **方式三**:使用现成的工具Mitt ``` npm install mitt -S ``` 在main.ts中引入并初始化 ## 8. 自定义指令 ### vue2中指令的钩子函数 bind inserted update componentUpdated unbind ### vue3中指令的钩子函数 created: 元素初始化的时候 beforeMount: 指令绑定元素后调用, 只调用一次 mounted: 元素插入父级dom调用 beforeUpdate: 元素被更新之前调用 updated: 元素被更新之后调用 unmounted: 指令被移除之后调用,只调用一次 ### 指令定义方式 命名要求:必须以v开头 ```typescript import type { Directive } from 'vue' const vLeit: Directive = { created () { console.log('指令created') }, beforeMount() { console.log('指令beforeMount') }, mounted (...args) { console.log('指令mounted') console.log('入参有',args) }, beforeUpdate () { console.log('指令beforeUpdate') }, updated () { console.log('指令updated') }, unmounted() { console.log('指令unmounted') } } ``` 钩子函数的参数为: 第一个参数为元素的element节点。 第二个参数为指令上传递的一些参数信息 第三个参数为当前的虚拟节点信息 第四个参数为更新前的虚拟节点信息 示例:[vite-demo\src\views\directiveDemo]() 该示例中有两个应用的示例: 1.按钮通过用户权限展示 2.定义拖拽指令 ## 9.自定义一个vue插件 可以查看我的笔记 #### 以定义一个Loading插件为例 ##### 1.**定义插件import type {App} from 'vue'** ```typescript import type {App} from 'vue' export default { install(app:App) { ... } } ``` 必须返回一个install方法。该方法中就是插件的具体内容了 ##### 2.**引入该插件** 在系统入口文件 main.ts 中引入该插件,使用`app.use()`进行注册 ```typescript import Loading from './components/loading' app.use(Loading) ``` 这样每次打开页面是,都会执行这个install函数了 ##### 3.**编写loading组件** 使用defineExpose 暴露所提供的方法和属性 ```vue ``` ##### 4.**在插件文件中引入loading组件** 使用Vnode.component?.exposed?.xxx 获取到组件中定义的属性和方法 ```typescript import type {App, VNode} from 'vue' import loading from './index.vue' import { createVNode,render } from 'vue' export default { install(app:App) { // 将vue组件转换成一个vnode对象 const loadingVnode:VNode = createVNode(loading) // 挂载vnode到某个节点中 render(loadingVnode, document.body) // 获取到组件暴露的方法,并挂载到app上 app.config.globalProperties.$bgLoading = { show: loadingVnode.component?.exposed?.show, hide: loadingVnode.component?.exposed?.hide } } } ``` ##### 5.**在vue中使用该插件** 使用getCurrentInstance 获取到实例后,使用instance?.proxy?.\$bgLoading?.show()进行调用 ```typescript ``` 在vsCode中instance?.proxy?.\$bgLoading?.show() 会有错误提示,因为没有找到对应的类型。所以需要在main.ts中定义相关类型 ```typescript type bglod = { show: () => void, hide: () => void } declare module '@vue/runtime-core' { export interface ComponentCustomProperties { $bgLoading: bglod } } ``` ##### 6.**扩展优化,可以让某个元素块单独loading** 在loading插件中对挂载的父节点进行判断 ```typescript if (el) { // 将loading挂载到父级元素中,父级元素必须要有position,且为fixed、 absolute 或 relative // 判断是否有position属性,并获取值 const position = getComputedStyle(el, null).position if (position === 'static') { el.style.position = 'relative' } else if (position !== 'relative' && position !== 'absolute' && position !== 'fixed') { console.warn('loading元素的position必须为fixed、 absolute 或 relative') } // 挂载loading节点到el节点下 render(loadingVnode, el) } else { // 没有指定节点挂载vnode到body节点中 render(loadingVnode, document.body) } ``` ##### 7.**配置loading指令** 改造插件定义的文件 ```typescript import type { App, VNode, Directive, DirectiveBinding } from 'vue' import loading from './index.vue' import { createVNode, render } from 'vue' const createdLoading = () => { // 将vue组件转换成一个vnode对象 const loadingVnode: VNode = createVNode(loading) const show: any = (el: HTMLElement) => { if (el) { // 将loading挂载到父级元素中,父级元素必须要有position,且为fixed、 absolute 或 relative // 判断是否有position属性,并获取值 const position = getComputedStyle(el, null).position if (position === 'static') { el.style.position = 'relative' } else if (position !== 'relative' && position !== 'absolute' && position !== 'fixed') { console.warn('loading元素的position必须为fixed、 absolute 或 relative') } // 挂载loading节点到el节点下 render(loadingVnode, el) } else { // 没有指定节点挂载vnode到body节点中 render(loadingVnode, document.body) } loadingVnode.component?.exposed?.show() } const hide = () => { loadingVnode.component?.exposed?.hide() } return { show, hide } } type bglod = { show: (el?:HTMLElement) => void, hide: () => void } export default { install(app: App) { const loading = createdLoading() // 获取到组件暴露的方法,并挂载到app上 app.config.globalProperties.$bgLoading = { show: loading.show, hide: loading.hide } // 使用指令loading时,可能存在一个页面有多个loading。使用集合对这些loading进行收集 const loadingMap = new WeakMap() // 定义loading指令 const vBgloading: Directive = (el:HTMLElement, binding:DirectiveBinding) => { let nowLoading = loadingMap.get(el) // 查找是否有现成的loading if (!nowLoading) { // 如果当前loading不存在,新建loading nowLoading = createdLoading() loadingMap.set(el, nowLoading) } if (binding.value === true || binding.value === 'true') { nowLoading.show(el) } else { nowLoading.hide() } } // 将指令挂载到app上 app.directive('Bgloading', vBgloading) } } ``` 改造loading组件文件 ```typescript ``` 使用 ```html
box
``` [loading插件代码](https://gitee.com/CQLeiTao/vite-ts-vue3/tree/master/src/components/loading) [使用插件示例](https://gitee.com/CQLeiTao/vite-ts-vue3/blob/master/src/views/useLoading/index.vue) ## 10.h函数的使用 h函数接受三个入参: 1. type 元素的类型 ,必填 2. propsOrChildren 数据对象, 可以是(props, attrs, dom props, class 和 style) 3. children 子节点 基本用法: ```typescript h('div',{ id:'testDiv', class:'test-div', // 类名,可以是对象、字符串或数组 innerHTML: '我是复杂的div', // 内部内容 style: {color: 'red'}, // 样式,可以是对象或数组 "^width": '300', // 以"^"开头的,可以用于定义其他属性名 "^name": 'hahah', onclick: () => {console.log('我是点击事件')} // 定义事件开头要是on }) ``` 使用props: ... type Props = { name: string } const propsDiv = (props: Props, ctx:any) => { return h('div', { style: {color: 'blue'} }, props.name) } 使用emit: ... const emitDiv = (props: Props, ctx:any) => { return h('div', { style: {color: 'yellow'}, onclick: () => { ctx.emit('on-click', '我是emit传递的数据') } },props.name) } const getEmitVal = (emitVal:string) => { console.log('emitVal', emitVal) } 使用插槽: ... const slotDiv = (props:Props, ctx:any) => { return h('div', { style: {color: 'red'}, },ctx.slots.default()) } [代码示例](https://gitee.com/CQLeiTao/vite-ts-vue3/tree/master/src/views/hRender)