vue入门(四)组件components
一、基本介绍
组件是可复用的Vue实例,可以在一个通过new Vue创建的Vue根实例中,把这个组件作为自定义元素来使用。
组件也可以包含 data
、computed
、watch
、methods
以及生命周期钩子等,除了el。
1、一个组件的data必须是一个函数
这样,每个实例可以维护自己独立的数据,而不是每个组件都使用同一个数据:
data:function(){
return{
count:0
}
}
2、通过Prop向子组件传递数据
Prop 是你可以在组件上注册的一些自定义特性。一个Prop被注册后,可以把数据作为一个自定义特性传递到子组件中。
Vue.component('blog-post', {
props: ['title'], //prop注册
template: '<h3>{{ title }}</h3>'
})
<blog-post title="My journey with Vue"></blog-post> //通过title特性传递数据
若在data中有一组数据posts,并为每一个数据渲染一个组件,可以用v-bind来动态传递prop
当需要多个特性时,可以通过以下,此时不论何时为post对象添加一个新的属性,都会自动的在<blog-post>内可用
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:post="post" //绑定post
></blog-post>
Vue.component('blog-post', {
props: ['post'],
template: `
<div class="blog-post">
<h3>{{ post.title }}</h3>
<div v-html="post.content"></div>
</div>
`
})
3、单个根元素
每个组件必须只有一个根元素。
4、监听子组件事件
在子组件中vm.$emit( eventName, data ) 事件,第二参数为子组件向父组件抛出的数据,当子组件中有事件触发时,会自动触发eventName。
<button v-on:click="$emit('enlarge-text',0.1)"> Enlarge text </button>
父组件可以像处理native DOM事件一样,实现eventName,可以通过$event访问被抛出的值,通过v-on监听子组件实例的任何事件。若事件处理函数是一个方法,v-on:enlarge-text="onEnlargeText" ,那么这个值会作为第一个参数传入此方法。
<blog-post v-on:enlarge-text="postFontSize += $event"> </blog-post>
5、在组件上使用v-model
使用v-model可以使输入的数据实现双向绑定。在组件上用v-model时,组件内的input必须:
- 将其
value
特性绑定到一个名叫value
的 prop 上 - 在其
input
事件被触发时,将新的值通过自定义的input
事件抛出
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
<custom-input v-model="searchText"></custom-input>
6、通过插槽分发内容
若要在组件内写内容,可能会渲染出错,此时通过加入slot元素,即写的内容会出现在相应slot位置上。
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot>
</div>
` })
二、注册组件
1、组件名
Vue.component('组件名',{ })
①全小写字母+至少一个连字符:引用时也必须使用此名
②大驼峰命名:引用时,可以是大驼峰,也可以是全小写+连字符
2、全局注册
全局注册:在注册之后可以用在任何新创建的Vue根实例中,在子组件各自内部也可以相互使用。
Vue.component('name-a',{ ... })
new Vue({el:'#app'}) //用在新创建的根实例中
3、局部注册
局部注册的组件在其子组件中不可用。通过普通的JS对象来定义组件,然后在components中定义想要使用的组件
var ComponentA ='{ }'
var ComponentB ='{ }'
var vm =new Vue({
el:'#app',
components:{
'component-a' : ComponentA
})
若要在ComponentB中使用ComponentA,则要在定义ComponentB时,将B加入components。
4、模块系统
创建一个components目录,并将每个组件放在各自的文件中。 在局部注册之前导入每一个想要使用的组件:
import ComponentA from './ComponentA'
export default {
components: {
ComponentA
},} //A可以在B中使用了
5、基础组件的自动化全局注册
基础组件:相对通用的组件,被频繁使用,如输入框或按钮。全局注册的行为必须在根 Vue 实例 (通过 new Vue
) 创建之前发生
代码详见:https://cn.vuejs.org/v2/guide/components-registration.html
三、Prop
1、Prop的大小写
在JS中,Props使用小驼峰命名,而在HTML中,需要使用kebab-case(短横线分隔)命名。
2、Prop类型
可以设置Prop各自的名称和类型
props:{
title:String,
lieks:Number}
任何类型的值都可以传递给prop,Number,String等数据类型(Boolean类没有值时默认true),对象、数组等引用类型都可以。
3、单向数据流
单向下行绑定:父级 prop 的更新会向下流动到子组件中,不能在一个子组件内部改变 prop,这样会在控制台发出警告。
4、Prop验证
可以为props中的值提供一个带有验证需求的对象。当验证失败时,会产生一个控制台的警告
Vue.component('my-component', {
props: {
propA: Number, // 多个可能的类型
propB: [String, Number], // 必填的字符串
}})
这里的type可以是原生的类型,还可以是一个自定义的构造函数,来验证prop值是通过构造函数创建的
5、禁用特性继承
如果你不希望组件的根元素继承特性,你可以在组件的选项中设置 inheritAttrs: false
四、自定义事件
1、事件名:
始终使用 kebab-case 的事件名,触发的事件名需要完全匹配监听这个事件所用的名称
2、v-model
一个组件上的 v-model
默认会利用名为 value
的 prop 和名为 input
的event,输入的值会传入prop中。
3、将原生事件绑定到组件
在一个组件的根元素上直接监听一个原生事件,使用.native修饰符
<base-input v-on:focus.native="onFocus"></base-input>
Vue 提供了一个 $listeners
属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。
4、子向父传值.sync
子组件更新数据通过事件触发,父组件可以监听子组件更新数据,通过.sync修饰符实现这种模式
<text-document v-bind:title.sync="doc.title"></text-document> //不能用表达式
五、插槽
在templ中写入<slot></slot>,可以插入任何模板代码,包括HTML、其他组件
1、编译作用域
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
2、后备内容:在没有提供内容的时候渲染,可以将默认内容放入<slot></slot>标签对内。
3、具名插槽
有时我们需要多个插槽,<slot>
元素的特性:name
可以用来定义额外的插槽:
<header> <slot name="header"></slot> </header>
<footer> <slot name="footer"></slot> </footer>
在向具名插槽写内容时,可以在一个 <template>
元素上使用 v-slot
指令,参数为slot的名称:
<template v-slot:header> a page title </template>
没有v-slot指令,则被视为默认插槽内容。v-slot
只能添加在一个 <template>
上
4、作用域插槽
让插槽内容能够访问子组件中才有的数据(假设user)可以将user作为<slot>元素的一个特性绑定上去。
<span>
<slot v-bind:user="user"> {{ user.lastName }} </slot>
</span>
此特性被称为插槽prop,在父级作用域中,给v-slot带一个值来定义插槽prop名字
<current-user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</current-user> //独占默认插槽的缩写语法,:default也可省略
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
只要出现多个插槽,就始终为所有的插槽使用完整的基于 <template>
的语法。
5、解构插槽prop
v-slot
的值可以是任何能够作为函数定义中的参数的 JavaScript 表达式。
<current-user v-slot="{ user }"> </current-user> //可以插入具体的插槽prop
<current-user v-slot="{ user: person }"> </current-user> //可以prop重命名
<current-user v-slot="{ user = { firstName: 'Guest' } }"> </current-user> //可以定义后备内容
6、动态指令参数也可用在v-slot上,来定义动态的插槽名。
v-slot:可以缩写成#,该缩写只在其有参数的时候才可用,即必须始终以明确插槽名取而代之。如v-slot:header→#header
六、动态组件
1、在动态组件上使用keep-alive
<component v-bind:is="currentTabComponent"></component>
在一个多标签的界面上使用is特性来切换不同的组件。当在这些组件之间切换的时候,会反复重新渲染。
当你想保持这些组件的状态时,即标签的组件实例能够被在它们第一次被创建的时候缓存下来,可以使用<keep-alive>元素将其动态组件包裹起来。
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
2、异步组件
以下工厂函数方式定义的组件,只有在这个组件需要被渲染的时候才会触发该工厂函数。
Vue.component('async-webpack-example', function (resolve) {
// 这个特殊的 `require` 语法将会告诉 webpack自动将你的构建代码切割成多个包,这些包会通过 Ajax 请求加载
require(['./my-async-component'], resolve)
})
resolve
回调函数会在你从服务器得到组件定义的时候被调用。
七、处理边界情况
1、访问元素、组件
①访问根实例:在每个 new Vue
实例的子组件中,其根实例可以通过 $root
属性进行访问。
this.$root.foo = 2 //写入根组件数据
②访问父级组件实例:$parent
属性可以用来从一个子组件访问父组件的实例。
③在JS里直接访问一个子组件:通过 ref
特性为这个子组件赋予一个 ID 引用,例如输入聚焦
<base-input ref="usernameInput"></base-input>
this.$refs.usernameInput //访问base-input实例
$refs
只会在组件渲染完成之后生效,并且它们不是响应式的,应避免在模板或计算属性中访问
2、依赖注入
依赖注入用到了两个新的实例选项:provide
和 inject
provide
选项允许我们指定我们想要提供给后代组件的数据/方法。
在任何后代组件里,都可以使用 inject
选项来接收想要添加在这个实例上的属性
3、事件侦听
- 通过
$on(eventName, eventHandler)
侦听一个事件 - 通过
$once(eventName, eventHandler)
一次性侦听一个事件 - 通过
$off(eventName, eventHandler)
停止侦听一个事件
4、循环引用
①递归组件:组件在自己的模板中调用自身,通过name:‘组件名’ 来实现,注意确保递归调用是条件性的
②循环引用:当父引用子,子引用父时,通过Vue.component全局注册组件是没问题,但是若通过webpack则会出错
此时可以通过以下两种方式解决
beforeCreate: function () {
this.$options.components.TreeFolderContents = require('./tree-folder-contents.vue').default
} //等到生命周期钩子beforeCreate时去注册它
components: {
TreeFolderContents: () => import('./tree-folder-contents.vue')
} //在本地注册组件时使用 webpack 的异步 import
5、内联模板
当 inline-template
特性出现在一个子组件上时,这个组件将会使用其里面的内容作为模板。
内联模板需要定义在 Vue 所属的 DOM 元素内。
<my-component inline-template> ... </my-component>
6、X-Template
X-Template需要定义在 Vue 所属的 DOM 元素外。
在一个 <script>
元素中,并为其带上 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'
})
7、通过v-once创建低开销的静态组件
组件包括了大量的静态内容,可以在根元素上添加v-once特性,确保这些内容只计算一次后缓存起来
更多推荐
所有评论(0)