前言

马上要秋招了,搜集整理了一些Vue面试题,包括组件、指令、API等相关内容,巩固基础😎秋招冲冲冲!!!本篇包括:

✅计算属性和侦听器的区别
✅事件修饰符
✅单页应用(SPA) VS 多页应用(MPA)
✅如何解决SPA首屏加载速度慢
✅v-if和v-for的优先级

计算属性和侦听器的区别

计算属性(computed)是自动监听依赖值的变化,从而动态返回内容(动态显示新的计算结果)。

监听(watch)是一个过程,在监听的值变化时,可以触发一个回调,并做一些事情。回调函数有两个参数,一个 val (修改后的 data数据),一个 oldVal(原来的 data 数据)。Vue 实例将会在实例化时调用$watch(),遍历 watch对象的每一个属性。

两者用于不同情况下完成计算,显示数据的操作。它们的区别主要来源于用法,只是需要动态值,那就用计算属性;需要知道值的改变后执行业务逻辑,才用 watch,用反或混用虽然可行,但都是不正确的用法。

下面给出这两个特性的基本用法:

计算属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app"> {{total}} </div>

<script>
var vm = new Vue({
el: "#app",
data: {
num: 10,
price: 8.8,
},
computed: {
total: function () {
return this.num * this.price;
},
},
});
</script>

侦听器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="app">  {{total}}  </div>

<script>
var vm = new Vue({
el: "#app",
data: {
num: 10,
price: 8.8,
},
watch: {
num: function (val) {
this.total = val * this.price;
},
price: function (val) {
this.total = this.num * val;
},
},
});
</script>

结合代码进行说明:

  1. 计算属性的应用场景是计算的内容需要依赖多个属性(num、price)的情况;侦听器的应用场景是计算的内容依赖一个属性(仅num发生变化、仅price发生变化)的情况

  2. 计算属性缓存结果时每次都会重新创建变量,而侦听器是直接计算,不会创建变量保存结果。也就意味着,数据如果会反复的发生变化,计算很多次的情况下,计算属性的开销将会更大,也就意味着这种情况不适合使用计算属性,适合使用侦听器。如果一个数据反复会被使用,但是它计算依赖的内容很少发生变化的情况下,计算属性会缓存结果,就更加适合这种情况。

如果 this.num 或者 this.price 没有发生变化,直接获取缓存的总结88作为计算属性的结果。
如果 this.num 或者 this.price 任何一项发生了变化,那么就会重新计算并得到一个总结结果,并重新将结果进行缓存。

  1. computed 的结果是通过return返回的,而 watch 不需要return。

  2. watch 中的参数可以得到侦听属性改变的最新结果,而computed函数没有这种参数。

补充:

  • computed是一个对象时,有 getset 两个选项。
  • computedmethods相比:methods 是一个方法,它可以接受参数,而 computed不能;computed是可以缓存的,methods 不会;一般在 v-for 里,需要根据当前项动态绑定值时,只能用 methods 而不能用 computed,因为 computed 不能传参。
  • computed 可以依赖其它 computed,甚至是其它组件的数据(data)。
  • watch是一个对象时,常用的配置有:handler(执行的函数)、deep(是否深度)、immediate(是否立即执行)
  • computed默认深度依赖,watch 默认浅度观测

参考链接:

事件修饰符

常见的事件修饰符: .stop.prevent.capture.self.once.passive

举例提问:如何给下面这个自定义组件绑定一个原生click事件

1
<custom-component>内容</custom-component>

注意: @click 是自定义事件 click,并不是原生事件 click。绑定原生的 click 是 @click.native="xxx",同时补充说明 .exact会有加分。

.exact 修饰符允许你控制由精确的系统修饰符组合触发的事件。

1
2
3
4
5
6
7
8
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button @click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button @click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button @click.exact="onClick">A</button>

参考链接:

单页应用(SPA) VS 多页应用(MPA)

SPA(single-page application),翻译过来就是单页应用SPA,是一种网络应用程序或网站的模型。

它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换时,打断用户体验。在单页应用中,所有必要的代码(HTMLJavaScriptCSS)都通过单个页面的加载而检索,或者根据需要(通常是为响应用户操作)动态装载适当的资源,并添加到页面。

页面在任何时间点都不会重新加载,也不会将控制转移到其他页面。举个例子来讲,一个杯子,早上装的牛奶,中午装的是开水,晚上装的是茶,我们发现,变的始终是杯子里的内容,而杯子始终是那个杯子。再通俗一点,就是局部刷新。我们熟知的JS框架如react,vue,angular,ember都属于SPA

杯子结构

MPA(MultiPage-page application),翻译过来就是多页应用。在MPA中,每个页面都是一个独立的主页面。当我们在访问另一个页面的时候,都需要重新加载htmlcssjs文件,公共文件则根据需求按需加载。

多页应用

单页应用和多页应用的区别

单页应用(SPA)多页应用(MPA)
组成一个主页面和多个页面片段多个主页面
刷新方式局部刷新整页刷新
url模式哈希模式历史模式
SEO搜索引擎优化难实现,可使用SSR方式改善容易实现
数据传递容易通过url、cookie、localStorage等传递
页面切换速度快,用户体验良好切换加载资源,速度慢,用户体验差
维护成本相对容易相对复杂
优点具有桌面应用的即时性、网站的可移植性和可访问性;
内容的改变不需要重新加载整个页面;
良好的前后端分离,分工更明确
首屏加载较快,SEO优化较好。
缺点不利于搜索引擎的抓取;
首次渲染速度相对较慢(加载整个项目使用的css、js)
页面跳转较慢

参考链接:

如何解决SPA首屏加载速度慢

首屏时间(First Contentful Paint),指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容。首屏加载可以说是用户体验中最重要的环节。

在页面渲染的过程,导致加载速度慢的原因是:网络延时问题、资源文件体积过大、重复发送请求以加载资源、加载脚本的时候,渲染内容堵塞了。

常见的几种SPA首屏优化方式

  1. 减小入口文件体积
  2. 静态资源本地缓存
  3. UI框架按需加载
  4. 图片资源的压缩
  5. 组件重复打包
  6. 开启GZip压缩
  7. 使用SSR

参考链接:

v-if和v-for的优先级

为什么不建议v-if和v-for一起使用?

vue在官方文档中明确指出,永远不要把 v-ifv-for 同时用在同一个元素上

Vue 2 中,v-for 优先于 v-if 被解析,即先执行循环,后判断条件。

举例说明

编写一个p标签,同时使用v-ifv-for

1
2
3
4
5
<div id="app">
<p v-if="isShow" v-for="item in items">
{{ item.title }}
</p>
</div>

创建vue实例,存放isShowitems数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const app = new Vue({
el: "#app",
data() {
return {
items: [
{ title: "foo" },
{ title: "baz" }]
}
},
computed: {
isShow() {
return this.items && this.items.length > 0
}
}
})

模板指令的代码都会生成在render函数中,通过app.$options.render就能得到渲染函数

1
2
3
4
5
6
ƒ anonymous() {
with (this) { return
_c('div', { attrs: { "id": "app" } },
_l((items), function (item)
{ return (isShow) ? _c('p', [_v("\n" + _s(item.title) + "\n")]) : _e() }), 0) }
}

_lvue的列表渲染函数,函数内部都会进行一次if判断

初步得到结论:v-for优先级是比v-if

再将v-forv-if置于不同标签

1
2
3
4
5
<div id="app">
<template v-if="isShow">
<p v-for="item in items">{{item.title}}</p>
</template>
</div>

再输出下render函数

1
2
3
4
5
6
ƒ anonymous() {
with(this){return
_c('div',{attrs:{"id":"app"}},
[(isShow)?[_v("\n"),
_l((items),function(item){return _c('p',[_v(_s(item.title))])})]:_e()],2)}
}

这时候我们可以看到,v-forv-if作用在不同标签时候,是先进行判断,再进行列表的渲染

注意

  1. 永远不要把 v-ifv-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)
  2. 如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环
1
2
3
<template v-if="isShow">
<p v-for="item in items">
</template>
  1. 如果条件出现在循环内部,可通过计算属性computed提前过滤掉那些不需要显示的项
1
2
3
4
5
6
7
computed: {
items: function() {
return this.list.filter(function (item) {
return item.isShow
})
}
}

参考链接:

相关文章