Vue学习笔记(三)
博客的Markdown解释器显示有问题,请移步github
Vue组件
组件基础
创建可复用的组件,利用Vue.component
方法
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me 8 times.</button>'
})
使用时可以像普通组件一样用,可以绑定在某个vue实例(vm)上,接受普通vm一样的属性值,比如data
,methods
,computed
,watch
以及一些声明周期方法等
<div id="components-demo">
<button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })
- 对于Vue组件,
data
必须是一个getter函数,而不是直接定义一个对象。
组件prop
组件间的prop
是父组件向子组件传递数据的方式,在Vue.component
方式中定义prop
:
Vue.component('blog-post', {
props: ['title'],
template: '<h3></h3>'
})
这样可以传递数据了
<blog-post title="Why Vue is so fun"></blog-post>
可以使用vue标签和组件prop配合,例如v-bind:title
。
单根元素限制
Vue在风格上强行限制了每一个组件的模板只能有一个根元素
子组件事件
Vue解决监听子组件事件的思维是:子组件监听原生事件=>子组件发出自定义事件=>由父组件处理自定义事件
-
子组件监听原生事件,当然是用
v-on:click
之类的方法 -
子组件发出自定义事件,使用内建的
$emit
方法<button v-on:click="$emit('action')"> button </button>
$emit
还可以跑出一个参数值,比如$emit('action',1)
-
父组件同样通过
v-on
监听事件<child v-on:action="handle"> </child>
- 如果子组件
$emit
抛出了值,父组件的处理函数将会将其作为第一个参数传入方法。
- 如果子组件
v-model
双向绑定
我们在表单中使用了v-model来绑定属性,同样父子组件也可以通过v-model进行双向数据绑定,公式如下:
<custom-input v-model="searchText"/>
等于
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
/>
<slot>
元素
<slot>
是Web Components技术标准的一部分,翻译为插槽。
在Vue中,<slot>
元素是用来在组件标签下插入元素,完成渲染,当然用prop也可以做到。
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
`
})
<alert-box>
Something bad happened.
</alert-box>
<component>
元素
<component>
元素是Vue内建的组件,用途是一个组件占位符,定义其is
属性,可以替换成别的元素
<component v-bind:is="currentComponent"></component>
通过绑定Vue实例中的currentComponent
数据,可以动态切换组件。currentComponent
可以是任何已注册的组件名称。
在特殊标签内正确渲染
有些 HTML 元素,诸如 <ul>
、<ol>
、<table>
和 <select>
,对于哪些元素可以出现在其内部是有严格限制的。可以使用is
属性来正确渲染。
<table>
<tr is="blog-post-row"></tr>
</table>
深入组件
命名风格
Vue推荐两种风格:
Vue.component('my-component-name', { /* ... */ })
Vue.component('MyComponentName', { /* ... */ })
//使用下面这种方式时,在html内引用时两种方式都可以用
组件注册
-
全局注册,这种方法注册的组件可以在任何地方使用。
Vue.component('my-component-name', {/*组件选项对象*/})
-
局部注册,在一个Vue实例中注册对象,仅在这个实例中使用,其子组件和父组件都不能用
new Vue({ el: '#app', components:{ my-component-name:{/*组件选项对象*/} } })
甚至可以简写为:
var myComponentA={/*组件选项对象*/} new Vue({ el: '#app', components:{ myComponentA } })
相当于一个名称为
'myComponentA'
的组件,其选项对象为myComponentA
如果要在一个组件内局部注册另外一个组件,可以用以下类似的形式
var ComponentA = { /*组件选项对象*/ } var ComponentB = { components: { 'component-a': ComponentA }, // 组件其他选项属性 }
组件与模块
使用Babel(ES2015)的模块系统来管理组件,Vue推荐创建一个components
目录,将组件放置在各自的文件中,通过import
和export
来导入/导出局部的组件。
import ComponentA from './ComponentA'
import ComponentC from './ComponentC'
export default {
components: {
ComponentA,
ComponentC
},
// ...
}
prop
-
命名风格
按照标准,HTML中的特性名是大小写不敏感的,所以推荐使用短横线命名法。如果用了驼峰命名法,会自动解释成短横线命名法。
Vue.component('blog-post', { props: ['postTitle'], template: '<h3></h3>' })
<blog-post post-title="hello!"></blog-post>
-
动态prop
我们知道
v-bind
标签是用来动态绑定DOM结点的属性,当然也可以绑定自定义的属性,实现动态prop。并且,
v-bind
标签也可以用来传入JS表达式,而直接写的自定义属性是字符串。 -
传入对象的所有属性
post: { id: 1, title: 'My Journey with Vue' }
<blog-post v-bind="post"></blog-post>
等价于:
<blog-post v-bind:id="post.id" v-bind:title="post.title" ></blog-post>
-
单向数据流
父子组件间通过prop形成单向数据流,prop的值应该完全由父组件决定,而子组件不应该去修改prop。而是通过事件去通知父组件从而改变父组件的状态。
-
prop验证
Vue提供了多种prop验证来在HTML自定义属性的基础上加强prop的鲁棒性。
Vue.component('my-component', { props: { // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证) 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 ['success', 'warning', 'danger'].indexOf(value) !== -1 } } } })
然而,Vue并不会强制执行验证,只是在开发版本的Vue会产生警告。
类型验证还可以是任意的构造函数,并不是只有原生对象。
-
非prop的特性
没有在prop中定义的属性,会直接作用于组件的根元素。如果组件已经有该名称的属性,将会覆盖。特别的,对于
class
和style
属性,不会覆盖而是合并。可以在组件选项中设置inheritAttrs: false
来禁用掉根元素继承特性。
自定义事件
我们知道组件自定义事件的触发是使用组件的$emit
方法
this.$emit('myEvent');
绑定原生事件:通过.native
修饰符来监听组件根元素的原生事件
插槽
在组件基础中我们知道了<slot>
可以作为承载inner元素内容的出口,在组件指定位置进行渲染,使用时需要注意如下特点:
-
编译作用域
插槽中内容编译的时候,仍然是在当前作用于中编译的,能够访问当前
vm
中的数据属性。<my-component> My name is ; </my-component>
-
插槽默认值
可以在
<slot>
标签内部写默认值,会在父组件不提供插槽内容时进行渲染。 -
具名插槽
在别的框架组件和HTML一些标签中,我们经常见到一些具有多插槽的组件,类似
<my-article> <header> 标题 </header> <main> 内容 </main> <footer> 脚注 </footer> </my-article>
对于vue,拥有具名插槽的支持,如下使用
<div> <header> <slot name="header"></slot> </header> <main> <slot></slot> </main> <footer> <slot name="footer"></slot> </footer> </div>
未命名的标签会有隐含的名称
default
,在Vue组件使用时,我们可以用<template>
元素配合v-slot
标签来插入到指定命名插槽。<base-layout> <template v-slot:header> <h1>Here might be a page title</h1> </template> <template v-slot:default> <p>A paragraph for the main content.</p> <p>And another one.</p> </template> <template v-slot:footer> <p>Here's some contact info</p> </template> </base-layout>
异步组件
Vue支持以类似Promise的语法来定义异步组件
Vue.component('async-example', function (resolve, reject) {
setTimeout(function () {
// 向 `resolve` 回调传递组件定义
resolve({
template: '<div>I am async!</div>'
})
}, 1000)
})
当resolve调用时,组件才会被注册
访问元素和组件
-
在Vue实例中通过
$root
属性访问组件根元素 -
在Vue实例中通过
$parent
属性访问组件的父元素 -
通过
ref
来访问任意元素和组件<base-input ref="usernameInput"></base-input>
this.$refs.usernameInput
依赖注入
Vue也提供了父子组件间进行依赖注入的方法
父组件定义provide
选项,它需要定义一个getter函数
provide: function () {
return {
getMap: this.getMap
}
}
子组件声明自己需要什么依赖
inject: ['getMap']
那么每个子组件就都拥有了getMap
这个方法