Vue基础知识
1:Vue生命周期以及钩子函数
Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载DOM渲染、更新渲染、销毁等一系列过程,我们称为是Vue的生命周期,也就是说从Vue实例创建到销毁这一过程,称为是生命周期
我们先来看一张非常经典的图片
每一个组件或者是实例都会经过一个完整的生命周期,总共分为三个部分:初始化,运行中,销毁。
实例和组件通过new Vue()创建出来之后会初始化事件和生命周期,然后执行beforeCreate钩子函数(此时,数据还没有开始挂载,只是一个空壳,无法访问到数据以及真是的DOM,这时一般不做挂在数据或者是绑定事件等);然后执行created函数(这个时候可以使用到数据,以及可以更改数据,这里是在渲染前倒数第二次更改数据的机会,不会触发其他的钩子函数。一般在这里做数据的获取);接下来开始找实例或者组件对应的模板,编译模板为虚拟的DOM放入到reader函数中准备渲染,然后执行beforeMount钩子函数(在这个函数中虚拟的DOM以及创建成功,马上就要渲染,在这里可以更改数据,这里是渲染前倒数第一次更改数据的机会,不会触发其他钩子函数,可以做初始数据的获取);接下来开始render,渲染出真实的DOM,然后执行mounted钩子函数(此时组件已经出现在页面中,真实的DOM已经处理好了,事件都已经挂在好了,可以操作真实的DOM等事件);当组件或者是实例的数据更改之后,会立即执行beforeuUpdate()然后虚拟的DOM机制会重新构建虚拟的DOM与上一次虚拟的DOM树利用diff算法进行对比之后重新渲染,一般不做什么事
当更新完成之后,执行updated(数据已经更新完成,DOM也重新render完成,可以执行更新之后的虚拟DOM),当通过某种途径调用$destroy,立即执行beforeDestroy(一般在这里面做一些善后工作,例如清楚计时器,清除指令绑定事件等,去除组件的数据绑定,监听,只剩下DOM空壳之后,这个时候执行destroyed,销毁
钩子函数:
beforeCreate() {}
created() {}
beforeMount() {}
mounted() {}
beforeUpdate() {}
updated() {}
beforeDestroy() {}
destroyed() {}
要注意的一点是:以上的生命周期钩子函数中,都是自动绑定this,因此就可以直接访问Vue实例中的数据,这就可以对实例的属性或者函数进行运算;也就是说我们就不能使用ES6的语法对生命周期钩子函数进行改造成箭头函数,这是因为箭头函数的this指向的是符上下文,箭头函数不存在this,箭头函数会向上寻找this的父上下文,所以就和我们期待的this就有所不同。
之前看到一篇写的很好的关于生命周期的博客,可以参考一下
https://mp.weixin.qq.com/s?src=11×tamp=1581051837&ver=2143&signature=4IUmKlHwNgVfv9kKGdVsjpPnvma3bcQ47BCwNvh*r88ioOgjRgVqzLNCmH2W2kzbTW8VlsVUrTwABZHxaE1bX4aLtqyxsoeq1sathoDrllf9SlvX1KAKax7c6D0RZ9TG&new=1
数据绑定
{{}}
说到绑定数据,最简单的方式就是使用{{ }}
如上图所示,定义一个变量msg,然后使用{{}} 绑定数据
v-model
v-model 绑定表单元素中的值 实现数据双向绑定
如上例,使用v-model绑定数据我们可以看到当改变表单中的数据时,同时下面再次引用时的数据也发生改变
v-bind
那么我们知道v-bind也可以实现绑定,但是==v-bind是用来绑定属性的并且不能实现数据双向绑定,他是单向绑定==
如上图所示,绑定value属性,然后改变表单中的数据,不会影响变量的值 ,v-bind可以简写成为:属性名="",
除此之外,v-bind除了可以绑定属性之外,还可以绑定单个或者是多个类名称
绑定单个类名称
绑定多个类名称
或者也可以使用这样的方式实现
<p v-bind:class="{'p1':false,'p2':true}">{{msg}}</p>
同时 v-bind也可以直接绑定样式style
<p v-bind:style="{color:'red'}">{{msg}}</p>
或者使用变量
v-for 循环绑定
同时v-for也可以绑定多个数据(v-for支持第二个参数既当前变量的索引)
条件渲染v-if
一般使用v-if,v-else,v-else-if条件判断,常用在某种情况下显示某种元素
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
v-show
v-show是用来显示元素,为true显示,为false不显示
<p v-show="num==0?true:false">我是显示的</p>
我们可以发现v-if和v-show都能实现元素的显示与隐藏,二者的区别在于:
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if 较好
v-html和v-text
这里需要注意的一点就是v-html会解析语句,而v-text不会
事件 v-on 和@
可以通过v-on给元素添加事件,它就等于@+事件类型
export default {
name: 'app',
data () {
return {
msg:"1111"
}
},
methods:{
getmsg(){
console.log(this.msg);
}
}
}
</script>
常用事件修饰符 stop,prevent,self ,capture,once
stop用于阻止点击事件继续传播,也可以说是阻止事件冒泡
<p v-on:click.stop="getmsg" class="mp">获取值</p>
prevent提交事件不再重载页面
<form v-on:submit.prevent="onSubmit"></form>
self 只当在 event.target 是当前元素自身时触发处理函数 ,即事件不是从内部元素触发的
<div v-on:click.self="doThat">...</div>
capture 相当于事件捕获,即内部元素触发的事件先在此处理,然后才交由内部元素进行处理
<div v-on:click.capture="doThis">...</div>
once指点击事件只触发一次
<a v-on:click.once="doThis"></a>
ref 获取DOM节点,通过下面的方式就可以拿到文本框中输入的数据
<template>
<div id="app">
<input type="text" ref="txt">
<button @click="getdom">获取DOM节点</button>
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
msg:"1111"
}
},
methods:{
getdom(){
console.log(this.$refs.txt.value);
}
}
}
</script>
<style lang="scss">
</style>
watch监听
<input type="text" v-model="cityName"/>
new Vue({
el: '#root',
data: {
cityName: 'shanghai'
},
watch: {
cityName(newName, oldName) {
// ...
}
}
})
直接写一个监听处理函数,当每次监听到 cityName 值发生改变时,执行函数。
filters 过滤器
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。里面必须有返回值return,在返回之前对值进行过滤。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式
<!-- 在双花括号中 -->
{{ message | capitalize }}
其中capitalize为过滤器函数,massage为接收到的值
<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>
以下是一个简单过滤器的样子
<template>
<div id="app">
<input type="text" v-model="sex"/>
<p>{{arr|mysort(sex)}}</p>
//其中sex相当于下面mysort过滤器函数中的第二个参数a
</div>
</template>
<script>
export default {
name: 'app',
data () {
return {
msg:111,
arr:["男","女","男","女","男","男","男"],
sex:"男",
}
},
filters:{
//过滤器里面必须有返回值,在返回之前对值进行过滤
mysort(val,a){
let att=[];
for(let key of val){
if(key===a){
att.push(key);
}
}
return att
}
}
}
</script>
<style lang="scss">
</style>
在过滤器这里需要注意的一点就是v-for不能直接使用{{ message | capitalize }}这样的形式来写
组件
1:子父组件之间的传值
第一种情况 父组件给子组件传值 使用props,有两种方式
第一种是利用数组直接传,第二种是条件约束之后传
子组件:(子组件在props中创建一个属性,用以接收父组件传过来的值,父组件在子组件标签中绑定子组件props中创建的属性,把需要传给子组件的值赋给该属性)
<template>
<div id="contentmenu">
{{title}}
<img :src="logo"/>>
</div>
</template>
<script>
//字符组件之间传值 props 两种方式 一种直接写,还有一种进行类型的约束
export default{
name:"contentmenu",
props:["title","logo"]
};
</script>
<style>
</style>
父组件:
<template>
<div id="app">
<contentmenu :title="title" :logo="logosrc"></contentmenu>
</div>
</template>
<script>
//直接引入组件
import contentmenu from './component/content'
export default {
name: 'app',
components:{
contentmenu//组件的挂载
},
data () {
return {
title:"中间",
logosrc:"https://cn.vuejs.org/images/dcloud.gif"
}
}
}
</script>
<style>
</style>
上例利用props直接将父组件中的值传递给子组件之中,或者说你希望每个 prop 都有指定的值类型,也就是对它的值进行约束。这时,你可以以对象形式列出 prop,这些属性的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // or any other constructor
}
单向数据流
props在父子组件之间形成了向下的单向数据流,只能从父组件向子组件之间传递,父级组件的更新会流动到子组件中,每当父级组件发生更新的时候,子组件中的props就会马上刷新到最新的值
第二种情况是子组件给父组件传值
1) 父组件获取子组件的方法和数据
在子组件上定义ref属性 通过 this.$refs.name
.属性 或者this.$refs.name.
方法
子组件代码
<template>
<div></div>
</template>
<script>
export default {
data () {
return {
msg: "子组件child的值"
}
},
methods: {
func () {
alert("子组件child的方法")
}
}
}
</script>
父组件代码:
<template>
<div>
<child-component ref="child"></child-component>
<button @click="getChildProp()">获取子组件的属性的值</button>
<button @click="getChildMethod()">获取子组件的方法</button>
</div>
</template>
<script>
import ChildComponent from './components/ChildComponent.vue'
export default {
components:{ ChildComponent },
methods: {
getChildProp () {
alert(this.$refs.child.msg) // 子组件child的值
},
getChildMethod () {
this.$refs.child.func() // 子组件child的方法
}
}
}
</script>
2)子组件主动获取父组件的属性和方法
This.$parent.数据 this.$parent.方法
子组件代码
<template>
<div id="headermenu">
头部
<button @click="sendparent">给父级传值</button>
</div>
</template>
<script>
export default {
name: 'headermenu',
props:{
parent:Object
},
methods: {
sendparent(){
this.parent.childData="我给你了";
}
},
};
</script>
<style>
</style>
父组件代码
<template>
<div id="app">
<div>{{childData}}</div>
<headermenu :parent="myself"></headermenu>
</div>
</template>
<script>
import headermenu from './component/header'
export default {
name: 'app',
components:{
headermenu
},
data () {
return {
childData:"儿子还没传",
myself:this
}
}
}
</script>
<style>
</style>
首先在子组件中通过props创建一个parent属性,然后在父组件中绑定这个属性在这个子组件的标签上面,然后绑定this,紧接着在子组件中修改值就可以传递
第三种情况是非父组件之间传值
1:使用事件车 $emit $on
首先新建一个js文件作为事件车
import Vue from 'vue'
let vue=new Vue();
export default vue;
然后再传递的组件中使用$emit
<template>
<div id="contentmenu">
<button @click="sendmsg">发送广播</button>
</div>
</template>
<script>
import Emit from '../emit/emit'
export default {
name:"contentmenu",
methods:{
sendmsg(){
//发送广播 $emit 用于触发当前实例上的事件
Emit.$emit("msginfo","你是脚!");
}
}
}
</script>
<style >
</style>
在另一个组件中通过 $on(eventName, eventHandler) 侦听一个事件
<template>
<div id="footermenu">底部</div>
</template>
<script>
import Emit from "../emit/emit";
export default {
name: "footermenu",
mounted() {
//自动监听
Emit.$on("msginfo", res => {
console.log(res);
});
}
};
</script>
<style >
</style>
2:还可是使用vuex 数据源管理实现非子父组件之间的传值