setup
组件选项,在组件创建之前执行,一旦 props 被解析,就将作为组合式 API 的入口
参数
- props
- 所有声明了的 prop,不管父组件是否向其传递了,都将出现在 props 对象中。其中未被传入的可选的 prop 的值会是undefined。
- setup 函数中的 props 是响应式的,当传入新的 prop 时,它将被更新,所以不能使用 ES6解构,会消除 prop 的响应性
- 如果需要解构 prop,可以在 setup 函数中使用 toRefs 函数来完成此操作
- 如果某个属性是可选的 prop,则传入的 props 中可能没有该属性。在这种情况下,toRefs 不适用 。需要使用 toRef 替代。
- context
- context.attrs —— // Attribute (非响应式对象,等同于 $attrs)
- context.slots —— // 插槽 (非响应式对象,等同于 $slots)
- context.emit —— // 触发事件 (方法,等同于 $emit)
- context.expose —— // 暴露公共 property (函数)
使用注意点
- setup 的调用发生在 data property、computed property 或 methods 被解析之前,就说在 setup函数中是无法 使用 data 和 methods 中的数据和方法的
- 在setup函数中定义的变量和方法最后都是需要 return 出去的,不然无法再模板中使用
- setup函数只能是同步的不能是异步的
- 生命周期钩子函数只能写在setup中
- provide/inject 只能写在setup中
ref和reactive
ref
- 在 Vue 3.0 中,通过ref 函数使任何响应式变量在任何地方起作用。即ref 为我们的值创建了一个响应式引用,访问时需要通过.value进行访问
reactive
- 返回对象的响应式副本
- reactive 将解包所有深层的 refs,同时维持 ref 的响应性
const count = ref(1)
const obj = reactive({ count })
// ref 会被解包
console.log(obj.count === count.value) // true
- 当将 ref 分配给 reactive property 时,ref 将被自动解包
const count = ref(1)
const obj = reactive({})
obj.count = count
console.log(obj.count) // 1
console.log(obj.count === count.value) // true
用法示例
<script>
import { ref, reactive } from "vue";
setup(){
const str = ref("vue3.0")
const obj = reactive ({name: '张三', age: 20})
function changeStr(){
str.value = "vue3.0牛逼"
}
function changeObj(){
obj.name= "李四"
}
return { str, obj, changeStr, changeObj }
}
</script>
区别
ref 和 reactive 本质我们可以简单的理解为ref是对reactive的二次包装, ref定义的数据访问的时候要多一个.value
- ref定义的数据需要通过.value才能使用,而reactive则不需要
- ref返回RefImpl对象(也叫ref对象),reactive返回Proxy对象。
- ref可以为基本类型添加响应式,也可以为引用类型添加响应式,reactive只能为引用类型添加响应式。
ps:只打算修改引用类型的一个属性,那么推荐用reactive,如果你打算变量重赋值(改变整个对象),那么一定要用ref。
原因在于jsonData尽管是响应式的,但是响应式的是它的属性,而不是它自身,重赋值它自身跟重赋值它的属性是两码事。所以,想在组合式API中让数据具备响应式,必须用ref,因为ref又对Proxy包装了一层,修改ref其实是修改它的value,它的value一定是响应式的,因此视图就正常更新了
更多详细讲解 https://www.jianshu.com/p/cfe25e757d0e
Provide / Inject
通常当我们需要将数据从父组件传递到子组件时,我们通过props实现。 设想一下这样的一个组件结构,这个结构中有一些嵌套层级很深的组件,我们想在内嵌层级很深的一个子组件中,调用顶层父组件的某些数据。在这种情况下,如果我们仍需要通过prop从父组件到子组件之间的组件链中传递数据,会进行很多次传递,这可能挺烦人的。
为了应对这种情况,我们可以使用provide与inject的组合。父组件可以无视组件层级的深度,作为所有子组件的依赖提供者。这个特性基于两部分:父组件通过provide选项提供数据,并且子组件通过inject选项使用所提供的数据。
provide
使用 provide 时,首先需要从 vue 显式导入 provide 方法
provide 函数定义property接收两个参数:
- name ( 类型)
- value
inject
使用 inject 时,也需要从 vue 显式导入
inject函数有两个参数:
- 要 inject 的 property 的 name
- 默认值 (可选)
ps:provide/inject 只能写在setup中,即不能写在异步函数和回调函数中,因为那时已经不在setup生命周期中了
用法
父组件
<template>
<div class="parent">
<h1>父组件的值:{{ primaryValue }}</h1>
<button @click="changeFatherValue(88)">父组件的值改变</button>
<chidCom></chidCom>
</div>
</template>
<script>
import chidCom from "../components/childCom.vue";
import { ref, reactive, provide } from "vue";
export default {
components: {
chidCom,
},
setup() {
let primaryValue = ref(4);
let objectValue = reactive({
name: "pzp",
age: "23",
});
provide("primaryValue", primaryValue);
provide("objectValue", objectValue);
console.log(primaryValue.value, objectValue.name);
let changeFatherValue = (val) => {
primaryValue.value = val;
objectValue.name = "pzpa";
};
provide('changeFatherValue', changeFatherValue)
return {
primaryValue,
objectValue,
changeFatherValue,
};
},
// methods: {
// changeFatherValue() {
// this.primaryValue = 8
// this.objectValue.name = 'pzpa'
// },
// },
};
</script>
子组件
<template>
<div>
<h1>父组件注入基础类型值:{{ fatherPrimary }}</h1>
<h1>父组件注入对象类型值:{{ fatherObject.name }}</h1>
<button @click="changeFatherValue(66)">子组件改变父组件值</button>
</div>
</template>
<script>
import { inject } from "vue";
export default {
setup() {
let fatherPrimary = inject("primaryValue");
let fatherObject = inject("objectValue");
let changeFatherValue = inject('changeFatherValue')
return {
fatherPrimary,
fatherObject,
changeFatherValue
};
},
};
</script>
效果展示
注意点
- 若需要provide/inject传递的值有响应性,被传递值的定义必须通过ref/reactive进行定义
- 当使用响应式 provide / inject 值时,建议尽可能将对响应式 property 的所有修改限制在定义 provide 的组件内部,即尽量在父组件内进行修改。若需要在子组件更新注入的数据,建议 provide 一个方法来负责改变响应式 property,比如上面的
changeFatherValue
- 如果要确保通过 provide 传递的数据不会被 inject 的组件更改,建议对提供者的 property 使用 readonly
getCurrentInstance
官文给出的该方法的描述为:getCurrentInstance 支持访问内部组件实例。特别强调了
getCurrentInstance 只暴露给高阶使用场景,典型的比如在库中。强烈反对在应用的代码中使用 getCurrentInstance。请不要把它当作在组合式 API 中获取 this 的替代方案来使用
计算属性-computed
接受一个 getter 函数,并根据 getter 的返回值返回一个不可变的响应式 ref 对象
const count = ref(1)
const plusOne = computed(() => count.value + 1)
console.log(plusOne.value) // 2
plusOne.value++ // 错误
或者,接受一个具有 get 和 set 函数的对象,用来创建可写的 ref 对象。
const count = ref(1)
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1
}
})
plusOne.value = 1
console.log(count.value) // 0
监听器-watch
侦听器数据源可以是返回值的 getter 函数,也可以直接是 ref
侦听单个数据源
// 侦听一个 getter
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
// 直接侦听ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
侦听多个数据源
const firstName = ref('')
const lastName = ref('')
watch([firstName, lastName], (newValues, prevValues) => {
console.log(newValues, prevValues)
})
firstName.value = 'John' // logs: ["John", ""] ["", ""]
lastName.value = 'Smith' // logs: ["John", "Smith"] ["John", ""]
ps:在同一个函数里同时改变这些被侦听的来源,侦听器仍只会执行一次,即多个同步更改只会触发一次侦听器
侦听响应式对象(深度嵌套对象)
useStore
在Vue3中,不能通过this.$store来获取使用vuex中存储的值,并且也不能使用getCurrentInstance来替代this进行使用。
此时若需要获取vuex的实例,就需要用到useStore
方法
ps:同理,对于路由的使用也是通过useRoute
和useRouter
方法
import { useRoute, useRouter } from "vue-router"
import { useStore } from 'vuex'
setup(props, context) {
// vue 3.0中vuex的使用
let store = useStore()
let storeValue = computed(() => store.state.user)
return {
storeValue
}
}
虚拟dom算法更新
- vue3修改了虚拟dom的算法(即diff算法 - 比对虚拟dom有没有变化)
- vue2需要diff所有的虚拟dom节点,而vue3参考了SVELTE框架的思想,先分层次-然后找不变化的层-针对变化的层进行diff,更新速度不会再受template大小的影响,而是仅由可变的内容决定。经过尤雨溪自己的测试,大概有6倍的速度提升。
数据的双向绑定
关于数据双向绑定的实现,vue2 采用了defineProperty,而vue3则采用了proxy。
优点:
- 使用proxy不污染源对象,会返回一个新对象,defineProperty是注入型的,会破坏源对象
- 使用proxy只需要监听整个源对象的属性,不需要循环使用Object.defineProperty监听对象的属性
- 使用proxy可以获取到对象属性的更多参数,使用defineProperty只能获取到监听属性的新值newvalue
/* vue2.0*/
var a = { b: 123, c: 444 };
Object.defineProperty(a, "b", {
set: function (newvalue) {
console.log("i am be set");
},
}); //只能获取到newvalue这个参数
/* vue3.0 */
var a = { b: 123, c: 444 };
var newa = new Proxy(a, {
set: function (target, key, newvalue) {
console.log(target, key, newvalue);
},
}); //可以获取到target,key,newvalue三个参数