in分为对象和数组两种
对象的话用defineReactive方法,内部采用Object.defineproperty给对象里面的每个属性都设置set和get方法,用递归的方式进行劫持对象里面的属性。
数组的话就会对数组里面的7个方法进行重写,通过引用数组原型对象身上的方法,进行重写的。
项目目录:
public ->index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
你好
<div id="app">wa</div>
<script src="../dist/umd/vue.js"></script>
<script>
// console.log(Vue);
const instance = new Vue({
el: '#app',
data: {
a: 100,
b: { m: 10 },
c: 30,
d: [8, 9, 10, { x: 99 }]
}
})
// instance._data.c = { x: 100 }
//console.log(instance.$options)
</script>
</body>
</html>
state.js
import { observe } from "./observer/index.js"
export default function initState(vm) {
//console.log(vm)
const ops = vm.$options //{el:'#app',data:{}}
if (ops.props) {
initProps(vm)
}
if (ops.methods) {
initMethods(vm)
}
if (ops.data) {
initData(vm)
}
if (ops.computed) {
initComputed(vm)
}
if (ops.watch) {
initWatch(vm)
}
}
function initProps(vm) {
}
function initMethods(vm) {
}
function initData(vm) {
//console.log(vm.$options.data) //{a:100} //data.call调用data同时改变this指向
let data = vm.$options.data
vm.data = data = typeof data === 'function' ? data.call(vm) : data //方便获取数据
//观测数据
observe(data)
}
function initComputed(vm) {
}
function initWatch(vm) {
}
init.js
import initState from "./state"
export default function initMixin(Vue) {
Vue.prototype._init = function (options) {
const vm = this
vm.$options = options
//初始化状态
initState(vm)
}
}
index.js
import initMixin from "./init"
function Vue(options) {
//初始化
this._init(options)
}
//在原型上扩展_init方法 (加下划线表示私有的)
//调用initMixin
initMixin(Vue)
export default Vue
observer-> index.js
import arrayMethods from "./array";
class Observer {
constructor(value) {
//vue不推荐我们操作数组用索引方式,推荐用数组方法操作 push shift pop……
//
Object.defineProperty(value, '__ob__', {
enumerable: false, //不可枚举
configurable: false,
value: this
})
if (Array.isArray(value)) {
//给value添加一个属性__ob__ 指向Observe实例
//value是数组?
value.__proto__ = arrayMethods //value._proto ={_proto_:Array.prototype}
this.observeArray(value)
} else {
this.walk(value)
}
}
observeArray(value) {
value.forEach(item => observe(item));
//数组的每个元素也要观测
}
walk(value) {
const keys = Object.keys(value) //获取对象所有的属性
for (let i = 0; i < keys.length; i++) {
let key = keys[i]
let v = value[key]
defineReactive(value, key, v)
}
}
}
function defineReactive(data, key, value) {
//value 也有可能是对象,需要观测一下
observe(value)
console.log(data, key, value);
Object.defineProperty(data, key, {
get() {
console.log('获取值');
return value
},
set(newV) {
if (newV === value) return
observe(newV) //属性重新赋值也可能赋值一个对象
console.log('设置值'); //更新视图逻辑
value = newV
}
})
}
export function observe(data) {
if (typeof data !== 'object' || data === null) {
return;
}
return new Observer(data)
}
array.js
//Object.create() 返回值是{_proto_:Array.prototype}
let arrayMethods = Object.create(Array.prototype);
//重写数组方法 push pop shift unshift sort splice reverse 变异方法
// arrayMethods['push'] = function () {
// console.log('数据变了,更新视图')
// }
['push', 'pop', 'shift', 'unshift', 'sort', 'splice', 'reverse']
.forEach((method) => {
arrayMethods[method] = function (...args) {
console.log('数据变了,更新视图');
//对传入的args参数进行响应式处理
let inserted;
switch (method) {
case 'push':
case 'unshift':
inserted = args;
break;
case 'splice': inserted = args.slice(2)
}
if (inserted) {
//对inserted每个元素进行观测
this.__ob__.observeArray(inserted)
}
return Array.prototype[method].call(this, ...args)
}
});
export default arrayMethods //{_proto_:Array.prototype,push:function……}
版本信息:
package.json
{
"name": "vue-advance",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "rollup -c -w"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.22.9",
"@babel/preset-env": "^7.22.9",
"rollup": "^2.79.1",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-serve": "^2.0.2"
}
}