vue3源码剖析 02
学习目标
- composition-api体验
- Vue3响应式源码学习
- 响应式原理:Vue2 vs Vue
- 造轮子之旅
composition-api
文档
https://vue-composition-api-rfc.netlify.com
初体验
<div id="app"><h1>composition-api</h1><p @click="add">{{state.counter}}</p><p>{{state.doubleCounter}}</p></div><script src="../dist/vue.global.js"></script><script>const { createApp, reactive, computed } = Vueconst app = createApp({setup () {const state = reactive({counter: 0,doubleCouter: computed(() => counter * 2)})const add = () => {data.counter++}return { state, add }},}).mount('#app')</script>
更好的逻辑复用和代码组织
<meta charset="UTF-8"><script src="../dist/vue.global.js"></script><div id="app"><h1>logic reuse</h1></div><script>const { createApp, reactive, onMounted, onUnmounted, toRefs } = Vue;// 鼠标位置侦听function useMouse () {// 数据响应化const state = reactive({ x: 0, y: 0 })const update = e => {state.x = e.pageXstate.y = e.pageY}onMounted(() => {window.addEventListener('mousemove', update)})onUnmounted(() => {window.removeEventListener('mousemove', update)})// 转换所有key为响应式数据return toRefs(state)}// 事件监测function useTime () {const state = reactive({ time: new Date() })onMounted(() => {setInterval(() => {state.time = new Date()}, 1000)})return toRefs(state)}// 逻辑组合const MyComp = {template: `<div>x: {{ x }} y: {{ y }}</div><p>time: {{time}}</p>`,setup () {// 使用鼠标逻辑const { x, y } = useMouse()// 使用时间逻辑const { time } = useTime()// 返回使用return { x, y, time }}}createApp(MyComp).mount('#app')</script>
对比mixins,好处显而易⻅:
- x,y,time来源清晰
- 不会与data、props等命名冲突
- 更好的维护性
更好的类型推断
Vue最初选项API中存在大量this上下文,对TypeScript类型推断很不友好。在composition-api中仅利用纯变量和函数,规避了对this的使用,自然的拥有良好的类型推断能力。
Vue3中响应式源码学习
测试代码
<div id="app">{{foo}}</div><script src="../dist/vue.global.js"></script><script>const { createApp } = VuecreateApp({data () {return {foo: 'foo'}}}).mount('#app')</script>
整体流程
applyOptions中对data选项做 响应式处理使用的是reactive函数
setupRenderEffect函数中 使用effect函数做依赖收集
响应式原理:vue2 vs vue
数据变化可侦测,从而对使用数据的地方进行更新。
vue2的方式
Object.defineProperty()
// 拦截每个key,从而可以侦测数据变化function defineReactive (obj, key, val) {Object.defineProperty(obj, key, {get () {return val},set (v) {val = vupdate()}})}function update () {console.log(obj.foo);}const obj = {}defineReactive(obj, 'foo', 'foo')obj.foo = 'foooooooo'
vue3的方式
Proxy
// 代理整个对象,从而侦测数据变化function defineReactive (obj) {return new Proxy(obj, {get (target, key) {return target[key]},set (target, key, val) {target[key] = valupdate()}})}function update () {console.log(obj.foo);}const obj = {}const observed = defineReactive(obj)observed.foo = 'foooooooo'
Vue2 vs Vue3
vue2中需要递归遍历对象所有key,速度慢
// 1.对象响应化:遍历每个key,定义getter、setterfunction observe (obj) {if (typeof obj !== 'object' || obj == null) {return}const keys = Object.keys(obj)for (let i = 0; i < keys.length; i++) {const key = keys[i]defineReactive(obj, key, obj[key])}}function defineReactive (obj, key, val) {observe(val)Object.defineProperty(obj, key, {get () {return val},set (newVal) {if (newVal !== val) {observe(newVal)val = newValdep.notify()}}})}
数组响应式需要额外实现
// 数组响应化:覆盖数组原型方法,额外增加通知逻辑const originalProto = Array.prototypeconst arrayProto = Object.create(originalProto);['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].forEach(method => {arrayProto[method] = function () {originalProto[method].apply(this, arguments)dep.notify()}})
新增或删除属性无法监听,需要使用特殊api
Vue.set(obj, 'foo', 'bar')Vue.delete(obj, 'foo')
不支持Map、Set、Class等数据结构

