# 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
使用loading
```
在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)
}
使用插槽:
我是slot
...
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)