前端面试题汇总--Vue2篇
一、Vue2篇
1. 关于生命周期
1.1 生命周期有哪些?发送请求在created还是mounted?
请求接口测试:https://fcm.52kfw.cn/index.php?_mall_id=1&r=api/default/district
Vue2.x系统自带有8个
beforeCreate(无数据,无dom)
created (有数据,无dom)
beforeMount (有数据,无dom)
mounted (有数据,有dom)
beforeUpdate
updated
beforeDestroy
destroyed
发送请求在created还是mounted?
这个问题具体要看项目和业务的情况了,因为组件的加载顺序是,父组件引入了子组件,那么先执行父的前3个生命周期,再执行子的前4个生命周期,那么如果我们的业务是父组件引入子组件,并且优先加载子组件的数据,那么在父组件中当前的请求要房mounted中,如果当前组件没有依赖关系那么放在哪个生命周期中请求都是可以的。
1.2 为什么发送请求不在beforeCreate里?beforeCreate和created有什么区别?
为什么发送请求不在beforeCreate里?
因为:如果请求是在methods封装好了,在beforeCreate调用的时候,beforeCreate阶段是拿不到methods里面的方法的(会报错了)。
beforeCreate和created有什么区别?
beforeCreate没有$data
created中有$datacreated是可以拿到methods的方法的
beforeCreate拿不到methods的方法
1.3 在created中如何获取dom
1. 只要写异步代码,获取dom是在异步中获取的,就可以了。例如:setTimeout、请求、Promise.xxx()等等...
2. 使用vue系统内置的this.$nextTick
1.4 一旦进入组件会执行哪些生命周期?
beforeCreate
created
beforeMount
mounted
1.5 第二次或者第N次进去组件会执行哪些生命周期?
如果当前组件加入了keep-alive,只会执行一个生命周期
activated
如果没有加入keep-alive
beforeCreate
created
beforeMount
mounted
1.6 父组件引入子组件,那么生命周期执行的顺序是?
父:beforeCreate、created、beforeMount
子:beforeCreate、created、beforeMount、mounted
...
父:mounted
1.7 加入keep-alive会执行哪些生命周期?
如果使用了keep-alive组件,当前的组件会额外增加2个生命周期(系统8 + 2 )
activated
deactivated
如果当前组件加入了keep-alive第一次进入这个组件会执行5个生命周期
beforeCreate
created
beforeMount
mounted
activated
1.8 你在什么情况下用过哪些生命周期?说一说生命周期使用场景
created ===> 单组件请求
mounted ===> 同步可以获取dom,如果先子组件请求后父组件请求
activated ===> 判断id是否相等,如果不相同发起请求
destroyed ===> 关闭页面记录视频播放的时间,初始化的时候从上一次的历史开始播放
2. 关于组件
2.1 组件传值(通信)的方式
父传后代 ( 后代拿到了父的数据 )
1. 父组件引入子组件,绑定数据<List :str1='str1'></List>子组件通过props来接收props:{str1:{type:String,default:''}}***这种方式父传子很方便,但是父传给孙子辈分的组件就很麻烦(父=》子=》孙)这种方式:子不能直接修改父组件的数据2. 子组件直接使用父组件的数据子组件通过:this.$parent.xxx使用父组件的数据这种方式:子可以直接修改父组件的数据3. 依赖注入优势:父组件(provide)可以直接向某个后代组件(inject)传值(不让一级一级的传递)
后代传父 (父拿到了后代的数据)
1. 子组件传值给父组件子组件定义自定义事件 this.$emit
2. 父组件直接拿到子组件的数据<List ref='child'></List>this.$refs.child
平辈之间的传值 ( 兄弟可以拿到数据 ) 通过新建bus.js文件来做
bus.$emit("change",this.str) =>自定义事件向bus添加数据
bus.$on("change",val=>{this.str = val}) =>获取bus数据
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/插槽
插槽主要用于封装组件
写法:在需要添加插槽的组件上引入一个插槽组件
,然后在插槽组件
上写solt
站位,在使用插槽组件时添加插槽内容就会在站位处插入对应内容
1、匿名插槽 :插槽没有名字
2、具名插槽 :插槽有名字
//插槽组件<template><div class="hello"><slot name="header"></slot><h1>插槽组件</h1><input type="text" /><slot name="footer"></slot></div>
</template>
//插槽使用<soltList><template v-slot:header><div>header</div></template><template #footer><div>温馨提示</div></template></soltList>
3、作用域插槽 :插槽组件向使用插槽组件传值//slotList.vue<slot name="footer" :arr="arr"></slot>//bb.vue<soltList><template #footer="{ arr }"><div>温馨提示</div>{{ arr[0] }}</template></soltList>
2.8 provide/inject
provide/inject ===> 依赖注入
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是不可以修改的
3.4 Vuex的mutations和actions区别
相同点:mutations和actions都是来存放全局方法的,这个全局方法return的值拿不到
区别:mutations ==》 同步actions ==》 返回的是一个Promise对象,他可以执行相关异步操作mutations是来修改state的值的,actions的作用是来提交mutations,实现异步操作
3.5 Vuex持久化存储 :在页面使用了state值:1,然后把1修改成2,然后刷新页面又回到了1为什么?
Vuex本身不是持久化存储的数据。Vuex是一个状态管理仓库(state:全局属性)==》就是存放全局属性的地方。
实现持久化存储:1. 自己写localStorage 2. 使用vuex-persistedstate插件
插件使用方式:https://www.xuexiluxian.cn/blog/detail/dae4073b07144d3c9abb3e2cc8495922
4. 关于路由
4.1 路由的模式和区别
路由的模式:history、hash
区别:
1. 关于找不到当前页面发送请求的问题history会给后端发送一次请求而hash不会
2. 关于项目打包前端自测问题hash是可以看到内容的history默认情况是看不到内容的
3. 关于表象不同hash:#history:/
4.2 子路由和动态路由
子路由:children
动态路由:path: '/user/:id'
4.3 路由传值
vuex
路劲跳转设置时:
显示:
http:/ / localhost:8080/about?a=1
传:
this.$router.push({path:'/about',query:{a:1}
})
接:
this.$route.query
隐示:
http: // localhost:8080/ about
传:
this.$router.push({name:'About',params:{key:'你好'}
})
接:
this.$route.params
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)
}
4.5 router和router和router和route区别
$router不仅包含当前路由还包含整个路由的属性和方法$route包含当前路由对象
4.6 导航守卫
1. 全局守卫beforeEach 路由进入之前afterEach 路由进入之后2. 路由独享守卫beforeEnter 路由进入之前(登录验证是否已登录,已登录进入对应页面,没登录跳转到登录页面)3. 组件内守卫beforeRouteEnter 路由进入之前beforeRouteUpdate 路由更新之前beforeRouteLeave 路由离开之前
5. 关于API
5.1 $set
面试官:你有没有碰到过,数据(数组、对象)更新视图没有更新的问题==》$set
this.$set(target,key,修改后的值)
(数组、对象)对应(key、value)修改成对应值
5.2 $nextTick
$nextTick返回的参数[函数],是一个异步的。功能:获取更新后的dom源码|原理:
$nextTick( callback ){return Promise.resolve().then(()=>{callback();})
}
5.3 $refs
来获取dom的
5.4 $el
$el 获取当前组件的根节点
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. reutnr内:是可以修改的
5.10 computed计算属性
computed计算属性的结果值,可以修改吗?可以的,需要通过get/set写法当前组件v-model绑定的值是computed来的,那么可以修改吗?可以的,需要通过get/set写法
5.11 watch
watch:{obj:{handler(newVal,oldVal){ console.log( 'obj',newVal , oldVal )},immediate:true,//初始监听deep:true //深度监听(false的话只能监听obj,true的话可以监听obj.xxx)},
}
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
6.3 v-if和v-for优先级
vue2中:v-for > v-if
vue3中:v-if > v-for
7. 关于原理
7.1 $nextTick原理
$nextTick功能:获取更新后的dom
$nextTick( callback ){return Promise.resolve().then(()=>{callback();})}
7.2 双向绑定原理
Vue2 : Object.defineProperty()***后添加的属性是劫持不到的,使用$set解决Vue3 : new Proxy()***即使后添加的也可以劫持到***还不需要循环
8、diff算法
功能:提升性能
虚拟dom ===》其实就是数据( 把dom数据化 )
主流:snabbdom、virtual-dom
snabbdom:https://www.npmjs.com/package/snabbdom
13.1 搭建环境
npm init -y
cnpm install webpack@5 webpack-cli@3 webpack-dev-server@3 -S
cnpm install snabbdom -S
新建webpack.config.js
配置webpack.config.js
13.2 虚拟节点 和 真实节点
虚拟节点:
{children: undefineddata: {}elm: h1key: undefinedsel: "h1"text: "你好h1"
}
真实节点:
<h2>你好</h2>
13.3 新老节点替换的规则
1、如果新老节点不是同一个节点名称,那么就暴力删除旧的节点,创建插入新的节点。
2、只能同级比较,不能跨层比较。如果跨层那么就暴力删除旧的节点,创建插入新的节点。
3、如果是相同节点,又分为很多情况
3.1 新节点有没有children如果新的节点没有children,那就证明新节点是文本,那直接把旧的替换成新的文本
3.2 新节点有children新的有children,旧的也有children ===》就是diff算法的核心了【3.3】新的有children,旧的没有 ===》创建元素添加(把旧的内容删除清空掉,增加新的)
3.3 diff算法的核心(最复杂的情况)1、 旧前 和 新前匹配:旧前的指针++ 、 新前的指针++
2、 旧后 和 新后匹配:旧后的指针-- 、 新后的指针--
3、 旧前 和 新后匹配:旧前的指针++ 、 新后的指针--
4、 旧后 和 新前匹配:旧后的指针-- 、 新前的指针++
5、 以上都不满足条件 ===》查找新的指针++,新的添加到页面上并且新在旧的种有,要给旧的复制成undefined
6、 创建或者删除
***注意:如果要提升性能,一定要加入key,key是唯一标示,在更改前后,确认是不是同一个节点。
9、谈一下MVVM框架
web1.0时代
文件全在一起,也就是前端和后端的代码全在一起
问题:
1、前端和后端都是一个人开发。(技术没有侧重点或者责任不够细分)
2、项目不好维护。
3、html、css、js页面的静态内容没有,后端是没办法工作的(没办法套数据)。
mvc…都是后端先出的
web2.0时代
ajax出现了,就可以:前端和后端数据分离了。
解决问题:后端不用等前端页面弄完没,后端做后端的事情(写接口)、前端布局、特效、发送请求。
问题:
1、html、css、js都在一个页面中,单个页面可能内容也是比较多的(也会出现不好维护的情况)。
出现前端的框架了MVC、MVVM
解决问题:可以把一个“特别大”页面,进行拆分(组件化),单个组件进行维护
什么是MVVM
Model-View-的简写
view : 视图【dom==》在页面中展示的内容】
model:模型【数据层:vue中的data数据】
viewModel:视图模型层【就是vue源码】
10、scoped
一、让css只在当前组件生效<style scoped>...</style>二、scss、stylus(1、为了提高开发效率,使用scss、stylus编写 2、在全局使用swiper等工具库的时候,使用scoped只能修改组件样式,不能修改工具库样式,需要使用穿透)
sCss
:
1.下载
npm install sass-loader node-sass --save
2.
<style lang='scss' scoped>
3.scss 样式穿透
父元素/deep/子元素
stylus: