vue2
生命周期
beforeCreate
:无data、methods、dom
created
:有data、methods,无dom
beforeMount
:有data,无dom
mounted
:有data,有dom
beforeUpdate
updated
deforeDestroy
destroy
:记录用户浏览、播放记录
keep-alive缓存的组件
activated
: 进入时会执行,mounted之后
deactivated
:离开时会执行(销毁)
进入组件执行顺序
beforeCreate,created,beforeMount
,
子组件 ->[beforeCreate,created,beforeMount,mounted
]
mounted
activated
(keep-alive):判断id,避免重复请求
组件通信
父传子
1、组件绑定,props取值(不可修改,优:传子方便,缺:不能传孙)
2、直接获取 (dom可直接使用,可修改,缺:复杂)
this.$parent.xxx
this.$parent.$parent.xxx
3、依赖注入:(优:子孙都可用,缺:数据来源不好定位)
//父组件
provide(){return {val: 'xxx'}
}
//子,孙组件
inject:['val']
子传父
1、$emit
,$on
(不可修改)
//父组件
created(){this.$on('handle1',val => {console.log(val)})
}
//子组件
this.$emit('handle1',val)
2、直接获取(dom不可直接使用,优:可修改,缺:复杂,不常用)
this.$children[0].xxx
this.$children[1].xxx
3、ref
(dom不可直接使用,需定义变量赋值,优:可修改)
//父组件
<child ref='child'></child>this.$refs.child.val
传兄弟
1、eventBus
//main.js
Vue.prototype.$Bus = new Vue()
//list1
created(){this.$Bus.$on('handle1',val => {console.log(val)})
}
destroyed(){// 移出监听this.$Bus.$off('handle1')
}
//list2
this.$Bus.$emit('handle1',val)
插槽
一般组件封装时使用
1、匿名插槽:所有内容都会插入(会带有隐含的名字default)
//父组件
<child><p>这是一些内容。</p>
</child>
//子组件
<template><div><slot></slot></div>
</template>
2、具名插槽:通过slot名来指定内容插入
v-slot:header可简写为 #header
// 父组件
<child><template v-slot:header><p>这是头部内容。</p></template><p>这是默认插槽的内容。</p><template #footer> // v-slot:footer的简写<p>这是底部内容。</p></template>
</child>
// 子组件
<template><div><slot name="header"></slot><slot></slot><slot name="footer"></slot></div>
</template>
3、作用域插槽:子组件可以通过:varName="value"传递数据给父组件,父组件通过v-slot:slotName="slotProps"来接收
// 父组件
<child><template v-slot:user="slotProps"><p>用户名: {{ slotProps.user.name }}</p><p>年龄: {{ slotProps.user.age }}</p></template>
</child>
// 子组件
<template><div><slot name="user" :user="userInfo"></slot></div>
</template>
<script>
export default {data() {return {userInfo: { name: '张三', age: 30 }};}
};
</script>
组件封装
需要介绍到:父子互传值,插槽的使用
Vuex
1、属性
state
:全局共享属性
getters
:state的计算属性
mutations
:同步方法
actions
:异步方法提交mutations
modules
:vuex的模块划分(更好维护)
2、使用
1、this.$store.state.val
:可直接修改值
2、辅助函数:不可直接修改值(映射)
import {mapState,mapGetters} from 'vuex'
computed:{...mapState([val]),...mapGetters([val1])
}
3、getters不可修改
4、module
const moduleA = {state: () => ({ ... }),mutations: { ... },actions: { ... },getters: { ... }
}
const moduleB = {state: () => ({ ... }),mutations: { ... },actions: { ... }
}
const store = new Vuex.Store({modules: {a: moduleA,b: moduleB}
})store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
...mapState('a', {state: state => state
})
...mapState({val: state => state.a.val,str: state => state.b.str,a: state => state.a,b: state => state.b
})
5、mutations,actions(return值拿不到)
mutations
:同步,修改state
actions
:异步,返回premise
,提交mutations
//辅助函数
mutations: {add( state ){state.num ++;}
}
actions: {addNum({commit,state}){//state.num += 2setTimeout(()=>{commit('add')},1000)}
}
6、vuex的持久化存储
vuex
不是持久化存储,需要用到插件 vuex-persistedstate
npm install vuex-persistedstate --saveimport Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from 'vuex-persistedstate';Vue.use(Vuex);export default new Vuex.Store({state: {// ...},mutations: {// ...},actions: {// ...},//storage选项可以是localStorage或sessionStorage//localStorage会持久化到浏览器会话结束,而sessionStorage只会持续到用户关闭浏览器标签//也可以通过paths选项来指定需要持久化状态的部分//或者通过reducer选项自定义持久化的逻辑plugins: [createPersistedState({storage: window.sessionStorage, // 或者 window.localStoragepaths: ['moduleA.token','moduleB.username'], // 只持久化特定的状态(推荐)reducer(val){ // 自定义持久化逻辑,例如加密return {...val,token: val.moduleA.token,username: val.moduleB.username}}})]
});
路由
1、模式:hash
和 history
2、子路由和动态路由
3、路由传值
4、导航故障:当前页跳当前页会报错,处理方法是重写router的push方法
import VueRouter from 'vue-router'
// 修改原型对象中的push方法
const routerPush = VueRouter.prototype.push
VueRouter.prototype.push = function push( location ) {return routerPush.call(this, location).catch(err => err)
}
5、$router
和 $route
$router
:路由器对象,用于管理路由,包含 push、replace、go、back、forward、currentRoute、beforeEach、afterEach路由属性和方法
$route
:当前路由对象,用于获取当前路由的信息。包含 path、params、query、hash、name、meta、fullPath等属性和方法
6、导航守卫
全局守卫
:判断登录状态
beforeEach:路由进入之前
afertEach:路由进入之后
单个路由独享守卫
:
beforeEnter:路由进入之前
组件内守卫
:
beforeRouteEnter:路由进入之前
beforeRouteUpdate:路由更新之前
beforeRouteLeave:路由离开之前
//全局 main.js
router.beforeEach((to,from,next)=>{if(to.meta.isAuth){if(localStorage.getItem('user')){next()}else{next('/login')}}else{next()}
})
//独享 router.js
{path: '/userInfo',name: 'userInfo',component: userInfo,meta:{ isAuth: false },beforeEnter:(to,from,next) =>{if(to.meta.isAuth){if(localStorage.getItem('user')){next()}}else{next('/login')}}
}
//组件内 component.vue
beforeRouteEnter (to, from, next) { if (localStorage.getItem('user')) {next()} else {next(vm => {vm.$router.push('/login')})}
}
API
1、$set
:
2、$nextTick
:在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM
//源码
class Vue{constructor(options){//this.$el=document.querySelector(options.el);options.created.bind(this)();options.mounted.bind(this)();this.$el=document.querySelector(options.el);}$nextTick( callback ){return Promise.resolve().then(()=>{callback();}}
})
new Vue({el:'#app',created(){this.$nextTick(()=>{console.log( this.$el);})},mounted(){}
}
3、$el
:获取当前组件根节点
4、$refs
:获取dom
5、$children
:获取当前组件所有子组件
6、$parent
:返回当前组件的父组件,找不到返回自身
7、$root
:返回根组件
8、data
return 内定义的变量是响应式
return 外定义的变量是非响应式
data(){this.val1 = 1 // 响应式return {val2: 2 // 非响应式}
}
9、computed
一般情况不可修改,使用 get set 时可以修改
如果computed的值被v-model绑定,使用 get set 时也可以修改,否则修改时会报错
data(){return {val: 1}
},
computed:{newVal1(){return val + 1},newVal2:{get(){return val + 2},set(v){//修改newVal2时,v赋值给val,再触发get返回计算后的值this.val = v}}
},
methods:{changeVal(){this.val = 10//val = 10,val1 = 11,val2 = 12this.newVal2 = 10//val = 10,val1 = 11,val2 = 12}
}
10、watch
一般初始化不执行,变化时执行
immediate
:初始化执行一次
deep
:深度监听(修改对象或数组内的值)
watch:{str(newval,oldVal){}, //初始化不执行str:{handler(newval,oldVal){},immediate:true, //初始化执行一次deep: true, //深度监听(修改对象或数组内的值)}
}
methods和computed的区别:
computed
:有缓存机制,使用n次只会执行一次,购物车数据
methods
:没有缓存机制,使用几次就会调用几次,每次修改也会调用
watch
:并且支持异步
指令
2和3的区别
全局指令:
局部指令:
知识点
1、v-if
和 v-for
优先级
vue2 中 v-for
> v-if
vue3 中 v-if
> v-for
2、proxyData
函数的实现(双向绑定核心)
注:把 this.$data
中的数据和 this
进行双向绑定,可以在this
中直接读写this.$data
prosyData(){for(let key in this.$data){Object.defineProperty(this,key,{get(){return this.$data[key]},set(val){this.$data[key] = val}})}
}
vue2 和 vue3 的区别
- 双向绑定方法不同
vue2:Object.defineProperty
劫持不到后添加的属性
vue3:new Proxy
可以劫持到后添加的属性 - vue2 + webpack ,vue3 + ts + pinia + vite
- vue2 是选项式API
vue3 是组合式API或者Setup语法糖,可以向下兼容选项式API - vue3中没有this.$set方法(vue3中data没有声明的变量也可以直接响应式的使用)
- v-if 和 v-for 优先级
- ref 和 children
let data = {a:1,b:2}
//vue2
let vueData = {}
for(let key in data){Object.defineProperty(vueData,key,{get(){return data[key]},set(val){data[key] = val}})
}
//vue3
let vueData = new Proxy(data,{get(target, propKey, receiver){return Reflect.get(target, propKey, receiver)},set(target, propKey, value, receiver){return Reflect.set(target, propKey, value, receiver)}
})vueData.c = 3
console.log(vueData.c)
vue3在setup中获取this
import { getCurrentInstance } from 'vue'
let that = getCurrentInstance()
vue3常用的API
1、createApp() 创建应用实例
说明:vue2中 new Vue()
场景:写插件、分装全局组件
2、provide/inject 依赖注入
说明:组件传值
场景:父组件向子组件多层传值
缺点:不好维护,不好定位数据来源
3、directive 自定义指令
说明:自定义指令
场景:后台管理中按钮权限控制
4、mixin 混入
说明:全局、局部混入
场景:添加生命周期
缺点:不好维护,不好定位数据来源
5、app.config.globalProperties
说明:获取vue对象的属性和方法
场景:封装插件时,添加属性和方法到vue对象
6、nextTick
7、computed
8、reactive 和 ref
说明:定义数据,vue2的data
9、watch
说明:监听,vue2不需要深度监听
10、markRaw
说明:静态数据,非响应式数据
11、defineProps()
说明:setup形式,接收父组件传递的值
12、defineEmits()
说明:setup形式,自定义事件
13、slot
说明:匿名、具名、作用域
vue3响应式数据类型
1、ref:定义基本类型
2、reactive:定义复杂类型
3、toRef:解构一个值
4、toRefs:解构多个值
import {ref,reactive,toRef,toRefs} from 'vue'
let sum = ref(10)
let obj = reactive({name:'张三',age: 16
})
let name = toRef(obj,'name')
let {name,age} = toRefs(obj)const btnClick = () => {name.value = '李四'
}
teleport
说明:将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置
场景:一般用来包裹模态框
好处:可以忽略 DOM 结构导致的布局问题,如position,z-index
<script setup>import { ref } from 'vue'const open = ref(false)
</script><template><button @click="open = true">Open Modal</button>//不使用Teleport <div v-if="open" class="modal"><p>Hello from the modal!</p><button @click="open = false">Close</button></div>//使用Teleport <Teleport to="body"><div v-if="open" class="modal"><p>Hello from the modal!</p><button @click="open = false">Close</button></div></Teleport>
</template><style scoped>
.modal {position: fixed;z-index: 999;top: 20%;left: 50%;width: 300px;margin-left: -150px;
}
</style>