一、 组件
html: 组件就是一段可以被复用的结构代码
css: 组件就是一段可以被复用的样式代码
js: 组件就是一段可以被复用的功能代码
vue: 组件是一个包含独立的结构,样式和脚本的代码。我们可以复用。
使用组件分成三步
第一步 在模板中使用组件
组件名称字母小写,横线分割单词。注意:首字母不区分大小写。
第二步 在脚本中,定义组件类:Vue.extend({})
参数对象跟new Vue时候传递的参数对象是一样的 。
例如:可以传递data, compted, methods, watch 等属性。
这些属性的功能都是一样的 ,但是有些属性的写法是特殊的。
data属性:
是一个函数,返回值是绑定的数据。
this指向组件实例(由于该方法执行完毕,才能绑定数据,因此当前的this无法访问模型数据)
template 定义模板的,有两种用法
第一种:属性值是模板字符串(将组件的模板写在脚本中了)
第二种:属性值是css选择器,此时会将页面中对应元素的内容作为组件的模板。
html定义模板有两种方式:
1 通过script模板标签定义
2 通过template标签定义(vue建议)
注意:模板中最外层有且只有一个根元素。
第三步 注册组件,有两种方式
1 全局注册
Vue.component(name, Comp):全局注册的组件可以在任何组件中使用。
2 局部注册
components: { name: Comp }:局部注册的组件只能在当前组件中使用
name表示组件名称,要使用驼峰式命名(首字母不区分大小写)
Comp表示组件类
注意:组件是完成独立的,彼此之间数据,方法等不会共享。
父子组件
由于组件类继承了Vue类,因此组件实例化对象可以看成是vue实例化对象,反之vue实例化对象也可以看成是组件实例化对象
在vue实例中使用组件,我们就可以将
vue实例看成是父组件
将自定义的组件看成是子组件。
00 组件的实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="msg">
<p @click="taggle">{{msg}}---{{dealMsg}}</p>
<hr>
<!-- 第一组件第一步 -->
<home></home>
<hr>
<!-- <Home></Home> -->
</div>
<!-- 模板视图渲染的 -->
<!-- 第一种方式 -->
<!-- 这种方式 不可行 会导致 元素渲染两遍 -->
<!-- <div id="tpl">
<h2>第一种模板定义的方式</h2>
</div> -->
<!-- 第二种方式 -->
<!-- 缺点:语义化不好 -->
<!-- <script type="text/template" id="tpl">
<h2>第二种模板定义的方式</h2>
</script> -->
<!-- 第三种方式 推荐使用 -->
<!-- 这种方式需要注意 最外层有且只有一个根元素 -->
<template id="tpl">
<div>
<h2>第三种模板的定义方式</h2>
<h3>第二条数据</h3>
<!-- 在子组件中不能访问 父组件的 msg 和 dealMsg 需要在自己的数据的定义 -->
<h4 @click="childTaggle">子组件自定义的数据 ---{{msg}} ---- {{dealMsg}}</h4>
</div>
</template>
<script src="./dist/00.js"></script>
</body>
</html>
00.js
import Vue from 'vue';
// 定义组件第二部
let Home = Vue.extend({
// 定义模板
// template: '<h1>Home</h1>'
// 模板渲染视图有三种方式 建议使用第三种
template:'#tpl',
// 定义自己的数据
data(){
return{
msg:"child hello"
}
},
// 定义计算属性数据
computed:{
dealMsg(){
return this.msg.toUpperCase()
}
},
// 定义自己的方法
methods:{
childTaggle(){
console.log(arguments,"子组件的方法");
}
}
})
// 第三部注册组件
// 全局注册组件
Vue.component('home', Home)
let app = new Vue({
el: "#app",
data: {
msg: "hello"
},
// 局部注册组件
// components:{Home,Home},
components:{Home},
computed: {
dealMsg() {
return this.msg.toUpperCase()
}
},
methods: {
taggle() {
console.log(arguments,"父组件的方法");
}
}
})
效果图
二、动态组件
我们目前使用的组件都是静态的:一个元素对应一个组件类
想使用动态组件(一个元素可以对应多个组件类),可以通过component组件实现
通过is属性定义渲染哪个组件类,
默认是属性值是字符串
我们可以通过v-bind指令动态绑定
01 动态组件.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="msg">
<h1 @click="toggle">{{msg}} --- {{dealMsg}}</h1>
<hr>
<!-- 引入组件的方式 -->
<!-- <Home></Home>
<List></List> -->
<!-- 组件的显示方式 -->
<!-- <component is="home"></component>
<component is="list"></component> -->
<!-- 通过数据的显示方式 -->
<!-- <component :is="page"></component> -->
<!-- 通过按钮的方式显示组件 -->
<button @click="page = 'home'">首页</button>
<button @click="page = 'list'">列表页</button>
<component :is="page"></component>
</div>
<!-- 定义第一个子组件 Home -->
<template id="oneHome">
<div>
<h2 @click="oneToggle">Home 组件 ---{{msg}} --- {{oneChild}}</h2>
</div>
</template>
<!-- 定义第二个子组件 List -->
<template id="towList">
<div>
<h2>List 组件 --- {{msg}} --- {{TowMsg}}</h2>
</div>
</template>
<script src="./dist/01.js"></script>
</body>
</html>
01.js
import Vue from 'vue';
// 定义第一个子组件
let Home = Vue.extend({
template:'#oneHome',
data(){
return{
msg:'one hello'
}
},
computed:{
oneChild(){
return this.msg.toUpperCase()
}
},
methods:{
oneToggle(){
console.log("子组件",this);
}
}
})
// 定义第二个组件
let List = Vue.extend({
template:'#towList',
data(){
return{
msg:"towHello"
}
},
computed:{
TowMsg(){
return this.msg.toUpperCase();
}
}
})
let app = new Vue({
el:"#app",
data:{
msg:"hello",
page:'home'
},
// 注册组件
components:{
'home':Home,
'list':List
},
computed:{
dealMsg(){
return this.msg.toUpperCase()
}
},
methods:{
toggle(){
console.log("父组件",this);
}
}
})
效果图
三、 组件的生命周期
对于一个组件来说,存在三个过程:创建,存在和销毁。因此vue为组件定义生命周期来说明这三个过程。
创建期:
当我们创建组件的时候(如在页面中使用),组件将执行创建期的方法,
beforeCreate 组件创建前,此时数据,事件等还没有初始化
created 组件创建后,此时组件已经绑定了数据,事件等;
beforeMount 组件构建前,此时确定了组件的模板,以及渲染的容器元素等
mounted 组件构建后,此时组件上树了
一个组件在一生中只能创建一次,因此创建期的方法只能执行一次。
一旦组件的模型数据发生改变,组件将会执行存在期的方法
存在期:
当组件创建完成,将进入存在期,共分两个阶段:
beforeUpdate 组件更新前,此时数据改变了,视图尚未更新
updated 组件更新后,此时数据和视图都更新了
在一个组件中,我们可以不停的改变模型数据,因此存在期的方法会不停的执行
存在期的方法执行完毕,只是说一次更新的结束,存在期仍然继续。
销毁期:
当组件从页面中删除(执行了$destroy方法),组件将进入销毁期,执行销毁期的方法,共分两个阶段:
beforeDestroy 组件销毁期,此时组件中的数据等还有监听器。
destroyed 组件销毁后,此时组件的数据所具有的监听器以及子组件等被销毁了。
一旦组件被销毁,我们就再也无法使用组件了,想使用只能创建新的组件了。
以上周期方法this都指向组件实例,没有参数,
02 组件的生命周期
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<hr>
<button @click="page = 'home'">首页</button>
<button @click="page = 'list'">列表页</button>
<component :is="page"></component>
</div>
<!-- 首页 -->
<template id="home">
<div>
<h2>Home 页展示</h2>
<input type="text" v-model="msg">
</div>
</template>
<!-- 列表页 -->
<template id="list">
<div>
<h2>List 页的展示</h2>
<input type="text" v-model="msg">
</div>
</template>
<script src="./dist/02.js"></script>
</body>
</html>
02 .js
import Vue from 'vue';
// 定义 Home 子组件
let Home = Vue.extend({
template:'#home',
data(){
return{
msg:'home hello'
}
},
// 组件的生命周期
// 创建期
// 创建前
beforeCreate(){
console.log('111','beforeCreate',this.msg,this,arguments);
},
// 创建后
created() {
console.log('222','create',this.msg,this,arguments);
},
// 构建期
// 构建前
beforeMount(){
console.log('333','beforeMount',this,arguments);
},
// 构建后
mounted() {
console.log('444','mounted',this,arguments);
},
// 存在期
// 更新前
beforeUpdate(){
console.log('555','beforeUpdate',this,arguments);
},
// 更新后
updated(){
console.log('666','updated',this,arguments);
},
// 销毁期
// 销毁前
beforeDestroy() {
console.log('777','beforeDestroy',this,arguments);
},
// 销毁后
destroyed(){
console.log('888','directives',this,arguments);
}
})
// 定义 List 子组件
let List = Vue.extend({
template:'#list',
data(){
return{
msg:"list hello"
}
}
})
let app = new Vue({
el:"#app",
data:{
msg:"父组件",
page:"home"
},
// 注册组件
components:{
'home':Home,
'list':List
}
})
效果图
四、keep-alive
当组件从页面中删除,组件将执行销毁期的方法,如果从页面中移除的时候,不想将组件销毁,可以使用keep-alive组件。
该组件会将从页面中移除的组件暂存到内存中,没有被销毁,再次显示的时候,直接从内存中读取。
由于组件没有被销毁,因此不会执行销毁期的方法。为了表示显示和隐藏的过程,keep-alive组件为内部的组件拓展了两个方法:
activated 组件被激活,可以在页面中看到
deactivated 组件被禁用,从也页面中移除了
注意:由于组件被缓存到内存中,因此没有被销毁,保留最后一个状态,因此当组件再次显示的时候,会显示被保留的最后一个状态
03 keep-alive.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<hr>
<button @click="page = 'home'">首页</button>
<button @click="page = 'list'">列表页</button>
<!-- keep-alive 实现数据的缓存 -->
<keep-alive>
<component :is="page"></component>
</keep-alive>
</div>
<!-- 首页 -->
<template id="home">
<div>
<h2>Home 页展示</h2>
<input type="text" v-model="msg">
</div>
</template>
<!-- 列表页 -->
<template id="list">
<div>
<h2>List 页的展示</h2>
</div>
</template>
<script src="./dist/03.js"></script>
</body>
</html>
03 keep-alive.js
import Vue from 'vue';
// 定义 Home 子组件
let Home = Vue.extend({
template: '#home',
data() {
return {
msg: 'home hello'
}
},
// 组件被激活
activated() {
console.log("1111", this.msg, this, arguments);
},
// 组件被禁用
deactivated() {
console.log("2222", this.msg, this, arguments);
}
})
// 定义 List 子组件
let List = Vue.extend({
template: '#list'
})
let app = new Vue({
el: "#app",
data: {
msg: "父组件",
page: "home"
},
// 注册组件
components: {
'home': Home,
'list': List
}
})
效果图
五、组件通信
组件是一个完整独立的个体,彼此之间的数据,方法等不会共享,想在组件之间共享数据,我们可以使用组件通信的技术。
通常有三个方向:
父组件向子组件通信
子组件向父组件通信
兄弟组件间通信。
5.1 父组件向子组件通信
父组件向子组件通信就是将父组件的数据,方法传递给子组件。
需要两步
第一步 在父组件模板中,为子组件元素传递数据。
属性值默认是字符串,想传递变量或者方法,要使用v-bind指令动态传递。
命名规范:字母小写,横线分割单词
第二步 在子组件中,接收数据或者方法。
在props属性中接收,有两种接收方式
第一种 属性值是数组,每一个成员代表一个接收的属性名称。
第二种 属性值是对象
key 表示接收的属性名称
value 有三种方式
可以是类型的构造函数:Number, String等
可以是数组,每一个成员代表一种类型(定义多种类型)
可以是对象:
type:表示类型的构造函数,
required:是否是必须的,
default:默认值,属性值可以是数据,属性值还可以是方法,返回值就是默认的数据。
validator:校验方法,在方法中,校验数据是否合法。
注意:接收属性的时候,属性命名规范:驼峰式命名。
props中接收的数据与模型中的数据一样,都添加给实例化对象自身,并设置了特性。
因此我们既可以在JS中使用,也可以在模板中使用
04 父组件向子组件通信.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<input type="text" v-model="msg">
<hr>
<home data="dazhaxie" num="100" :data-msg="msg"></home>
<hr>
<home data="dazhaxie" num="100"></home>
</div>
<script src="./dist/04.js"></script>
</body>
</html>
04.js
import Vue from 'vue';
// 定义子组件
let Home = Vue.extend({
// 接收数据
// 接收的数据,添加给实例化对象自身并设置特性,可以在模板中直接使用
// props:['data','num','dataMsg'],
// 添加接收数据的约束
props:{
data:String,
// num:Number, // 如果不是 Number 类型会报错
num:[Number,String], // 可以是多种类型的
// dataMsg:String,
// 更多的限制
dataMsg:{
type:String,
// 当组件第二条没有传递 dataMag 数据时的限制
// 设置为必须有数据的
// required:true,
// 如果没有传递 给默认值
// default:'abc',
// 也可以返回一个默认值
default(){
return 'hello abc'
},
// 也可以添加校验
// 限制动态长度最少为 5
validator(value){
// console.log(this,arguments);
return value.length >=5
}
}
},
template:`
<div>
<h2>子组件 home --- {{data}} --- {{num}} --- {{dataMsg}}</h2>
</div>
`,
// created(){
// console.log(this,typeof this.num); // num 是 string类型的
// }
})
let app = new Vue({
el:"#app",
data:{
msg:'hello'
},
components:{
'home':Home
}
})
效果图
5.2 $parent
子组件中还可以通过$parent属性访问父组件,因此就可以间接的获取父组件中的数据。
但是工作中,不建议这么使用,因为这种方式与父组件耦合并且无法校验数据,
所以我们要使用父组件向子组件传递属性的方式实现通信
5.3 自定义事件
vue也实现了观察者模式,提供了订阅消息,发布消息,注销消息等方法。
$on(type, fn) 订阅消息方法
type:消息名称,
fn: 消息回调函数,参数是由 $emit 方法传递的
$emit(type, …args) 发布消息方法
type:消息名称
…args:从第二个参数开始,表示传递的数据
$off(type, fn) 注销消息方法
type:消息名称
fn:消息回调函数
组件是一个完整独立的个体,因此彼此之间数据不会共享,所以发布消息与订阅消息必须是同一个组件
5.4 子组件向父组件通信
子组件向父组件通信的实现
在父组件中,订阅消息,
在子组件中,通过this.$parent访问父组件,发布消息并传递数据
在父组件中,接收并存储数据
在通信过程中,我们访问了$parent,因此产生了耦合,所以在工作中,这种方式不常用。
子组件向父组件通信有两种常用方式:
第一种:模拟DOM事件
第二种:传递属性方法
05 子向父$parent + 发送事件(不建议).html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>{{msg}}</h1>
<input type="text" v-model="msg">
<hr>
<home data="dazhaxie" num="100" :msg="msg"></home>
</div>
<script src="./dist/05.js"></script>
</body>
</html>
05 .js
import Vue from 'vue';
// 定义子组件
let Home = Vue.extend({
props:['data','num','msg'],
template:`
<div>
<h2>child {{data}} --- {{num}} --- {{msg}}</h2>
<h3>{{$parent.msg}}</h3>
</div>
`,
created(){
// console.log(this,this.$parent.msg);
// 子组件向父组件发送数据
setTimeout(()=>{
// 发布消息
// 通过子组件发布消息
// this.$emit('ickt',200,500,false)
// 通过父组件发布
// $parent 在通信过程中,访问了$parent ,因此产生了 耦合,所以在工作中,这种方式不常用
this.$parent.$emit('dzx','from child',200,100)
},1000)
},
})
let app = new Vue({
el:"#app",
data:{
msg:'hello'
},
components:{
'home':Home
},
// 父组件自行传递订阅消息
created(){
// 订阅消息
this.$on('dzx',(msg,...args)=>{
// console.log(args);
// console.log(msg);
// 父组件接收子组件传递过来的数据
this.msg=msg
})
// 发送消息
// this.$emit('dzx',100,200,true)
}
})
效果图
5.4.1 子向父 模拟 DOM 事件
绑定DOM事件:v-on:click=”fn”
模拟DOM事件:v-on:demo=”fn” demo事件名称就是自定义事件名称
子组件向父组件通信
1在父组件模板中,为子组件元素绑定自定义事件。
消息名称:字母小写横线分割单词。
注意:绑定事件的时候,不要添加参数集合
如果没有添加参数集合,可以接收所有的数据
如果添加了参数集合,不能接收数据,即使传递了$event也只能接收一条数据
2 在子组件中,通过$emit发布消息,并传递子组件中的数据。
消息名称:字母小写横线分割单词。注意:这是唯一一个不需要做驼峰式命名转换的地方。
3 在父组件事件回调函数中,接收数据并存储数据。
06 子向父模拟 DOM 事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>parent ----{{ msg }}</h1>
<hr>
<!-- 引入子组件 -->
<!-- 第一步 绑定自定义事件 -->
<!-- 谁的模板使用的数据就是谁的 -->
<home @da-zha-xie="childToggle"></home>
<!-- 不要添加参数集合,否则接收的数不完整 -->
<!-- <home @da-zha-xie="childToggle($event,200,'dazha')"></home> -->
</div>
<script src="./dist/06.js"></script>
</body>
</html>
06 .js
import Vue from 'vue';
// 定义子组件
let Home = Vue.extend({
template:`
<div>
<input type="text" v-model="msg">
<h2>child ---- {{ msg }}</h2>
</div>
`,
data(){
return{
msg :"hello child "
}
},
// 监听数据的改变
watch:{
// 第二次发布更新的数据
msg(value){
this.$emit('da-zha-xie',value)
}
},
created(){
// 第一次 发布静态消息
this.$emit('da-zha-xie',this.msg,100,200)
}
})
let app = new Vue({
el:"#app",
data:{
msg:"hello parent"
},
// 注册子组件
components:{
'home':Home,
},
// 定义事件
methods:{
childToggle(msg){
// 此时的 this 指向父组件
// console.log(this,arguments);
// 更新数据
this.msg = msg
}
}
})
效果图
5.4.2 子向父 传递属性方法
可以向子组件传递父组件方法,让子组件执行父组件方法并传递数据的形式,来实现通信。
第一步 在父组件模板中,为子组件传递父组件中的方法。
第二步 在子组件中,接收方法,执行方法并传递数据,
第三步 在父组件的方法中,接收数据,并存储数据。
07 子向父 传递属性的方法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>parent ---- {{msg}}</h1>
<hr>
<!-- 谁的模板使用的数据就是谁的 -->
<!-- 第一步 向子组件传递方法 -->
<home :da-zha-xie="childToggle"></home>
</div>
<script src="./dist/07.js"></script>
</body>
</html>
07 .js
import Vue from 'vue';
// 定义子组件
let Home = Vue.extend({
//第二部 子组件接收数据
props:['daZhaXie'],
template:`
<div>
<input type="text" v-model="msg">
<h2>child ---- {{msg}}</h2>
</div>
`,
data(){
return{
msg:"child msg"
}
},
// 第四步 监听数据 并 发送数据
watch:{
msg(value){
// 第一种方法
// this.daZhaXie(value)
// 第二种方法
this.daZhaXie(this.msg)
}
},
created(){
// 第三 部 传递静态数据
this.daZhaXie(this.msg,100,200)
}
})
let app = new Vue({
el:"#app",
data:{
msg:"parnent hello"
},
// 注册子组件
components:{
'home':Home
},
// 定义事件
methods:{
childToggle(msg){
console.log(this,arguments);
// 第 五 步更新数据
this.msg = msg
}
}
})
效果图
5.5 兄弟组件间通信
两个兄弟组件之间没有必然的联系,但是它们都与父组件有联系,因此要实现兄弟组件间通信就是综合
使用以上两种通信方式。
父组件向子组件通信
子组件向父组件通信
流程:
1 将一个子组件的数据传递给父组件。
2 父组件存储数据,再将新的数据传递给另一个子组件。
对于不相干的两个组件之间的通信,后面会学习VueX来解决。
08 兄弟组件之间的通信.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>parnet-----{{ msg }}</h1>
<hr>
<!-- 第一种 模拟 DOM 事件 通信 -->
<!-- <Home @demo="homeToggle"></Home> -->
<!-- 第二种 传递方法 通信 -->
<Home :da-zha-xie="homeToggle"></Home>
<hr>
<!-- 将数据传递给兄弟组件 -->
<list :msg="msg"></list>
</div>
<script src="./dist/08.js"></script>
</body>
</html>
08 .js
import Vue from 'vue';
// 定义第一个子组件
let Home =Vue.extend({
// 第二种接收方法
props:['daZhaXie'],
template:`
<div>
<input type="text" v-model="msg" >
<h2>home ---- {{msg}}</h2>
</div>
`,
data(){
return {
msg:"home chile"
}
},
created(){
// 第一种 事件 传递事件的第一次传递 静态
// this.$emit('demo',this.msg)
// 第二种 执行方法
this.daZhaXie(this.msg)
},
watch:{
msg(value){
//第一种 传递事件的第二次传递 动态
// this.$emit('demo',value)
// 第二种 执行方法
this.daZhaXie(this.msg)
}
}
})
// 定义第二个子组件
let List =Vue.extend({
// 接收数据
props:['msg'],
template:`
<h3>listChild --- {{msg}}</h3>
`,
})
let app = new Vue({
el:"#app",
data:{
msg:"parent hello"
},
// 注册子组件
components:{
'home':Home,
'list':List
},
// 定义事件
methods:{
homeToggle(msg){
console.log(msg,"打印的 msg 数据");
this.msg = msg
}
}
})
效果图
六、 数据双向绑定的实现
vue为简化数据双向绑定,提供了v-model指令。数据双向绑定有两个方向
一个方向是数据由模型进入视图
通过为元素的value属性绑定数据实现的。
一个方向是数据由视图进入模型
通过为元素订阅input事件实现的
v-model指令可以看成是语法糖指令,简化了数据双向绑定的实现。
对于组件实现数据双向绑定与input元素是一样的。在组件中,
通过value属性接收父组件传递的数据(父组件向子组件通信)
通过input事件向父组件传递数据(子组件向父组件通信)
09 v-model 指令实现数据通信.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<!-- <input type="text" v-model="msg"> -->
<h1> hello --- {{msg}}</h1>
<!-- 数据由模型进入视图 -->
<!-- <input type="text" :value="msg"> -->
<!-- 数据由视图进入模型 -->
<!-- <input type="text" @input="updateValue"> -->
<!-- 对以上方法综合使用 -->
<!-- <input type="text" :value="msg" @input="updateValue"> -->
<!-- 简写的方式 -->
<!-- 因为这是 一个 js 环境 所以可以这么写 -->
<input type="text" :value="msg" @input=" e => this.msg = e.target.value">
<hr>
<home v-model="msg" ></home>
</div>
<script src="./dist/09.js"></script>
</body>
</html>
09 .js
import Vue from 'vue';
// 定义子组件
let Home = Vue.extend({
props:['value'],
template:`
<div>
<h1>home ----- {{value}}</h1>
<input type="text" :value="value" @input=" e => this.$emit('input',e.target.value) ">
<!-- 内部使用 v-model 不能将数据传递给外部 -->
<!-- <input type="text" v-model="value"> -->
</div>
`
})
let app = new Vue({
el: "#app",
data: {
msg: " World"
},
components:{
'home':Home
},
// 方法
methods:{
// updateValue(e){
// // 更新数据
// this.msg = e.target.value;
// }
}
})
效果图
七、 ref
想在脚本中,获取模板中的元素或者组件,可以使用ref属性。
分成两步
第一步 为元素或者组件添加ref属性。属性值代表它的名称
第二步 在脚本中,通过this.$refs属性,即可获取对应的元素或者组件。
为元素设置ref,获取的是源生的DOM对象,要使用源生的API操作
对组件设置ref,获取的是组件实例化对象。
10 ref.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>parent-----{{msg}}</h1>
<h2 ref="title">{{msg}}</h2>
<hr>
<home ref="demo"></home>
</div>
<script src="./dist/10.js"></script>
</body>
</html>
10 .js
import Vue from 'vue';
// 定义子组件
let Home = Vue.extend({
template:`
<div>
<h5>home child</h5>
</div>
`
})
let app = new Vue({
el:"#app",
data:{
msg:"hello world"
},
// 注册组件
components:{
'home':Home
},
mounted(){
console.log(this.$refs);
this.$refs.title.style.color="red";
console.log(this.$refs.title);
}
})
效果图
八、 插槽
让我们在组件的模板中,使用组件元素内部的其它元素(内容)。
使用插槽分成两步:
第一步 为组件元素内部的子元素设置slot属性,定义插槽名称
第二步 在组件模板中,通过slot组件,插入内容。
通过name属性定义插槽名称,如果没有定义name属性,将插入默认的的元素。
如果不想引入slot属性所在的元素,我们可以使用template模板元素。
此时slot属性要改成v-slot指令,
使用方式: v-slot:name
v-slot指令语法糖:# 可以写成 #name
注意:我们不能在普通的元素上使用v-slot指令。
11 slot + v-clot 插槽.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>app-page ----{{msg}}</h1>
<home>
<!-- <div slot="header">
<h5>header page</h5>
</div>
<div>
<h5>body page</h5>
</div>
<div slot="footer">
<h5>footer page</h5>
</div> -->
<!-- 使用 template -->
<!-- template 建议我们将 slot 属性 改成 v-slot 指令 -->
<!-- <template slot="header">
<h5>header page</h5>
</template> -->
<template v-slot:header>
<h5>header page</h5>
</template>
<template>
<h5>body page</h5>
</template>
<!-- v-slot 的语法糖是 # -->
<template #footer>
<h5>footer page</h5>
</template>
</home>
</div>
<template id="tpl">
<div>
<slot name="header"></slot>
<h3>home page</h3>
<!-- 没有name 引入默认的 -->
<slot></slot>
<h3>{{msg}}</h3>
<slot name="footer"></slot>
</div>
</template>
<script src="./dist/11.js"></script>
</body>
</html>
11 .js
import Vue from 'vue';
let Home = Vue.extend({
template:"#tpl",
data(){
return{
msg:"dazhaxie"
}
}
})
let app = new Vue({
el:"#app",
data:{
msg:"hello World"
},
components:{
'home':Home
}
})
效果图
九、组件与语法糖
语法糖
: => v-bind:
@ => v-on:
# => v-slot:
组件
component, slot, transition, transtion-group, keep-alive
十、 插槽作用域
插槽作用域就是说,我们在组件元素内部的子元素中,使用的数据存储在哪里。
在父组件模板中,组件元素内部的子元素仍然使用父组件中的数据。
插槽作用域技术就是让我们在组件元素内部的子元素中,使用子组件中的数据。
共分两步
第一步 在子组件模板中,为slot组件传递属性数据。
命名规范:字母小写,横线分割单词。
想动态传递数据,要使用v-bind指令。
第二步 在子组件元素内,通过v-slot指令定义数据的命名空间
命名空间中的数据名称:要使用驼峰式命名。
12 插槽作用域.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>app-page ------ {{msg}}</h1>
<home>
<template v-slot:header>
<div>header page</div>
</template>
<template>
<div>body page</div>
</template>
<template #footer="homeData">
<div>footer page --- {{msg}}</div>
<hr>
<div>home--{{homeData.color}} ---- {{homeData.num}} --- {{homeData.homeMsg}}</div>
<hr>
{{homeData}}
</template>
</home>
</div>
<template id="tpl">
<div>
<slot name="header"></slot>
<hr>
<h6>home-page ---- {{msg}}</h6>
<slot></slot>
<hr>
<slot name="footer" color="blue" :num="100" :home-msg="msg"></slot>
</div>
</template>
<script src="./dist/12.js"></script>
</body>
</html>
12 .js
import Vue from 'vue';
let Home = Vue.extend({
template:'#tpl',
data(){
return{
msg :"home-dazhaxie"
}
}
})
let app = new Vue({
el:"#app",
data:{
msg:"parent-dazhaxie"
},
components:{
'home':Home
}
})
效果图