一、HTML 篇
1. 简述一下你对 HTML 语义化的理解?
2. 标签上 title 与 alt 属性的区别是什么?
3. iframe的优缺点?
4. href 与 src?
二、CSS 篇
1. 介绍一下 CSS 的盒子模型?
2. css 选择器优先级?
3. 垂直居中几种方式?
4. 简明说一下 CSS link 与 @import 的区别和用法?
5. rgba和opacity的透明效果有什么不同?
6. display:none和visibility:hidden的区别?
7. position的值, relative和absolute分别是相对于谁进行定位的?
8. 画一条0.5px的直线?
height: 1px; transform: scale(0.5); 12
9. calc, support, media各自的含义及用法?
10. 1rem、1em、1vh、1px各自代表的含义?
11. 画一个三角形?
.a { width: 0; height: 0; border-width: 100px; border-style: solid; border-color: transparent #0099CC transparent transparent; transform: rotate(90deg); /*顺时针旋转90°*/ } <div class="a"></div> 123456789
三、HTML / CSS 混合篇
1. HTML5、CSS3 里面都新增了那些新特性?
2. BFC 是什么?
3. 常见兼容性问题?
四、JS 篇
1. JS 数据类型 ?
2. 判断一个值是什么类型有哪些方法?
3. null 和 undefined 的区别?
4. 怎么判断一个变量arr的话是否为数组(此题用 typeof 不行)?
5. “ ===”、“ ==”的区别?
6. “eval是做什么的?
7. 箭头函数有哪些特点?
8. var、let、const 区别?
9. new操作符具体干了什么呢?
10. JSON 的了解?
11. document.write 和 innerHTML 的区别?
12. ajax过程?
13. 请解释一下 JavaScript 的同源策略?
14. 介绍一下闭包和闭包常用场景?
15. javascript的内存(垃圾)回收机制?
16. JavaScript原型,原型链 ? 有什么特点?
17. 用js递归的方式写1到100求和?
function add(num1, num2) { const num = num1 + num2; if(num2 === 100) { return num; } else { return add(num, num2 + 1) } } var sum = add(1, 2); 123456789
18. 事件队列(宏任务微任务)
可以分为微任务(micro task)队列和宏任务(macro task)队列。
微任务一般比宏任务先执行,并且微任务队列只有一个,宏任务队列可能有多个。另外我们常见的点击和键盘等事件也属于宏任务。
下面我们看一下常见宏任务和常见微任务。
常见宏任务:
-
setTimeout()
-
setInterval()
-
setImmediate()
常见微任务:
-
promise.then()、promise.catch()
-
new MutaionObserver()
-
process.nextTick()
微任务和宏任务的本质区别。
-
宏任务特征:有明确的异步任务需要执行和回调;需要其他异步线程支持。
-
微任务特征:没有明确的异步任务需要执行,只有回调;不需要其他异步线程支持。
setTimeout(function () { console.log("1"); }, 0); async function async1() { console.log("2"); const data = await async2(); console.log("3"); return data; } async function async2() { return new Promise((resolve) => { console.log("4"); resolve("async2的结果"); }).then((data) => { console.log("5"); return data; }); } async1().then((data) => { console.log("6"); console.log(data); }); new Promise(function (resolve) { console.log("7"); resolve() }).then(function () { console.log("8"); }); // 2 4 7 5 8 3 6 async2的结果 1 123456789101112131415161718192021222324252627282930
19. async/await
async 是一个通过异步执行并隐式返回 Promise 作为结果的函数。是Generator函数的语法糖,并对Generator函数进行了改进。 改进:
-
内置执行器,无需手动执行 next() 方法。
-
更好的语义
-
更广的适用性:co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。
-
返回值是 Promise,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用。
-
async 隐式返回 Promise 作为结果的函数,那么可以简单理解为,await后面的函数执行完毕时,await会产生一个微任务(Promise.then是微任务)。
20. JavaScript 是单线程的,浏览器是多进程的
-
每打开一个新网页就会创建一个渲染进程
-
渲染进程是多线程的
-
负责页面渲染的 GUI 渲染线程
-
负责JavaScript的执行的 JavaScript 引擎线程,
-
负责浏览器事件循环的事件触发线程,注意这不归 JavaScript 引擎线程管
-
负责定时器的定时触发器线程,setTimeout 中低于 4ms 的时间间隔算为4ms
-
负责XMLHttpRequest的异步 http 请求线程
-
GUI 渲染线程与 JavaScript 引擎线程是互斥的
-
单线程JavaScript是因为避免 DOM 渲染的冲突,web worker 支持多线程,但是 web worker 不能访问 window 对象,document 对象等。
五、Vue 篇
1. 谈谈你对MVVM开发模式的理解?
2. v-if 和 v-show 有什么区别?
3. 你使用过 Vuex 吗?
4. 说说你对 SPA 单页面的理解,它的优缺点分别是什么?
5. Class 与 Style 如何动态绑定?
-
对象语法:
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div> data: { isActive: true, hasError: false } 12345
-
数组语法:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div> data: { activeClass: 'active', errorClass: 'text-danger' } 12345
-
对象语法:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> data: { activeColor: 'red', fontSize: 30 } 12345
-
数组语法:
<div v-bind:style="[styleColor, styleSize]"></div> data: { styleColor: { color: 'red' }, styleSize:{ fontSize:'23px' } } 123456789
6. 怎样理解 Vue 的单向数据流?
7. computed 和 watch 的区别和运用的场景?
8. 直接给一个数组项赋值,Vue 能检测到变化吗?
-
为了解决第一个问题,Vue 提供了以下操作方法:
// Vue.set Vue.set(vm.items, indexOfItem, newValue) // vm.$set,Vue.set的一个别名 vm.$set(vm.items, indexOfItem, newValue) // Array.prototype.splice vm.items.splice(indexOfItem, 1, newValue) 123456
-
为了解决第二个问题,Vue 提供了以下操作方法:
// Array.prototype.splice vm.items.splice(newLength) 12
9. 谈谈你对 Vue 生命周期的理解?
-
生命周期是什么?
-
各个生命周期的作用
10. Vue 的父组件和子组件生命周期钩子函数执行顺序?
11. 父组件可以监听到子组件的生命周期吗?
// Parent.vue <Child @mounted="doSomething"/> // Child.vue mounted() { this.$emit("mounted"); } 1234567
// Parent.vue <Child @hook:mounted="doSomething" ></Child> doSomething() { console.log('父组件监听到 mounted 钩子函数 ...'); }, // Child.vue mounted(){ console.log('子组件触发 mounted 钩子函数 ...'); }, // 以上输出顺序为: // 子组件触发 mounted 钩子函数 ... // 父组件监听到 mounted 钩子函数 ... 123456789101112131415
12. 谈谈你对 keep-alive 的了解?
13. 组件中 data 为什么是一个函数?
-
为什么组件中的 data 必须是一个函数,然后 return 一个对象,而 new Vue 实例里,data 可以直接是一个对象?
14. v-model 的原理?
-
以 input 表单元素为例:
<input v-model='something'> 1
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value"> 1
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件: <ModelChild v-model="message"></ModelChild> 子组件: <div>{{value}}</div> props:{ value: String }, methods: { test1(){ this.$emit('input', '小红') }, }, 1234567891011121314
15. Vue 组件间通信有哪几种方式?
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。
16. 使用过 Vue SSR 吗?说说 SSR?
17. vue-router 路由模式有几种?
switch (mode) { case 'history': this.history = new HTML5History(this, options.base) break case 'hash': this.history = new HashHistory(this, options.base, this.fallback) break case 'abstract': this.history = new AbstractHistory(this, options.base) break default: if (process.env.NODE_ENV !== 'production') { assert(false, `invalid mode: ${mode}`) } } 123456789101112131415
18. 能说下 vue-router 中常用的 hash 和 history 路由模式实现原理吗?
19. Vue 框架怎么实现对象和数组的监听?
20. Vue 是如何实现数据双向绑定的?
/** * Observe a list of Array items. */ observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) // observe 功能为监测数据的变化 } } /** * 对属性进行递归遍历 */ let childOb = !shallow && observe(val) // observe 功能为监测数据的变化 12345678910111213
21. Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?
export function set (target: Array<any> | Object, key: any, val: any): any { // target 为数组 if (Array.isArray(target) && isValidArrayIndex(key)) { // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误 target.length = Math.max(target.length, key) // 利用数组的splice变异方法触发响应式 target.splice(key, 1, val) return val } // key 已经存在,直接修改属性值 if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ // target 本身就不是响应式数据, 直接赋值 if (!ob) { target[key] = val return val } // 对属性进行响应式处理 defineReactive(ob.value, key, val) ob.dep.notify() return val } 12345678910111213141516171819202122232425
22. 虚拟 DOM 的优缺点?
23. 虚拟 DOM 实现原理?
24. Vue 中的 key 有什么作用?
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 } 123456789
25. 你有对 Vue 项目进行哪些优化?
26. 对于 vue3.0 特性你有什么了解的吗?
27. 响应式原理(变化侦测)
使用发布订阅模式将数据劫持和模板编译结合,实现双向绑定
1、observer: 封装 Object.defineProperty 方法用来劫持对象属性的getter和setter,以此来追踪数据变化。
2、读取数据时触发getter来收集依赖(Watcher)到Dep。 3、修改数据时触发setter,并遍历依赖列表,通知所有相关依赖(Watcher) 4、Dep 类为依赖找一个存储依赖的地方,用来收集和管理依赖,在getter中收集,在setter中通知。 5、Watcher 类就是收集的依赖,实际上是一个订阅器,Watcher会将自己的实例赋值给window.target(全局变量)上,然后去主动访问属性,触发属性的getter,getter中会将此Watcher收集到Dep中,Watcher的update方法会在Dep的通知方法中被调用,触发更新。 6、Observer 类用来将一个对象的所有属性和子属性都变成响应式的,通过递归调用defineReactive来实现。 7、由于无法侦测对象上新增/删除属性,所以提供 $set 和 $delete API5。
28. Object.defineProperty怎么用, 三个参数?,有什么作用啊?
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
obj:需要定义属性的对象 prop:需要定义的属性 {}:要定义或修改的属性描述符。 value: "18", // 设置默认值 (与 get() 互斥) enumerable: true, //这一句控制属性可以枚举 enumerable 改为true 就可以参与遍历了 默认值false writable: true, // 该属性是否可写 默认值false (与 set() 互斥) configurable: true, // 该属性是否可被删除 默认值false get // 当有人读取 prop 的时候 get函数就会调用,并且返回就是 sss 的值 set // 当有人修改 prop 的时候 set函数就会调用, 有个参数这个参数就是修改后的值 123456789
29. vue2和vue3的响应式原理都有什么区别呢?
vue2 用的是 Object.defindProperty 但是vue3用的是Proxy
Object.defindProperty 缺点:
-
一次只能对一个属性进行监听,需要遍历来对所有属性监听
-
对于对象的新增属性,需要手动监听
-
对于数组通过
push
、unshift
方法增加的元素,也无法监听
Proxy就没有这个问题,可以监听整个对象的数据变化,所以用vue3.0会用Proxy代替definedProperty。
30. Vue的patch diff 算法
patch将新老VNode节点进行比对,然后将根据两者的比较结果进行最小单位地修改视图,而不是将整个视图根据新的VNode重绘。patch的核心在于diff算法,这套算法可以高效地比较virtual DOM的变更,得出变化以修改视图。
diff算法核心是通过同层的树节点进行比较而非对树进行逐层搜索遍历的方式,所以时间复杂度只有O(n),是一种相当高效的算法。
-
同层级比较(只比较同一层级,不跨级比较)
-
tag 不相同,则直接删除重建,不在深度比较
-
tag 和 key,两个都相同,则认为是相同节点,会进行深度比较
31. Vue 模板编译原理
-
模板字符串 转换成 element AST(解析器)
-
对 AST 进行静态节点标记,主要用来做虚拟DOM的渲染优化(优化器)
-
使用 element AST 生成 render 函数代码字符串(代码生成器)
在组件渲染的时候直接调用 render 即可
32. Vue原理总结
【模板编译】将template模板,经过编译系统后生成VNode,(模板字符串→AST→Render函数)
【渲染】然后再通过渲染系统来将VNode生成真实DOM(document.createElement && Mount挂载到真实DOM节点上)
【响应式】通过响应式系统对数据进行监听,当数据发生改变时,触发依赖项(组件)
【Diff & Patch】组件内收到通知后,会通过diff算法对比VNode的变化,尽可能复用代码,找出最小差异,保证性能消耗最小。
【渲染】拿到需要新增/删除/修改的VNode后,逐一去操作真实DOM进行修改(通过选择器选择到对应真实DOM节点进行修改)
六、Webpack 篇
1. 谈谈你对Webpack的理解(Webpack是什么?)
Webpack 是一个 静态模块打包器,可以分析各个模块的依赖关系,项目中的所有资源皆为模块,通过分析模块间的依赖关系,在其内部递归构建出一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个 bundle
。最终编绎输出模块为 HTML、JavaScript、CSS 以及各种静态文件(图片、字体等)。
webpack 就像一条生产线,要经过一系列处理流程(loader)后才能将源文件转换成输出结果。 这条生产线上的每个处理流程的职责都是单一的,多个流程之间有存在依赖关系,只有完成当前处理后才能交给下一个流程去处理。 插件就像是一个插入到生产线中的一个功能,在特定的时机对生产线上的资源做处理。 webpack 在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。
webpack的主要作用如下:
-
模块打包 可以将不同模块的文件打包整合在一起,并且保证它们之间的引用正确,执行有序。利用打包我们就可以在开发的时候根据我们自己的业务自由划分文件模块,保证项目结构的清晰和可读性。
-
编译兼容 在前端的“上古时期”,手写一堆浏览器兼容代码一直是令前端工程师头皮发麻的事情,而在今天这个问题被大大的弱化了,通过webpack的Loader机制,不仅仅可以帮助我们对代码做polyfill,还可以编译转换诸如.less,.vue,.jsx这类在浏览器无法识别的格式文件,让我们在开发的时候可以使用新特性和新语法做开发,提高开发效率。
-
能力扩展 通过webpack的Plugin机制,我们在实现模块化打包和编译兼容的基础上,可以进一步实现诸如按需加载,代码压缩等一系列功能,帮助我们进一步提高自动化程度,工程效率以及打包输出的质量。
2. Webpack的打包过程/打包原理/构建流程?
-
初始化:启动构建,读取与合并配置参数,加载plugin,实例化Compiler
-
编译:从Entry出发,针对每个Module串行调用对应的Loader去翻译文件中的内容,再找到该Module依赖的Module,递归的进行编译处理
-
输出:将编译后的Module组合成Chunk,将Chunk转换成文件,输出到文件系统中
细节:
Webpack CLI 通过 yargs模块解析 CLI 参数,并转化为配置对象option(单入口:Object,多入口:Array),调用 webpack(option) 创建 compiler 对象。
如果有 option.plugin,则遍历调用plugin.apply()来注册 plugin,
判断是否开启了 watch,如果开启则调用 compiler.watch,否则调用 compiler.run,开始构建。
创建 Compilation 对象来收集全部资源和信息,然后触发 make 钩子。
make阶段从入口开始递归所有依赖,
每次遍历时调用对应Loader翻译文件中内容,然后生成AST,遍历AST找到下个依赖继续递归,
根据入口和模块之间关系组装chunk,输出到dist中的一个文件内。
在以上过程中,webpack会在特定的时间点(使用tapable模块)广播特定的事件,插件监听事件并执行相应的逻辑,并且插件可以调用webpack提供的api改变webpack的运行结果
3. loader的作用
webpack中的loader是一个函数,主要为了实现源码的转换,所以loader函数会以源码作为参数,比如,将ES6转换为ES5,将less转换为css,然后再将css转换为js,以便能嵌入到html文件中。
4. 有哪些常见的Loader?他们是解决什么问题的?
常用的loader如下:
-
image-loader:加载并且压缩图片文件。
-
less-loader:加载并编译 LESS 文件。
-
sass-loader:加载并编译 SASS/SCSS 文件。
-
css-loader:加载 CSS,支持模块化、压缩、文件导入等特性,使用css-loader必须要配合使用style-loader。
-
style-loader:用于将 CSS 编译完成的样式,挂载到页面的 style 标签上。需要注意 loader 执行顺序,style-loader 要放在第一位,loader 都是从后往前执行。
-
babel-loader:把 ES6 转换成 ES5
-
postcss-loader:扩展 CSS 语法,使用下一代 CSS,可以配合 autoprefixer 插件自动补齐 CSS3 前缀。
-
eslint-loader:通过 ESLint 检查 JavaScript 代码。
-
vue-loader:加载并编译 Vue 组件。
-
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
-
url-loader:与 file-loader 类似,区别是用户可以设置一个阈值,大于阈值会交给 file-loader 处理,小于阈值时返回文件 base64 形式编码 (处理图片和字体)。
-
source-map-loader:加载额外的 Source Map 文件,以方便断点调试。
5. plugin的作用
plugin是一个类,类中有一个apply()方法,主要用于Plugin的安装,可以在其中监听一些来自编译器发出的事件,在合适的时机做一些事情。
6. 有哪些常见的Plugin?他们是解决什么问题的?
-
html-webpack-plugin:可以复制一个有结构的html文件,并自动引入打包输出的所有资源(JS/CSS)
-
clean-webpack-plugin:重新打包自动清空 dist 目录
-
mini-css-extract-plugin:提取 js 中的 css 成单独文件
-
optimize-css-assets-webpack-plugin:压缩css
-
uglifyjs-webpack-plugin:压缩js
-
commons-chunk-plugin:提取公共代码
-
define-plugin:定义环境变量
7. Webpack中Loader和Plugin的区别
运行时机 1.loader运行在编译阶段 2.plugins 在整个周期都起作用
使用方式 Loader:1.下载 2.使用 Plugin:1.下载 2.引用 3.使用
8. webpack的热更新是如何做到的?说明其原理?
热更新的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk 需要更新的部分),实际上webpack-dev-server与浏览器之间维护了一个websocket,当本地资源发生变化时,webpack-dev-server会向浏览器推送更新,并带上构建时的hash,让客户端与上一次资源进行对比。客户端对比出差异后会向webpack-dev-server发起 Ajax 请求来获取更改内容(文件列表、hash),这样客户端就可以再借助这些信息继续向webpack-dev-server发起 jsonp 请求获取该chunk的增量更新。
后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)由HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像react-hot-loader和vue-loader都是借助这些 API 实现热更新。
9. 如何解决循环依赖问题
Webpack 中将 require 替换为 webpack_require,会根据 moduleId 到 installedModules 找是否加载过,加载过则直接返回之前的 export,不会重复加载。
10. 如何提高Webpack构建速度
1. 代码压缩
module.exports = { plugin:[ new HtmlwebpackPlugin({ minify:{ minifyCSS: false, // 是否压缩css collapseWhitespace: false, // 是否折叠空格 removeComments: true // 是否移除注释 } }) ] } 1234567891011
2. 图片压缩
3. Tree Shaking
Tree Shaking是一个术语,在计算机中表示消除死代码,依赖于 ES Module 的静态语法分析(不执行任何的代码,可以明确知道模块的依赖关系)。在webpack实现Tree shaking有两种方案:
usedExports:通过标记某些函数是否被使用,之后通过 Terser 来进行优化的
module.exports = { ... optimization:{ usedExports } } 123456
使用之后,没被用上的代码在webpack打包中会加入unused harmony export mul注释,用来告知Terser在优化时,可以删除掉这段代码。
sideEffects:跳过整个模块/文件,直接查看该文件是否有副作用
sideEffects用于告知webpack compiler哪些模块时有副作用,配置方法是在package.json中设置sideEffects属性。如果sideEffects设置为false,就是告知webpack可以安全的删除未用到的exports。如果有些文件需要保留,可以设置为数组的形式,如:
"sideEffecis":[ "./src/util/format.js", "*.css" // 所有的css文件 ] 1234
4. 缩小打包域
排除webpack不需要解析的模块,即在使用loader的时候,在尽量少的模块中去使用。可以借助 include和exclude这两个参数,规定loader只在那些模块应用和在哪些模块不应用。
5. 减少 ES6 转为 ES5 的冗余代码
使用bable-plugin-transform-runtime
插件
6. 提取公共代码
通过配置CommonsChunkPlugin插件,将多个页面的公共代码抽离成单独的文件
7. 其他
组件懒加载、路由懒加载、开启gzip、公共的第三方包上cdn、配置cache缓存Loader对文件的编译副本、配置resolve提高文件的搜索速度(@: src)
七、性能优化篇
1. 浏览器缓存优化
为了让浏览器缓存
发挥最大作用,该策略尽量遵循以下五点就能发挥浏览器缓存
最大作用。
-
「考虑拒绝一切缓存策略」:
Cache-Control:no-store
-
「考虑资源是否每次向服务器请求」:
Cache-Control:no-cache
-
「考虑资源是否被代理服务器缓存」:
Cache-Control:public/private
-
「考虑资源过期时间」:
Expires:t/Cache-Control:max-age=t,s-maxage=t
-
「考虑协商缓存」:
Last-Modified/Etag
缓存策略
通过设置HTTP
报文实现,在形式上分为「强缓存/强制缓存」和「协商缓存/对比缓存」。为了方便对比,笔者将某些细节使用图例展示,相信你有更好的理解。
整个缓存策略
机制很明了,先走强缓存,若命中失败才走协商缓存
。若命中强缓存
,直接使用强缓存
;若未命中强缓存
,发送请求到服务器检查是否命中协商缓存
;若命中协商缓存
,服务器返回304通知浏览器使用本地缓存
,否则返回最新资源
。
有两种较常用的应用场景值得使用缓存策略
一试,当然更多应用场景都可根据项目需求制定。
-
「频繁变动资源」:设置
Cache-Control:no-cache
,使浏览器每次都发送请求到服务器,配合Last-Modified/ETag
验证资源是否有效 -
「不常变化资源」:设置
Cache-Control:max-age=31536000
,对文件名哈希处理,当代码修改后生成新的文件名,当HTML文件引入文件名发生改变才会下载最新文件
2. 渲染层面性能优化
「渲染层面」的性能优化,无疑是如何让代码解析更好执行更快
。因此笔者从以下五方面做出建议。
-
「CSS策略」:基于CSS规则
-
「DOM策略」:基于DOM操作
-
「阻塞策略」:基于脚本加载
-
「回流重绘策略」:基于回流重绘
-
「异步更新策略」:基于异步更新
上述五方面都是编写代码时完成,充满在整个项目流程的开发阶段里。因此在开发阶段需时刻注意以下涉及到的每一点,养成良好的开发习惯,性能优化
也自然而然被使用上了。
渲染层面
的性能优化
更多表现在编码细节上,而并非实体代码。简单来说就是遵循某些编码规则,才能将渲染层面
的性能优化
发挥到最大作用。
「回流重绘策略」在渲染层面
的性能优化
里占比较重,也是最常规的性能优化
之一。上年笔者发布的掘金小册《玩转CSS的艺术之美》使用一整章讲解回流重绘
,本章已开通试读,更多细节请戳这里。
CSS策略
-
避免出现超过三层的
嵌套规则
-
避免为
ID选择器
添加多余选择器 -
避免使用
标签选择器
代替类选择器
-
避免使用
通配选择器
,只对目标节点声明规则 -
避免重复匹配重复定义,关注
可继承属性
DOM策略
-
缓存
DOM计算属性
-
避免过多
DOM操作
-
使用
DOMFragment
缓存批量化DOM操作
阻塞策略
-
脚本与
DOM/其它脚本
的依赖关系很强:对<script>
设置defer
-
脚本与
DOM/其它脚本
的依赖关系不强:对<script>
设置async
回流重绘策略
-
缓存
DOM计算属性
-
使用类合并样式,避免逐条改变样式
-
使用
display
控制DOM显隐
,将DOM离线化
异步更新策略
-
在
异步任务
中修改DOM
时把其包装成微任务
3. 性能优化六大指标
六大指标
基本囊括大部分性能优化
细节,可作为九大策略
的补充。笔者根据每条性能优化建议
的特征将指标
划分为以下六方面。
-
「加载优化」:资源在加载时可做的性能优化
-
「执行优化」:资源在执行时可做的性能优化
-
「渲染优化」:资源在渲染时可做的性能优化
-
「样式优化」:样式在编码时可做的性能优化
-
「脚本优化」:脚本在编码时可做的性能优化
-
「V8引擎优化」:针对
V8引擎
特征可做的性能优化
八、其他杂项篇
1. 常见的浏览器内核有哪些?
2. 网页前端性能优化的方式有哪些?
3. 网页从输入网址到渲染完成经历了哪些过程?
4. 线程与进程的区别?
5. HTTP常见的状态码?
6. 图片懒加载?
7. 移动端性能优化?
8. TCP 传输的三次握手、四次挥手策略
9. HTTP 和 HTTPS,为什么HTTPS安全?
10. axios和fetch区别对比
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,本质上也是对原生XHR的封装,只不过它是Promise的实现版本,符合最新的ES规范,它本身具有以下特征
-
从浏览器中创建 XMLHttpRequest
-
支持 Promise API
-
客户端支持防止CSRF
-
提供了一些并发请求的接口(重要,方便了很多的操作)
-
从 node.js 创建 http 请求
-
拦截请求和响应
-
转换请求和响应数据
-
取消请求
-
自动转换JSON数据
fetch优势:
-
语法简洁,更加语义化
-
基于标准 Promise 实现,支持 async/await
-
同构方便,使用 isomorphic-fetch
-
更加底层,提供的API丰富(request, response)
-
脱离了XHR,是ES规范里新的实现方式
fetch存在问题
-
fetch是一个低层次的API,你可以把它考虑成原生的XHR,所以使用起来并不是那么舒服,需要进行封装。
-
fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
-
fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: ‘include’})
-
fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
-
fetch没有办法原生监测请求的进度,而XHR可以
九、主观题篇
1. 你都做过什么项目呢?具体聊某一个项目中运用的技术.
2. 你遇到过比较难的技术问题是?你是如何解决的?
3. 常使用的库有哪些?常用的前端开发工具?开发过什么应用或组件?
4. 除了前端以外还了解什么其它技术么?你最最厉害的技能是什么?
5. 对前端开发工程师这个职位是怎么样理解的?它的前景会怎么样?
6. 你的优点是什么?缺点是什么?
本文收集了各大公司的前端面试题,并且提供了详细的答案。如果您正在准备前端**面试,这篇文章可能会为您提供帮助。这些面试题覆盖了不同的前端知识领域,包括HTML、CSS、JavaScript、框架等等。无论您是初学者还是经验丰富的前端开发人员,这些面试题都能够帮助您更好地了解前端知识,提高自己的技能。除了提供面试题和答案外,我们还为您提供了一些面试建议和技巧,帮助您在面试中脱颖而出。希望这些资料能够对您有所帮助,祝您面试顺利! 此外,我们还附带了一些常见的面试问题和答案,以帮助您更好地准备面试。这些问题可以帮助您了解面试官对您的了解程度和技能水平的期望。同时,我们还提供了一些实战项目和代码示例,以帮助您更好地理解和掌握前端技能。我们希望这些资料对您有所帮助,并且能够帮助您在面试中取得成功。如果您有任何疑问或建议,请随时联系我们。祝您好运! 如果您想进一步提高自己的前端技能,我们建议您参加一些在线课程和培训项目。这些课程和项目可以帮助您更深入地了解前端知识,并且提供实践经验和反馈。另外,我们也建议您积极参与开源项目和社区,与其他前端开发人员交流和分享经验。这些机会可以帮助您建立自己的声誉。