Vue2 常见知识点(一)
一、Vue2篇
1. 关于生命周期
1.1 生命周期有哪些?
Vue2.x系统自带有8个
beforeCreate // 无data 无dom
created // 有data 无dom
beforeMount // 有data 无dom 有虚拟dom
mounted // 有data 有dom
beforeUpdate
updated
beforeDestroy
destroyed
beforeCreate 数据对象(data)已经初始化,但是还不能访问数据中的属性。这是因为 Vue 实例的代理(将data中的属性代理到this上)还没有完成
beforeMount 在这个阶段,可以访问到虚拟 DOM 相关的内容,但实际的页面 DOM 中还没有这个组件的节点。
发送请求在created还是mounted?
题具体要看项目和业务的情况了,
如果我们的业务是父组件引入子组件,并且优先加载子组件的数据,那么在父组件中当前的请求要放mounted中,
如果当前组件没有依赖关系那么放在哪个生命周期中请求都是可以的。
1.2 beforeCreate和created有什么区别?
为什么发送请求不在beforeCreate里?
如果请求是在methods封装好了,在beforeCreate调用的时候,beforeCreate阶段是还拿不到methods里面的方法的(会报错了)。
1.3 在created中如何获取dom
1. 只要写异步代码,获取dom是在异步中获取的,就可以了。
例如:setTimeout、请求、Promise.xxx()等等...
2. 使用vue系统内置的this.$nextTick
1.4 一旦进入组件会执行哪些生命周期?
beforeCreate
created
beforeMount
mounted
1.5 加入keep-alive会执行哪些生命周期?
如果使用了keep-alive组件,当前的组件会额外增加2个生命周期(系统8 + 2 )
activated
deactivated
如果当前组件加入了keep-alive第一次进入这个组件会执行5个生命周期
beforeCreate
created
beforeMount
mounted
activated
1.6 第二次或者第N次进去组件会执行哪些生命周期?
如果没有加入keep-alive
beforeCreate
created
beforeMount
mounted
如果当前组件加入了keep-alive,只会执行一个生命周期
activated
1.7 父组件引入子组件,那么生命周期执行的顺序是?
父:beforeCreate、created、beforeMount
子:beforeCreate、created、beforeMount、mounted
...
父:mounted
1.8 生命周期有哪些使用场景?
beforeCreate
这个阶段实例刚刚被创建,数据观测(data)和事件机制(events)都还没有被初始化。
它主要用于一些初始的加载动作
由于此时组件的数据,方法,dom都不可用,所以使用场景相对较少。
created
实例已经创建完成,数据观测(data)和事件机制(events)都已经初始化,
但是模板(template)还没有被挂载到 DOM 中。
它常用于初始化数据,发送异步请求获取组件所需的数据。
例如,在一个用户信息展示组件中,可以在这个阶段发送请求获取用户数据,
然后将数据赋值给组件的data属性,当组件挂载后就可以直接渲染数据。
beforeMount
在模板编译之后,挂载开始之前被调用。
此时虚拟 DOM 已经创建完成,但是还没有真正渲染到真实 DOM 中。
这个阶段可以对即将挂载的 DOM 进行最后的修改或者检查。
例如,可以在这个阶段检查生成的虚拟 DOM 结构是否符合预期,或者对一些特殊的 DOM 属性进行预处理。
mounted
组件已经挂载到 DOM 中,真实 DOM 已经渲染完成。
这是最常用的生命周期之一,通常用于操作 DOM 元素,比如获取 DOM 节点的高度、宽度等
例如,在一个图表组件中,挂载完成后可以获取图表容器的尺寸,然后初始化图表库
beforeUpdate生命周期
当数据发生变化,但是 DOM 更新还没有开始的时候被调用。
这个阶段可以用于在 DOM 更新之前获取旧的数据或者状态,或者进行一些数据对比等操作。
例如,在一个数据表格组件中,当数据即将更新时,可以记录下旧的数据,用于后续比较数据的变化情况。
updated生命周期
DOM 已经根据新的数据更新完成。
可以在这个阶段检查 DOM 更新后的状态是否符合预期,或者执行一些依赖于更新后的 DOM 的操作。
但是要注意,在这个阶段避免再次修改数据,否则可能会导致无限循环更新。
例如,在一个动画组件中,当 DOM 更新完成后,可以触发动画的播放。
beforeDestroy生命周期
在实例销毁之前被调用。
这个阶段可以用于清理一些定时器、取消订阅事件等操作,以防止内存泄漏。
例如,如果组件中有一个定时器在不断更新数据,在组件销毁之前需要清除这个定时器。
destroyed生命周期
实例已经销毁,所有的子组件也都已经被销毁。
这个阶段通常用于一些最后的清理工作或者记录组件销毁的状态。
不过在实际应用中,beforeDestroy已经完成了大部分的清理工作,destroyed使用场景相对较少。
例如,关闭页面记录视频播放的时间,初始化的时候从上一次的历史开始播放
2. 关于组件
2.1 组件传值(通信)的方式
1. 父组件引入子组件,绑定数据 父传后代 ( 后代拿到了父的数据 )
<List :str1='str1'></List>
子组件通过props来接收
props:{
str1:{
type:String,
default:''
}
}
***这种方式父传子很方便,但是父传给孙子辈分的组件就很麻烦(父=》子=》孙)
这种方式:子不能直接修改父组件的数据
2. 子组件直接使用父组件的数据
子组件通过:this.$parent.xxx使用父组件的数据
这种方式:子可以直接修改父组件的数据
3. 依赖注入provide和inject
优势:父组件可以直接向某个后代组件传值(不让一级一级的传递)
这种方式可以跨越多个组件层级来共享数据,而不需要通过层层传递props的方式。
**provide:当有一些全局的数据或方法,需要在多个深层次的子孙组件中使用时**
export default {
provide() {
return {
userInfo: this.userData
};
},
data() {
return {
userData: {
id: 1,
name: 'John'
}
};
}
};
**inject:子孙组件如果需要使用祖先组件提供的数据或方法,就可以使用inject**
export default {
inject: ['userInfo'],
mounted() {
console.log('用户名:', this.userInfo.name);
}
};
后代传父 (父拿到了后代的数据)
1. 子组件传值给父组件
子组件定义自定义事件 this.$emit
2. 父组件直接拿到子组件的数据
<List ref='child'></List>
this.$refs.child
平辈之间的传值 ( 兄弟可以拿到数据 bus )
通过新建bus.js文件来做
bus(事件总线)是一种组件间通信的方式,它本质上是一个空的 Vue 实例。通过这个实例,可以在不同组件之间传递事件和数据。
bus:
import Vue from 'vue';
const eventBus = new Vue();
export default eventBus;
兄弟1
import eventBus from './eventBus.js';
export default {
methods: {
handleClick() {
eventBus.$emit('custom-event', data);
}
}
};
兄弟2
import eventBus from './eventBus.js';
export default {
mounted() {
eventBus.$on('custom-event', (data) => {
console.log('收到事件,数据为:', data);
});
}
};
在 Vue 中,每个 Vue 实例都有一套完整的事件系统。
当创建一个空的 Vue 实例作为事件总线(bus)时,这个实例也具备事件触发( e m i t )和事件监听( emit)和事件监听( emit)和事件监听(on)的功能。
2.2 父组件如何直接修改子组件的值
<List ref='child'></List>
this.$refs.child.xxx = 'yyyy';
2.3 子组件如何直接修改父组件的值
子组件中可以使用:this.$parent.xxx去 修改
2.4 如何找到父组件
this.$parent
2.5 如何找到根组件
this.$root
2.6 keep-alive
keep-alive是用來缓存当前组件
2.7 slot/插槽
匿名插槽 :插槽没有名字,向组件内部的某个特定位置整个插入内容
具名插槽 :插槽有名字,为了解决组件中需要在不同位置插入不同内容的情况
<template>
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
<HeaderFooter>
<template v - slot:header>
<h1>这是头部标题</h1>
</template>
<p>这是主体内容</p>
<template v - slot:footer>
<p>这是底部版权信息</p>
</template>
</HeaderFooter>
作用域插槽 : 传值 ,在一个组件内部有数据,但是不同的使用场景需要不同的展示方式。
作用域插槽 核心问题:在一个组件内部有数据,但是不同的使用场景需要不同的展示方式。
以表格组件为例详细说明:
我们创建了一个TableComponent.vue,这个组件内部通过dataList存储了一些数据,就像一个小数据库。它的任务是把这些数据以表格的形式展示出来。但是,对于表格中的每一个单元格,展示方式可能有多种。
比如日期这一列,在一个场景下,可能只需要简单地把日期中的 “-” 换成 “/” 来展示。在TableComponent.vue的模板中,通过具名插槽date()把日期数据传递出去。
然后在父组件中,使用v - slot:date="slotProps"接收这个数据。slotProps是一个对象,里面包含了从子组件传递过来的数据(在这里是value,即日期数据)。然后就可以在父组件的模板中通过{{ slotProps.value.split(‘-’).join(‘/’) }}对日期数据进行处理并展示。
在另一个场景下,可能需要在日期后面添加时间并且设置颜色。同样是通过v - slot:date="slotProps"接收数据,然后在父组件的模板中通过{{ slotProps.value +’ 00:00:00’ }}来处理和展示数据。这样,同一个表格组件就可以根据不同的父组件需求,灵活地展示数据。
2.8 provide/inject
provide/inject ===> 依赖注入 这种方式可以跨越多个组件层级来共享数据,而不需要通过层层传递props的方式。
2.9 如何封装组件
涉及 :slot、组件通信、
自定义标签样式, 传递不同颜色,不同文字,点击后出发父级事件
自定义水印组件, 传入位置,文字,颜色
3. 关于Vuex
3.1 Vuex有哪些属性
state ==> 全局共享属性
getters ==> 针对于state数据进行二次计算
mutatioins ==> 存放同步方法的
actions ==> 存放异步方法的,并且是来提交mutations
modules ==> 把vuex再次进行模块之间的划分
3.2 Vuex使用state值
this.$store.state.xxx
辅助函数:mapState
以上俩种方式都可以拿到state的值,那么区别是什么?
使用this.$store.state.xxx是可以直接修改vuex的state数据的
使用辅助函数的形式,是不可以修改的
3.3 Vuex的getters值修改
组件使用了getters中的内容,组件使用采用v-model 的形式会发生什么?
getters是不可以修改的,(v-model会报错)
3.4 Vuex的mutations和actions区别
相同点:mutations和actions都是来存放全局方法的,这个全局方法return的值拿不到
区别:
mutations ==》 同步
actions ==》 返回的是一个Promise对象,他可以执行相关异步操作
mutations是来修改state的值的,actions的作用是来提交mutations,如需修改数据,actions内部调用mutations
3.5 Vuex持久化存储
在页面使用了state值:1,然后把1修改成2,然后刷新页面又回到了1
Vuex本身不是持久化存储的数据。
Vuex是一个状态管理仓库(state:全局属性)==》就是存放全局属性的地方。
实现持久化存储:1. 自己写localStorage 2. 使用vuex-persistedstate插件
const store = new Vuex.Store({
state,
mutations,
actions,
/* vuex数据持久化配置 */
plugins: [
createPersistedState({
// 存储方式:localStorage、sessionStorage、cookies
storage: window.sessionStorage,
// 存储的 key 的key值
key: "store",
render(state) {
// 要存储的数据:本项目采用es6扩展运算符的方式存储了state中所有的数据
return { ...state };
}
})
]
});
[插件使用方法](https://www.xuexiluxian.cn/blog/detail/dae4073b07144d3c9abb3e2cc8495922)
4. 关于路由
4.1 路由的模式和区别
路由的模式:history、hash
Hash 模式: http://example.com/#/page1
在前端路由中,hash模式是基于 URL 的hash部分(即#后面的内容)来实现页面的切换。
浏览器在加载带有hash的 URL 时,不会向服务器发送请求获取新的资源(对于hash部分的变化),因为hash部分被浏览器视为页面内部的定位符。
当在hash模式下进行路由切换时,浏览器的地址栏会显示带有hash的完整 URL,并且可以通过window.location.hash在 JavaScript 中获取当前的hash值。
这使得用户能够直观地看到路由的变化路径,并且可以通过手动修改hash值来触发路由的切换。
History 模式: http://example.com/page1
history模式则是利用 HTML5 的History API来实现页面的切换。
在这种模式下,当进行路由切换时,浏览器会向服务器发送请求,请求新的页面资源。
然而,在单页应用(SPA)的开发中,我们通常不希望服务器对这些路由请求进行实际的页面渲染,因为单页页面内容是通过 JavaScript 动态加载和渲染的。
所以,需要在服务器端(nginx)进行配置,让服务器对于这些请求返回相同的index.html文件,然后由前端 JavaScript 来处理具体的路由展示。
如果服务器没有进行正确的配置,当用户直接访问http://example.com/page2这样的 URL 时,可能会出现 404 错误,
因为没有正确的服务器配置,浏览器可能无法正确加载页面内容。
区别:
1. 如果找不到当前页面
history会给后端发送一次请求
hash不会
3. 关于项目打包前端自测问题
hash是可以看到内容的
history默认情况是看不到内容的
4. 关于表象不同
hash: #
history: /
4.2 子路由和动态路由
清晰的层次结构,便于权限管理:
const routes = [
{
path: '/product - detail',
component: ProductDetailComponent,
children: [
{
path: 'description',
component: ProductDescriptionComponent
},
{
path: 'reviews',
component: ProductReviewsComponent
},
{
path: 'purchase - options',
component: ProductPurchaseOptionsComponent
}
]
}
];
动态路由是指路由的路径部分是动态变化的,通常包含一个或多个参数,高度灵活性与数据驱动
const routes = [
{
path: '/article/:articleId',
component: ArticleDetailComponent
}
];
4.3 路由传值
this.$router.push({
path: '/user/' + userId
});
可以通过$route.params.userId来获取传递过来的参数值
this.$router.push({
path: '/product - detail',
query: {
id: productId,
color: productColor
}
});
在目标组件中,可以通过$route.query.id和$route.query.color来获取相应的查询
const routes = [
{
path: '/admin - page',
component: AdminPageComponent,
meta: {
requiresAuth: true,
permissionLevel: 'admin'
}
}
];
在全局导航守卫或者其他需要获取路由权限信息的地方,
可以通过$route.meta.requiresAuth和$route.meta.permissionLevel来获取这些信息,用于权限验证等操作。
4.4 导航故障
官网说明:https://v3.router.vuejs.org/zh/guide/advanced/navigation-failures.html#%E6%A3%80%E6%B5%8B%E5%AF%BC%E8%88%AA%E6%95%85%E9%9A%9C
解决:
import VueRouter from 'vue-router'
const routerPush = VueRouter.prototype.push
VueRouter.prototype.push = function (location) {
return routerPush.call(this, location).catch(error => error)
}
// 重写:.catch(error => error)
4.5 r o u t e r 和 router和 router和route区别
router用于路由跳转和管理,例如使用
this.router.push('home')
进行页面跳转。
route表示的是当前页面的路由信息: 用于获取当前的路由信息,例如获取当前路径、参数、查询字符串等
4.6 导航守卫
1. 全局守卫 用的不多
beforeEach 路由进入之前
afterEach 路由进入之后
2. 路由独享守卫
beforeEnter 路由进入之前
const routes = [
{
path: '/discounted - products',
component: DiscountedProductsComponent,
beforeEnter: (to, from, next) => {
const isPromotionPeriod = checkPromotionPeriod();
if (isPromotionPeriod) {
next(); // 展示页面
} else {
next('/');// 跳转首页
}
}
}
];
3. 组件内守卫
beforeRouteEnter 路由进入之前
beforeRouteUpdate 路由更新之前
beforeRouteLeave 路由离开之前
export default {
data() {
return {
formData: {}
};
},
beforeRouteLeave(to, from, next) {
saveFormData(this.formData);
next();// 表示执行
}
};
5. 关于API
5.1 $set
你有没有碰到过,数据更新视图没有更新的问题==》$set
this.$set(target,key,修改后的值)
5.2 $nextTick
$nextTick返回的参数[函数],是一个异步的。功能:获取更新后的dom
源码|原理:
$nextTick( callback ){
return Promise.resolve().then(()=>{
callback(); // 内部代码异步执行 dom渲染后执行
})
}
5.3 $refs
来获取dom的 this.$refs.xxx 与document.getElementById ......相同
5.4 $el
$el 获取当前组件的根节点 $root 是项目根节点
5.5 $data
$data 获取当前组件data数据的
5.6 $children
$children 获取到当前组件的所有子组件的
5.7 $parent
找到当前组件的父组件,如果找不到返回自身
5.8 $root
找到根组件
5.9 data定义数据
数据定义在data的return内和return外的区别:
1. return外:单纯修改这个数据是不可以修改的,因为没有被get/set
2. return内:是可以修改的
5.10 computed计算属性
computed计算属性的结果值,可以修改,需要通过get/set写法
当前组件v-model绑定的值是computed来的, 可以修改,需要通过get/set写法
fullName: {
get() {
return this.firstName +'' + this.lastName;
},
set(newValue) {
const names = newValue.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
}
}
5.11 watch
watch:{
obj:{
handler(newVal,oldVal){
console.log( 'obj',newVal , oldVal )
},
immediate:true, // 初始化第一次就触发
deep:true // 深度监听
},
}
5.12 methods和computed区别
computed是有缓存机制的,没变化不调用,
methods是没有缓存机制的(调用几次执行几次)
6. 关于指令
6.1 如何自定义指令
全局:
Vue.directive('demo', {
inserted: function (a,b,c) {
console.log( a,b,c );
}
})
局部:
<script>
export default {
directives: {
demo: {
bind: function (el) {
console.log( 1 )
}
}
}
}
</script>
6.2 vue单项绑定
双向绑定:v-model
单项绑定:v-bind => :msg="msg"
6.3 v-if和v-for优先级
vue2中:v-for > v-if
vue3中:v-if > v-for 性能更好