跳到主要内容

计算属性

计算属性用于解决模板中复杂的计算以及数据复用的问题。它仅在其依赖的数据发生变化时才会重新计算,并且会缓存上一次的计算结果。当多个地方需要复用相同值的数据时,计算属性只会调用一次,从而提高性能和效率。

模板中嵌入方法

在模板中嵌入方法时,每次计算的结果都会形成一个对象,以便保存和管理数据。这种方式有助于避免不必要的重复计算,确保数据的一致性和高效性。

实现依赖收集

var Vue = (function () {
var templateVariableRegex = /\{\{(.+?)\}\}/g,
computedData = {},
dataPool = {};

/**
* computedData 结构:
* {
* total: {
* value: 函数执行返回的结果,
* get: 获取值的函数,
* dependencies: ['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) {
makeDataReactive(vm);
setupComputedProperties(vm, computed);
renderTemplate(vm, template);
};

function renderTemplate(vm, template) {
var container = document.createElement('div'),
rootElement = vm.$el;

container.innerHTML = template;

var compiledDom = compileTemplate(vm, container);

rootElement.appendChild(compiledDom);
}

function updateView(vm, key) {
dataPool[key].textContent = vm[key];
}

function compileTemplate(vm, container) {
var allNodes = container.getElementsByTagName('*'),
currentNode = null;

for (var i = 0; i < allNodes.length; i++) {
currentNode = allNodes[i];

var matches = currentNode.textContent.match(templateVariableRegex);

if (matches) {
currentNode.textContent = currentNode.textContent.replace(templateVariableRegex, function (node, key) {
dataPool[key.trim()] = currentNode;
return vm[key.trim()];
});
}
}

return container;
}

function makeDataReactive(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;
updateView(vm, key);
updateComputedProperties(vm, key, function (computedKey) {
updateView(vm, computedKey);
});
},
});
})(key);
}
}

function setupComputedProperties(vm, computed) {
initializeComputedData(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);
}
}

function initializeComputedData(vm, computed) {
for (var key in computed) {
var descriptor = Object.getOwnPropertyDescriptor(computed, key),
computeFn = descriptor.value.get ? descriptor.value.get : descriptor.value;

/**
* computedData 结构:
* {
* total: {
* value: 函数执行返回的结果,
* get: 获取值的函数,
* dependencies: ['a', 'b']
* }
* }
*/

computedData[key] = {};
computedData[key].value = computeFn.call(vm);
computedData[key].get = computeFn.bind(vm);
computedData[key].dependencies = collectDependencies(computeFn);
}
}

function collectDependencies(fn) {
var dependencies = fn.toString().match(/this\.(\w+)/g);

if (dependencies) {
dependencies = dependencies.map((dep) => dep.split('.')[1]);
} else {
dependencies = [];
}

return dependencies;
}

function updateComputedProperties(vm, changedKey, callback) {
for (var key in computedData) {
var deps = computedData[key].dependencies;

if (deps.includes(changedKey)) {
vm[key] = computedData[key].get();
callback(key);
}
}
}

return Vue;
})();

export default Vue;

计算属性的实现涉及到依赖收集和数据响应式处理。通过正则表达式识别模板中的变量,并在数据变化时自动更新相关的视图。此外,计算属性通过缓存机制避免了不必要的重复计算,提高了应用的性能。在实际开发中,合理使用计算属性能够显著简化模板逻辑,提升代码的可维护性和效率。