VUE学习笔记-基础语法及其应用-2-深入组件开发
了解组件,什么是组件概念?
从图中我们可以看到,总共的大区域分为了三个板块
上面一个Header,下面左边一个LeftSide以及右边的RightSide
根据每个区域,又可以分为两个(最下层的绿色中间)、以及三个(最下层的绿色右边)
组件定义:
所谓组件,我们就可以看成是:一个页面中的某一个Section,通过不同的组件共同拼凑
从而,形成一个完整的组件,这也是目前前端最流行的开发方式:
vue_component.html
1 |
|
我们可以看到,现在我们将一个新的VUE实例挂载到页面ID为app的区域
定义全局组件并使用
现在我们的app可以注册全局的组件,注册好后可以直接在根组件上进行使用
比如我们要显示两个信息(紧接着上方的基本代码)
这是我的网站
https://www.vincent990413.gitee.io/myblog
1
2
3
4
5
6
7
8
9
10//第一个全局组件
app.component('my-component-describe',{
template:` <h1> 这是我的网站 </h1> `
})
//第二个全局组件
app.component('my-component-website',{
template:` <h1> https://www.vincent990413.gitee.io/myblog </h1>`
})
以上就完成了对组件的定义,接下来我们要做的就是使用这些组件
1 | //在创建实例的同时 使用自己的组件 |
可以将这些步骤想象成 “初始化数据” 初始化数据之后 -> 最后一步才是挂载在网页中
全局组件的可复用性
什么叫可复用性,就是可重用性,我们将一个组件封装好之后,
可以在网页中的各个区域去重复引用这个组件
全局组件的弊端?!
全局组件是有弊端的,因为你处处都可以使用,但是性能不高,应用打开就开始复杂的初始化
局部组件
针对与局部组件:我现在不注册,我要用的时候才注册这个组件,才进行使用
而全局组件是,我不用之前一直都被注册了,一直都存在在那里!
我们可以把局部组件,想象成,一个变量!我定义这个变量,我用的时候再注册它。
创建局部组件
1 | const myComponentCounter = { |
通过这种定义“变量”的方式,我们就已经定义好一个组件了,接下来我们需要在vue.CreateApp()方法里去注册
直接用component: { myComponentCounter }, 声明即可
1 | const app = Vue.CreateApp({ |
我们的局部组件一般使用驼峰命名法
但多个单词,我们应该遵守规范,
1. const变量用驼峰myComponentCounter
2. 起别名时 单引号且中间加- 小写每个字母: my-component-counter
(具体见上方代码)
父组件与子组件以及他们之间的静态、动态传值
什么是父组件,什么是子组件?
我们在父组件中,定义子组件并使用子组件
被注册,被使用的那个组件叫做:子组件
使用 “定义好的组件” 的组件叫做父组件
定义全局组件Son 并在父组件中使用
1 | app.component('Son',{ |
父组件的参数传给子组件(静态传值)
所谓父组件向子组件传值,就是在调用子组件的时候,静态绑定props参数
1 | //父组件传入name参数 那么子组件就要有一个name处于prop数组中 |
这种方法是静态传输,一旦运行起来,就无法改变了
父组件的参数传给子组件(动态传值)
一般我们利用:将父组件中data中的参数与传入子组件的参数进行绑定
从而使得传入的值,是动态的,是可变的
1 | const app = Vue.CreateApp({ |
这样通过修改data中的参数 完成动态绑定
父组件的参数传给子组件(传入函数参数)
1 | //父组件 |
父组件的参数传给子组件(Slot插槽)
这种情况适用于,父组件传入给子组件一段HTML代码,欲在部分区域显示:
在子组件想要显示的区域,声明插槽
在父组件使用子组件的双标签形式,并插入HTML代码/其他子组件
<Son> <div> {{counter}}: <my-component-others /></div> </Son>
1 | const app = Vue.createApp({ |
值得注意的是,当父组件与子组件具有同名的变量时,我们父组件传入参数时,子组件的slot中会显示父组件的属性。
父模板里调用的数据属性,使用的都是父模板里的数据。
子模板里调用的数据属性,使用的都是子模板里的数据。
{{counter}}
是在父模板中调用的,那么使用的是父模板的数据。
slot中的默认数据
当slot中无数据传递过来是,写在slot中的数据就是默认显示数据。
1 | <slot> 这是默认数据 </slot> |
指定若干个插槽的位置(指定不同的name)
1 | app.component('Son',{ |
我们可以看到,指定了name显示区域,在父组件传值的时候,也指定显示在对应name的区域即可。
1 | const app = Vue.createApp({ |
在代码中,我们可以看到,利用 v-slot:XX
可以指定显示在对应子组件name为XX的区域
也可以简写成 #XX
子组件
子组件向父组件传递数据
子组件调用父组件事件
子组件向父组件事件中传递参数
子组件传递参数时,如何通过
emits
进行校验
根据组件之间的单向数据流,子组件是没办法直接更改父组件传递过来的参数
解决办法
1. 将父组件传递过来的参数赋给子组件的数据 在子组件中更改自己的数据(本段稍后一点内容)
2. 我们可以调用父组件的 “可以改变这个参数值” 的方法,本质上是父组件来改变(紧接着读下去)
比如:我们用父组件更改传递给子组件的counter变量(用一个方法)
在子组件中调用这个方法
仔细看懂,接下来的代码!
仔细看懂,接下来的代码!
仔细看懂,接下来的代码!
1 | //父组件 |
关键代码:
1. 首先在父组件中定义传递过去的变量counter data(){return{ counter:1 }}
2. 父组件传递过去 <Son :counter="counter" />
3. 子组件中声明props用来接收 props:[ 'counter' ]
4. 父组件中写可以更改counter变量的方法 methods:{parentUpdateCounter(){this.counter++}}
5. 父组件传递过去 <Son :counter="counter" @parentUpdateCounter="parentUpdateCounter"/>
(@引用名=“父组件方法名”)
6. 子组件中声明emits用来接收 emits:['parentUpdateCounter']
(与父组件传递过来的引用名一致)
7. 子组件在需要的时候(一般在子组件自己的方法内部),调用这个父组件的引用名(代表对应的父组件方法)this.$emit('parentUpdateCounter')
子组件利用slot向父组件传递List中的数据
在Javascript中的list中,我们视为对象,因此调用属性是需要 obj.item
- 为子组件中添加list数据
1
2
3
4
5
6
7
8
9
10
11
12app.component('Son',{
data(){
return{
names: ['Vincent', 'Beatrix', 'James']
}
},
template:`
<div>
<slot v-for="name in names" :item="name"> </slot>
</div>
`
})
- 最重要的就是我们为这个插槽,绑定了item属性,这样父组件可以调用到这个属性。
- 父组件调用这个子组件,并得到绑定的item值
1
2
3
4
5
6
7
8
9
10
11
12const app=Vue.createApp({
data(){
return{
}
},
template:`
<Son v-slot="props">
<div> {{ props.item }} </div>
</Son>
`
})
- props可以任意取名字,但是一定是要绑定
item
这个属性
校验器(引入 ElementUI)
组件之间的单向数据流
一句话说明,就是数据从父级组件传递给子组件,只能这样单向(从父到子)绑定
子组件内部不能直接修改从父组件传递过来的数据
比如我们在父组件中定义一个计数器counter,传给子组件后,在子组件中更改这个数据
结论是,我们没办法看到这个数据的变化。
既然我们没办法,直接改变父组件传过来的变量,不如我们只更改子组件data中的变量
但将这个子组件data中的变量 赋值为父组件传过来的变量
1 |
|
这样我们就可以看到数据的变化了!
- 思考,为什么要有单向数据流的概念呢?
- 因为,如果父组件使用多个相同的子组件,传入不同的参数,我们希望这些子组件互不干扰。
也就是说,子组件不能直接更改父组件的数据,而是更改自己的数据。
异步组件和Promise讲解
异步的概念
所谓异步,就是几个操作不是按严格的顺序执行(真正的执行顺序未知)
与它相反的概念是同步:
- 举个例子,如何将大象塞到冰箱里?
打开冰箱
将大象塞进去
关掉冰箱门
以上的操作就是同步的,具有严格的前后时序。
一般而言,我们将经常要去后台请求数据的业务/组件,封装成异步的。
Promise
1 | app.component('async-component',Vue.defineAsyncComponent(()=>{ |
我们在调用这个组件时,三秒钟后,会出现对应组件中的内容
- 关于Promise的概念,详情点我去JavaSCript基础教程
爷孙级别组件数据传递(多用于多级组件)
顺序依次继承数据
如果有一个爷爷,他现在有一套房子(北京四合院),想过继下去。
我们就可以实现:
- 从爷爷过继给父亲,再从父亲过继给孙子 props参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29const app=Vue.createApp({
data(){
return{
house: '北京四合院,价值8000万!'
}
},
template:`
<h1> 这是爷爷,爷爷过继给儿子! </h1>
<Son :house="house" />
`
})
app.component('Son',{
props:['house'],
template:`
<h2>儿子接受房子,这个房子是: {{ house }} <h2>
<h2>儿子继续过继给孙子</h2>
<Grandson :house="house" />
`
})
app.component('Grandson',{
props:['house'],
template:`
<h3>这是孙子,接受房子,房子信息是: {{house}} </h3>
`
})
const vm=app.mount("#app")
这样通过依次继承的方法,的确可以做到将“爷爷”级别的组件数据传递给“孙子”组件
但如果项目中出现5,6次继承层次的孙孙孙…这样代码就太冗余了
指定组件层次接受参数 Provide&Inject
Provide意为提供参数,Inject引入接受、注入参数
也就是说,我们在“祖先”级别的组件提供某个参数,可以跳过中间的若干个组件,直接过继给指定的组件
1 | const app=Vue.createApp({ |
关键代码:
在爷爷组件中,提供属性
provide:{ newHounse:'新房子' }
在任意后代组件中,提供注入参数:
inject:['newHouse']
即可引用
如果这种层级关系达到5-6级,再使用props进行传递,那就会非常麻烦,而且会有大量的代码冗余。
使用provide和inject可以解决这个问题。