Vue面试题

Vue面试题

1.前端路由原理?两种实现方式有什么区别?

前端路由本质就是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新页面。目前前端使用的路由就只有两种实现方式:Hash 模式和History 模式。

Hash 模式

Hash模式会在请求的URL后拼接#,当 # 后面的哈希值发生变化时,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是不包含#的URL。使用window.location.hash可以读取#后的内容。

History 模式

History 模式是 HTML5 新推出的功能,主要使用 history.pushState 和 history.replaceState 来改变URL。通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录。

  • history.pushState:在history中新增一条记录,参数有三个。第一个参数是传入的数据,可以为空;第二参数修改的浏览器标题,可以为空;第三个参数是新的URL地址,不可以跨域。
  • history.replaceState:在history中修改当前记录,参数和pushState一致。

当用户做出浏览器动作时,比如history.back()、history.forward()、history.go(),会触发 popState 事件,在popState事件里可以拿到路由信息,从而进行页面跳转。

两种模式对比

Hash 模式只可以更改 # 后面的内容,History 模式可以通过 API 设置任意的同源 URL History 模式可以通过

API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串 Hash

Hash模式无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端需要配置index.html 页面用于匹配不到静态资源的情况

2.keep-alive 组件有什么作用?

vue在切换页面的时候会将原来的组件注销掉然后渲染新的组件,看似一样实则每次打开的都是新页面。如果我们需要在页面切换的时候,不让他重新渲染,就可以使用 keep-alive 组件包裹需要保存的组件。

对于 keep-alive 组件来说,它拥有两个独有的生命周期钩子函数,分别为 activated 和 deactivated 。

用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,根据缓存渲染后会执行 actived 钩子函数。

假设一个页面比较长,每次切换重新渲染的话都要在页面顶端开始浏览,这对用户来说不太友好。如果我们使用keep-alive每次切换页面前将状态存到内存中,然后返回时再从内存中读取,每次打开都是上次浏览的地方,相对体验比较好

3.对react中key值的理解

React利用key来识别组件,它是一种身份标识,相同的key,react会认为是同一个组件,这样后续相同的key对应的组件都不会创建。

有了key属性之后,就可以和组件建立一种对应关系,通过key来决定是重建组件还是更新组件;

如果key相同,看组件属性有没有变化来决定更不更新;

如果key不同,就先销毁该组件再重建。

4.说一下vue-router 守卫有哪些,如何实现路由懒加载?

vue的路由守卫:全局前置守卫(beforeEach)、全局解析守卫(beforeResolve)、全局后置守卫(afterEach)、路由独享守卫(beforeEnter)、组件内守卫(beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave)

路由懒加载就是把不同的路由对应的组件分割成不同的代码块,当路由被访问的时候才加载对应的组件,主要实现原理就是vue的异步组件和webpack的代码分割。

第一步 我们可以将异步组件定义为返回一个promise的工厂函数

第二 在webpack中使用动态import语法来定义代码分块点

在路由配置中什么都不需要改变

5.什么是Vue.js 动态组件与异步组件?

动态组件:如果我们打算在一个地方根据不同的状态引用不同的组件的话,vue给我们提供了动态组件。

实现就是通过给元素加一个特殊的is属性。

如果我们希望组件实例在它们第一次被创建的时候缓存下来,可以使用元素将动态组件包裹起来。

异步组件:在一些大型的应用中,我们可能需要将应用分割成一些小的代码块,并且只在需要的时候才从服务器加载。vue允许以一个工厂函数的方式定义组件,这个工厂函数会异步解析组件定义。vue只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

我们通常就是将异步组件和webpack的code-splitting功能配合使用,比如实现路由懒加载。

6.说一下vue单页面应用和多页面应用的区别

  1. 刷新方式: 单页面:组件进行切换,页面进行局部刷新或更改 多页面:整页刷新
  2. 路由模式:单页面: 可以使用hash,也可以使用history 多页面:普通的链接跳转
  3. 搜索引擎优化: 单页面:不利用seo检索,需要利用服务器端渲染优化 多页面比较利于seo
  4. 资源文件: 单页面:组件公用的资源只需要加载一次 多页面:每个页面都需要加载公共的资源
  5. 数据传递: 单页面:可以比较容易的实现数据传递,比如路由传参,vuex传参等 多页面: 依赖url传参,或者使用cookie,本地存储等
  6. 在用户体验方面:单页面:页面片段切换时间较快,可以实现转场动画,用户体验较好 多页面:在切换页面时加载缓慢特别是在网速较慢的情况下,而且无法实现转场动画的效果
  7. 开发和维护成本:单页面:开发成本高,但后期维护相对容易 多页面与单页面相反。

7.Vuex和redux有什么区别,他们共同的思想

  1. 区别:vuex改进了redux中的action和reducer,用mutation函数来取代reducer,只需要在对应的mutation函数中改变state的值。Vuex弱化了redux中的dispatch,通过commit进行store状态的变更;取消了action的概念,不用传入特定的action来进行指定变更;弱化reducer,基于commit参数直接对数据进行转变。Redux每次返回的是一个新的state,而vuex直接改变state
  2. 共同思想: 单一的数据源 变化可以预测 他们都是将数据从视图中抽离的一种方案;vuex借鉴了redux,将store作为全局的数据中心,进行模式管理

8.vue项目常见优化点

  1. keep-alive缓存页面
  2. 使用v-show复用DOM
  3. v-for 遍历避免同时使用 v-if
  4. 事件的销毁
    例如组件销毁时清除定时器
  5. 图片懒加载
    对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。
  6. 第三方插件按需引入
    像element-ui这样的第三方组件库可以按需引入避免体积太大**
  7. 无状态的组件标记为函数式组件
  8. 路由懒加载**
    ES6的import()方法 , 按需加载
    vue的异步组件,require()方法。(按需加载)
    vue的异步组件+webpack的ensure()方法。
    最优官方,懒加载和打包分离代码

9.前端路由原理

本质就是监听 URL 的变化,然后匹配路由规则,显示相应的页面,并且无须刷新 路由需要实现三个功能:

①浏览器地址变化,切换页面;

②点击浏览器【后退】、【前进】按钮,网页内容跟随变化;

③刷新浏览器,网页加载当前路由对应内容

目前前端使用的路由就只有两种实现方式

  • Hash 模式
    点击跳转或者浏览器历史跳转当 # 后面的哈希值发生变化时,不会向服务器请求数据,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面手动刷新不会触发hashchange事件,可以采用load事件监听解析URL匹配相应的路由规则,跳转到相应的页面,然后通过DOM替换更改页面内容
  • History 模式
    利用history API实现url地址改变, 网页内容改变
    History.back()、History.forward()、History.go()移动到以前访问过的页面时,页面通常是从浏览器缓存之中加载,而不是重新要求服务器发送新的网页
    History.pushState()
    用于在历史中添加一条记录。该方法接受三个参数,依次为一个与添加的记录相关联的状态对象:state;新页面的标题title;必须与当前页面处在同一个域下的新的网址url
    该方法不会触发页面刷新,只是导致 History 对象发生变化,地址栏会有反应,不会触发hashchange事件
    History.replaceState()方法用来修改 History 对象的当前记录hash 模式

两种模式对比

  • Hash 模式只可以更改 # 后面的内容,History 模式可以通过 API 设置任意的同源 URL
  • History 模式可以通过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
  • Hash 模式无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端需要配置 index.html 页面用于匹配不到静态资源的时候

10.vue中render函数与template对比。

介绍:

render --- js的方式做渲染。

tempalte --- html方式做渲染。

render里有一个函数h,这个h的作用是将单文件组件进行虚拟DOM的创建,然后再通过render进行解析。h就是createElement()方法:createElement( 标签名称, 属性配置, children )。

template是一种编译方式,但是template最终还是要通过render的方式再次进行编译。

区别:

  1. render渲染方式可以让我们将js发挥到极致,因为render的方式其实是通过createElement()进行虚拟DOM的创建。逻辑性比较强,适合复杂的组件封装;
  2. template是类似于html一样的模板来进行组件的封装,适合简单的组件封装;
  3. render的性能比template的性能好很多;
  4. render函数优先级大于template。

11.vue-router路由权限认证如何做?

vue-router做路由权限认证,首先需求来源,根据不同的用户拥有不同的权限,例如:管理员,超级管理员,普通用户,他们登陆成功,所看到的东西是不一样的,所以也引发了不同角色登录成功会出现不同的路由这个话题了。来讲讲具体怎么做吧!!!首先在没有登录之前,可能除了登录页面和注册页面,其他的页面是没有办法进入,的这个时候我们需要使用到vue的路由前置守卫beforeEach了

router.beforeEach((to,from,next)  => {
  // 如果元信息的信息是需要登录或者前端埋点信息中是未登录状态比如没有登录token
  if(!to.meta.isLogin && !token){
    // 跳转登录页面
    next("/login")
  }else {
    // 放行
    next();
  }
})

上面是第一步,如果登录成功之后后端一定会返回一系列的数据,数据里面包含了路由的信息,我们需要在其前端通过addRoutes的方式为前端进行健全






这样话,登录完成之后我们前端的路由就完整了,不同的角色拥有不同的权限路由。

12.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在分模块的时候是必须,例如:购物车数据和商品数据就是两个不同的模块,我们需要将他分开进行处理,但是在合并节点的时候,为了不发生冲突,我们需要使用命名空间的方式来区分他们。

13.简单的介绍一下vue和react的区别,他们当中都有key这个说法,它具体是做什么的

先说相同点:

  • 都是用虚拟DOM的技术
  • 都使用组件化思想
  • 都是响应式,推崇单向数据流
  • 都有成熟的社区,都支持服务端渲染

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)?,个人认为主要有以下几点:

  1. 减少直接操作DOM。框架给我们提供了屏蔽底层dom书写的方式,减少频繁的整更新dom,同时也使得数据驱动视图
  2. 为函数式UI编程提供可能(React核心思想)
  3. 可以跨平台,渲染到DOM(web)之外的平台。比如ReactNative,Weex。

不同点:

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指向用户自定义的类,对用户来说是透明的。
Vue面试题_第1张图片

3、响应式原理不同

这个问题网上已经有许多优秀文章都详细讲解过,这里就不具体展开讲,对Vue3响应式原理有兴趣可以看笔者 Vue3响应式原理(Vue2和Vue3响应式原理基本一致,都是基于依赖收集,不同的是Vue3使用Proxy)。

Vue

  • Vue依赖收集,自动优化,数据可变。
  • Vue递归监听data的所有属性,直接修改。
  • 当数据改变时,自动找到引用组件重新渲染。

React

  • React基于状态机,手动优化,数据不可变,需要setState驱动新的State替换老的State。
  • 当数据改变时,以组件为根目录,默认全部重新渲染

4、diff算法不同

两者流程思维上是类似的,都是基于两个假设(使得算法复杂度降为O(n)):

  1. 不同的组件产生不同的 DOM 结构。当type不相同时,对应DOM操作就是直接销毁老的DOM,创建新的DOM。
  2. 同一层次的一组子节点,可以通过唯一的 key 区分。

但两者源码实现上有区别:

Vue基于snabbdom库,它有较好的速度以及模块机制。Vue Diff使用双向链表,边对比,边更新DOM。

React主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。
Vue面试题_第2张图片

5#####、事件机制不同

Vue

  • Vue原生事件使用标准Web事件
  • Vue组件自定义事件机制,是父子组件通信基础
  • Vue合理利用了snabbdom库的模块插件

React

  • React原生事件被包装,所有事件都冒泡到顶层document监听,然后在这里合成事件下发。基于这套,可以跨端使用事件机制,而不是和Web DOM强绑定。
  • React组件上无事件,父子组件通信使用props

Vue 和 React源码流程图

Vue整体流程图
Vue面试题_第3张图片

React整体流程图
Vue面试题_第4张图片

关于vue中的key:

在执行动态列表渲染的时候我们需要给每一个组件添加一个key的属性。

首先这个属性与diff算法是相关的。不管是vue还是react自己都有一套自己的虚拟dom算法,只操作数据就可以重新渲染页面,背后则是高校的Diff算法

1.两个相同的组件产生的类似的DOM结构,不同的组件产生不同的DOM结构

2.同一级的一组节点,他们可以通过唯一的id进行区分

使用Diff将之前的组件节点之间的比较算法的时间复杂度由O(n^3)变为了O(n)
Vue面试题_第5张图片

当页面数据发生变化的时候,Diff算法只会比较同一层级的节点

如果节点类型不同,直接干掉前面的节点,在创建并插入新的节点,不会再比较节点以后的子节点了。

如果节点相同,则会重新设置该节点的属性,从而实现节点的更新

例如:

我们希望可以在B和C之间加一个F,Diff算法默认执行起来就是这样的:
Vue面试题_第6张图片

即把C更新成F,D更新成C,E更新成D,最后在插入E,是不是更有效率呢?

所以我们需要使用key来给每一个节点做一个唯一标识,Diff算法可以正确的识别此节点,找到正确的位置区插入新的节点。
Vue面试题_第7张图片

所以一句话。key的作用主要就是为了高效的更新虚拟DOM,另外vue中的使用相同标签名元素的 过度切换时, 也会使用key属性, 其目的也是为了让vue可以区分它们

14.template 模板是怎样通过 Compile 编译的

Compile 编译有三个阶段

parse函数:通过正则表达式解析模板中的指令,生成AST抽象语法树

optimize函数:优化AST,标记静态的(不需要修改的DOM)节点。diff算法更新时,会跳过静态节点, 8减少比较的过程,优化patch的性能

generate: 将AST生成render函数

https://www.jianshu.com/p/700b8e6347ff

15.MVVM框架设计理念

Model–View–ViewModel (MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样 也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表

MVVM 源自于经典的 Model–View–Controller(MVC)模式 ,MVVM 的出现促进了前端开发与后端业 务逻辑的分离,极大地提高了前端开发效率,MVVM 的核心是 ViewModel 层,它就像是一个中转站 (value converter),负责转换 Model 中的数据对象来让数据变得更容易管理和使用,该层向上与视 图层进行双向数据绑定,向下与 Model 层通过接口请求进行数据交互,起呈上启下作用。

(1)View 层

View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。

(2)Model 层

Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。

(3)ViewModel 层

ViewModel 是由前端开发人员组织生成和维护的视图数据层。在这一层,前端开发者对从后端获取的 Model 数据进行转换处理,做二次封装,以生成符合 View 层使用预期的视图数据模型。需要注意的是 ViewModel 所封装出来的数据模型包括视图的状态和行为两部分,而 Model 层的数据模型是只包含状 态的,比如页面的这一块展示什么,而页面加载进来时发生什么,点击这一块发生什么,这一块滚动时 发生什么这些都属于视图行为(交互),视图状态和行为都封装在了 ViewModel 里。这样的封装使得 ViewModel 可以完整地去描述 View 层。

MVVM 框架实现了双向绑定,这样 ViewModel 的内容会实时展现在 View 层,前端开发者再也不必低 效又麻烦地通过操纵 DOM 去更新视图,MVVM 框架已经把最脏最累的一块做好了,我们开发者只需要 处理和维护 ViewModel,更新数据视图就会自动得到相应更新。这样 View 层展现的不是 Model 层的 数据,而是 ViewModel 的数据,由 ViewModel 负责与 Model 层交互,这就完全解耦了 View 层和 Model 层,这个解耦是至关重要的,它是前后端分离方案实施的重要一环。

16.对于即将到来的vue3.0特性你有什么了解的吗

Vue 3.0 正走在发布的路上,Vue 3.0 的目标是让 Vue 核心变得更小、更快、更强大,因此 Vue 3.0 增加以下这些新特性:

(1)监测机制的改变

3.0 将带来基于代理 Proxy 的 observer 实现,提供全语言覆盖的反应性跟踪。这消除了 Vue 2 当中基 于 Object.defineProperty 的实现所存在的很多限制:

function createKeyToOldIdx (children, beginIdx, endIdx) {
  let i, key
  const map = {}
  for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
    if (isDef(key)) map[key] = i
  }
return map }
  • 只能监测属性,不能监测对象
  • 检测属性的添加和删除;
  • 检测数组索引和长度的变更;
  • 支持 Map、Set、WeakMap 和 WeakSet。

新的 observer 还提供了以下特性:

  • 用于创建 observable 的公开 API。这为中小规模场景提供了简单轻量级的跨组件状态管理解决方 案。
  • 默认采用惰性观察。在 2.x 中,不管反应式数据有多大,都会在启动时被观察到。如果你的数据集 很大,这可能会在应用启动时带来明显的开销。在 3.x 中,只观察用于渲染应用程序最初可见部分 的数据。
  • 更精确的变更通知。在 2.x 中,通过 Vue.set 强制添加新属性将导致依赖于该对象的 watcher 收 到变更通知。在 3.x 中,只有依赖于特定属性的 watcher 才会收到通知。
  • 不可变的 observable:我们可以创建值的“不可变”版本(即使是嵌套属性),除非系统在内部暂 时将其“解禁”。这个机制可用于冻结 prop 传递或 Vuex 状态树以外的变化。
  • 更好的调试功能:我们可以使用新的 renderTracked 和 renderTriggered 钩子精确地跟踪组件在 什么时候以及为什么重新渲染。

(2)模板

模板方面没有大的变更,只改了作用域插槽,2.x 的机制导致作用域插槽变了,父组件会重新渲染,而3.0 把作用域插槽改成了函数的方式,这样只会影响子组件的重新渲染,提升了渲染的性能。 同时,对于 render 函数的方面,vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。

(3)对象式的组件声明方式

vue2.x 中的组件是通过声明的方式传入一系列 option,和 TypeScript 的结合需要通过一些装饰器的方 式来做,虽然能实现功能,但是比较麻烦。3.0 修改了组件的声明方式,改成了类式的写法,这样使得 和 TypeScript 的结合变得很容易。

此外,vue 的源码也改用了 TypeScript 来写。其实当代码的功能复杂之后,必须有一个静态类型系统 来做一些辅助管理。现在 vue3.0 也全面改用 TypeScript 来重写了,更是使得对外暴露的 api 更容易结 合 TypeScript。静态类型系统对于复杂代码的维护确实很有必要。

(4)其它方面的更改

vue3.0 的改变是全面的,上面只涉及到主要的 3 个方面,还有一些其他的更改:

  • 支持自定义渲染器,从而使得 weex 可以通过自定义渲染器的方式来扩展,而不是直接 fork 源码 来改的方式。
  • 支持 Fragment(多个根节点)和 Protal(在 dom 其他部分渲染组建内容)组件,针对一些特殊 的场景做了处理。
  • 基于 treeshaking 优化,提供了更多的内置功能。

17.如果线上出现bug git怎么操作

方法1:在当前主分支修改bug,暂存当前的改动的代码,目的是让工作空间和远程代码一致:

Git stash

修改完bug后提交修改:

git add.

git commit --m “fix bug 1”

git push

从暂存区把之前的修改恢复,这样就和之前改动一样了

git stash pop

这时可能会出现冲突,因为你之前修改的文件,可能和bug是同一个文件,如果有冲突会提示:

Auto–merging xxx.Java

conflice(content):Merge conflict in xxx.java

前往xxx.java解决冲突

注意stash pop意思是从暂存区恢复到工作空间,同时删除此条暂存记录

方法2:拉一个新分支,老司机都推荐这样做,充分利用了git特性,先暂存一下工作空间改动:

git stash

新建一个分支,并切换到这个新分支

git branch fix-bug//新建分支

git checkbox fix-bug//切换分支

这时候就可以安心在这个分之下修改bug了,改完之后:

git add.

git commit --m “fix a bug”

切换到master主分支

git checkout master

从fix-bug合并到master分支

git merge fix-bug

提交代码

git push

然后从暂存区恢复代码

git stash pop

此时有冲突需要解决冲突

  1. vuex刷新数据页面会丢失吗?咋解决的?

会!!!

    因为vuex里的	数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里	面的数据就会被重新赋值。

用vuex-persistedstate插件可以解决

在vuex配置项中配置 plugins: [createPersistedState()],默认存储在localStorage

传参方式:createPersistedState({ storage: window.sessionStorage})

将vuex中的数据直接保存到浏览器缓存中(sessionStorage、localStorage、cookie)

缺点是不安全,不适用大数据量的存储;

19.vue.nextTick()

定义:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操作的 js代码;

理解:nextTick(),是将回调函数延迟在下一次dom更新数据后调用,简单的理解是:当数据更新了,在dom中渲染后,自动执行该函数,

注意:Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 nextTick,则可以在回调中获取更新后的 DOM,

20.V-if和v-show的区别,应用场景

v-show 只是在 display: none 和 display: block 之间切换。无论初始条件是什么都会被渲染出来,后面只需要切换 CSS,DOM 还是一直保留着的。所以总的来说 v-show 在初始渲染时有更高的开销,但是切换开销很小,更适合于频繁切换的场景。

v-if 的话就得说到 Vue 底层的编译了。当属性初始为 false 时,组件就不会被渲染,直到条件为 true,并且切换条件时会触发销毁/挂载组件,所以总的来说在切换时开销更高,更适合不经常切换的场景。

并且基于 v-if 的这种惰性渲染机制,可以在必要的时候才去渲染组件,减少整个页面的初始渲染开销。

  1. vuex和redux的区别

  2. 表面区别就是vuex是通过将store注入到组件实例中,通过dispatch和commit来维护state的状态,并可以通过mapstate和this.$store来读取state数据。而redux则是需要通过connect将state和dispatch链接来映射state并操作state。redux没有commit,直接通过dispatch派发一个action来维护state的数据。并且只能通过reducer一个函数来操作state

  3. rudex使用的是不可变数据;vuex使用的是可变数据

  4. rudex每次都是返回一个新的state;而vuex是直接改变state。

  5. vue路由懒加载方式

  6. resolve

    • 主要是使用了resolve的异步机制,用require代替了import,实现按需加载
  7. 官网方法

    • vue-router在官网提供了一种方法,可以理解也是为通过Promise的resolve机制。因为Promise函数返回的Promise为resolve组件本身,而我们又可以使用import来导入组件
  8. require.ensure

    • 可以通过参数中的webpackChunkName将js分开打包 结合 Vue 的异步组件和 Webpack 的代码分割功能,把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件
  9. Vue中的key值、React中的key值

  10. Vue

    • key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。所以key 的作用主要是为了高效的更新虚拟 DOM
  11. Raect (Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识)

    • 在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React Diff 算法中 React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重渲染。此外,React 还需要借助 Key 值来判断元素与本地状态的关联关系,因此我们绝不可忽视转换函数中 Key 的重要性
  12. Vue中的单向数据流

  • 数据从父级组件传递给子组件,只能单向绑定
  • 子组件内部不能直接修改从父级传递过来的数据
  1. Vue组件中的data为什么是一个函数,根组件却是对象

  2. 为什么根实例的data是一个对象?

    • new Vue()中只有一个data属性,共用该data
  3. 为什么组件中的data必须是一个函数

    • 如果data是一个对象,对象是引用类型,那复用的所有组件实例都会共享这些数据,就会导致修改一个组件实例上的数据,其他复用该组件的实例上对应的数据也会被修改
    • 如果data是一个函数,函数虽然也是引用类型,但是函数是有作用域的,函数内的变量不能被外部访问到,这样每个组件实例都会有个独立的拷贝同时又因为函数作用域的限制修改自己的数据时其他组件实例的数据是不会受到影响的
  4. 总结

    • 对象是引用类型,且没有作用域,会导致一改全改
    • 函数是引用类型,但它有作用域,不会彼此受牵连
  5. Vue的性能优化

  6. SPA 页面采用keep-alive缓存组件

  7. 在更多的情况下,使用v-if替代v-show

  8. 第三方模块按需导入

  9. 服务端渲染SSR (更利于SEO,更利于首屏渲染)

  10. 压缩代码

  11. Tree Shaking

    • 用来尽可能的删除没有被使用过的代码,一些被 import 了但其实没有被使用的代码
  12. source-map

    • 提供源代码到构建后代码映射技术,如果构建后代码出错了,通过映射可以追踪源代码错误
  13. 服务端开启gzip压缩

  14. 什么是nextTick

  • 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

应用场景:

  1. 在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中
    原因:是created()钩子函数执行时DOM其实并未进行渲染

  2. 在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作应该放在Vue.nextTick()的回调函数中
    原因:Vue异步执行DOM更新,只要观察到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变,如果同一个watcher被多次触发,只会被推入到队列中一次

  3. 介绍一下单页面和多页面的区别

  • 单页面应用(SPA) 通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源
    • 优点:
      1. 用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
      2. 前后端分离
      3. 页面效果会比较炫酷(比如切换页面内容时的专场动画)
    • 缺点:
      1. 导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)
      2. 初次加载时耗时多
      3. 页面复杂度提高很多
  • 多页面(MPA) 就是指一个应用中有多个页面,页面跳转时是整页刷新
  1. 介绍一下Vue中父子组件生命周期的执行顺序
  • 加载渲染过程
    • 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
  • 子组件更新过程
    • 父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
  • 父组件更新过程
    • 父 beforeUpdate -> 父 updated
  • 销毁过程
    • 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
  1. Vue父子组件的通信方式有哪些

Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信

  1. props / $emit 适用 父子组件通信

  2. ref 与 parent / children 适用 父子组件通信

    • ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
    • parent/children:访问父 / 子实例
  3. EventBus (emit / on)适用于父子、隔代、兄弟组件通信

    • 这种方法通过一个空的 Vue 实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件
  4. attrs / listeners 适用于 隔代组件通信

    • attrs:包含了父作用域中不被prop所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用
    • listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
  5. provide / inject 适用于 隔代组件通信

    • 祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系
  6. Vuex 适用于 父子、隔代、兄弟组件通信

    • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )
    • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新
  7. Vue中响应式的原理

Vue的响应式原理也就是数据的双向绑定原理,数据双向绑定主要是指:数据变化更新视图,视图变化更新数据

步骤

  1. 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化

  2. 实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新

  3. 实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数

  4. 实现一个订阅器 Dep:订阅器采用 发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理

  5. 虚拟DOM为什么会提高性能

虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 所记录的差异应用到所构建的真正的 DOM 树上,视图就更新了

  1. vue中 computed 的原理

  2. 当组件初始化的时候,computed 和 data 会分别建立各自的响应系统,Observer遍历 data 中每个属性,使用 Object.defineProperty 的 get/set 方法对其进行数据劫持

  3. 初始化 computed 会调用 initComputed 函数

    • new 一个 watcher 实例,watcher 中实例化一个 Dep (消息订阅器) 收集依赖
      • data -> Dep -> watcher ,通过dep在data和watcher之间建立了依赖
      • 创建的时候不会触发 get 方法
  4. 调用 computed 时会触发其 Object.defineProperty 的 get 访问器函数

    • dirty = true 会调用 watcher.get 方法重新计算,随后 dirty = false
    • 启动依赖收集,调用 watcher.depend() 方法向自身的消息订阅器 dep.subs 中添加订阅者(其他属性的watcher)
  5. 当某个属性(依赖项)发生变化,触发 set 拦截函数,然后调用自身消息订阅器dep, dep 通过 notify 遍历当前 dep.subs 通知每个 watcher 更新(watcher.update())

34.为什么在 Vue3.0 采用了 Proxy,抛弃了Object.defineProperty

Object.defineProperty 本身有一定的监控到数组下标变化的能力,但是在 Vue 中,从性能/体验的性价比考虑,尤大大就弃用了这个特性(Vue 为什么不能检测数组变动 )。为了解决这个问题,经过 vue 内部处理后可以使用以下几种方法来监听数组 ( push() pop() shift() unshift() splice() sort() reverse() )

Object.defineProperty 只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历。

Proxy 可以劫持整个对象,并返回一个新的对象。 Proxy 不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

  1. keep-alive 的实现原理和缓存策略

实现原理

  • 获取 keep-alive 包裹着的第一个子组件对象及其组件名
  • 根据设定的 include/exclude(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例
  • 根据组件 ID 和 tag 生成缓存 Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该 key 在 this.keys 中的位置(更新 key 的位置是实现 LRU 置换策略的关键)
  • 在 this.cache 对象中存储该组件实例并保存 key 值,之后检查缓存的实例数量是否超过 max 的设置值,超过则根据 LRU 置换策略删除最近最久未使用的实例(即是下标为 0 的那个 key)
  • 最后组件实例的 keepAlive 属性设置为 true,这个在渲染和执行被包裹组件的钩子函数会用到

缓存策略(LRU缓存淘汰算法)

LRU(Least recently used)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

keep-alive 的实现正是用到了 LRU 策略,将最近访问的组件 push 到 this.keys 最后面,this.keys[0]也就是最久没被访问的组件,当缓存实例超过 max 设置值,删除 this.keys[0]
Vue面试题_第8张图片

  1. 什么是vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

五个属性

  1. State

    • Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data
    • state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新
    • 它通过mapState把全局的 state 和 getters 映射到当前组件的 computed 计算属性中
  2. Getter

    • getters 可以对State进行计算操作,它就是Store的计算属性
    • 虽然在组件内也可以做计算属性,但是getters 可以在多组件之间复用
    • 如果一个状态只在一个组件内使用,是可以不用getters
  3. Mutation

    • Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作
  4. Action

    • Action提交的是mutation,而不是直接变更状态,也就是说我们的状态值需要通过mutation来提交;那么mutation它可以通过Action来提交
    • 就是mutation的函数必须是同步的;而Action可以做任意异步的操作
  5. Module

    • Module 可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理。
  6. Vue的性能优化技巧

  7. 使用函数式组件
    函数式组件和普通的对象类型的组件不同,它不会被看作成一个真正的组件,我们知道在 patch 过程中,如果遇到一个节点是组件 vnode,会递归执行子组件的初始化过程;而函数式组件的 render 生成的是普通的 vnode,不会有递归子组件的过程,因此渲染开销会低很多。
    因此,函数式组件也不会有状态,不会有响应式数据,生命周期钩子函数这些东西。你可以把它当成把普通组件模板中的一部分 DOM 剥离出来,通过函数的方式渲染出来,是一种在 DOM 层面的复用

  8. 子组件拆分
    优化后的方式是把这个耗时任务 heavy 函数的执行逻辑用子组件 ChildComp 封装了,由于 Vue 的更新是组件粒度的,虽然每一帧都通过数据修改导致了父组件的重新渲染,但是 ChildComp 却不会重新渲染,因为它的内部也没有任何响应式数据的变化。所以优化后的组件不会在每次渲染都执行耗时任务,自然执行的 JavaScript 时间就变少了。

  9. 使用 v-show 复用 DOM
    v-show 相比于 v-if 的性能优势是在组件的更新阶段,如果仅仅是在初始化阶段,v-if 性能还要高于 v-show,原因是在于它仅仅会渲染一个分支,而 v-show 把两个分支都渲染了,通过 style.display 来控制对应 DOM 的显隐

38.vue中使用过ssr吗?说说ssr

Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务端的 HTML 字符串,将它们直接发送 到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。

服务端渲染 SSR 的优缺点如下:

(1)服务端渲染的优点:

更好的 SEO: 因为 SPA 页面的内容是通过 Ajax 获取,而搜索引擎爬取工具并不会等待 Ajax 异步 完成后再抓取页面内容,所以在 SPA 中是抓取不到页面通过 Ajax 获取到的内容;而 SSR 是直接由 服务端返回已经渲染好的页面(数据已经包含在页面中),所以搜索引擎爬取工具可以抓取渲染好 的页面;

更快的内容到达时间(首屏加载更快): SPA 会等待所有 Vue 编译后的 js 文件都下载完成后,才 开始进行页面的渲染,文件下载等需要一定的时间等,所以首屏渲染需要一定的时间;SSR 直接由 服务端渲染好页面直接返回显示,无需等待下载 js 文件及再去渲染等,所以 SSR 有更快的内容到 达时间;

(2) 服务端渲染的缺点:

更多的开发条件限制: 例如服务端渲染只支持 beforCreate 和 created 两个钩子函数,这会导致 一些外部扩展库需要特殊处理,才能在服务端渲染应用程序中运行;并且与可以部署在任何静态文 件服务器上的完全静态单页面应用程序 SPA 不同,服务端渲染应用程序,需要处于 Node.js server 运行环境;

更多的服务器负载:在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用CPU 资源 (CPU-intensive - CPU 密集),因此如果你预料在高流量环境 ( high traffic ) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。

  1. computed和watch的区别?

computed:

	如果不使用计算属性,那么 message.split('').reverse().join('') 就会直接写到 template 里,那么在模版中放入太多声明式的逻辑会让模板本身过重,尤其当在页面中使用大量复杂的逻辑表达式处理数据时,会对页面的可维护性造成很大的影响,而且计算属性如果依赖不变的话,它就会变成缓存,computed 的值就不会重新计算。而且他返回的是一个值,同时也不能进行异步操作。所以就有了watch。

watch:

	也是用来监听数据的变化的,watch与computed的区别就是它支持异步,不支持缓存。而且watch在第一次监听的时候不会触发,想要触发必须得在第二个参数上面配置(immeiate)。此外还有另外一个参数是deep,可以用来深度监听一个数组或者是对象。
  1. Vue的生命周期
    Vue面试题_第9张图片

  2. 第一个生命周期函数,表示实例完全被创建之前,会执行这个函数
    在beforeCreate生命周期函数执行的时候,data 和 methods 中的数据还没有被初始化
    
  3. 第二个生命周期函数
    在 created 中,data 和 methods 都已经被初始化好了!
    如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
    
  4. 第三个生命周期函数,表示模板已经在内存中编译完成,但是尚未把模板渲染到页面中
    在beforeMount执行的时候,页面中的元素没有被真正替换过来,知识之前写的一些模板字符串
    
  5. 第四个生命周期函数,表示内存中的模板已经真实的挂载到页面中,用户已经可以看到渲染好的页面
    这个mounted是实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了
    
  6. 第五个生命周期函数,表示界面还没有被更新,但是数据肯定被更新了
    得出结论:
    当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时data 数据是最新的,页面尚未和最新的数据保持同步
    
  7. 第六个生命周期函数
    updated事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
    
  8. 第七个生命周期函数
    当执行beforeDestroy的时候,实例身上的data及所有的methods都处于可用状态,还没有真正执行销毁过程
    
  9. 第八个生命周期函数
    当执行到destroyed时,组件已被完全销毁
    
  10. hash、chunkhash、contenthash三者区别

  11. hash

    • 所有文件哈希值相同,只要改变内容跟之前的不一致,所有哈希值都改变,没有做到缓存意义
  12. chunkhash

    • 同一个模块,就算将js和css分离,其哈希值也是相同的,修改一处,js和css哈希值都会变,同hash,没有做到缓存意义
  13. contenthash

    • 只要文件内容不一样,产生的哈希值就不一样

你可能感兴趣的:(面试技巧,vue.js)