Vue源码解析(五)

来源:互联网 发布:vb用户管理系统 编辑:程序博客网 时间:2024/05/19 06:51

接下来看看renderMixin方法,这个方法在Vue的原型上增加了$nextTick_render以及_o、_n、_s等方法。

Vue.prototype.$nextTick = function (fn) {    return nextTick(fn, this)  };

$nextTick方法传入一个函数,同时调用nextTick方法,传入的fn是dom刷新之后的回调。

var nextTick = (function () {  var callbacks = [];  var pending = false;  var timerFunc;  function nextTickHandler () {    pending = false;    var copies = callbacks.slice(0);    callbacks.length = 0;    for (var i = 0; i < copies.length; i++) {      copies[i]();    }  }  // the nextTick behavior leverages the microtask queue, which can be accessed  // via either native Promise.then or MutationObserver.  // MutationObserver has wider support, however it is seriously bugged in  // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It  // completely stops working after triggering a few times... so, if native  // Promise is available, we will use it:  /* istanbul ignore if */  if (typeof Promise !== 'undefined' && isNative(Promise)) {    var p = Promise.resolve();    var logError = function (err) { console.error(err); };    timerFunc = function () {      p.then(nextTickHandler).catch(logError);      // in problematic UIWebViews, Promise.then doesn't completely break, but      // it can get stuck in a weird state where callbacks are pushed into the      // microtask queue but the queue isn't being flushed, until the browser      // needs to do some other work, e.g. handle a timer. Therefore we can      // "force" the microtask queue to be flushed by adding an empty timer.      if (isIOS) { setTimeout(noop); }    };  } else if (typeof MutationObserver !== 'undefined' && (    isNative(MutationObserver) ||    // PhantomJS and iOS 7.x    MutationObserver.toString() === '[object MutationObserverConstructor]'  )) {    // use MutationObserver where native Promise is not available,    // e.g. PhantomJS IE11, iOS7, Android 4.4    var counter = 1;    var observer = new MutationObserver(nextTickHandler);    var textNode = document.createTextNode(String(counter));    observer.observe(textNode, {      characterData: true    });    timerFunc = function () {      counter = (counter + 1) % 2;      textNode.data = String(counter);    };  } else {    // fallback to setTimeout    /* istanbul ignore next */    timerFunc = function () {      setTimeout(nextTickHandler, 0);    };  }  return function queueNextTick (cb, ctx) {    var _resolve;    callbacks.push(function () {      if (cb) {        try {          cb.call(ctx);        } catch (e) {          handleError(e, ctx, 'nextTick');        }      } else if (_resolve) {        _resolve(ctx);      }    });    if (!pending) {      pending = true;      timerFunc();    }    if (!cb && typeof Promise !== 'undefined') {      return new Promise(function (resolve, reject) {        _resolve = resolve;      })    }  }})();

nextTick方法采用了一个闭包,然后返回的函数为queueNextTick。如其名queueNextTick是一个NextTick队列,多次调用nextTick方法会放入队列中,然后按照顺序依次进入传入的回调函数。callbacks为回调函数的队列,dom更新完成后(如何确定的更新完成?),依次执行。执行回调队列方法一共采用了3中方式:1、Promise 2、MutationObserver 3、setTimeout。

Vue.prototype._render = function () {    var vm = this;    var ref = vm.$options;    var render = ref.render;    var staticRenderFns = ref.staticRenderFns;    var _parentVnode = ref._parentVnode;    if (vm._isMounted) {      // clone slot nodes on re-renders      for (var key in vm.$slots) {        vm.$slots[key] = cloneVNodes(vm.$slots[key]);      }    }    vm.$scopedSlots = (_parentVnode && _parentVnode.data.scopedSlots) || emptyObject;    if (staticRenderFns && !vm._staticTrees) {      vm._staticTrees = [];    }    // set parent vnode. this allows render functions to have access    // to the data on the placeholder node.    vm.$vnode = _parentVnode;    // render self    var vnode;    try {      vnode = render.call(vm._renderProxy, vm.$createElement);    } catch (e) {      handleError(e, vm, "render function");      // return error render result,      // or previous vnode to prevent render error causing blank component      /* istanbul ignore else */      {        vnode = vm.$options.renderError          ? vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)          : vm._vnode;      }    }    // return empty vnode in case the render function errored out    if (!(vnode instanceof VNode)) {      if ("development" !== 'production' && Array.isArray(vnode)) {        warn(          'Multiple root nodes returned from render function. Render function ' +          'should return a single root node.',          vm        );      }      vnode = createEmptyVNode();    }    // set parent    vnode.parent = _parentVnode;    return vnode  };

_render方法为内部方法,主要是渲染vnode(虚拟节点)。

var VNode = function VNode (  tag,  data,  children,  text,  elm,  context,  componentOptions) {  this.tag = tag;  this.data = data;  this.children = children;  this.text = text;  this.elm = elm;  this.ns = undefined;  this.context = context;  this.functionalContext = undefined;  this.key = data && data.key;  this.componentOptions = componentOptions;  this.componentInstance = undefined;  this.parent = undefined;  this.raw = false;  this.isStatic = false;  this.isRootInsert = true;  this.isComment = false;  this.isCloned = false;  this.isOnce = false;};

VNode的构造函数,传递了tag,data,children等参数,VNode构造函数的参数比较多,某些参数的作用如isStatic不是特别清楚。

  Vue.prototype._o = markOnce;  Vue.prototype._n = toNumber;  Vue.prototype._s = toString;  Vue.prototype._l = renderList;  Vue.prototype._t = renderSlot;  Vue.prototype._q = looseEqual;  Vue.prototype._i = looseIndexOf;  Vue.prototype._m = renderStatic;  Vue.prototype._f = resolveFilter;  Vue.prototype._k = checkKeyCodes;  Vue.prototype._b = bindObjectProps;  Vue.prototype._v = createTextVNode;  Vue.prototype._e = createEmptyVNode;  Vue.prototype._u = resolveScopedSlots;

vue的原型上添加了_o、_n、_s 等14个内部方法,源码的注释说明是render函数的一些帮助函数,都放在了vue的原型上。这个地方不是很理解的是为什么要采用单字母来命名方法,有什么特别的意义?