1 创建vue3项目(首先要确保@vue/cli版本在4.5.0以上)
vue --version ## 检查版本
vue install -g @vue/cli ## 全局安装vue脚手架
vue create vue_test (项目名) ## 创建项目
2 相较于vue2的变化
-
不再引入Vue构造函数,而是引入一个叫做createApp的工厂函数
-
template中不再需要根标签
3 SetUp
- setup的执行时机:在beforeCreate之前执行一次,this是undefined
- setup的参数:
- props:值为对象,包括组件外部传递过来,且组件内部声明接受了的属性
- context:上下文对象 - attrs 相当于 this.$attrs,props中没有接受的属性都会放到attrs中
- slots 收到的插槽内容,相当于this.$slots
- emit 相当于 this.$emit
4 ref函数(类似react中的UseState)
直接在setup中定义的数据不能响应式的改变,我们需要将数据交给ref
- 作用: 定义一个相应式的数据
- 语法: const xxx = ref(initValue)
- js中操作数据 xxx.value
- 响应式依靠Object.defineProperty()中的get和set完成
5 reactive函数
- 作用:类似于ref作用是定义一个对象类型的响应式数据类型
- 语法: const xxx = reactive(源对象)
- 可以处理深层对象数据
- 相应式基于ES6的Proxy实现,通过代理队形操作源对象内部数据进行操作。
!! 有一点需要注意,reactive定义的数组和对象直接赋值[],和{}是没有作用的,数组将length设为0,对象暂时没有什么好办法,循环delete所有项是可以或者用ref定义也可以删
6 computed函数
与vue2中的computed功能一致,并且可以在vue3中继续使用computed配置项,但是不推荐使用。
let fullName = computed(() => {
return persion.firstName + '-' + persion.lastName
})
7 watch函数
有三个参数,
- 第一个参数代表要监控的值
- 第二个参数是回调值变化之后调用的回调函数,回调函数也有两个参数第一个是新值,第二个是旧值
- 第三个参数是watch的配置项
- 监控的是一个ref定义的数据时
watch(num,(newValue, oldValue)=>{ console.log(newValue, oldValue) })
- 监控多个ref定义的数据时,将第一个参数写为一个数组
watch([num1, num2],(newValue, oldValue)=>{ console.log(newValue, oldValue) })
回调函数的参数得到的值也是一个数组
- 监控reactive定义的数据时
watch(persion, (newValue, oldValue) => { console.log(newValue, oldValue) }) // 监控的是reactive定义的数据时,oldvalue无法正常获取, // 并且强制开启深度监控,即deep配置失效
watch(() => persion.firstName, (newValue, oldValue) => { console.log(newValue, oldValue) }) // 监控reactive定义的数据的某个属性时,可以正常获取oldValue,但是参数要写成函数的形式
8 watchEffect函数
类似watch,但是不需要知名监控的属性,也不需要指定监控的回调,代码中含有的属性变化时就执行回调,有点像computed,但时watch注重的时过程不需要写返回值。
watchEffect(() => {
console.log(num.value)
})
9 生命周期钩子
beforeCreate ===> setup()
created ===> setup()
beforeMount ===> onBeforeMount
mounted ===> onMounted
beforeUpdate ===> onBeforeUpdate
updated ===> onUpdated
beforeUnmount==> onBeforeUnmont // 原beforeDestroy
unmounted ===> onUnmounted // 原destroyed
10 自定义hooks函数
类似于vue2中的mixin,将setup中的代码封装起来,之后其他的文件调用这些代码时,使用自定义hooks。
11 toRef
- 作用: 创建一个ref对象,其value值指向另一个对象中的某个属性
- 语法: const name = toRef(person, 'name')
- 应用:将响应式的对象单独提供给外部使用
setup () { let person = reactive({ firstName: '张', lastName: '三' }) return { // 如果想要使用person中的数据通常需要把person返回出去 firstName: toRef(person, 'firstName') // 使用toRef可以直接返回内部的数据 } }
12 shallowReactive 与 shallowRef
- shallowReactive: 只处理对象最外层属性的相应式(浅响应式)
- shallowRef:只处理基本数据类型的响应式,不仅行对象的响应式的处理
13 readonly 与 shallowReadonly
- readonly: 让一个响应式的数据变为只读 (深只读)
- shallowReadonly: 让一个响应式的数据变为只读 (浅只读)
14 toRaw 与 markRaw
- toRaw: 将reactive生成的响应式的的对象转为普通对象
- markRaw: 标记一个对象时其用于不会再成为相应式的对象(提高性能和复杂第三方库使用)
15 customRef
创建一个自定义的ref,并对其以来项跟踪和更新触发进行显式控制(不懂。。。)
16 provide 与 inject
实现祖孙组件间的通信
- 祖先组件通过provide传递数据,子孙组件通过inject接收数据
// 祖先组件
setup () {
provide('msg', 100)
...
}
// 子孙组件
setup () {
const msg = inject('msg')
return {
msg
}
}
- 子孙组件向祖先组件传值
// 祖先组件
setup () {
const handleMsg = (value) => {
console.log('孙子传递的数据', value)
}
provide('handleMsg', handleMsg)
...
}
// 子孙组件传值
setup () {
const handleMsg = inject('handleMsg')
const Fn = () => {
handleMsg(100)
}
}
17 响应式判断
- isRef:检查一个值是否是ref对象
- isReactive: 检查一个对象是否是由reactive创建的响应式代理
- isReadonly: 检查一个对象时否时由readonly创建的只读代理
- isProxy:检查一个对象是否是由reactive或者readonly方法创建的代理
18 Teleport
能将html结构移动到指定位置,使用to属性指定
1 setup script
vue的语法糖
通常我们使用composition API的时候需要使用 setup 钩子,然后还要把页面上要使用的变量return返回出去,例如:
<template>
<div>{{name}}{{age}}{{persion}}</div>
<button @click="sayHello">说话</button>
<div>computed: {{ persion.fullName }}</div>
<div>{{ num }}</div>
<button @click="add">加1</button>
<div>{{ num2 }}</div>
<button @click="change">添-</button>
</template>
<script>
import { ref, reactive, computed } from 'vue'
export default {
name: 'HelloWorld',
setup() {
let name = ref('lyk')
let age = ref(18)
let num = ref(1)
let num2 = ref(2)
let persion = reactive({
firstName: '张',
lastName: '三'
})
persion.fullName = computed(() => {
return persion.firstName + '-' + persion.lastName
})
function sayHello() {
alert(`我叫${name.value},我${age.value}岁了,
${persion.firstName}---${persion.lastName}`)
}
function add() {
num.value++
num2.value++
}
function change() {
persion.firstName += '-'
persion.lastName += '+'
}
return {
name,
age,
persion,
sayHello,
num,
num2,
add,
change
}
}
}
</script>
<style scoped>
</style>
使用起来很麻烦。
<template>
<div>{{name}}{{age}}{{persion}}</div>
<button @click="sayHello">说话</button>
<div>computed: {{ persion.fullName }}</div>
<div>{{ num }}</div>
<button @click="add">加1</button>
<div>{{ num2 }}</div>
<button @click="change">添-</button>
</template>
<script setup>
import { ref, reactive, computed } from 'vue'
const name = ref('lyk')
const age = ref(18)
const num = ref(1)
const num2 = ref(2)
const persion = reactive({
firstName: '张',
lastName: '三'
})
persion.fullName = computed(() => {
return persion.firstName + '-' + persion.lastName
})
const sayHello = () => {
alert(`我叫${name.value},我${age.value}岁了,
${persion.firstName}---${persion.lastName}`)
}
const add = () => {
num.value++
num2.value++
}
const change = () => {
persion.firstName += '-'
persion.lastName += '+'
}
</script>
<style scoped>
</style>
这样就简单很多了。
现在来详细的说一下优点
- 组件不需要在components里注册,引入之后直接在页面上使用即可
- 属性方法不需要人return返回
- 新增了defineProps、defineEmit和useContext,但是useContext后来又被弃用了,用useSlots和useAttrs来代替,总共四个新的api,顾名思义defineProps充当props接受父组件的参数,defineEmit充当$emit自定义事件来给父组件传参,useSlots用于获取插槽数据,useAttrs可以获取到attrs的数据
// 首先是defineProps,父组件跟vue2完全没区别 // 子组件: import { defineProps } from 'vue' const props = defineProps({ xxx: { type: xxx, required: true/false } })
// 然后是defineEmits 父组件同样没区别 // 子组件: import { defineEmits } from 'vue' const emit = defineEmits('fn') // 先定义一个事件 const click = () => { emit('fn', '参数') // 然后触发这个事件 }