vue 中directive功能的简单实现

2020-06-16 06:06:39易采站长站整理

const Seed = function(opts) {
const self = this,
root = this.el = document.getElementById(opts.id),
// 筛选出el下所能支持的directive的nodes列表
els = this.el.querySelectorAll(selector),
bindings = {};
this.scope = {};
// 解析节点
[].forEach.call(els, processNode);
// 解析根节点
processNode(root);
// 给scope赋值,触发setter方法,此时会调用与其相对应的directive的update方法
Object.keys(bindings).forEach((key) => {
this.scope[key] = opts.scope[key];
});
function processNode(el) {
cloneAttributes(el.attributes).forEach((attr) => {
const directive = parseDirective(attr);
if (directive) {
bindDirective(self, el, bindings, directive);
}
});
}
};

可以看到核心方法 processNode 主要做了两件事一个是 parseDirective ,另一个是 bindDirective 。

先来看看 parseDirective 方法:


function parseDirective(attr) {
if (attr.name.indexOf(prefix) == -1) return;
// 解析属性名称获取directive
const noprefix = attr.name.slice(prefix.length + 1),
argIndex = noprefix.indexOf('-'),
dirname = argIndex === -1 ? noprefix : noprefix.slice(0, argIndex),
arg = argIndex === -1 ? null : noprefix.slice(argIndex + 1),
def = Directives[dirname] // 解析属性值获取filters
const exp = attr.value,
pipeIndex = exp.indexOf('|'),
key = (pipeIndex === -1 ? exp : exp.slice(0, pipeIndex)).trim(),
filters = pipeIndex === -1 ? null : exp.slice(pipeIndex + 1).split('|').map((filterName) => filterName.trim());
return def ? {
attr: attr,
key: key,
filters: filters,
argument: arg,
definition: Directives[dirname],
update: typeof def === 'function' ? def : def.update
} : null;
}

以 sd-on-click=”toggle | .button” 为例来说明,其中attr对象的name为 sd-on-click ,value为 toggle | .button ,最终解析结果为:


{
"attr": attr,
"key": "toggle",
"filters": [".button"],
"argument": "click",
"definition": {"on": {}},
"update": function(){}
}

紧接着调用 bindDirective 方法


/**
* 数据绑定
* @param {Seed} seed Seed实例对象
* @param {Element} el 当前node节点
* @param {Object} bindings 数据绑定存储对象
* @param {Object} directive 指令解析结果
*/
function bindDirective(seed, el, bindings, directive) {
// 移除指令属性
el.removeAttribute(directive.attr.name);
// 数据属性
const key = directive.key;
let binding = bindings[key];
if (!binding) {
bindings[key] = binding = {
value: undefined,