1. VUE特点
轻量
使用gzip最小压缩后有20kb,react有35kb,angular有60kb渐进式框架
你不需要完全了解他,就可以用,各取所需,只是个轻量视图而已响应式的更新机制
数据改变后,视图会自动更新,而不像react那样用shouldComponentUpdate进行性能优化学习成本低
模板语法是基于html,容易上手
2. 生命周期
Vue的所有生命周期函数都是自动绑定到this的上下文上,所以不需要用到箭头函数。
页面加载大致过程:
- beforeCreate:无法获取到prop的data
- created: 经过数据初始化,此时可以获取prop和data,但是组件尚未挂载;
- beforeMount: 开始创建虚拟dom,如果组件中有子组件,先递归挂载子组件,再挂载根组件的
- mounted: 将虚拟dom渲染为真实dom。
- 数据更新
更新前:beforeUpdates
更新后: updated - 页面销毁
销毁前:beforeDestory
销毁后: destoryed
4. Vue 的父组件和子组件生命周期钩子函数执行顺序
(1)加载渲染过程
beforeCreated(父) ->>created(父)->> beforeMounted(父) ->>
子组件渲染:beforeCreated(子) ->> created(子)->> beforeMounted(子) ->> mounted(子)
--> mounted(父)
(2)子组件更新
beforeUpdate(父) --> beforeUpdate(子) -> updated( 子) -> updated(父)
(3)父组件更新
beforeUpdate(父) --> updated(父)
(4)销毁
beforeDestroy(父) -> beforeDestroy (子)-> destroyed(子) -> destroyed(父)
3. vue基本指令
v-show
当条件为true时候display: block,否则是display: none;无论什么条件都会被渲染出来,渲染之后只是css属性的切换,渲染时候有一定的开销,适合频繁切换的场景。v-if
根据条件来渲染,如果是false,那一开始就不会渲染,但是切换条件时会触发销毁和挂载,所以在切换时候开销比较大,不适合频繁切换的场景v-for
<li v-for="(item, index) in items" :key="item.message">
{{index}} {{ item.message }}
</li>
- v-model
v-model其实是一个语法糖
v-model
会忽略所有表单元素的value
、checked
、selected
attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的data
选项中声明初始值。
<input v-model="name"></input>
//等价于
<input
v-bind:value="name"
v-bind:input="name = arguments[0]"
></input>
就是一种双向绑定的过程,视图影响数据,数据影响视图,说明视图是可交互的,一般和表单有关系
- v-on简写@ 绑定事件
- v-bind:简写: 绑定属性
- v-text显示文本
- v-pre不编译双花括号,直接显示字符串
- v-html可以显示html字符串
4. 组件的三大核心概念
(1)属性
① 自定义属性props,表示组件props中声明的属性
②原生属性,比如<Test title="测试"></Test>
,默认在组件中的根元素上就会有title="测试"的属性;
在Test组件中设置inheritAttrs: false就可以关闭自动挂载
③特殊属性,style, class挂载到组件根元素上
(2)事件
①普通事件
- @click
- @change
- @input
- @xxx
子组件通过this.$emit("xxx", 参数1, 参数2, 参数...)
②修饰符事件
@input.trim (去掉两端空格)
@click.stop(阻止冒泡事件或者在事件中使用e.stopPropagation)
-
@submit.prevent(去除默认事件)
一般用于原生html的行为
(3)插槽
① 普通插槽
//老语法
<template slot="xxx"> ...<template>
//2.6版本语法
<template v-slot:xxx> ...<template>
② 作用域插槽
可以获取属性
<template slot="xxx" slot-scope="props">{{props}}</template>
<template v-slot:xxx="props">{{props}}</template>
组件:
<template>
<div>
<slot name="xxx" />
<slot v-bind="{value: 'value'}" />
</div>
</template>
5. MVVM和MVC模式的区别
- MVC
MVC是最经典的模型,即Model(模型)、Controller(控制器)、View(视图)
当用户操作页面时,view层会发出指令到controller,controller去通知model更新数据。一旦model发生改变,就通知视图更新。
- MVVM
MVVM即Model-View-ViewModel,实现了view和model的自动同步,ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来。VM起到了双向绑定的作用,View(视图) 和 Model(数据) 无法直接通讯,在它们之间存在着 ViewModel 中间介充当观察者角色。当用户操作 View(视图),ViewModel 感知到变化,然后通知 Model 发生相应改变;反之当 Model(数据) 发生改变,ViewModel 也能感知到变化,使 View 作出相应更新。
6. 单向数据流和双向绑定
双向绑定:
model更新会触发view更新;
view的更新会触发model更新;
单向数据流:
model更新会触发view更新;
view的更新和model没有关系
vue是单向数据流,不是双向绑定,vue的双向绑定不过是语法糖,而Object.defineProperty是用来做响应式更新的,对属性设置set函数,当属性改变时候,就会触发这个函数
实现过程:
vue自定义v-model
一个组件的v-model默认会有prop: value,event: input,但是我们可以自定义
//定义子组件
Vue.component('Child', {
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: Boolean
},
template: <input type="checkbox" :checked="checked" @change="$emit('change', $event.target.checked)">
})
//在父组件中使用
<Child v-model="data" />
此时data的值会被传递到子组件中,为checked属性
7. vue中sync修饰符
.sync是为了方便父子之间通信,当子组件想要改变父组件传递进来的属性时,比如一个控制modal是否显示的属性,子组件就要使用$emit('xxx', 'xxx')
vue提供update: propertyName模式触发事件
//子组件更改属性
this.$emit("update:visible", false)
//父组件
<Child :visible="show" @update:visible="val => show = val" />
那么为了简化写法,出现了sync修饰符
//子组件更改属性
this.$emit("update:visible", false)
//父组件
<Child :visible.sync="show" />
父组件不需要定义,默认存在@update:visible="val => show = val"
8. 组件更新
组件更新的条件
①属性需要在data里面声明,这样才能做响应式
②如果data中声明一个对象
data() {
return {
info: {}
}
}
//这种不会触发更新
this.info.name = "a"
修改为:
data() {
return {
info: { name: undefined }
}
}
③如果在模板中没有用到某个属性,那么修改这个属性也不会触发组件更新
④如果data中有一个数组属性
直接push也会触发组件更新
那么vue是如何进行组件更新的呢?
当vue进行实例化时候,会使用Object.defineProperty对data中的属性设置set和get方法,相当于代理层;
将模板中需要用到的属性放到watch,如果修改了属性,在set函数中会去通知watch,然后watch去触发组件的更新。
9. compute和watch属性的区别
计算属性compute
作用:
① 减少模板中计算逻辑
② 缓存数据
③ 依赖固定的数据类型,不会重新再计算,只有在数据变化时候才会计算
对比:
<p>{{reverseStr}}</p>
<p>{{reverseStr2}}</p>
computed: {
reverseStr: function() {
this.str.split("").reverse.join("")
}
}
data() {
reverseStr2: function() {
this.str.split("").reverse.join("")
}
}
$forceUpdate刷新页面,str没变化时候,reverseStr不会执行计算,reverseStr2会被调用
监听属性watch
监听数据的变化
data() {
a:1,
b: {
c: 2
},
d: {
e: {
f: 3
}
}
}
//各个属性监听方式,
watch: {
a(val, old) {},
"b.c": function(val, old),
// d及以下的所有属性都会被监听,deep表示深度监听;immediate表示是否在第一次渲染就执行这个监听
d: {
handler: function(val, old) {},
deep: true,
immediate: true
}
}
如果一个数据需要经过复杂计算就用 computed;
如果一个数据需要被监听并且对数据做一些操作就用 watch
10. vue组件通信方式
①props和$emit父子组件通信
这个是最基础的组件通信,父组件通过v-bind:xxx="xx"传递属性,v-on:xxx="xx"绑定事件;
子组件通过props获取,$emit触发父组件传递的事件。
//父组件
<template>
<div>
<Child :msg="message" @changeStates="change"></child>
</div>
</template>
//子组件
<template>
{{msg}}
<button @click="clickHandle">点击</button>
</template>
<script>
import Child from "./Child";
export default {
name: "Child",
props: ["msg"],
components:{
Child
},
methods: {
clickHandle() {
this.$emit("changeStates", "变了");
}
}
};
</script>
②ref
ref如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。
//父组件
<template>
<div>
<child ref="son"></child>
</div>
</template>
//子组件
<template>
<div>
<input ref="inputRef" type="text" value="hi"></child>
</div>
</template>
// 获取子组件的属性和方法
//获取子组件input框的内容
this.$refs.son.$refs.inputRef.value
③children
直接使用,但是建议少用
父组件:
<template>
<div>
<child></child>
</div>
</template>
<script>
export default {
name: 'parent',
data: () => ({
msg: "Hi, I am your father"
}),
mounted() {
console.log(this.$children[0].msg)
}
}
</script>
子组件:
<template>
<div>Hello Boy</div>
</template>
<script>
export default {
name: "child",
data: () => ({
msg: "hi"
}),
mounted() {
console.log(this.$parent.msg); //
}
};
</script>
④EventBus中央数据总线,on
通过创建一个空的vue实例作为中央事件总线,用于触发和监听事件,类型于addListener
⑤listeners隔代组件通信
$attrs 里存放的是父组件中绑定的非 Props 属性;
$listeners里存放的是父组件中绑定的非原生事件;
// 父组件
<template>
<div>
<child-a
:name="name"
:age="age"
:job="job"
title="This is a title"
@click="post"
></child-a>
</div>
</template>
// 子组件child-a中
<template>
<div>
<child-b v-bind="$attrs" v-on="$listeners"></child-b>
</div>
</template>
this.$attrs
// {name: "name", age: "28", job: "worker", title: "This is a title"}
this.$listeners.click()
// hello
// 子组件child-b中
<template>
<div>
<p>B-listeners: {{ this.$listeners.click() }}</p>
</div>
</template>
<script>
export default {
props: ["name"], // name 作为props属性绑定
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
created() {
console.log(this.$attrs);
// {age: "28", job: "worker", title: "This is a title"}
console.log(this.$listeners.click()); // hello
}
};
</script>
⑥provide和inject
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。
provide 和 inject 主要在开发高阶插件/组件库时使用
除此之外,还有vuex状态管理,如下所示
11. Vuex状态管理
vuex类似于react的redux,当开发大型单页应用,多个视图组件依赖同一个状态,比如登录状态,用户信息等。当状态更新时候,这些使用过状态的组件都要更新。针对这种情况,可以使用vuex来解决。
特点:
(1)状态存储是响应式的,组件从store中读取状态,如果状态发生变化,则会自动更新到组件
(2)不能直接修改store中的状态
使用方式:
import vue from 'vue';
import vuex from 'vuex';
const store = new Vuex.Store({
state: {},
getters: {}
mutations: {},
actions: {}
})
// 通过根组件直接注入,这样所有的组件都可以使用store
new Vue({
e1: "#app",
store
})
组成部分:
-
state
作为模块唯一数据源,包含了所有的状态,访问state方式:
this.$store.state.xxx
mapState简化方式访问:
import { mapState } from 'vuex';
export default {
computed: mapState({
count: state => state.count,
myCount: 'count',
// 和当前的数据操作
add(state) {
return state.count + this.nowCount
}
})
}
store中的变量名和当前组件中要使用的变量名相同
import { mapState } from 'vuex';
export default {
computed: mapState([
'count'
])
}
最常用的就是对象展开符的方式:
import { mapState } from 'vuex';
export default {
computed: {
...mapState([
'count'
])
other(){}
}
}
-
getters
当我们不满足直接使用store中state数据,比如筛选计算等处理,如果多个组件都需要这种,那么最好的方式是在store中定义:
getters: {
toDo : state => {
return state.toDo.filter(i => i.toDoThings)
}
}
视图中访问方式:
this.$store.getters.toDo
mapGetters简化的方式:
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'tODo',
])
}
}
//访问方式: this.tODo
-
mutation
这个用于修改state属性,调用的必须是同步的事件
mutations: {
increment(state, num) {
state.count = state.count + num
}
}
调用方式:
this.$store.commit('increment', 10)
mapMutations简化方式:
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
-
action
类似于mutation,可以提交mutation修改state
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
//或者通过参数解构
actions: {
increment ({ commit }) {
commit('increment')
}
}
})
处理异步的业务
actions: {
increment ({ commit, state }, payload) {
// ...
}
}
触发:
this.$store.dispatch('increment')
mapAtions简化方式
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}