面试题(第一弹)
1.vue-router路由权限认证如何做?
vue-router做路由权限认证,首先需求来源,根据不同的用户拥有不同的权限,例如:管理员,超级管理员,普通用户,他们登陆成功,所看到的东西是不一样的,所以也引发了不同角色登录成功会出现不同的路由这个话题了。来讲讲具体怎么做吧!!!首先在没有登录之前,可能除了登录页面和注册页面,其他的页面是没有办法进入,的这个时候我们需要使用到vue的路由前置守卫beforeEach了
router.beforeEach((to,from,next) => {
// 如果元信息的信息是需要登录或者前端埋点信息中是未登录状态比如没有登录token
if(!to.meta.isLogin && !token){
// 跳转登录页面
next("/login")
}else {
// 放行
next();
}
})
上面是第一步,如果登录成功之后后端一定会返回一系列的数据,数据里面包含了路由的信息,我们需要在其前端通过addRoutes的方式为前端进行健全
<template>
<div>
<button @click="handleLogin">登录</button>
</div>
</template>
<script>
import axios from "axios"
export default {
methods: {
async handleLogin(){
let result = await axios({
url : "",
method : "POST",
data : {
username,
password
}
});
// 需要传入一个路由的数组
this.$router.addRoutes([])
}
},
}
</script>
<style>
</style>
这样话,登录完成之后我们前端的路由就完整了,不同的角色拥有不同的权限路由。
2.vuex是有哪几个部分组成的(怎么去存储数据)
五大核心:
- action(主要用来处理异步操作的)
- mutation(主要用来将数据赋值给state的)
- state(存储数据的)
- getter(类似于vue的computed的计算属性,可以用于比较复杂的数据计算和双向同步)
- modules(用来分模块的,将每一个对象在树节点上分离,左后通过命名空间进行分离)
如何存储数据?
首先需要我们去dispatch方法给到action,action里面的函数会接受外部传来的payload的值,然后在action中我们再去commit找到mutation中的函数,在这个函数中我们吧在action中处理过的数据拿到存储在state中。
注意:这里面在修改值的时候我们需要注意 一个问题就是,关于vue2.0在修改对象或者修改数组值的时候会出现修改完之后vue无法监听到,我们需要使用深拷贝,扩展运算符或者$set这样的操作来重新让vue监听这个值
getter就是一个缓存属性,我们可以再计算一些比较复杂的数据的时候使用getter,也可以帮我们做到同步计算的效果
modules在分模块的时候是必须,例如:购物车数据和商品数据就是两个不同的模块,我们需要将他分开进行处理,但是在合并节点的时候,为了不发生冲突,我们需要使用命名空间的方式来区分他们。
3.react hooks用过么(如果我们使用hooks的方式开发,我们在第一次需要加载数据的操作应该放在什么hooks里面)
首先react hooks是16.8之后提出来的新特性,只是用函数式编程,抛弃了生命周期钩子,解决类组件生命周期开发的绝大数的问题。
那么在hooks中有一个问题,我们在之前的方式我们第一次发送ajax请求的是在componentDIdMount中,在hooks中我们应该在那个里面去调用ajax的请求,或者初始化的操作呢?这里我们需要引出关于useEffect钩子的作用。useEffect钩子是干什么?
useEffect:
他是一个接收包含式命令,且可能有副作用代码的函数。
在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。
使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。
默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。
因此我们在他里面可以帮我们发送第初始化的请求,同时这个函数还可以帮我们做到何时去重新发送请求这样的优化操作
import React, {useEffect} from 'react'
export default function App() {
/*
第二参数:
不填: 函数里面没有修改useState状态会陷入死循环
填一个空数组: 执行一次
填一个具体的值,函数里面执行的次数是按照具体值是否变化来执行的
*/
useEffect(() => {
// request
}, [])
return (
<div>
</div>
)
}
4.简单的介绍一下vue和react的区别,他们当中都有key这个说法,它具体是做什么的
先说相同点:
- 都是用xuniDOM的技术
- 都使用组件化思想
- 都是响应式,推崇单向数据流
- 都有成熟的社区,都支持服务端渲染
Vue和React实现原理和流程基本一致,都是使用Virtual DOM + Diff算法
。不管是Vue的template模板 + options api写法,还是React的Class或者Function(js 的class写法也是function函数的一种)写法,底层最终都是为了生成render函数,render函数执行返回VNode(虚拟DOM的数据结构,本质上是棵树)。当每一次UI更新时,总会根据render重新生成最新的VNode,然后跟以前缓存起来老的VNode进行比对,再使用Diff算法(框架核心)去真正更新真实DOM(虚拟DOM是JS对象结构,同样在JS引擎中,而真实DOM在浏览器渲染引擎中,所以操作虚拟DOM比操作真实DOM开销要小的多)。
Vue和React通用流程:vue template/react jsx -> render函数 -> 生成VNode -> 当有变化时,新老VNode diff -> diff算法对比,并真正去更新真实DOM。
核心还是Virtual DOM,为什么Vue和React都选择Virtual DOM
(React首创VDOM,Vue2.0开始引入VDOM)?,个人认为主要有以下几点:
- 减少直接操作DOM。框架给我们提供了屏蔽底层dom书写的方式,减少频繁的整更新dom,同时也使得数据驱动视图
- 为函数式UI编程提供可能(React核心思想)
- 可以跨平台,渲染到DOM(web)之外的平台。比如ReactNative,Weex。
不同点:
1.1核心思想不同
Vue早起定位是尽可能降低前端的开发门槛,Vue推崇灵活易用(渐进开发体验),数据可变,双向数据绑定(依赖收集)
React早期口号是Rethinking Best Practices。背靠大公司Facebook的React,从开始起就不缺关注和用户,而且React想要做的是用更好的方式去颠覆前端开发方式(事实上跟早期jquery称霸前端,的确是颠覆了)。所以React推崇函数式编程(纯组件),数据不可变以及单向数据流
。函数式编程最大的好处是其稳定性(无副作用)和可测试性(输入相同,输出一定相同),所以通常大家说的React适合大型应用,根本原因还是在于其函数式编程。
1.1 核心思想不同导致写法差异
Vue推崇template(简单易懂,从传统前端转过来易于理解)、单文件vue
。而且虽然Vue2.0以后使用了Virtual DOM,使得Vue也可以使用JSX(bebel工具转换支持),但Vue官方依然首先推荐template,这跟Vue的核心思想和定位有一定关系。
React推崇JSX、HOC、all in js
。
1.2 核心思想不同导致api差异
Vue定位简单易上手,基于template模板 + options API
,所以不可避免的有较多的概念和api。比如template模板中需要理解slot、filter、指令等概念和api,options API中需要理解watch、computed(依赖收集)等概念和api。
React本质上核心只有一个Virtual DOM + Diff算法
,所以API非常少,知道setState就能开始开发了。
1.3 核心思想不同导致社区差异
由于Vue定义简单易上手,能快速解决问题,所以很多常见的解决方案,是Vue官方主导开发和维护
。比如状态管理库Vuex、路由库Vue-Router、脚手架Vue-CLI、Vutur工具等。属于那种大包大揽,遇到某类通用问题,只需要使用官方给出的解决方案即可。
React只关注底层,上层应用解决方案基本不插手
,连最基础的状态管理早期也只是给出flow单向数据流思想,大部分都丢给社区去解决。比如状态管理库方面,有redux、mobx、redux-sage、dva等一大堆(选择困难症犯了),所以这也造就了React社区非常繁荣。同时由于有社区做上层应用解决方案,所以React团队有更多时间专注于底层升级,比如花了近2年时间把底层架构改为Fiber架构,以及创造出React Hooks来替换HOC,Suspense等。 更多框架设计思想可看 尤雨溪 - 在框架设计中寻求平衡。
1.4 核心思想不同导致未来升级方向不同
核心思想不同,决定了Vue和React未来不管怎么升级变化,Vue和React考虑的基本盘不变。
Vue依然会定位简单易上手(渐进式开发),依然是考虑通过依赖收集来实现数据可变
。这点从Vue3核心更新内容可以看到:template语法基本不变、options api只增加了setup选项(composition api)、基于依赖收集(Proxy)的数据可变。更多Vue3具体更新内容可看笔者总结 Vue3设计思想 或者 尤雨溪 - 聊聊 Vue.js 3.0 Beta 官方直播。
React的函数式编程这个基本盘不会变
。React核心思想,是把UI作为Basic Type,比如String、Array类型,然后经过render处理,转换为另外一个value(纯函数)。从React Hooks可以看出,React团队致力于组件函数式编程,(纯组件,无class组件),尽量减少副作用(减少this,this会引起副作用)。
2. 组件实现不同
Vue源码实现是把options挂载到Vue核心类上,然后再new Vue({options})拿到实例
(vue组件的script导出的是一个挂满options的纯对象而已)。所以options api中的this指向内部Vue实例,对用户是不透明的,所以需要文档去说明this.xxx这些api。另外Vue插件都是基于Vue原型类基础之上建立的,这也是Vue插件使用Vue.install的原因,因为要确保第三方库的Vue和当前应用的Vue对象是同一个。
React内部实现比较简单,直接定义render函数以生成VNode,而React内部使用了四大组件类包装VNode
,不同类型的VNode使用相应的组件类处理,职责划分清晰明了(后面的Diff算法也非常清晰)。React类组件都是继承自React.Component类,其this指向用户自定义的类,对用户来说是透明的。
3. 响应式原理不同
这个问题网上已经有许多优秀文章都详细讲解过,这里就不具体展开讲,对Vue3响应式原理有兴趣可以看笔者 Vue3响应式原理(Vue2和Vue3响应式原理基本一致,都是基于依赖收集,不同的是Vue3使用Proxy)。
Vue
Vue依赖收集,自动优化
,数据可变。- Vue递归监听data的所有属性,直接修改。
- 当数据改变时,自动找到引用组件重新渲染。
React
React基于状态机,手动优化
,数据不可变,需要setState驱动新的State替换老的State。- 当数据改变时,以组件为根目录,默认全部重新渲染
4. diff算法不同
两者流程思维上是类似的,都是基于两个假设(使得算法复杂度降为O(n)):
- 不同的组件产生不同的 DOM 结构。当type不相同时,对应DOM操作就是直接销毁老的DOM,创建新的DOM。
- 同一层次的一组子节点,可以通过唯一的 key 区分。
但两者源码实现上有区别:
Vue基于snabbdom库,它有较好的速度以及模块机制。Vue Diff使用双向链表,边对比,边更新DOM。
React主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM
。
5. 事件机制不同
Vue
Vue原生事件使用标准Web事件
- Vue组件自定义事件机制,是父子组件通信基础
- Vue合理利用了snabbdom库的模块插件
React
React原生事件被包装
,所有事件都冒泡到顶层document监听,然后在这里合成事件下发。基于这套,可以跨端使用事件机制,而不是和Web DOM强绑定。- React组件上无事件,父子组件通信使用props
Vue 和 React源码流程图
Vue整体流程图
[]:
React整体流程图
关于vue中的key:
在执行动态列表渲染的时候我们需要给每一个组件添加一个key的属性。
首先这个属性与diff算法是相关的。不管是vue还是react自己都有一套自己的虚拟dom算法,只操作数据就可以重新渲染页面,背后则是高校的Diff算法
1.两个相同的组件产生的类似的DOM结构,不同的组件产生不同的DOM结构
2.同一级的一组节点,他们可以通过唯一的id进行区分
使用Diff将之前的组件节点之间的比较算法的时间复杂度由O(n^3)变为了O(n)
当页面数据发生变化的时候,Diff算法只会比较同一层级的节点
如果节点类型不同,直接干掉前面的节点,在创建并插入新的节点,不会再比较节点以后的子节点了。
如果节点相同,则会重新设置该节点的属性,从而实现节点的更新
例如:
我们希望可以在B和C之间加一个F,Diff算法默认执行起来就是这样的:
即把C更新成F,D更新成C,E更新成D,最后在插入E,是不是更有效率呢?
所以我们需要使用key来给每一个节点做一个唯一标识,Diff算法可以正确的识别此节点,找到正确的位置区插入新的节点。
所以一句话。key的作用主要就是为了高效的更新虚拟DOM,另外vue中的使用相同标签名元素的 过度切换时, 也会使用key属性, 其目的也是为了让vue可以区分它们
5.小程序的性能为什么那么好,为什么能做到即用即走这样的效果
因为他的双线程的模型的原因,让小程序可以快速的一下将数据渲染出来呈现在用户的面前
小程序是基于卫星或者支付宝这样的宿主环境的,微信客户端提供双线程去执行wxml, wxss, js文件
双线程模型
渲染层使用的是webview线程来进行渲染的(一个程序会有多个页面,也就是有多个view进行运行)
通过双线程界面的渲染过程是怎样的?
wxml与DOM树
其实我们wxml文件与我们html中的DOM树是一样的,这样我们就可以有js来模拟一个虚拟的DOM树:
初始化渲染
如果我们的wxml文件中如果有变量:要与js逻辑层共同渲染页面成为一个真正的DOM树:
界面数据发生变化
1如果通过setDat把hell改成dsb,则js对象的的节点会发生改变.
2 这时会用diff算法对比两个对象的变化,
3 然后将变化的部分应用到DOM树上
4 从而达到更新页面的目的,这也就是数据驱动的原理
总结
界面渲染的整体流程
1在渲染层将wxml文件与wxss文件转化成js对象也就是虚拟DOM
2 在逻辑成将虚拟的DOM对象配合生成,真实的DOM树,在交给渲染层渲染
3 当数据变化是,逻辑层提供更新数据,js对象发生改变,用diff算法进行比较
4 将更新的内容,反馈到真实的DOM树中,更新页面
6.平时你在开发的时候是怎样的前后端联调的,怎么去上线的,上线出现bug你是怎么处理的?
首先前端后端正式进入联调的时候我们需要使用后端给我们提供的真实接口地址进行测试,所以这个时候在我们的项目中将之前的假的接口地址给替换成真实的接口, 所以在一开始的时候我们需要把接口的配置放在一个配置文件之中,这样方便我们好调试,你也可以使用本地配置host的方式来达到真实环境下的不同域名请求的接口的调试,在这个过程中可能出现后端的接口不能满足前端之前编写的代码的数据格式,这个时候你可以根据具体情况前端进行处理或者让后端帮你在改一下接口的数据格式,也难免不了数据缺少这样的情况,也有可能是产生真实情况下域名产生跨域问题,这个时候你需要使用具体的方案去解决这个问题,比如jsonp跨域,服务器代理,后端cros处理。联调成功之后呢我们需要对代码提审然后进行上线了。
将代码交给你自的组长或者leader由他们进行上线,一般上线需要运维工程师的参与。但是上线可能会出现一系列的问题,比如测试环境中代码正常运行,一旦上线之后用户使用时,出现了bug,我们如何处理,这个时候我们需要代码回滚,但这个时候代码回滚又会出现前后端接口不一致的问题,怎么去解决。两个思路如果bug轻微我们可以暂时已提示的方式告诉用户请重试这样的话,然后我们需要对代码重新进行review和排除bug。如果bug问题较为严重,我们需要对该模块的直接暂停功能,代码进行回滚,重新审查一遍,修复bug。后端公司内部人员使用的项目可以不需要考虑这么多,等待bug修复完成之后然后把补丁打上去就行了。一般在公司会有专门的团队出一套关于bug紧急修复的方案,为了应对上线的项目出现bug的问题。