Compsition API
- 为什么有mixin和mixin的合并规则是怎样的
- 组件和组件之间存在着相同的代码逻辑, 我们希望对相同的代码进行逻辑抽取
- vue2和vue3版本中都使用了mixin来完成
- mixin的合并规则 3.1 data中的返回对象, 返回的对象默认情况下会进行合并,如果合并对象中的命名发生冲突,以组件自身的数据为准 3.2 生命周期中相同的钩子函数会被合并到数组中,数组中的逻辑都会被调用 3.3 值为对象的选项(methods, directives等),在methods的方法都会生效,如果键名相同, 以组件的键名为准
- 全局混入
- 如果某个选项 是所有组件都需要拥有的,那么我们可以在根组件使用全局混入的功能, 这样每个组件都会在初始化的时候被混入这个功能实现
app.mixin({ created() { console.log('全局的mixin') } })
- 如果某个选项 是所有组件都需要拥有的,那么我们可以在根组件使用全局混入的功能, 这样每个组件都会在初始化的时候被混入这个功能实现
为什么使用Compsition API
- optionsAPI的特点在实现一个功能时,这个功能的代码会被编写到各个功能模块中, 比如data, methods,filters等, 随着业务和功能复杂度的增加,模块中的逻辑代码就会变得越长,这样同一个功能的逻辑就会被拆得很分散,而且难以阅读和定位。这些碎片化的代码在理解和维护上面越来月困难,而且隐藏了潜在的问题逻辑。当我们在处理单个逻辑点时,需要不断的切换到模块中
这是我更加喜欢React的原因,而且Vue对于有追求的开发, 不涉及底层实现,真的毫无技术含量- 大组件optionApi页面
- 如果我们编写功能代码,同一个逻辑能够收集在一起,岂不是更好, 这就是CompisitionAPI想做以及能帮我们完成的事情.
认识CompisitionAPI
- 在Vue3中兼容了Vue2的写法基础上, 增加了setup函数
- setup其实就是Vue3在组件中的新增配置选项
- 只不过这个选项可以强大到可以替换之前编写的大部分的选项功能
- 比如 metods computed watch data 生命周期等
- 了解setup函数
-
setup有两个参数 props和context
- props就是父组件传递的子组件的属性被放在了props中,当在setup中使用的时候,可以在props上直接获取
- 另外一个参数时context,称为setupContext. 这个对象包含了三个属性
- attrs: 所有非props的attribute集合
- slots: f父组件传递过来的slot插槽集合
- emit: 组件想要内部发出事件的功能函数
-
setup的返回值
- setup的返回值可以在template模板中使用
- 也就是可以通过setup的返回值代替Vue2中的data
- setup中也可以返回一个执行函数替代Vue2中methods
setup(props, context) { let foo = 'foo' let counter = 0 //不具有响应式 const changeCounter = () => counter++ return { foo, counter, changeCounter } } - 但是对于Vue来说, 默认情况下
Vue并不会跟踪它的变化,来响应页面的更新。
-
setup中不可以使用this
- 这段表达的意思是
-
this没有指向当前的实例
-
在setup调用之前data,properrty computed或者methods都没有被解析
-
无法在setup中获取this
-
其实之前在描述setup不能使用this的原因是
组件的实例还没有被创建出来,但是在源码中可以看到setup函数执行之前组件的实例已经被创建。
-
-
响应式API
- reactive函数和ref函数
<template> <div>{{myName}}</div> <div>{{state.age}}</div> <div>需要value: {{info.name.value}}</div> <div>不需要value: {{info1.name}}</div> </template>setup(props, context) { // reactive响应式 传递的数据类型是对象或者数组(Object, Array) const state = reactive({ name: 'hawkeye', age: 18 }) // ref函数也是响应式的, 不过一般传递的是基本数据类型数据(Number,String,Boolean) const myName = ref('hawkeye') const info = { name: myName } const info1 = reactive({ name: myName }) return { state, myName, info, info1 } } - reactive为什么会是响应式?
- 因为我们在使用reactive函数处理我们的数据以后, 数据再次被使用时就会进行依赖收集
- 当数据发生变化时,所有收集的响应式依赖都是进行对应的响应式操作(比如页面跟新, 更改数 据等)
- 其实在Vue3中的data属性也是交给了reactive函数将其变成响应式的,而Vue2中使用的时 Object.defineProperty
- ref是怎样应用的?
ref会返回一个可变的响应式对象,该对象作为一个响应式的引用维护着内部的值- ref对象内部的值是通过
value的属性值进行维护 - 在
模板template中使用ref,Vue会自动帮助我们进行解包操作,所以我们并不需要在return使 用ref.value的方式 - 但是在
setup中它依然是一个引用的值,使用的时候通过ref.value来使用 - ref的解包功能是
浅层解包, 在ref放在reactive属性中,在模板中使用也会自动解包
- reactive函数和ref函数
-
readonly
- reactive和ref可以获取一个响应式的对象, 但是某些情况下我们在
数据传递给其他组件的时候,我们希望我们传递的数据只能被使用,不能被更改, 像props provide提供的数据等 - Vue3提供了readonly, readonly会返回一个对原生对象只读的代理,再开发中常见的readonly数据类型
- 普通对象
- reactive返回的对象
- ref返回的对象
- readonly的使用规则
-
readonly返回的对象都是不能被修改的
-
但是经过readonly处理的原对象是可以被修改的
const info = { name: 'hawkeye', age: 18 } const info1 = readonly(info) console.log(info1) const state = reactive({ name: 'hawkeye', age: 18 }) const state1 = readonly(state) console.log(state1) const name = ref('hawkeye') const name1 = readonly(name) console.log(name1) // info对象是不允许被修改的, 但是当obj对象被修改时,应用类型的关系 info的属性也被修改了,但是我们不能直接去对象info的属性进行修改 -
其本质就是再readonly函数返回的对象中对setter函数进行了劫持,但是被代理的对象并没有
-
修改readonly产生的警告
-
-
- reactive和ref可以获取一个响应式的对象, 但是某些情况下我们在
-
响应式API的判断
- isProxy(): 检查对象是否时reactive或者readonly创建的proxy对象
- isReactive(): 检查对象是否是有reactive创建出来的代理对象, 还有一种如果这个对象是由readonly代理的reactive的对象, 结果也会返回true
const info = reactive({name: 'hawkeye', age: 18}) console.log(isReactive(info)) // true console.log(isReactive(readonly(info))) // true - isReadonly():检查是否由readonly创建的只读代理对象
- toRaw(): 返回reactive或者readonly的代理原始对象
- shadowReactive(): 创建一个响应式代理,他跟踪自身property属性,但是不嵌套深层次的响应式转换
- shadowReadonly(): 创建一个proxy,使其自身是只读的,但是不嵌套深层次的响应式转换
-
toRefs
使用es6的解构特性, 对reactive返回的对象进行解构,都会让数据失去响应式,那么我们怎样可以让解构的数据也具有响应式呢?
- vue给我们提供了toRefs的函数, 可以将reactive返回对象中的属性都转换为ref
- 这种做法相当于reactive中的属性ref建立了链接, 任何一个修改都会引起另一个变化
- 如果我们只想修改reactive中的一个某个属性,可以使用toRef, toRef(reactiveObj, property)
const state = reactive({ name: 'haw', age: 18, height: 1.80 }) const {name, age} = toRefs(state) const height = toRef(state, 'height') console.log(height) // 响应式的ref对象 const changeName = () => { info.name = 'hawkeye' console.log(state) // 会发生变化 } return { ...toRefs(state), // 不涉及到变量的修改可以直接这样写 name, age, changeName }- isRef() 判断值是否未一个ref对象
- shallowRef(): 创建一个浅层的ref
- triggerRef():手动触发和shallowRef相关联的副作用
const info = shallowRef({name: 'haw'}) // 这种修改不死响应式的 const changeRef = () => { info.value.name = 'hawkeye' // 获取的时候是ref.value.property // 手动触发shallowRef的响应式 triggerRef(info) } return { info, changeRef }
-