面试题集训4

# 面试题1

说明:问题末尾的数字代表对自己当时回答的打分,✔ 代表后面有关于该问题的重新整理。

  1. 转行原因
  2. vue 和 react 用起来有什么区别?2 ✔
  3. vue双向数据绑定的原理? 数组的更新。8 ✔
  4. 组件间通信的方式?vuex实现原理?state改变,怎么促使视图改变?6 ✔
  5. 为什么选用uni-app?8
  6. (搜索查看电商小程序后)你做的哪些部分?图片加载问题,横向为什么没有也用intersection observer呢? Intersection Observer是浏览器自带的。7 ✔
  7. 为什么要做直播插件?8
  8. 做过哪些项目优化?5 ✔
  9. webpack配置,loader和plugin区别。如何去掉冗余的代码? tree-shaking实现原理。5 ✔
  10. 一个新的路由,怎么知道下载对应的文件?(扩展: 动态插入js的方式) 2 ✔
  11. 按需加载?有哪些方式?最后选择了什么?5
  12. 缓存,强缓存是谁设置的?html文件会缓存吗?4 ✔
  13. cdn内容分发系统?为什么你们项目的静态资源文件没有放到CDN上。5 ✔
  14. 使用腾讯云的移动直播、即时通讯sdk,有什么困难的地方?4 ✔
  15. sentry错误监控,错误是如何收集上报到平台的?(http, websoket, 跨域)2 ✔
  16. 某某项目(简历中的),具体讲解一下?8
  17. 项目里遇到难点、挑战。 4

# 问题2 Vue vs React

相同点:

  1. 使用virtural DOM + Diff算法。
  2. 组件化思想。

不同点:

  1. 模板语法的不同:react通过JSX渲染模板,vue通过拓展的html语法进行渲染。比如react中插值、条件、循环都通过JS语法实现,vue是通过指令v-bind、v-if、v-for实现的。
  2. 监听数据变化原理不同:vue通过getter、setter劫持通知数据变化,react通过比较引用的方式进行。vue使用的响应式数据,而react是不可变数据。vue改变数据直接赋值,react需要调用setState方法(用新的state替换旧的state)。

# 问题3 vue响应式原理

level1: vue2.0中,响应式实现的核心就是 ES5的Object.defineProperty(obj, prop, descriptor). 通过Object.defineProperty()劫持data和props各个属性的getter和setter,getter做依赖收集,setter派发更新。整体来说是一个 数据劫持 + 发布-订阅者模式。

level2: 具体来说, ① vue初始化阶段(beforeCreate之后create之前),遍历data/props,调用Object.defineProperty给每个属性加上getter、setter。② 每个组件、每个computed都会实例化一个watcher(当然也包括每个自定义watcher),订阅渲染/计算所用到的所用data/props/computed,一旦数据发生变化,setter被调用,会通知渲染watcher重新计算、更新组件。

# 问题4 vue组件通信

level1: props+events 父子组件通信(parent/parent/parent/children),vuex 任何组件通信,事件中心emit/emit / emit/on 任何组件的通信, attrs/attrs/attrs/listeners 后代通信(provide / inject)。

level2: vuex运行机制:vuex的state作为一个仓库,提供数据驱动vue component渲染。视图通过dispach派发actions,actions中可以做一些异步操作。actions或者视图通过commit提交给mutations,mutation去改变state。

level3: 源码分析:vuex其实是一个Vue.js插件,插件都需要提供一个install方法,install方法调用会将Vue作为参数传入。Vue.user(plugin)安装插件,也就是执行插件的install方法。会在全局混入一个beforeCreate钩子函数,把示例化的Store保存到所有组件的this.$store中。

level4: mutation改变state, 会触发视图改变的原因?通过vue实现的,[实例化vue,把state作为一个data属性。] ↔️ 核心

let Vue
function install(_Vue) {
  Vue = _Vue
  function vuexInit() {
    const options = this.$options
    console.log('vuexInit -> this.$options', this.$options)
    if (options.store) {
			// // 根实例 this --> Vue
      this.$store =
        typeof options.store === 'function' ? options.store() : options.store
    } else if (options.parent && options.parent.$store) {
			// 组件实例 this --> VueComponent, 如 APP, Home, About...
      this.$store = options.parent.$store
    }
  }
  Vue.mixin({ beforeCreate: vuexInit })
}

class Store {
  constructor(options = {}) {
    const { state = {}, mutations = {}, getters = {} } = options
    this._mutations = mutations
    // getter实现原理
    const computed = {}
    this.getters = {}
    for (let [key, fn] of Object.entries(getters)) {
      computed[key] = () => fn(this.state)
      Object.defineProperty(this.getters, key, {
        get: () => this._vm[key]
      })
    }
    this._vm = new Vue({
      data: { $$state: state }, // 核心原理
      computed
    })
  }

  commit(type, payload) {
    if (this._mutations[type]) {
      this._mutations[type](this.state, payload)
    }
  }

  get state() {
    return this._vm._data.$$state
  }
}

export default { Store, install }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# 问题6 Intersection Observer

level1: 推断节点是否为用户可见,以及多少比例可见,交叉观察器。web API中也有。

level2: 小程序中的IntersectionObserver在Web的基础上,做了一些封装,通过createIntersectionObserver返回一个IntersectionObserver实例。实例方法包括relativeTo、relativeToViewPort、observer、disconnect。relativeTo和relativeToViewPort,相对指定元素或视口的指定位置。observer(selector, cb),观察指定节点,可见性发生变化时触发回调。

level3: Web API, new IntersectionObserver(cb, options)。options里root、rootMargin的配置,可实现小程序中relativeTo、relativeToViewPort方法的效果。另外 thresholds 属性也蛮有趣的,在指定的几个相交比例时触发回调。

# 问题8 项目优化

  1. 移除生产环境的控制台打印。方案很多,esling+pre-commit、使用插件自动去除,插件包括babel-plugin-transform-remove-console、uglifyjs-webpack-plugin、terser-webpack-plugin。最后选择了terser-webpack-plugin,脚手架vue-cli用这个插件来开启缓存和多线程打包,无需安装额外的插件,仅需在configureWebpack中设置terser插件的drop_console为true即可。最好还是养成良好的代码习惯,在开发基本完成后去掉无用的console,vscode中的turbo console就蛮好的。
  2. 第三方库的按需加载。echarts,官方文档里是使用配置文件指定使用的模块,另一种使用babel-plugin-equire实现按需加载。element-ui使用babel-plugin-component实现按需引入。
  3. 公有样式,比如对element-ui部分组件(如弹框、表格、下拉选框等)样式的统一调整。公共组件,比如date-picker、upload-file等在element-ui提供的组件基本上做进一步的封装。自定义组件包括preview-file、搜索框等。
// babel.config.js配置如下:
plugins: ['equire']
// echarts.js
const echarts = equire(['line', 'tooltip', 'legend', 'dataZoom', 'grid']);
export default echarts;
复制代码
1
2
3
4
5
6

前后端数据交换方面,推动项目组使用蓝湖、接口文档,与后端同学协商,规范后台数据返回。

雅虎军规提到的,避免css表达式、滤镜,较少DOM操作,优化图片、精灵图,避免图片空链接等。

性能问题:页面加载性能、动画性能、操作性能。Performance API,记录性能数据。

winter重学前端 优化技术方案:

  1. 缓存:客户端控制的强缓存策略。
  2. 降低请求成本:DNS 由客户端控制,隔一段时间主动请求获取域名IP,不走系统DNS(完全看不懂)。TCP/TLS连接复用,服务器升级到HTTP2,尽量合并域名。
  3. 减少请求数:JS、CSS打包到HTML。JS控制图片异步加载、懒加载。小型图片使用data-uri。
  4. 较少传输体积:尽量使用SVG\gradient代替图片。根据机型和网络状况控制图片清晰度。对低清晰度图片使用锐化来提升体验。设计上避免大型背景图。

# 问题9 loader、plugin、tree shaking

loader

对模块的源代码进行转换,将不同的语言转换为JS,或将内联图像转换为data url。如:文件,url-loader、file-loader。转换编译,babel-loader、ts-loader。模板,html-loader。样式,style-loader、css-loader、less-loader。清理,eslint-loader。框架,vue-loader。

plugin

解决loader无法实现的其他事儿。比如 HtmlWebpackPlugin、CleanWebpackPlugin、webpack-bundle-analyzer、DllPlugin、HotModuleReplacementPlugin。

tree shaking

消除无用的js代码(剔除模块中没有导出或引用的部分)。仅支持ES Module静态引入方式,不支持require运行时动态引入方式。

ES6模块引入是静态分析的,故而可在编译时正确判断加载哪些代码。

可剔除的内容有限。webpack配合uglifyJS打包文件,只能shaking部分代码,像模块代码存在副作用,立即执行函数等都不能shaking。uglifyJS不进行程序流分析,只简单判断变量后续是否被引用、修改,不去排除有可能有副作用的代码。(rollup会)还有,比如项目中router.js引用了页面组件,但是在路由渲染中没有用到,也无法shaking掉。

webpack-deep-scope-analysis-plugin:利用webpack解析出来模块的AST,利用scoped分析工具解析引用关系,排除掉没有用到的模块。

1. babel.config.js 配置,分析文件模块依赖关系,生成AST时,保持ES6不动。
{
	"presets": [ ["env", { "modules": false }] ]
}

2.  方式1,import {Button} from 'antd'; 
    方式2,import {Buttion} from 'antd/lib/button';
           import 'antd/lib/style';
这两种方式,tree-shaking效果差别很大。(副作用范围不同)
babel-plugin-import-fix 插件,遍历AST找出类似import {Button} from 'antd'的结构,进行转换重新生成代码。

3. CSS tree-shaking <https://juejin.im/post/6844903808397475847>
  方式1:mini-css-extract-plugin + purifycss-webpack
	方式2:webpack-css-treeshaking-plugin。
	利用postCSS提供的解析器,将CSS解析成AST,遍历获取选择器与js、html代码匹配,删除匹配不到的,返回AST,重新生成代码。
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 问题10 懒加载路由

考点,vue-router实现原理。

level1:

  1. 将需要懒加载的子模块,打包成单独的文件。ES6的import()。
  2. hashChange时,根据hash变化执行特定的函数,加载子模块。

level2: 实现的三种方式,location.hash + hashChange(),HTML5规范的pushState(IE10) + popState事件监听,abstract nodejs默认值。

**level3:**源码分析。路由安装,利用mixin给每个组件注入beforeCreated和destory钩子函数,在Vue原型上定义route和route和route和router,并进行响应式处理,定义全局的roter-link和router-view组件。根据路由配置创建映射关系。根据传入路径计算出新的路径,在路劲切换过程中,执行一系列的导航守卫函数,更改Url,渲染对应组件。

# 问题12 缓存

html文件也会缓存。项目中使用index.php,后端返回html内容,不会被缓存。

浏览器缓存策略:

  • 强制缓存:(在指定时间内,浏览器直接使用强缓存的内容)

    Expires:Thu.21 Jan 2019 23:59:59 GMT; (HTTP1.0)

    Cache-Control:max-age=3600(HTTP1.1,优先级更高)

    【缓存指令:no-cache需要协商缓存来验证是否过期;no-store不缓存;public客户端代理服务器都可以缓存;private客户端可缓存】

  • 协商缓存:(与服务器协商,确定资源是否更新)

    Last-Modified(服务器下发时间):Thu.21 Jan 2018 23:59:59 GMT;(HTTP1.0)

    If-Modified-Since(浏览器询问) 【可能时间变了,内容没变】

    Etag(服务器下发); (HTTP1.1)

    If-None-Match(浏览器询问)

# 问题13 CDN

CDN,内容分发网络,是建立再承载网基础上的虚拟分布式网络,能够将源站内容缓存到全国或全球的节点服务器上。用户就近获取内容,提高了资源的访问速度,分担源站压力。

img img

使用DNS域名解析引导用户来访问cache服务器。

# 问题14 腾讯云SDK难点

封装IM类,在IM类上定义创建SDK实例,登录/退出,群组加入/退出,IM事件监听和移除。

  1. 移动直播SDK。最开始没有做小程序主播端,使用APP推流。主要做PC和H5拉流这块内容。
  2. 直播协议有以下几种...。
  3. 最开始使用的HLS协议,使用腾讯云提供的TC-Player作为播放器。遇到的问题,延迟高。
  4. 考虑更换协议,但是移动端浏览器不支持flv、rtmp。
  5. 腾讯云web端拉流提供了TC-Player插件,TC-Player本质上就是利用浏览器自带的video标签来做的。
  6. 使用flv作为直播协议,但不直接使用flv格式播放。github两种解决方案,一种是bilibili flv.js,将flv文件流转码复用成MPE随便,通过Media Source Extensions API实现视频的播放。另一种解决方案,是将视频的每一帧画面使用CANVAS绘制。

img

# 问题15 sentry

sentry错误日志通过https发送到sentry的web站点。使用cors实现跨域。

  1. 应用程序(Sentry客户端SDK) 消息上报 web端
  2. 消息处理 放入消息队列(Redis/Rabbitmq)
  3. worker从消息队列中取数据进行处理
  4. 最后由postgresql完成消息存储
**request headers:**
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross site
**response headers:**
access-control-allow-origin: <http://localhost:8080>
access-control-expose-headers: x-sentry-error, retry-after, x-sentry-rate-limits
1
2
3
4
5
6

面试题3

# 面试题2

# 面试题(一面)

  1. 为什么转行?8
  2. 重绘和回流?9 ✔
  3. 3D动画?5
  4. vue-router 路由守卫?全局、单个组件。8 ✔
  5. 闭包?7 ✔
  6. JS线程?宏任务、微任务。8 ✔
  7. v-for和v-if哪个优先级高?6 ✔
  8. 兄弟组件间通信,使用$emit最简单的方法?5 ✔
  9. 有没有自己实现一个简单的 vue? 6 ✔
  10. 是否有关注 vue 的最新动态?8

# 问题2 重绘和回流

回流(重排)

layout布局 > layer分层 > Paint(sheet) >> tiles >> raster >> draw quad >> display

更新元素的几何属性,如改变宽高、位置等,浏览器会触发重新计算布局,排列元素。

  • 改变元素位置、尺寸、字体大小
  • 改变浏览器窗口
  • DOM操作,DOM元素的添加、删除,DOM元素属性的获取和修改

大量、频繁的重绘,会对性能造成影响。应对措施如下:

  • 多个节点的添加,使用fragment包裹一次性添加
  • 多个样式的切换,使用切换class类名的方式
  • 避免频繁读取会引起重排的属性和方法,主要是宽高、位置等布局类的,如offsetWidth、offsetHeight、getComputedStyle()等
  • 将可能重绘的元素,使用absolute或fixed脱离文档流,较少重排影响的范围
  • 优化动画,canvas、CSS3 transform3D、WebGL,启用GPU硬件加速。

关于DOM操作这块,浏览器自身也做了很多优化,比如将多次回流重绘合到一起。

重绘

元素的外观发生变化,但是没有改变布局。如color、border-style、visibility、background等。

直接进入重新绘制阶段,执行效率比重排高。

Paint(sheet) >> tiles >> raster >> draw quad >> display

# 问题4 路由守卫

全局守卫

  1. 全局前置守卫。router.beforeEach
  2. 全局解析守卫。router.beforeResolve。调用时机(不同于beforeEach):导航被确认之前,所有组件内守卫和异步路由组件被解析之后。
  3. 全局后置守卫。router.afterEach。

路由守卫(独享)

在路由配置文件定义beforeEnter。

组件内守卫

  1. beforeRouteEnter。渲染该组件的对应路由被confirm前调用,不能获取组件示例this(还没创建)。
  2. beforeRouteUpdate。调用时机:foo/1与foo/2之间跳转时,复用当前组件实例,
  3. beforeRouteLeave。
import VueRouter from 'vue-router';
const router = new VueRouter({
	route: [
		{
			path: '/foo',
			component: Foo,
			beforeEnter: (to, from, next) => {}
		}
	]
});
router.beforeEach((to, from, next) => {});
router.beforeResolve((to, from, next) => {});
router.afterEach((to, from) => {});

new Vue({ router, render:h=>h(APP) }).$mount('#app');

// 组件内
const Foo = {
	template: '...',
	beforeRouteEnter(to, from, next) {
		next(vm => {}); // 可以传入回调,导航被确认时执行回调,通过vm访问组件示例
	},
	beforeRouteUpdate(to, from, next) {}, // next不支持传入回调
	beforeRouteLeave(to, from, next) {}
}
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 问题5 闭包

一个带了环境的函数。相比纯函数,多引用一些外部变量。

通常函数的作用域及其所有变量都会在函数执行结束后被销毁;而闭包,可以访问上级作用域,即使外层函数执行完,外层函数的作用域中能被闭包访问的,会一直保存在内存中,直到闭包不存在。

  • ES6 之前,利用立即执行函数(创建闭包后立即执行销毁)模仿块级作用域。
  • 节流、防抖、函数柯里化等,都用到了闭包。

# 问题6 JS线程

JS是单线程的,JS引擎内部会维护一个任务队列。

  1. 接受到任务后,首先判断是同步还是异步任务。
  2. 如果是同步任务,压入执行栈立即执行,执行完毕后出栈。
  3. 如果是异步任务,交给定时器触发线程或HTTP请求线程,在特定时机,由事件触发线程将对应的回调函数的任务加入到队列中。
  4. 当同步任务执行完毕,执行栈清空,会从任务队列头部读取、执行任务。

事件触发线程:用来控制浏览器事件循环,注意这不归 JavaScript 引擎线程管,当事件被触发时,该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。

主线程从任务队列读取事件,是循环不断的。

优先执行主线程上的任务,然后执行微任务,最后执行宏任务。

# 问题7 v-for、v-if优先级

v-for的优先级更高。也就是v-if将在每个循环项中重复执行。

vue风格指南以及eslint都告诉我们不要把v-if和v-for同时用在同一个元素上。原因:在每个循环项重复执行v-if的判断,对性能有一定的影响。

两种常见的场景,第一是需要使用循环项或其属性判断是否渲染,这个可以将循环列表替换为一个计算属性。第二是需要使用另一个变量作为渲染条件,这个v-if可以移动到容器元素,或者使用template元素。

HTML5的内容模板template标签元素,一个天然display为none的元素(设置display:block就会显示)。加载页面时不会显示,运行时可以进行JS操作。template元素的content属性,是只读的DocumentFragment。

# 问题8 兄弟组件使用$emit

vm.$root,当前组件的根Vue实例。

vm.$on(event, callback),监听当前实例上的自定义事件。

vm.$emit(event, data),触发当前实例上的自定义事件。

# 问题9:vue 简单实现

详见 理解&实现(一):Vue2 响应式原理 (opens new window)理解&实现(二):Vue3 响应式原理 (opens new window)

# 面试题(二面)

  1. 为什么转行?
  2. 目前在公司负责什么项目?在团队中扮演什么角色?
  3. 是否有做过 h5 小游戏?
  4. 你最看重团队的哪些方面?
  5. 你觉当刚才面试你人的水平如何?是否能对你的技术成长有帮助?
  6. 是否能接受加班?「我们不996,但是项目排期紧的时候,需要你每天都到公司。不忙的时候可以申请调休,但不能连续休好几天 /(ㄒoㄒ)/」
  7. 什么时候能入职?

# 面试题3

  1. 介绍一个你做得最好的项目。8
  2. 前端控制路由,如果只是在前端控制路由,用户随便输入一个地址,后台需要接口判断,会存在先跳到页面再跳转到登录页面?7 ✔
  3. vue生命周期,详细讲解一下。created和mounted周期的区别,哪个更适合请求后台数据?8 ✔
  4. computed和watch区别?6 ✔
  5. 移动端适配方案?rem布局具体实现?7 ✔
  6. cookie、localStorage、SessionStorage 9 ✔
  7. preload 和 prefetch 7 ✔
  8. 性能分析 6 ✔
  9. undefined 和 null 的区别 9 ✔
  10. ES6新增的数组操作方法 「脑子突然短路😵,没说出来几个 」 6 ✔

# 问题2 路由控制

除部分路由如login,其他路由必须登录过(有token、知道用户角色)。可以放到beforeEach路由守卫里判断当前路由角色权限,是否包含当前用户。

# 问题3 vue生命周期

  1. new Vue(),初始化事件和生命周期
  2. beforeCreate($el和data都是undefined)
  3. 初始化数据和方法(data和props的响应式处理,mehods方法声明)
  4. created($el是undefined,修改data不触发update)
  5. 判断有没有el项(vm.$mount(el)),判断有没有模板(没有将el外层的HTML当模板),将模板编译成渲染函数,返回虚拟DOM
  6. beforeMounted($el是虚拟DOM,修改data不触发update)
  7. 创建正式DOM替换虚拟DOM,挂载到页面指定容器显示
  8. mounted(可操作真实DOM)
  9. 数据变更
  10. beforeUpdate
  11. 重新渲染虚拟DOM并通过DIFF算法比较差异更新真实DOM
  12. updated
  13. 调用vm.$destory()
  14. beforeDestory(清理计时器、事件)
  15. 移除数据监听、事件监听和子组件
  16. destoryed(实例不可用)

created vs mounted

created和mounted时间差为几十ms,异步请求返回花费的时间一般都会比这个差值长,所以都会引起页面的重新渲染。只是created会早几十ms。

# 问题4 computed和watch

computed计算属性,是基于他的响应式依赖进行缓存的,只有相关响应式依赖变更才会重新求值。多次访问计算属性或立即返回之前的计算结果,不会再次执行函数。(即时重新渲染时使用缓存,相比而言方法会在每次重新渲染时调用)

watch侦听器,更适用于监听数据变化是执行一些异步操作或其他逻辑处理,watch还可以设置deep为true,实现对象属性的深度监听(嵌套多深都可以)。

值得注意的一点,不要使用箭头函数定义watcher函数,平时的vue项目里使用箭头函数,this指向undefined,html中script引入vue这种this指向window。

# 问题5 移动端适配方案

px+flex方案

一些场景可能希望手机越大,看到的内容越多,可以直接使用px。不过元素、图片布局的适应需根据业务需求来做。

rem布局

通过html根元素进行适配,1rem=1根元素字体大小,根据不同屏幕使用CSS媒体查询或JS设定根元素的大小。

px2rem,可以自己写方法,也可以使用依赖包px2rem。

vw布局

依赖包,postcss-px-to-viewport

vw + rem布局

1vw = 视口宽度 * 1%

如根元素的大小,使用vw,其他元素布局使用rem。

viewport

移动端必须设置viewport,@media才能拿到符合视觉宽度的尺寸。

  • layoutviewport:布局视口,大于实际屏幕。
  • visualviewport:可见视口
  • idealviewport:理想视口,设备尺寸

# 问题6 cookie、localStorage、SessionStorage

  1. 数据存储方面:cookie在同源的HTTP请求里,在服务器和客户端来回传递。storage是本地保存。
  2. 存储数据大小:cookie限制4kb,storage约5MB。
  3. 数据有效期:cookie的有效期与过期时间设置有关(默认是会话),sessionStorage 当前标签页有效,localStorage始终有效。
  4. 作用域:cookie、localStorage同源窗口,sessionStorage当前标签页
  5. 操作:cookie只作为document的一个属性可获取,没有其他操作方法。storage邮getItem\setItem\removeItem\clear等方法。

# 问题7 preload和prefetch

preload

让浏览器提前加载指定资源(不执行,多为当前页面的资源),不阻塞渲染和document的onload时间。

preload会提升资源加载的优先级(一定加载,不管有没有遇到资源依赖)。

跨域文件preload,需要加上 crossorigin属性。(跨域不加crossorigin的preload请求缺少origin字段)

prefetch

告诉浏览器可能需要的资源(下一个页面的资源),不一定会加载。

preload、prefetch混用,不会复用资源,会重复加载。

import(/* webpackPreload: true */ 'ChartingLibrary');
import(/* webpackPrefetch: true */ 'LoginModal');
复制代码
<!--type提示MIME类型,crossorigin处理跨域-->
<link rel="preload" href="sintel-short.mp4" as="video" type="video/mp4">
<link rel="preload" href="fonts/cicle_fina-webfont.svg" as="font" type="image/svg+xml" crossorigin="anonymous">
<!--响应式预加载-->
<link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)">
<link rel="preload" href="bg-image-wide.png" as="image" media="(min-width: 601px)">
复制代码
1
2
3
4
5
6
7
8
9
10

加载优先级:

  1. Highest:html、css、font
  2. High:JS(直接引入、preload)、font(preload)
  3. Low:JS(异步加载 async)
  4. Lowest:css/js(prefetch)

async:异步加载js文件,如果浏览器空闲并且load事件触发,会在load事件触发前执行。

defer:异步加载js文件,脚本延迟到文档解析、显示之后执行,效果与window.onload类似。

# 问题8 性能分析

  • chrome开发工具的network面板,有waterfall(Timing),有TCP连接 queueing事件,请求发送requres sent时间,TTFB服务器处理返回的时间,content download时间。
  • chrome插件,lighthouse,检测页面性能,生成报告,提交优化建议。
  • performance API:timing字段如下
{
  "navigationStart": 1599730498850,
  "unloadEventStart": 1599730499155,
  "unloadEventEnd": 1599730499156,
  "redirectStart": 0,
  "redirectEnd": 0,
  "fetchStart": 1599730498852,
  "domainLookupStart": 1599730498852,
  "domainLookupEnd": 1599730498852,
  "connectStart": 1599730498852,
  "connectEnd": 1599730498852,
  "secureConnectionStart": 0,
  "requestStart": 1599730498854,
  "responseStart": 1599730499151,
  "responseEnd": 1599730499163,
  "domLoading": 1599730499172,
  "domInteractive": 1599730499517,
  "domContentLoadedEventStart": 1599730499518,
  "domContentLoadedEventEnd": 1599730499525,
  "domComplete": 1599730501370,
  "loadEventStart": 1599730501370,
  "loadEventEnd": 1599730501373
}
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 问题9 null和undefined

相同点:

  • 都是一种基础数据类型,且该类型只有一个值
  • 布尔转换都是false,undefined == null √
  • 没有属性和方法

不同点

  • undefined不是关键字,null是
  • undefined未初始化,null已初始化
  • typeof undefined结果是undefined,typeof null结果是Object
  • Number(null) 0,Number(undefined) NaN。

# 问题10 ES6数组操作方法

  • includes、fill、find、findIndex
  • entries、keys、values(用的比较少,Object.entries/keys/values用的比较多)
  • copywith、flat、flatMap

**Array.from:**把两类对象转换成真正的数组,一类是类似数组的对象(array-like,像HTMLCollection、NodeList集合、arguments对象),另一类是可遍历的对象(包括ES6的Set和map)。所有部署了Iterator接口的数据结构。

Array.from与拓展运算符的区别

  1. Array.from还支持类似数组的对象,如Array.from({length:2}),返回[undefined, undefined]。
  2. Array.from支持第二个参数,类似数组的map方法。

Array.of:将一组值转换成数组。区别于 Array() 或 new Array() ,Array(3) 得到的是 [ , , ],Array.of(3) 得到的是 [3]。

# 参考链接

https://juejin.cn/post/6937656750242070559

上次更新: 2022/04/15, 05:41:34
×