Vue3 Composition API
Vue3 Composition API
setup 函数的使用
setup 在 created 函数被完全实例化之前执行
setup
只能是同步
的,不能是异步的由于在执行
setup
时尚未创建组件实例,因此在setup
选项中没有this
。这意味着,除了props
之外,将无法访问组件中声明的任何属性 —— 本地状态、计算属性或方法。从
setup
返回的所有内容都将暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
setup(props,context){
// 第一个参数 props,响应式对象,不能被解构
// 第二个参数 context, attrs、emit、slots
//code
}
//ctx 和 2.x 中 this 并不完全一样,而是选择性地暴露了一些 property,主要有 `[attrs,emit,slots]`
setup(props,context){
const {attrs,slots,emit} = context
//attrs第一个参数是None-Props属性
//slot是一个方法,调用default方法会返回一个VNODE
//emit是各种事件
}
ref reactive 响应式引用
- ref 处理基础类型的数据
- reactive 处理数组、对象数据
- toRefs 处理对象解构之后的数据,无默认值
- toRef 处理对象解构之后的数据,有默认值
- readonly 只能读的响应式数据
ref 将给定的值创建一个响应式的数据对象并赋值初始值(int 或者 string)
reactive 可以直接定义复杂响应式对象。
原理:通过 proxy 对数据进行封装,当数据变化时,触发模板等内容的更新
ref 处理基础类型的数据
proxy : ‘r’ 变成 proxy( { value : ’r’ } ) 这样的一个响应式引用
const app = Vue.createApp({
template:`
<div>{{name}}</div>
`,
setup(props,context) {
const {ref} = Vue;
let name = ref('r'); //ref类似于定义变量
setTimeout(() => {
name.value = 'm'
},2000)
return {name}
}
})
reactive 处理非基础类型的数据
proxy : {name : ‘r’ }变成 proxy( { name: ’r’ } ) 这样的一个响应式引用
const app = Vue.createApp({
template:`
<div>{{nameObj[0]}}</div>
`,
setup(props,context) {
const {reactive} = Vue;
const nameObj = reactive([123]) //定义变量
setTimeout(() => {
nameObj[0] = 456
},2000)
return {nameObj}
}
})
reactive 创建的响应式数据解构后不再是响应式,toRefs 可以把响应式对象的所有属性也转换成响应式的,所以可以解构 toRefs 返回的对象,解构之后还是响应式数据;
reactive 是把普通对象转化成响应式对象,而 ref 是将基本类型数据包装成响应式对象。
ref 的本质
它的本质还是 reactive,当我们给 ref 函数传递一个值时,ref 函数会自动将 ref 转换成 reactive
ref(0) --> reactive({
value:0
})
- 所以在
script
中修改 ref 创建的数据时,必须通过.value
来获取 - 需要注意的是,如果是通过 ref 创建出来的数据,
在template
中使用的时候不用通过.value来获取
。因为 Vue 会自动
给我们添加.value 。
vue 是如何判断当前的数据是否是 ref 类型的?
通过当前数据的__v_ref 来判断的,如果有这个私有的属性,并且取值为 true,那么就代表是一个 ref 类型的数据。
开发者自己判断。isRef (数据),返回 true 或者是 false。
import {isRef} from 'vue'
ref 和 reactive 区别:
- 如果在 template 里使用的是 ref 类型的数据,那么 Vue 会自动帮我们添加.value
如果在 template 里使用的是 reactive 类型的数据,那么 Vue 不会自动帮我们添加.value- Vue 是如何决定是否需要自动添加.value 的
Vue 在解析数据之前,会自动判断这个数据是否是 ref 类型的,
如果是就自动添加.value, 如果不是就不自动添加.value- Vue 是如何判断当前的数据是否是 ref 类型的
通过当前数据的__v_ref 来判断的
如果有这个私有的属性,并且取值为 true, 那么就代表是一个 ref 类型的数据
toRefs 将 proxy({name:’r’}) 转化为 {name:proxy({value:’m’})}
const {toRefs = Vue;
const nameObj = reactive({name:'r'})
setTimeout(() => {
nameObj.name = 'm'
},2000)
const {name} = toRefs(nameObj) //可以看成赋值,这里是解构赋值
return {name}
toRefs 需要数据存在,toRef 不用
const {toRef} = Vue
const age = toRef(data,’age’)
Composition API Demo
// 关于 list 操作的内容进行了封装
const listRelativeEffect = () => {
const { reactive } = Vue;
const list = reactive([]);
const addItemToList = (item) => {
list.push(item);
}
return { list, addItemToList }
}
// 关于 inputValue 操作的内容进行了封装
const inputRelativeEffect = () => {
const { ref } = Vue;
const inputValue = ref('');
const handleInputValueChange = (e) => {
inputValue.value = e.target.value
}
return { inputValue, handleInputValueChange}
}
const app = Vue.createApp({
setup() {
// 流程调度中转
const { list, addItemToList } = listRelativeEffect();
const { inputValue, handleInputValueChange} = inputRelativeEffect();
return {
list, addItemToList,
inputValue, handleInputValueChange
}
},
template: `
<div>
<div>
<input :value="inputValue" @input="handleInputValueChange" />
<button @click="() => addItemToList(inputValue)">提交</button>
</div>
<ul>
<li v-for="(item, index) in list" :key="index">{{item}}</li>
</ul>
</div>
`,
});
computed方法生成计算属性
const app = Vue.createApp({
setup() {
const {ref,computed} = Vue
const count = ref(0)
const handleClick = () => {
count.value += 1
}
const countAddFive = computed(() => {
return count.value + 5
})
return {count,handleClick,countAddFive}
},
template: `
<div>
<span @click = 'handleClick'>{{count}}</span> -- {{countAddFive}}
</div>
`,
});
watch 和 watchEffect 的使用
- 第一个参数:要监听的数据,可以是 ref 或者是 reactive 响应式数据
- 第二个参数:监听到数据变化后执行的函数,这个函数有两个参数分别是新值和旧值
- 第三个参数:选项对象,deep 和 immediate
watch 具备一定的惰性 lazy
参数可以拿到原始和当前值
setup(){
const {ref,watch} = Vue
const name = ref('rmm')
watch(name,(currentValue,prevValue) => {
console.log(currentValue,prevValue)
})
return {name}
}
当用 reactive 时,第一个参数变成箭头函数
setup(){
const {reactive,watch,toRefs} = Vue
const nameObj = reactive({name:'rmm'})
watch(() => nameObj.name,(currentValue,prevValue) => {
console.log(currentValue,prevValue)
})
const {name} = toRefs(nameObj)
return {name}
}
可以侦听多个数据的变化,用一个侦听器承载
将第一个参数变成一个数组
watch([() => nameObj.name,() => nameObj.englishName] , () => {
//code
})
watchEffect 侦听器,偏向于 effect
- 是 Watch 函数的简化版本,也用来监视数据的变化,不同的是没有第二个参数
- 接受一个函数作为参数,监听函数内响应式数据的变化,当函数内响应式数据发生变化时函数会立即再调用一次
- 也是返回一个取消监听的函数
立即执行,没有惰性, immediate
不需要传递要侦听的内容,自动会感知代码依赖,不需要传递很多参数,只要传递一个回调函数
不能获取之前数据的值
const {watchEffect} = Vue
watchEffect(() => {
console.log(nameObj.name)
})
生命周期函数
在 setup 中可以使用组件生命周期的钩子函数,但是要在函数名称前面加 on
并且函数名称首字母大写,例如:mounted 在 setup 中要写成 onMounted
,还要在 import 中导入使用的钩子函数
setup 在 beforeCreate 和 created 之间执行,对应的代码都可以在 setup 中执行,所以不需要再在 setup 中注册 beforeCreate 和 created
onUnmounted 相当于 2.x 版本中的 destoryed
新增钩子函数:onRenderTracked
onRenderTriggered
(都是在 render 函数被重新调用的时候触发,区别在于 onRenderTracked
在首次调用 render 时也会触发,而 onRenderTriggered
首次调用 render 时不会触发)
setup(){
const { OnBeforeMount,OnMounted,OnBeforeUpdate,OnUpdated,OnUnmounted,OnRenderTracked,OnRenderTriggered} = Vue
OnBeforeMount(() => {
console.log('OnBeforeMount')
})
OnMounted(() => {
console.log('OnMounted')
})
OnBeforeUpdate(() => {
console.log('OnBeforeUpdate')
})
OnUpdated(() => {
console.log('OnUpdated')
})
OnUnmounted(() => {
console.log('OnUnmounted')
})
// 每次渲染后重新收集响应式依赖
OnRenderTracked(() => {
console.log('OnRenderTracked')
})
// 每次触发页面重新渲染时自动执行
OnRenderTriggered(() => {
console.log('OnRenderTriggered')
})
}
provide,inject 的用法
父子组件传值,父组件用 provide ,子组件用 inject 接受
const app = Vue.createApp({
setup(){
const {provide} = Vue
provide('name','rmm') //如果将此行注释,则显示 hello ,不注释显示 rmm
return {}
},
templete:`
<div>
<child />
</div>
`
})
app.component('child',{
setup(){
const {inject} =Vue
const name = inject('name','hello') //hello 作为默认值
return {name}
},
templete;`<div=>{{name}}</div>`
})
改进一下,如果子组件想修改父组件的数据怎么办
const app = Vue.createApp({
setup(){
const {provide,ref,readonly} = Vue
const name = ref('rmm')
provide('name',readonly(name)) //加 readonly 是为了防止 子组件里直接name.value = 'sb'
provide('changeName',(value)=>{
name.value = value
})
return {}
},
templete:`
<div>
<child />
</div>
`
})
app.component('child',{
setup(){
const {inject} =Vue
const name = inject('name')
const changeName = inject('changeName')
const handleClick = () => {
changeName('lee')
}
return { name , handleClick }
},
templete;`<div @click="handleClick">{{name}}</div>`
})
模板 ref 的用法
Composition API 的语法下,获取真实的 DOM 元素节点
const app = Vue.createApp({
setup(){
const {ref,onMounted} = Vue
const hello = ref(null) //这里的a一定是和标签中的ref值对应,然后给ref传null
onMounted(() => {
console.log(hello.value)
})
return { hello }
},
templete:`
<div>
<div ref="hello">hello world</div>
</div>
`
})