阿里、网易、滴滴共十次前端面试碰到的问题

阿里、网易、滴滴共十次前端面试碰到的问题

前言

虽然我很讨厌whiteboards面试,但我还是决定整理一下。毕竟,作为几年前端的我,看到这些题目确实有点虚。。。一直在创业公司工作,知识体系的特点是大、散、浅,来一个问题,解决一个问题,然后没后续了 。相比大企业的小、专、精,还是需要努力努力的(如果想转大公司的话)。

关于这种 whiteboards 面试,我的态度是:给我Google,我能给你一个更满意的答案。

文章来源: 阿里、网易、滴滴共十次前端面试碰到的问题

文章还是编辑修改中,由于内容太多了,欢迎一起编辑讨论。

HTML5新增了哪些内容或API,使用过哪些

参考Wiki上对HTML5的解释以及W3C school 的内容。

  • 在API层,HTML5 增加了更多样化的应用程序接口
  • canvas: 用来写游戏还是很不错的,推荐开源游戏框架:pixi.js
  • 离线: 想起 Cache Manifest , 和 Cache APIs 。再加上 Service Worker 的特性,用户体验能提升不少。
  • 拖放: Drag & Drop , 对用户体验也有很大的提升。推荐开源库:dragula
  • 历史: 简而言之就是可以使用history对象控制 url 地址了,一般会被单页应用用作路由控制,如果不支持,然后降级为hash具体的接口文档点这里
  • 网络存储sessionStorage & localStorage ,这个应该不陌生,保存一些稍大的数据,或者不适合放在Cookie的,就用网络存储。 类似的还有 IndexedDB 和 WebSQL。 推荐开源库:localForage
  • 更多特性请查看 wiki
  • 元素与属性
  • 新元素:
  •  写文章的时候会经常用到,w3school的说法是:文档中的节(section、区段)。比如章节、页眉、页脚或文档中的其他部分。
  •  和 : 视频和音频。相关开源库:video.js
  •  和 : 之前的写法换成标签 和 就行了,为了语义化,推荐。
  •  标记高亮一个词。
  •  提醒用户可以输入哪些,查看 w3school 的 demo
  • 表示导航等等,还要更多的标签,目的是为了写出更好语义化的HTML。

个人观点:

能用HTML写,就不用CSS和JS,能用CSS写,就不用JS,只能用JS的,才用JS。对于 JS去写CSS,甚至HTML,个人不太赞成。

input 和 textarea 的区别

参考:一篇博客 和 stackoverflow上的回答

不同:

input详细文档

  • 可以指定 type 为 url, email, 可以方便地检测用户输入。还是指定为 file, submit, button, checkbox, radio, datetime, color等,改变input的样式和行为。
  • 可以通过 value 属性指定初始值
  • 宽高只能通过 CSS 指定

textarea详细文档

  • 可以输入多行文字
  • 输入值初始化需要用标签对包裹,并可以夹杂 HTML 代码,而不会被浏览器解析

<h1></span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>h1</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(0, 0, 128);”></h1>

  • 宽高能用 CSS 或 rowscols 指定

相同:

  • 都可以使用 maxlengthminlength等限制输入

用一个div模拟textarea的实现

加个 contenteditable 属性就行(不知道是不是出题人的意思)。

在项目中如果需要用到富文本,在 Github 中搜索 rich editor 就行了,或者搜索 WYSIWYG (what you see is what you get), 百度的 ueditor 也是不错的。

忽略页面中的电话号码

name=“format-detection” content=“telephone=no” />

参考:how-to-disable-phone-number-linking-in-mobile-safari

左右布局:左边定宽、右边自适应,不少于3种方法

  • absolute + padding

class=‘example-1 auto-width’>

 

class=‘left’>left

class=‘right’>right

left

right

  • flex,未来趋势,推荐!

class=‘example-2 auto-width’>

 

class=‘left’>left

class=‘right’>right

left

right

  • table

class=‘example-3 auto-width’>

 

class=‘left’>left

class=‘right’>right

left

right

  • float 解决方案

class=‘example-4 auto-width’>

 

class=‘left’>left

class=‘right’>right

left

right

  • 参考:aloniacalc 属性!

class=‘example-5 auto-width’>

 

class=‘left’>left

class=‘right’>right

left

right

CSS3用过哪些新特性

关于CSS3,可以先查看一下文档

新特性有:

  • border-radius 圆角, @font-face 字体, box-shadow text-shadow 文本和框的阴影
  • word-wrapbackground-sizebackground-originborder-imagebox-sizingcalclinear-gradient 等等
  • transform 转换
  • 2D 转换
  • rotate 旋转,图片转个90或180度什么的
  • translate 位置移动
  • scaleskewmatrix 等
  • 3D 转换
  • rotate(XYZ) 根据x,y,z轴旋转
  • translate(XYZ)scale(XYZ) 同理
  • perspective 透视,这个很多3D效果都要设置一下,不然3D还是只会有”2D”的效果
  • transition: 过渡,简单的动画(如:移个位置,变个长短),直接用这个属性就能搞定。
  • animation: 动画,3D可以调用硬件渲染。
  • 新的长度单位:rem, chvwvhvmaxvmin 等。
  • clip-path: 绘制路径,类似SVG技术。 国外炫酷产品
  • flexflex布局,继 table 和 div 后的趋势,不了解或不熟悉的可以参考cssreference
  • 伪类选择器:如::target:enabled:disabed:first-childlast-child等等
  • @media 媒体查询,适用于一些响应式布局中
  • columns: 分栏布局。
  • will-change: 改善渲染性能, 参考使用CSS3 will-change提高页面滚动、动画等渲染性能

具体查看文档,或 Google 吧

BFC、IFC

BFC: ‘Block Formatting Context’, BFC 表现原则: 内部子元素再怎么翻江倒海,翻云覆雨都不会影响外部的元素,自成一方天地。

IFC: Inline Formatting Contexts, 直译为”内联格式化上下文”,个人理解为行内盒子模型

参考:深入理解流体特性和BFC特性, css行高line-height的一些深入理解及应用

对栅格的理解

以规则的网格阵列来指导和规范网页中的版面布局以及信息分布,更利于代码层的开发和维护工作。

水平居中有哪些实现方式

  •  (不推荐使用)

center

center

  • margin

style=‘width: 3em; margin: 0 auto;’>margin

margin

  • text-align

style=‘text-align: center’>text-align

text-align

  • table + margin

display: table 在表现上类似 block 元素,但是宽度为内容宽。

 

table + margin

table + margin

  • transform

 

transform

transform

  • flex + justify-content

style=‘display: flex; justify-content: center;’>

 

flex

flex

1像素边框问题

这个默认是移动端的问题了。由于移动端一般都会设置屏幕宽度为设备宽度,width=device-width,initial-scale=1, 而有些屏幕是2倍屏,导致在移动端上设置1px就是看上去的2px

解决方法:

  • 通过transform将宽度缩小一半,transform:scaleY(0.5)
  • 通过@media媒体查询,查询当前设置的屏幕倍率,统一设置transform, 参考: 移动端(手机)1像素边框真正实现
  • 模仿淘宝(不确定是不是来自淘宝的),设置屏幕宽度为设计师的设计尺寸(一般为750)。

name=“viewport” content=“width=750, user-scalable=no”>

图片懒加载

实现过一个简单的图片懒加载工具。支持  和 background-image

核心代码时检测当前元素是否在当前视图中:

function elementInViewport(el) {

var rect = el.getBoundingClientRect()

// For invisible element.

if (rect.top + rect.bottom + rect.left + rect.right + rect.height + rect.width === 0) {

return false;

}

return (

rect.top >= 0

// Pre load.

&& rect.top <= ((window.innerHeight || document.documentElement.clientHeight) + 100)

&& rect.left >= 0

// Hide carousel except the first image. Do not add equal sign.

&& rect.left < (window.innerWidth || document.documentElement.clientWidth)

)

}

实现页面加载进度条

描述一下PACE的实现原理即可。

  • AJAX
  • Elements
  • Document
  • Event Lag

事件委托

利用事件冒泡e.target来确定事件和元素。在jQuery中有$.delegate方法去代理事件。使用委托代理的原因:

  • 需要绑定事件的元素很多,且处理逻辑类似。
  • 元素是动态创建,或频繁增加、删除,导致元素绑定事件过于复杂的。

// 参考 https://github.com/zenorocha/delegate/blob/master/src/delegate.js

const delegate = (element, selector, type, callback) => {

element.addEventListener(type, (e) => {

let target = e.path.find(ele => ele.matches(selector))

if (target) {

callback.call(element, e);

}

});

};

实现 extend 函数

浅拷贝使用 Object.assign 就够了,大多数情况下,使用该方法。

深拷贝: (参考: zepto extend)

var class2type = {}

function type(obj) {

return obj == null ? String(obj) :

class2type[toString.call(obj)] || “object”

}

function isFunction(value) { return type(value) == “function” }

function isWindow(obj) { return obj != null && obj == obj.window }

function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE }

function isObject(obj) { return type(obj) == “object” }

function isPlainObject(obj) {

return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype

}

var isArray = Array.isArray ||

function(object){ return object instanceof Array }

function extend(target, source, deep) {

for (key in source)

if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {

if (isPlainObject(source[key]) && !isPlainObject(target[key]))

target[key] = {}

if (isArray(source[key]) && !isArray(target[key]))

target[key] = []

extend(target[key], source[key], deep)

}

else if (source[key] !== undefined) target[key] = source[key]

}

直接 Clone 一个 Nested Object 的简便方法:

var origin = {“a”: “a”}

var copy = JSON.parse(JSON.stringify(origin));

为什么会有跨域的问题以及解决方式

参考前端解决跨域问题的8种方案HTTP访问控制(CORS)浏览器的同源策略

出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求。例如,XMLHttpRequest 和 Fetch 遵循同源策略。

同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。

解决方式:

  • JSONP(JSON with Padding): 利用加载 JS 文件不需要遵循同源策略的原理。
  • CORS(Cross-Origin Resource Sharing): 在服务器端返回允许跨域访问的头。
  • WebSockt:利用 WebSocket 不需要遵循同源策略的原理。

JSONP原理、postMessage原理

参考can-anyone-explain-what-jsonp-is-in-layman-terms

  • JSONP 原理是加载一个 script,并执行一段回调 JS ,因为加载 JS 不需要遵循同源策略。但由此也带来了JSONP的一些问题:
  • 无法发送特定的头部
  • 只能是 GET 请求
  • 无法发送 body
  • postMessage 文档

拖拽功能

比如把5个兄弟节点中的最后一个节点拖拽到节点1和节点2之间

id=‘drag’>

draggable=“true”>1

draggable=“true”>2

draggable=“true”>3

draggable=“true”>4

draggable=“true”>5

</span></p> <p><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”> </span><strong class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>var</strong><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”> ele;</span></p> <p> <span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(0, 134, 179);”>document</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>.querySelector(</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(221, 17, 68);”>’#drag'</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>).addEventListener(</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(221, 17, 68);”>’dragstart'</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>, </span><strong class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>function</strong><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”> (e) {</span></p> <p> ele <strong class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>=</strong><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”> e.target;</span></p> <p> ele.classList.add(<span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(221, 17, 68);”>’draging'</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>);</span></p> <p><span style=”color: rgb(17, 17, 17);”> })</span></p> <p><span style=”color: rgb(17, 17, 17);”> </span><span style=”color: rgb(0, 134, 179); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>document</span><span style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>.querySelector(</span><span style=”color: rgb(221, 17, 68); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>’#drag'</span><span style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>).addEventListener(</span><span style=”color: rgb(221, 17, 68); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>’dragover'</span><span style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>, </span><strong class=”ql-font-monospace” style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);”>function</strong><span class=”ql-font-monospace” style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);”> (e) {</span></p> <p> e.preventDefault();</p> <p> <strong class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>if</strong><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”> (e.target.nodeName </span><strong class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>===</strong><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”> </span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(221, 17, 68);”>’LI'</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>) {</span></p> <p><span style=”color: rgb(17, 17, 17);”> e.target.parentNode.insertBefore(ele, e.target);</span></p> <p><span style=”color: rgb(17, 17, 17);”> }</span></p> <p><span style=”color: rgb(17, 17, 17);”> })</span></p> <p><span style=”color: rgb(17, 17, 17);”> </span><span style=”color: rgb(0, 134, 179); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>document</span><span style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>.querySelector(</span><span style=”color: rgb(221, 17, 68); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>’#drag'</span><span style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>).addEventListener(</span><span style=”color: rgb(221, 17, 68); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>’drop'</span><span style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);” class=”ql-font-monospace”>, </span><strong class=”ql-font-monospace” style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);”>function</strong><span class=”ql-font-monospace” style=”color: rgb(17, 17, 17); background-color: rgb(246, 248, 250);”> (e) {</span></p> <p> ele.classList.remove(<span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(221, 17, 68);”>’draging'</span><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(17, 17, 17);”>);</span></p> <p><span style=”color: rgb(17, 17, 17);”> })</span></p> <p><span class=”ql-font-monospace” style=”background-color: rgb(246, 248, 250); color: rgb(0, 0, 128);”>

  • 1

 

  • 2

 

  • 3

 

  • 4

 

  • 5

动画

setTimeout 何时执行:

关于setTimeoutPromiseEvents Loop等概念的执行,推荐: Tasks, microtasks, queues and schedules

requestAnimationFrame的优点

按照官方文档的说法:

window.requestAnimationFrame() 方法告诉浏览器您希望执行动画,并请求浏览器调用指定的函数在下一次重绘之前更新动画。该方法将在重绘之前调用的回调作为参数。

它的主要优点是:

  • 在浏览器重绘前调用,保证浏览器渲染效率和性能
  • 可以精准地控制动画的每一帧

它的主要场景在游戏,动画方面,用这个方法可以保持较高帧率和效率。比如一个60帧率的游戏,requestAnimationFrame一般会以16ms的间隔调用一次。

PS: 关于Event LoopTasks, MicrotasksJS StackQueue等概念及执行先后顺序,我也是记不清,文章看过一次,记一次,然后又忘了。说实话,主要是实践中用得太少了。

手写 parseInt 的实现

要求简单一些,把字符串型的数字转化为真正的数字即可,但不能使用 JS 原生的字符串转数字的 API ,比如Number()

复杂写法:

const parseInt = str => {

let n = 0;

let i = 1;

str.split().reverse().map(s => {

n += i * (s.charCodeAt(0) 48);

i *= 10;

});

return n;

}

@YAOHAO9 提供的方法

const parseInt = str => str 0;

const parseInt = str => str / 1;

const parseInt = str => str * 1;

此外,通过+运算也能得到类似的效果:

const parseInt = str => +str;

分页器组件

为了减少服务端查询次数,点击“下一页”怎样能确保还有数据可以加载(请求数据不会为空)?

这是在考服务端吗?还是我没有理解对题目?

  • 服务器需要返回总数,当前偏移量,根据总数和偏移量判断是否是最后一页。
  • 参考微信的接口,给一个下一页的起始项的id,如果当前页最后一个id和下一页起始id相同,就是最后一页。

ES6新增了哪些特性

参考:es6features

也可参考:ECMAScript 6 入门

require.js的实现原理

require.js 实现原理

与webpack相比,两者打包的异同及优缺点

同:

  • 都以模块化方式组织代码

异:

  • requirejs 只能加载JS文件
  • webpack 可以打包JS,CSS,甚至是图片

与webpack相比,两者打包的异同及优缺点

Promise 的实现原理

Promise 简易实现, 参考: Promise简单实现

/*

* 注意! 本样例为了最简化,未添加reject, all, rece等功能

* 完整的Promise实现可参考:https://github.com/taylorhakes/promise-polyfill/blob/master/promise.js

*/

class MyPromise {

constructor (fn) {

this.deferreds = [];

this.state = ‘pending’;

// 创建完Promise后,需要异步执行回调

setTimeout(() => {

fn(this.resolve.bind(this));

}, 0);

return this;

}

resolve(value) {

this.state = “fulfilled”;

// 所有回调执行完成

if (this.deferreds.length === 0) return;

let result = this.deferreds.shift()(value);

// 回调后返回新的Promise,需要将后续的回调复制给新Promise

if (result instanceof MyPromise) {

result.deferreds = this.deferreds;

return result;

}

// 返回如果不是Promise,则异步执行下一个回调

setTimeout(() => {

this.resolve()

}, 0)

}

// then就是不断push回调函数

then(fn) {

this.deferreds.push(fn);

return this;

}

}

// 以下是测试样例

new MyPromise((resolve) => {

setTimeout(() => {

resolve(‘first promise’)

}, 1000)

}).then(v => {

console.log(v)

return new MyPromise(resolve => {

setTimeout(() => {

resolve(‘second promise’)

}, 1000)

})

}).then((v) => {

console.log(v);

}).then(v => {

console.log(v)

})

/*

输出:

first promise

second promise

undefined

*/

进一步会问 async、await 是否使用过

async & await 异步代码书写十分优雅,例如

async function () {

let file = await readFile(‘xxx’);

console.log(file.toString());

}

看起来完全是同步的书写方式,解决了异步代码中

  • 开发者需要不断跳跃的阅读异步代码
  • 回调地狱

参考: Try-node7-async-await

实现 gulp 的功能

这题我就当是在考Stream这个知识点了。

const fs = require(‘fs’);

const { Transform } = require(‘stream’);

class Uglify extends Transform {

constructor(options) {

super(options);

}

_transform(chunk, encoding, callback) {

this.push(`${chunk}\n // This file is uglified. this.push.`);

// callback(null, `${chunk}\n // This file is uglified. callback.`);

}

};

const glup = {

stream: null,

src(p) {

glup.stream = fs.createReadStream(p);

return glup;

},

pipe(fn) {

return glup.stream.pipe(fn);

},

dest(p) {

return fs.createWriteStream(p);

},

}

glup.src(‘test.js’)

.pipe(new Uglify())

.pipe(glup.dest(‘test.min.js’));

使用框架 ( vue / react 等)带来好处( 相对jQuery )

  • MVVC架构,数据驱动视图,数据绑定,减少DOM操作。
  • 组件化组织页面,效率更高,维护更简便。
  • 局部 CSS 样式,避免给全局带来混乱
  • 局部 JS 逻辑,更好的封装性
  • HTML 模板,使得 DOM 变更更为方便快捷
  • Virtual Dom 带来性能上的提升
  • 路由控制,单页应用更为简便

vue双向数据绑定的实现

实现双向数据绑定的关键是Observer, 即用户改变了数据,框架如何知晓,并及时更新视图。而Observer的实现包括:

  • Object.defineProperty:

var obj = {}

Object.defineProperty(obj, ‘key’, {

enumerable: true,

set(x) {

console.log(`set key: ${x}`);

obj.__ob__ = obj.__ob__ || {};

obj.__ob__.key = x;

},

get() {

return obj.__ob__.key;

}

})

obj.key = ‘value’;

/**

* 输出:

* set key: value

*/

  • Proxy:

var ele = {

data: null

};

var handler = {

get: function(target, key) {

if (typeof target[key] === ‘object’ && target[key] !== null) {

return new Proxy(target[key], handler)

} else {

return target[key];

}

},

set: function(target, key, value) {

console.log(‘set ‘ + key)

target[key] = value;

return value;

}

}

ele = new Proxy(ele, handler);

ele.data = {a: ‘a’, b: {bb: ‘bb’}}

ele.data.a = ‘aa’;

ele.data.b.bb = ‘bb1’;

ele.data.c = ‘cc’;

/*

* 输出:

* set data

* set a

* set bb

* set c

*/

// 参考: [observer](http://zhoukekestar.github.io/notes/2017/02/22/observer.html)

其他的数据操作,如:数组的push等,只需要在原生上加Hook就行了

var arr = [];

var __push = Array.prototype.push;

Array.prototype.push = function (…items) {

console.log(`push: ${items}`);

return __push.apply(this, items);

}

arr.push(‘value’)

/**

* 输出:

* push: value

*/

从视图反向把数据流过来,稍微简单些,只需要记录对应的key值,然后在输入框触发changekeypress事件的时候,更新对应key的数据即可。

推荐: vue2.17源码学习

ps: 我是没在实际项目中使用Vue的人,只是略知一二,希望熟悉的同学修改补充。

单页应用,如何实现其路由功能

  • Hash

window.addEventListener(‘hashchange’, () => {

// 隐藏其他页面

Array.from(document.querySelectorAll(‘.page’)).map(page => {

page.style.display = ‘none’;

});

// 根据hash值显示对应的页面

document.querySelector(location.hash).style.display = ‘block’;

});

// 参考:https://github.com/zhoukekestar/modules/blob/master/src/views/views.js

使用HASH实现的简单路由,在线测试

  • History

// push 页面

history.pushState(, , url);

// pop 页面

window.onpopstate = (e) => {

};

// 参考:https://github.com/zhoukekestar/modules/blob/master/src/loadpage/loadpage.js

使用History实现的简单路由, 在线测试

项目中使用过哪些优化方法

  • 页面静态化,(如:Jada, Pug在静态编译后部署)
  • CDN加速, 多地缓存
  • 前端渲染 (Data + View) / 后端渲染( SSR, SEO 等), 视具体情况选择,如:
  • 前端渲染,适合大流量的场景
  • 后端渲染,适合SEO优化,用户体验提升等场景
  • 缩减域名,以减少DNS解析时间,(可采用进行优化)
  • 如果遇到域名解析的问题,可尝试HTTPDNS方案
  • Combo服务器合并CSSJS请求, 减少第一屏网络请求。(如果采用HTTP2.0方案,资源合并可省略)
  • 异步加载非核心业务逻辑资源
  • 资源和请求缓存,可参考缓存的答案
  • Cache-Control/Expires 前端缓存
  • Last-Modified/Etag 服务器端缓存,304
  • 如果是和Native混合开发的,还可以使用Native缓存
  • DNS就近解析应用服务器,需要和CDN配合使用

输入一个URL,Enter之后发生了什么

此答案忽略了与Web关联性不大的回答,包括(物理按键原理,系统中断等)

  • 浏览器解析URL, 如: https://www.google.com.hk/#newwindow=1&q=hello包括
  • 协议:httphttps
  • 域名:www.google.com.hk
  • 资源路径: /
  • 参数查询:q=hello, 关键词hello
  • DNS
  • 浏览器 DNS 缓存
  • HOSTS 查询
  • DNS 服务器查询
  • ARP 查询
  • TCP 握手, TLS 握手
  • HTTP(s), (或SPDY, 或HTTP2.0)
  • Header
  • Domain
  • Body
  • Gateway / Nginx,网关和负载均衡服务器
  • 查询本地缓存
  • 请求上游应用服务器
  • 浏览器解析HTML,并请求资源
  • CSS
  • JS
  • 图片
  • 生成 DOM-Tree,结合CSS进行渲染

更为完整详细的答案请参考: what-happens-when-zh_CN

页面的渲染过程

  • 解析整个HTML,得到DOM树和样式树
  • DOM树和样式树,经过渲染,得到一颗渲染树
  • 根据渲染树,开始布局,计算各个节点宽度,位置,高度等
  • 然后开始绘制整个页面并显示
  • 在渲染过程中如果使用了GPU,还可以进行GPU渲染

参考: what-happens-when-zh_CN在前端开发中,页面渲染指什么

静态资源或者接口等如何做缓存优化

  • redis/memcache 做数据缓存
  • SQL 查询做缓存
  • 指定 Cache-Control/Expires 缓存时间
  • Last-Modified/Etag 缓存 ( 304 ) 方案
  • 网关服务器做缓存,需要更新时,再回源到应用服务器
  • CDN多机房,多网关缓存

页面DOM节点太多,会出现什么问题?如何优化?

问题

  • 页面卡顿,帧率下降

优化:

  • 采用Virtual Dom技术,可参考: virtual-dom
  • 多次操作DOM,改为批量一次操作DOM
  • 及时移走页面不用的DOM
  • 避免不必要的DIV嵌套

前端安全问题:CSRF和XSS

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。[1] 跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。 — 参考: 跨站请求伪造

  • 防御措施
  • 检查refererX-Requested-WithOrign
  • 使用POST代替GET
  • 添加校验Token至表单中
  • 添加验证码或其他人机验证手段,如 Google 的 recaptcha
  • Token放到自定义的HTTP HeaderCookie-to-Header Token
  • XSS: Cross-site_scripting
  • 简单描述:

跨站脚本(英语:Cross-site scripting,通常简称为:XSS)是一种网站应用程序的安全漏洞攻击,是代码注入的一种。它允许恶意用户将代码注入到网页上,其他用户在观看网页时就会受到影响。这类攻击通常包含了HTML以及用户端脚本语言。—参考:跨站脚本

  • 防御措施
  • 过滤特殊字符串 ( encoding / escaping )
  • 保护Cookie, 使用HttpOnly字段防止被JS获取,(因为攻击通常会采集敏感信息)
  • 使用HTTPs代替HTTP,(运营商经常会通过注入广告)
  • 禁用JS,(这个不太现实)
  • 推荐!设置CSPContent_Security_Policy 介绍Content-Security-Policy 文档。这个在Github有使用:

Content-Security-Policy:default-src ‘none’; base-uri ‘self’; block-all-mixed-content; child-src render.githubusercontent.com; connect-src ‘self’ uploads.github.com status.github.com collector.githubapp.com api.github.com www.google-analytics.com github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com wss://live.github.com; font-src assets-cdn.github.com; form-action ‘self’ github.com gist.github.com; frame-ancestors ‘none’; img-src ‘self’ data: assets-cdn.github.com identicons.github.com collector.githubapp.com github-cloud.s3.amazonaws.com *.githubusercontent.com; media-src ‘none’; script-src assets-cdn.github.com; style-src ‘unsafe-inline’ assets-cdn.github.com

  • 设置 X-XSS-Protection 头
  • HTTP 安全头
  • Strict-Transport-SecurityStrict-Transport-Security: max-age=31536000 ; includeSubDomains
  • Public-Key-PinsPublic-Key-Pins: pin-sha256=”d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=”; pin-sha256=”E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=”; report-uri=”http://example.com/pkp-report“; max-age=10000; includeSubDomains
  • X-Frame-OptionsX-Frame-Options: deny
  • X-XSS-ProtectionX-XSS-Protection: 1; mode=block
  • X-Content-Type-OptionsX-Content-Type-Options: nosniff
  • Content-Security-PolicyContent-Security-Policy: script-src ‘self’
  • X-Permitted-Cross-Domain-PoliciesX-Permitted-Cross-Domain-Policies: none
  • Referrer-PolicyReferrer-Policy: no-referrer

参考: 安全 checklist security-guide-for-developers浅谈CSRF攻击方式

「点点赞赏,手留余香」

    还没有人赞赏,快来当第一个赞赏的人吧!
生活
生活
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论