组件:把页面中相同的功能封装成一个组件,以后再有这样的功能,使用这个组件就会有对应的功能,用组件的思想开发可以大幅度提升开发效率,提高代码的可复用度和可维护性
在 Vue 中,创建组件有两种
全局组件:创建之后,所有的 Vue 实例都可以使用这个组件
局部组件:只能在注册的实例中使用局部组件
一、全局组件
创建全局组件:Vue.component(componentName, config);
- componentName 组件名字,可以是驼峰命名,也可以连字符;
- config 是一个对象,和创建 Vue 实例的配置对象一样;
- 在 html 或者模板中使用组件时,只能写连字符 component-name 的形式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--使用组件:把驼峰命名法变成连字符形式;使用组件就需要写一个自定义标签;-->
<handsome-man></handsome-man>
</div>
<div id="app2">
<handsome-man></handsome-man>
<handsome-man />
</div>
<script src="vue.js"></script>
<script>
// 创建一个全局组件
Vue.component('handsomeMan', {
template: `<div @click="fn">{{msg}}</div>`,
data() { // 组件化 data 需要写一个函数,在函数中 return 一个对象,把数据写在 return 后面的对象中
return {
msg: '小飞'
}
},
methods: {
fn() {
console.log('Muscle');
}
}
});
// 使用全局组件
let vm1 = new Vue({
el: '#app',
data: {}
});
let vm2 = new Vue({
el: '#app2',
data: {}
});
// 每个组件都是一个 Vue 的实例,创建 Vue 实例时使用的属性,创建组件时,这些属性都可以有;template data filter computed methods watch 生命周期的钩子函数 组件都可以有;
</script>
</body>
</html>
二、局部组件
局部组件使用步骤
- 创建组件
- 注册组件
- 使用组件
```html
<!DOCTYPE html>
<a name="wv8pA"></a>
## 三、组件嵌套
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="vue.js"></script>
<script type="module">
import son from './son.js';
let vm = new Vue({
template: `<div>{{gen}} <son></son></div>`,
data: {
gen: 'Parent'
},
components: {
son
}
});
vm.$mount('#app');
</script>
</body>
</html>
四、组件间的通信
1. 父传子
使用 props
- 在使用子组件时,在子组件标签行内动态绑定一个属性,如 :outermsg, 属性右侧引号中是一个变量,这个变量就绑定了父组件中的数据;写在子组件行内的属性,如 outermsg,叫做一个 props;
- 在子组件的 props 属性中注册当前的 props;例如,props: [‘outermsg’]
- 使用 props,vm 最终会代理所有的 props,所以和使用 data 中的数据相同的方式来使用 props;但是不能修改!
- 使用 props 动态绑定父组件的数据,当父组件的数据发生变化时,子组件收到数据自动跟着变化;
父传子用 props;
Vue 父子组件通信是单向数据流,数据只能是父组件传给子组件,子组件不能直接修改父组件的数据;
单向数据流,父组件的数据发生变化时,子组件收到的数据自动发生变化;
当子组件想修改父组件的数据时,子组件要通知父组件,让父组件去修改数据,父组件修改后,子组件就会自动获得到最新的数据;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--<div> 写在根 DOM 元素下面的东西就是模板,和 template 属性一样的作用
<input type="text" v-model="pmsg">
<son :outermsg="pmsg" double="pmsg"></son>
</div>-->
</div>
<script src="vue.js"></script>
<script>
let son = {
template: `<div>{{outermsg}} | {{double}}<button @click="fn">修改</button></div>`,
data() {
return {
msg: 'hello'
}
},
props: ['outermsg', 'double'],
methods: {
fn() {
// console.log(this.outermsg);
// this.outermsg = '呵呵'; // 报错
}
}
};
let vm = new Vue({
template: `<div><input type="text" v-model="pmsg"><son :outermsg="pmsg" double="pmsg"></son></div>`,
el: '#app',
data: {
pmsg: 'msg from parent'
},
components: {
son
}
});
</script>
</body>
</html>
2. 子传父
当子组件要修改父组件数据时,要通知父组件,让父组件修改数据;
子传父:
- 父组件在引用子组件的地方,要监听一个自定义事件(事件名可以用连字符,但是尽量不用驼峰命名法),并且给这个事件绑定一个事件函数,这个函数有一个形参,用来接收子组件传递过来的数据;
- 子组件中,当子组件想要修改来自父组件的数据时,用 this.$emit() 触发父组件监听的自定义事件,同时传入数据;
当自定义事件被子组件触发时,会执行事件函数,我们把修改数据的逻辑写在这个函数就可以了;
子传父通过事件,父组件监听事件,子组件触发事件;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="vue.js"></script>
<script>
let son = {
template: `<div>{{outermsg}} | {{double}}<button @click="fn">修改</button></div>`,
data() {
return {
msg: 'hello'
}
},
props: ['outermsg', 'double'],
methods: {
fn() {
// 在这里通知父组件修改数据,即在这里触发事件
this.$emit('change-msg', '呵呵哒'); // this.$emit(事件名, 传递给父组件的数据)
}
}
};
let vm = new Vue({
template: `<div><input type="text" v-model="pmsg">
<son :outermsg="pmsg" double="pmsg" @change-msg="changeMsg"></son></div>`,
el: '#app',
data: {
pmsg: 'msg from parent'
},
components: {
son
},
methods: {
changeMsg(val) {
// val 子组件触发事件时传入的数据
this.pmsg = val;
// console.log(val);
}
}
});
</script>
</body>
</html>