注册
注册一个全局组件:Vue.component(tagName, options) Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
<div id="example">
<my-component></my-component>
</div> new Vue({
el: '#example'
}) 局部注册:通过组件实例选项注册
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
el: '#example',
components: {//<my-component> 将只在父模板可用
'my-component': Child
}
})
使用is属性:
<table>
<tr is="my-row"></tr>
//==<my-row>...</my-row>,table下标签有HTML的限制可以换用is的形式
</table>
使用模板,这些限制将不适用:
<script type="text/x-template">
JavaScript 内联模板字符串
- .vue 组件
data必须是一个函数,并且,如果返回一个公共的变量,实例之间将共享数据。
props://prop 是单向绑定的
<child my-message="hello!"></child>
//当使用的不是字符串模板,camelCased (驼峰式) 命名的 prop 需要转换为相对应的 kebab-case (短横线隔开式) 命名:
Vue.component('child', {
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
动态prop:
<div>
<input v-model="parentMsg">
<child :my-message="parentMsg"></child>
</div>
props 传递所有的属性,绑定一个对象: todo: {
text: 'Learn Vue',
isComplete: false
}
<child v-bind="todo"></child>
<!-- 传递实际的 number -->
<comp v-bind:some-prop=""></comp>
props验证 原生构造器类型:
String 、Number、 Boolean、 Function、 Object、 Array、 Symbol
type 也可以是一个自定义构造器函数,使用 instanceof 检测。 Vue.component('example', {
props: {
// 基础类型检测 (`null` 意思是任何类型都可以)
propA: Number,
// 多种类型
propB: [String, Number],
// 必传且是字符串
propC: {
type: String,
required: true
},
// 数字,有默认值
propD: {
type: Number,
default:
},
// 数组/对象的默认值应当由一个工厂函数返回
propE: {
type: Object,
default: function () {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator: function (value) {
return value >
}
}
}
})
非prop属性,也允许加入到属性,(如一些第三方组件,可以把属性直接添加到组件上 ,不需要事先定义 prop
)
<bs-date-input data-3d-date-picker="true"></bs-date-input>
从父组件传来的属性值,如class会和组件模板定义的同名属性合并
自定义事件
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter> //$on用来监听increment事件
<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:
}
},
methods: {
incrementCounter: function () {
this.counter +=
this.$emit('increment') //$emit用来触发increment事件,调用incrementTotal方法
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total:
},
methods: {
incrementTotal: function () {
this.total +=
}
}
})
.native 修饰符
//在某个组件的根元素上监听一个原生事件。可以使用 .native 修饰 v-on
<my-component v-on:click.native="doTheThing"></my-component>
.sync 修饰符
//双向绑定,只是作为一个编译时的语法糖
<comp :foo.sync="bar"></comp>
自定义的表单输入组件
货币筛选器
html:
<div id="app">
<currency-input label="Price" v-model="price"></currency-input>
<currency-input label="Shipping" v-model="shipping"></currency-input>
<currency-input label="Handling" v-model="handling"></currency-input>
<currency-input label="Discount" v-model="discount"></currency-input>
<p>Total: ${{ total }}</p>
</div> js:
Vue.component('currency-input', {
template: '\
<div>\
<label v-if="label">{{ label }}</label>\
$\
<input\
ref="input"\
v-bind:value="value"\
v-on:input="updateValue($event.target.value)"\
v-on:focus="selectAll"\
v-on:blur="formatValue"\
>\
</div>\
',
props: {
value: {
type: Number,
default:
},
label: {
type: String,
default: ''
}
},
mounted: function () {
this.formatValue()
},
methods: {
updateValue: function (value) {
var result = currencyValidator.parse(value, this.value)
if (result.warning) {
this.$refs.input.value = result.value
}
this.$emit('input', result.value)
},
formatValue: function () {
this.$refs.input.value = currencyValidator.format(this.value)
},
selectAll: function (event) {
setTimeout(function () {
event.target.select()
}, )
}
}
}) new Vue({
el: '#app',
data: {
price: ,
shipping: ,
handling: ,
discount:
},
computed: {
total: function () {
return ((
this.price * +
this.shipping * +
this.handling * -
this.discount *
) / ).toFixed()
}
}
})
自定义组件的v-model,默认一个组件的 v-model
会使用 value
属性和 input
事件,但是诸如单选框、复选框之类的输入类型把 value
属性用作了别的目的。这时需要设置组件的model选项来指定prop和event
<my-checkbox v-model="foo" value="some value"></my-checkbox>
Vue.component('my-checkbox', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean,
// this allows using the `value` prop for a different purpose
value: String
},
// ...
})
编译的作用域:
<child-component v-show="someChildProperty"></child-component>
//someChildProperty存在于父作用域
Vue.component('child-component', {
// someChildProperty存在于子作用域
template: '<div v-show="someChildProperty">Child</div>',
data: function () {
return {
someChildProperty: true
}
}
})
内容分发:插槽
<div>
<h2>我是子组件的标题</h2>
<slot>
只有在没有要分发的内容时才会显示。
</slot>
<slot name="footer"></slot>
//具名插槽
</div>
<div>
<h1>我是父组件的标题</h1>
<my-component>
<p>这是一些初始内容</p>
<p>这是更多的初始内容</p>
<p slot="footer">这里有一些联系信息</p>
</my-component>
</div>
作用域插槽://子组件的内容可以在父组件指定
父:
<my-awesome-list :items="items">
<!-- 作用域插槽也可以是具名的 -->
<template slot="item" scope="props">
<li class="my-fancy-item">{{ props.text }}</li>
</template>
</my-awesome-list> 子:
<ul>
<slot name="item"
v-for="item in items"
:text="item.text">
<!-- 这里写入备用内容 -->
</slot>
</ul>
动态组件
var vm = new Vue({
el: '#example',
data: {
currentView: 'home'
},
components: {
home: { /* ... */ },
posts: { /* ... */ },
archive: { /* ... */ }
}
}) <component v-bind:is="currentView">
<!-- 组件在 vm.currentview 变化时改变! is指向的组件名随之变化-->
</component>
如果把切换出去的组件保留在内存中, keep-alive,
可以保留它的状态或避免重新渲染
<keep-alive>
<component :is="currentView">
<!-- 非活动组件将被缓存! -->
</component>
</keep-alive>
子组件索引:ref
为子组件指定一个索引 ID
<div id="parent">
<user-profile ref="profile"></user-profile>
</div> var parent = new Vue({ el: '#parent' }) // 直接访问子组件
var child = parent.$refs.profile
异步组件
全局异步组件:
Vue.component(
'async-webpack-example',
() => import('./my-async-component')
) 局部异步组件:
new Vue({
// ...
components: {
'my-component': () => import('./my-async-component')
}
}) 高级异步组件://当一个异步组件被作为vue-router
的路由组件使用时,这些高级选项都是无效的,因为在路由切换前就会提前加载所需要的异步组件
const AsyncComp = () => ({
// 需要加载的组件。应当是一个 Promise
component: import('./MyComp.vue'),
// loading 时应当渲染的组件
loading: LoadingComp,
// 出错时渲染的组件
error: ErrorComp,
// 渲染 loading 组件前的等待时间。默认:200ms。
delay: ,
// 最长等待时间。超出此时间则渲染 error 组件。默认:Infinity
timeout:
})
组件命名约定:
当注册组件 (或者 props) 时,可以使用 kebab-case,camelCase,或 PascalCase。
// 在组件定义中
components: {
// 使用 kebab-case 形式注册
'kebab-cased-component': { /* ... */ },
// register using camelCase
'camelCasedComponent': { /* ... */ },
// register using 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>
如果组件未经 slot
元素传递内容,你甚至可以在组件名后使用 /
使其自闭合<my-component/>
递归组件:
组件在它的模板内可以递归地调用自己,不过,只有当它有 name 选项时才可以
Vue.component('stack-overflow', {
template: '<div><stack-overflow></stack-overflow></div>'
})
父组件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>
循环引用时,需向注明模块化管理系统循环引用的组件间的处理优先级: beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
}
内联模板
如果子组件有 inline-template 特性,组件将把它的内容当作它的模板,而不是把它当作分发内容。这让模板更灵活,不推荐。 <my-component inline-template>
<div>
<p>These are compiled as the component's own template.</p>
<p>Not parent's transclusion content.</p>
</div>
</my-component>
模板x-template:
//x-template:
<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.component('terms-of-service', {
template: '\
<div v-once>\
<h1>Terms of Service</h1>\
... a lot of static content ...\
</div>\
'
})