Home  >  Article  >  Web Front-end  >  In-depth understanding of Vue global API

In-depth understanding of Vue global API

小云云
小云云Original
2017-12-18 13:50:342232browse

Does everyone know the API? This article mainly introduces an in-depth understanding of the global API of Vue official documentation. The editor thinks it is quite good, so I will share it with you now and give it as a reference. Let’s follow the editor to take a look, I hope it can help everyone.

Vue.extend

The configuration item data must be function, otherwise the configuration is invalid. The merging rules of data (you can see "Vue Official Documentation - Global Configuration") source code is as follows:

Pass in non-function type data (the data in the picture above Configured as {a:1}), when merging options, if data is not of function type, the development version will issue a warning and then directly return parentVal, which means that the data option passed in by extend is ignored.

We know that when instantiating Vue, data can be an object. Aren't the merging rules here universal? Note that there is an if(!vm) judgment above. vm has a value when instantiated, so it is different from Vue.extend. In fact, the following comments also explain it (in a Vue.extend merge, both should be function) , which is why the official documentation says data is a special case.

In addition, the "subclass" mentioned in the official document is because Vue.extend returns a function that "inherits" Vue. The source code structure is as follows:


Vue.extend = function (extendOptions) {
  //***
  var Super = this;
  var SuperId = Super.cid;
  //***
  var Sub = function VueComponent(options) {
    this._init(options);
  };
  Sub.prototype = Object.create(Super.prototype);
  Sub.prototype.constructor = Sub;
  //***
  return Sub
};

Vue.nextTick

Since you use vue, of course you have to think in a data-driven way. The so-called data-driven means not to directly operate dom, dom All operations can be completed using various instructions of vue. The instructions "bind" the data to the dom. Operating data can not only update the dom, but also be more convenient.

If the browser supports Promise, or uses the Promise library (but the one exposed to the outside must be called Promise, because the judgment in the source code is typeof Promise !== 'undefined'), nextTick returns Promise object.


Vue.nextTick().then(() => {
  // do sth
})

The callback for Vue to execute nextTick uses the call method cb.call(ctx); ctx is the current Vue instance, so you can directly use this call in the callback The configuration of the instance.
nextTick can be simply understood as placing the callback at the end for execution. If Promise and MutationObserver are not currently supported in the source code, the setTimeout method will be used to execute the callback. Isn't this the common way we delay the execution of code.


 if (typeof Promise !== 'undefined' && isNative(Promise)) {
 } else if (typeof MutationObserver !== 'undefined' && (
     isNative(MutationObserver) ||
     // PhantomJS and iOS 7.x
     MutationObserver.toString() === '[object MutationObserverConstructor]'
   )) {
 } else {
   // fallback to setTimeout
   /* istanbul ignore next */
   timerFunc = function () {
     setTimeout(nextTickHandler, 0);
   };
 }

Let’s take an example to see it in practice:


{{a}}

new Vue({ el: '#app', data: { a: 1 }, mounted: function name(params) { console.log('start'); this.$nextTick(function () { console.log('beforeChange', this.$refs.dom.textContent) }) this.a = 2; console.log('change'); this.$nextTick(function () { console.log('afterChange', this.$refs.dom.textContent) }) console.log('end'); } }) // 控制台依次打印 // start // change // end // beforeChange 1 // afterChange 2

You may have some I wonder, since they are executed last, why does beforeChange output 1 instead of 2? This is because the dom update triggered behind this.a=2 also uses nextTick. The actual execution order of the above code is: beforeChange> update dom>afterChange.

Vue.set

Vue.set( target, key, value ), the target cannot be a Vue instance, or the root data object of a Vue instance, because it is done in the source code The following judgment is made:


var ob = (target).__ob__;
if (target._isVue || (ob && ob.vmCount)) {
"development" !== 'production' && warn(
  'Avoid adding reactive properties to a Vue instance or its root $data ' +
  'at runtime - declare it upfront in the data option.'
);
return val
}

target._isVue prevents adding attributes to the Vue instance, ob && ob.vmCount prevents adding attributes to the root data object of the Vue instance .

Vue.delete

If Vue can detect the delete operation, then this API will not appear. If you must use delete to delete the $data attribute, use Vue.delete, otherwise the dom update will not be triggered.

Same as Vue.set, the target of Vue.delete(target, key) cannot be a Vue example or the root data object of a Vue example. The blocking method in the source code is the same as Vue.set.

In version 2.2.0+, if the target is an array, the key is the array subscript. Because Vue.delete actually uses splice to delete an array, although delete can be used to delete an array, the position is still there and cannot be considered a true deletion.


var a = [1, 2, 3];
delete a[0];
console.log(a); // [undefined, 2, 3]

Vue.use

Vue.use The source code is relatively simple and can be posted in full.


Vue.use = function (plugin) {
  var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));
  if (installedPlugins.indexOf(plugin) > -1) {
    return this
  }
  // additional parameters
  var args = toArray(arguments, 1);
  args.unshift(this);
  if (typeof plugin.install === 'function') {
    plugin.install.apply(plugin, args);
  } else if (typeof plugin === 'function') {
    plugin.apply(null, args);
  }
  installedPlugins.push(plugin);
  return this
};

The installed plug-ins are placed in installedPlugins. Before installing the plug-in, use installedPlugins.indexOf(plugin) to determine whether the plug-in has been installed, thereby preventing multiple registrations of the same plug-in. Second-rate.

The plug-in type is object, and the install attribute must be specified to install the plug-in (typeof plugin.install === 'function'). In addition, plug-in execution uses plugin.install.apply(plugin, args);, Therefore this accesses other properties of object. The args here are the parameters other than plugin passed in by Vue (args.unshift(this);) and Vue.use (toArray(arguments, 1), 1 means intercepting starting from arguments[1]).


Vue.use({
  a: 1,
  install: function (Vue) {
    console.log(this.a) // 1
    console.log(arguments) // [function Vue(options),"a", "b", "c"]
  }
}, 'a', 'b', 'c')

The plug-in type is function, and the installation calls plugin.apply(null, args);, so in strict mode the plug-in runtime context this is null, not Strict mode is Window.


'use strict'
Vue.use(function plugin() {
  console.log(this) // null
  console.log(arguments) // [function Vue(options),"a", "b", "c"]
}, 'a', 'b', 'c')

Vue.compile

和众多 JS 模板引擎的原理一样,预先会把模板转化成一个 render 函数,Vue.compile 就是来完成这个工作的,目标是将模板(template 或 el)转化成 render 函数。
Vue.compile 返回了{render:Function,staticRenderFns:Array},render 可直接应用于 Vue 的配置项 render,而 staticRenderFns 是怎么来的,而且按照官网的例子,Vue 还有个隐藏的配置项 staticRenderFns,先来个例子看看。


var compiled = Vue.compile(
  '

' + '

no data binding

' + '
{{prop}}
' + '

' ) console.log(compiled.render.toString()) console.log(compiled.staticRenderFns.toString()) // render function anonymous() { with(this) { return _c('p', [_m(0), _c('section', [_v(_s(prop))])]) } } // staticRenderFns function anonymous() { with(this) { return _c('header', [_c('h1', [_v("no data binding")])]) } }

原来没有和数据绑定的 dom 会放到 staticRenderFns 中,然后在 render 中以_m(0)来调用。但是并不尽然,比如上述模板去掉4a249f0d628e2318394fd9b75b4636b1,staticRenderFns 长度为 0,header 直接放到了 render 函数中。


function anonymous() {
  with(this) {
    return _c('p', [_c('header', [_v("no data binding")]), _c('section', [_v(_s(prop))])])
  }
}

Vue.compile 对应的源码比较复杂,上述渲染 1aa9e5d373740b65a0cc8f0a02150c53 没有放到 staticRenderFns 对应源码的核心判断如下:


 // For a node to qualify as a static root, it should have children that
 // are not just static text. Otherwise the cost of hoisting out will
 // outweigh the benefits and it's better off to just always render it fresh.
 if (node.static && node.children.length && !(
     node.children.length === 1 &&
     node.children[0].type === 3
   )) {
   node.staticRoot = true;
   return
 } else {
   node.staticRoot = false;
 }

1aa9e5d373740b65a0cc8f0a02150c53 不符判断条件 !(node.children.length === 1 && node.children[0].type === 3), 1aa9e5d373740b65a0cc8f0a02150c53 有一个子节点 TextNode(nodeType=3)。 注释也说明了一个 node 符合静态根节点的条件。

另外官网说明了此方法只在独立构建时有效,什么是独立构建?这个官网做了详细的介绍,不再赘述。对应官网地址:对不同构建版本的解释。

仔细观察编译后的 render 方法,和我们自己写的 render 方法有很大区别。但是仍然可以直接配置到 render 配置选项上。那么里面的那些 _c()、_m() 、_v()、_s() 能调用?随便看一个 Vue 的实例的 __proto__ 就会发现:


// internal render helpers.
// these are exposed on the instance prototype to reduce generated render
// code size.
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.prototype._g = bindObjectListeners;

正如注释所说,这些方法是为了减少生成的 render 函数的体积。

全局 API 还剩 directive、filter、component、mixin,这几个比较类似,而且都对应着配置项,会在「选项」中再详细介绍。

相关推荐:

Vue官方文档梳理的全局配置详解

Vue拦截器vue-resource 拦截器使用详解

vue项目的构建,打包,发布详解

The above is the detailed content of In-depth understanding of Vue global API. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn