Options API
特点
对应的属性中编写对应模块
弊端
options API
- data
- props
- components
- methods
- computed
- watch
- created
- mixins
- 当我们实现某一功能时候 这个功能对应的代码逻辑会被拆分到各个属性中
- 当我们组件变得更大 更复杂时,逻辑关注点的列表就会增长 那么同一个功能的逻辑就会被拆分的很分散
- 对于一开始没有编写这些组件的人来说 这个组件代码逻辑会难以阅读
- 当我们处理单个逻辑关注点 需要不断跳到相应的代码块中
- 这种方法一个变量的各种操作会分布的太多地方 导致不好寻找维护
Composition API
特点
将同一个逻辑关注点相关的代码 收集在一起
setup函数
本质:setup函数可以用它代替之前所编写的大部分选项(methods,computed,wact,data,生命周期) 好处:
- 把一个变量的操作都放在一起 方便维护
- 可以把一个变量的操作抽取出一个函数(hooks) 方便维护
setup 函数的参数(props,context)
props:
他其实就是父组件传递过来的属性 会被放到props对象中 我们在setup中使用 那么可以直接从props参数获取
- 对于定义props类型 和vue2一样 在props选项中定义
- 在template中依旧正常使用
- 如果在setup函数中使用props 不可以通过this获取
- props 在setup函数中直接作为参数 可以直接使用
<template>
<h2>{{name}}</h2>
</template>
export default {
props: {
name: String
},
setup(props) {
console.log(props.name)
// 也可以通过watch监听到
watchEffect(()=>{
console.log('name',props.name)
})
}
但是千万不要对props进行结构赋值 这样会失去他的响应式
setup({name}) {}
}
context :它包含三个属性
- attrs:所有非props的属性
- slots:父组件传递过来的插槽(在渲染函数(render)返回时会有作用 )
- emit:当我们组件内部需要发出事件(因为setup中没有this 所以不能用this.$emit来发出事件)
export default {
setup(props,context) {
context.attrs
context.slots
context.emit
}
有很多理由将 props 作为单独的第一个参数而不是将其包含在上下文中:
- 组件使用props 比其他 property 更常见,并且很多情况下组件仅使用 props。
- 将 props 作为单独的参数可以使单独键入更容易,而不会弄乱上下文中其他 property 的类型。
- 这也使得在具有 TSX 支持的 setup、render 和普通功能组件之间保持一致的签名成为可能。
setup 函数需要有返回值 他的返回值主要用于
- setup的返回值可以在template中被使用
- 通过setup的返回值来代替data选项 在setup中定义一个变量 默认情况下 vue并不会跟踪他的变化 来引起界面的响应式操作 vue 父子组件传值规范 单向数据流 父组件给子组件传值 不可以子组件props直接修改值 因为如果修改了 会直接修改到父组件的值 这就叫单向数据流 如果真的想修改props 得通过事件监听传递给父组件 然后修改父组件的值
对于为什么setup中为什么不能使用this
官方文档上解释为 因为setup里面的this 并没有指向当前组件实例 并且在setup被调用之前 data computed method等都没有被解析 所以无法在setup中获取到this 但是 阅读过vue源码上解析发现 并不是文档描述的那样 在阅读源码调用过程中
- 他先调用createComponentInstance 创建组件实例
- 调用setupCpmponent 初始化component内部的操纵
- 调用setupStatefulComponent初始化有状态的组件
- 在setupStatefulComponent函数中取出setup函数
- 通过callWithErrorHanding的函数中执行setup 从上面的源码可以看出来 组件instance(实例) 肯定是在执行setup函数之前就被创建出来了 但是执行setup的时候没有用apply/bind/call的方法来绑定this调用 而是直接调用 所以说setup中没有this(组件实例)
Reactive API (定义复杂类型响应式数据)
1.定义复杂类型(Object ,Array) 的数据 如果传入一个普通类型的数据 他会报一个警告 为什么用Reactive会把数据变成响应式呢?
因为我们使用Reactive函数处理我们的数据之后会被劫持进行收集依赖 当数据发生改变的时候(再次被使用)她就会被收集依赖然后进行对应的响应式操作(更新页面)
事实上我们在Options 里面编写data选项 也是将内部交给了reactive函数 将变成了对应的响应式对象 在Composition中相当于自己手动使用Reactive进行数据处理
当一个Reactive 被结构后 将会变成一个普通的值 失去响应式
const state = reactive({ name:'xsh', age:19 })
- isProxy 检查对象是否为reactive/readonly创建的proxy
- isReactive 检查对象是否有reactive创建的响应式代理 如果该代理为readonly建的 但是包裹了一层reactive创建的 它也会返回true
- isReadonly 检查对象是否是有readonly创建的
- toRaw 返回reactive/readonly 代理的原始对象 (不建议对原始对象的直接操作)
- shallowReactive 创建一个响应式代理 他跟踪其自身的property的响应式 但是不执行嵌套对象的深层次响应式转换(深层还是原生对象)
- shallowReadonly 创建一个proxy 使其自身property为制度 但不执行嵌套对象的深度只读转换(深层还是可读可写的)
Ref API (定义响应式数据)
- 在vue3中提供的响应式ref对象会返回一个可响应式对象 相当于吧定义的值放到了ref里面 定义简单普通(Srting,Number,Boolean)类型数据 在使用变量的时候 直接变量.value即可拿到 但是在template中不需要使用.value 因为template在使用ref的时候 vue会进行自动解包(取出其中的value值) 模版中的解包 在vue官方版本是一个浅层的解包 但是现在看来他已经不一致了 如果把这个定义的ref放到一个新定义的对象里面(进行深层次嵌套) 在vue3.1版本他是不会给你自动解包的 3.1以后的版本他是可以的 。即使你是操作这个数据 她也会自动解包。 但是如果定义到新对象里面嵌套的ref 进行数据操作 他就不会给你自动解包了使用的时候需要加上**.value获取值了** 这块自我感觉 可能是vue3一个遗漏的点
- 可以通过ref获取绑定的元素
<h2 ref="titleRef"></h2>
获取到元素/也可以获取到组件的ref 也就是组件实例
conts titleRef=ref()
console.log(titleRef.value)
- toRefs/toRef
toRefs
因为我们使用ES6的解构赋值 会对reactive返回的对象进行解构获取值 那么之后无论修改结构后的变量 还是修改reactive返回的变量 数据将都不是响应式的 这个时候vue就提供了toRefs这个函数 可以将reactive返回的对象中的属性转换成ref 那么我们将进行结构出来的name和age都是ref的 这种做法相当于state.name和ref.value之间进行建立了连接 任何一个修改都会有互相变化
const state = reactive({
name:'xsh',
age:12
})
const {name,age}= toRefs(state)
toRef
如果我们希望只转换reactive对象中的属性为ref 那么可以用toRef
const name = toRef(state,'name')
- unref
如果我们想获取一个ref引用中的value 也可以通过unref的方法
它内部会先判断是不是一个ref 如果是 则返回ref.value 否则返回参数本身
这是 val = isRef(val) ? val.value : val 的语法糖函数; - idRef 判断是否为ref
- shallowRef 创建一个浅层的ref对象 修改shallowRef对象内部属性值 将不会触发响应式
- triggerRef 手动触发和shallowRef相关联的副作用(也就是响应式)
const info = shallowRef({name:'xsh'})
这样修改name他不会触发响应式
info.value.name='222'
手动触发
rtiggerRef(info)
readonly 创建只读对象
使用readonly 会返回原始对象的只读代理(也就是他依然是一个proxy 但是这个proxy的set方法被劫持 并且不能对其进行修改)只能修改原始对象info 的值 如果担心子组件的props会被修改 使用readonly创建对象 就不会被修改并且给予警告
总结:ref和reactive使用场景
- ref ref也可以定义复杂类型数据
- 定义本地的简单数据 2.** 定义从网络请求中获取的数据** 因为如果使用reactive的话 他只能通过遍历来给定义的reactive赋值 这样很浪费性能 如果使用ref的话 可以直接通过ref.value进行全量赋值
- reactive
- 定义本地复杂数据
- 定义多个数据之间有关系/联系的数据(集合的数据 组合在一起有特定的作用 有聚合的意义) 类似于上面的name 和age 组合一起就是一个人的自我介绍