钩子函数的生命周期
根实例,在初始化时会调用很多方法,这些方法被称为钩子函数 生命周期: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:"
{{a}}
",
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:'{{msg}}
',
//只能是一个根元素
//相当于用这个模板替换这个标签
//组件名不要带有大写,多个单词用-
//只要组件名和定义名字相同是可以的(首字母可以大写)
//html采用的短横线隔开命名法 js中转驼峰也是可以的
data(){
//组件中的数据必须是函数类型的,返回一个实例作为数组的实例
return {msg:'大白白熊'}
}
})
组件名
组件名不要带有大写,有多个单词用- 只要组件名和定义名字相同是可以大写的,但是只有首字母可以大写 html采用的短横线隔开命名法,js的转驼峰法也可以
template属性
DOM中的template是一个没有意义的标签,用来框东西 这里的template会给出一个DOM模板,替换标签中的内容 只能是一个根元素
template:'{{msg}}
'
data
组件中的函数必须是函数类型的,并且返回一个实例作为数组的实例
DOM
虽然DOM中是大白熊,但是polarbear的template属性给出了内容为大白白熊,所以页面中的内容会被替换
局部组件
必须告诉这个组件是属于谁的
三步骤
创建这个组件
注册这个组件
引用这个组件
创建组件
和创建Vue的过程相似,但是要记住组件的data是函数,需要返回一个形式与Vue data形式相同的对象
let polarbear={
template:'大白白熊
',
data(){
return {msg:'大白白熊'}
}
};
let penguin={
template:'企鹅
'
};
如果组件共用了数据,数据会同时刷新,而组件的特点就是独立性,所以最好不要
子组件不能直接使用父组件的数据
组件理论上可以无限嵌套
注册组件
在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
组件之间引用
如果要在一个组件中使用另一个组件
首先必须要保证使用的组件真实存在
在需要引用这个组件的实力上通过components注册这个组件
组件需要在父级的模板中通过标签的 形式引入
let littlebear={
template:"小白熊
"
};
let polarbear={
template:'白熊
',
components:{littlebear}
};
let bigbear={
template:'',
components:{polarbear}
};
let vm=new Vue(
{
el:"#app",
components:{bigbear},
template:" "
}
最终显示结果是大白熊 白熊 小白熊 除了标签的简单引用,想要数据之间的引用就需要使用其他的方法了
父传子
父亲定义好数据,通过属性的方式传递给孩子 以父亲给孩子零花钱为例
首先父亲中要有money这个数据
孩子通过DOM获取
DOM中数据传递
父亲给了{{money}}
这里有标签的相关知识,在一个标签中
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:'儿子 {{money}}
',
//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
}
}
}
全部程序
{{money}}
发布订阅模型
发布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个弯
首先既然触发父的事件,那父亲里必须有这个事件
然后得把这个事件绑定到子组件上
绑定后还要触发这个事件,让父亲的方法执行
那触发这个事件还要在子组件上定义一个事件
事件绑定儿子上
父亲给{{money}}
儿子得
changemoney是父亲的事件,childmessage属于儿子 相当于child.on('childmessage',changemoney)
父亲事件定义
methods:{
changemoney(val){
this.money=val
}
}
儿子上的事件
template:'多要钱
'
触发儿子自定义事件,父亲的方法被执行
getmoney(){
//触发自己的自定义事件,让父亲的方法执行
this.$emit('childmessage',800)
}
将800的值传递给childmessage,通过触发childmessage事件,执行父组件的changemoney,最终money被改变为800 全部程序
父亲给{{money}}
儿子得
.sync的用法 语法糖
.sync修饰符 是一种双向绑定 但是双向绑定会带来一定维护的问题 sync修饰符相当于将emit处的方法名改变为update:m
然后再子组件
getmoney(){
this.$emit('update:m',800)
}
模态框实例
这个实例是点击主界面上的按钮弹出警告框,然后点击警告框上的关闭按钮,关闭这个警告窗 需要利用bootstrap的警告框,子组件向父组件传递事件,父组件向子组件传递数据
父组件中定义flag属性,true的时候显示警告窗,false时不显示
将flag传递到子组件
子组件关闭按键按下时触发自定义事件执行父组件的改变flag的方法
弹