vue2.0经典高频面试题@小四 - 王云飞

1、vue是一套渐进式框架的理解
在我看来,渐进式代表的含义是:主张最少。
每个框架都不可避免会有自己的一些特点,从而会对使用者有一定的要求,这些要求就是主张,主张有强有弱,它的强势程度会影响在业务开发中的使用方式。
比如说,Angular,它两个版本都是强主张的,如果你用它,必须接受以下东西:
必须使用它的模块机制- 必须使用它的依赖注入- 必须使用它的特殊形式定义组件(这一点每个视图框架都有,难以避免)
所以Angular是带有比较强的排它性的,如果你的应用不是从头开始,而是要不断考虑是否跟其他东西集成,这些主张会带来一些困扰。
比如React,它也有一定程度的主张,它的主张主要是函数式编程的理念,比如说,你需要知道什么是副作用,什么是纯函数,如何隔离副作用。它的侵入性看似没有Angular那么强,主要因为它是软性侵入。
Vue可能有些方面是不如React,不如Angular,但它是渐进的,没有强主张,你可以在原有大系统的上面,把一两个组件改用它实现,当jQuery用;也可以整个用它全家桶开发,当Angular用;还可以用它的视图,搭配你自己设计的整个下层用。你可以在底层数据逻辑的地方用OO和设计模式的那套理念,也可以函数式,都可以,它只是个轻量视图而已,只做了自己该做的事,没有做不该做的事,仅此而已。
渐进式的含义,我的理解是:没有多做职责之外的事。
2、Vue常用的指令
3、v-if VS v-show区别
4、Vue常用修饰符
5、v-on可以监听多个方法吗
可以,代码如下:


6、vue中key值的作用
需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
其实不只是vue,react中在执行列表渲染时也会要求给每个组件添加上key这个属性。
要解释key的作用,不得不先介绍一下虚拟DOM的Diff算法了。
vue和react的虚拟DOM的Diff算法大致相同。
虚拟DOM见“68题” ,diff算法见“69题”

7、$nextTick的作用
8、Vue组件中的data为什么必须是函数
当我们定义一个 组件时( ),你可能会发现它的 data 并不是像这样直接提供一个对象:

data: {
count: 0
}
取而代之的是,一个组件的 data 选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:

data: function () {
return {
count: 0
}
}
9、v-for和v-if的优先级
当它们处于同一节点,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。当你想为仅有的一些项渲染节点时,这种优先级的机制会十分有用。

10、详述组件通信
11、keep-alive组件的作用
当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。例如我们来展开说一说这个多标签界面:

未使用keep-alive.gif

你会注意到,如果你选择了一篇文章,切换到 Archive 标签,然后再切换回 Posts,是不会继续展示你之前选择的文章的。这是因为你每次切换新标签的时候,Vue 都创建了一个新的 currentTabComponent 实例。

重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 元素将其动态组件包裹起来。

使用了keep-alive.gif

现在这个 Posts 标签保持了它的状态 (被选中的文章) 甚至当它未被渲染时也是如此

12、Vue生命周期详解
什么是Vue的生命周期
Vue实例有一个完整的生命周期,也就是说从开始创建、初始化数据、编译模板、挂载DOM、渲染-更新-渲染、卸载等一系列过程,我们称为Vue实例的生命周期。钩子就是在某个阶段给你一个做某些处理的机会。
生命周期的作用
就是在某个阶段给你一个做某些处理的机会。
生命周期总共有几个阶段
beforeCreate(创建前):在实例初始化之后,数据观测和事件配置之前被调用,此时组件的选项对象还未创建,因此无法访问methods,data,computed等上的方法和数据。
created(创建后):实例已经创建完成之后被调用,在这一步,实例已完成了以下配置:数据观测、属性和方法的运算,watch/event事件回调。然而,挂载阶段还没有开始,$el属性目前不可见。created钩子可以获取Vue的data,调用Vue方法,获取原本HTML上的直接加载出来的DOM,但是无法获取到通过挂载模板生成的DOM。这是一个常用的生命周期,因为你可以调用methods中的方法、改变data中的数据,并且修改可以通过vue的响应式绑定体现在页面上、获取computed中的计算属性等等。通常我们可以在这里对实例进行预处理。也有一些童鞋喜欢在这里发ajax请求,值得注意的是,这个周期中是没有什么方法来对实例化过程进行拦截的。因此假如有某些数据必须获取才允许进入页面的话,并不适合在这个页面发请求。
建议在组件路由勾子beforeRouteEnter中来完成。
beforeMonut:挂载开始之前被调用:虚拟dom已经创建完成,马上就要渲染,在这里也可以更改数据,相关的 render 函数首次被调用(虚拟DOM),实例已完成以下的配置:编译模板,把data里面的数据和模板生成html。注意此时还没有挂载html到页面上。
mounted[ˈmaʊntɪd]:挂载完成,也就是模板中的HTML渲染到了HTML页面中,此时一般可以做一些ajax操作,组件已经出现在页面中,数据、真实dom都已经处理好了,事件都已经挂载好了mounted只会执行一次。
beforeUpdate(更新前):在数据更新之前调用,发生在虚拟DOM重新渲染和打补丁之前。可以在该钩子中进一步地更改状态,不会触发附加的重渲染过程。然后vue的虚拟dom机制会重新构建虚拟dom与上一次的虚拟dom树利用diff算法进行对比之后重新渲染。
updated[ʌp’deɪtɪd](更新后):在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。
beforeDestroy[dɪˈstrɔɪ](销毁前):在实例销毁之前调用。实例仍然完全可用。1.这一步还可以用this来获取实例。2.一般在这一步做一些重置的操作。比如清除掉组件中的 定时器 和 监听的dom事件。
destroyed[dɪs’trɔɪd](销毁后):在实例销毁之后调用。调用后,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
第一次页面加载会触发哪几个钩子
beforeCreate
DOM 渲染在哪个周期中就已经完成
mounted
生命周期使用场景举例
beforeCreate:可以在这里加一个loading
created:loading结束做一些初始化操作
mounted:ajax请求,配合路由钩子做一些事情
beforeDestory:你确认删除吗?
destoryed:当前组件已被删除,清空相关内容
13、Vue如何监听键盘事件中的按键
14、Vue中的过滤器有什么用?
Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示

15、单页面应用和多页面应用区别及优缺点
单页面应用(SPA),通俗一点说就是指只有一个主页面的应用,浏览器一开始要加载所有必须的 html, js, css。所有的页面内容都包含在这个所谓的主页面中。但在写的时候,还是会分开写(页面片段),然后在交互的时候由路由程序动态载入,单页面的页面跳转,仅刷新局部资源。多应用于pc端。

多页面(MPA),就是指一个应用中有多个页面,页面跳转时是整页刷新

单页面的优点:
用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小
前后端分离
页面效果会比较炫酷(比如切换页面内容时的专场动画)
单页面缺点:
不利于seo
导航不可用,如果一定要导航需要自行实现前进、后退。(由于是单页面不能用浏览器的前进后退功能,所以需要自己建立堆栈管理)
初次加载时耗时多
页面复杂度提高很多
姓名 单页面应用(SinglePage Web Application,SPA) 多页面应用(MultiPage Application,MPA)
组成 一个外壳页面和多个页面片段组成 多个完整页面构成
资源共用(css,js) 共用,只需在外壳部分加载 不共用,每个页面都需要加载
刷新方式 页面局部刷新或更改 整页刷新
url 模式 a.com/#/pageone a.com/pageone.html
用户体验 页面片段间的切换快,用户体验良好 页面切换加载缓慢,流畅度不够,用户体验比较差
转场动画 容易实现 无法实现
数据传递 容易 依赖 url传参、或者cookie 、localStorage等
搜索引擎优化(SEO) 需要单独方案、实现较为困难、不利于SEO检索 可利用服务器端渲染(SSR)优化 实现方法简易
试用范围 高要求的体验度、追求界面流畅的应用 适用于追求高度支持搜索引擎的应用
开发成本 较高,常需借助专业的框架 较低 ,但页面重复代码多
维护成本 相对容易 相对复杂
16、什么是计算属性?什么情况使用?
模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,例如:

{{ message.split('').reverse().join('') }}
在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。

所以,对于任何复杂逻辑,你都应当使用计算属性。

17、vue-cli提供了几种脚手架模板
六种
https://github.com/vuejs/vue-cli/tree/v2#vue-cli–

18、computed、methods的区别
两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。只在相关依赖发生改变时它们才会重新求值。这就意味着只要值还没有发生改变,多次访问 定义的计算属性会立即返回之前的计算结果,而不必再次执行函数。
相比之下,每当触发重新渲染时,调用方法(methods)将总会再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A 。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。

19、什么是自定义指令,有哪些钩子函数及自定义指令的使用场景
有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
一个指令定义对象可以提供如下几个钩子函数 (均为可选):

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

unbind:只调用一次,指令与元素解绑时调用。

20、父组件获取异步动态数据传递给子组件
在父组件中使用axios获取异步数据传给子组件,但是发现子组件在渲染的时候并没有数据,在created里面打印也是空的,结果发现一开始子组件绑定的数据是空的,在请求数据没有返回数据时,子组件就已经加载了,并且他绑定的值也是空的,问题找到了,怎么解决呢?

开始的时候让子组件隐藏,然后等数据返回的时候,让子组件显示
通过v-if,也就是判断数据是否为空,为空就不渲染,也能解决了
为不能读取的属性添加一个默认值,就可以很好的解决了
21、vue-router导航解析流程
22、vue-router实现原理
这里指的路由并不是指我们平时所说的硬件路由器,这里的路由就是SPA(单页应用)的路径管理器。 换句话说,vue-router就是WebApp的链接路径管理系统。

vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。

那与传统的页面跳转有什么区别呢?
1.vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。
2.传统的页面应用,是用一些超链接来实现页面切换和跳转的。
在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起url和页面之间的映射关系。
至于为啥不能用a标签,这是因为用Vue做的都是单页应用,就相当于只有一个主的index.html页面,所以你写的标签是不起作用的,必须使用vue-router来进行管理。

SPA(single page application):单一页面应用程序,有且只有一个完整的页面;当它在加载页面的时候,不会加载整个页面的内容,而只更新某个指定的容器中内容。

单页面应用(SPA)的核心之一是:
更新视图而不重新请求页面;
vue-router在实现单页面前端路由时,提供了三种方式:Hash模式、History模式、abstract模式,根据mode参数来决定采用哪一种方式。
路由模式
vue-router 提供了三种运行模式:

● hash: 使用 URL hash 值来作路由。默认模式。

● history: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式。

● abstract: 支持所有 JavaScript 运行环境,如 Node.js 服务器端。

Hash模式
vue-router 默认模式是 hash 模式 —— 使用 URL 的 hash 来模拟一个完整的 URL,当 URL 改变时,页面不会去重新加载。

hash(#)是URL 的锚点,代表的是网页中的一个位置,单单改变#后的部分(/#/…),浏览器只会加载相应位置的内容,不会重新加载网页,也就是说 #是用来指导浏览器动作的,对服务器端完全无用,HTTP请求中不包括#;同时每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置;所以说Hash模式通过锚点值的改变,根据不同的值,渲染指定DOM位置的不同数据。
JavaScript实现SPA路由hash模式详解

History模式
HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL,就是利用 history.pushState API 来完成 URL 跳转而无须重新加载页面;

由于hash模式会在url中自带#,如果不想要很丑的 hash,我们可以用路由的 history 模式,只需要在配置路由规则时,加入"mode: ‘history’",这种模式充分利用 history.pushState API 来完成 URL 跳转而无须重新加载页面。

//main.js文件中
const router = new VueRouter({
mode: ‘history’,
routes: […]
})
当使用 history 模式时,URL 就像正常的 url,例如 yoursite.com/user/id,比较好… 不过这种模式有点问题,还需要后台配置支持。你要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面,如果不这么做,直接访问页面空白

配置Apache
第一步:新建:.htaccess文件放在服务器根目录下 (命令type null>.htaccess)

第二步: src/router/index.js
mode: ‘history’,
base: ‘/dist/’,
第三步:访问:地址进行测试

abstract模式
abstract模式是使用一个不依赖于浏览器的浏览历史虚拟管理后端。

根据平台差异可以看出,在 Weex 环境中只支持使用 abstract 模式。 不过,vue-router 自身会对环境做校验,如果发现没有浏览器的 API,vue-router 会自动强制进入 abstract 模式,所以 在使用 vue-router 时只要不写 mode 配置即可,默认会在浏览器环境中使用 hash 模式,在移动端原生环境中使用 abstract 模式。 (当然,你也可以明确指定在所有情况下都使用 abstract 模式)

23、vue-router有哪几种导航钩子
24、vue-router参数传递方法详述及区别
25、如何定义嵌套路由
26、router-link是什么及其常用属性配置
27、如何实现路由懒加载有什么好处
28、vue-router共有几种模式,有什么区别?
29、什么是Vuex及使用场景
30、vuex的常用属性有哪几个,分别是做什么的
31、简述vuex更新数据流程或机制
Vuex

用户在组件中发起动作,然后从API中拿数据,就会牵扯到异步操作,所以我们通过dispatch来提交一个action,在action里面发起ajax请求,拿到数据以后我们只需要通过commit提交mutations改变我的state状态就可以了,状态改变后视图就会改变因为Vuex是响应式的,这就是Vuex的运作流程机制

32、vuex中如何异步修改数据
Action 类似于 mutation,不同在于:
Action 提交的是 mutation,而不是直接变更状态。
Action 可以包含任意异步操作。

33、axios、fetch和ajax有什么区别?
34、axios有哪些特点
35、组件样式中的scoped有什么用
36、vue中常用的UI组件库有哪些?
37、如何优化首屏加载速度
路由懒加载
vue项目作为一个单页面应用,如果不对路由进行处理,在加载首页的时候,就会将所有组件全部加载,并向服务器请求数据,这必将拖慢加载速度;当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

路由懒加载

图片资源的压缩
严格说来这一步不算在编码技术范围内,但是却对页面的加载速度影响很大,特别是对于移动端的项目来说。
对于非logo的图片文件,让UI设计师提供jpg格式的,不要用png
对于所有的图片文件,都可以在一个叫tinypng的网站上去压缩一下或采用webpack插件进行压缩

使用cdn
在Vue项目中,引入到工程中的所有js、css文件,编译时都会被打包进vendor.js,浏览器在加载该文件之后才能开始显示首屏。若是引入的库众多,那么vendor.js文件体积将会相当的大,影响首开的体验。

解决方法是,将引用的外部js、css文件剥离开来,不编译到vendor.js中,而是用资源的形式引用,这样浏览器可以使用多个线程异步将vendor.js、外部的js等加载下来,达到加速首开的目的。

外部的库文件,可以使用CDN资源,或者别的服务器资源等。

下面,以引入vue、vuex、vue-router为例,说明处理流程。

module.exports = {
context: path.resolve(__dirname, ‘…/’),
entry: {
app: ‘./src/main.js’
},
externals:{
‘vue’:‘Vue’,
‘vue-router’:‘VueRouter’,
‘vuex’:‘Vuex’
},
// 格式为’aaa’:‘bbb’,其中,aaa表示要引入的资源的名字,bbb表示该模块提供给外部引用的名字,由对应的库自定。例如,vue为Vue,vue-router为VueRouter
去掉原有的引用直接使用就可以了,否则还是会打包

具体步骤为

1、引入
在bulid/webpack.base.conf.js文件中,增加externals,将引用的外部模块导入,如下:

module.exports = {
entry: {
app: ‘./src/main.js’
},
externals:{
‘vue’: ‘Vue’,
‘vue-router’: ‘VueRouter’,
‘vuex’:‘Vuex’
}
2、在index.html中引入cdn。推荐引入 百度静态资源库的
地址为:https://www.bootcdn.cn/

注意一点: 格式为 'aaa' : 'bbb', 其中,aaa表示要引入的资源的名字,bbb表示该模块提供给外部引用的名字,由对应的库自定。例如,vue为Vue,vue-router为VueRouter.

3、去掉原有的引用
main.js中

// import Vue from ‘vue’
// import Router from ‘vue-router’
去掉Vue.use(XXX),如:

// Vue.use(Router)
4、重新npm run build,会看到 vendor.js体积有所下降了
通过开发者模式的Network工具,可以看到vue.js、vuex.js、vendor.js等文件会分别由一个线程进行加载。且因为使用了CDN,减轻了带宽压力。

前流行的UI框架如iview,muse-ui,Element UI都支持按需加载,
gzip压缩
38、打包命令是什么?
39、打包后会生成哪些文件?
40、如何配置打包后生成的文件路径错误问题
41、简述MVVM、MVP、MVC模式及区别
MVVM
MVVM 是 Model-View-ViewModel 的缩写。
Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View 和 Model的对象,连接Model和View。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
42、MVVM模式的理解
MVVM 由 Model、View、ViewModel 三部分构成,Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
43、Vue中的双向数据绑定是如何实现的
Vue双向数据绑定实现原理
分成两个进程,一个进程是对挂载目标元素模板里的v-model和{{ }}这两个指令进行编译(绿色)。另一个进程是对传进去的data对象里面的数据进行监听(红色)。

红色:

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部加上set和get访问器,这样在设置data的属性值的时候,会触发set方法,那么set方法主要有两个作用,一是改变data里面的属性值,二是发出数据变化的通知。Observer作为数据的观察者,让数据对象的读写操作都处于自己的监管之下,Dep作为Watcher(订阅器)的收集者,当数据发生变化set会发出通知,会被Observer观察到,然后由Dep通知到Watcher,最后更新视图。
绿色:
指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher,同样由Dep进行收集,然后由Dep通知到Watcher,最后更新视图。

节点介绍

数据监听器观察者Observer,能够对数据对象的所有属性进行监听,让数据对象的读写操作都处于自己的监管之下,当数据发生变化set会发出通知,会被Observer观察到,然后由Dep通知到Watcher,最后更新视图。
实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性

Watcher将数据监听器和指令解析器连接起来,数据的属性变动时,执行指令绑定的相应回调函数,
1.如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。

指令解析器Compile,
对每个节点元素进行扫描和解析,将相关指令对应初始化成一个订阅者Watcher

Dep:因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的

43、Object.defineProperty()方法做什么用
44、vue-cli中常用的配置
45、简述vue内部运作机制
46、vuex内部运作机制
47、axios内部运作机制
48、vue-router内部运作机制
49、在vue-cli中怎么使用scss
50、vue和jquery有什么区别?
51、vue的双向数据绑定原理是什么?
52、你是怎么理解组件化开发的
53、简述vue-cli每个目录的作用
54、为什么选择vue?和其它框架对比的优劣势在哪?
55、route和router的区别
56、vue两个核心点是什么?
数据驱动、组件系统

57、用Vuex和不用vuex有什么区别?
58、第一次页面加载会触发哪几个钩子
59、v-model是什么?
60、vue中的数组和原生js中的数组有什么区别?
61、简述$set及使用场景
62、ajax应该放在组件中还是视图中或是vuex中
63、你觉得什么样的项目比较适合用vue框架
64、列举vue中触发视图更新的方法
65、Vue不能检测数组或对象变动问题的解决方法有哪些
66、vue-router,history模式下打包后访问空白
67、打包后访问某个视图,刷新404问题
68、详述虚拟DOM
第一种:
1、state数据
2、JSX模板
3、 数据 + 模板 结合,生成真实的DOM -> 视图
4、state发生了变化
5、数据 + 模板 结合,生成真实的DOM,替换原始的DOM

缺陷:
1、第一次生成了完整的DOM片段
2、第二次生成了完整的DOM片段
3、第二次的DOM替换第一次的DOM,非常耗费性能

第二种:
1、state数据
2、JSX模板
3、数据 + 模板 结合, 生成真实的DOM -> 视图
4、state发生变化
5、数据 + 模板 结合,生成真实的DOM,并不直接替换原始的DOM
6、新的DOM(DocumentFragment)和原始的DOM做比对,找差异
7、找出input框发生了变化
8、只用新的DOM中的input元素,替换掉老的DOM中input元素

缺陷:
虽然DOM只是局部替换,但是在比对时候的计算是比较耗费性能的,因此,性能的提升并不明显

第三种:
1、state数据
2、JSX模板
3、数据 + 模板 生成虚拟DOM(虚拟DOM就是一个JS对象,用它来描述真实DOM)(损耗一点性能)
虚拟DOM:[‘div’, {id: ‘abc’}, [‘span’, ‘’, ‘hello world’]]
4、用虚拟DOM的结构生成真实的DOM -> 视图显示
真实DOM:


5、state发生了变化
6、数据 + 模板 生成新的虚拟DOM:[‘div’, {id: ‘abc’}, [‘span’, ‘’, ‘hi world’]](极大提升性能)
7、比较原始虚拟DOM和新的虚拟DOM的区别,找到的区别是span中的内容发生了变化(极大提升了性能)
8、直接操作DOM,改变span中的内容

总结:
减少了真实DOM的创建及对比,创建都是JS对象,对比的也都是JS的对象,在JS底层实现了极大的性能飞越

组件生成流程:
JSX -> JS对象(虚拟DOM) -> 真实的DOM

用React.createElement改写JSX模板:
JSX:return

{ item }

JSX -> JS对象(虚拟DOM) -> 真实的DOM
React.createElement(‘div’, {}, React.createElement(‘span’, {}, ‘item’))
JSX -> createElement -> JS对象(虚拟DOM) -> 真实的DOM

虚拟DOM优点:
1、性能提升了
2、它使得跨端应用得以实现,Ract Native
React可以写原生应用了,得益于React中的虚拟DOM,如果没有虚拟DOM是不能写原生应用的。原生系统是不支持DOM不存在DOm这个概念的,但是支持虚拟DOM(虚拟DOM就是一个JS对象);虚拟DOM可以在浏览器端被解析为真实的DOM,在原生端可以被解析原生所支持的组件等格式

69、详述虚拟DOM中的diff算法
虚拟DOM对比时,会用到diff算法

虚拟DOM什么时候会被比对?
当数据发生变化的时候就会被比对
那什么时候数据会发生改变呢?
要么改变了state,要么改变了props(props的改变其实是他的父组件的state发生了改变)

setState方法,其实是异步的,为什么是异步的?实际为了提升React底层的性能,假设:调用三次setState变更三组数据,大家想页面会怎么做或者说React会怎么做?我们想的是React可能会做三次比对更新三次视图。又假设三次更新间隔非常小,这样会耗费性能,React可以把三次合并为一次,只去做一次虚拟DOM的比对,然后更新一次视图,这样的话就可以省去两次比对性能上的耗费。

同学们听我说.png

同层比对,如果一致,那么继续比对第二层,如果比对一样了,继续往下比对。
如果比对到不一样了,React会这么做,它不会再继续往下比对了,而是从不一样的这一层开始直接用新的覆盖掉就得DOM节点,这样的话岂不是性能并未得到最大提升?这样的话会造成重复节点的浪费,。那这样比对会有什么好处呢?同层比对带来的好处就是比对的算法特别简单,虽然可能会造成DOM上的重新渲染的浪费,但是大大的减少了虚拟DOM之间比对的算法上的性能消耗,所以React中采用了同层比对的算法。

遍历时候key的问题:

同学们听我说

假如:数组中有五条数据,渲染到页面,然后生成五个虚拟DOM树,接下来我往里面增加了一条数据于是数据发生变化会生成一个新的虚拟DOM树,然后我们会做两个虚拟DOM的比对也就是上下进行比对匹配关系,如果每一个虚拟DOM的节点没有一个key值,它就没有一个自己的名字,当我们在做两个虚拟DOM树的比对的时候节点和节点之间的关系就很难被确立,我们得做两层循环的比较,这样的话比较起来就很麻烦了,当然也是很耗费性能的。
我们可以这样优化,假如我们在做DOM节点的循环的时候,我们可以给每个节点起个名字,A、B、C、D、E在第二次循环的时候我们有六个,以前的ABCDE还存在还是叫做ABCDE,我又增加了一个节点Z进来这个时候比对就很简单了,我们根据他们的名字进行比对,马上就能知道ABCDE都一致,可以继续复用,只有Z不同,我们快速的建立关联后把Z增加到这个DOM树上就可以了。所以极大的提升了虚拟DOM比对的性能。
如果提升性能有个前提我们尽量不要用下标,因为大家看按照下标的话右图ABCDE,下面新的DOM树ABCDE和上面的其实不再是对应的关系了,对导致key值不稳定,key值是变化的,失去了存在的意义了。那用什么比较合适呢?唯一不变化的、稳定的值。

70.ajax、axios、fetch之间的详细区别以及优缺点
71. 异步组件

你可能感兴趣的:(Vue,JavaScript,王云飞,vue,JavaScript,小四,面试题)