概述
文章目录
- 一、组件与复用
- 注意点
- 1.全局组件注册
- 2.局部组件注册
- (1)在Vue根实例内注册
- (2)在父组件内嵌套注册子组件
- 3.使用template模板标签
- 4.HTML语法导致组件使用受限
- 5.组件的属性
- 二、组件通讯
- 1.父向子传递数据props
- (1)props使用数组
- (2)props使用对象(数据验证)
- 2.子向父传递数据
- (1)自定义事件$emit
- 3.组件通讯中的v-model
- (1)使用v-model绑定父组件传递过来的数据
- (1)使用v-model改变子组件数据时也改变父组件
- 三、组件访问
- 1.中央事件总线
- 2.父链与子组件索引
- (1)父链介绍
- $parent
- (2)子组件索引
- $children
- $refs(重点)
- 四、solt插槽
- 1. 为什么使用插槽
- 2. 如何使用插槽封装组件
- 3. 如何使用插槽
- (1)基本使用
- (2)使用插槽默认值
- (3)注意
- 4. 具名插槽的使用
- (1)注意
- 5. 编译的作用域
- 6. 作用域插槽
一、组件与复用
注意点
- 代表组件的标签名不支持使用驼峰命名,建议使用
-
分隔符分离单词 - 子组件的
props
属性在HTML
中接收数据的时候不支持驼峰,建议使用-
分隔符分离单词 - 子向父传递传递数据自定义事件不支持驼峰,也不支持
-
,建议全部小写 - 组件的内容必须使用
div
包裹,不然无法渲染到页面 - 修改
props
的属性不影响父组件的数据,但是如果要修改props
最好是通过父组件修改,不然会报错 - 组件内部不能访问
Vue
实例的数据。
1.全局组件注册
//全局组件注册:全局注册的组件在任何Vue实例对应的HTML代码内都能使用
<div>
<my-component></my-component>
</div>
----------------------------------------
Vue.component("my-component",{
template:"<div>这里是组件的内容</div>"
})
const app = new Vue({
el:"#app"//使用全局组件首先需要挂载DOM,不然在HTML代码内无法使用组件
})
下面是效果图:
2.局部组件注册
(1)在Vue根实例内注册
(1)在Vue的实例中,可以使用components来注册组件内容,这样注册后的组件只能在当前Vue实例对应的HTML代码中使用。
--------------------------------------------------------------
<div id="app">
<my-child></my-child>
</div>
--------------------------------------------------------------
let child = {
template: "<div>这里是子组件</div>"
}
let app = new Vue({
el: '#app',
components: {
"my-child": child
//components选项内,key是使用组件时的名字,value则是组件
}
});
(2)在父组件内嵌套注册子组件
(1)组件中也可以使用components来嵌套注册组件.
<div id="app">
<my-father></my-father>
</div>
-------------------------------------
let child = {
template: "<div>这是子组件</div>"
}
let father = {
template: `
<div>
这是父组件
<my-child></my-child>//这里使用了注册的子组件
</div>
`,
components: {
'my-child': child
//在父组件中注册的子组件并不能在Vue实例对应的HTML中使用,只能在父组件中使用
}
}
let app = new Vue({
el: '#app',
components: {
'my-father': father
}
});
下面是渲染之后的结构图
3.使用template模板标签
- 使用
template
标签模板可以让代码不臃肿
el: '#app',
components: {
'my-component':{
template:"<div>当组件代码过多时就会显的臃肿,此时可以使用template标签</div>"
}
}
- 使用
template
<div id="app">
<mycpn></mycpn>
</div>
<template id="my-template">
<div>
这是使用了template标签的组件
</div>
</template>
-------------------------------------------
let myTemplate = { template: "#my-template" }
//注意:template属性对应的是template标签的id
let app = new Vue({
el: '#app',
components: {
mycpn: myTemplate
}
});
4.HTML语法导致组件使用受限
解释:在某些标签内只能使用特定的标签,比如table
只能使用tr
,th
,这种情况下能使用特殊的属性来挂载组件
1.首先看不使用特殊属性挂载
<div id="app">
<table>
<tbody>
<my-component></my-component>
</tbody>
</table>
</div>
-----------------------------------------
Vue.component("my-component", {
template: "<div>这里是组件的内容</div>"
})
DOM图
使用特殊属性挂载
<div id="app">
<table>
<tbody is="my-component"></tbody>
</table>
</div>
-------------------------------------
Vue.component("my-component", {
template: "<div></div>"
})
DOM图
5.组件的属性
- 组件就是一个封闭的空间,它有
template
属性,也可以和vue实例一样拥有其他属性,如methods
,data
,computed
等 - 组件内部是不能访问vue实例的数据的,所以组件需要有一个自己的
data
,但是和vue实例的data
不同。组件内部的data
必须是一个函数,并且应该返回对象,对象里保存数据
<div id="app">
<my-component></my-component>
</div>
--------------------------------
Vue.component("my-component", {
template: "<div>{{message}}</div>",
data: function () {
return { message: "组件内容" }
}
})
- 关于
data
为什么需要是一个函数
<div id="app">
<my-component></my-component>
<my-component></my-component>
</div>
<template id="cpn">
<div>
<p>计数器:{{count}}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
--------------------------------------------
Vue.component("my-component", {
template: "#cpn",
data() {
return { count: 0 }
},
methods: {
increment() {
this.count++;
},
decrement() {
this.count--;
}
}
})
/*
每当在HTML内使用一次组件时(本例子组件为my-component)就会调用一次data函数并返回一个对象,
当修改第一个计数器的数据修改时并不会该改变第二个计数器的数据,虽然它们返回的对象保存的数据都一样,但是对象的内存地址不同,不会相互影响。
这样就达成了一个目的:组件复用,但是修改某一个组件的数据不会对其他组件产生连锁反应。
如果需要组件之间产生连锁反应,则在return返回一个外部对象即可,不用创建对象然后返回
*/
let obj = {count:0}
data() {
return obj
}
二、组件通讯
解释:组件通讯可以分为父子组件通讯,兄弟组件通讯和跨级组件通讯
1.父向子传递数据props
props
和data
函数返回的数据主要区别就是props
来自父组件,而data
的数据是自己的数据,作用域是组件本身。两种值都可以在组件中使用。- 子组件不能直接访问父组件的数据。所以需要父组件传递给子组件数据。
(1)props使用数组
注意:使用props
数组接收父组件的数据,数组内的元素必须是字符串
- 先看基本使用,使用
props
绑定固定的值
<div id="app">
<my-component message="这是来自父组件的数据"></my-component>
//一般这里会使用v-bind绑定父组件的值,这个例子是写死的。
</div>
<template id="cpn">
<div>
{{message}}
</div>
</template>
---------------------------------------------------------
Vue.component("my-component", {
template: "#cpn",
props: ["message"]
})
DOM图
- 使用
props
接收父组件的数据,当父组件的数据发生变化时,会动态的传递给子组件。
<div id="app">
//4.使用子组件,并把父组件vue实例的数据传递到子组件即cpn:通过v-bind:子数据=父数据
<my-component :hobby="hobbies"></my-component>
//这两个input是用来测试父组件变化时会影响子组件的测试代码
<input type="checkbox" v-model="hobbies" value="爬山">爬山
<input type="checkbox" v-model="hobbies" value="打球">打球
</div>
//1.创建组件模板
<template id="cpn">
<div>
<ul>
//5.此时在子组件的模板中已经接收到了父组件的数据,就可以使用了
<li v-for="myHobby of hobby">{{myHobby}}</li>
</ul>
</div>
</template>
------------------------------------------------------------
//2.创建组件实例,并绑定组件模板
const cpn = {
template: "#cpn",
props: ["hobby"]
}
let app = new Vue({
el: '#app',
data: {
hobbies: ["唱歌", "跑步", "游戏"]//这是父组件的数据
},
components: {
//3.在vue实例中使用cpn组件,这时vue实例就是父组件,cpn就是子组件
"my-component": cpn
}
});
DOM图
(2)props使用对象(数据验证)
- 数据验证可以对传入的数据进行验证,不符合的就会报错
<div id="app">
<my-cpn :message="message"></my-cpn>
</div>
<template id="cpnTemplate">
<div>
{{message}}
</div>
</template>
----------------------------------------------
let cpn = {
template: "#cpnTemplate",
props: {
message: {
//type写验证的数据类型
type: String,
//当父组件没有传递数据的时候就会使用这里的默认值
default:"默认值",
//这向数据是否必须传递
required: false,
}
}
}
/*
需要注意:当type为Array或者Object的时候,默认值不能直接写在后面
而是要写default函数,通过这个函数返回默认值
type: Array/Object,
default(){
return []/{}
}
*/
let app = new Vue({
el: '#app',
data: {
message: "message",
},
components: {
'my-cpn': cpn
}
});
2.子向父传递数据
(1)自定义事件$emit
- 子组件向父组件传递数据用
$emit
<div id="app">
/*
3.v-on不仅可以监听DOM事件,还可以监听自定义事件,这里的自定义事件是cpnclick,
监听DOM事件不写括号自动传event,而自定义事件则是把数据传过去,这里的数据是product
*/
<cpn @cpnclick="cpnClick"></cpn>
</div>
<template id="cpn">
<div>
//1.遍历sort分类,监听点击事件,并把当前分类传过去
<button v-for="product in sort" @click="btnClick(product)">{{product.name}}</button>
</div>
</template>
-----------------------------------------------------
const cpn = {
template: "#cpn",
methods: {
btnClick(product) {
//2.通过自定义事件向父组件发送数据,第一个参数为事件名,第二个参数为向父组件传递的数据
this.$emit('cpnclick', product)
}
},
data() {
return {
sort: [
{ id: "iPhone", name: "手机" },
{ id: "book", name: "书籍" },
]
}
}
}
let app = new Vue({
el: '#app',
methods: {
cpnClick(product) {
//4.这里是父组件,在这里就已经接收到了子组件传递的数据
console.log(product.name);
}
},
components: {
cpn,
}
});
//注意:自定义事件不支持驼峰和分隔符,只能全部小写
3.组件通讯中的v-model
(1)使用v-model绑定父组件传递过来的数据
注释:因为vue
不支持在子组件中修改props
,所以直接绑定是不行的,需要把props
的数据保存到本组件的data
函数里面,最后绑定函数的数据才是才是正确的
<div id="app">
<cpn :test-value="parentValue"></cpn>
</div>
<template id="cpnTemplate">
<div>
<h1>标题</h1>
<p>{{reValue}}</p>
<p>{{testValue}}</p>
//这里的reValue,testValue输出的数据一样
<input type="text" v-model="testValue">//修改这里会报错,vue不支持在子组件中修改props
<input type="text" v-model="reValue">//修改这里不会保存,修改的是data的数据
</div>
</template>
---------------------------------------------------
const cpn = {
template: "#cpnTemplate",
props: ["testValue"],
data() {
//把props中从父组件接收的数据保存到本组件。
return { reValue: this.testValue, }
}
}
let app = new Vue({
el: '#app',
data: {
parentValue: "这是测试数据"
},
components: {
cpn,
}
});
(1)使用v-model改变子组件数据时也改变父组件
<div id="app">
<p>父组件数据: {{total}}</p>
//2.当子组件的自定义事件是input时,此时使用v-model绑定的数据会被赋值:赋的值是子组件自定义事件传递过来的数据
<cpn v-model="total"></cpn>
</div>
<template id="cpn">
<div>
<p>子组件数据: {{counter}}</p>
<button @click="handleClick">+</button>
</div>
</template>
----------------------------------------------
const cpn = {
template: "#cpn",
data() {
return {
counter: 0
}
},
methods: {
handleClick() {
this.counter++;
//1.当子组件数据改变时,产生一个input事件,并传递一个数据
this.$emit('input', this.counter);
}
}
}
let app = new Vue({
el: '#app',
data: {
total: 0,
},
components: {
cpn,
}
});
三、组件访问
1.中央事件总线
解释:以后补充
2.父链与子组件索引
(1)父链介绍
$parent
//不要使用$parent来访问父组件,其实在子组件中,按照规范是不能依赖父组件的数据的,
//更别说去修改了,能不用$parent就不用
//所以父子组件之间的还是用$emit
this.$parent.$parent.message = "Change parent message"
(2)子组件索引
$children
/*
了解,因为根据索引选择资元素不靠谱,索引改变之后就不是自己想选择的了
*/
this.$children[0]//返回包含所有子组件的数组
$refs(重点)
<div id="app">
<cpn ref="cpnA"></cpn>
<cpn ref="cpnB"></cpn>
<cpn ref="cpnC"></cpn>
<button @click="enterCpn">访问子组件</button>
</div>
-------------------------------------------------
const app = new Vue({
el: '#app',
methods: {
enterCpn() {
//$refs一开始是一个空对象,但使用子组件的时候给标签添加了ref属性后,对应的组件就添加到了$refs对象中,此时就可以直接访问了
console.log(this.$refs.cpnA.message);
}
},
});
四、solt插槽
1. 为什么使用插槽
解释:为了提高组件的可复用性月扩展性,组件的功能由外部决定
<template>
<div>
<nav>导航</nav>
<button>登录</button>
</div>
</template>
/*
在导航条上,未登录的时候显示登录按钮,但是登录之后复用这个组件还显示登录按钮明显不合适.
此时组件复用的价值就减少了,此时可以使用插槽slot,就是给组件中的某个部分预留位置.
这个位置显示什么,做什么功能由外部决定,也就是使用者决定.
*/
<template>
<div>
<nav>导航</nav>
<slot>这里写什么由具体情况决定</slot>
</div>
</template>
2. 如何使用插槽封装组件
- 在使用slot封装组件的时候,把相同的部分抽取出来写在组件里,不同的地方预留成
slot
,即抽取共性,保留不同,这个组件不管如何复用,也不管复用了多少次,它们的结构都是相同的.
3. 如何使用插槽
(1)基本使用
<div id="app">
//2.在cpn标签里面写的代码会代替插槽的位置显示
<cpn><button>登录</button></cpn>
</div>
<template id="cpn">
<div>
<nav>导航</nav>
//1.这里预留位置便于组件复用的时候显示什么东西或者做什么功能
<slot></slot>
</div>
</template>
----------------------------------------
const cpn = {
template: "#cpn",
}
const app = new Vue({
el: '#app',
components: {
cpn
}
});
DOM图
(2)使用插槽默认值
<div id="app">
//2.在cpn标签写了代码的时候,会覆盖插槽的默认值
<cpn><p>这里覆盖了插槽的button这个默认值</p></cpn>
//3.cpn标签内部不写代码显示插槽默认值
<cpn></cpn>
</div>
<template id="cpn">
<div>
<p>导航</p>
//1.当插槽里面写了代码的时候,这个代码就相当于插槽的默认值.
<slot><button>登录</button></slot>
</div>
</template>
DOM图
(3)注意
补充:使用这种方式会有一个缺点,如果一个组件由多个插槽,在cpn
标签中给插槽赋值的时候会给所有插槽赋值,这不是我们想要的效果
<div id="app">
//2.给插槽赋值的时候,会给两个插槽都赋值,
//这不是我想要的效果,我只想给一个插槽赋值.明显不合理
<cpn>1</cpn>
<cpn>2</cpn>
</div>
<template>
<div>
//1.这里有两个插槽.
<slot>导航</slot>
<slot></slot>
</div>
</template>
//3.此时可以使用具名插槽
4. 具名插槽的使用
- 作用:给具体的某个插槽赋值
<div id="app">
<cpn>
//2.根据名字来进行插槽的替换
<span slot="left">替换了左边的插槽</span>
</cpn>
</div>
<template id="cpn">
<div>
//1.给插槽起名.
<slot name="left"><span>左边</span></slot>
<slot name="center"><span>中间</span></slot>
<slot name="right"><span>右边</span></slot>
</div>
</template>
注意:
在cpn标签里面替换插槽不根据插槽名字替换的时候,它只会替换没有起名字的插槽.
(1)注意
- 在cpn标签里面替换插槽不根据插槽名字替换的时候,它只会替换没有起名字的插槽.
5. 编译的作用域
解释:在模板里面使用的都是对应组件的变量,比如子组件和父组件都有一个数据叫message
,在父模板里使用message
的时候,绑定的是父组件的message
数据,在子模版使用message
的时候绑定的是子组件的message
。父组件模板的内容在父组件作用域内编译,子组件模板的内容在子组件作用域内编译
<div id="app">
//这是vue实例作用域,在这个例子中充当了父组件,这里的isShow使用的是父组件即vue实例的值true
<cpn v-show="isShow"></cpn>
</div>
<template id="cpn">
//这是子组件作用域,这里的isShow使用的是子组件的值false
<div v-show="isShow">
<p>子组件</p>
</div>
</template>
--------------------------------------------------------
const cpn = {
template: "#cpn",
data() {
return {
isShow: false,
}
}
}
const app = new Vue({
el: '#app',
data: {
isShow: true,
},
components: {
cpn
}
6. 作用域插槽
解释:父组件替换插槽的标签,但是内容由子组件提供。
个人理解:就是在父组件模板内可以使用子组件插槽里的数据,这就是作用域插槽的作用
<div id="app">
<cpn>
/*
注意:此时是在父组件的模板中
5.在使用插槽作用域的时候需要使用template模板
6.scope="byChildData":这句话的意思就是绑定子组件的插槽,byChildData只是一个变量名可以随便写
7.byChildSlot:此时是在父组件中,这个值就代表了子组件插槽,可以理解成引用指向对象。byChildSlot就是引用,插槽就是对象
可以使用它去访问子组件插槽里的数据
8.需要注意是不能直接访问子组件的数据的,这里访问的是插槽的数据,因为在子组件中把数据绑定到了插槽里所以在这里才可以访问
9.slot="slotName":代表替换所有名字叫"slotName"的插槽
*/
<template scope="byChildSlot" slot="slotName">
{{byChildSlot.bookName}}
</template>
</cpn>
</div>
<template id="cpn">
<div>
/*
1.在子组件模板内使用插槽slot
2.name属性:给插槽起名
3.v-for:遍历子组件自身的数据,此时会产生多个叫slotName的插槽
4.使用v-bind给book-name这个变量绑定book的name值
*/
<slot name="slotName" v-for="book of books" :book-name="book.name"></slot>
</div>
</template>
-----------------------------------------------------------
const cpn = {
template: "#cpn",
data() {
return {
books: [
{ name: "数据结构与算法" },
{ name: "操作系统" },
{ name: "计算机网络" },
{ name: "计算机组成原理" }
]
}
}
}
const app = new Vue({
el: '#app',
components: {
cpn
}
});
最后
以上就是幸福学姐为你收集整理的Vue组件详解一、组件与复用二、组件通讯三、组件访问四、solt插槽的全部内容,希望文章能够帮你解决Vue组件详解一、组件与复用二、组件通讯三、组件访问四、solt插槽所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复