导航
解决模板中复杂的逻辑运算以及复用的问题
const App = {
data() {
return {
studentCount: 1
}
},
template: `
<h1>{{ studentCount > 0 ? ('学生数:' + studentCount) : '暂无学生' }}</h1>
`
}
Vue.createApp(App).mount('#app');
避免多次计算,只要data下属性不改变,只会计算一次,也就是缓存
const App = {
data() {
return {
studentCount: 1
}
},
template: `
<h1>{{ studentCountInfo }}</h1>
<h1>{{ studentCountInfo }}</h1>
`,
computed: {
studentCountInfo() {
console.log('Invoked');
return this.studentCount > 0
? ('学生数:' + this.studentCount)
: '暂无学生';
}
}
}
Vue.createApp(App).mount('#app');

const vm = new Vue({
data() {
return {
name: 'Lance',
age: 28
}
},
computed: {
// 默认为 getter
getName() {
return this.name;
},
// getter、setter 配合使用
ageData: {
get() {
return this.age;
},
set(newVal) {
this.age = newVal;
}
}
}
});
/**
var 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;
}
}
});
*/
var Vue = (function() {
var reg_var = /\\{\\{(.+?)\\}\\}/g,
computedData = {}, // 计算属性集合
dataPool = {}; // 收集有 {{ }} 需要响应式的dom
/**
* total: {
* value: 函数执行返回的结果
* get: get
* dep: ['a', 'b']
* }
*/
var Vue = function(options) {
this.$el = document.querySelector(options.el);
this.$data = options.data();
this._init(this, options.computed, options.template);
}
Vue.prototype._init = function(vm, computed, template) {
dataReactive(vm); // 数据响应式
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 update(vm, key) {
dataPool[key].textContent = vm[key];
}
function _compileTemplate(vm, container) {
// 获取 template 下所有tag
var allNodes = container.getElementsByTagName('*'),
nodeItem = null;
for (var i = 0; i < allNodes.length; i++) {
nodeItem = allNodes[i];
// 匹配 {{ }} 中的data和computed
var matched = nodeItem.textContent.match(reg_var);
if (matched) {
nodeItem.textContent = nodeItem.textContent.replace(reg_var, function(node, key) {
// 收集有 {{ }} 需要响应式的dom
dataPool[key.trim()] = nodeItem;
return vm[key.trim()];
})
}
}
return container;
}
function dataReactive(vm) {
var _data = vm.$data;
for (var key in _data) {
(function(key) {
Object.defineProperty(vm, key, {
get() {
return _data[key];
},
set(newVal) {
_data[key] = newVal;
update(vm, key);
_updateComputedData(vm, key, function(key) {
update(vm, key);
})
}
});
})(key);
}
}
function computedReactive(vm, computed) {
_initComputedData(vm, computed);
for (var key in computedData) {
(function(key) {
Object.defineProperty(vm, key, {
get() {
return computedData[key].value;
},
set(newVal) {
computedData[key].value = newVal;
}
});
})(key);
}
}
function _initComputedData(vm, computed) {
for (var key in computed) {
// console.log(Object.getOwnPropertyDescriptor(computed, key));
// configurable: true
// enumerable: true
// value: ƒ total()
var descriptor = Object.getOwnPropertyDescriptor(computed, key),
// computed有可能是对象,也有可能是getter
descriptorFn = descriptor.value.get
? descriptor.value.get
: descriptor.value;
/**
* total: {
* value: 函数执行返回的结果
* get: get
* dep: ['a', 'b']
* }
*/
computedData[key] = {};
computedData[key].value = descriptorFn.call(vm);
computedData[key].get = descriptorFn.bind(vm);
computedData[key].dep = _collectDep(descriptorFn);
console.log(computedData);
}
}
function _collectDep(fn) {
var _collection = fn.toString().match(/this.(.+?)/g);
console.log(_collection);
if (_collection.length > 0) {
for (var i = 0; i < _collection.length; i++) {
_collection[i] = _collection[i].split('.')[1];
}
}
return _collection;
}
function _updateComputedData(vm, key, update) {
var _dep = null;
// 循环 computedData 中的计算属性们
for (var _key in computedData) {
_dep = computedData[_key].dep;
// 查看收集的当前计算属性的dep中的依赖data
for (var i = 0; i < _dep.length; i++) {
// 如果对应上了当前变化的data属性
if (_dep[i] === key) {
// 就重新执行一次computedData下对应方法,赋值给vm下对应计算属性
vm[_key] = computedData[_key].get();
update(_key);
}
}
}
}
return Vue;
})();
export default Vue;
/**
* 计算属性:解决模板中复杂的逻辑运算以及复用的问题
* ● 计算属性只在内部逻辑依赖的数据发生变化的时候才会被再次调用
* ● 计算属性会缓存其依赖的上一次计算出的数据结果
* ● 多次复用一个相同的数据,计算属性只会调用一次
*/
import Vue from '../module/Vue';
var 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;
}
// total: {
// get() {
// console.log('computed total');
// return this.a + this.b;
// }
// }
}
});
console.log(vm);
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);
console.log(vm.total);
console.log(vm.total);