1.自定义全局组件
(1)什么是组件
组件就是把一个很大的界面拆分为多个小的界面, 每一个小的界面就是一个组件,将大界面拆分成小界面就是组件化。
(2)创建全局组件方式一
① 创建组件构造器
let Profile = Vue.extend({
// 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
template: `
<div>
<img src="images/fm.jpg"/>
<p>我是描述信息</p>
</div>
`
});
② 注册组件
第一个参数: 指定注册的组件的名称
第二个参数: 传入已经创建好的组件构造器
Vue.component("abc", Profile );
let vue = new Vue({
el: '#app',
// 实例中的data是一个对象,可直接保存数据
data: {
name: "xhl"
},
// 专门用于存储监听事件回调函数
methods: {},
// 专门用于定义计算属性的
computed: {}
});
③ 使用组件
<div id="app">
<!--// 3.3使用注册好的组件-->
<abc></abc>
</div>
(3)创建全局组件方式二
① 编写组件模板
在编写组件模板的时候, 除了可以在script中编写以外, vue还专门提供了一个编写模板的标签template。
<template id="info">
<div>
<img src="images/fm.jpg"/>
<p>我是描述信息</p>
<button @click="abcFn">我是按钮</button>
<p>{{abcMsg}}</p>
</div>
</template>
② 注册组件
在注册组件的时候, 除了传入一个组件构造器以外, 还可以直接传入一个对象。
Vue.component("abc", {
// 注意点: 在创建组件指定组件的模板的时候, 模板只能有一个根元素
template: "#info",
data: function () {
return {
abcMsg: "学院"
}
},
methods: {
abcFn(){
alert("abcFn");
}
}
});
let vue = new Vue({
el: '#app',
// 实例中的data是一个对象,可直接保存数据
data: {
name: "xhl"
},
// 专门用于存储监听事件回调函数
methods: {},
// 专门用于定义计算属性的
computed: {}
});
③ 使用组件
<div id="app">
<!--// 3.3使用注册好的组件-->
<abc></abc>
</div>
2.自定义局部组件
(1)全局组件和局部组件的区别
全局组件:在任何一个Vue实例控制的区域中都可以使用
局部组件:只能在自定义的那个Vue实例控制的区域中可以使用
(2)创建局部组件
① 编写组件模板
<template id="info">
<div>
<img src="images/fm.jpg"/>
<p>我是描述信息</p>
</div>
</template>
② 在vue实例中创建组件
let vue2 = new Vue({
el: '#app2',
// 专门用于定义局部组件的
components: {
"abc": {
template: "#info"
}
}
});
③ 局部使用组件
<div id="app2">
<abc></abc>
</div>
3.切换组件(v-if)
<div id="app">
<button @click="toggle">切换</button>
<home v-if="isShow"></home>
<photo v-else></photo>
</div>
<template id="home">
<div>
<p>我是首页</p>
</div>
</template>
<template id="photo">
<div>
<img src="images/fm.jpg" alt="">
</div>
</template>
自定义全局组件
Vue.component("home", {
template: "#home",
});
Vue.component("photo", {
template: "#photo",
});
// 这里就是MVVM中的View Model
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
isShow: true
},
// 专门用于存储监听事件回调函数
methods: {
toggle() {
this.isShow = !this.isShow;
}
}
});
4.动态组件
(1)动态组件格式:
<component v-bind:is="需要显示组件名称"></component>
(2)为什么可以通过v-if切换还要有component
因为component可以配合keep-alive来保存被隐藏组件隐藏之前的状态。
示例:
<div id="app">
<button @click="toggle">切换</button>
<keep-alive>
<component v-bind:is="name"></component>
</keep-alive>
</div>
<template id="home">
<div>
<p>我是首页</p>
<input type="checkbox">
</div>
</template>
<template id="photo">
<div>
<img src="images/fm.jpg" alt="">
</div>
</template>
自定义全局组件
Vue.component("home", {
template: "#home",
});
Vue.component("photo", {
template: "#photo",
});
// 这里就是MVVM中的View Model
let vue = new Vue({
el: '#app',
// 这里就是MVVM中的Model
data: {
name: "home"
},
// 专门用于存储监听事件回调函数
methods: {
toggle() {
this.name = this.name === "home" ? "photo" : "home";
}
}
});
5.组件动画
(1)如何给组件添加动画?
给组件添加动画和过去给元素添加动画一样,
如果是单个组件就使用transition,
如果是多个组件就使用transition-group。
(2)注意点
默认情况下进入动画和离开动画是同时执行的,
如果想一个做完之后再做另一个, 需要指定动画模式。
(3)示例
<div id="app">
<button @click="toggle">切换</button>
<transition mode="out-in">
<component v-bind:is="name"></component>
</transition>
</div>
6.父子组件
(1)父子组件格式
<div id="app">
<father></father>
</div>
<template id="father">
<div>
<p>我是父组件</p>
<son></son>
</div>
</template>
<template id="son">
<div>
<p>我是子组件</p>
</div>
</template>
let vue = new Vue({
el: '#app',
data: {},
methods: {},
computed: {},
// 专门用于定义局部组件的
components: {
"father": {
template: "#father",
components: {
"son": {
template: "#son"
}
}
}
}
});
(2)父组件传递数据给子组件
① 在父组件中通过v-bind传递数据:
在用子组件的时候,v-bind:自定义接收名称 = “要传递数据”
② 在子组件中通过props接收数据:
在子组件中,props: [“自定义接收名称”]
<template id="father">
<div>
<!--注意点: 组件是可以使用自己的数据的-->
<p>{{name}}</p>
<p>{{age}}</p>
<!--这里将父组件的name通过parentname传递给了子组件-->
<son :parentname="name" :abc="age"></son>
</div>
</template>
<template id="son">
<div>
<!--这里通过parentname使用了父组件传递过来的数据-->
<p>{{parentname}}</p>
<p>{{abc}}</p>
</div>
</template>
Vue.component("father", { // 父组件
template: "#father",
data: function(){
return {
name: "lnj",
age: 33
}
},
components: { // 子组件
"son": {
template: "#son",
// 这里通过parentname接收了父组件传递过来的数据,然后可以直接使用
props: ["parentname", "abc"]
}
}
});
(3)父组件传递方法给子组件
① 在父组件中通过v-on传递方法
在用子组件的时候,@自定义接收名称 = “要传递方法”
② 在子组件中自定义一个方法,在自定义方法中通过 this.$emit(‘自定义接收名称’);
触发传递过来的方法。
<template id="father">
<div>
<button @click="say">我是按钮</button>
<!--这里通过parentsay将父组件的say方法传递给了子组件-->
<son @parentsay="say"></son>
</div>
</template>
<template id="son">
<div>
<button @click="sonFn">我是按钮</button>
</div>
</template>
Vue.component("father", { // 父组件
template: "#father",
methods: {
say(){
alert("www.it666.com");
}
},
components: { // 子组件
"son": {
template: "#son",
methods: {
//在子组件中自定义方法,调用该方法就触发父组件传过来的方法
sonFn(){
this.$emit("parentsay");//触发父组件传过来的方法
}
}
}
}
});
(4)子组件传递数据给父组件
① 父组件给子组件传递方法
② 子组件接收方法然后给父组件传递数据(return数据)
③ 父组件在传递的方法参数中接收到数据
<template id="father">
<div>
<button @click="say">我是按钮</button>
<son @parentsay="say"></son>
</div>
</template>
<template id="son">
<div>
<button @click="sonFn">我是按钮</button>
</div>
</template>
Vue.component("father", {
template: "#father",
methods: {
say(data) {
// alert("www.it666.com");
console.log(data); //子组件传递过来的数据
}
},
components: { // 子组件
"son": {
template: "#son",
methods: {
sonFn() {
//触发父组件方法的同时向父组件传递参数
// 第一个参数: 需要调用的函数名称
// 后续的参数: 给调用的函数传递的参数
this.$emit("parentsay", "知播渔");
}
}
}
}
});
7.组件命名
(1)注册组件的时候使用了"驼峰命名", 那么在使用时需要转换成"短横线分隔命名",例如: 注册时: myFather -> 使用时: my-father。
(2)在传递参数的时候如果想使用"驼峰名称", 那么就必须写"短横线分隔命名"
传递时: parent-name=“name” -> 接收时: props: [“parentName”]。
(3)在传递方法的时候不能使用"驼峰命名", 只能用"短横线分隔命名"
@parent-say=“say” -> this.$emit(“parent-say”);
<div id="app">
<my-father></my-father>
</div>
<template id="father">
<div>
<p>{{name}}</p>
<button @click="say">我是按钮</button>
<son :parent-name="name" @parent-say="say"></son>
</div>
</template>
<template id="son">
<div>
<p>{{parentName}}</p>
<button @click="sonFn">我是按钮</button>
</div>
</template>
Vue.component("myFather", { // 父组件
template: "#father",
data: function(){
return {
name: "lnj"
}
},
methods: {
say(){
console.log("www.it666.com");
}
},
components: { // 子组件
"son": {
template: "#son",
props: ["parentName"],
methods: {
sonFn(){
this.$emit("parent-say");
}
}
}
}
});
8.特定组件缓存(新添加)
<keep-alive include="Singer,Search">
<router-view></router-view>
</keep-alive>
9.数据和方法的多级传递
在Vue中如果儿子想使用爷爷的数据和方法, 必须一层一层往下传递。
<template id="grandfather">
<div>
<p>{{name}}</p>
<button @click="say">我是按钮</button>
<father :gfname="name" @gfsay="say"></father>
</div>
</template>
<template id="father">
<div>
<p>{{gfname}}</p>
<button @click="fatherFn">我是按钮</button>
<son :fname="gfname" @fsay="fatherFn"></son>
</div>
</template>
<template id="son">
<div>
<p>{{fname}}</p>
<button @click="sonFn">我是按钮</button>
</div>
</template>
Vue.component("grandfather", {
template: "#grandfather",
data: function(){
return {
name: "lnj"
}
},
methods: {
say(){
console.log("我是爷爷的方法");
}
},
components: {
"father": {
template: "#father",
props: ["gfname"],
methods:{
fatherFn(){
this.$emit("gfsay");
}
},
// 儿子组件
components: {
"son": {
template: "#son",
props: ["fname"],
methods: {
sonFn(){
this.$emit("fsay");
}
},
}
}
}
}
});
10.插槽
(1)插槽
默认情况下使用子组件时在子组件中编写的元素是不会被渲染的,如果我们想在使用子组件中在子组件中编写元素,那么我们就可以使用插槽,插槽就是在子组件中放一个"坑", 以后由父组件来"填"。
(2)匿名插槽
没有指定名称的插槽, 会利用使用时指定的内容替换整个插槽。
匿名插槽默认为default。
注意点: 插槽可以指定默认数据, 如果使用者没有填这个坑, 那么就会显示默认数据。
如果使用者填了这个坑, 那么就会利用使用者填坑的内容替换整个插槽。
如果有多个匿名插槽,有多少个匿名插槽, 填充的数据就会被拷贝几份,每一个匿名插槽都会被指定的内容替换。
虽然写多个匿名插槽不会报错, 但是在企业开发中不推荐使用多个匿名插槽。
<template id="father">
<div>
<!--需求: 在使用子组件的时候给子组件动态的添加一些内容-->
<son>
<div>我是追加的内容1</div>
<div>我是追加的内容2</div>
<div>我是追加的内容3</div>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部</div>
<!-- 有填坑显示填坑内容,没有填坑显示默认数据 -->
<slot>我是默认数据</slot>
<div>我是底部</div>
</div>
</template>
Vue.component("father", {
template: "#father",
// 子组件
components: {
"son": {
template: "#son",
}
}
});
(3)具名插槽(slot=“name”)
通过插槽的name属性给插槽指定名称,在使用时可以通过slot="name"方式, 指定当前内容用于替换哪一个插槽。如果没有指定要替换哪个插槽中的内容, 则不会被替换。
<template id="father">
<div>
<son>
<!--这里通过slot属性告诉Vue,
当前的内容是要填充到哪一个插槽中的-->
<div slot="one">我是追加的内容1</div>
<div slot="one">我是追加的内容11</div>
<div slot="two">我是追加的内容2</div>
<div slot="two">我是追加的内容22</div>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部</div>
<slot name="one">我是默认内容</slot>
<slot name="two">我是默认内容</slot>
<div>我是底部</div>
</div>
</template>
Vue.component("father", { // 父组件
template: "#father",
// 子组件
components: {
"son": {
template: "#son",
}
}
});
(4)v-slot(#)
v-slot指令是Vue2.6中用于替代slot属性的一个指令,
在Vue2.6之前, 我们通过slot属性告诉Vue当前内容填充到哪一个具名插槽,
从Vue2.6开始, 我们通过v-slot指令告诉Vue当前内容填充到哪一个具名插槽。
我们除了可以通过v-slot指令告诉Vue内容要填充到哪一个具名插槽中,
还可以通过v-slot指令告诉Vue如何接收作用域插槽暴露的数据。
注意点: v-slot指令只能用在template标签上
可以使用#号替代v-slot;
简写字符:
v-bind: :
v-on: @
v-slot:#
v-slot指令告诉Vue内容要填充到哪一个具名插槽中示例:
<template id="father">
<div>
<son>
<template v-slot:one>
<div>我是追加的内容1</div>
<div>我是追加的内容11</div>
</template>
<template v-slot:two>
<div>我是追加的内容2</div>
<div>我是追加的内容22</div>
</template>
<!-- 简写 -->
<template #one>
<div>我是追加的内容1</div>
<div>我是追加的内容11</div>
</template>
<template #two>
<div>我是追加的内容2</div>
<div>我是追加的内容22</div>
</template>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部</div>
<slot name="one">我是one默认内容</slot>
<slot name="two">我是two默认内容</slot>
<div>我是底部</div>
</div>
</template>
<script>
// 父组件
Vue.component("father", {
template: "#father",
// 子组件
components: {
"son": {
template: "#son",
}
}
});
v-slot指令告诉Vue如何接收作用域插槽暴露的数据:v-slot:插槽名称=“作用域名称”
<template id="father">
<div>
<son>
<template slot-scope="abc">
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
//匿名插槽默认为default.
<template v-slot:default="abc">
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
<template #default="abc">
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
<template #one="abc">
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部 {{names}}</div>
<slot name="one" v-bind:names="names">
我是默认内容 {{names}}</slot>
<div>我是底部</div>
</div>
</template>
// 父组件
Vue.component("father", {
template: "#father",
// 子组件
components: {
"son": {
template: "#son",
data: function () {
return {
names: ["zs", "ls", "ww", "zl"]
}
}
}
}
});
(5)作用域插槽(slot-scope)
作用域插槽就是带数据的插槽, 就是让父组件在填充子组件插槽内容时也能使用子组件的数据。
应用场景: 子组件提供数据, 父组件决定如何渲染。
如何使用作用域插槽:
在slot中通过 v-bind:数据名称=“数据名称” 方式暴露数据
在父组件中通过 接收数据
在父组件的中通过作用域名称.数据名称方式使用数据。
<template id="father">
<div>
<son>
<!-- 自定义作用域名称,接收子组件插槽暴露的数据 -->
<template slot-scope="abc">
<!-- 利用作用域名称.数据名称使用子组件传递过来的数据 -->
<li v-for="(name, index) in abc.names">{{name}}</li>
</template>
</son>
</div>
</template>
<template id="son">
<div>
<div>我是头部 {{names}}</div>
<!-- 将当前子组件的names数据暴露给父组件 -->
<slot v-bind:names="names">我是默认内容 {{names}}</slot>
<div>我是底部</div>
</div>
</template>
Vue.component("father", { // 父组件
template: "#father",
// 子组件
components: {
"son": {
template: "#son",
data: function () {
return {
names: ["zs", "ls", "ww", "zl"]
}
}
}
}
});