组件化开发的思想:将复杂的问题拆分成很多小问题。若我们将页面中所有的逻辑处理全部放在一起,处理起来会非常复杂,且不利于后续的管理和扩展。若我们将一个页面拆分成一个个小的功能块,每个功能块完成属于自己这部分独立的功能,那么之后整个页面的管理和维护都会变得容易
组件的使用的三个步骤:(Vue 2.x开始实际上就不用,只是是基础)
组件必须挂载在某个Vue实例下,否则起不生效
// 注册组件(全局组件,意味着可以在多个Vue实例下使用)
Vue.component('cpn',cpnC); //全局组件
const app = new Vue({
el:'#app',
data: {
message:'你好'
},
components: {
//cpn使用组件时的标签名
cpn: cpnC //局部组件
}
})
// 创建第一个组件构造器(子组件)
const cpnC1 = Vue.extend({
template:`
xxxx
hhhh
`
});
// 创建第二个组件构造器(父组件)
const cpnC2 = Vue.extend({
template:`
xxxx
hhhh
`,
components: {
cpn1: cpnC1
}
});
// root组件
const app = new Vue({
el:"#app",
data: {
message: '你好啊'
},
components: {
cpn2: cpnC2
}
})
Vue.component('cpn1',{
template:`
xxxx
hhhh
`
});
注意:cpn1用单引号或双引号包裹的
const app = new Vue({
el:"#app",
data: {
message: '你好啊'
},
components: {
'cpn2': {
template:`
xxxx
hhhh
`
}
}
})
注意:cpn2用单引号或双引号包裹的
<script type="text/x-template" id="cpn">
<div>
<h2>xxxx</h2>
<p>hhhh</p>
</div>
script>
// 注册全局组件
<script>
Vue.component('cpn', {
template: '#cpn'
})
script>
<div id="app">
<my-cpn>my-cpn>
div>
<template id="myCpn">
<div>消息:{
{message}}div>
template>
<script>
const app = new Vue({
el:"#app",
components: {
'my-cpn': {
template: "#myCpn",
data() {
return {
message: 'hello world'
}
}
}
}
})
script>
背景:我们知道组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据或是Vue实例数据的。 但在开发中,往往会有一些数据需要从上层传递到下层的,如:在页面中,从服务器请求到了很多数据,其中一部分数据并非是整个大组件来展示的,而是需要下面的子组件来展示。这时并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)
父组件向子组件传递数据分为两种方式:动态和静态
子组件要显式地用 props
选项声明它期待获得的数据,静态 Props 通过为子组件在父组件中的占位符添加特性的方式来达到传值的目的
<div id="app">
<child-cpn :message="message">child-cpn>
div>
<template id="childCpn">
<div>消息:{
{message}}div>
template>
<script>
const app = new Vue({
el:"#app",
data: {
message: 'hello'
},
components: {
'child-cpn': {
template: "#childCpn",
props: ['message']
}
}
})
script>
props传递的过程:
通过:message="message"将data中的数据传给了props(双引号中的message才是绑定的数据变量)
在模板中,要动态地绑定父组件的数据到子模板的 props,与绑定到任何普通的HTML特性相类似,就是用 **v-bind
。**每当父组件的数据变化时,该变化也会传导给子组件
const cpn = {
template: '#cpn',
props:['cmovies','cmessage']
}
const cpn = {
template: '#cpn',
props: {
// 1.类型限制
// cmovies: Array,
// cmessage: String
//2.或提供一些默认值
cmessage: {
type: String,
default: 'hhhhh',
required: true //意味着必须传值,否则报错
}
// 类型是对象或数组时,默认值必须是一个函数
cmovies: {
type: Array,
default() {
return []
}
}
}
}
var parentNode = {
template: `
`,
components: {
'child': childNode
}
};
var childNode = {
template: '{
{myMessage}}', //子级模板使用从父级传来的变量:使用小驼峰写法
props:['myMessage'] // 子级props属性声明时,使用小驼峰或者中划线写法都可以
// 或者 props:['my-message']
}
对 props 进行类型等验证时,需要用对象写法
验证支持的数据类型有:
当有自定义的构造函数时,也支持自定义的类型
Vue.component('my-component', {
props: {
//基础的类型检查(‘null’匹配任何类型)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function() {
return {
message: 'hello'}
}
},
// 自定义验证函数
propF: {
validator: function(value) {
return ['success','warning','danger'].indexOf(value) !== -1;
}
}
}
})
方法:通过自定义事件来完成
自定义事件的流程:
<div id="app">
<cpn @itemClick="cpnClick">cpn> // 监听到子组件事件:itemClick后触发cpnClick事件
div>
<template id="childCpn">
<div>
<button v-for="item in cactegories"
@click="btnClick(item)">
{
{item.name}}
button>
div>
template>
<script>
// 子组件
const cpn = {
template: '#cpn',
data() {
return {
categories:[
{
id: 'aaa', name: '1'},
{
id: 'bbb', name: '2'},
{
id: 'ccc', name: '3'},
{
id: 'ddd', name: '4'}
]
}
},
methods: {
// 子组件的自定义事件
btnClick(item) {
// 发射事件
this.$emit('item-click',item)
}
}
}
// 父组件
const app = new Vue({
el:"#app",
data: {
message: 'hello'
},
components: {
cpn
},
methods: {
//父组件接收事件
cpnClick(item) {
console.log('cpnClick');
}
}
})
script>
案例:实现两个按钮+1和-1,点击后修改counter。操作过程在子组件中完成,展示交给父组件,就需要把子组件中的counter传给父组件的某个属性,如total
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1change="num1change"
@num2change="num2change">
cpn>
div>
<template id="cpn">
<div>
<h2>props:{
{number1}}h2>
<h2>data:{
{dnumber1}}h2>
<input type="text" v-model="dnumber1">
<h2>props:{
{number2}}h2>
<h2>data:{
{dnumber2}}h2>
<input type="text" v-model="dnumber2">
div>
template>
<script>
// 子组件
const cpn = {
template: '#cpn',
data() {
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
methods: {
// 子组件的自定义事件
num1Input(event) {
//将input中的value赋值到dnumber中
this.dnumber1 = event.target.value;
this.$emit('num1change',this.dnumber1)
this.dnumber2 = this.dnumber1 *100;
this.$emit('num2change',this.dnumber2)
}
num2Input(event) {
//将input中的value赋值到dnumber中
this.dnumber2 = event.target.value;
this.$emit('num2change',this.dnumber2)
this.dnumber2 = this.dnumber2 /100;
this.$emit('num1change',this.dnumber1)
}
}
}
// 或者用watch监听属性的变化
const cpn = {
template: '#cpn',
data() {
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
watch: {
dnumber1(newvalue) {
this.dnumber2 = newvalue * 100;
this.$emit('num1change',newvalue);
},
dnumber2(newvalue) {
this.dnumber1 = newvalue / 100;
this.$emit('num2change',newvalue);
}
}
}
// 父组件
const app = new Vue({
el:"#app",
data: {
num1:1,
num2:0
},
components: {
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number
}
}
},
methods: {
num1change(value) {
this.num1 = parseFloat(value)
},
num2change(value) {
this.num2 = parseFloat(value)
}
}
})
script>
注意:不要直接去绑定num1,num2 来改变值,改变数据时写一个值
this.$children 是一个数组类型,包含所有子组件对象
<template id="cpn">
<div>
<h2>hhhh2>
<button @click="btnClick">button>
div>
template>
<script>
const app = new Vue({
el:"#app",
data:{
message:'hhh'
}
components: {
cpn: {
template: '#cpn',
data() {
return {
name:'我是cpn组件的name'
}
},
components: {
ccpn: {
template: "#ccpn",
methods: {
btnClick() {
//1. 访问父组件$parent
console.log(this.$parent);
console.log(this.$parent.name);
//2. 访问根组件$root
console.log(this.$root);
console.log(this.$root.message);
}
}
}
}
}
}
})
script>