vuejs 组件化
什么是组件化开发?
组件化开发是近两年流行起来的一个开发模式,把日常项目开发中一些常用的代码块封装起来,封装成一个一个的功能块,这个功能块里面包含了 结构、样式、行为、数据(state)、生命周期等。然后在做项目开发时候,项目的那些页面就由这些小的组件然后进行构成,类似乐高积木。
网站:(网页—>组件—>封装代码块)
常见的可以复用的代码:
- 按钮
- 弹出框
- 模态框
- badge 徽章
- Icon
…
组件其实就是开发者自己封装好的 HTML标签。因为原生的html标签非常的有限。
为什么要引入组件化开发?
就是为了代码的复用,后期的维护方便。同时为了后期的扩展功能也是非常方便,同时后期别人接受也是很好接受。
vuejs如何进行组件化开发?
Vue.component()
vuejs 常见的第三方的组件库有哪些?
-
ElementUI (pc端项目) (饿了么前端团队)
- https://element.eleme.cn/#/zh-CN/component/drawer
-
MintUI (移动端项目) (饿了么前端团队)
- http://mint-ui.github.io/docs/#/
-
iview (个人)
- https://weapp.iviewui.com/components/switch
-
uview (一般配合 uniapp 做小程序和其他的移动端使用)
- https://www.uviewui.com/components/button.html
-
vantUI(移动端UI)(有赞团队)
- https://vant-contrib.gitee.io/vant/#/zh-CN/goods-action
…
组件
全局组件的基本用法
先学习自己自定义组件尝试一下.
vuejs 组件: 1. 全局组件 2. 局部组件 定义组件,必须写在实例化之前。
参数1(string): 组件的名称,
1.1 一般使用烤串法进行命名(abc-def-ghi)每个单词使用小写,中间使用中线串起来。
1.2 同时也可以使用小驼峰的方式,但是如果是以小驼峰的方式命名的话,则在调用的时候,要使用烤串法调用。 参数2(object):组件的描述对象(1. 结构 html代码 2. 样式 Css 3. 行为 js事件 4. data 5.
生命周期函数)
Vue.component('mycompoent1', {
/* tempalate 必须的,同时在写的过程中,这个模板文件有且仅有一个根节点*/
template: `
<div>
<h2>组件的HTML结构</h2>
<h2>组件的HTML结构</h2>
</div>
`
});
//html页面里之间引用定义好的组件名
<mycompoent1></mycompoent1>
烤串法和小驼峰命名
Vue.component('my-compoent2', {
template: `
<div>
<h2>组件的HTML结构</h2>
<h2>组件的HTML结构</h2>
</div>
`
});
/*小驼峰命名法*/
/* 调用:myCompoent3--->my-component3*/
Vue.component('myCompoent3', {
template: `
<div>
<h2>组件的HTML结构</h2>
<h2>组件的HTML结构</h2>
</div>
`
});
<my-compoent2></my-compoent2>
<hr>
<my-compoent3></my-compoent3>
总结: 在用户自定义组件的时候,组件的名称建议使用 烤串法命名,这样的定义的时候不出错,调用时候自然不会出错!
全局组件的样式和行为
Vue.component('mycompoent1', {
created() {
console.log('组件 mycomponent1产生了');
// 一般会发生网络请求
this.getCommentList();
},
/*属于组件自己的数据要使用一个 data函数来定义,该函数的返回值是一个对象,该对象里面的数据我们一般称之为 模型数据,模型数据里面的属性称之为模型变量,模型变量可以在template里面使用*/
data() {
return {
title: 'hi component!',
counter: 0,
result: [],
page: 1,
limit: 5,
}
},
/*组件也是可以存在 methods 的这个里面就写我们事件的回调函数*/
methods: {
async getCommentList() {
// axios 发生网络请求
let url = `http://localhost:3000/api/v1/comment/list?page=${this.page}&limit=${this.limit}`;
try {
// 请求完成
this.isLoading = false;
const res = await axios.get(url); // res.data 响应体
this.result = res.data.data.result;
console.log('this.result ', this.result);
} catch (e) {
this.result = [];
alert('获取信息失败!');
return;
}
},
clickHandler() {
this.counter++;
console.log('clickHandler');
}
},
template: `
<div style="color:red;">
<h2>我的第一个组件</h2>
<p>行为:事件</p>
<h3>{{ title }}</h3>
<p>counter: {{ counter }}</p>
<button @click="clickHandler()">点击我</button>
<hr>
<hr>
<ul v-for="ele in result">
<li>序号:{{ ele._id }}</li>
<li>用户名:{{ele.username}}</li>
<li>评论日期:{{ele.addTime}}</li>
<li>评论内容:{{ele.content}}</li>
</ul>
</div>
`
});
<mycompoent1></mycompoent1>
<mycompoent1></mycompoent1>
<mycompoent1></mycompoent1>
组件是可以高度复用的,想用多少次就用多少次
局部组件
要先定义
new Vue({
el: '#app',
data: {},
/*定义局部组件的位置*/
components: {
/*key:组件名称 烤串法; value:组件描述对象*/
'my-component2': myComponent1,
'my-component1': myComponent2,
}
});
将组件描述对象提取出来了,可以不提取,但是提取了会更精简一些。
const myComponent1 = {
//支持生命周期函数
created() {
console.log('created');
},
data() {
return {
counter: 0,
}
},
methods: {
clickHandler() {
this.counter++;
console.log('clickHandler');
}
},
template: `
<div style="color: blue;">
<h1>我是第二个局部组件!</h1>
<p>counter:{{ counter }}</p>
<button @click="clickHandler">点击我</button>
</div>
`
};
const myComponent2 = {
created() {
console.log('created');
},
data() {
return {
counter: 0,
}
},
methods: {
clickHandler() {
this.counter++;
console.log('clickHandler');
}
},
template: `
<div style="color: blue;">
<h1>我是第一个局部组件!</h1>
<p>counter:{{ counter }}</p>
<button @click="clickHandler">点击我</button>
</div>
`
};
组件调用:1. 双标签 2. 单标签 自闭合
建议:双标签调用;如果是一单一双,有一个出不来
<!--<my-component1/>-->
<my-component1></my-component1>
<hr>
<my-component2></my-component2>
动态组件
官方提供:
component 特点:该组件显示什么内容,取决于我们给该组件的is属性对应的属性是哪个组件,就显示哪个组件的内容
<component :is="my-component1"></component>
Vue.component('my-compoent1', {
template: `
<div>
<h2>组件的HTML结构</h2>
<h2>组件的HTML结构</h2>
</div>
`
});
一般可以去做一个选项卡
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
<style>
* {
margin: 0;
padding: 0;
}
.list {
width: 200px;
height: 50px;
display: flex;
}
.list li {
border: 1px solid #000;
width: 80px;
height: 50px;
line-height: 50px;
text-align: center;
list-style: none;
cursor: pointer;
}
.list li.active {
background-color: gold;
}
</style>
</head>
<body>
<div id="app">
<ul class="list">
<li :class="{'active': currentComponent == 'my-component1'}" @click="selectComponent('my-component1')">正在热映...
</li>
<li :class="{'active': currentComponent == 'my-component2'}" @click="selectComponent('my-component2')">即将上映...
</li>
</ul>
<!--keep-alive 也是官方提供的,可以直接使用,该组件的作用可以保存组件的状态,不会让组件重新的生成和销毁-->
<keep-alive>
<component :is="currentComponent"></component>
</keep-alive>
</div>
</body>
<script src="https://cdn.bootcss.com/jquery/2.0.1/jquery.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
Vue.component('my-component1', {
template: `
<div>
<h2>我是第一个组件!</h2>
<p>正在热映...</p>
<hr>
</div>
`
});
Vue.component('my-component2', {
template: `
<div>
<h2>我是第二个组件!</h2>
<p>即将上映...</p>
<hr>
</div>
`
});
new Vue({
el: '#app',
data: {
currentComponent: 'my-component1',
},
methods: {
selectComponent(item) {
console.log('item', item);
this.currentComponent = item;
}
}
});
</script>
</html>
组件间的通信问题
(vuejs面试必问)
什么是组件间的通信问题?
由于页面是由组件构成的,那么组件间存在关系。
常见的关系如下:
- 嵌套关系
- 父子嵌套(父组件,子组件)
- 爷孙嵌套
- 平级
- 兄弟组件
有了这种关系后,组件是需要进行数据的传递,这个时候就称之为 组件间的通信问题。
常见的通信:
- 父子通信(属性传递)
- 子父通信(回调函数)
- 兄弟通信(1. 公共的父组件 2. EventBus 事件车)
上面的这些通信方式全部可以使用一个叫做 vuex 方案进行解决。
父子通信(属性传递)
Vue.component('father-component', {
template: `
<div>
<h2>父组件</h2>
<p>父亲的钱:{{ money }}</p>
<hr>
//直接在父组件里用子组件
<son-component></son-component>
</div>
`
});
Vue.component('son-component', {
template: `
<div>
<h3>子组件</h3>
<hr>
</div>
`
});
<father-component></father-component>
如果父组件要往子组件传东西,父亲定义一个data放东西,子组件定义prpos拿东西
Vue.component('father-component', {
data() {
return {
money: 1000,
title: '我是父组件的内容!'
}
},
template: `
<div>
<h2>父组件</h2>
<p>父亲的钱:{{ money }}</p>
<hr>
<son-component :qian="money" :title="title"></son-component>
</div>
`
});
Vue.component('son-component', {
props: ['qian', 'title'],
template: `
<div>
<h3>子组件</h3>
<p>来自父亲的钱:{{ qian }}</p>
<hr>
<p>{{ title }}</p>
<hr>
</div>
`
});
子父通信(回调函数)
1.需要在父组件自定义一个事件(事件的名称用户自定义),然后在 methods 定义好自定义事件的回调函数
<son-component @giveMoney="callback" :qian="money" :title="title"></son-component>
定义方法
methods: {
callback(data) {
console.log('来自儿子的问候:', data); // data 儿子传递过来的信息(儿子:子组件)
}
},
- 在子组件里面触发父组件里面自定义的事件
<button @click="giveMeSameMoney">老爸,给我点生活费</button>
定义方法
methods: {
giveMeSameMoney() {
console.log('giveMeSameMoney');
/*第一个参数,父组件里面定义的自定义事件,第二个参数传递该事件回调函数的参数*/
/*第二个参数:一般叫做 payload 载荷*/
this.$emit('giveMoney', {money: 2000}); // this.$emit 是 vuejs 提供的
}
},
3.父亲接收
方法接收
this.message = data.money;
<p>来自儿子的问候:{{ message }}</p>
整个代码
Vue.component('father-component', {
data() {
return {
money: 1000,
title: '我是父组件的内容!',
message: '',
}
},
methods: {
callback(data) {
console.log('来自儿子的问候:', data); // data 儿子传递过来的信息(儿子:子组件)
this.message = data.money;
}
},
/* 子父通信:1.需要在父组件自定义一个事件(事件的名称用户自定义),然后在 methods 定义好自定义事件的回调函数*/
template: `
<div>
<h2>父组件</h2>
<p>父亲的钱:{{ money }}</p>
<p>来自儿子的问候:{{ message }}</p>
<hr>
<son-component @giveMoney="callback" :qian="money" :title="title"></son-component>
</div>
`
});
Vue.component('son-component', {
props: ['qian', 'title'],
methods: {
giveMeSameMoney() {
console.log('giveMeSameMoney');
/*第一个参数,父组件里面定义的自定义事件,第二个参数传递该事件回调函数的参数*/
/*第二个参数:一般叫做 payload 载荷*/
this.$emit('giveMoney', {money: 2000}); // this.$emit 是 vuejs 提供的
}
},
/*子父通信:2. 在子组件里面触发父组件里面自定义的事件*/
template: `
<div>
<h3>子组件</h3>
<p>来自父亲的钱:{{ qian }}</p>
<p>需求:儿子觉得父亲给的钱不够,那么希望父亲给多一点?(回调函数实现?)</p>
<button @click="giveMeSameMoney">老爸,给我点生活费</button>
<hr>
<p>{{ title }}</p>
<hr>
</div>
`
});
new Vue({
el: '#app',
data: {}
});
<div id="app">
<father-component></father-component>
</div>
兄弟通信
例子:熊大要发送一个信息给熊二,告知熊二,今天下午五点一起去揍光头强;熊二回复熊大,信息已收到,没有问题。
<xiong-da-component></xiong-da-component>
<hr>
<xiong-er-component></xiong-er-component>
Vue.component('xiong-da-component', {
template: `
<div>
<h2>熊大-组件</h2>
</div>
`
});
Vue.component('xiong-er-component', {template: `
<div>
<h3>熊二二-组件</h3>
</div>
`
});
公共的父组件 1. 熊大信息给父组件(回调函数)2. 父组件信息给熊二(属性传递)
EventBus 事件车 计算机网络里的三大总线 1. 地址总线 2. 控制总线 3. 寻址总线 2^64
- 事件车:1. 实例化一个事件车
const EventBus = new Vue();
- 通信:存在一个主动方(信息是谁发起的),被动方(信息的接受者)
- 事件车:2. 需要在被动方的 created 生命周期函数里面设置监听事件(用户自定义)
- 事件车:3. 需要在主动方的业务代码去触发第二步里面的监听事件
一开始熊二是被动方,在熊二组件的生命周期函数里用到事件车,通过.$on设置监听函数(函数用户自定义)
created() {
/*被动方:onClick*/
EventBus.$on('waitForMyBigBrotherGiveMeCall', (data) => {
console.log('来自大哥的电话:', data);
});
},
熊大作为主动方来触发这件事情,在熊大组件里button创建一个点击事件
<button @click="callMyLitterBrother">打电话告知熊二</button>
data() {
return {
message: '今天下午五点一起去揍光头强',
}
},
methods: {
/*主动方:触发监听事件*/
callMyLitterBrother() {
EventBus.$emit('waitForMyBigBrotherGiveMeCall', {message: this.message});
}
},
熊二接收到信息之后
data() {
return {
myMessage: '',
}
},
created() {
/*被动方:onClick*/
EventBus.$on('waitForMyBigBrotherGiveMeCall', (data) => {
console.log('来自大哥的电话:', data);
this.myMessage = data.message;
});
},
<p>来自大哥的电话信息:{{ myMessage }}</p>
熊二收到信息后要给熊大回信息,此时熊二为主动方,熊大为被动方。
此时熊大做事件监听:
created() {
/*被动方:熊大等待熊二传递信息*/
EventBus.$on('waitForMyLitterBrotherGiveMeCall', (data) => {
console.log('来自小弟熊二的问候', data);
})
},
熊二回消息:
<button @click="giveMeMyBigBrotherCall">回个电话给熊大</button>
data() {
return {
myMessage: '',
message: '信息已收到,没有问题!!!',
}
},
methods: {
giveMeMyBigBrotherCall(){
/*熊二 主动方*/
EventBus.$emit('waitForMyLitterBrotherGiveMeCall', {message: this.message});
}
},
熊大接收到信息之后拿出来:
data() {
return {
message: '今天下午五点一起去揍光头强',
myMessage: '',
}
},
created() {
/*被动方:熊大等待熊二传递信息*/
EventBus.$on('waitForMyLitterBrotherGiveMeCall', (data) => {
console.log('来自小弟熊二的问候', data);
this.myMessage = data.message;
})
},
<p>来自熊二的回复:{{ myMessage }}</p>
整个代码:
const EventBus = new Vue();
Vue.component('xiong-da-component', {
created() {
/*被动方:熊大等待熊二传递信息*/
EventBus.$on('waitForMyLitterBrotherGiveMeCall', (data) => {
console.log('来自小弟熊二的问候', data);
this.myMessage = data.message;
})
},
data() {
return {
message: '今天下午五点一起去揍光头强',
myMessage: '',
}
},
methods: {
/*主动方:触发*/
callMyLitterBrother() {
EventBus.$emit('waitForMyBigBrotherGiveMeCall', {message: this.message});
}
},
template: `
<div>
<h2>熊大-组件</h2>
<button @click="callMyLitterBrother">打电话告知熊二</button>
<p>来自熊二的回复:{{ myMessage }}</p>
</div>
`
});
Vue.component('xiong-er-component', {
data() {
return {
myMessage: '',
message: '信息已收到,没有问题!!!',
}
},
created() {
/*被动方:onClick*/
EventBus.$on('waitForMyBigBrotherGiveMeCall', (data) => {
console.log('来自大哥的电话:', data);
this.myMessage = data.message;
});
},
methods: {
giveMeMyBigBrotherCall(){
/*熊二 主动方*/
EventBus.$emit('waitForMyLitterBrotherGiveMeCall', {message: this.message});
}
},
template: `
<div>
<h3>熊二二-组件</h3>
<p>来自大哥的电话信息:{{ myMessage }}</p>
<button @click="giveMeMyBigBrotherCall">回个电话给熊大</button>
</div>
`
});
new Vue({
el: '#app',
data: {}
});
<xiong-da-component></xiong-da-component>
<hr>
<xiong-er-component></xiong-er-component>