实现reactive的数据劫持功能
项目结构
vue3/index.js
import {reactive} from './vue3/reactivity'
const state = reactive({
title: '零三',
numArr: [1,2,3],
strArr: ['vue','react','html'],
objArr: [
{
name: 'mike',
hobby: ['足球','篮球','code'],
skill: [
{
text: '吹牛皮',
lv: 3
},
{
text: '撩妹',
lv: 0
}
]
}
]
})
// 直接获取
// console.log('-----state.title-----')
// console.log(state.title)
// 先获取state.objArr再取值[0]获取hobby
// console.log('-----state.objArr[0].hobby-----')
// console.log(state.objArr[0].hobby)
// 先获取state.objArr再取值[0]获取hobby[0]
// console.log('-----state.objArr[0].hobby[0]-----')
// console.log(state.objArr[0].hobby[0])
// push不会出发设置
// console.log('-----state.objArr[0].skill.push( {\n' +
// ' text: \'吃吃吃\',\n' +
// ' lv: 6\n' +
// '})-----')
// state.objArr[0].skill.push( {
// text: '吃吃吃',
// lv: 6
// })
// console.log(state)
//数组方法不会触发设置
// console.log('-----state.objArr[0].skill.splice(1,0)-----')
// console.log(state.objArr[0].skill.splice( 1,1))
// console.log(state.objArr[0].skill)
// 直接设置 触发修改
// console.log('-----state.title=零零三-----')
// state.title = '零零三'
// 设置一个不存在的对象,触发新增
console.log('-----state.title=零零三-----')
state.url = 'web03.cn'
// 触发修改 被新增的也是响应式的
state.url = 'https://web03.cn'
// 设置相同的基本类型数据 不会触发新增和修改
console.log('-----state.title=零零三-----')
state.title = '零三'
vue3/reactivity/index.js
import {reactive} from './reactive'
export {
reactive
}
vue3/reactivity/reactive.js
import {isObject} from "./shared/utils";
import {mutableHandle} from './mutableHandle'
function reactive(target) {
return createReactiveObject(target, mutableHandle)
}
function createReactiveObject(target,baseHandler) {
if (!isObject(target)){
return target;
}
return new Proxy(target, baseHandler);
}
export {
reactive
}
vue3/reactivity/mutableHandle.js
import {isObject, hasOwnProperty, isEqual} from "./shared/utils";
import {reactive} from './reactive'
const get = createGetter();
const set = createSetter();
function createGetter() {
return function get(target, key, receiver) {
const res = Reflect.get(target,key,receiver);
console.log('响应式获取',target[key])
// 如果还是obj,继续进行响应式设置
if (isObject(res)){
reactive(res)
}
return res;
}
}
function createSetter() {
return function set(target, key, value, receiver) {
const isKeyExist = hasOwnProperty(target,key),
oldValue = target[key],
res = Reflect.set(target, key, value, receiver);
// 是否存在 新老value是否一样
if (!isKeyExist){
// 新增
console.log('响应式新增','value='+value);
} else if (!isEqual(value,oldValue)){
// 修改
console.log('响应式修改','key='+key,'value='+value);
}
return res;
}
}
const mutableHandle = {
get,
set
}
export {
mutableHandle
}
vue3/reactivity/shared/utils.js
function isObject(value) {
return typeof value === 'object' && value !== null;
}
// 判断是否存在
function hasOwnProperty(target,key) {
return Object.prototype.hasOwnProperty.call(target,key);
}
// 是否相等
function isEqual(newValue,oldValue) {
return newValue === oldValue;
}
export {
isObject,
hasOwnProperty,
isEqual
}
实现原理和vue2的数据劫持相差不大,vue2使用Object.defineProperty需要处理数组方法,vue使用Proxy和Reflect,省去了很多不必要的操作
流程图