一 组件化开发的概念。
所谓组件化,就是把页面拆分成多个组件,每个组件依赖的 CSS、JS、模板、图片等资源放在一起开发和维护。
因为组件是资源独立的,所以组件在系统内部可复用,组件和组件之间可以嵌套,如果项目比较复杂,可以极大简化代码量,并且对后期的需求变更和维护也更加友好。
二 全局组件和局部组件
组件的注册方式有两种,一种是全局组件一种是局部组件。
- 全局注册,通过
Vue.component
。 - 局部注册,通过
components:{cpnc:cpnc}
。
<div id="app">
<h2>全局组件</h2>
<my-cpn></my-cpn>
<h2>局部组件</h2>
<cpnc></cpnc>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 1.创建组件构造器对象
const cpnc = Vue.extend({
template:`
<div>
<h2>标题</h2>
<p>内容1</p>
<p>内容2</p>
</div>`
})
// 2.注册组件(全局组件,可以在多个vue实例中使用)
Vue.component('my-cpn', cpnc)
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpnc:cpnc
}
})
</script>
注册组件的语法糖:注册组件时候可以不实例化组件对象,直接在注册的时候实例化。{}
就是一个组件对象。
<div id="app">
<cpn1></cpn1>
<cpn2></cpn2>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 1.注册全局组件语法糖
Vue.component('cpn1', {
template:`
<div>
<h2>全局组件语法糖</h2>
<p>全局组件语法糖</p>
</div>`
})
const app = new Vue({
el:"#app",
components:{//局部组件创建
cpn2:{
template:`
<div>
<h2>局部组件语法糖</h2>
<p>局部组件语法糖</p>
</div>`
}
}
})
</script>
三 父组件向子组件传值,props
3.1
用
法
:
3.1 用法:
3.1用法:
1
在
父
组
件
的
d
a
t
a
中
定
义
值
1 在父组件的data中定义值
1在父组件的data中定义值
2
在
父
组
件
中
引
用
子
组
件
2 在父组件中引用子组件
2在父组件中引用子组件
3
父
组
件
的
值
绑
定
给
子
组
件
v
−
b
i
n
d
3 父组件的值绑定给子组件 v-bind
3父组件的值绑定给子组件v−bind
4
子
组
件
通
过
p
r
o
p
s
接
受
父
组
件
传
过
来
的
值
4 子组件通过props接受父组件传过来的值
4子组件通过props接受父组件传过来的值
3.2
详
解
:
3.2 详解:
3.2详解:
p
r
o
p
s
可
以
是
数
组
或
对
象
,
用
于
接
受
来
自
父
组
件
的
数
据
。
p
r
o
p
s
可
以
是
简
单
的
数
组
,
或
者
使
用
对
象
作
为
替
代
,
对
象
允
许
props 可以是数组或对象,用于接受来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许
props可以是数组或对象,用于接受来自父组件的数据。props可以是简单的数组,或者使用对象作为替代,对象允许
配
置
高
级
选
项
,
如
类
型
检
测
、
自
定
义
验
证
和
设
置
默
认
值
。
配置高级选项,如类型检测、自定义验证和设置默认值。
配置高级选项,如类型检测、自定义验证和设置默认值。
类 型 : A r r a y < s t r i n g > ∣ O b j e c t 类型:Array<string> | Object 类型:Array<string>∣Object
你 可 以 基 于 对 象 的 语 法 使 用 一 下 选 项 : 你可以基于对象的语法使用一下选项: 你可以基于对象的语法使用一下选项:
1 type:可以是下列原生构造函数中的一种:String、Number、Boolean、Array、Object、Data、Function、Symbol、任何自定义的构造函数、或上述内容组成的数组。会检查一个prop是否是给定的类型,否则抛出警告。
2 default:any
为该prop指定一个默认值。如果该prop没有传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
3 required:Boolean
定义该prop是否是必填项。是非生产环境中,如果这个值truthy且该prop没有被传入,则会被控制台抛出一个警告。
4 validator:Function
自定义验证函数会将该prop的值作为唯一的参数带入。在非生产环境下,如果该函数返回一个falsy的值(也就是验证失败),控制台将会抛出一个警告。
3.3
V
u
e
在
内
部
会
对
p
r
o
p
s
选
项
进
行
处
理
,
无
论
开
发
时
使
用
了
哪
种
语
法
,
V
u
e
都
会
将
其
规
范
化
为
对
象
的
形
式
。
3.3 Vue在内部会对 props 选项进行处理,无论开发时使用了哪种语法,Vue都会将其规范化为对象的形式。
3.3Vue在内部会对props选项进行处理,无论开发时使用了哪种语法,Vue都会将其规范化为对象的形式。
具体规范方式见Vue源码 src/core/util/options.js 文件中的 normalizeProps 函数.
3.4
V
u
e
父
子
组
件
传
值
中
‘
.
s
y
n
c
‘
的
理
解
3.4 Vue父子组件传值中 `.sync` 的理解
3.4Vue父子组件传值中‘.sync‘的理解
由于父组件向子组件传值是单向的,子组件不能修改父组件的值,修改了会报错,所以出现了 .sync
可以理解为以下操作:
1 子组件在props中声明,与父组件商量好的传值暗号xxx(用来作为传递值的别名)
2 子组件通过触发一个事件来告诉父组件:“我要改xxx对应的值!” (this.$emit('update: value', this.xxx=yyy))
3 父组件在引用子组件时,在其标签内通过:xxx='local_value' @update:value='local_value = $event'
来监听update:value事件,对更改请求作出响应
4 local_value
的值更改为yyy
update:value其实是一个字符串,只要父子组件对应即可,不强制要求,但官方建议写为’update:value’,大概是为了语义性吧~
而.sync
是用来简化第3步操作的::xxx.sync = 'local_value'
等价于:xxx='local_value' @update:value='local_value = $event'
这么一长串,最后实现的效果一样,.sync
就是这么一颗语法糖。
同时,可以用v-bind.sync = 'xxx’来一次传递多个值,这些值都封装在对象xxx内,xxx对象中的每一个属性都会作为一个独立的 prop 传给子组件,然后各自添加用于更新的 v-on 监听器。
props举例子
<div id="app">
<cpn :cMovies="movies" :cMessage="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="(item, index) in cmovies" :key="index">{{item}}</li>
</ul>
<h2>{{cmessage}}</h2>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
function Person(firstName,lastName) {
this.firstName = firstName
this.lastName = lastName
}
// 父传子:props
const cpn = {
template: "#cpn",
// props: ['cmovies', 'cmessage'],//数组写法
props: { //对象写法
// 1.类型限制(多个类使用数组)
// cmovies:Array,
// cmessage:String,
// cmessage:['String','Number'],
// 2.提供一些默认值,以及必传值
cmessage: {
type: String,
default: 'zzzzz',
required: true //在使用组件必传值
},
//类型是Object/Array,默认值必须是一个函数
cmovies: {
type: Array,
default () {
return [1, 2, 3, 4]
}
},
// 3.自定义验证函数
// vaildator: function (value) {
// //这个传递的值必须匹配下列字符串中的一个
// return ['zzzzz', 'ttttt', 'yyy'].indexOf(value) !== -1
// }
// 4.自定义类型
// cmessage:Person,
},
data() {
return {
}
},
methods: {
},
};
const app = new Vue({
el: "#app",
data: {
message: "你好",
movies: ["复仇者联盟", "钢铁侠", "星际穿越", "哪吒传奇"]
},
components: {
cpn
}
})
</script>
四 子组件向父组件传值
1 在子组件中定义一个方法btnClick(item)
,使用$emit
,'itemclick’是事件名,item
是传过去的值。
2 在子组件中监听点击事件并回调此方法.
3 在父组件中<cpn @itemclick="cpnClcik"></cpn>
定义一个方法cpnClcik(item) ,参数就是上面传来的item
<!-- 父组件 -->
<div id="app">
<!-- 不写参数默认传递btnClick的item -->
<cpn @itemclick="cpnClcik"></cpn>
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<button v-for="(item, index) in categoties" :key="index" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 父传子:props
const cpn = {
template: "#cpn",
data() {
return {
categoties: [{
id: 'aaa',
name: '热门推荐'
},
{
id: 'bbb',
name: '手机数码'
},
{
id: 'ccc',
name: '家用家电'
},
{
id: 'ddd',
name: '电脑办公'
},
]
}
},
methods: {
btnClick(item) {
this.$emit('itemclick', item)
}
},
};
const app = new Vue({
el: "#app",
data() {
return {
}
},
methods: {
cpnClcik(item) {
console.log('cpnClick'+item.name);
}
},
components: {
cpn
},
})
</script>
五 组件通信-父子通信案例(watch实现)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>组件通信-父子通信案例(watch实现)</title>
</head>
<body>
<!-- 父组件 -->
<div id="app">
<cpn :number1='num1' :number2='num2' @num1change="num1Change" @num2change="num2Change"></cpn>
<h2>父组件{{num1}}</h2>
<input type="text" v-model="num1" >
<h2>父组件{{num2}}</h2>
<input type="text" v-model="num2">
</div>
<!-- 子组件 -->
<template id="cpn">
<div>
<h2>{{number1}}</h2>
<input type="text" v-model="dnumber1">
<h2>{{number2}}</h2>
<input type="text" v-model="dnumber2">
</div>
</template>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
<script>
// 父传子:props
const cpn = {
template: "#cpn",
data() {
return {
dnumber1:this.number1,
dnumber2:this.number2
}
},
props:{
number1:[Number,String],
number2:[Number,String],
},
watch: {
dnumber1(newValue){
this.dnumber1 = newValue * 100
this.$emit('num1change',newValue)
},
dnumber2(newValue){
this.dnumber1 = newValue * 100
this.$emit('num2change',newValue)
}
},
};
const app = new Vue({
el: "#app",
data() {
return {
num1:1,
num2:2,
}
},
methods: {
num1Change(value){
this.num1=value
},
num2Change(value){
this.num1=value
}
},
components: {
cpn
},
})
</script>
</body>
</html>