Vue3源码模块化分
- runtime-core、runtime-dom、runtime-test这三个文件夹都是Vue 运行时相关的核心代码。
- compiler-core、compiler-dom、compiler-sfc这三个文件夹是 Vue 实现的编译器相关代码。
- server-renderer 是服务端渲染部分。
- vue 文件夹是整个项目打包构建之后的出口文件。
- reactive 文件夹是响应式系统部分
ref VS reactive
对于基本数据类型,函数传递或者对象解构时,会丢失原始数据的引用,换言之,我们没法让基本数据类型,或者解构后的变量(如果它的值也是基本数据类型的话),成为响应式的数据。
- 通过创建一个对象(Ref), 将原始数据保存在Ref的属性value当中,再将它的引用返回给使用者
- 通过toRefs(object)函数, 解决对象的解构丢失原始数据引用的问题
通过遍历对象,将每个属性值都转成Ref数据,这样解构出来的还是Ref数据,自然就保持了响应式数据的引用 toRefs 解决的问题就是,开发者在函数中错误的解构 reactive,来返回基本类型。const { x, y } = = reactive({ x: 1, y: 2 }),这样会使 x, y 失去响应式,于是官方提出了 toRefs 方案,在函数返回时,将 reactive 转为 refs,来避免这种情况。 - 总的来说, reactive 目前支持的类型为 Object|Array|Map|Set|WeakMap|WeakSet , refs支持的类型为基本数据类型, toRefs解决对象解构赋值后引用丢失问题
<template>
<div class="about">
<h1>This is an about page</h1>
<p>
<span>{{ obj1.fruit }}</span>
<button @click.prevent="updateObj1">更新1</button>
</p>
<p>
<span>{{ fruit }} -- {{ animal }}</span>
<button @click="updateObj2">更新2</button>
</p>
</div>
</template>
<script>
import { ref, toRefs, reactive } from 'vue'
// 修改对象1
function useObject1() {
let obj1 = reactive({ fruit: 'apple' })
let updateObj1 = () => obj1.fruit = obj1.fruit == 'apple' ? 'banana' : 'apple'
return {
obj1,
updateObj1
}
}
// 修改对象2
function useObject2() {
let obj2 = reactive({ fruit: 'apple', animal: 'monkey' })
let updateObj2 = () => obj2 = { fruit: 'pear', animal: 'snake' }
return toRefs(obj2)
}
export default {
name: 'About',
setup(props, context) {
const { obj1, updateObj1 } = useObject1()
const { fruit, animal } = useObject2()
return {
obj1,
updateObj1,
fruit,
animal
}
}
}
</script>
computed
计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 msg 还没有发生改变,多次访问 text 计算属性会立即返回之前的计算结果,而不必再次执行函数。
通过 computed 传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。或者传入一个拥有 get 和 set 函数的对象,创建一个可手动修改的计算状态。
<template>
<h1>{{ text }}</h1>
<button @click="change">count is: {{ state.count }}</button>
<p>Edit <code>components/HelloWorld.vue</code> to test hot module replacement.</p>
</template>
<script>
import { ref, reactive, computed } from 'vue'
export default {
name: 'HelloWorld',
props: {
msg: String
},
setup(props){
let state = reactive({count: 0})
let text = computed(() => {
return props.msg.split('').reverse().join('')
})
const change = () => state.count++
return { state, change, text }
}
}
</script>
effect
effect其实就是一个依赖收集函数,在它内部访问了响应式数据,响应式数据就会把这个effect函数作为依赖收集起来,下次响应式数据改了就触发它重新执行。
reactive返回的就是个响应式数据,这玩意可以和effect搭配使用。
首先要知道,effect函数会立即开始执行,再执行之前,先把effect自身变成全局的activeEffect,以供响应式数据收集依赖。
并且activeEffect的记录是用栈的方式,随着函数的开始执行入栈,随着函数的执行结束出栈,这样就可以维护嵌套的effect关系。
举个简单的栗子吧:
// 响应式数据
const data = reactive({ count: 0 })
// 依赖收集
effect(() => console.log(data.count))
// 触发上面的effect重新执行
data.count ++
就这个例子来说,data是一个响应式数据。
effect传入的函数因为内部访问到它上面的属性count了,
所以形成了一个count -> effect的依赖。
下次count改变了,这个effect就会重新执行