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 += 2
setTimeout(()=>{
commit('add')
},1000)
}
}
6、vuex的持久化存储
vuex
不是持久化存储,需要用到插件 vuex-persistedstate
npm install vuex-persistedstate --save
import 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.localStorage
paths: ['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 = 12
this.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>