组件html
# 建立一个 Vue 实例:
new Vue({
el: '#some-element',
// 选项
})
# 注册全局组件
Vue.component('my-component', {
// 选项 組件名小写,而且包含一个短杠
})
组件在注册以后,即可以做为自定义元素 ```<my-component></my-component>```
# 局部注册
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父组件模板中可用
'my-component': Child
}
})
复制代码
DOM 模板解析注意事项vue
<ul>、<ol>、<table>、<select>这样的元素里容许包含的元素有限制,而另外一些像<option> 这样的元素
只能出如今某些特定元素的内部。
在自定义组件中使用这些受限制的元素时会致使一些问题,例如:
<table>
<my-row>...</my-row>
</table>
自定义组件 <my-row>
会被看成无效的内容,所以会致使错误的渲染结果。变通的方案是使用特殊的 is 特性:
<table>
<tr is="my-row"></tr>
</table>
复制代码
datawebpack
必须是函数:可是在组件中,由于可能在多处调用同一组件,因此为了避免让多处的组件共享同一data对象,只能
返回函数。
那么 Vue 会中止运行,并在控制台发出警告,告诉你在组件实例中 data 必须是一个函数。但理解这种规则
为什么存在也是颇有益处的,因此让咱们先做个弊:
<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'
})
因为这三个组件实例共享了同一个 data 对象,所以递增一个 counter 会影响全部组件!这就错了。咱们能够
经过为每一个组件返回全新的数据对象来修复这个问题:
data: function () {
return {
counter: 0
}
}
如今每一个 counter 都有它本身内部的状态了:
复制代码
父子组件Propweb
prop 向下传递,事件向上传递。父组件的数据须要经过 prop才能下发到子组件中。
子组件要显式地用 props 选项声明它预期的数据:
Vue.component('child', {
// 声明 props
props: ['message'],
// 就像 data 同样,prop 也能够在模板中使用
// 一样也能够在 vm 实例中经过 this.message 来使用
template: '<span>{{ message }}</span>'
})
而后咱们能够这样向它传入一个普通字符串:
<child message="hello!"></child>
# 动态 Prop
与绑定到任何普通的 HTML 特性相相似,咱们能够用 v-bind 来动态地将 prop 绑定到父组件的数据。每当
父组件的数据变化时,该变化也会传导给子组件:
<child v-bind:my-message="parentMsg"></child>
# Message from parent
若是你想把一个对象的全部属性做为 prop 进行传递,可使用不带任何参数的 v-bind (即用 v-bind 而
不是 v-bind:prop-name)。例如,已知一个 todo 对象:
todo: {
text: 'Learn Vue',
isComplete: false
}
<todo-item
:text="todo.text"
:is-complete="todo.isComplete"
/>
# 字面量语法 vs 动态语法
初学者常犯的一个错误是使用字面量语法传递数值:
<!-- 传递了一个字符串 "1" -->
<comp some-prop="1"></comp>
由于它是一个字面量 prop,它的值是字符串 "1" 而不是一个数值。若是想传递一个真正的 JavaScript
数值,则须要使用 v-bind,从而让它的值被看成 JavaScript 表达式计算:
<!-- 传递真正的数值 -->
<comp v-bind:some-prop="1"></comp>
# 单向数据流:Prop 是单向绑定的
每次父组件更新时,子组件的全部 prop 都会更新为最新值。
在两种状况下,咱们很容易忍不住想去修改 prop 中数据:
Prop 做为初始值传入后,子组件想把它看成局部数据来用;
Prop 做为原始数据传入,由子组件处理成其它数据输出。
对这两种状况,正确的应对方式是:
定义一个局部变量,并用 prop 的值初始化它:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
定义一个计算属性,处理 prop 的值并返回:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
注意在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,若是 prop 是一个对象或数组,在子
组件内部改变它会影响父组件的状态。
# Prop 验证
为组件的 prop 指定验证规则。若是传入的数据不符合要求,Vue 会发出警告。这对于开发给他人使用的组件
很是有用。
要指定验证规则,须要用对象的形式来定义 prop,而不能用字符串数组:
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 也能够是一个自定义构造器函数,使用 instanceof 检测。
当 prop 验证失败,Vue 会抛出警告 (若是使用的是开发版本)。注意 prop 会在组件实例建立以前进行校验,
因此在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还没法使用。
# 非 Prop 特性:能够直接传入组件,而不须要定义相应的 prop。
组件能够接收任意传入的特性,这些特性都会被添加到组件的根元素上。
把特性直接添加到组件上 (不须要事先定义 prop):
<bs-date-input data-3d-date-picker="true"></bs-date-input>
# 替换/合并现有的特性
<input type="date" class="form-control">
为了给该日期选择器插件增长一个特殊的主题,咱们可能须要增长一个特殊的 class,好比:
<bs-date-input
data-3d-date-picker="true"
class="date-picker-theme-dark"
></bs-date-input>
在这个例子当中,咱们定义了两个不一样的 class 值:
对于多数特性来讲,传递给组件的值会覆盖组件自己设定的值。即例如传递 type="large" 将会覆盖
type="date" 且有可能破坏该组件!所幸咱们对待 class 和 style 特性会更聪明一些,这两个特性的值都会作
合并 (merge) 操做,让最终生成的值为:form-control date-picker-theme-dark。
复制代码
子-父通讯:$emit&自定义事件系统vue-router
使用 $on(eventName) 监听事件
使用 $emit(eventName, optionalPayload) 触发事件
父组件能够在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
不能用 $on 监听子组件释放的事件,而必须在模板里直接用 v-on 绑定,参见下面的例子。
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter @increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
template: '<button @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
}
}
})
在本例中,子组件已经和它外部彻底解耦了。它所作的只是报告本身的内部事件,由于父组件可能会关心这些事件。
# 这里有一个如何使用载荷 (payload) 数据的示例:
<div id="message-event-example" class="demo">
<p v-for="msg in messages">{{ msg }}</p>
<button-message @message="handleMessage"></button-message>
</div>
Vue.component('button-message', {
template: `<div>
<input type="text" v-model="message" />
<button @click="handleSendMessage">Send</button>
</div>`,
data: function () {
return {
message: 'test message'
}
},
methods: {
handleSendMessage: function () {
this.$emit('message', { message: this.message })
}
}
})
new Vue({
el: '#message-event-example',
data: {
messages: []
},
methods: {
handleMessage: function (payload) {
this.messages.push(payload.message)
}
}
})
记录其自身的活动,活动记录是包括一份传入事件触发器的载荷数据在内的,只是为了展现父组件能够不关注的
一个场景。
# camelCase vs. kebab-case
HTML 特性是不区分大小写的。因此,当使用的不是字符串模板时,camelCase (驼峰式命名) 的 prop
须要转换为相对应的 kebab-case (短横线分隔式命名):
Vue.component('child', {
// 在 JavaScript 中使用 camelCase
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
<!-- 在 HTML 中使用 kebab-case -->
<child my-message="hello!"></child>
若是你使用字符串模板,则没有这些限制。
复制代码
非父子组件的通讯 vue实例数组
非父子关系的两个组件之间通讯。
# 在简单的场景下,可使用一个空的 Vue 实例做为事件总线:
var bus = new Vue()
// 触发组件 A 中的事件
bus.$emit('id-selected', 1)
// 在组件 B 建立的钩子中监听事件
bus.$on('id-selected', function (id) {
// ...
})
# 在复杂的状况下,咱们应该考虑使用专门的状态管理模式。
复制代码
给组件绑定原生事件浏览器
在某个组件的根元素上监听一个原生事件。可使用 v-on 的修饰符 .native。例如:
<my-component @click.native="doTheThing"></my-component>
# .sync 修饰符(2.3.0+)
对一个 prop 进行“双向绑定”.当一个子组件改变了一个带 .sync 的 prop 的值时,这个变化也会同步到
父组件中所绑定的值。这很方便,但也会致使问题,由于它破坏了单向数据流。在 2.0 发布以后的实际应用中,
.sync 仍是有其适用之处,好比在开发可复用的组件库时。咱们须要作的只是让子组件改变父组件状态的代码更
容易被区分。
可是此次它只是做为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。
以下代码
<comp :foo.sync="bar"></comp>
会被扩展为:
<comp :foo="bar" @update:foo="val => bar = val"></comp>
当子组件须要更新 foo 的值时,它须要显式地触发一个更新事件:
this.$emit('update:foo', newValue)
当使用一个对象一次性设置多个属性的时候,这个 .sync 修饰符也能够和 v-bind 一块儿使用:
<comp v-bind.sync="{ foo: 1, bar: 2 }"></comp>
这个例子会为 foo 和 bar 同时添加用于更新的 v-on 监听器。
# 使用自定义事件的表单输入组件
自定义事件能够用来建立自定义的表单输入组件,使用 v-model 来进行数据双向绑定。要牢记:
<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>
因此要让组件的 v-model 生效,它应该 (从 2.2.0 起是可配置的):
#接受一个 value prop(待研究)
在有新的值时触发 input 事件并将新值做为参数
咱们来看一个很是简单的货币输入的自定义控件:
<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))
}
}
})
固然,上面的例子仍是比较初级的。好比,用户输入多个小数点或句号也是容许的,好恶心吧!所以咱们须要
一个复杂一些的例子,下面是一个更加完善的货币过滤器:
# 自定义组件的 v-model(2.2.0 新增)
默认状况下,一个组件的 v-model 会使用 value prop 和 input 事件。可是诸如单选框、复选框之类的
输入类型可能把 value 用做了别的目的。model 选项能够避免这样的冲突:
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。
复制代码
内容分发slot缓存
简单来讲,假如父组件须要在子组件内放一些DOM,那么这些DOM是显示、不显示、在哪一个地方显示、如何显示,
就是slot分发负责的活。这个过程被称为内容分发 (即 Angular 用户熟知的“transclusion”)。Vue.js
实现了一个内容分发 API,参照了当前 Web Components 规范草案,使用特殊的 <slot>元素做为原始内容
的插槽。
# 编译做用域
在深刻内容分发 API 以前,咱们先明确内容在哪一个做用域里编译。假定模板为:
<child-component>
{{ message }}
</child-component>
message 应该绑定到父组件的数据,组件做用域简单地说是:
父组件模板的内容在父组件做用域内编译;子组件模板的内容在子组件做用域内编译。
一个常见错误是试图在父组件模板内将一个指令绑定到子组件的属性/方法:
<!-- 无效 -->
<child-component v-show="someChildProperty"></child-component>
假定 someChildProperty 是子组件的属性,上例不会如预期那样工做。父组件模板并不感知子组件的状态。
若是要绑定子组件做用域内的指令到一个组件的根节点,你应当在子组件本身的模板里作:
Vue.component('child-component', {
// 有效,由于是在正确的做用域内
template: '<div v-show="someChildProperty">Child</div>',
data: function () {
return {
someChildProperty: true
}
}
})
相似地,被分发的内容会在父做用域内编译。
# 单个插槽
除非子组件模板包含至少一个 <slot> 插口,不然父组件的内容将会被丢弃。当子组件模板只有一个没有属性
的插槽时,父组件传入的整个内容片断将插入到插槽所在的 DOM 位置,并替换掉插槽标签自己。
最初在<slot>标签中的任何内容都被视为备用内容。备用内容在子组件的做用域内编译,而且只有在宿主元素为
空,且没有要插入的内容时才显示备用内容。
假定 my-component 组件有以下模板:
<div>
<h2>我是子组件的标题</h2>
<slot>
只有在没有要分发的内容时才会显示。
</slot>
</div>
父组件模板:
<div>
<h1>我是父组件的标题</h1>
<my-component>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</my-component>
渲染结果:
<div>
<h1>我是父组件的标题</h1>
<div>
<h2>我是子组件的标题</h2>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
</div>
</div>
# 具名slot
将放在子组件里的不一样html标签放在不一样的位置
父组件在要分发的标签里添加 slot=”name名” 属性
子组件在对应分发的位置的slot标签里,添加name=”name名” 属性,
而后就会将对应的标签放在对应的位置了。
示例代码:
<div id="app">
<children>
<span slot="first">12345</span>
<span slot="second">56789</span>
<!--上面这行不会显示-->
</children>
</div>
<script>
var vm = new Vue({
el: '#app',
components: {
children: { //这个无返回值,不会继续派发
template: "<button> <slot name='first'></slot>为了明确做用范围, <slot name='second'></slot>因此使用button标签 </button>"
}
}
});
</script>
显示结果为:(为了方便查看,已手动调整换行)
<button>
<span slot="first">12345</span>
为了明确做用范围,
<span slot="second">56789</span>
因此使用button标签
</button>
# 做用域插槽(2.1.0 新增)
做用域插槽是一种特殊类型的插槽,用做一个 (能被传递数据的) 可重用模板,来代替已经渲染好的元素。
在子组件中,只需将数据传递到插槽,就像你将 prop 传递给组件同样:
<div class="child">
<slot text="hello from child"></slot>
</div>
在父级中,具备特殊特性 slot-scope 的 <template> 元素必须存在,表示它是做用域插槽的模板。
slot-scope 的值将被用做一个临时变量名,此变量接收从子组件传递过来的 prop 对象:
<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>
在 2.5.0+,slot-scope 能被用在任意元素或组件中而再也不局限于 <template>。
做用域插槽更典型的用例是在列表组件中,容许使用者自定义如何渲染列表的每一项:
<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>
# 解构
slot-scope 的值其实是一个能够出如今函数签名参数位置的合法的 JavaScript 表达式。这意味着在
受支持的环境 (单文件组件或现代浏览器)
<child>
<span slot-scope="{ text }">{{ text }}</span>
</child>
复制代码
动态组件bash
经过使用保留的 <component>元素,并对其 is 特性进行动态绑定,你能够在同一个挂载点动态切换多个
组件:
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服务器
若是把切换出去的组件保留在内存中,能够保留它的状态或避免从新渲染。为此能够添加一个 keep-alive
指令参数:
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
复制代码
编写可复用组件
在编写组件时,最好考虑好之后是否要进行复用。一次性组件间有紧密的耦合不要紧,可是可复用组件应当定义
一个清晰的公开接口,同时也不要对其使用的外层数据做出任何假设。
Vue 组件的 API 来自三部分——prop、事件和插槽:
Prop 容许外部环境传递数据给组件;
事件容许从组件内触发外部环境的反作用;
插槽容许外部环境将额外的内容组合在组件中。
使用 v-bind 和 v-on 的简写语法,模板的意图会更清楚且简洁:
<my-component
:foo="baz"
:bar="qux"
@event-a="doThis"
@event-b="doThat"
>
<img slot="icon" src="...">
<p slot="main-text">Hello!</p>
</my-component>
复制代码
子组件引用
尽管有 prop 和事件,可是有时仍然须要在 JavaScript 中直接访问子组件。为此可使用 ref 为子组件
指定一个引用 ID。例如:
// 访问子组件实例
当 ref 和 v-for 一块儿使用时,获取到的引用会是一个数组,包含和循环数据源对应的子组件。
$refs 只在组件渲染完成后才填充,而且它是非响应式的。它仅仅是一个直接操做子组件的应急方案——应当避
免在模板或计算属性中使用 $refs。
复制代码
异步组件
在大型应用中,咱们可能须要将应用拆分为多个小模块,按需从服务器下载。为了进一步简化,Vue.js 容许
将组件定义为一个工厂函数,异步地解析组件的定义。Vue.js 只在组件须要渲染时触
发工厂函数,而且把结果缓存起来,用于后面的再次渲染.
复制代码
工厂函数: 就是指这些内置(建)函数都是类对象,当你调用他们时,其实是建立了一个类实例”。意思就是当我调用这个函数,其实是先利用类建立了一个对象,而后返回这个对象 例如:
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 将组件定义传入 resolve 回调函数
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
工厂函数接收一个 resolve 回调,在收到从服务器下载的组件定义时调用。也能够调用 reject(reason)
指示加载失败。这里使用 setTimeout 只是为了演示,实际上如何获取组件彻底由你决定。推荐配合
webpack 的代码分割功能 来使用:
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')
}
})
若是你是 Browserify 用户,可能就没法使用异步组件了,它的做者已经代表 Browserify 将“永远不会支
持异步加载”。Browserify 社区发现了一些解决方法,可能会有助于已存在的复杂应用。对于其余场景,咱们
推荐使用 webpack,由于它对异步加载进行了内置、全面的支持。
复制代码
高级异步组件(2.3.0 新增)待研究
异步组件的工厂函数也能够返回一个以下的对象:
const AsyncComp = () => ({
// 须要加载的组件。应当是一个 Promise
component: import('./MyComp.vue'),
// 加载中应当渲染的组件
loading: LoadingComp,
// 出错时渲染的组件
error: ErrorComp,
// 渲染加载中组件前的等待时间。默认:200ms。
delay: 200,
// 最长等待时间。超出此时间则渲染错误组件。默认:Infinity
timeout: 3000
注意,当一个异步组件被做为 vue-router 的路由组件使用时,这些高级选项都是无效的,由于在路由切换前
就会提早加载所须要的异步组件。另外,若是你要在路由组件中使用上述写法,须要使用 vue-router 2.4.0
以上的版本。
复制代码
组件命名约定
当注册组件 (或者 prop) 时,可使用 kebab-case (短横线分隔命名)、camelCase (驼峰式命名) 或
PascalCase (单词首字母大写命名)。
// 在组件定义中
components: {
// 使用 kebab-case 注册
'kebab-cased-component': { /* ... */ },
// 使用 camelCase 注册
'camelCasedComponent': { /* ... */ },
// 使用 PascalCase 注册
'PascalCasedComponent': { /* ... */ }
}
在 HTML 模板中,请使用 kebab-case:
<!-- 在 HTML 模板中始终使用 kebab-case -->
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<pascal-cased-component></pascal-cased-component>
当使用字符串模式时,能够不受 HTML 大小写不敏感的限制。这意味实际上在模板中,你可使用下面的方式来
引用你的组件:
kebab-case
camelCase 或 kebab-case (若是组件已经被定义为 camelCase)
kebab-case、camelCase 或 PascalCase (若是组件已经被定义为 PascalCase)
components: {
'kebab-cased-component': { /* ... */ },
camelCasedComponent: { /* ... */ },
PascalCasedComponent: { /* ... */ }
}
<kebab-cased-component></kebab-cased-component>
<camel-cased-component></camel-cased-component>
<camelCasedComponent></camelCasedComponent>
<pascal-cased-component></pascal-cased-component>
<pascalCasedComponent></pascalCasedComponent>
<PascalCasedComponent></PascalCasedComponent>
这意味着 PascalCase 是最通用的声明约定而 kebab-case 是最通用的使用约定。
若是组件未经 slot 元素传入内容,你甚至能够在组件名后使用 / 使其自闭合:
<my-component/>
固然,这只在字符串模板中有效。由于自闭的自定义元素是无效的 HTML,浏览器原生的解析器也没法识别它。
复制代码
递归组件(待研究)
# 组件在它的模板内能够递归地调用本身。不过,只有当它有 name 选项时才能够这么作:
name: 'unique-name-of-my-component'
当你利用 Vue.component 全局注册了一个组件,全局的 ID 会被自动设置为组件的 name。
Vue.component('unique-name-of-my-component', {
// ...
})
若是稍有不慎,递归组件可能致使死循环:
name: 'stack-overflow',
template: '<div><stack-overflow></stack-overflow></div>'
上面组件会致使一个“max stack size exceeded”错误,因此要确保递归调用有终止条件 (好比递归调用时
使用 v-if 并最终解析为 false)。
# 组件间的循环引用
假设你正在构建一个文件目录树,像在 Finder 或资源管理器中。你可能有一个 tree-folder 组件:
<p>
<span>{{ folder.name }}</span>
<tree-folder-contents :children="folder.children"/>
</p>
以及一个 tree-folder-contents 组件:
<ul>
<li v-for="child in children">
<tree-folder v-if="child.children" :folder="child"/>
<span v-else>{{ child.name }}</span>
</li>
</ul>
当你仔细看时,会发如今渲染树上这两个组件同时为对方的父节点和子节点——这是矛盾的!当使用
Vue.component 将这两个组件注册为全局组件的时候,框架会自动为你解决这个矛盾。若是你已是这样作的
,就跳过下面这段吧。
然而,若是你使用诸如 webpack 或者 Browserify 之类的模块化管理工具来 require/import 组件的话,就会
报错了:
Failed to mount component: template or render function not defined.
为了解释为何会报错,简单的将上面两个组件称为 A 和 B。模块系统看到它须要 A,可是首先 A 须要 B,
可是 B 须要 A,而 A 须要 B,循环往复。由于不知道到底应该先解析哪一个,因此将会陷入无限循环。要解决
这个问题,咱们须要在其中一个组件中告诉模块化管理系统:“A 虽然最后会用到 B,可是不须要优先导入 B”。
在咱们的例子中,能够选择让 tree-folder 组件中来作这件事。咱们知道引发矛盾的子组件是
tree-folder-contents,因此咱们要等到 beforeCreate 生命周期钩子中才去注册它:
beforeCreate: function () {
this.$options.components.TreeFolderContents =
require('./tree-folder-contents.vue').default
}
复制代码
内联模板
# 若是子组件有 inline-template 特性,
组件将把它的内容看成它的模板,而不是把它看成分发内容。这让模板编写起来更灵活。
<my-component inline-template>
<div>
<p>这些将做为组件自身的模板。</p>
<p>而非父组件透传进来的内容。</p>
</div>
</my-component>
可是 inline-template 让模板的做用域难以理解。使用 template 选项在组件内定义模板或者在 .vue
文件中使用 template 元素才是最佳实践。
# X-Template
另外一种定义模板的方式是在 JavaScript 标签里使用 text/x-template 类型,而且指定一个 id。例如:
<script type="text/x-template" id="hello-world-template">
<p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
template: '#hello-world-template'
})
这在有不少大模板的演示应用或者特别小的应用中可能有用,其它场合应该避免使用,由于这将模板和组件的
其它定义分离了。
# 对低开销的静态组件使用 v-once
尽管在 Vue 中渲染 HTML 很快,不过当组件中包含大量静态内容时,能够考虑使用 v-once 将渲染结果缓存
起来,就像这样:
Vue.component('terms-of-service', {
template: '\ <div v-once>\ <h1>Terms of Service</h1>\ ...不少静态内容...\ </div>\ '
})
复制代码
$mount()
当Vue实例没有el属性时,则该实例尚没有挂载到某个dom中;
假如须要延迟挂载,能够在以后手动调用vm.$mount()方法来挂载。例如:
<div id="app">
{{a}}
</div>
<button onclick="test()">挂载</button>
<script>
var obj = {a: 1}
var vm = new Vue({
data: obj
})
function test() {
vm.$mount("#app");
}
初始,显示的是{{a}}
当点击按钮后,变成了1
复制代码