正文:

源码来源于vuejs的commit记录:a5e27b1174e9196dcc9dbb0becc487275ea2e84c

作者在src下实现了一个简单的指令和过滤器, 下面来看下基本的实现过程(代码有部分删改和伪代码):

  1. 首先定义了一个prefix用来标记自定义的指令,找出所有带有指令的dom节点

        var prefix = 'sd',
        selector = Object.keys(Directives).map(function (d) {
            return '[' + prefix + '-' + d + ']'
        }).join()
    
        function Seed (opts) {
            var self = this,
            root = this.el = document.getElementById(opts.id),
            // els即为所有带有sd前缀指令的dom
            els  = root.querySelectorAll(selector);
            // ...
        }
  2. 遍历所有dom节点和自定义指令,然后全部记录在bindings中,最后在 bindAccessors中进行进行数据劫持。

    ;[].forEach.call(els, processNode)
    
    function processNode (el) {
        cloneAttributes(el.attributes).forEach(function (attr) {
            var directive = parseDirective(attr)
            if (directive) {
                bindDirective(self, el, bindings, directive)
            }
        })
    }
    
    function bindDirective (seed, el, bindings, directive) {
        el.removeAttribute(directive.attr.name)
        var key = directive.key,
            binding = bindings[key]
        if (!binding) {
            bindings[key] = binding = {
                value: undefined,
                directives: []
            }
        }
        directive.el = el
        binding.directives.push(directive)
        if (!seed.scope.hasOwnProperty(key)) {
            bindAccessors(seed, key, binding)
        }
    }
  3. 看下劫持的时候做了什么:

    • get: 和第一版一样,从binding中取值
    • set: 在bingding中设置新值,遍历binding中存储的自定义指令并进行绑定和更新
    
    function bindAccessors (seed, key, binding) {
        Object.defineProperty(seed.scope, key, {
            get: function () {
                return binding.value
            },
            set: function (value) {
                binding.value = value
                binding.directives.forEach(function (directive) {
                    if (value && directive.filters) {
                        value = applyFilters(value, directive)
                    }
                    directive.update(
                        directive.el,
                        value,
                        directive.argument,
                        directive,
                        seed
                    )
                })
            }
        })
    }

具体实现可以参考代码实现,以上只是大体思路


clipboard.png


我有一只小腊肠
304 声望17 粉丝

曾经真的以为自己永远都不会胖..