<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
<script>
//计算属性:解决模板中复杂的逻辑运算及复用的问题
//计算属性只有在内部逻辑依赖的数据发生变化的时候才会被再次调用
//计算属性会缓存其依赖的上一次计算出的数据结果
//多次复用一个相同值得数据,计算属性只调用一次
let Vue = (function () {
var reg_var = /\{\{(.+?)\}\}/g,
computedData = {},
dataPool = {};
//参数传递处理
var Vue = function (options) {
this.$el = document.querySelectorAll(options.el)[0];
this.$data = options.data();
this._init(this, options.computed, options.template);
}
//初始化
Vue.prototype._init = function (vm, computed, template) {
//data值拦截
dataReactive(vm);
//computed拦截
computedReactive(vm, computed);
//渲染
render(vm, template);
}
//模板编译渲染
function render(vm, template) {
var container = document.createElement('div'),
_el = vm.$el;
container.innerHTML = template;
var domTree = _compileTemplate(vm, container);
_el.appendChild(domTree);
}
//计算属性拦截
function computedReactive(vm, computed) {
_initComputedData(vm, computed);
for (var key in computedData) {
(function (key) {
Object.defineProperty(vm, key, {
get() {
return computedData[key].value;
},
set(newValue) {
computedData[key].value = newValue;
}
})
})(key)
}
}
//data属性绑定拦截
function dataReactive(vm) {
var _data = vm.$data;
for (var key in _data) {
(function (key) {
Object.defineProperty(vm, key, {
get() {
return _data[key];
},
set(newValue) {
_data[key] = newValue;
//对data记录进行缓存
update(vm, key);
//值改变去判断是不是computed依赖的值变了
_updateComputeData(vm, key, function (key) {
update(vm, key)
})
}
})
})(key);
}
}
//完成模板渲染以及记录和data有绑定的dom
function _compileTemplate(vm, container) {
var allNodes = container.getElementsByTagName('*'),
nodeItem = null;
for (var i = 0; i < allNodes.length; i++) {
nodeItem = allNodes[i];
var matched = nodeItem.textContent.match(reg_var);
if (matched) {
nodeItem.textContent = nodeItem.textContent.replace(reg_var, function (node, key) {
console.log(vm)
dataPool[key.trim()] = nodeItem;
return vm[key.trim()]
})
}
}
return container;
}
//构建computed处理对象,记录计算结果的值和计算函数及牵扯到的data对象
function _initComputedData(vm, computed) {
for (var key in computed) {
var descriptor = Object.getOwnPropertyDescriptor(computed, key),
descriptorFn = descriptor.value.get
? descriptor.value.get
: descriptor.value
computedData[key] = {};
computedData[key].value = descriptorFn.call(vm);
computedData[key].get = descriptorFn.bind(vm);
computedData[key].dep = _collectDep(descriptorFn)
}
}
//分析出依赖了那些data中的值
function _collectDep(fn) {
var _collection = fn.toString().match(/this.(.+?)/g);
if (_collection.length > 0) {
for (var i = 0; i < _collection.length; i++) {
_collection[i] = _collection[i].split('.')[1];
}
}
return _collection;
}
//当依赖的data值发生改变时
function _updateComputeData(vm, key, update) {
var _dep = null;
for (var _key in computedData) {
_dep = computedData[_key].dep;
for (var i = 0; i < _dep.length; i++) {
//如果要更改的值是被computed依赖的值
if (_dep[i] === key) {
//计算的最终结果给到data属性值
vm[_key] = computedData[_key].get();
//数据缓存
update(_key)
}
}
}
}
function update(vm, key) {
dataPool[key].textContent = vm[key];
console.log( dataPool)
}
return Vue;
})()
let vm =new Vue({
el: '#app',
template:
`
<span>{{a}}</span>
<span>+</span>
<span>{{b}}</span>
<span>=</span>
<span>{{total}}</span>
`,
data() {
return {
a: 1,
b: 2
}
},
computed: {
total() {
console.log('computed total');
return this.a + this.b;
}
},
})
console.log(vm.total);
console.log(vm.total);
console.log(vm.total);
console.log(vm.total);
// vm.a = 100;
// vm.b = 200;
console.log(vm.total);
console.log(vm.total);
</script>
</html>