computed原理:

  1. data 属性初始化 getter setter
  2. computed 计算属性初始化,提供的函数将用作属性 vm.reversedMessagegetter
  3. 当首次获取 reversedMessage 计算属性的值时,Dep 开始依赖收集
  4. 在执行 message getter 方法时,如果 Dep 处于依赖收集状态,则判定 messagereversedMessage 的依赖,并建立依赖关系
  5. message 发生变化时,根据依赖关系,触发 reverseMessage 的重新计算

computed与watch的区别,谁性能更好:

computed: 可以关联多个实时计算的对象,当这些对象中的其中一个改变时都会出发这个属性。具有缓存能力,所以只有当数据再次改变时才会重新渲染,否则就会直接拿取缓存中的数据。

computed特性

1.是计算值,
2.应用:就是简化tempalte里面计算和处理props$emit的传值
3.具有缓存性,页面重新渲染值不变化,计算属性会立即返回之前的计算结果,而不必再次执行函数

watch特性

1.是观察的动作,
2.应用:监听props$emit或本组件的值执行异步操作
3.无缓存性,页面重新渲染时值不变化也会执行

所以相对来说,计算属性性能上面会有优势。

Babel原理:

Babel是一个转译器,感觉相对于编译器compiler,叫转译器transpiler更准确,因为它只是把同种语言的高版本规则翻译成低版本规则,而不像编译器那样,输出的是另一种更低级的语言代码。但是和编译器类似,babel的转译过程也分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程如下:

1
ES6代码输入 ==》 babylon进行解析 ==》 得到AST==》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树==》 用babel-generator通过AST树生成ES5代码

1px解决方案(TODO)

Dom,js,css阻塞关系

  • 没有 async 和 defer 属性的 script 加载或执行都会阻塞 dom 的解析。
  • 带有 async 或 defer 属性的 script 加载或执行都不会阻塞 dom 的解析。

    async 的脚本加载完毕后立即执行,不保证执行顺序,而 defer 脚本在 dom 解析完毕后才执行,基本能保证按着脚本加载顺序执行。

  • css 的加载解析会阻塞后续的 script 执行,但不会阻塞 dom 解析。
  • css 的加载解析会阻塞 dom 的渲染。

浏览器渲染过程;

  • 当浏览器获得一个html文件时,会“自上而下”加载,并在加载过程中进行解析渲染解析:
  • 浏览器会将HTML解析成一个DOM树,DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
  • 将CSS解析成 CSS Rule Tree 。
  • 根据DOM树和CSSOM来构造Rendering Tree。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。
  • 有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作称之为Layout,顾名思义就是计算出每个节点在屏幕中的位置。
  • 再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。
  1. Reflow(回流):浏览器要花时间去渲染,当它发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染。
  2. Repaint(重绘):如果只是改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的repaint,重画某一部分。

defer和async的区别:

  • async 属性会使脚本后续文档的加载渲染和脚本的加载执行并行进行。async 脚本在下载完成后立即执行,所以不能保证脚本的执行顺序,以乱序执行为主。此外,async 不支持内联脚本;
  • defer 会使脚本后续文档的加载渲染和脚本的加载并行进行,但 defer 脚本的执行要在所有元素解析完成之后 DOMContentLoaded 事件触发前完成,它是按着脚本加载顺序进行执行。

loader 和 plugin 不同

  • Loader直译为”加载器”。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。
  • Plugin直译为”插件”。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
  • Loader在module.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)
  • Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。

webpack构建流程

  1. 初始化参数,从配置文件和shell语句中读到的参数合并,得到最后的参数
  2. 开始编译:用合并得到的参数初始化complier对象,加载是所有配置的插件,执行run方法开始编译
  3. 确定入口,通过entry找到入口文件
  4. 编译模块,从入口文件出发,调用所有配置的loader对模块进行解析翻译,在找到该模块依赖的模块进行处理
  5. 完成模块编译,得到每个模块被翻译之后的最终的内容和依赖关系
  6. 输出资源,根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk,在把每个chunk转换成一个单独的文件加载到输出列表
  7. 输出完成,确定输出的路径和文件名,把内容写到文件系统中
    在以上过程中,webpack会在特定的时间点广播出特定的事件,插件在监听感兴趣的事件后会执行特定的逻辑,改变webpack的运行结果

如何利用webpack来优化前端性能

  1. 压缩代码。uglifyJsPlugin 压缩js代码, mini-css-extract-plugin 压缩css代码
  2. 利用CDN加速,将引用的静态资源修改为CDN上对应的路径,可以利用webpack对于output参数和loader的publicpath参数来修改资源路径
  3. 删除死代码(tree shaking),css需要使用Purify-CSS
  4. 提取公共代码。webpack4移除了CommonsChunkPlugin (提取公共代码),用optimization.splitChunks和optimization.runtimeChunk来代替

什么是bundle,什么是chunk,什么是module:

  • bundle是由webpack打包出来的文件,
  • chunk是指webpack在进行模块依赖分析的时候,代码分割出来的代码块,
  • module是开发中的单个模块

有哪些常见的Loader?他们是解决什么问题的?

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
  • image-loader:加载并且压缩图片文件
  • babel-loader:把 ES6 转换成 ES5
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
  • eslint-loader:通过 ESLint 检查 JavaScript 代码

webpack的热更新是如何做到的?说明其原理?

webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。

原理:

HMR原理
首先要知道server端和client端都做了处理工作

  1. 第一步,在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。
  2. 第二步是 webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。
  3. 第三步是 webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念。
  4. 第四步也是 webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。
  5. webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。
  6. HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。
  7. 而第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
  8. 最后一步,当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。

手写Promise,以及Promise.all或Promise.race

参照 -> 从0到1实现Promise

Loadsh库(TODO)

移动端 300ms延迟,解决方法以及原理

300ms延迟出现的原因

移动端会有双击缩放的这个操作,因此浏览器在click之后要等待300ms,看用户有没有下一次点击,也就是这次操作是不是双击

300ms解决方法以及原理

  1. 【禁用缩放】

    既然双击缩放仅对那些可被缩放的页面来说有存在意义,那对于不可缩放的页面,直接去掉点击延迟,何乐而不为呢?这里所说的不可缩放,是指使用了下述标签的页面。

    1
    2
    <meta name="viewport" content="user-scalable=no">
    <meta name="viewport" content="initial-scale=1,maximum-scale=1">

    缺点:你必须完全禁用缩放来达到目的,而从移动端站点的可用性和可访问性来看,缩放是相当关键的一环。你很可能已经遇到过这个问题,即你想要放大一张图片或者一段字体较小的文本,却发现无法完成操作。

  2. 【width=device-width】

    除了双击缩放的约定外,iPhone 诞生时就有的另一个约定是,在渲染桌面端站点的时候,使用 980 像素的视口宽度,而非设备本身的宽度(iPhone 是 320 像素宽)。

    1
    <meta name="viewport" content="width=device-width"> // 告诉浏览器 视口宽度 = 设备宽度

    那么设置这个跟300ms有什么关系呢?
    在 Chrome 32 这一版中,他们将在包含 width=device-width 或者置为比 viewport 值更小的页面上禁用双击缩放。当然,没有双击缩放就没有 300 毫秒点击延迟。

除了浏览器开发商提供的方案外,还有一个更加通用的方案FastClick

  1. fastClick
    FastClick 是 FT Labs 专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。
    【使用】

    1
    2
    3
    window.addEventListener( "load", function() {
    FastClick.attach( document.body );
    }, false );

    attach() 方法虽可在更具体的元素上调用,直接绑定到 上可以确保整个应用都能受益。当 FastClick 检测到当前页面使用了基于 标签或者 touch-action 属性的解决方案时,会静默退出。可以说,这是真正的跨平台方案出来之前一种很好的变通方案。

    【原理】
    移动浏览器的事件触发顺序:touchstart -> touchend -> click

    FastClick 在检测到 touchend 事件的时候,会通过 DOM 自定义事件立即触发一个模拟 click 事件,并把浏览器在 300 毫秒之后真正触发的 click 事件阻止掉。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 业务代码
    var $test = document.getElementById('test')
    $test.addEventListener('click', function () {
    console.log('test click')
    })

    // FastClick简单实现
    var targetElement = null
    document.body.addEventListener('touchstart', function () {
    // 记录点击的元素
    targetElement = event.target
    })
    document.body.addEventListener('touchend', function (event) {
    // 阻止默认事件(屏蔽之后的click事件)
    event.preventDefault()
    var touch = event.changedTouches[0]
    // 合成click事件,并添加可跟踪属性forwardedTouchEvent
    var clickEvent = document.createEvent('MouseEvents')
    clickEvent.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null)
    clickEvent.forwardedTouchEvent = true // 自定义的
    targetElement.dispatchEvent(clickEvent)
    })

点击穿透问题

什么是点击穿透

假如页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数,该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件。

出现的原因

这是因为在移动端浏览器,事件执行的顺序是touchstart > touchend > click。而click事件有300ms的延迟,当touchstart事件把B元素隐藏之后,隔了300ms,浏览器触发了click事件,但是此时B元素不见了,所以该事件被派发到了A元素身上。如果A元素是一个链接,那此时页面就会意外地跳转。

Websocket断点重连实现,什么是心跳

心跳机制

心跳机制是每隔一段时间会向服务器发送一个数据包,告诉服务器自己还活着,同时客户端会确认服务器端是否还活着,如果还活着的话,就会回传一个数据包给客户端来确定服务器端也还活着,否则的话,有可能是网络断开连接了。需要重连~

断点重连实现

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// 初始化创建socket
function createWebSocket() {
try {
ws = new WebSocket(wsUrl);
init();
} catch(e) {
console.log('catch');
reconnect(wsUrl);
}
}

// 初始化方法,初始化相关事件的处理
function init() {
ws.onclose = function () {
console.log('链接关闭');
reconnect(wsUrl);
};
ws.onerror = function() {
console.log('发生异常了');
reconnect(wsUrl);
};
ws.onopen = function () {
//心跳检测重置
heartCheck.start();
};
ws.onmessage = function (event) {
//拿到任何消息都说明当前连接是正常的
console.log('接收到消息');
heartCheck.start();
}
}

var lockReconnect = false;//避免重复连接
// 重新连接方法
function reconnect(url) {
if(lockReconnect) {
return;
};
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
tt && clearTimeout(tt);
tt = setTimeout(function () {
createWebSocket(url);
lockReconnect = false;
}, 4000);
}

//心跳检测
var heartCheck = {
timeout: 3000,
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
console.log('start');
var self = this;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
// 这里发送一个心跳,后端收到后,返回一个心跳消息,
// onmessage拿到返回的心跳就说明连接正常
console.log('55555');
ws.send("123456789");
self.serverTimeoutObj = setTimeout(function() {
console.log(111);
console.log(ws);
ws.close();
// createWebSocket();
}, self.timeout);

}, this.timeout)
}
}

【小结】

实现心跳检测的思路是:每隔一段固定的时间,向服务器端发送一个ping数据,如果在正常的情况下,服务器会返回一个pong给客户端,如果客户端通过onmessage事件能监听到的话,说明请求正常,这里我们使用了一个定时器,每隔3秒的情况下,如果是网络断开的情况下,在指定的时间内服务器端并没有返回心跳响应消息,因此服务器端断开了,因此这个时候我们使用ws.close关闭连接,在一段时间后(在不同的浏览器下,时间是不一样的,firefox响应更快),可以通过 onclose事件监听到。因此在onclose事件内,我们可以调用 reconnect事件进行重连操作。

Nodejs监控性相关监控运维体系(TODO)

CRSF && XSS

XSS攻击–跨站脚本攻击

原理:

其原理是攻击者向有 XSS漏洞的网站中输入(传入)恶意的HTML代码,当其它用户浏览该网站时,这段HTML代码会自动执行,从而达到攻击的目的。如,盗取用户 Cookie、破坏页面的正常结构,插入广告等恶意内容、重定向到其它网站,D-doss攻击等;XSS攻击类似于SQL注入攻击,攻击之前,我们先找到一个存在XSS漏洞的网站。理论上,所有可输入的地方没有对输入数据进行处理的话,都会存在XSS漏洞,漏洞的危害取决于攻击代码的威力。

XSS的攻击方式
  • 反射型
    发出请求时,XSS代码出现在url中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,所以叫反射型XSS。
  • 存储型
    存储型XSS和反射型XSS的差别在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求时目标页面时不用再提交XSS代码。
XSS的防范措施
  • 编码:
    对用户输入的数据进行 HTML Entity 编码。
    Encode的作用是将等一些字符进行转化,使得浏览器在最终输出结果上是一样的。
  • 过滤:
    移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。移除用户输入的Style节点、Script节点、Iframe节点。(尤其是Script节点,它可是支持跨域的呀,一定要移除)。
  • 校正
    避免直接对HTML Entity进行解码。使用DOM Parse转换,校正不配对的DOM标签。
XSS的影响
  • 利用虚假输入表单骗取用户个人信息
  • 利用脚本窃取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送恶意请求
  • 显示伪造的文章和图片

CSRF攻击–跨站请求伪造攻击

原理:

一种网络攻击方式,该攻击可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击站点,从而在未授权的情况下执行在权限保护之下的操作,具有很大的危害性。具体来讲,可以这样理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

CSRF原理

CSRF如何防御
  • 方法一、Token 验证:(用的最多)
    服务器发送给客户端一个token;
    客户端提交的表单中带着这个token。
    如果这个 token 不合法,那么服务器拒绝这个请求。
  • 方法二:隐藏令牌:
    把 token 隐藏在 http 的 head头中。
  • 方法三、Referer 验证:
    Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。

方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。

CSRF的影响
  • 利用已通过认证的用户权限更新设定信息等
  • 利用已通过认证的用户权限购买商品
  • 利用已通过认证的用户权限在留言板上发表言论

移动端适配方案(TODO)