Vue
MVVM
Model- 模型 :js - data中的数据
view- 视图: dom - 模板
viewmodel -视图模型: vue实例对象
生命周期
- vue2和vue3生命周期的区别
vue2 -optionsAPI
new Vue()
↓ //初始化 数据代理还未开始
beforeCreate
↓ //初始化 完成数据监测、数据代理
created
↓ //解析模板,生成虚拟DOM,无法显示解析后的页面
beforeMount //无法操作DOM
↓ //将内存中的 虚拟DOM 转为真实DOM插入页面($el备份)
mounted //初始化结束 挂载流程结束
↓ //数据更新
beforeUpdate //数据是新的,页面是旧的
↓ //生成新虚拟DOM与旧的虚拟DOM进行比较
updated
↓ //销毁实例 this.$destroy()
beforeDestroy //所有指令仍然有效 但数据更新不会触发页面更新
↓
destroyed //所有自定义事件无效(DOM原生事件仍然有效如@click)
其他生命周期钩子
$nextTick
vue批量异步渲染
$nextTick会在vue异步渲染执行之后再执行,就能获取dom元素属性更新后的数据值
activated/deactivated
路由独有的生命周期钩子,当组件处于可见状态触发 activated
当组件处于不可见状态触发 deactivated
vue3 -compositionAPI(新增)
数据代理
Object.defineProperty双向绑定数据
let number = 18
let person = {
name:'',
sex:''
}
Object.defineProperty(person,'age',{
value:19,
enumerable:true,//是否可枚举,默认为false(枚举:能否被遍历获得)
writable:true,//是否可被修改,默认为false
configurable:true,//是否可被删除,默认为false
get(){
//age属性被读取
//get必须有返回值
return number //将age绑定成number数据
}
set(value){
//age属性被修改
number = value //修改number数据形成双向绑定
}
})
数据代理: 通过一个对象代理对另一个对象中属性的操作
obj2能够修改obj1中的属性数据
let obj = {x:111};
let obj = {y:222};
Object.defineProperty(obj2,'x',{
get(){
return obj.x
}
set(value){
obj.x = value
}
})
vue中的数据代理:
vue通过vm对象将data对象中的属性进行代理
vm._data = data
将_data
中的所有属性通过```Object.defineProperty``添加getter和setter添加到vm上
相当于获取data中数据时如果没有数据代理:
{{_data.name}}
有了数据代理
{{name}} //name直接被添加到vm对象上成为了一个属性
vue中的数据劫持:
_data中的数据进行数据劫持,对数据的变化进行监听,才能做到修改数据,页面上跟着改变
事件修饰符
@click.prevent //阻止默认事件
@click.stop //阻止事件冒泡
@click.once //事件只能触发一次
计算属性computed
有缓存:只有在相关的变量发生改变时才会重新执行,其他无关值的改变不会影响
与v-model配合使用时需要定义set(),否则出错
//默认存在get
double(){
return this.number * 2
}
//相当于
double: {
get() {
return this.number * 2
}
}
//与v-model绑定时由于v-model是双向绑定需要定义set()
double:{
get(){
return this.number * 2
},
set(value){
this.number = value / 2
}
}
监听器 watch
watch在监听对象时需要深度监听
data() {
return{
name: 'name',
info: {
hobby: 'hobby'
}
}
}
watch:{
//监听基本类型
name(newValue, oldValue) {
console.log(...)
}
//监听对象
info: {
handler: function(newValue){
console...
},
deep:true //深度监听
}
}
两者的区别
1.computed能完成的功能,watch都能完成
2.反之不一定,watch适用进行异步操作
插值表达式语法
//{{ }}只要vue实例中的属性都可以写在插值中
<p>{{ number }} </p>
<p>{{ number + 1}} </p>
<p>{{ flag ? 'yes' : 'no'}} </p>
<p>{{ name.split('').reverse().join('')}}<p>
内置指令
v-bind //:
v-model //双向绑定
v-for //遍历
v-on //@
v-if/v-else //节点是否存在
v-show //节点是否展示
v-text/v-html
v-cloak//在vue接管的瞬间被删除
v-once//所在节点初始化后,就是静态内容,不会随着数据更新而改变
v-pre//所在节点跳过编译过程,使用在没有指令语法的节点,可以加快编译
- v-model数据双向绑定
<input type="text" v-model="message" />
//等价于
<input type="text" :value="message" //message改变时value也改变
@input="valueChange" /> //输入value改变时message更新
data(){
return{
message:''
}
}
methods:{
valueChange(event) {
this.message = event.target.value
}
}
-
v-html安全问题
容易导致xss攻击(冒充用户值守)
不能用在用户提交的内容上 -
v-cloak
当遇到js阻塞时,为了防止在vue接管前就已经渲染的画面上出现类似{{xxx.name}}
这样未编译的内容
给对应节点添加v-cloak
并给v-cloak
添加样式
<style>
[v.cloak]{display:none;}
</style>
<body>
<h2 v-cloak>{{name}}</h2>
</body>
-
v-if 和 v-show的区别
v-if 只渲染符合条件的元素,不符合的不渲染
v-show两者都渲染,不符合条件的元素设置为display:none
v-if 与 v-for 不要在一个元素中连用,v-for的执行优先级高于v-if -
v-for 遍历
//遍历数组
array: [
{id:2,option:1},
{id:1,option:1},
]
<li v-for="(item, index) in array" :key="item.id">
{{index}} - {{item.option}}
</li>
//遍历对象
obj:{
name:'',
hobby:''
}
<li v-for="(value, key, index) in obj" :key="key">
{{index}} - {{key}} - {{value}}
</li>
key属性
给节点一个标识
不推荐给key赋值index,效率低
eg.当一组数据,给数据最前方增加一个数据
新增数据变成index为0的节点
vue将更新前后虚拟dom的index:0对比,没法分辨真实dom中的input输入,造成更新后错位,且所有节点无法复用,造成效率低
ref属性
原生js的id替代品获取DOM
this.$ref.name
对于组件而言,id获取的是真实DOM元素
而ref是vc实例
父子通信
父→ 子 :props
//App.vue
<children :list = "list"></children>
data() {
return{
list:[]
}
}
//Children.vue
props:{
list:Array
}
子→父 :emit
//children.vue
data() {
return{
title:''
}
}
add(){
this.$emit('add', this.title)
}
//App.vue
<children @add="add" />
methods:{
add(){console.log(title)}
}
兄弟通信
$Eventbus
//event-bus.js
import Vue from 'vue';
const eventBus = new Vue(); //创建新的实例
export default eventBus;
//List.vue
import eventBus from './event-bus'
export default {
mounted(){
eventBus.$on('funcname',console.log(title)) //接收
}
}
//input.vue
import eventBus from './event-bus'
export default {
data(){
return{
title:''
}
}
mounted(){
eventBus.$emit('funcname', this.title) //接收
}
}
消息订阅与发布
pubsub-js
插槽slot
作用域插槽:在父组件调用子组件slot时,使用子组件变量数据赋值
//Children.vue
<div>
<slot :title="title">default<slot>
</div>
data() {
return{
title: {
id:'',
title:''
}
}
}
//App.vue
<children>
<template v-slot:default="props">
{{props.title.id}}
</template>
<children>
具名插槽
<slot name ="name"></slot>
<template v-slot:name></template>
动态组件
不确定需求加载组件顺序时,动态绑定组件
<component :is="组件名"></component>
hash & histroy
location.hash = 'aaa'
history.pushState({},'','aaa')//前进到一个新网页,可以后退
history.replaceState({},'','aaa')//替换前一个网页,不能后退
history.back() = history.go(-1) //后退
history.forward() = history.go(1) //前进
路由v-router
- 懒加载:用到的时候再加载对应界面,提高页面加载效率
将路由对应的组件打包成一个一个js代码块
异步请求:
第一次请求时不希望把所有的js都请求下来,而是用到的时候再请求
通过改变导入组件的方式
//同步方式
import Test from './components/Test';
...
components:{
Test
}
//异步方式
components: {
Test:() => import('./components/Test');
}
-
$route & $router
$router
全局路由对象
$route
当前处于活跃的路由this.$router.push('/home')//跳转 this.$router.push({ path:'/profile', query:{name:''}//传值 }) $route.query.id//取到传送的值
-
全局守卫
全局监听路由跳转router.beforeEach((to, from, next) =>{ //前置钩子 document.title = to.meta.title next()//必须执行 next('/login')//满足某种条件比如未登录强制跳转登录界面等 }) router.afterEach((to, from) =>{ //后置钩子 })
独享守卫
beforeEnter
组件内守卫beforeRouteEnter
-
keep-alive
组件缓存
缓存渲染后的结果,并不销毁
mixin 抽离公共逻辑
//mixin.js
export default{
data(){
return {
commonData:''
}
},
method:{},
mounted:{}
}
//component.vue
import mixin from './mixin'
export default{
mixins:[mixin],
data(){}
}
<div>{{commonData}}</div>
vuex插件
实现集中式状态管理
多组件共享数据(全局单例模式)
不要直接修改state里的数据,而是通过actions/mutations的流程进行修改,以便devtools进行监控
//store/index.js
const store = new Vuex.store{
state:{//变量 //单一状态树
x:1,
},
mutations:{ //同步操作
mufunc(state, count){
state.x += count
}
},
actions:{ //异步操作
acUpdate(context, payload){
return new Promise((resolve,reject) =>{
setTimeout(()=>{
context.commit('mufunc')//下一步提交给mutation
console.log(payload)
resolve()
})
})
}
},
getters:{ //类似于computed
return x * x
},
module:{ //将store分割成模块
}
}
//App.vue
methods:{
add(count){
this.$store.commit('mufunc', count)
}
multi(payload){
this.$store.dispatch('acUpdate',payload)
.then(res =>{})
}
}
- state单一状态树:只定义一个store
- module分割成模块: