钩子函数的生命周期
根实例,在初始化时会调用很多方法,这些方法被称为钩子函数
生命周期:beforeCreate created beforeMount mounted beforeUpdate updated beforeDestory destoryed
beforeCreate(){},此时只有内部方法
此方法一般用不到
create(){}
获取ajax,初始化操作
beforeMount(){}编译前的操作,也没有什么实际意义
一定要保证有编译的元素el,否则会中断
挂载el,也可以使用
new Vue().mount('#app')
如果有实例template属性,会用模板替换掉外部html,只要有此属性,app中的内容没有任何意义,template中只能有一个根元素。
let vm=new Vue(){
el:"#app",
template:"</div>{{a}}</div>",
data:{a:1
}}
之前第三天的时候又提到过,DOM中也有一个tempelate标签,是没有任何意义,用来包裹元素的。
mounted(){} 操作DOM
用编译好的el替换掉el,然后调用mounted()
真实dom渲染完了可以操作dom了
beforeUpdate(){}
当数据变化的时候,调用beforeUpdate()
updated(){}
一般用watch来替换,watch能够更准确的知道是谁更新了。
beforedestory(){}销毁前,可以清除定时器,或清除事件绑定
destoried(){}销毁后
watch,computed以及方法全部被销毁
实例上的方法
this.$data vm上的数据
this.$watch 监控
this.$el 当前el元素
this.$set 后加的属性实现响应式的变化
this.$options 获取vm中所有的属性
this.$nextTick(){} 异步方法,可以等待dom完成后获取vm,是当前代码全部执行完毕后,执行
另外dom渲染是异步的,想要数据变化后想获取真实dom中的内容,必须使用nextTick
this.$refs 所有refs的集合,假如有一个标签 ref="myp" 可以直接通过this.$ refs.myp。ref 被用来给DOM元素或子组件注册引用信息。如果dom元素不是通过v-for循环出来的,只能获取一个,通过v-for则可以获取多个,输出为数组
组件
可以将很复杂的页面分割成若干个独立组件,每个组件包含自己的逻辑和样式
减少逻辑复杂度,实现了代码的重用
页面级组件:1、一个页面是一个组件
2、将可复用的部分抽离出来,作为基础组件
优点
- 提高开发效率
- 方便重复使用
- 便于协同开发
- 更容易被管理和维护
Vue的组件
一个自定义的标签Vue就会把它看成一个组件
div p span a header section都是W3C规定的,不是自定义的标签,但是Vue可以给它们赋予一定的意义
全局组件
声明一次,可以在任何地方使用
一般使用局部组件,写插件的时候使用全局组件
之前Vue.filter("函数名",函数)全局过滤器
全局组件的基本形式
一个对象可以看成是一个组件
Vue.component('polarbear',{
//一个对象可以看成一个组件
template:'<div>{{msg}}</div>',
//只能是一个根元素
//相当于用这个模板替换这个标签
//组件名不要带有大写,多个单词用-
//只要组件名和定义名字相同是可以的(首字母可以大写)
//html采用的短横线隔开命名法 js中转驼峰也是可以的
data(){
//组件中的数据必须是函数类型的,返回一个实例作为数组的实例
return {msg:'大白白熊'}
}
})
组件名
组件名不要带有大写,有多个单词用-
只要组件名和定义名字相同是可以大写的,但是只有首字母可以大写
html采用的短横线隔开命名法,js的转驼峰法也可以
template属性
DOM中的template是一个没有意义的标签,用来框东西
这里的template会给出一个DOM模板,替换标签中的内容
只能是一个根元素
template:'<div>{{msg}}</div>'
data
组件中的函数必须是函数类型的,并且返回一个实例作为数组的实例
DOM
<div id="app">
<polarbear>大白熊</polarbear>
</div>
虽然DOM中是大白熊,但是polarbear的template属性给出了内容为大白白熊,所以页面中的内容会被替换
局部组件
必须告诉这个组件是属于谁的
三步骤
- 创建这个组件
- 注册这个组件
- 引用这个组件
创建组件
和创建Vue的过程相似,但是要记住组件的data是函数,需要返回一个形式与Vue data形式相同的对象
let polarbear={
template:'<div>大白白熊</div>',
data(){
return {msg:'大白白熊'}
}
};
let penguin={
template:'<div>企鹅</div>'
};
- 如果组件共用了数据,数据会同时刷新,而组件的特点就是独立性,所以最好不要
- 子组件不能直接使用父组件的数据
- 组件理论上可以无限嵌套
注册组件
在Vue实例中添加一个components属性
let vm=new Vue(
{
el:"#app",
data:{
a:1
},
components:{
polarbear,
penguin
//它们的parent是vm
//不能跨作用域调用vm的data,组件相互独立不能直接跨作用域 vm的实例也是一个组件
//也拥有生命周期函数
},
}
)
- 在哪里注册组件,这些组件的父组件就是谁
- 但是组件是不能调用父组件的数据的,polarbear和penguin都不能调用a
- 这些组件也有生命周期函数
DOM的写法
除了直接在DOM中写,也可以在Vue实例中添加
template:'<polarbear><penguin></penguin></polarbear>'
注意以上这种写法是不会显示企鹅的,因为polarbear会替换掉标签之中的所有内容包括penguin
组件之间引用
如果要在一个组件中使用另一个组件
- 首先必须要保证使用的组件真实存在
- 在需要引用这个组件的实力上通过components注册这个组件
- 组件需要在父级的模板中通过标签的
形式引入
let littlebear={
template:"<div>小白熊</div>"
};
let polarbear={
template:'<div>白熊<littlebear></littlebear></div>',
components:{littlebear}
};
let bigbear={
template:'<div>大白熊<polarbear></polarbear></div>',
components:{polarbear}
};
let vm=new Vue(
{
el:"#app",
components:{bigbear},
template:"<bigbear></bigbear>"
}
最终显示结果是大白熊 白熊 小白熊
除了标签的简单引用,想要数据之间的引用就需要使用其他的方法了
父传子
父亲定义好数据,通过属性的方式传递给孩子
以父亲给孩子零花钱为例
- 首先父亲中要有money这个数据
- 孩子通过DOM获取
DOM中数据传递
<div id="app">
父亲给了{{money}}
<child :m="money/2"></child>
<!--m是属于child的属性,1是属于父级的,当前组件的属性=父级的值-->
<!--用冒号把值变成属性,不加冒号,money就变成字符串了,key:value,这里永远是字符串-->
</div>
这里有标签的相关知识,在一个标签中
- key:value形式,前面的key是当前组件的属性,后面value是父级标签的值
- 如果不使用冒号的方式,传递的值会变为字符串格式,使用冒号,值会变为属性的形式
如果上文中
:m="money"改为m="money"
那么m是字符串money
props属性
props中的数据可以是数组,也可以是对象
但是不能和data中的名字相同,校验时不能阻断代码的执行,只是进行警告
单向数据流
prop使得父子prop之间形成了单向下行绑定,父级prop的更新会向下流动到子组件中。
prop:['m']
会在当前子组件上声明一个m属性,值是父亲的
m不能直接写m必须写成字符串格式
m可以直接用,但是不能改,只能通过data或者compute计算新的值
computed:{
money(){
return this.m
}}
这时
child:{
template:'<div>儿子 {{money}}</div>',
//props:['m'],
computed:{
money(){
return this.m
}
}
显示结果为儿子获得50
props校验功能
可以使用对象的形式进行校验
type
校验属性的类型
type可以师原生构造函数中的一个
String,Number,Boolean,Array,Object,Date,Function,Symbol
如果传递的属性类型不正确,只会warn并不会报错
default
默认值,如果没有传数据过来,自动取默认值
required
当这个属性为true时,此属性必须传递,不传就报warn,不能和default同时使用
不传递的时候显示会是false
validator(val){}
自定义校验器
第一个参数是当前传递的值
返回true则表示通过,false为不通过
但是false也只是warn,不报错
props:{
//属性名和data中的名字不能相同,校验时不能阻断代码的执行,只是进行警告
m:{
//校验属性的类型,如果不带冒号,得到的是字符串
type:[String,Boolean,Function,Object,Arrey,Number],
//对象的形式可以进行校验
//默认值,如果不传的话,自动取默认值
default:0,
//required:true
//此属性必须传递,不传就报warn,现在{{money}}显示为false,但是不能和default同时使用
validator(val){
//第一个参数是当前传递的值
//返回true表示通过
//false表示不通过
//依然只是warn,自定义校验器,用的不是很多
return val>300
}
}
}
全部程序
<body>
<div id="app">
{{money}}
<child :m="money/2"></child>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let vm=new Vue( //parent组件
{
el:"#app",
data:{
money:100
},
components:{
child:{
template:'<div>儿子 {{money}}</div>',
computed:{
money(){
return this.m
}
},
props:{
m:{
type:[String,Boolean,Function,Object,Arrey,Number],
default:0,
//required:true
validator(val){
return val>300
}
}
}
}
}
)
</script>
</body>
发布订阅模型
发布emit 订阅 on
说白了就是触发和绑定
利用这两个事件,可以创建一对多的关系
实例,创建一个{日常行为:['睡觉','游泳','吃海豹']}
function polarbear(){
this._events={}
//下划线表示是私有的
//{日常行为:['睡觉','游泳','吃海豹']}
}
let pb=new polarbear()
on
为polarbear绑定on,emit事件
因为是所有实例共用的,所以放在原型上
polarbear.prototype.on=function(eventName,callback){
if(this._events[eventName]){
//不是第一次
//不能用点,点是返回字符
this._events[eventName].push(callback)
}
else{
this._events[eventName]=[callback]
}
}
为日常行为绑定相关事件
let sleep=(who)=>{console.log(who+"睡觉")}
let swim=(who)=>{console.log(who+"游泳")}
let eat=(who)=>{console.log(who+"吃海豹")}
pb.on('日常行为',sleep);
pb.on('日常行为',swim);
pb.on('日常行为',eat);
emit
原型绑定
polarbear.prototype.emit=function(eventName,...args){
if(this._events[eventName])
{
this._events[eventName].forEach(cb=>cb(...args)))
//cb.apply(this,args)
//展开数组
//只运行数组第一项
}
}
这个过程是将emit后面的值赋给前面的事件
因为参数的数量不确定,使用ES6中的...args,将剩余的参数作为数组args存储
pb.emit('日常行为','我','熊大','熊二');
最终运行结果 我睡觉 我游泳 我吃海豹,如果想要每一个都运行,还是要取args.length去做
子传父
通过事件,子触发父的事件,传递数据
儿子不能改变父亲的值,但是可以告诉他让他改
父亲绑定一些事件,儿子触发这个事件,将参数传递过去,单向数据流,父亲数据刷新,儿子也随之刷新
子传父超复杂,饶了180个弯
- 首先既然触发父的事件,那父亲里必须有这个事件
- 然后得把这个事件绑定到子组件上
- 绑定后还要触发这个事件,让父亲的方法执行
- 那触发这个事件还要在子组件上定义一个事件
事件绑定儿子上
<div id="app">
父亲给{{money}}
儿子得<child :m="money" @childmessage="changemoney"></child>
</div>
changemoney是父亲的事件,childmessage属于儿子
相当于child.on('childmessage',changemoney)
父亲事件定义
methods:{
changemoney(val){
this.money=val
}
}
儿子上的事件
template:'<div><button @click="getmoney()">多要钱</button></div>'
触发儿子自定义事件,父亲的方法被执行
getmoney(){
//触发自己的自定义事件,让父亲的方法执行
this.$emit('childmessage',800)
}
将800的值传递给childmessage,通过触发childmessage事件,执行父组件的changemoney,最终money被改变为800
全部程序
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="app">
父亲给{{money}}
儿子得<child :m="money" @childmessage="changemoney"></child>
<!--child.on('childmessage',changemoney),把事件绑定在儿子上-->
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--父亲绑定一些事件,儿子触发这个事件,将参数传递过去,单向数据流,父亲数据刷新,儿子就刷新-->
<!--儿子不能改父亲的值,但是可以告诉他,让他改-->
<script>
let vm=new Vue(
{
el:"#app",
data:{money:400},
components:{
child:{
template:'<div><button @click="getmoney()">多要钱</button></div>',
props:['m'],
methods:{
getmoney(){
//触发自己的自定义事件,让父亲的方法执行
this.$emit('childmessage',800)
}
}
}
},
methods:{
changemoney(val){
this.money=val
}
}
}
)
</script>
</body>
</html>
.sync的用法 语法糖
.sync修饰符
是一种双向绑定
但是双向绑定会带来一定维护的问题
sync修饰符相当于将emit处的方法名改变为update:m
<child :m.sync="money"></child>
<!--相当于<child :m="money" @update:m="val=>{this.money=val}"></child>-->
然后再子组件
getmoney(){
this.$emit('update:m',800)
}
模态框实例
这个实例是点击主界面上的按钮弹出警告框,然后点击警告框上的关闭按钮,关闭这个警告窗
需要利用bootstrap的警告框,子组件向父组件传递事件,父组件向子组件传递数据
- 父组件中定义flag属性,true的时候显示警告窗,false时不显示
- 将flag传递到子组件
- 子组件关闭按键按下时触发自定义事件执行父组件的改变flag的方法
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<div id="app">
<button @click="clickparent">弹</button>
<modal :show="flag" @close="fn"></modal>
<!--:show="flag"这是父传子,@close="fn"是子传父-->
<!--如果show的值为true则显示,如果是false则隐藏 @close对应的函数是点击关闭按钮时触发的函数-->
<!--<dialog :show="" @close="fn"></dialog>-->
</div>
<template id="dialog">
<div class="container" v-show="show">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="alert alert-danger" style="height:200px" role="alert">
<button class="pull-right" @click="closealert">关闭</button>
</div>
</div>
</div>
</div>
</template>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let modal={
template:'#dialog',
props:['show'],//接收父组件的属性
methods:{
closealert(){
this.$emit('close')
}
}
}
let vm=new Vue({
el:"#app",
components:{
modal//名字不能叫dialog,原生已经占用了这个名字
},
data:{
flag:false
},
methods:
{
clickparent(){
this.flag=true
},
fn(){
this.flag=false
}
}
})
</script>
</html>