vue self-assembly

Vue assembly

What are the components?

Component (Component) Vue.js is one of the most powerful features. HTML elements that could be extended, the package reusable code. At a high level, the components are custom element, Vue.js compiler to add special features to it. In some cases, the components can also be expressed as been expanded with native HTML elements is characteristic.
Using Components

Global registration

We already know that you can create a Vue instance in the following ways:

new Vue({
  el: '#some-element',
  // 选项
})

To register a global component, you can use Vue.component (tagName, options). E.g:

Vue.component('my-component', {
  // 选项
})

Please note that for the self-named Vue.js custom tags is not mandatory to follow the W3C rules (lowercase, and includes a short bar), best practices, although this is considered to be.

After registration assembly, as can custom element <my-component> </ my-component> using a template instance. Note sure to register before initializing the root component instance:

<div id="example">
  <my-component></my-component>
</div>
// 注册
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
  el: '#example'
})
渲染为:
<div id="example">
  <div>A custom component!</div>
</div>
A custom component!

Local registration

You do not have to register each component to the overall situation. You can register only in the component domain by its role instance option available components of a Vue examples / components:

var Child = {
  template: '<div>A custom component!</div>'
}
new Vue({
  // ...
  components: {
    // <my-component> 将只在父组件模板中可用
    'my-component': Child
  }
})

This package is also applicable to other registered Vue functions, such instructions.
DOM parsing template Notes

When using the DOM as a template (for example, using el option to mount the Vue instance on an existing element content), you will be subject to some limitations of HTML itself, because only in the browser parses Vue, in order to obtain standardized template after its contents. Particular attention, like <ul>, <ol>, <table>, <select> element in this element contains a limited permit, while others like <option> This element can only appear in certain elements internal.
When custom components in restricted use these elements can lead to problems, such as:

<table>
  <my-row>...</my-row>
</table>

Custom Component <my-row> will be treated as invalid content, and therefore can lead to erroneous rendering results. Alternative solution is to use special characteristic is:

<table>
  <tr is="my-row"></tr>
</table>

It should be noted that if the string template from one of the following sources, you do not have these restrictions:
<Script of the type = "text / the X-Template-">
JavaScript inline template string
.vue components
therefore, use the string as a template .
data must be a function

Incoming constructing Vue examples of the various options can be used in most of the components inside. With one exception: data must be a function. In fact, if you do the following:

Vue.component('my-component', {
  template: '<span>{{ message }}</span>',
  data: {
    message: 'hello'
  }
})

So Vue will stop running, and alert console, to tell you the component instance data must be a function. But to understand why there is such a rule is very beneficial, so let's make a disadvantages:

<div id="example-2">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>
var data = { counter: 0 }
Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // 技术上 data 的确是一个函数了,因此 Vue 不会警告,
  // 但是我们却给每个组件实例返回了同一个对象的引用
  data: function () {
    return data
  }
})
new Vue({
  el: '#example-2'
})
0 0 0

Since these three components share the same instance of a data object, so incrementing a counter affects all components! This is wrong. We can fix this problem by returning a new data object for each component:

data: function () {
  return {
    counter: 0
  }
}
现在每个 counter 都有它自己内部的状态了:
0 0 0

Combinations of components

The most common is the relationship between father and son form a component assembly is designed to be used in conjunction with: A component used in the assembly of its template B. Necessarily need to communicate between them: the parent component may deliver data to give sub-assemblies, subassemblies, you may want to inform what happened inside its parent component. However, through a well-defined interfaces to decouple components as much as possible and his son it is also very important. This ensures that each component of the code can be written in a relatively isolated environment and understanding, to improve the maintainability and reusability.
In the Vue, the parent-child relationship can be summarized as a prop assembly passed down, the event passed up. Deliver data to a parent element via prop subassembly, the subassembly sends a message to the event by the parent component. To see how they work.

prop-down pass, the event passed up
Prop

Use Prop transfer data

Scoped component instance is isolated. This means you can not (and should not) reference data directly within the parent component template subcomponents. The parent element data need to be sent to prop subassembly.
Subcomponents to explicitly declare it with props options expected data:

Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 就像 data 一样,prop 也可以在模板中使用
  // 同样也可以在 vm 实例中通过 this.message 来使用
  template: '<span>{{ message }}</span>'
})
然后我们可以这样向它传入一个普通字符串:
<child message="hello!"></child>
结果:
hello!
camelCase vs. kebab-case

HTML attributes are not case sensitive. Therefore, when the template is not a string, camelCase (Camel naming) of the prop to be converted into the corresponding kebab-case (with dashes formula name):

Vue.component('child', {
  // 在 JavaScript 中使用 camelCase
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})

<! - Use kebab-case in HTML ->
<-the Message = My Child "the Hello!"> </ Child>
If you use a string template, you do not have these restrictions.
Dynamic Prop

That are bound to any conventional HTML similar characteristics, we can use v-bind to dynamically bind data to prop the parent component. Whenever data changes parent element, this change will be conducted to the sub-assembly:

<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>
你也可以使用 v-bind 的缩写语法:
<child :my-message="parentMsg"></child>
结果:

Message from parent
 
Message from parent

If you want all the attributes of an object is passed as a prop, v-bind can be used without any arguments (i.e., instead of v-bind with v-bind: prop-name). For example, it is known a todo objects:

todo: {
  text: 'Learn Vue',
  isComplete: false
}
然后:
<todo-item v-bind="todo"></todo-item>
将等价于:
<todo-item
  v-bind:text="todo.text"
  v-bind:is-complete="todo.isComplete"
></todo-item>

Literal syntax vs dynamic grammar

A beginner often make errors using literal value transfer syntax:
<! - passed a string ". 1" ->
<= prop-CoMP some ". 1"> </ CoMP>
because it is a literal prop, its value is a string of "1" instead of a value. If you want to pass a JavaScript real value, you need to use v-bind, so it's value is calculated as a JavaScript expression:
<- - deliver real value!>
<CoMP v-the bind: some-prop = "1"> </ comp>

Unidirectional data flow

Prop binding is unidirectional: when the attribute change of the parent component, is conducted to the subassembly, but not vice versa. This is to prevent undesired modification of the sub-assembly between the parent component state, to avoid data stream application becomes difficult to understand.
In addition, each time a parent component update, all the prop will be updated to the latest value of the sub-assemblies. This means that you should not change prop inside the subassembly. If you do that, Vue will give a warning in the console.
In both cases, we could not readily want to modify the data prop:
After Prop passed as an initial value, the subassembly as it wants to use local data;
Prop incoming original data, processed by the sub-assembly into other data output.
Both cases, the correct response is:
the definition of a local variable, and initialize it with a value of prop:

props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}
定义一个计算属性,处理 prop 的值并返回:
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

Note that in JavaScript objects and arrays are reference types point to the same memory space, if the prop is an object or array, changing it will affect the state of the parent component within the sub-components.

Prop verification

We can prop validation rules specified components. If the incoming data does not meet the requirements, Vue will be issued a warning. This is useful for the development of components for others to use.
To specify validation rules need to be defined using object prop, rather than using an array of strings:

Vue.component('example', {
  props: {
    // 基础类型检测 (`null` 指允许任何类型)
    propA: Number,
    // 可能是多种类型
    propB: [String, Number],
    // 必传且是字符串
    propC: {
      type: String,
      required: true
    },
    // 数值且有默认值
    propD: {
      type: Number,
      default: 100
    },
    // 数组/对象的默认值应当由一个工厂函数返回
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // 自定义验证函数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})
type 可以是下面原生构造器:
String
Number
Boolean
Function
Object
Array
Symbol

type may be a custom constructor function, using instanceof detected.
When prop verification fails, Vue will throw a warning (if you are using a development version). Note prop will be verified before the component instance is created, it is in default or in the validator function, such as a data, computed instance properties or methods can not be used.
Non-Prop properties

The so-called non-prop properties, meaning it can direct incoming assembly, without the need to define the corresponding prop.
Despite the clear component definition for the prop is the recommended way to pass parameters, the authors of the components but not always foresee the scene components to be used. Therefore, any component can receive incoming properties, which will be added to the root element assembly.
For example, suppose we use a third-party components bs-date-input, it contains a Bootstrap plug-in, you need to add data-3d-date-picker feature on this input. At this characteristic can be added directly to the assembly (no pre-defined prop):
<INPUT BS-DATE-Data-3D-DATE-Picker = "to true"> </ BS-DATE-INPUT>
add the attribute data-3d- after the date-picker = "true", it will be automatically added to the root element of bs-date-input.
Replace / merging existing features

Assuming this is bs-date-input template:
<the INPUT of the type = "DATE" class = "form-Control">
In order to give the date picker plugin adds a special theme, we may need to add a special class, for example:

<bs-date-input
  data-3d-date-picker="true"
  class="date-picker-theme-dark"
></bs-date-input>

In this example, we define two different class values:
form-Control, a template from the component itself
date-picker-theme-dark, from the parent component
for most properties, the value passed to the component itself will cover assembly the set value. That is, for example, transmission type = "large" will override type = "date" and possibly damaging the assembly! Fortunately, we deal with class and style characteristics are smarter, the value of these two properties will be merged (merge) operation, so that ultimately generate value: form-control date-picker- theme-dark.
Custom Event

We know that the use of prop parent component to pass data to a subcomponent. But how subassembly components that communicate with a parent do? This time Vue custom event system will come in handy.
Using the v-on binding custom events

Each Vue instance implements the event interface, namely:
the use of $ on (eventName) monitor events
using $ emit (eventName) trigger event
Vue event system and browser EventTarget API is different. Despite their similar running, but $ on $ emit and not an alias of addEventListener and dispatchEvent.

In addition, the parent component can be directly used v-on to listen to events triggered by the subassembly where the use of sub-assemblies.
It can not be released $ on the event component listens son, and must bind with v-on directly in the template, see the following examples.

Below is an example:

<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
0
0 0

In the present embodiment, the sub-assembly and has its outer completely decoupled. All it does is report on their internal events, because the parent assembly may be concerned about these events. Note that this is very important.
Component binding to native events

Sometimes, you may want to monitor a native event on the root element of a component. You can use v-on modifiers .native. For example:
<Component V-My-ON: click.native = "doTheThing"> </ My-Component>
.sync Modifier

2.3.0+
In some cases, we may need a prop for a "two-way binding." In fact, this is the function 1.x Vue in .sync modifiers offer. When a sub-assembly to change the value of a prop with .sync of this change will be synchronized to a value in the parent component is bound. This is convenient, but it can also cause problems, because it destroys the way data flow. Since the sub-assembly code changes and the general state of prop changes to the code, without distinction, when the optical subassembly look at the code, you do not know when it quietly changed the status of the parent component. This application in the debug complex structure will bring high maintenance costs.
The above mentioned reason why we .sync removed in 2.0. In practice, however after the release of 2.0, we found .sync still has its place applicable, such as in the development of reusable component library. We need to do is make changes in the parent component sub-assemblies state code easier to distinguish.
From 2.3.0 we reintroduced .sync modifiers, but this time it's just syntactic sugar exists as a compile-time. It will be extended to automatically update a parent component attribute v-on listener.
The following code
<comp: foo.sync = "bar" > </ comp>
is extended to:
<CoMP: foo = "bar" @Update: foo = "Val => bar = Val"> </ CoMP>
when the sub foo components require update value, which needs to explicitly trigger an update event:
. $ EMIT the this ( 'update: foo', newValue)
use custom events form input component

Custom events can be used to create custom components form input, using the v-model to carry out two-way data binding. To keep in mind:

<input v-model="something">
这不过是以下示例的语法糖:
<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">
所以在组件中使用时,它相当于下面的简写:
<custom-input
  v-bind:value="something"
  v-on:input="something = arguments[0]">
</custom-input>

So let components of the v-model into effect, it should be (starting from 2.2.0 is configurable):
accept a value prop
trigger input event when there is a new value and new value as a parameter
we look at a very simple enter the custom control money:

<currency-input v-model="price"></currency-input>
Vue.component('currency-input', {
  template: '\
    <span>\
      $\
      <input\
        ref="input"\
        v-bind:value="value"\
        v-on:input="updateValue($event.target.value)"\
      >\
    </span>\
  ',
  props: ['value'],
  methods: {
    // 不是直接更新值,而是使用此方法来对输入值进行格式化和位数限制
    updateValue: function (value) {
      var formattedValue = value
        // 删除两侧的空格符
        .trim()
        // 保留 2 位小数
        .slice(
          0,
          value.indexOf('.') === -1
            ? value.length
            : value.indexOf('.') + 3
        )
      // 如果值尚不合规,则手动覆盖为合规的值
      if (formattedValue !== value) {
        this.$refs.input.value = formattedValue
      }
      // 通过 input 事件带出数值
      this.$emit('input', Number(formattedValue))
    }
  }
})
$  

Of course, the above example is still relatively early. For example, a plurality of user input decimal point or period is allowed, it was disgusting! So we need a few examples of the complex, the following is a more complete monetary Filters:

Custom components v-model

2.2.0 New
By default, a component of the v-model will use the value prop and input events. However, such radio button, check box or the like enter the type of the value may be used as the other purposes. model option to avoid such a conflict:

Vue.component('my-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean,
    // 这样就允许拿 `value` 这个 prop 做其它事了
    value: String
  },
  // ...
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>
上述代码等价于:
<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>
注意你仍然需要显式声明 checked 这个 prop。

Sons non-communication assembly

Sometimes, the need for communication between two components of non-paternity. In the simplest scenario, you can use an empty bus Vue instance as an event:

var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
// 在组件 B 创建的钩子中监听事件
bus.$on('id-selected', function (id) {
  // ...
})

In complex cases, we should consider the use of specialized state management model.
Use content distribution slot

When using the component, we often want to combine them like this:

<app>
  <app-header></app-header>
  <app-footer></app-footer>
</app>

Note two things:
<App> component does not know what it will receive. This is used by the <app> parent element determined.
<app> component is likely to have its own template.
To make components may be combined, we need a way to mix the contents and sub-assemblies own template parent component. This process is referred to as content delivery (i.e. Angular user familiar "transclusion"). Vue.js implement a content distribution API, referring to the current Web Components draft specification, use the special <slot> element as the original content of the slot.
Compile Scope

Before delving into the content distribution API, let's clear the contents compiled in which a scope is. Template is assumed:
<Child-Component>
{}} {Message
</ Child-Component>
Message parent component should be bound to data, the data sub-assembly is bound to the? The answer is the parent component. Components scope Simply put is:
compile the contents of the parent component within the template parent component role; compile the contents of sub-components within the template subcomponents role.
A common mistake is trying to parent components within the template will bind a command to a subcomponent of the properties / methods:
<! - ineffective ->
<Child-v-Show the Component = "someChildProperty"> </ Child-the Component>
assumed someChildProperty subassembly is a property, the embodiment will not work as expected. Parent component templates do not perceive the state subcomponents.
If you want to bind subassemblies within the scope of the directive to the root of a component, you should do subassembly own template:

Vue.component('child-component', {
  // 有效,因为是在正确的作用域内
  template: '<div v-show="someChildProperty">Child</div>',
  data: function () {
    return {
      someChildProperty: true
    }
  }
})

Similarly, the content will be distributed compiling domain in the parent role.
Single Slot

Unless subcomponents template contains at least one <slot> jack, otherwise the contents of the parent component will be discarded. When the slot is not a sub-assembly template only attribute, the entire contents of the parent component of the incoming fragment inserted into the slot position where the DOM, and replace the label on the slot itself.
Initially any content <slot> tag are considered alternate content. Auxiliary content compile action subassembly art, and only in a host element is empty, and if there is no content to insert auxiliary content display.
My-component assembly assumes the following templates:

<div>
  <h2>我是子组件的标题</h2>
  <slot>
    只有在没有要分发的内容时才会显示。
  </slot>
</div>
父组件模板:
<div>
  <h1>我是父组件的标题</h1>
  <my-component>
    <p>这是一些初始内容</p>
    <p>这是更多的初始内容</p>
  </my-component>
</div>
渲染结果:
<div>
  <h1>我是父组件的标题</h1>
  <div>
    <h2>我是子组件的标题</h2>
    <p>这是一些初始内容</p>
    <p>这是更多的初始内容</p>
  </div>
</div>
具名插槽

<slot> element can use a special feature name to further configure how to distribute content. Multiple slots may have different names. The slot named segment matches the corresponding slot with a characteristic element.
We can still have an anonymous slot, which is the default slot as a spare slot pieces of content did not match. If there is no default slots, these pieces of content did not match will be abandoned.
For example, suppose we have an app-layout component that template is:

<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
父组件模板:
<app-layout>
  <h1 slot="header">这里可能是一个页面标题</h1>
  <p>主要内容的一个段落。</p>
  <p>另一个主要段落。</p>
  <p slot="footer">这里有一些联系信息</p>
</app-layout>
渲染结果为:
<div class="container">
  <header>
    <h1>这里可能是一个页面标题</h1>
  </header>
  <main>
    <p>主要内容的一个段落。</p>
    <p>另一个主要段落。</p>
  </main>
  <footer>
    <p>这里有一些联系信息</p>
  </footer>
</div>
在设计组合使用的组件时,内容分发 API 是非常有用的机制。
作用域插槽

2.1.0 Add
scope slot is a special type of slot, as a (data can be transferred) reusable template, the rendered elements already in place.
In sub-assembly, simply pass the data to the slot, just as you would prop the component passed as:

<div class="child">
  <slot text="hello from child"></slot>
</div>

<Template> element in the parent having special characteristics slot-scope must exist, indicating that it is the scope of the template slot. Slot-scope values ​​to be used as a temporary variable name, prop receiving this variable from the object is passed over the sub-assembly:

<div class="parent">
  <child>
    <template slot-scope="props">
      <span>hello from parent</span>
      <span>{{ props.text }}</span>
    </template>
  </child>
</div>
如果我们渲染上述模板,得到的输出会是:
<div class="parent">
  <div class="child">
    <span>hello from parent</span>
    <span>hello from child</span>
  </div>
</div>

In 2.5.0 +, slot-scope can be used in any element or component is no longer limited to <template>.
Scope slot embodiment is more typically used in the component list, allow the user to customize how to render each list:

<my-awesome-list :items="items">
  <!-- 作用域插槽也可以是具名的 -->
  <li
    slot="item"
    slot-scope="props"
    class="my-fancy-item">
    {{ props.text }}
  </li>
</my-awesome-list>
列表组件的模板:
<ul>
  <slot name="item"
    v-for="item in items"
    :text="item.text">
    <!-- 这里写入备用内容 -->
  </slot>
</ul>
解构

the value of slot-scope is actually a legitimate JavaScript can appear in an expression signature function parameter location. This means that you can also use a supported environment (single file component or modern browsers) in the expression ES2015 deconstruction:
<Child>
<span slot-scope = "{text}"> {{text}} </ span>
</ Child>
dynamic component

By using the reserved <component> element, dynamically bound to it is characteristic, we make a number of components can use the same mount point and dynamic switching:

var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: { /* ... */ },
    posts: { /* ... */ },
    archive: { /* ... */ }
  }
})
<component v-bind:is="currentView">
  <!-- 组件在 vm.currentview 变化时改变! -->
</component>
也可以直接绑定到组件对象上:
var Home = {
  template: '<p>Welcome home!</p>'
}
var vm = new Vue({
  el: '#example',
  data: {
    currentView: Home
  }
})
keep-alive

If the switched-out assembly remains in memory, or can retain its status to avoid re-rendering. For this purpose, a keep-alive add command parameters:
<keep-alive>
<Component: IS = "CurrentView">

<!-- 非活动组件将被缓存! -->

</ Component>
</ Keep-Alive>
Read <keep-alive> API Reference in detail.
Miscellaneous

Write reusable components

In the preparation of the component, whether or not to reuse the best after a good consideration. Disposable components are closely inter-coupling does not matter, but a reusable component should define a clear exposed interface, while not making any assumptions used by the data of its outer layer.
API Vue from the assembly of three parts --prop, events and slots:
Prop allow data to pass the external environment component;
event trigger side effects allow the assembly from the external environment;
slot allows additional content to the external environment in combination assembly.
Using the v-bind and v-on shorthand syntax, the intent of the template will be more clear and concise:

<my-component
  :foo="baz"
  :bar="qux"
  @event-a="doThis"
  @event-b="doThat"
>
  <img slot="icon" src="...">
  <p slot="main-text">Hello!</p>
</my-component>

Subassemblies reference

Despite the prop and events, but sometimes still need direct access to sub-assemblies in JavaScript. Ref may be used for this sub-assembly is designated a reference ID. For example:
<div ID = "parent">
<REF = User-Profile "Profile"> </ User-Profile>
</ div>
var = new new parent Vue ({EL: '#parent'})
// access subassembly examples
var child = parent. $ refs.profile
when used with ref and v-for, the acquired reference may be an array containing the data sources and the corresponding cyclic subassembly.
$ refs rendering is complete only after the assembly is filled, and it is non-responsive. It is only a direct operation of the emergency scheme subassembly - $ refs should be avoided in the template or computing properties.

Asynchronous component

In large applications, we may need to split the application into multiple small modules, on-demand download from the server. To further simplify, Vue.js allows the assembly plant is defined as a defined function, the parsing component asynchronously. Vue.js triggered only when the component factory functions need to be rendered, and the result is cached for rendering back again. E.g:

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 将组件定义传入 resolve 回调函数
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

Factory callback function receives a resolve, calling upon receipt of component definitions downloaded from the server. You can also call reject (reason) indicates the load failed. SetTimeout used here only to demonstrate, in fact, how to obtain the components is entirely up to you. Recommended fits webpack code division function is used:

Vue.component('async-webpack-example', function (resolve) {
  // 这个特殊的 require 语法告诉 webpack
  // 自动将编译后的代码分割成不同的块,
  // 这些块将通过 Ajax 请求自动下载。
  require(['./my-async-component'], resolve)
})
你可以在工厂函数中返回一个 Promise,所以当使用 webpack 2 + ES2015 的语法时可以这样:
Vue.component(
  'async-webpack-example',
  // 该 `import` 函数返回一个 `Promise` 对象。
  () => import('./my-async-component')
)
当使用局部注册时,也可以直接提供一个返回 Promise 的函数:
new Vue({
  // ...
  components: {
    'my-component': () => import('./my-async-component')
  }
})

If you are Browserify users may not be able to use asynchronous components, and its authors have shown that Browserify would "never support asynchronous loading." Browserify community found some solutions that may contribute to complex applications that already exists. For other scenarios, we recommend using webpack, because it was built asynchronous loading, comprehensive support.

Other stay tuned. . .

Guess you like

Origin www.cnblogs.com/baimeishaoxia/p/12196254.html