Vue.js 组件
- 组件用于封装页面的部分功能,将功能的结 构、样式、逻辑代码封装为整体。
- 提高功能的复用性与可维护性,更好的专注 于业务逻辑。
- 组件使用时为自定义 HTML 标签形式,通过 组件名作为自定义标签名。
组件注册
全局注册
- 全局注册的组件在注册后可以用于任意实例或组件中。
- 注意:全局注册必须设置在根 Vue 实例创建之前。
Vue.component('my-component',{ template:'<div>这是我们全局注册的组件</div>' })
组件基础
- 本质上,组件是可复用的 Vue 实例,所以它们可与 new Vue 接 收相同的选项,例如 data、methods 以及生命周期钩子等。
- 仅有的例外是像 el 这样根实例特有的选项。
组件命名规则
- 组件具有两种命名规则:
• kebab-case:'my-component’
• PascalCase:'MyComponent - 注意:无论采用哪种命名方式,在 DOM 中都只有 kebab-case 可以使用。
// kaba-case进行注册
Vue.component('my-com-a',{
template:'<div>这是a组件的内容</div>'
})
// Pascalcase进行注册
Vue.component('MyComB',{
template:'<div>这是b组件的内容</div>'
})
template 选项
- template 选项用于设置组件的结构,最终被引入根实例或其他组 件中。
- 注意:组件必须只有一个根元素。
Vue.component('MyComA', {
template: `
<div>
这是组件 A 的内容: {{ 1 + 2 * 3 }}
</div>
`
});
data 选项
- data 选项用于存储组件的数据,与根实例不同,组件的 data选项必须为函数,数据设置在返回值对象中。
- 这种实现方式是为了确保每个组件实例可以维护一份被返回对象的独立的拷贝,不会相互影响。
Vue.component('MyComA',{
template: `
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data:function(){
return{
title:'这是组件标题',
content:'这是组件内容'
}
}
})
局部注册
- 局部注册的组件只能用在当前实例或组件中。
<div id="app">
<my-com-a></my-com-a>
<my-com-b></my-com-b>
</div>
<div id="app2">
<my-com-a></my-com-a>
<my-com-b></my-com-b>
</div>
new Vue({
el: '#app',
data: {
},
components:{
// 必须要加引号不然会出现语法错误
'my-com-a':{
template:`
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data(){
return{
title:'组件A标题',
content:'组件A内容'
}
}
},
MyComB:{
template:`
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`,
data(){
return{
title:'组件B',
content:'组件B内容'
}
}
}
}
});
// 不能被当前组件其他的实例所使用
// new Vue({
// el:'#app2'
// })
- 单独配置组件的选项对象
<div id="app">
<my-component-a></my-component-a>
<my-component-b></my-component-b>
</div>
// 组件 A 的选项对象
var MyComponentA = {
template: `
<div>
<h3>{{ title }}</h3>
<p>{{ content }}</p>
</div>
`,
data () {
return {
title: '组件 A 标题',
content: '组件 A 内容'
}
}
};
// 组件 B 的选项对象
var MyComponentB = {
template: `
<div>
<h3>{{ title }}</h3>
<p>{{ content }}</p>
</div>
`,
data () {
return {
title: '组件 B',
content: '组件 B 内容'
}
}
}
// ES6的对象属性简写
new Vue({
el: '#app',
data: {
},
components: {
// 兼容性比简写好
'my-component-a': MyComponentA,
// 简写
MyComponentB
}
});
组件通信
在组件间传递数据的操作,称为组件通信。
父组件向子组件传值
- 通过子组件的 props 选项接收父组件的传值。
- 注意:props 不要与 data 存在同名属性。
- props内部默认为数组结构
<div id="app">
<!-- 静态属性设置 -->
<my-component-a
title="这是静态的标题"
content="这是静态的内容"
></my-component-a>
<!-- 动态属性绑定 -->
<my-component-a
v-bind:title="'这是静态的标题,这是演示,一般不会用动态绑定这是静态属性'"
content="'这是静态内容'"
></my-component-a>
<!-- 动态属性绑定常用操作 -->
<my-component-a
:title="item.title"
:content="item.content"
></my-component-a>
</div>
// 创建子组件
Vue.component('my-component-a',{
// props内部默认为数组结构
props:['title','content'],
template:`
<div>
<h3>{{title}}</h3>
<p>{{content}}</p>
</div>
`
})
new Vue({
el: '#app',
data: {
item:{
title:'这是示例标题',
content:'这是示例内容'
}
}
});
Props 命名规则
- 建议 prop 命名使用 camelCase,父组件绑定时使用 kebab-case。`
- 练习常见操作:
• 通过 v-for 创建组件
<div id="app">
<!-- 通过v-for遍历数据item。创建组件并生成内容 -->
<!-- 数据要绑定才能传入 -->
<!-- key绑定是给v-for看的,不是为了传到组件内部 -->
<demo-item v-for="item in items"
:item-title="item.title"
:item-content="item.content"
:key="item.title"
:item="item"
></demo-item>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('demoItem',{
props:['itemTitle','itemContent','item'],
template:`
<div>
<h3>{{ itemTitle }}</h3>
<p>{{ itemContent }}</p>
</div>
`
})
new Vue({
el: '#app',
data: {
// 准备给子组件使用的数据
items:[
{
title:'示例标题1',
content:'示例内容1'
},
{
title:'示例标题2',
content:'示例内容2'
},
{
title:'示例标题3',
content:'示例内容3'
},
]
}
})
单向数据流
- 父子组件间的所有 prop 都是单向下行绑定 的。
- 如果子组件要处理 prop 数据,应当存储在 data 中后操作。
- 注意,如果 prop 为数组或对象时,子组件操作将会影响到父组 件的状态。
<div id="app">
<my-component
:initial-title="title"
:obj="obj"
></my-component>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('my-component',{
props:['initialTitle','obj'],
template:`
<div>
{{ title }}
<button @click="fn">按钮</button>
</div>
`,
data(){
return{
title:this.initialTitle
}
},
methods:{
fn(){
// console.log(this);
// this.title='这是新的标题'
// this.initialTitle='这是新的标题'//不会影响父组件
this.obj.name='jack';
}
}
});
new Vue({
el: '#app',
data: {
title:'这是示例标题',
obj:{
name:'rae',
age:22,
}
}
});
</script>
Props 类型
- Prop 可以设置类型检查,这时需要将 props 更改为一个带有验 证需求的对象,并指定对应类型。
- prop 还可以同时指定多个类型,通过数组方式保存即可。
<div id="app">
<my-component
:par-str="str"
:par-num="num"
:par-arr="arr"
:par-obj="obj"
:par-any="any"
:par-data="false"
></my-component>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('my-component',{
// 如果要设置props的具体规则需要更改为对象写法
props:{
parStr: String,
parNum: Number,
parArr: Array,
parObj: Object,
parAny: null ,//undefined,
parData:[String, Boolean]
},
template:`
<div>
{{ parStr }}
{{ parNum }}
{{ parArr }}
{{ parObj }}
{{ parAny }}
{{ parData }}
</div>
`
})
new Vue({
el: '#app',
data: {
num:100,
str: 'abc',
arr: [1,2,3],
obj:{
content1: '示例内容1',
content2: '示例内容2'
},
any:false
},
});
Props 验证
- 当 prop 需要设置多种规则时,可以将 prop 的值设置为选项对象。
- 之前的类型检测功能通过 type 选项设置。
props:{
parStr:{
type:String
},
parData:{
type:[Array,Object]
},
}
- required 用于设置数据为必填项。
parNum:{
type:Number,
required:true,
},
- default 用于给可选项指定默认值,当父组件未传递数据时生效。
parNum2:{
type:Number,
default:100
},
- 注意:当默认值为数组或对象时,必须为工厂函数返回的形式。
parArr:{
type:Array,
default:function(){
return [1,2,3];
}
},
- validator 用于给传入的 prop 设置校验函数,return 值为 false 时 Vue.js 会发出警告。
parContent:{
type:String,
validator(value){
return value.startsWith('lagou')//以拉勾开头的字符串
}
}
- 注意:验证函数中无法使用实例的 data、methods 等功能。
非 Props 属性
- 当父组件给子组件设置了属性,但此属性在 props 中不存在,这 时会自动绑定到子组件的根元素上。
- 如果组件根元素已经存在了对应属性,则会替换组件内部的值。
- class 与 style 是例外,当内外都设置时,属性会自动合并。
- 如果不希望继承父组件设置的属性,可以设置 inheritAttrs: false,但只适用于普通属性,class 与 style 不受影响。
<div id="app">
<my-component
:title="'示例标题内容'"
style="height:200px;"
class="colorRed"
data-index="3"
></my-component>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('MyComponent', {
inheritAttrs:false,
template:`
<div data-index="6"
title="旧的title"
class="abc"
style="width:200px;">
<p>这是组件的内容</p>
</div>
`
});
new Vue({
el: '#app',
data: {
}
});
</script>
子组件向父组件传值
子向父传值需要通过自定义事件实现。
- 商品为子组件,购物车为父组件,父组件需要统计商品个数,就 需要在子组件个数变化时传值给父组件。
- 子组件数据变化时,通过 $emit() 触发自定义事件。
- 自定义事件名称建议使用 kebab-case。
- 父组件监听子组件的自定义事件,并设置处理程序。
<div id="app">
<h3>购物车</h3>
<product-item
v-for="product in products"
:key="product.id"
:title="product.title"
@count-change="totalCount++"
>
</product-item>
<p>商品总个数为:{{ totalCount }}</p>
</div>
<script src="lib/vue.js"></script>
<script>
Vue.component('ProductItem',{
props:['title'],
template:`
<div>
<span>商品名称:{{ title }},商品个数: {{ count }}</span>
<button @click="countIns">+1</button>
</div>
`,
data(){
return{
count:0
}
},
methods:{
countIns(){
// emit是触发的意思
this.$emit('count-change');
this.count++;
}
}
})
new Vue({
el: '#app',
data: {
products:[
{
id:1,
title:'苹果一斤'
},
{
id:2,
title:'香蕉一根'
},
{
id:3,
title:'橙子一个'
}
],
totalCount:0
}
});
</script>
自定义事件传值
- 子组件触发事件时可以向父组件传值。
<div id="app">
<h3>购物车</h3>
<product-item
v-for="product in products"
:key="product.id"
:title="product.title"
@count-change="onCountChange"
></product-item>
<p>商品总个数为:{{ totalCount }}</p>
</div>
<script src="lib/vue.js"></script>
<script>
// 子组件
Vue.component('ProductItem', {
props: ['title'],
template: `
<div>
<span>商品名称: {{ title }}, 商品个数: {{ count }}</span>
<button @click="countIns1">+1</button>
<button @click="countIns5">+5</button>
</div>
`,
data () {
return {
count: 0
}
},
methods: {
countIns1 () {
this.$emit('count-change', 1);
this.count++;
},
countIns5 () {
this.$emit('count-change', 5);
this.count += 5;
}
}
});
// 父组件
new Vue({
el: '#app',
data: {
products: [
{
id: 1,
title: '苹果一斤'
},
{
id: 2,
title: '香蕉一根'
},
{
id: 3,
title: '橙子一个'
}
],
totalCount: 0
},
methods: {
onCountChange (productCount) {
this.totalCount += productCount;
}
}
});
</script>
组件与 v-model
- v-model 用于组件时,需要通过 props 与自定义事件实现。
<div id="app">
<p>输入框内容为:{{ iptValue }}</p>
<com-input v-model="iptValue"></com-input>
</div>
<script src="lib/vue.js"></script>
<script>
var ComInput ={
props:['value'],
template:`
<input
type="text"
:value="value"
@input="onInput"
>
`,
methods:{
onInput(){
this.$emit('input',event.target.value)
}
}
}
// 根实例
new Vue({
el: '#app',
data: {
iptValue:''
},
components:{
ComInput
}
});
</script>
非父子组件传值
- 非父子组件指的是兄弟组件或完全无关的两 个组件。
兄弟组件传值
- 兄弟组件可以通过父组件进行数据中转
EventBus
当组件嵌套关系复杂时,根据组件关系传值 会较为繁琐。
组件为了数据中转,data 中会存在许多与当 前组件功能无关的数据。
- EventBus (事件总线)是一个独立的事件中心,用于管理不同组 件间的传值操作。
- EventBus 通过一个新的 Vue 实例来管理组件传值操作,组件通 过给实例注册事件、调用事件来实现数据传递。
- 发送数据的组件触发 bus 事件,接收的组件给 bus 注册对应事 件。
- 给 bus 注册对应事件通过 $on() 操作。
- 最后创建根实例执行代码即可。
<div id="app">
<h3>购物车</h3>
<product-item></product-item>
<product-total></product-total>
</div>
<script src="lib/vue.js"></script>
<script src="EventBus.js"></script>
<script>
// 商品组件
Vue.component('ProductItem',{
template:`
<div>
<span>商品名称:苹果,商品个数:{{ count }}</span>
<button
@click="countIns"
>+1</button>
</div>
`,
data(){
return{
count:0
}
},
methods:{
countIns(){
// 给bus触发自定义事件,传递数据
bus.$emit('countChange',1)
this.count++
}
}
});
// 计数组件
Vue.component('ProductTotal',{
template:`
<p>总个数为: {{ totalCount }} </p>
`,
data(){
return{
totalCount:0
}
},
created(){
// 给bus注册时间,并接收数据
bus.$on('countChange',(productCount)=>{
// 实例创建完毕,可以使用data等功能
this.totalCount += productCount;
});
}
})
// 根实例
new Vue({
el: '#app',
data: {
}
});
</script>
其他通信方式.
$root
- $root 用于访问当前组件树根实例,设置简单的 Vue 应用时可以 通过此方式进行组件传值。
- $root 用于访问当前组件树根实例,设置简单的 Vue 应用时可以 通过此方式进行组件传值。
// 跟实例的子组件A的子组件B
var ComB={
template:`
<div>
组件B:{{ $root.count }}
<button @click="clickFn">按钮</button>
</div>
`,
methods:{
clickFn(){
this.$root.count = 200;
}
},
}
// 子组件A
var ComA ={
template:`
<div>
组件A:{{ $root.count }}
<button @click="clickFn">按钮</button>
<com-b></com-b>
</div>
`,
methods:{
clickFn(){
this.$root.count = 100;
}
},
components:{
ComB
}
};
// 根实例
new Vue({
el: '#app',
data: {
count:0
},
components:{
ComA
}
});
</script>
- 除了 $root , Vue.js 中还提供了 $parent 与 $children 用于 便捷访问父子组件(vue.js官网有学习方式)
$refs
- $refs 用于获取设置了 ref 属性的 HTML 标签或子组件。
- 给普通 HTML 标签设置 ref 属性,$refs 可以获取 DOM 对象。
- 给子组件设置 ref 属性,渲染后可通过 $refs 获取子组件实例。
<div id="app">
<!-- 给html标签设置ref属性 -->
<input type="text" ref="inp">
<!-- 给子组件设置ref属性 -->
<com-a ref="comA"></com-a>
<button @click="fn">按钮</button>
</div>
<script src="lib/vue.js"></script>
<script>
var ComA = {
template:`
<div>
组件A的内容:{{ value }}
</div>
`,
data(){
return{
value:'示例内容'
}
}
}
var vm = new Vue({
el: '#app',
methods:{
fn(){
// 按钮点击后修改html标签焦点的状态
this.$refs.inp.focus();
this.$refs.comA.value='新的内容'
}
},
// 对象
components:{
ComA
},
// 函数
mounted(){
console.log(this.$refs);
this.$refs.comA.value='修改后的内容'
}
});
</script>
组件插槽
组件插槽可以便捷的设置组件内容。
单个插槽
- 如果我们希望组件标签可以像 HTML 标签一样设置内容,那么组 件的使用灵活度会很高。
- 但平常我们书写的组件,组件首尾标签中书写的内容会被抛弃。
- 我们需要通过 进行插槽设置。
<com-a>
<span>这是组件的内容</span>
</com-a>
<com-a>
这是第二个组件的内容:
<span>这是span的内容</span>
</com-a>
Vue.component('ComA', {
template: `
<div>
<h3>组件标题</h3>
<slot></slot>
</div>
`,
data () {
return {
value:'子组件的数据'
}
}
});
- 需要注意模板内容的渲染位置
<com-a>这里是父组件的视图模板,只能使用父组件的数据:{{parValue}}</com-a>
new Vue({
el: '#app',
data: {
parValue:'父组件数据'
}
})
- 我们可以在 中为插槽设置默认值,也称为后备内容。
<com-a></com-a>
Vue.component('ComA', {
template: `
<div>
<h3>组件标题</h3>
<slot>这是默认版本</slot>
</div>
`,
data () {
return {
value:'子组件的数据'
}
}
});
具名插槽
- 如果组件中有多个位置需要设置插槽,据需要给 设置 name,称为具名插槽。
作用域插槽
用于让插槽可以使用子组件的数据。
- 组件将需要被插槽使用的数据通过 v-bind 绑定给 ,这种 用于给插槽传递数据的属性称为插槽 prop。
- 组件绑定数据后,插槽中需要通过 v-slot 接收数据。
- 如果只存在默认插槽,同时又需要接收数据,可以进行简写
- 还可以通过 ES6 的解构操作进行数据接收。
内置组件
动态组件
动态组件适用于多个组件频繁切换的处理。
- 用于将一个‘元组件’渲染为动态组件,以 is 属 性值决定渲染哪个组件。
<!-- component设置动态组件 -->
<component :is="currentCom"></component>
- 用于实现多个组件的快速切换,例如选项卡效果。
<!-- 按钮代表选项卡的标题功能 -->
<button
v-for="title in titles"
:key="title"
@click="currentCom = title"
>{{ title }}</button>
- is 属性会在每次切换组件时,Vue 都会创建一个新的组件实例。
// 设置要切换的子组件选项对象
var ComA = {
template:`<p>这是组件A的内容:<input type="text"></p>`
};
var ComB = {
template:`<p>这是组件B的内容:<input type="text"></p>`
};
var ComC = {
template:`<p>这是组件C的内容:<input type="text"></p>`
};
new Vue({
el: '#app',
data: {
// 所有组件名称
titles:['ComA','ComB','ComC'],
// 当前组件名称
currentCom:'ComA'
},
components: {
ComA,ComB,ComC
}
});
keep-alive 组件
- 主要用于保留组件状态或避免组件重新渲染。
<keep-alive>
<!-- 动态组件 -->
<component :is="currentCom"></component>
</keep-alive>
- include 属性用于指定哪些组件会被缓存,具有多种设置方式。
有三种设置方式
<keep-alive include="ComA,ComB,ComC">
<!-- 动态组件 -->
<component :is="currentCom"></component>
</keep-alive>
<keep-alive :include="['ComA','ComB','ComC']">
<!-- 动态组件 -->
<component :is="currentCom"></component>
</keep-alive>
<keep-alive :include="/Com[ABC]/">
<!-- 动态组件 -->
<component :is="currentCom"></component>
</keep-alive>
- exclude 属性用于指定哪些组件不会被缓存。
<keep-alive exclude="ComD">
- max 属性用于设置最大缓存个数。
<keep-alive max="2">
过渡组件
用于在 Vue 插入、更新或者移除 DOM 时, 提供多种不同方式的应用过渡、动画效果。
transition 组件
- 用于给元素和组件添加进入/离开过渡:
• 条件渲染 (使用 v-if )
• 条件展示 (使用 v-show )
• 动态组件
• 组件根节点 - 组件提供了 6个 class,用于设置过渡的具体效果。
- 进入的类名:
• v-enter 设置入场前的样式
• v-enter-to 入场完毕后显示完全的状态
• v-enter-active 设置入场过渡的设置 - 离开的类名:
• v-leave 准备离场前的状态
• v-leave-to 离场以后的状态
• v-leave-active 离场过渡的设置
通常设置四个即可
<style>
/* 用于设置过渡的执行过程 */
.v-enter-active,.v-leave-active{
transition: opacity 1s;
}
/* 用于设置出场的最终状态 */
.v-leave-to{
/* 透明度 */
opacity: 0;
}
/* 用于设置入场的初始状态 */
.v-enter{
opacity: 0;
}
/* 入场过程 */
.v-enter-active{
transition: all .5s;
}
</style>
相关属性
- 给组件设置 name 属性,可用于给多个元素、组件设置不同的过 渡效果,这时需要将 v- 更改为对应 name- 的形式。
- 通过 appear 属性,可以让组件在初始渲染时实现过渡。
<style>
/* 第一组过渡效果设置 */
.v-enter,.v-leave-to{
opacity: 0;
}
.v-enter-active,.v-leave-active{
transition: opacity .5s;
}
/* 第二组过渡效果设置 */
.demo-enter,.demo-leave-to{
opacity: 0;
transform: translateX(20px)
}
.demo-enter-active,.demo-leave-active{
transition: all .5s;
}
</style>
<div id="app">
<button @click="show = !show">切换1</button>
<!-- 没有设置name命名的transition组件,类名采用v-前缀 -->
<transition appear><p v-if="show">这是要切换的元素1</p></transition>
<br>
<button @click="showDemo = !showDemo">切换2</button>
<!-- 设置了name的transition组件,类名需要将v-修改为demo- -->
<transition name="demo" appear>
<p v-if="showDemo">这是要切换的元素2</p></transition>
</div>
<script src="./lib/vue.js"></script>
<script>
new Vue({
el: '#app',
data: {
show: true,
showDemo: true
}
});
</script>
自定义过渡类名
自定义类名比普通类名优先级更高,在使用 第三方 CSS 动画库时非常有用。
- 用于设置自定义过渡类名的属性如下:
• enter-class
• enter-active-class
• enter-to-class
• leave-class
• leave-active-class
• leave-to-class - 用于设置初始过渡类名的属性如下:
• appear-class
• appear-to-class
• appear-active-class
.test{
transition: all 3s;
}
<transition
enter-active-class="test"
leave-active-class="test"
>
<p v-if="show">p标签</p>
</transition>
- Animate.css 是一个第三方 CSS 动画库,通过设置类名来给元素 添加各种动画效果。
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<!-- 不需要添加 animate__ 的兼容版本,但是官方建议使用完整版本 -->
<!-- "https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.compat.css" -->
<transition
enter-active-class="animate__bounceInDown"
leave-active-class="animate__bounceOutDown"
>
<!-- 必须给要使用动画的元素设置基础类名 animate__animated -->
<p
v-if="show"
class="animate__animated"
>hello world</p>
</transition>
- 使用注意:
• animate__ 前缀与 compat 版本
• 基础类名 animated
transition-group 组件
- 用于给列表统一设置过渡动画。
• tag 属性用于设置容器元素,默认为 。
• 过渡会应用于内部元素,而不是容器。
• 子节点必须有独立的 key,动画才能正常工作。
<style>
ul {
position: relative;
}
.v-enter, .v-leave-to {
opacity: 0;
transform: translateX(100px);
}
.v-enter-active, .v-leave-active {
transition: all .5s;
}
/* 让元素在离场的过程中脱离标准流 */
.v-leave-active {
position: absolute;
}
.v-move {
transition: all .5s;
}
</style>
<div id="app">
<input type="text"
v-model="newTitle"
@keyup.enter="addItem"
>
<transition-group
tag="ul"
>
<li
v-for="item in items"
:key="item.id"
@click="removeItem(item)"
>
{{ item.title }}
</li>
</transition-group>
new Vue({
el: '#app',
data: {
items: [
{ id: 1, title: '示例内容1'},
{ id: 2, title: '示例内容2'},
{ id: 3, title: '示例内容3'},
{ id: 4, title: '示例内容4'},
{ id: 5, title: '示例内容5'},
],
newTitle: '',
latestId: 5
},
methods: {
addItem () {
this.items.push({
id: this.latestId + 1,
title: this.newTitle
});
this.latestId++;
this.newTitle = '';
},
removeItem (item) {
var i = this.items.indexOf(item);
this.items.splice(i, 1);
}
}
});
- 当列表元素变更导致元素位移,可以通过 .v-move 类名设置移动 时的效果。
文章内容输出来源:拉勾教育大前端就业急训营