组件(Component)是Vue最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用is特性
进行了扩展的原生HTML元素。
Vue让我们可以使用独立可复用的小组件来构建大型应用,任意类型的应用界面都可以抽象为一个组件树。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ugvqRms-1690870417749)(/004.webp)]
所有的Vue组件同时也都是Vue的实例,所以可以接受相同的选项对象。
通俗来说,比如说有些元素,比如ul
里面只能直接包含li
元素、select
里面只能包含option
、table
表格里面只能包含tr
、td
、tbody
等。比如像这样:
<ul>
<li>li>
<li>li>
ul>
这种写法是没有问题的,浏览器可以正确的解析,但是你要这么做,浏览器就看不懂了,比如下面的:
<ul>
<my-li>my-li>
<my-li>my-li>
ul>
这样就不能使用my-li
这种标签了,如果要达到我们的目的,我们就要使用is
特性。像这样,其中is属性的值是组件名:
<ul>
<li is="my-li">li>
<li is="my-li">li>
ul>
组件和模块的定位不同。组件一般用于前端,模块化在后台运用的比较多。例如vue中的组件,主要是为了拆分vue实例的代码量,让我们可以以不同的组件来划分不同的功能模块,将来我们需要什么样的功能,就直接调用对应的组件即可。
模块化中的模块一般指的是 Javascript 模块
组件则包含了 template、style 和 script,而它的 Script 可以由各种模块组成。也可以理解为“框架”,意思是把功能进行划分,将同一类型的代码整合在一起。组件化就相当于做一个页面,把页面中的每一个独立的功能拆分出来,可以尽情的拆分,最后组装成一个完整的页面。
组件化
主要从ui界面上进行划分。例如前端的组件化,方便ui组件的调用。类似于以前的导航栏。
模块化
主要从代码逻辑的角度进行划分,方便代码分层开发,保证每个功能模块职责单一。
<body>
<div id="myDiv">
<my-component>my-component>
<my-component>my-component>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 1、创建组件构造器
const myComponent = Vue.extend({
template: `
爱学习的少年
HelloWorld
HelloWorld
`
});
// 2、注册组件
Vue.component("my-component",myComponent);
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-component>my-component>
<my-component>my-component>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 1、创建组件构造器
const myComponent = Vue.extend({
template: ` // 模板只能有一个而且只能有一个根元素
爱学习的少年
HelloWorld
HelloWorld
`
});
// 2、注册组件【注意:如果使用的是驼峰命名,则在使用组件的时候需要使用'-'连接】
Vue.component("myComponent",myComponent);
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-component>my-component>
<my-component>my-component>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 1、直接使用Vue.component创建组件
Vue.component("myComponent",{
template: `
爱学习的少年
HelloWorld
`
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-component>my-component>
<my-component>my-component>
div>
<template id="myTemplate">
<div>
<h1>爱学习的少年h1>
<p>Springp>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 1、引入外部一个模板片段
Vue.component("myComponent",{
template: "#myTemplate"
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-component>my-component>
div>
<script type="text/x-template" id="myTemplate">
<h1>使用script实现注册组件</h1>
script>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate"
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-nav>my-nav>
<my-nav>my-nav>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
const app = new Vue({
el: "#myDiv",
components: {
myNav: {
template: "这个是导航条,私有组件
"
}
}
});
script>
body>
<body>
<div id="myDiv">
<my-nav>my-nav>
<my-nav>my-nav>
div>
<template id="myTemplate">
<h1>这个是导航条,私有组件h1>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
const app = new Vue({
el: "#myDiv",
components: {
myNav: {
template: "#myTemplate"
}
}
});
script>
body>
<body>
<div id="myDiv">
<my-nav>my-nav>
<my-nav>my-nav>
div>
<template id="myTemplate">
<h1>用户名是: {{username}}h1>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("my-nav",{
template: "#myTemplate",
data: function(){
return {username: 'HelloWorld'}
}
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-component>my-component>
<my-component>my-component>
<my-component>my-component>
div>
<template id="myTemplate">
<div>
计数器: {{count}} <br/>
<button @click="count++">+button>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate",
data: function () {
console.log("--"); // 打印了3次
return {count: 0}
}
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<a href="" @click.prevent="login">登录a>
<a href="" @click.prevent="register">注册a>
<login v-if="isLogin">login>
<register v-else>register>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("login",{
template: `
用户名登录:
`
});
Vue.component("register",{
template: `
用户名注册:
`
});
const app = new Vue({
el: "#myDiv",
data: {
isLogin: true
},
methods: {
login(){
this.isLogin = true;
},
register(){
this.isLogin = false;
}
}
});
script>
body>
<body>
<div id="myDiv">
<a href="" @click.prevent="componentName = 'login'">登录a> // 记得加单引号
<a href="" @click.prevent="componentName = 'register'">注册a> // 记得加单引号
<component :is="componentName">component>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("login",{
template: `
用户名登录:
`
});
Vue.component("register",{
template: `
用户名注册:
`
});
const app = new Vue({
el: "#myDiv",
data: {
componentName: "login"
}
});
script>
body>
<body>
<div id="myDiv">
<father>father>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 1、定义子组件
const children = Vue.extend({
template: `
这个子组件
`
});
// 2、定义父组件
const father = Vue.extend({
template: `
这个是父组件
`,
components: {
children
}
});
const app = new Vue({
el: "#myDiv",
components: {
father
}
});
script>
body>
<body>
<div id="myDiv">
<my-component>my-component>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
const app = new Vue({
el: "#myDiv",
data: {
username: "HelloWorld"
},
components: {
myComponent: {
template: '姓名是: {{username}}
' // 会报错
}
}
});
script>
body>
// 按道理来说,子作用域应该可以访问父级别作用域的,但是发现并不行。
解决思路可以这样:在父组件中,可以在使用子组件的时候,通过属性绑定的方式,把需要传递给子组件的数据以属性绑定的形式传递到子组件内部,这样的话,子组件内部就可以接收到了。
方式:通过props向子组件传递数据
字符串数组,数组中的字符串就是传递时的名称。
说明:让Vue的实例作为父组件,之后再定义一个子组件
<body>
<div id="myDiv">
<my-component v-bind:subaddresses="fatherAddresses">my-component>
div>
<template id="myTemplate">
<div>
<p v-for="item in subaddresses">
{{item}}
p>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate",
props: ["subaddresses"]
});
const app = new Vue({
el: "#myDiv",
data: {
fatherAddresses: [
'河南林州',
"陕西西安",
"浙江杭州"
]
}
});
script>
body>
父组件要向子组件传递数据,总结如下:
第一步:
在父组件去应用子组件的时候,去动态的绑定一个属性。
<my-component v-bind:subusername="username">my-component>
subusername
这个属性就是自定义的一个属性名称,目的是向子组件中传递数据的这么一个名称。而username
就是父组件的data
中的数据。
第二步:
自定义属性做好之后,子组件还不能直接使用,还需要接收。即:把父组件传递过来的subusername
属性,需要在子组件的props
数组中定义,注意是一个字符串的形式,这样的话,子组件才能使用这个数据。
也可以这么说:组件中所有的props中定义的数据,都是通过父组件传递给子组件的。
第三步:
子组件在template
中使用子组件props
定义的名称,就可以使用数据了。
说明几个细节问题:
data
属性,也就是说子组件可以有自己的数据,而data
中的数据并不是通过父组件传递过来的,而是子组件自身所独有的,常用的方式是:子组件可以通过调用ajax请求数据,之后把查询出来的数据填充到data
属性中。而子组件的props
属性一定是从父组件传递过来的。data
属性中的数据都是可读可更改的,而props
属性中的数据是只读的。 对象,对象可以设置传递时的类型,也可以设置默认值
<body>
<div id="myDiv">
<my-component v-bind:subaddresses="fatherAddresses">my-component>
div>
<template id="myTemplate">
<div>
<p v-for="item in subaddresses">
{{item}}
p>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subaddresses: Array // 对象,可以指定类型
}
});
const app = new Vue({
el: "#myDiv",
data: {
fatherAddresses: [
'河南林州',
"陕西西安",
"浙江杭州"
]
}
});
script>
body>
指定默认值:基本类型
<body>
<div id="myDiv">
<my-component v-bind:subusername="fatherUsername">my-component> // HelloWorld
<my-component>my-component> //使用组件没有传,就用默认的值
div>
<template id="myTemplate">
<div>
{{subusername}}
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subusername: {
type: String,
default: "Spring"
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
fatherUsername: "HelloWorld"
}
});
script>
body>
指定默认值:对象或者数组
<body>
<div id="myDiv">
<my-component v-bind:subaddresses="fatherAddresses">my-component>
<my-component>my-component>
div>
<template id="myTemplate">
<div>
<p v-for="item in subaddresses">{{item}}p>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subaddresses: {
type: Array,
default: function(){
return ['HelloWorld' , "Spring"]; // 对象或数组默认值的话要用函数
}
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
fatherAddresses: [
"河南林州",
"浙江杭州"
]
}
});
script>
body>
指定是否必传
<body>
<div id="myDiv">
<my-component v-bind:subusername="fatherUsername">my-component>
<my-component>my-component> // 会报错,要必传才行
div>
<template id="myTemplate">
<div>
{{subusername}}
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subusername: {
type: String,
required: true // 表示必传
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
fatherUsername: "HelloWorld"
}
});
script>
body>
<body>
<div id="myDiv">
<my-component v-bind:sub-addresses="fatherAddresses">my-component>// 标签需要"-"连接
<my-component>my-component>
div>
<template id="myTemplate">
<div>
<p v-for="item in subAddresses">{{item}}p> // 拿数据依然是驼峰法方式
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 注册组件
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subAddresses: { // 驼峰命名
type: Array,
default: function(){
return ['HelloWorld' , "Spring"];
}
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
fatherAddresses: [
"河南林州",
"浙江杭州",
"云南大理"
]
}
});
script>
body>
方式:通过自定义事件向父组件发送消息
<body>
<div id="myDiv">
<my-component @fn="fatherFn">my-component>
div>
<template id="myTemplate">
<div>
<button @click="subCompClick">子组件向父组件传递参数使用的事件的方式button>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 定义子组件
Vue.component("myComponent",{
template: "#myTemplate",
data: function(){
return {
person: {
username: '我是子组件HelloWorld'
}
}
},
methods: {
subCompClick(){
// 点击之后,子组件发射一个自定义的事件
this.$emit("fn",this.person);
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
username: "HelloWorld"
},
methods: {
fatherFn(obj){
console.log(obj);
}
}
});
script>
body>
解决的思路是这样的:既然我们能做到父组件的data数据传递到子组件中,我们就可以实现将父组件的方法传递到子组件中。【实际上,虽然我们现在的确是在讲子组件向父组件传递数据,如果要按照这种思维方式去实现代码的话,不太好理解,我个人更推荐这种理解方式,即:现在不考虑子组件向父组件传递数据,我们就一律看做是:父组件向子组件传递数据,如果按照这种方式来去理解的话,写代码就会顺畅很多,通过此种方式去理解代码,写着写着就自然而然的就成了子组件向父组件传递数据了。】==
子组件向父组件传递数据/父组件向子组件传递方法,步骤总结如下:
第一步
在父组件中定义一个方法。本案例中Vue实例作为父组件,自定义的方法是fatherFn
。
第二步
在应用子组件的标签上,去动态绑定一个事件。本案例中的事件是fn
。
<my-component @fn="fatherFn">my-component>
代码的含义:就相当于是将父组件中的fatherFn
方法的引用传递给子组件的fn
事件函数。此时该fn
函数肯定是需要在某个时刻要用到的。
第三步
既然我们是子组件向父组件传递数据,那么肯定是在子组件中做了一些操作,然后将数据传递给父组件。本案例中,在子组件template
中有一个button
按钮,该按钮的作用就是当点击的时候,向父组件传递数据。并且为该按钮绑定了一个事件,属于该按钮的事件,是subCompClick
。
第四步
在子组件中的methods
属性中去定义subCompClick
事件,当点击按钮的时候就会触发该事件,那么在
subCompClick
事件中,操作是:调用$emit
方法去发射我们的fn
事件,并且通过该方法传递数据,而我们知道,我们的fn
其实就是我们第一步骤中的fatherFn
。 然后在fatherFn
函数中可以通过参数的形式去接收子组件传递的数据。
<body>
<div id="myDiv">
<my-component v-on:get-list-click="fatherHandle">my-component>
div>
<template id="myTemplate">
<div>
<a href=""
v-for="item in categories"
@click.prevent="aClick(item)">
{{item.name}} <br>
a>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
// 子组件
Vue.component("myComponent",{
template: "#myTemplate",
data: function(){
return {
categories: [
{id: 1, name: "动作片"},
{id: 2, name: "爱情片"},
{id: 3, name: "悬疑片"},
{id: 4, name: "悬疑片"}
]
};
},
methods: {
aClick(item){
// 点击之后,子组件发射一个自定义的事件
this.$emit("get-list-click",item);
}
}
});
const app = new Vue({
el: "#myDiv",
methods: {
fatherHandle(item){
console.info("接收的信息是:" ,item);
}
}
});
script>
body>
该案例:子组件向父组件传数据综合运用。该案例是我们二期jQuery的一个案例:发表评论的案例。
<body>
<div id="myDiv">
<p v-for="item in list" :key="item.id">
评论人: {{ item.user }} 内容: {{ item.content }} 发表日期: {{ item.pubDate }}
p>
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
const app = new Vue({
el: "#myDiv",
data: {
list: [
{id: 1, user: 'HelloWorld', content: '美好的一天', pubDate: new Date()},
{id: 2, user: 'Spring', content: '很高兴', pubDate: new Date()}
]
}
});
script>
body>
<body>
<div id="myDiv">
<my-component>my-component>
<p v-for="item in list" :key="item.id">
评论人: {{ item.user }} 内容: {{ item.content }} 发表日期: {{ item.pubDate }}
p>
div>
<template id="myTemplate">
<div>
评论id: <input type="text"> <br/>
评论人: <input type="text"> <br/>
评论内容:<textarea>textarea><br/>
<input type="button" value="提交">
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
});
const app = new Vue({
el: "#myDiv",
data: {
list: [
{id: 1, user: 'HelloWorld', content: '美好的一天', pubDate: new Date()},
{id: 2, user: 'Spring', content: '很高兴', pubDate: new Date()}
]
}
});
script>
body>
<body>
<div id="myDiv">
<my-component @fn="fatherFn">my-component>
<p v-for="item in list" :key="item.id">
评论人: {{ item.user }} 内容: {{ item.content }} 发表日期: {{ item.pubDate }}
p>
div>
<template id="myTemplate">
<div>
评论id: <input type="text" v-model="id"> <br/>
评论人: <input type="text" v-model="user"> <br/>
评论内容:<textarea v-model="content">textarea><br/>
<input type="button" value="发表" @click="publish">
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
data:function(){
return {id: '' , user: '' , content: ''}
},
methods: {
publish: function(){
// 发表评论业务处理
// 1、构造一个评论数据对象
let comment = {id: this.id, user: this.user , content: this.content, pubDate: new Date()};
// 2、需要将创建好的评论数据保存到父组件中的data中,涉及到了子组件向父组件传值
// >> 子组件发射一个自定义的事件
this.$emit("fn",comment);
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
list: [
{id: 1, user: 'HelloWorld', content: '美好的一天', pubDate: new Date()},
{id: 2, user: 'Spring', content: '很高兴', pubDate: new Date()}
]
},
methods: {
fatherFn(comment){
// 刚刚新增评论就插入到数组的头部
this.list.unshift(comment);
}
}
});
script>
body>
说明:该案例体现了父子组件相互传递数据。
<body>
<div id="myDiv">
父组件的数据:{{username}} {{age}}
<my-component :subusername="username" :subage="age">my-component>
div>
<template id="myTemplate">
<div>
通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}}
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subusername: String,
subage: Number
}
});
const app = new Vue({
el: "#myDiv",
data: {
username: 'HelloWorld',
age: 12
}
});
script>
body>
说明:通过案例可以发现,子组件的确可以使用到了父组件中的数据。子组件可以使用props中的数据并显示到页面上,不过props中的数据是从父组件中获取的,是只读数据。
在子组件中,定义两个input
,实现双向绑定,绑定到了props
属性的subusername
和subage
这两个属性,看下实验效果。
<body>
<div id="myDiv">
父组件的数据:{{username}} {{age}}
<my-component :subusername="username" :subage="age">my-component>
div>
<template id="myTemplate">
<div>
通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}} <br/>
双向绑定props属性中的subusername: <input type="text" v-model="subusername"><br/>
双向绑定props属性中的subage: <input type="text" v-model="subage"><br/>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subusername: String,
subage: Number
}
});
const app = new Vue({
el: "#myDiv",
data: {
username: 'HelloWorld',
age: 12
}
});
script>
body>
通过实验发现,该案例是有问题的,控制台会报错。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UlBrOQmL-1690870417750)(/005.png)]
结论:根据提示,避免去直接修改props属性中的数据,因为props属性中的数据都是通过父组件传递过来的,是只读的,避免去覆盖。取而代之的是,可以使用data和computed计算属性。
<body>
<div id="myDiv">
父组件的数据:{{username}} {{age}}
<my-component :subusername="username" :subage="age">my-component>
div>
<template id="myTemplate">
<div>
通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}} <br/>
子组件data属性中的数据:{{data_subusername}}---{{data_subage}} <br/>
双向绑定data属性中的subusername: <input type="text" v-model="data_subusername"><br/>
双向绑定data属性中的subage: <input type="text" v-model="data_subage"><br/>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subusername: String,
subage: Number
},
data: function(){
return {
data_subusername: this.subusername,
data_subage: this.subage
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
username: 'HelloWorld',
age: 12
}
});
script>
body>
使用data问题解决。但是还有一个需求就是:如果我想改变子组件中的文本框的数据,也想同步修改到父组件,让父组件也实现同步更新。这样的话,就涉及到了子组件向父组件传递数据,需要使用到自定义事件。
需求:要想实现子组件的数据可以传递到父组件,也就是说子组件中的data数据如果发生了改变,那么父组件也可以感知到,则需要发射自定义事件来解决。此时,可以在子组件中为data中的属性设置侦听器来实现,当属性发生了修改,立马侦听到之后,再发送事件。
<body>
<div id="myDiv">
父组件的数据:{{username}} {{age}}
<my-component :subusername="username" :subage="age"
@subchangeusername="fatherChangeUsername"
@subchangeage="fatherChangeAge"
>my-component>
div>
<template id="myTemplate">
<div>
子组件通过props属性获取到的父组件中的数据:{{subusername}}---{{subage}} <br/>
子组件通过data属性获中的数据:{{data_subusername}}---{{data_subage}} <br/>
双向绑定props属性中的subusername: <input type="text" v-model="data_subusername"><br/>
双向绑定props属性中的subage: <input type="text" v-model="data_subage"><br/>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
props: {
subusername: String,
subage: Number
},
data: function(){
return {
data_subusername: this.subusername,
data_subage: this.subage
}
},
watch: {
data_subusername(newValue){
// 发生了修改,发送事件
this.$emit("subchangeusername",newValue);
},
data_subage(newValue){
// 发生了修改,发送事件
this.$emit("subchangeage",newValue);
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
username: 'HelloWorld',
age: 12
},
methods: {
fatherChangeUsername(value){
this.username = value;
},
fatherChangeAge(value){
this.age = parseInt(value);
}
}
});
script>
body>
使用方式:$children
或者是$refs
<body>
<div id="myDiv">
<my-component>my-component>
<my-component>my-component>
<input type="button" value="单击" @click="handleClick">
div>
<template id="myTemplate">
<div>
这个是子组件
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
data: function(){
return {
username: "HelloWorld"
}
},
methods: {
info(){
console.info("子组件的用户名:" + this.username);
}
}
});
const app = new Vue({
el: "#myDiv",
methods: {
handleClick(){
// 单击父组件的按钮,执行该函数,目的是访问子组件中的数据和调用子组件中的方法
console.info(this.$children); // 返回的结果是一个数组
for (let comp of this.$children){
console.info(comp.username);
comp.info();
}
}
}
});
script>
body>
总结:此种方式使用不多,要获取具体的子组件还需要通过下标的方式来去获取,非常不方便。
<body>
<div id="myDiv">
<my-component ref="comp1">my-component>
<my-component ref="comp2">my-component>
<input type="button" value="单击" @click="handleClick">
div>
<template id="myTemplate">
<div>
这个是子组件
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
data: function(){
return {
username: "HelloWorld"
}
},
methods: {
info(){
console.info("子组件的用户名:" + this.username);
}
}
});
const app = new Vue({
el: "#myDiv",
methods: {
handleClick(){
// 单击父组件的按钮,执行该函数,目的是访问子组件中的数据和调用子组件中的方法
console.log(this.$refs.comp1.username);
this.$refs.comp1.info();
console.log(this.$refs.comp2.username);
this.$refs.comp2.info();
}
}
});
script>
body>
总结:这种方式类似于给组件起了一个id值,通过id的方式直接获取到了某个组件。通常,通过$refs
来去获取Dom元素。
<body>
<div id="myDiv">
<p ref="p1">HelloWorldp>
<p ref="p2">Springp>
<input type="button" value="操作Dom元素" @click="handleClick">
div>
<script src="../js/vue.js">script>
<script type="text/javascript">
const app = new Vue({
el: "#myDiv",
methods: {
handleClick(){
console.log(this.$refs.p1.innerText);
console.log(this.$refs.p2.innerText);
}
}
});
script>
body>
访问父组件使用方式:$parent
访问根组件使用方式:$root
,也就是访问的Vue实例这个根组件
<body>
<div id="myDiv">
<my-component>my-component>
div>
<template id="myTemplate">
<div>
这个是子组件
<input type="button" value="访问父组件" @click="subHandleClick">
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
methods: {
subHandleClick(){
// 访问父组件
console.log(this.$parent.message);
this.$parent.fatherInfo();
// 访问根组件
console.log(this.$root.message);
this.$root.fatherInfo();
}
}
});
const app = new Vue({
el: "#myDiv",
data: {
message: 'Spring'
},
methods: {
fatherInfo(){
console.info("父组件的信息:" + this.message);
}
}
});
script>
body>
插槽,其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,让你来传入一些东西。决定将所携带的内容,插入到指定的某个位置,从而使模板分块,具有模块化的特质和更大的重用性。
插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。
适用场景:是那些可以将多个组件看做一个整体,这个整体会被复用。但其中的一些部分内容不固定。
需求:我现在有一个div,是一个子组件,里面有公共的代码,就是p标签,但是这个子组件在不同页面上所展示的效果还是有细微区别的,可能A页面是一个button按钮,B页面可能是一个p标签。
<body>
<div id="myDiv">
<my-component>
<button>这个是按钮button>
my-component>
-------------------------------------- <br>
<my-component>
<p>这个是p标签p>
my-component>
div>
<template id="myTemplate">
<div>
<p>我是子组件p>
<slot>slot> // 相当于是占位符
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-component>
my-component>
-------------------------------------- <br>
<my-component>
<p>这个是p标签p>
my-component>
div>
<template id="myTemplate">
<div>
<p>我是子组件p>
<slot>
<button>这个是按钮,为插槽指定默认内容button>
slot>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
总结:该案例是在slot
插槽中设置了一个button
按钮,相当于是一个默认值,那么此时,在使用该组件的时候,可以不传入了,那么就用默认值button
,如果传入了,则就用指定的传入Dom模板。
说明:如果在一个组件内有多个插槽,如何为指定的插槽填充内容呢,此时就需要为每个插槽提供一个名字,这种插槽就叫做具名插槽。
<body>
<div id="myDiv">
<my-component>
<button>此处替换为按钮button>
<p>此处是P标签p>
my-component>
div>
<template id="myTemplate">
<div>
<slot><p>Tomcatp>slot>
<p>我是子组件p>
<slot><p>HelloWorldp>slot>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
总结:会发现,组件中的slot
插槽中的内容都被button
和p
所替代了。
<body>
<div id="myDiv">
<my-component>
<button slot="slot1">此处替换为按钮button>
<p>此处是P标签p>
my-component>
div>
<template id="myTemplate">
<div>
<slot name="slot1"><p>Tomcatp>slot>
<p>我是子组件p>
<slot name="slot2"><p>HelloWorldp>slot>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
<body>
<div id="myDiv">
<my-component>
<template v-slot:slot1>
<button>此处替换为按钮button>
template>
<p>此处是P标签p>
my-component>
div>
<template id="myTemplate">
<div>
<slot name="slot1"><p>Tomcatp>slot>
<p>我是子组件p>
<slot name="slot2"><p>HelloWorldp>slot>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
注意:在使用slot的时候,单独的使用slot已经在Vue2.6版本已经废弃,取而代之的是v-slot。
<body>
<div id="myDiv">
<my-component v-show="isShow">my-component>
div>
<template id="myTemplate">
<div>
<p>我是HelloWorldp>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
data: function () {
return {
isShow: true
}
}
});
const app = new Vue({
el: "#myDiv",
data:{
isShow: false
}
});
script>
body>
看代码:在Vue实例中的data属性中有一个isShow,值是false,在子组件中也有一个isShow属性,值是true,在使用子组件的时候,my-component v-show="isShow"
中的isShow实际上使用的是Vue实例中的isShow,所以页面中是不显示子组件的。
注意:通过该案例发现,在使用isShow这个变量的时候,操作是这样的:是看这个isShow这个变量是在哪个模板/组件中的,而不是看这个变量被哪个子组件使用的。以当前案例为例,isShow这个变量是在一个叫做myDiv
这个模板中的,所以isShow这个变量的作用域就是Vue实例,那么当然使用的是Vue实例中的data。
<body>
<div id="myDiv">
<my-component v-show="isShow">my-component>
div>
<template id="myTemplate">
<div>
<p v-show="isShow">我是HelloWorldp>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
data: function () {
return {
isShow: false
}
}
});
const app = new Vue({
el: "#myDiv",
data:{
isShow: true
}
});
script>
body>
案例:将Vue实例的data中的isShow的值改为true,子组件中的isShow的值改为false,并且在template
模板/子组件中也使用了isShow,会发现,页面依然是不显示子组件内容,此时,模板中的isShow使用的就是子组件中的isShow。
含义:父组件替换插槽的标签,但是内容由子组件提供。
需求:子组件有一组数据,比如说是一个数组,那么这些数据需要在多个界面进行展示,可能有的界面需要横向排列,有的界面需要纵向排列,有些界面可能就是直接显示一个数组,问题是:数据在子组件中,希望父组件
告诉我们如何显示,如何做?此时,就需要用到==slot作用域插槽==。
<body>
<div id="myDiv">
<my-component>my-component>
<my-component>
<div slot-scope="subData">
{{ subData.data.join(' - ') }}
div>
my-component>
<my-component>
<template slot-scope="subData">
{{ subData.data.join(' * ') }}
template>
my-component>
div>
<template id="myTemplate">
<div>
<slot :data="addresses">
<ul>
<li v-for="item in addresses">
{{ item }}
li>
ul>
slot>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
data: function () {
return {
addresses: ['河南林州',"浙江杭州","陕西西安"]
}
}
});
const app = new Vue({
el: "#myDiv"
});
script>
body>
自 2.6.0 起有所更新。已废弃的使用 slot-scope
,使用v-slot
代替,直写在组件标签上。
<body>
<div id="myDiv">
<my-component>my-component>
<my-component v-slot="subData">
{{ subData.data.join(' - ') }}
my-component>
<my-component v-slot="subData">
{{ subData.data.join(' * ') }}
my-component>
div>
<template id="myTemplate">
<div>
<slot :data="addresses">
<ul>
<li v-for="item in addresses">
{{ item }}
li>
ul>
slot>
div>
template>
<script src="../js/vue.js">script>
<script type="text/javascript">
Vue.component("myComponent",{
template: "#myTemplate",
data: function () {
return {
addresses: ['河南林州',"浙江杭州","陕西西安", "云南大理"]
}
}
});
const app = new Vue({
el: "#myDiv"
});
script>
body>