美团前端经典 vue 面试题总结
Watch 中的 deep:true 是如何实现的
当用户指定了
watch
中的 deep 属性为true
时,如果当前监控的值是数组类型。会对对象中的每一项进行求值,此时会将当前watcher
存入到对应属性的依赖中,这样数组中对象发生变化时也会通知数据更新
源码相关
delete 和 Vue.delete 删除数组的区别?
delete
只是被删除的元素变成了empty/undefined
其他的元素的键值还是不变。Vue.delete
直接删除了数组 改变了数组的键值。
params 和 query 的区别
用法:query 要用 path 来引入,params 要用 name 来引入,接收参数都是类似的,分别是 this.$route.query.name
和 this.$route.params.name
。
url 地址显示:query 更加类似于 ajax 中 get 传参,params 则类似于 post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
注意:query 刷新不会丢失 query 里面的数据 params 刷新会丢失 params 里面的数据。
构建的 vue-cli 工程都到了哪些技术,它们的作用分别是什么
vue.js
:vue-cli
工程的核心,主要特点是 双向数据绑定 和 组件系统。vue-router
:vue
官方推荐使用的路由框架。vuex
:专为Vue.js
应用项目开发的状态管理器,主要用于维护vue
组件间共用的一些 变量 和 方法。axios
( 或者fetch
、ajax
):用于发起GET
、或POST
等http
请求,基于Promise
设计。vuex
等:一个专为vue
设计的移动端 UI 组件库。创建一个
emit.js
文件,用于vue
事件机制的管理。webpack
:模块加载和vue-cli
工程打包器。
vue-cli 工程常用的 npm 命令有哪些
下载
node_modules
资源包的命令:
启动
vue-cli
开发环境的 npm 命令:
vue-cli
生成 生产环境部署资源 的npm
命令:
用于查看
vue-cli
生产环境部署资源文件大小的npm
命令:
在浏览器上自动弹出一个 展示
vue-cli
工程打包后app.js
、manifest.js
、vendor.js
文件里面所包含代码的页面。可以具此优化vue-cli
生产环境部署的静态资源,提升 页面 的加载速度
v-once 的使用场景有哪些
分析
v-once
是Vue
中内置指令,很有用的API
,在优化方面经常会用到
体验
仅渲染元素和组件一次,并且跳过未来更新
回答范例
v-once
是vue
的内置指令,作用是仅渲染指定组件或元素一次,并跳过未来对其更新如果我们有一些元素或者组件在初始化渲染之后不再需要变化,这种情况下适合使用
v-once
,这样哪怕这些数据变化,vue
也会跳过更新,是一种代码优化手段我们只需要作用的组件或元素上加上
v-once
即可vue3.2
之后,又增加了v-memo
指令,可以有条件缓存部分模板并控制它们的更新,可以说控制力更强了编译器发现元素上面有
v-once
时,会将首次计算结果存入缓存对象,组件再次渲染时就会从缓存获取,从而避免再次计算
原理
下面例子使用了v-once
:
我们发现v-once
出现后,编译器会缓存作用元素或组件,从而避免以后更新时重新计算这一部分:
参考 前端进阶面试题详细解答
vue-loader 是什么?它有什么作用?
回答范例
vue-loader
是用于处理单文件组件(SFC
,Single-File Component
)的webpack loader
因为有了
vue-loader
,我们就可以在项目中编写SFC
格式的Vue
组件,我们可以把代码分割为<template>
、<script>
和<style>
,代码会异常清晰。结合其他loader
我们还可以用Pug
编写<template>
,用SASS
编写<style>
,用TS
编写<script>
。我们的<style>
还可以单独作用当前组件webpack
打包时,会以loader
的方式调用vue-loader
vue-loader
被执行时,它会对SFC
中的每个语言块用单独的loader
链处理。最后将这些单独的块装配成最终的组件模块
原理
vue-loader
会调用@vue/compiler-sfc
模块解析SFC
源码为一个描述符(Descriptor
),然后为每个语言块生成import
代码,返回的代码类似下面
我们想要script
块中的内容被作为js
处理(当然如果是<script lang="ts">
被作为ts
理),这样我们想要webpack
把配置中跟.js
匹配的规则都应用到形如source.vue?vue&type=script
的这个请求上。例如我们对所有*.js
配置了babel-loader
,这个规则将被克隆并应用到所在Vue SFC
将被展开为:
类似的,如果我们对.sass
文件配置了style-loader + css-loader + sass-loader
,对下面的代码
vue-loader
将会返回给我们下面结果:
然后webpack
会展开如下:
当处理展开请求时,
vue-loader
将被再次调用。这次,loader
将会关注那些有查询串的请求,且仅针对特定块,它会选中特定块内部的内容并传递给后面匹配的loader
对于
<script>
块,处理到这就可以了,但是<template>
和<style>
还有一些额外任务要做,比如需要用
Vue
模板编译器编译template
,从而得到render
函数需要对
<style scoped>
中的CSS
做后处理(post-process
),该操作在css-loader
之后但在style-loader
之前
实现上这些附加的loader
需要被注入到已经展开的loader
链上,最终的请求会像下面这样:
Vue 为什么没有类似于 React 中 shouldComponentUpdate 的生命周期
考点:
Vue
的变化侦测原理前置知识: 依赖收集、虚拟
DOM
、响应式系统
根本原因是
Vue
与React
的变化侦测方式有所不同
当 React 知道发生变化后,会使用
Virtual Dom Diff
进行差异检测,但是很多组件实际上是肯定不会发生变化的,这个时候需要shouldComponentUpdate
进行手动操作来减少diff
,从而提高程序整体的性能Vue
在一开始就知道那个组件发生了变化,不需要手动控制diff
,而组件内部采用的diff
方式实际上是可以引入类似于shouldComponentUpdate
相关生命周期的,但是通常合理大小的组件不会有过量的 diff,手动优化的价值有限,因此目前Vue
并没有考虑引入shouldComponentUpdate
这种手动优化的生命周期
Vue 为什么需要虚拟 DOM?优缺点有哪些
由于在浏览器中操作
DOM
是很昂贵的。频繁的操作DOM
,会产生一定的性能问题。这就是虚拟Dom
的产生原因。Vue2
的Virtual DOM
借鉴了开源库snabbdom
的实现。Virtual DOM
本质就是用一个原生的JS
对象去描述一个DOM
节点,是对真实DOM
的一层抽象
优点:
保证性能下限 : 框架的虚拟
DOM
需要适配任何上层API
可能产生的操作,它的一些DOM
操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的DOM
操作性能要好很多,因此框架的虚拟DOM
至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;无需手动操作 DOM : 我们不再需要手动去操作
DOM
,只需要写好View-Model
的代码逻辑,框架会根据虚拟DOM
和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;跨平台 : 虚拟
DOM
本质上是JavaScript
对象,而DOM
与平台强相关,相比之下虚拟DOM
可以进行更方便地跨平台操作,例如服务器渲染、weex
开发等等。
缺点:
无法进行极致优化:虽然虚拟
DOM
+ 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟DOM
无法进行针对性的极致优化。首次渲染大量
DOM
时,由于多了一层虚拟DOM
的计算,会比innerHTML
插入慢。
虚拟 DOM 实现原理?
虚拟 DOM
的实现原理主要包括以下 3
部分:
用
JavaScript
对象模拟真实DOM
树,对真实DOM
进行抽象;diff
算法 — 比较两棵虚拟DOM
树的差异;pach
算法 — 将两个虚拟DOM
对象的差异应用到真正的DOM
树。
说说你对虚拟 DOM 的理解?回答范例
思路
vdom
是什么引入
vdom
的好处vdom
如何生成,又如何成为dom
在后续的
diff
中的作用
回答范例
虚拟
dom
顾名思义就是虚拟的dom
对象,它本身就是一个JavaScript
对象,只不过它是通过不同的属性去描述一个视图结构通过引入
vdom
我们可以获得如下好处:
将真实元素节点抽象成
VNode
,有效减少直接操作dom
次数,从而提高程序性能直接操作
dom
是有限制的,比如:diff
、clone
等操作,一个真实元素上有许多的内容,如果直接对其进行diff
操作,会去额外diff
一些没有必要的内容;同样的,如果需要进行clone
那么需要将其全部内容进行复制,这也是没必要的。但是,如果将这些操作转移到JavaScript
对象上,那么就会变得简单了操作
dom
是比较昂贵的操作,频繁的dom
操作容易引起页面的重绘和回流,但是通过抽象VNode
进行中间处理,可以有效减少直接操作dom
的次数,从而减少页面重绘和回流方便实现跨平台
同一
VNode
节点可以渲染成不同平台上的对应的内容,比如:渲染在浏览器是dom
元素节点,渲染在Native( iOS、Android)
变为对应的控件、可以实现SSR
、渲染到WebGL
中等等Vue3
中允许开发者基于VNode
实现自定义渲染器(renderer
),以便于针对不同平台进行渲染
vdom
如何生成?在 vue 中我们常常会为组件编写模板 -template
, 这个模板会被编译器 -compiler
编译为渲染函数,在接下来的挂载(mount
)过程中会调用render
函数,返回的对象就是虚拟dom
。但它们还不是真正的dom
,所以会在后续的patch
过程中进一步转化为dom
。
挂载过程结束后,
vue
程序进入更新流程。如果某些响应式数据发生变化,将会引起组件重新render
,此时就会生成新的vdom
,和上一次的渲染结果diff
就能得到变化的地方,从而转换为最小量的dom
操作,高效更新视图
为什么要用 vdom?案例解析
现在有一个场景,实现以下需求:
将该数据展示成一个表格,并且随便修改一个信息,表格也跟着修改。 用 jQuery 实现如下:
这样点击按钮,会有相应的视图变化,但是你审查以下元素,每次改动之后,
table
标签都得重新创建,也就是说table
下面的每一个栏目,不管是数据是否和原来一样,都得重新渲染,这并不是理想中的情况,当其中的一栏数据和原来一样,我们希望这一栏不要重新渲染,因为DOM
重绘相当消耗浏览器性能。因此我们采用 JS 对象模拟的方法,将
DOM
的比对操作放在JS
层,减少浏览器不必要的重绘,提高效率。当然有人说虚拟 DOM 并不比真实的
DOM
快,其实也是有道理的。当上述table
中的每一条数据都改变时,显然真实的DOM
操作更快,因为虚拟DOM
还存在js
中diff
算法的比对过程。所以,上述性能优势仅仅适用于大量数据的渲染并且改变的数据只是一小部分的情况。
如下DOM
结构:
映射成虚拟DOM
就是这样:
使用 snabbdom 实现 vdom
这是一个简易的实现
vdom
功能的库,相比vue
、react
,对于vdom
这块更加简易,适合我们学习vdom
。vdom
里面有两个核心的api
,一个是h
函数,一个是patch
函数,前者用来生成vdom
对象,后者的功能在于做虚拟dom
的比对和将vdom
挂载到真实DOM
上
简单介绍一下这两个函数的用法:
现在我们就来用snabbdom
重写一下刚才的例子:
你会发现, 只有改变的栏目才闪烁,也就是进行重绘 ,数据没有改变的栏目还是保持原样,这样就大大节省了浏览器重新渲染的开销
vue 中使用
h函数
生成虚拟DOM
返回
Vue 项目性能优化-详细
Vue
框架通过数据双向绑定和虚拟DOM
技术,帮我们处理了前端开发中最脏最累的DOM
操作部分, 我们不再需要去考虑如何操作DOM
以及如何最高效地操作DOM
;但Vue
项目中仍然存在项目首屏优化、Webpack
编译配置优化等问题,所以我们仍然需要去关注Vue
项目性能方面的优化,使项目具有更高效的性能、更好的用户体验
代码层面的优化
1. v-if 和 v-show 区分使用场景
v-if
是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块v-show
就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS
display
的none/block
属性进行切换。所以,
v-if
适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show
则适用于需要非常频繁切换条件的场景
2. computed 和 watch 区分使用场景
computed
: 是计算属性,依赖其它属性值,并且computed
的值有缓存,只有它依赖的属性值发生改变,下一次获取computed
的值时才会重新计算 computed 的值;watch
: 更多的是「观察」的作用,类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作
运用场景:
当我们需要进行数值计算,并且依赖于其它数据时,应该使用
computed
,因为可以利用computed
的缓存特性,避免每次获取值时,都要重新计算;当我们需要在数据变化时执行异步或开销较大的操作时,应该使用
watch
,使用watch
选项允许我们执行异步操作 ( 访问一个API
),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的
3. v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
v-for
遍历必须为item
添加key
在列表数据进行遍历渲染时,需要为每一项
item
设置唯一key
值,方便Vue.js
内部机制精准找到该条列表数据。当state
更新时,新的状态值和旧的状态值对比,较快地定位到diff
v-for
遍历避免同时使用v-if
vue2.x
中v-for
比v-if
优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成computed
属性
推荐:
不推荐:
4. 长列表性能优化
Vue
会通过Object.defineProperty
对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要 Vue 来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止Vue
劫持我们的数据呢?可以通过Object.freeze
方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了
5. 事件的销毁
Vue
组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。 如果在 js
内使用 addEventListener
等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露,如:
6. 图片资源懒加载
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用 Vue
的 vue-lazyload
插件
在入口文件 man.js
中引入并使用
在 vue
文件中将 img
标签的 src
属性直接改为 v-lazy
,从而将图片显示方式更改为懒加载显示
以上为 vue-lazyload
插件的简单使用,如果要看插件的更多参数选项,可以查看 vue-lazyload 的 github 地址(opens new window)
7. 路由懒加载
Vue 是单页面应用,可能会有很多的路由引入 ,这样使用 webpcak
打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来
路由懒加载:
8. 第三方插件的按需引入
我们在项目中经常会需要引入第三方插件,如果我们直接引入整个插件,会导致项目的体积太大,我们可以借助 babel-plugin-component
,然后可以只引入需要的组件,以达到减小项目体积的目的。以下为项目中引入 element-ui
组件库为例
将 .babelrc
修改为:
在 main.js
中引入部分组件:
9. 优化无限列表性能
如果你的应用存在非常长或者无限滚动的列表,那么需要采用虚拟列表
的技术来优化性能,只需要渲染少部分区域的内容,减少重新渲染组件和创建 dom
节点的时间。 你可以参考以下开源项目 vue-virtual-scroll-list (opens new window) 和 vue-virtual-scroller (opens new window)来优化这种无限列表的场景的
10. 服务端渲染 SSR or 预渲染
服务端渲染是指 Vue
在客户端将标签渲染成的整个 html
片段的工作在服务端完成,服务端形成的 html
片段直接返回给客户端这个过程就叫做服务端渲染。
如果你的项目的
SEO
和首屏渲染
是评价项目的关键指标,那么你的项目就需要服务端渲染来帮助你实现最佳的初始加载性能和SEO
如果你的
Vue
项目只需改善少数营销页面(例如/
,/about
,/contact
等)的SEO
,那么你可能需要预渲染,在构建时简单地生成针对特定路由的静态HTML
文件。 优点是设置预渲染更简单 ,并可以将你的前端作为一个完全静态的站点,具体你可以使用 prerender-spa-plugin (opens new window) 就可以轻松地添加预渲染
Webpack 层面的优化
1. Webpack 对图片进行压缩
对小于 limit
的图片转化为 base64
格式,其余的不做操作。所以对有些较大的图片资源,在请求资源的时候,加载会很慢,我们可以用 image-webpack-loader
来压缩图片
2. 减少 ES6 转为 ES5 的冗余代码
Babel 插件会在将 ES6 代码转换成 ES5 代码时会注入一些辅助函数,例如下面的 ES6 代码
这段代码再被转换成能正常运行的 ES5 代码时需要以下两个辅助函数:
在默认情况下, Babel
会在每个输出文件中内嵌这些依赖的辅助函数代码,如果多个源代码文件都依赖这些辅助函数,那么这些辅助函数的代码将会出现很多次,造成代码冗余。为了不让这些辅助函数的代码重复出现,可以在依赖它们时通过 require('babel-runtime/helpers/createClass')
的方式导入,这样就能做到只让它们出现一次。babel-plugin-transform-runtime
插件就是用来实现这个作用的,将相关辅助函数进行替换成导入语句,从而减小 babel 编译出来的代码的文件大小
修改 .babelrc
配置文件为:
3. 提取公共代码
如果项目中没有去将每个页面的第三方库和公共模块提取出来,则项目会存在以下问题:
相同的资源被重复加载,浪费用户的流量和服务器的成本。
每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。Webpack
内置了专门用于提取多个Chunk
中的公共部分的插件 CommonsChunkPlugin
,我们在项目中 CommonsChunkPlugin
的配置如下:
4. 模板预编译
当使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数。通常情况下这个过程已经足够快了,但对性能敏感的应用还是最好避免这种用法。
预编译模板最简单的方式就是使用单文件组件——相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串。
如果你使用 webpack,并且喜欢分离 JavaScript 和模板文件,你可以使用 vue-template-loader (opens new window),它也可以在构建过程中把模板文件转换成为 JavaScript 渲染函数
5. 提取组件的 CSS
当使用单文件组件时,组件内的 CSS 会以 style 标签的方式通过 JavaScript 动态注入。这有一些小小的运行时开销,如果你使用服务端渲染,这会导致一段 “无样式内容闪烁 (fouc) ” 。将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存
6. 优化 SourceMap
我们在项目进行打包后,会将开发中的多个文件代码打包到一个文件中,并且经过压缩、去掉多余的空格、babel 编译化后,最终将编译得到的代码会用于线上环境,那么这样处理后的代码和源代码会有很大的差别,当有 bug 的时候,我们只能定位到压缩处理后的代码位置,无法定位到开发环境中的代码,对于开发来说不好调式定位问题,因此 sourceMap
出现了,它就是为了解决不好调式代码问题的
SourceMap
的可选值如下(+
号越多,代表速度越快,-
号越多,代表速度越慢,o
代表中等速度)
开发环境推荐:
cheap-module-eval-source-map
生产环境推荐:
cheap-module-source-map
原因如下:
cheap
: 源代码中的列信息是没有任何作用,因此我们打包后的文件不希望包含列相关信息,只有行信息能建立打包前后的依赖关系。因此不管是开发环境或生产环境,我们都希望添加cheap
的基本类型来忽略打包前后的列信息;module
:不管是开发环境还是正式环境,我们都希望能定位到bug
的源代码具体的位置,比如说某个Vue
文件报错了,我们希望能定位到具体的Vue
文件,因此我们也需要module
配置;soure-map
:source-map
会为每一个打包后的模块生成独立的soucemap
文件 ,因此我们需要增加source-map
属性;eval-source-map
:eval
打包代码的速度非常快,因为它不生成map
文件,但是可以对eval
组合使用eval-source-map
使用会将map
文件以DataURL
的形式存在打包后的js
文件中。在正式环境中不要使用eval-source-map
, 因为它会增加文件的大小,但是在开发环境中,可以试用下,因为他们打包的速度很快。
7. 构建结果输出分析
Webpack 输出的代码可读性非常差而且文件非常大,让我们非常头疼。为了更简单、直观地分析输出结果,社区中出现了许多可视化分析工具。这些工具以图形的方式将结果更直观地展示出来,让我们快速了解问题所在。接下来讲解我们在 Vue 项目中用到的分析工具:webpack-bundle-analyzer
执行 $ npm run build --report
后生成分析报告如下
基础的 Web 技术优化
1. 开启 gzip 压缩
gzip
是GNUzip
的缩写,最早用于UNIX
系统的文件压缩。HTTP
协议上的gzip
编码是一种用来改进web
应用程序性能的技术,web
服务器和客户端(浏览器)必须共同支持 gzip。目前主流的浏览器,Chrome,firefox,IE 等都支持该协议。常见的服务器如 Apache,Nginx,IIS 同样支持,zip
压缩效率非常高,通常可以达到70%
的压缩率,也就是说,如果你的网页有30K
,压缩之后就变成了9K
左右
以下我们以服务端使用我们熟悉的 express
为例,开启 gzip
非常简单,相关步骤如下:
重启服务,观察网络面板里面的 response header
,如果看到如下红圈里的字段则表明 gzip
开启成功
Nginx 开启 gzip 压缩
要想配置生效,记得重启nginx
服务
2. 浏览器缓存
为了提高用户加载页面的速度,对静态资源进行缓存是非常必要的,根据是否需要重新向服务器发起请求来分类,将 HTTP 缓存规则分为两大类(强制缓存,对比缓存)
3. CDN 的使用
浏览器从服务器上下载 CSS、js 和图片等文件时都要和服务器连接,而大部分服务器的带宽有限,如果超过限制,网页就半天反应不过来。而 CDN 可以通过不同的域名来加载文件,从而使下载文件的并发连接数大大增加,且 CDN 具有更好的可用性,更低的网络延迟和丢包率
4. 使用 Chrome Performance 查找性能瓶颈
Chrome
的 Performance
面板可以录制一段时间内的 js
执行细节及时间。使用 Chrome
开发者工具分析页面性能的步骤如下。
打开
Chrome
开发者工具,切换到Performance
面板点击
Record
开始录制刷新页面或展开某个节点
点击
Stop
停止录制
Vue3.2 setup 语法糖汇总
提示:vue3.2
版本开始才能使用语法糖!
在 Vue3.0
中变量必须 return
出来, template
中才能使用;而在 Vue3.2
中只需要在 script
标签上加上 setup
属性,无需 return
, template
便可直接使用,非常的香啊!
1. 如何使用 setup 语法糖
只需在 script
标签上写上 setup
2. data 数据的使用
由于 setup
不需写 return
,所以直接声明数据即可
3. method 方法的使用
4. watchEffect 的使用
5. watch 的使用
6. computed 计算属性的使用
computed
计算属性有两种写法(简写和考虑读写的完整写法)
7. props 父子传值的使用
父组件代码如下(示例):
子组件代码如下(示例):
8. emit 子父传值的使用
父组件代码如下(示例):
子组件代码如下(示例):
9. 获取子组件 ref 变量和 defineExpose 暴露
即vue2
中的获取子组件的ref
,直接在父组件中控制子组件方法和变量的方法
父组件代码如下(示例):
子组件代码如下(示例):
10. 路由 useRoute 和 useRouter 的使用
11. store 仓库的使用
12. await 的支持
setup
语法糖中可直接使用await
,不需要写async
,setup
会自动变成async setup
13. provide 和 inject 祖孙传值
父组件代码如下(示例):
子组件代码如下(示例):
组件中写 name 属性的好处
可以标识组件的具体名称方便调试和查找对应属性
你有对 Vue 项目进行哪些优化?
(1)代码层面的优化
v-if 和 v-show 区分使用场景
computed 和 watch 区分使用场景
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
长列表性能优化
事件的销毁
图片资源懒加载
路由懒加载
第三方插件的按需引入
优化无限列表性能
服务端渲染 SSR or 预渲染
(2)Webpack 层面的优化
Webpack 对图片进行压缩
减少 ES6 转为 ES5 的冗余代码
提取公共代码
模板预编译
提取组件的 CSS
优化 SourceMap
构建结果输出分析
Vue 项目的编译优化
(3)基础的 Web 技术的优化
开启 gzip 压缩
浏览器缓存
CDN 的使用
使用 Chrome Performance 查找性能瓶颈
Vue 模板编译原理
Vue 的编译过程就是将 template 转化为 render 函数的过程 分为以下三步
相关代码如下
Vue 模版编译原理
vue 中的模板 template 无法被浏览器解析并渲染,因为这不属于浏览器的标准,不是正确的 HTML 语法,所有需要将 template 转化成一个 JavaScript 函数,这样浏览器就可以执行这一个函数并渲染出对应的 HTML 元素,就可以让视图跑起来了,这一个转化的过程,就成为模板编译。模板编译又分三个阶段,解析 parse,优化 optimize,生成 generate,最终生成可执行函数 render。
解析阶段:使用大量的正则表达式对 template 字符串进行解析,将标签、指令、属性等转化为抽象语法树 AST。
优化阶段:遍历 AST,找到其中的一些静态节点并进行标记,方便在页面重渲染的时候进行 diff 比较时,直接跳过这一些静态节点,优化 runtime 的性能。
生成阶段:将最终的 AST 转化为 render 函数字符串。
Vue.extend 作用和原理
官方解释:
Vue.extend
使用基础Vue
构造器,创建一个“子类”。参数是一个包含组件选项的对象。
其实就是一个子类构造器 是 Vue
组件的核心 api
实现思路就是使用原型继承的方法返回了 Vue 的子类 并且利用 mergeOptions
把传入组件的 options
和父类的 options
进行了合并
extend
是构造一个组件的语法器。然后这个组件你可以作用到Vue.component
这个全局注册方法里还可以在任意vue
模板里使用组件。 也可以作用到vue
实例或者某个组件中的components
属性中并在内部使用apple
组件。Vue.component
你可以创建 ,也可以取组件。
相关代码如下
谈谈 Vue 和 React 组件化的思想
1.我们在各个页面开发的时候,会产生很多重复的功能,比如 element 中的 xxxx。像这种纯粹非页面的 UI,便成为我们常用的 UI 组件,最初的前端组件也就仅仅指的是 UI 组件
2.随着业务逻辑变得越来多是,我们就想要我们的组件可以处理很多事,这就是我们常说的组件化,这个组件就不是 UI 组件了,而是包具体业务的业务组件
3.这种开发思想就是分而治之。最大程度的降低开发难度和维护成本的效果。并且可以多人协作,每个人写不同的组件,最后像撘积木一样的把它构成一个页面
怎么实现路由懒加载呢
这是一道应用题。当打包应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应组件,这样就会更加高效
回答范例
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。利用路由懒加载我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样会更加高效,是一种优化手段
一般来说,对所有的路由都使用动态导入是个好主意
给
component
选项配置一个返回Promise
组件的函数就可以定义懒加载路由。例如:{ path: '/users/:id', component: () => import('./views/UserDetails') }
结合注释
() => import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
可以做webpack
代码分块
Vue 的基本原理
当一个 Vue 实例创建时,Vue 会遍历 data 中的属性,用 Object.defineProperty(vue3.0 使用 proxy )将它们转为 getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。
diff 算法
答案时间复杂度: 个树的完全 diff
算法是一个时间复杂度为 O(n*3)
,vue 进行优化转化成 O(n)
。
理解:
最小量更新,
key
很重要。这个可以是这个节点的唯一标识,告诉diff
算法,在更改前后它们是同一个 DOM 节点扩展
v-for
为什么要有key
,没有key
会暴力复用,举例子的话随便说一个比如移动节点或者增加节点(修改 DOM),加key
只会移动减少操作 DOM。只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的。
只进行同层比较,不会进行跨层比较。
diff 算法的优化策略:四种命中查找,四个指针
旧前与新前(先比开头,后插入和删除节点的这种情况)
旧后与新后(比结尾,前插入或删除的情况)
旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
--- 问完上面这些如果都能很清楚的话,基本 O 了 ---
以下的这些简单的概念,你肯定也是没有问题的啦😉
评论