2023-11-17 10:54:23 +08:00
|
|
|
|
## 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({
|
2024-01-16 11:21:05 +08:00
|
|
|
|
message:{
|
|
|
|
|
type: String,
|
|
|
|
|
default: ''
|
|
|
|
|
},
|
|
|
|
|
info:String
|
|
|
|
|
})
|
|
|
|
|
// ts泛型的写法
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
title:string
|
|
|
|
|
}>()
|
|
|
|
|
// ts泛型带上默认值
|
|
|
|
|
withDefaults(defineProps<{
|
|
|
|
|
message:string,
|
|
|
|
|
arr: string[]
|
|
|
|
|
}>(), {
|
|
|
|
|
arr:() => ['A', 'B']
|
2023-11-17 10:54:23 +08:00
|
|
|
|
})
|
|
|
|
|
console.log('父组件传入的值:',props.message)
|
|
|
|
|
</script>
|
|
|
|
|
<template>
|
|
|
|
|
{{message}}
|
|
|
|
|
</template>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> 如果父组件传入的是响应式数据,则数据变化,子组件接受的数据也会变化
|
|
|
|
|
|
2024-01-16 11:21:05 +08:00
|
|
|
|
扩展:利用ts的泛型限定传参类型,**message参数为必传,arr为可选**。
|
|
|
|
|
|
|
|
|
|
```html
|
|
|
|
|
<script setup>
|
|
|
|
|
// 通过 deineProps “编译器宏函数” 接收父组件传递的数据
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
message:String,
|
|
|
|
|
arr?:string[]
|
|
|
|
|
}>()
|
|
|
|
|
console.log('父组件传入的值:',props.message)
|
|
|
|
|
</script>
|
|
|
|
|
<template>
|
|
|
|
|
{{message}}
|
|
|
|
|
</template>
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-11-17 10:54:23 +08:00
|
|
|
|
**子传父**
|
|
|
|
|
|
|
|
|
|
基本操作:
|
|
|
|
|
|
|
|
|
|
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 {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|