meface/docs/article/web/js/vue3.md

386 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Vue 3
### 1.环境搭建
**vue3项目创建**
> node版本要在15.0或更高版本
```shell
npm install vue@latest
```
这个指令将会安装并执行`create-vue`它是Vue官方的项目脚手架工具。
> 创建时名称不要有大写
**开发环境**
`VScode` + `volar`插件
### 2.Vue API 风格
> vue的组件可以按两种不同的风格书写**选项式APIvue2** 和 **组合式API(vue3)**
> 大部分的核心概念在两种风格之间都是通用的。
**选项式APIOptions API**
使用选项式API我们可以用包含多个选项的对象来描述组件的逻辑例如`data`、`methods`、`created`、`mounted`、`computed`。选项所定义的属性都会暴露在函数内部的`this`上,它会指向当前的组件实例。
### 3.组合式API
#### 3.1 setup函数
- setup函数的执行时机beforeCreate钩子之前 自动执行
- setup写代码的特点是定义数据 + 函数 然后以对象方式return 后才可以在模板中使用
- `<script setup>`语法糖解决以对象的形式返回繁琐写法
- setup中的this无法找到组件实例此时this是一个undefined
#### 3.2 响应式数据
- reactive(): 声明对象类型的值,如数组
```html
<script setup>
import { reactive } from 'vue'
// reactive()的作用是接受对象类型数据的参数并返回一个响应式的对象
const obj = reactive({
name: '',
age: 90
})
</script>
```
- ref()函数:声明基础数据类型的值,
```html
<script setup>
import { ref } from 'vue'
const count = ref(0);
const setCount = ()=>{
// 在脚本区域修改ref产生的响应式对象数据必须通过.value属性
count.value++
}
</script>
```
#### 3.3 计算属性
`computed` 计算属性函数基本思想和vue2的完全一致组合式API下的计算属性只是修改了写法
````html
<script setup>
import { computed } from 'vue'
const count = ref(0);
const computedCountFlag =computed( ()=>{
// 返回基于响应式数据做计算后的值
return count.value > 10
})
</script>
````
注意:
1. 计算属性中不应该有“副作用”比如异步请求或是修改dom
2. 计算属性应该是只读的,避免直接修改计算属性的值
#### 3.4 watch
作用:侦听*一个或者多个数据* 的变化,数据(响应式的数据)变化时执行回调函数,额外参数:
1. `immediate`:立即执行
2. `deep`:深度侦听
> deep 默认机制通过watch监听的ref对象默认是千层监听的直接修改嵌套的对象属性不会触发回调执行需要开启deep选项
**精确监听**
````html
<script setup>
import { ref ,watch } from 'vue'
const person = ref({
name:'',
age:100
});
// 只有当age变化时在执行
watch(()=>person.value.age,(newAge,oldAge)=>{
console.log('age变化了')
})
</script>
````
#### 3.5 生命周期函数
Vue3中生命周期API选项式 VS 组合式)
| 选项式API | 组合式API |
| :------------------: | :-------------: |
| beforeCreate/created | setup |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
可多次执行函数
```html
<script setup>
import { onMounted } from 'vue'
onMounted(()=>{
console.log('组件挂载完毕1')
})
onMounted(()=>{
console.log('组件挂载完毕3')
})
onMounted(()=>{
console.log('组件挂载完毕2')
})
// log结果1/3/2
</script>
```
#### 3.6 组件通信
**父传子**
基本操作:
1. 父组件中给子组件绑定属性
2. 子组件内部通过**props**选项接收
```html
<script setup>
//引入子组件setup语法糖下局部组件无需注册直接可以使用
import sonCom from './sonCom'
let msg = 'this is app message'
</script>
<template>
<!-- 绑定属性 message-->
<sonCom :message="msg"></sonCom>
</template>
```
```html
<script setup>
// 通过 deineProps “编译器宏函数” 接收父组件传递的数据
const props = defineProps({
message:{
type: String,
default: ''
},
info:String
})
// ts泛型的写法
const props = defineProps<{
title:string
}>()
// ts泛型带上默认值
withDefaults(defineProps<{
message:string,
arr: string[]
}>(), {
arr:() => ['A', 'B']
})
console.log('父组件传入的值:',props.message)
</script>
<template>
{{message}}
</template>
```
> 如果父组件传入的是响应式数据,则数据变化,子组件接受的数据也会变化
扩展利用ts的泛型限定传参类型**message参数为必传,arr为可选**。
```html
<script setup>
// 通过 deineProps “编译器宏函数” 接收父组件传递的数据
const props = defineProps<{
message:String,
arr?:string[]
}>()
console.log('父组件传入的值:',props.message)
</script>
<template>
{{message}}
</template>
```
**子传父**
基本操作:
1. 父组件中给子组件标签通过`@`绑定事件
2. 子组件内部通过`$emit`方法触发事件
```html
<script setup>
//引入子组件setup语法糖下局部组件无需注册直接可以使用
import sonCom from './sonCom'
const getMessage = (msg)=>{
console.log(msg)
}
</script>
<template>
<!-- 绑定属性 message-->
<sonCom @get-message="getMessage"></sonCom>
</template>
```
```html
<script setup>
// 通过 deineEmits “编译器宏函数” 生成 emit 方法
const emit = deineEmits(['get-message'])
const sendMsg = ()=>{
emit('get-message','发送给父组件的数据')
}
</script>
<template>
<button @click="sendMsg">发送</button>
</template>
```
#### 3.7 模版引用
通过`ref标识`获取真实的dom对象或者**组件实例对象**。
示例:
```html
<script setup>
import {ref} from 'vue'
import TestCom from './test-com.vue'
// 1.调用ref函数得到ref对象相当于生成了一个容器一样用于存放dom对象
const h1Ref = ref(null)
const testRef = ref(null)
// 组件挂载完毕之后才能获取
onMounted(()=>{
console.log(h1Ref.value)//<h1>H1标签</h1>
console.log(testRef.value)//组件的代理对象
})
</script>
<template>
<!-- 2.通过ref标识绑定ref对象-->
<h1 ref='h1Ref'>H1标签</h1>
<TestCom ref='testRef'></TestCom>
</template>
```
`defineExpose()`
默认情况下在`<script setup>`语法糖下组件内部的属性和方法是不开放给父组件访问的可以通过defineExpose编译宏指定哪些属性和方法允许访问。
```html
<script setup>
import {ref} from 'vue'
const name = ref('test name')
const setName = (name='new name')=>{
name.value = name
}
// 为了防止组件内部的属性和方法被修改默认是不暴露的如果明确要提供给外部需要用defineExpose编译宏显示声明
defineExpose({
name,
setName
})
</script>
```
#### 3.8 provide和inject
作用和场景:**顶层组件向任意的底层组件传递数据和方法,实现跨层组件通信**。
基本操作:
1. 顶层组件通过`provide`函数提供数据
```javascript
// 传递的过程中保持响应式传入ref对象
provide('key',顶层组件中的数据)
```
2. 底层组件通过`inject`函数获取数据
```javascript
const message = inject('key')
```
**跨层传递方法**
场景:在底层组件需要修改顶层组件的数据,但单向数据流的原则,不建议直接修改传过来的数据。可以通过在顶层组件中提供一个修改数据的方法,传递给底层组件让其调用来修改数据。(谁的数据就由谁修改)
顶层组件:
```javascript
const setCount = ()=>{
count.value++
}
provide('setCount-key',setCount)
```
底层组件:
```javascript
const setCount = inject('setCount-key')
```
#### 3.9 获取组件实例
`getCurrentInstance` 的返回值是一个组件实例对象.
- 可以通过 `.ctx` 来访问该实例的上下文对象;
- 者通过 `.proxy `来访问该实例的代理对象。
两者的区别在于,通过` .ctx `访问的是真实的组件实例,而通过 `.proxy` 访问的是一个代理对象,该代理对象可以在模板中直接使用。
> getCurrentInstance 只能在 setup 或生命周期钩子中使用
```javascript
import { getCurrentInstance, onMounted} from 'vue'
export default {
setup() {
onMounted(() => {
const instance = getCurrentInstance()
console.log('实例', instance)
})
return {}
}
}
```