什么是组件:如果我们把所有的代码都写进一个Vue实例中,那这个实例就太大了。
组件的出现,就是为了拆分Vue实例的代码量,能够让我们以不同的组件,划分不同的功能模块,将来需要什么功能就去调用什么组件。
组件和组件化和模块化的不同
模块化:是从代码逻辑的角度进行划分的,方便代码分层开发,保证每个功能模块的职能单一。
组件化:是从UI界面的角度进行划分的,方便UI组件的重用
创建组件
1. Vue.extend 、 Vue.component、 html标签
<div id="app">
<!-- 4.直接把组件的名称以Html标签的形式引入到文档中 ,注意驼峰式命名以-替换-->
<my-h3></my-h3>
</div>
......
<script>
//1.使用Vue.extend来创建全局的组件
//第一个参数是一个对象
var H3 = Vue.extend({
//2.通过template指向了组件要展示的html结构
template: "<h3>这里是一个h3标签</h3>",
});
// 3.使用component("组件的名称",创建出来的组件模板对象)
Vue.component("myH3",H3);
new Vue({
el: "#app",
});
</script>
定义成功
组件名称使用驼峰式命名,则需要把大写的驼峰改为小写的字母,两个单词之间用“-”连接。
上面把组件的定义分为了两部,但事实上,我们可以合为一步。
<myh4></myh4>
.......
Vue.component("myh4",Vue.extend({
template: "<h4>这是一个h4标签</h4>"
}));
2.Vue.component 、html标签
更近一步,我们可以继续简化,只使用Vue.component 、html标签注册组件
<my-h5></my-h5>
......
Vue.component("myH5",{
template:"<h5>这是一个h5标签,直接通过Vue.component创建出来的</h5>"
});
注册成功
3. Vue.component ,template元素,id属性(不能改为class)
上面两种方式写组件的html都没有提示,也不用高亮,下面这种写法就解决了这两个问题。
<div id="app">
<my-h6></my-h6>
</div>
<!-- 在div#app的外面,使用template定义组件的Html结构 ,注意这里也要符合只有一个根元素-->
<template id="myh6">
<div>
<h6>这是一个h6标签,我们通过template元素,把它写在了div#app的外</h6>
<h6>有代码的提示和高亮,好用</h6>
</div>
</template>
......
Vue.component("myH6",{
template: "#myh6"
});
注意,不论是哪种方式注册的组件,组件的template属性指向的模板内容,必须有且仅有唯一的一个根元素
定义私有组件:
跟咱们前面定义私有过滤器,定义私有自定义指令类似
<div id="app">
<login></login>
</div>
<!-- 在div#app的外面,使用template定义组件的Html结构 ,注意这里也要符合只有一个根元素-->
<template id="login">
<form action="">
<input type="text" value="请输入登录名">
<input type="text" value="请输入登陆密码">
</form>
</template>
......
<script>
new Vue({
el: "#app",
components: {
//定义vue实例的私有组件
login: {
template : "#login"
},
},
});
image.png
组件中的data和methods
我们知道Vue实例有自己的data,那么,组件也可以有吗?不妨一试
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<link rel="stylesheet" href="../bootstrap/css/bootstrap.min.css">
......
<div id="app">
<login></login>
</div>
<template id="login">
<form action="" class="form form-inline">
<label for="">{{ msg1 }} <input type="text" value="请输入登录名" class="form-control"></label>
<label for="">{{ msg2 }} <input type="text" value="请输入登陆密码" class="form-control"></label>
</form>
</template>
......
<script>
new Vue({
el: "#app",
data: {},
methods: {},
components: {
login: {
template: "#login",
data: {
msg1: "用户名:",
msg2: "密码:",
},
},
},
});
</script>
结果是报错了,难道是不可以有自己的data了吗?当然不是,是我们的定义方式错了
login: {
template: "#login",
// data: {
// msg1: "用户名:",
// msg2: "密码:"
// },
data: function(){
return {
msg1: "用户名:",
msg2: "密码:"
}
},
},
✌,组件也可以有自己的data
得出结论:
组件可以有自己的数据,组件的data和实例的data有点不一样,实例中的data可以为一个对象,但是组件中的data必须是一个方法,这个方法必须返回一个对象。组件中的data属性的使用方式与实例的data属性使用一模一样。
组件也可以有自己的methods(就不测试了😂)
❓为什么第一个写法错了,而第二个写法就可以了呢
先看下面这个例子
<div id="app">
<counter></counter>
</div>
<template id="counter">
<div class="container">
<input type="button" value="+1" @click="increment()">
<h6 v-text="count"></h6>
</div>
</template>
......
<script>
var countObj = {
count: 0,
}
new Vue({
el: "#app",
components: {
counter: {
//这是一个计数器的组件,身上有个按钮,每点击按钮,让data中的count +1
template: "#counter",
data: function(){
return countObj
},
methods: {
increment(){
this.count += 1;
}
}
}
},
});
</script>
能成功的加一
咱们定义组件是为了复用,我们来复用一下这些个组件。看看这样写法会出啥问题😏
<div id="app">
<counter></counter>
<hr>
<counter></counter>
<hr>
<counter></counter>
</div>
oh,god,下面的数据也跟着变了,可咱们要的不是这样啊
出现这样的原因就是,我们在外部定义了一个object,该对象是引用类型。所以当使用组件的时候,return出去的永远都是这个object的地址,都是一样的,才会出现这些组件指向了同一个data。我们不想这样,那怎么办呢?那就return出去一个新的object呗!
counter: {
//这是一个计数器的组件,身上有个按钮,每点击按钮,让data中的count +1
template: "#counter",
data: function(){
// return countObj
return {
count: 0,
}
},
methods: {
increment(){
this.count += 1;
}
}
}
每个组件都有自己的数据,而不是共享一个数据了
用function,再return一个object对象出去,就是为了解决多个组件共享一个data的尴尬情形。所以,如果我们直接在组件的data里写一个对象,或者用function,return出去了一个早已定义好的对象的话,都是行不通的。关键点在于给每个组件一个属于自己的data,即是,每个组件的data的引用地址需不同。
组件的切换
小例子:
组件切换例子
我们实现登陆/注册的切换
- 1.先把组件的布局搞出来
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<link rel="stylesheet" href="../bootstrap/css/bootstrap.min.css">
......
<div id="app">
<login></login>
<register></register>
</div>
<template id="login">
<div class="container">
<div class="h1">登陆</div>
<form action="" class="form form-inline">
<label for="">用户名:<input type="text" class="form-control"></label>
<label for="">密码:<input type="paassowrd" class="form-control"></label>
</form>
</div>
</template>
<template id="register">
<div class="container">
<div class="h1">注册</div>
<form action="" class="form form-inline">
<label for="">用户名:<input type="text" class="form-control"></label>
<label for="">密码:<input type="password" class="form-control"></label>
</form>
</div>
</template>
......
<script>
new Vue({
el: "#app",
data: {},
methods: {},
components: {
//定义vue实例的私有组件
login: {
template : "#login",
},
register: {
template: "#register",
},
},
});
</script>
有点丑,不要紧
- 2.实现切换
- (1),用v-if,v-else,flag实现切换
<div id="app">
<div class="container">
<a href="" @click.prevent="flag = true">登陆</a>
<a href="" @click.prevent="flag = false">注册</a>
</div>
<login v-if="flag"></login>
<register v-else></register>
</div>
......
new Vue({
el: "#app",
data: {
flag: true,
},
......
为了更醒目,给注册加了一个确认密码框
这样的缺陷就是只能切换两个组件
- (2),用Vue提供的component元素实现组件切换
component是Vue提供的元素,来展示对应名称的组件。component是一个占位符,is属性指定要展示的组件的名称,组件的名称是一个字符串,故在is属性里组件的名称要用单引号包起来。
<!-- 第二种方式 :component是Vue提供的元素,来展示对应名称的组件。
component是一个占位符,is属性指定要展示的组件的名称
组件的名称是一个字符串,故在is属性里组件的名称要用单引号包起来-->
<div class="container">
<a href="" @click.prevent="comName = 'login'">登陆</a>
<a href="" @click.prevent="comName = 'register'">注册</a>
</div>
<component :is="comName"></component>
......
new Vue({
el: "#app",
data: {
comName: "login",
},
.......
效果一样的,就不放图了。
这种方式只要名字能对应上,多少个组件都没问题。
Vue提供的标签:
template
,component
,还有以前学过的transition
,transitionGroup
咱们学过动画,动画用于改善用户体验,例如这里组件切换时有点突兀,可以加一点动画
非常简单,给component元素包一个transition元素就可以了!
<style>
.v-enter, .v-leave-to{
opacity: 0;
transform: translateX(150px);
}
.v-enter-active, .v-leave-active{
transition: all 1s ease;
}
</style>
......
<transition>
<component :is="comName"></component>
</transition>
我觉得还行,但是老师说需要改进,让前一个出去完后,后一个再进来
<transition mode="out-in">
<component :is="comName"></component>
</transition>
好像确实好了一点
通过mode属性设置组件切换时的模式:out-in , in-out
组件传值
❓什么是父组件,什么是子组件,父子组件之间如何传值
父向子传值
其实,我们的Vue实例,就可以看成一个大的组件。Vue实例与私有组件可以看成父组件与子组件。
我们学习组件的data的时候,就介绍过组件里的data跟实例的data用法一样,即用v-text或者是插值表达式,故现在,如果我们直接在子组件里面用v-text或者插值表达式访问data的话,访问到的是自己(子组件)的data,访问不到父组件的data的。
子组件中默认没法访问到父组件中的data数据和methods方法。但有的时候,有一种需求:展示父组件提供的数据。那该怎么办呢?这就涉及到父组件向子组件传值了——父组件可以在引用子组件的时候通过属性绑定的形式(v-bind)把需要传递给子组件的数据以属性绑定的形式传递到子组件内部,供子组件使用。子组件把传递过来的数据当成props来用。
注意这个属性不能有大写,比如parentmsg 不能写成parentMsg,否则没作用
注意子组件的props都是父组件传递过来的。子组件的data里的数据是自己私有的,比如子组件通过ajax请求回来的数据可以放在data身上。
对于子组件而言,data上面的数据是可读可写的,而props中的数据是只读的(虽然你可以修改成功,但会报错,不建议这么做)。
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<link rel="stylesheet" href="../bootstrap/css/bootstrap.min.css">
......
<div id="app">
<com1 :parentmsg="msg"></com1>
</div>
<template id="com1">
<div class="container">
<p class="h1">这是子组件</p>
<p class="h1">{{ parentmsg }}</p>
</div>
</template>
......
<script>
var com1 = {
template: "#com1",
props: ["parentmsg"], //把父组件传递过来的 parentMsg 属性在子组件的props属性中定义一下,才可以使用。
}
new Vue({
el: "#app",
data: {
msg: "我是父组件中的数据"
},
methods: {
show(){
console.log("调用了父组件的show方法");
}
},
components: {
// com1: {
// template: "#com1"
// }
com1//这种写法会解析出一个com1属性,和com1对应的值
},
});
</script>
父向子传递方法:
对于父组件的方法,用事件绑定机制(v-on:func)自定义一个事件属性,子组件调用传递进来的方法。
<com1 :parentmsg="msg" @func="show"></com1>
执行到这一句时,先去实例的data里面找show属性,没找到,就去实例的methods里面找show方法,找到了后把show方法的引用传递给了子组件。注意,这里不能写成@func="show()"
,这样写的意思是执行show方法,再把结果传给子组件。
总的代码:
<div id="app">
<com1 :parentmsg="msg" @func="show"></com1>
</div>
<template id="com1">
<div class="container">
<p class="h1">这是子组件</p>
<p class="h1">{{ parentmsg }}</p>
<input type="button" name="" id="" value="子组件的按钮,触发父组件的show" @click="myshow">
</div>
</template>
.......
<script>
var com1 = {
template: "#com1",
props: ["parentmsg"], //把父组件传递过来的 parentMsg 属性在子组件的props属性中定义一下,才可以使用。
methods:{
myshow(){
//当点击子组件的按钮时,触发这个函数,这个函数如何拿到父组件传递过来的方法并调用它讷?
//emit英文意思:触发,调用
this.$emit("func");
}
}
}
new Vue({
el: "#app",
data: {
msg: "我是父组件中的数据"
},
methods: {
show(){
console.log("调用了父组件的show方法");
}
},
components: {
// com1: {
// template: "#com1"
// }
com1//这种写法会解析出一个com1属性,和com1对应的值
},
});
</script>
image.png
this.$emit() , props:[] ,事件绑定机制,属性绑定机制
子向父传值
在上面的传递函数的基础上,只需改变两个地方
//子组件的data
data: function(){
return {
sonmsg1: {name:"小头儿子",age:6},
}
}
......
this.$emit("func",this.sonmsg1, 456);
...
show(v1,v2){
console.log("调用了父组件的show方法");
console.log(v1);
console.log(v2);
}
子向父传值成功
父组件还可以把拿到的数据赋值到自己私有的data数据里。
ref:获取元素或组件,另一种父子组件传值的方式
ref是英文reference的缩写,是引用的意思
获取元素
<div id="app">
<p class="h3" ref="myH3">我是一个p标签</p>
</div>
<script>
var vm = new Vue({
el: "#app",
data:{},
methods: {},
});
</script>
查看vm实例,居然在$refs属性里面看到了我们的myH3
获取组件
<div id="app">
<p class="h3" ref="myH3">我是一个p标签</p>
<login ref="myLogin"></login>
</div>
......
<script>
var login = {
template: "<p>登陆组件</p>"
}
var vm = new Vue({
el: "#app",
data:{},
methods: {},
components: {
login
}
});
</script>
成功获取组件
我们知道,组件有自己的模板,自己的data和methods。它现在又有了refs属性,那我们可以通过refs属性访问组件的data和methods了!父子组件通信又多了一种方法。
<script>
var login = {
template: "<p>登陆组件</p>",
data: function() {
return {
msg:"子组件里的msg",
}
},
methods:{
fn1(){
console.log("子组件里的方法!");
}
}
}
var vm = new Vue({
el: "#app",
data:{},
methods: {},
components: {
login
},
mounted(){
this.$refs.myLogin.fn1();
console.log(this.$refs.myLogin.msg);
},
});
</script>
注意必须挂载完之后才能获取到
网友评论