FED

©FrontEndDev.org
2015 - 2024
web@2.23.0 api@2.21.1

Vuejs核心代码解析之编译阶段2(三)

最近比较懒,工作也比较忙。这节拖后较久。 声明本人非大大,有错误请指出。

这节说下只处理文本节点的情况。

例子:

html:

	<div id="output">
        {{show}}
    </div>

js:

var vm = new Vue({
            data:{
                show:'init'
            }
 });
 vm.$mount('#output');

其实就是把{{show}}对应替换成相应的值。

简化版vue,可以看这里,现在还有点复杂,直接看src比较好。

简化版 ,跪求几个star, (hi star)

之后还会进一步整理。

#编译执行阶段.


function compositeLinkFn (vm, el) {

    // cache childNodes before linking parent, fix #657
    var childNodes = _.toArray(el.childNodes)

    // link
    if (nodeLinkFn) nodeLinkFn(vm, el)
    if (childLinkFn) childLinkFn(vm, childNodes)

  }
  

因无nodeLinkFn 直接执行 childLinkFn:

function makeChildLinkFn (linkFns) {
	  return function childLinkFn (vm, nodes, host) {
	    var node, nodeLinkFn, childrenLinkFn
	    for (var i = 0, n = 0, l = linkFns.length; i < l; n++) {
	      node = nodes[n]
	      nodeLinkFn = linkFns[i++]
	      childrenLinkFn = linkFns[i++]
	      // cache childNodes before linking parent, fix #657
	      var childNodes = _.toArray(node.childNodes)
	      if (nodeLinkFn) {
	        nodeLinkFn(vm, node, host)
	      }
	      if (childrenLinkFn) {
	        childrenLinkFn(vm, childNodes, host)
	      }
	    }
	  }
	}

为初始化的时候返回的linker

linkFns:

 //初始化遍历后的结果
 [function textNodeLinkFn(vm, el) { ... }, null]

依次执行 linkFns 内函数 :(先本身,后子)

本例只有一个文本节点(本身), 就是 textNodeLinkFn:


function makeTextNodeLinkFn (tokens, frag) {
	  return function textNodeLinkFn (vm, el) {
	    var fragClone = frag.cloneNode(true)
	    var childNodes = _.toArray(fragClone.childNodes)
	    var token, value, node
	    for (var i = 0, l = tokens.length; i < l; i++) {
	      token = tokens[i]
	      value = token.value
	      if (token.tag) {
	          node = childNodes[i]
	          vm._bindDir(token.type, node,  token.descriptor, token.def)
	      }
	    }
	    _.replace(el, fragClone)
	  }
	}

上节的连接器: tokens 与 frag 为:

//frag 为 fragment 有三文本节点data分别为:

1. "↵ "
2. " "
3. "↵ "

//tokens 为一个数组, 数组内三个对象:
1.{value:"↵ "} 
2. {
		html: false,
		tag: true,
		type: "text",
		value: "show",
		def:{
			bind:function(){..},
			update:function(){..}
		},
		descriptor:{
			expression: "show",
			raw: "show"
		}
   }
3.{value:"↵ "}

遍历tokens, 对tag为true的进行处理(也就是{{show}}):


if (token.tag) {
	  node = childNodes[i]
      vm._bindDir(token.type, node,  token.descriptor, token.def)
 }

执行bindDir:

创建指令


exports._bindDir = function (name, node, desc, def, host) {
	  this._directives.push(
	    new Directive(name, node, this, desc, def, host)
	  )
	}

指令构造:


function Directive (name, el, vm, descriptor, def, host) {
	  // public
	  this.name = name
	  this.el = el
	  this.vm = vm
	  // copy descriptor props
	  this.raw = descriptor.raw
	  this.expression = descriptor.expression
	  this.arg = descriptor.arg

	  // private
	  this._descriptor = descriptor
	  this._host = host
	  this._locked = false
	  this._bound = false
	  // init
	  this._bind(def)
}

执行_bind:


p._bind = function (def) {

	  _.extend(this, def)
	  
	  this._watcherExp = this.expression
	  
	  if (this.bind) {
	    //指令def中的bind方法
	    // after this.attr = 'nodeValue'
	    this.bind()
	  }
	  
	  if (this.update || this.twoWay) {
	    // wrapped updater for context
	    var dir = this
	    //dir内update方法
	    var update = this._update = this.update
	      ? function (val, oldVal) {
	          if (!dir._locked) {
	            dir.update(val, oldVal)
	          }
	        }
	      : function () {}

	    var watcher = this.vm._watchers[this.raw]
	    // v-repeat always creates a new watcher because it has
	    // a special filter that's bound to its directive
	    // instance.
	    if (!watcher) {
	     // this.raw = 'show'
	      watcher = this.vm._watchers[this.raw] = new Watcher(
	        this.vm,
	        this._watcherExp,
	        update, // callback
	        {
	          twoWay: this.twoWay,
	          deep: this.deep
	        }
	      )
	    } else {
	      watcher.addCb(update)
	    }

	    this._watcher = watcher
	    if (this._initValue != null) {
	      watcher.set(this._initValue)
	    } else if (this.update) {
	      this.update(watcher.value)
	    }
	  }
	  this._bound = true
	}

其他比较简单主要看 new Watcher:


	function Watcher (vm, expression, cb, options) {
	  this.vm = vm
	  vm._watcherList.push(this)
	  this.expression = expression
	  this.cbs = [cb]
	  this.id = ++uid // uid for batching
	  this.active = true
	  options = options || {}
	  this.deep = !!options.deep
	  this.user = !!options.user
	  this.deps = []
	  this.newDeps = []
	  // parse expression for getter/setter

	  //expression = 'show' , options.twoWay = undefined
	  //对表达式进行处理,
	  var res = expParser.parse(expression, options.twoWay)
	  this.getter = res.get
	  this.setter = res.set
	  //获取第一节初始化中Oberver中的值,也就是vm.data,也就是init
	  this.value = this.get()
	}

this.value = this.get() 获取data的值,初始化已给data添加访问器,所以获取直接触发可以获取到'init':


		p.get = function () {
	  this.beforeGet()
	  var vm = this.vm
	  var value
	  try {
	    value = this.getter.call(vm, vm)
	  } catch (e) {
	    if (config.warnExpressionErrors) {
	      _.warn(
	        'Error when evaluating expression "' +
	        this.expression + '":\n   ' + e
	      )
	    }
	  }
	  // "touch" every property so they are all tracked as
	  // dependencies for deep watching
	  if (this.deep) {
	    traverse(value)
	  }
	  value = _.applyFilters(value, this.readFilters, vm)
	  this.afterGet()
	  //this.value = value = 'init'
	  return value
	}

最后更新值 this.update(watcher.value):

//p._bind
// 上面的返回值(p.get )value = watcher.value = 'init'
// this.attr dir.bind 执行结果 'nodeValue'
 update: function (value) {
    this.el[this.attr] = _.toString(value)
  }
  

this.el 就是 fragment中的node, 最后replace 结束。。。


//textNodeLinkFn

{{show}} => 'init'
_.replace(el, fragClone)

有兴趣可以看下peel-vue,地址在上面。thx