386 lines
8.7 KiB
Markdown
386 lines
8.7 KiB
Markdown
## Vue 3
|
||
### 1.环境搭建
|
||
**vue3项目创建**
|
||
|
||
> node版本要在15.0或更高版本
|
||
|
||
```shell
|
||
npm install vue@latest
|
||
```
|
||
|
||
这个指令将会安装并执行`create-vue`,它是Vue官方的项目脚手架工具。
|
||
|
||
> 创建时名称不要有大写
|
||
|
||
**开发环境**
|
||
`VScode` + `volar`插件
|
||
|
||
### 2.Vue API 风格
|
||
|
||
> vue的组件可以按两种不同的风格书写:**选项式API(vue2)** 和 **组合式API(vue3)**
|
||
> 大部分的核心概念在两种风格之间都是通用的。
|
||
|
||
**选项式API(Options 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 {}
|
||
}
|
||
}
|
||
```
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|