注册
注册一个全局组件: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="1"></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: 100 }, // 数组/对象的默认值应当由一个工厂函数返回 propE: { type: Object, default: function () { return { message: 'hello' } } }, // 自定义验证函数 propF: { validator: function (value) { return value > 10 } } } })
非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: 0 } }, methods: { incrementCounter: function () { this.counter += 1 this.$emit('increment') //$emit用来触发increment事件,调用incrementTotal方法 } }, })
new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { incrementTotal: function () { this.total += 1 } } })
.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: 0 }, 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() }, 0) } } }) new Vue({ el: '#app', data: { price: 0, shipping: 0, handling: 0, discount: 0 }, computed: { total: function () { return (( this.price * 100 + this.shipping * 100 + this.handling * 100 - this.discount * 100 ) / 100).toFixed(2) } } })
自定义组件的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: 200,
// 最长等待时间。超出此时间则渲染 error 组件。默认:Infinity
timeout: 3000
})
组件命名约定:
当注册组件 (或者 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> ' })