- 😳
- 解决模板中复杂逻辑运算的问题 (模板与逻辑分离)
- 计算属性会缓存其依赖数据的上一次计算结果( 缓存到实例)
- 多次复用一个相同值的结果,计算属性只调用一次
- 只有在内部逻辑依赖的数据发生变化时,才会被再次调用
模拟

let vm = new Vue({
el: "#app",
template: `
<span>{{a}}</span>
<span>+</span>
<span>{{b}}</span>
<span>=</span>
<span>total:{{total}}</span>
<span>||</span>
<span>num:{{res}}</span>
`,
data() {
return {
a: 2,
b: 2,
};
},
computed: {
total() {
console.log("computed total");
return this.a + this.b;
},
res() {
return this.a * this.b
}
},
});
console.log(vm);
vm.a = 100
let Vue = (function () {
let computedData = {},
domData = {};
console.log(computedData, "computedData");
console.log(domData, "domData");
class React {
constructor(options) {
this.$el = document.querySelector(options.el);
this.$data = options.data();
this._init(this, options.computed, options.template);
}
_init(vm, computed, template) {
this.dataReactive(vm);
this.computedReactive(vm, computed);
this.render(vm, template);
}
dataReactive(vm) {
let _data = vm.$data;
for (let key in _data) {
(function (key) {
Object.defineProperty(vm, key, {
get() {
return _data[key];
},
set(newValue) {
_data[key] = newValue;
this.update(vm, key);
this.updateComputedData(vm, key, function (keys) {
vm.update(vm, keys);
});
},
});
})(key);
}
}
computedReactive(vm, computed) {
this._initComputedData(vm, computed);
for (let key in computedData) {
(function (key) {
Object.defineProperty(vm, key, {
get() {
return computedData[key].value;
},
set(newValue) {
computedData[key].value = newValue;
},
});
})(key);
}
}
_initComputedData(vm, computed) {
for (let key in computed) {
let descriptor = Object.getOwnPropertyDescriptor(computed, key),
descriptFn = descriptor.value.get
? descriptor.value.get
: descriptor.value;
computedData[key] = {};
computedData[key].value = descriptFn.call(vm);
computedData[key].get = descriptFn.bind(vm);
computedData[key].dep = this._collectDep(descriptFn);
}
}
render(vm, template) {
let container = document.createElement("div"),
_el = vm.$el;
container.innerHTML = template;
let domTree = this._compivatemplate(vm, container);
_el.appendChild(domTree);
}
_compivatemplate(vm, container) {
let allNodes = container.getElementsByTagName("*"),
nodeItem = null;
for (let i = 0; i < allNodes.length; i++) {
nodeItem = allNodes[i];
let matched = nodeItem.textContent.match(/\{\{(.+?)\}\}/g);
if (matched) {
nodeItem.textContent = nodeItem.textContent.replace(
/\{\{(.+?)\}\}/g,
function (node, key) {
domData[key.trim()] = nodeItem;
return vm[key.trim()];
}
);
}
}
return container;
}
_collectDep(fn) {
let _collection = fn.toString().match(/this.(.+?)/g);
if (_collection.length > 0) {
for (let i = 0; i < _collection.length; i++) {
_collection[i] = _collection[i].split(".")[1];
}
}
return _collection;
}
update(vm, key) {
domData[key].textContent = vm[key];
}
updateComputedData(vm, key, fn) {
let _dep = null;
for (let _key in computedData) {
_dep = computedData[_key].dep;
for (let i = 0; i < _dep.length; i++) {
if (_dep[i] === key) {
vm[_key] = computedData[_key].get();
fn(_key);
}
}
}
}
}
return React;
})();