我什么都不会

我什么都不会 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 个人简介什么都没有

个人动态

我什么都不会 赞了文章 · 2019-04-12

📚JavaScript设计模式实践:18份笔记、例子和源码📚

专注前端与算法的系列干货分享,欢迎关注(¬‿¬):
「微信公众号:心谭博客」| xin-tan.com | GitHub

背景介绍

之前在阅读《JavaScript设计模式和开发实践》这本书的时候,收货颇丰,学习了设计模式在很多场景下的应用。

但也是因为书上场景过多,所以当记不清某一种设计模式的时候,翻书温习复杂案例的成本是相对较高的。有时候,只需要一段经典、简洁的demo就可以迅速回顾起精髓,在快速业务开发中,这是个比较经济的做法。

除此之外,当主要工作语言发生变化的时候(例如:js -> python),简洁的demo更能帮助开发者快速回忆某种设计模式的精髓和实现思路,方便开发者根据语言特性再做实现。

因此,对于比较重要的18种设计模式,我都挑选了它的一种经典应用,并且尽量使用ES6的语法和编程习惯来进行实现。前10个设计模式还提供了Python3的实现版本(后来比较忙,遂放弃)

文章地址

一共记录了18个设计模式,部分文章发到了掘金,由于精力有限,后面几篇文章就直接放在了Github仓库 / 个人博客

  1. 单例模式:https://godbmw.com/passages/2018-10-23-singleton-pattern/
  2. 策略模式: https://godbmw.com/passages/2018-10-25-stragegy-pattern/
  3. 代理模式: https://godbmw.com/passages/2018-11-01-proxy-pattern/
  4. 迭代器模式: https://godbmw.com/passages/2018-11-06-iter-pattern/
  5. 订阅-发布模式: https://godbmw.com/passages/2018-11-18-publish-subscribe-pattern/
  6. 命令模式: https://godbmw.com/passages/2018-11-25-command-pattern/
  7. 组合模式: https://godbmw.com/passages/2018-12-12-composite-pattern/
  8. 享元模式:https://godbmw.com/passages/2018-12-16-flyweight-pattern/
  9. 责任链模式: https://godbmw.com/passages/2019-01-07-chain-of-responsibility-pattern/
  10. 装饰者模式: https://godbmw.com/passages/2019-01-12-decorator-pattern/
  11. 状态模式: https://godbmw.com/passages/2019-01-16-state-pattern/
  12. 适配器模式: https://godbmw.com/passages/2019-01-17-adapter-pattern/
  13. 桥接模式: https://godbmw.com/passages/2019-01-19-bridge-pattern/
  14. 解释器模式: https://godbmw.com/passages/2019-01-25-interpreter-pattern/
  15. 备忘录模式: https://godbmw.com/passages/2019-01-26-memento-pattern/
  16. 模板模式: https://godbmw.com/passages/2019-01-31-template-pattern/
  17. 工厂模式: https://godbmw.com/passages/2019-03-31-factory-pattern/
  18. 抽象工厂模式: https://godbmw.com/passages/2019-04-01-abstract-factory-pattern/

放在最后

其实整理这些的原因还有一个,就是为了准备今年春招的面试。然后过了腾讯的校招和阿里的前三面发现,竟然没有专门问到设计模式相关知识!

但回首看,系统地学习、理智地使用设计模式(不是为了用而用),确实能提升代码的可读性,实现业务解耦。而在写这些文章的过程中,每种设计模式自己也是会找很多的实现(包括不限于python、java、c++)来参考,探索式学习还是蛮有趣的。

尽管如此,有2篇文章的瑕疵还是很多,希望您抱着交流的心态来阅读,如有不当,欢迎指出、共同提升


专注前端与算法的系列干货分享,欢迎关注(¬‿¬)
查看原文

赞 65 收藏 57 评论 3

我什么都不会 收藏了文章 · 2019-04-12

📚JavaScript设计模式实践:18份笔记、例子和源码📚

专注前端与算法的系列干货分享,欢迎关注(¬‿¬):
「微信公众号:心谭博客」| xin-tan.com | GitHub

背景介绍

之前在阅读《JavaScript设计模式和开发实践》这本书的时候,收货颇丰,学习了设计模式在很多场景下的应用。

但也是因为书上场景过多,所以当记不清某一种设计模式的时候,翻书温习复杂案例的成本是相对较高的。有时候,只需要一段经典、简洁的demo就可以迅速回顾起精髓,在快速业务开发中,这是个比较经济的做法。

除此之外,当主要工作语言发生变化的时候(例如:js -> python),简洁的demo更能帮助开发者快速回忆某种设计模式的精髓和实现思路,方便开发者根据语言特性再做实现。

因此,对于比较重要的18种设计模式,我都挑选了它的一种经典应用,并且尽量使用ES6的语法和编程习惯来进行实现。前10个设计模式还提供了Python3的实现版本(后来比较忙,遂放弃)

文章地址

一共记录了18个设计模式,部分文章发到了掘金,由于精力有限,后面几篇文章就直接放在了Github仓库 / 个人博客

  1. 单例模式:https://godbmw.com/passages/2018-10-23-singleton-pattern/
  2. 策略模式: https://godbmw.com/passages/2018-10-25-stragegy-pattern/
  3. 代理模式: https://godbmw.com/passages/2018-11-01-proxy-pattern/
  4. 迭代器模式: https://godbmw.com/passages/2018-11-06-iter-pattern/
  5. 订阅-发布模式: https://godbmw.com/passages/2018-11-18-publish-subscribe-pattern/
  6. 命令模式: https://godbmw.com/passages/2018-11-25-command-pattern/
  7. 组合模式: https://godbmw.com/passages/2018-12-12-composite-pattern/
  8. 享元模式:https://godbmw.com/passages/2018-12-16-flyweight-pattern/
  9. 责任链模式: https://godbmw.com/passages/2019-01-07-chain-of-responsibility-pattern/
  10. 装饰者模式: https://godbmw.com/passages/2019-01-12-decorator-pattern/
  11. 状态模式: https://godbmw.com/passages/2019-01-16-state-pattern/
  12. 适配器模式: https://godbmw.com/passages/2019-01-17-adapter-pattern/
  13. 桥接模式: https://godbmw.com/passages/2019-01-19-bridge-pattern/
  14. 解释器模式: https://godbmw.com/passages/2019-01-25-interpreter-pattern/
  15. 备忘录模式: https://godbmw.com/passages/2019-01-26-memento-pattern/
  16. 模板模式: https://godbmw.com/passages/2019-01-31-template-pattern/
  17. 工厂模式: https://godbmw.com/passages/2019-03-31-factory-pattern/
  18. 抽象工厂模式: https://godbmw.com/passages/2019-04-01-abstract-factory-pattern/

放在最后

其实整理这些的原因还有一个,就是为了准备今年春招的面试。然后过了腾讯的校招和阿里的前三面发现,竟然没有专门问到设计模式相关知识!

但回首看,系统地学习、理智地使用设计模式(不是为了用而用),确实能提升代码的可读性,实现业务解耦。而在写这些文章的过程中,每种设计模式自己也是会找很多的实现(包括不限于python、java、c++)来参考,探索式学习还是蛮有趣的。

尽管如此,有2篇文章的瑕疵还是很多,希望您抱着交流的心态来阅读,如有不当,欢迎指出、共同提升


专注前端与算法的系列干货分享,欢迎关注(¬‿¬)
查看原文

我什么都不会 评论了文章 · 2019-03-22

Babylon-AST初探-代码生成(Create)

  业余时间写了Babylon-AST的系列文章,这里是第一篇,后面还有三篇。趁着今天有空都一起发上来啦。

  最近想研究react小程序代码的,后来感觉跨度有些大,因为平时也会写一些vue的代码,而且vue小程序更接近一些,所以还是先做了一个vue小程序的PoC。可是这些都不是重点啊,重点是在这一过程中学习并使用了babylonAST。因为也是第一次接触,所以想写点笔记记录一下,也希望能给大家一点参考。

  代码是写出来的,一定一定多写多练,所以我这里还是以实例代码为主,涉及到的点也是在vue小程序中用到的,或者是转换的基础。可是也会有很多超出此范围的知识点,我们这里就先不做具体讨论啦,这里给出了一些参考资料,大家可以参考下。

涉及到的参考资料:

  1. AST explorer: https://astexplorer.net/ 神器,阅读和书写AST操作全靠它
  2. Babel Plugin Handbook https://github.com/jamiebuild... 从这里找到了一些API
  3. Babylon和babel-traverse详解 https://github.com/xtx1130/bl... 其实也称不上详解,不过看到的比较早,算是篇启蒙文章吧
  4. Babel types https://babeljs.io/docs/core-... 生成代码时会大量用到

AST explorer查看代码

  首先打开 https://astexplorer.net/ ,直接在代码里输入1,查看基本效果。
图片描述

  我们要做的只是代码的转换,这里只需关心program.body部分即可(是否可以操作tokens来更改代码,还没有研究)。

  如上图,程序代码里只有一个表达式ExpressionStatement。点击+展开,可以看到内部的细节,如下图:

图片描述

  内部结构只有一个Literal,非常简单。更复杂的代码,我们在后面再来解释。

AST 的CRUD(Create-Retrieve-Update-Delete)

1. Create

  实际上,Create是个相对复杂的操作,通常会结合RetrieveUpdate使用。可以结合实际需要,选择阅读顺序。

  首先,构造一个空的node工程,后续会基于该项目一步步拓展。

npm install babylon @babel/types @babel/generator @babel/traverse

测试代码

const babylon = require('babylon')
const t = require('@babel/types')
const generate = require('@babel/generator').default
const traverse = require('@babel/traverse').default

const code = ''
const ast = babylon.parse(code)
// manipulate ast
const output = generate(ast, {}, code)  
console.log('Input \n', code)
console.log('Output \n', output.code)

因为这里是空的code,所以InputOutput都没有输出,只是搭一个代码结构。

构造一个1的代码

  根据上面的两个图,只有1代码由一个ExpressionStatement内嵌一个Literal组成。直接代码如下,可以参考下注释。

const code = ''
const ast = babylon.parse(code)

// 生成literal
const literal = t.numericLiteral(1)
// 生成expressionStatement
const exp = t.expressionStatement(literal)  
// 将表达式放入body中
ast.program.body.push(exp)

const output = generate(ast, {}, code)
console.log('Input \n', code)
console.log('Output \n', output.code)

运行输出

Input

Output
 1;

  这个例子中首先用到了t(babel-types)。可以在 https://babeljs.io/docs/core-... 查看文档。t里面有类型判断和生成实例等方法。
  如t.expressionStatement(literal)就是生成一个ExpressionStatement,这个函数要用到的参数在文档中(https://babeljs.io/docs/core-... 有描述,可是文档真心不好看,大家还是根据AST explorer中查找对应的方法,然后在文档看个参数就好了:

图片描述

构造const a = 1的代码

  上述1的代码过于简单,我们来生成const a = 1的代码。将const a = 1输入到AST explorer中,查看语法树信息如下图:

图片描述

  在这段代码中涉及了VariableDeclaration, VariableDeclarator, Identifier, Literal几个babel-typesLiteral中使用的是数字类型NumericLiteral。这时就可以分别查看文档了,比如:VariableDeclaration

图片描述

  这个t.variableDeclaration有两个参数kinddeclarations,第二个参数是个数组。

  根据语法树,一层层的生成代码如下:

const code = ''
const ast = babylon.parse(code)

// 生成 VariableDeclarator
const id = t.identifier('a')
const literal = t.numericLiteral(1)
const declarator = t.variableDeclarator(id, literal)

// 生成 VariableDeclaration
const declaration = t.variableDeclaration('const', [declarator])

// 将表达式放入body中
ast.program.body.push(declaration)

const output = generate(ast, {}, code)
console.log('Input \n', code)
console.log('Output \n', output.code)

执行结果如下:

Input

Output
 const a = 1;

Create总结

  根据AST explorer可以完美生成代码,常见的异常是参数没有填对,特别是数组什么的,一定要注意。多结合API文档和从小的代码片段做起能够规避这类错误。

  最后,生成一个稍复杂一点的代码。

function add(a, b) {
  return a + b
}

AST树如下

图片描述

  感兴趣的同学可以先尝试根据语法树提示写一写,再看下面的对照代码,如果上面看懂了其实写这个真心不是很难了。

const code = ''
const ast = babylon.parse(code)

// BinaryExpression a + b
const binaryExp = t.binaryExpression('+', t.identifier('a'), t.identifier('b'))
const returnStatement = t.returnStatement(binaryExp)

// function body
const fnBody = t.blockStatement([returnStatement])
const params = [t.identifier('a'), t.identifier('b')]

const fnDeclaraton = t.functionDeclaration(t.identifier('add'), params, fnBody)
ast.program.body.push(fnDeclaraton)

const output = generate(ast, {}, code)
console.log('Input \n', code)
console.log('Output \n', output.code)

  以上,就是ASTCreate的介绍,想进一步学习的接着看后面几篇文章哦

查看原文

我什么都不会 评论了文章 · 2019-03-19

deno 如何偿还 node.js 的十大技术债

根据网络资料整理

“Node现在太难用了!”。Node.js之父 Ryan Dahl 去年初要开发一款 JavaScript 互动式数据分析工具时,忍不住抱怨起自己十年前一手创造的技术。

Ryan Dahl 想要设计出一款类似 IPython 的互动式数据科学分析命令行工具,但改用 JavaScript 语言,要让 JavaScript 也可以像 Python 那样,进行各式各样的数据分析、统计计算以及数据视觉化战士。一度离开 Node.js 开发社区的 Rayn Dahl,再次拿起自己发明的 Node.js 来开发这个新的数据分析工具,但是越用越别扭,他开始思考,有什么方法可以改进 Node.js。

Node.js 是他在 2009 年 11 月 8 日时,在 JavaScript 社区欧洲 JSConf 大会上首度发布的,它把浏览器端的 JavaScript 技术,带入了服务器端应用领域。前端网页工程师从来都没想过,自己也可以成为后端工程师,但 Node.js 让前端技术走出了浏览器,前端工程师甚至可以成为全端工程师,Node.js 改变了前端工程师的世界。

从2009年 Ryan Dahl 设计出这个服务器端的 JavaScript 框架,至今已经发展到了第 10 版。而随 Node.js 而生,另一位开发者 Isaac 设计出的 JavaScript 包管理工具 npm,更成了网页开发者必懂得技术,在 npm 的储存库上,注册了超过 60 万个 Node.js 模块,这更让 Node.js 的应用遍及各类开发或软件需求。

JavaScript是最普及的语言,而Node.js是最受欢迎的框架

根据 Stack OverFlow 在 2018年度的开发者大调查(全球超过 10 万开发者参与),JavaScript 是开发者中最普及的技术,近 7 成开发者都会用,比 HTML 或 CSS 的普及率还要要高,而最多人懂的开发框架排名中,第一名就是 Node.js ,将近5成开发者(49.6%)经常使用,比 2017 年还小幅上升了 2 个百分点,同时使用者还在持续增加,远高于排名第二的 Angular(36.9%),这正是因为 Node.js 是前端和后端工程师都能用的技术。

Node.js 不只是当前的主流技术,也是下一代网页应用架构 Serverless(无服务器)架构的关键技术。负责 Azure Functions 项目的微软资深首席软件工程师 Christopher Anderson 就曾直言,主流无服务器服务商纷纷把宝押在 Node.js,因为看上了 JavaScript 工具的丰富生态,再加上 Node.js 的轻量化、容易分散与水平扩充、各种操作系统都容易运行的特性,将 Node.js 作为无服务器服务优先支持的框架,这也让 Node.js 更适合用于超大规模部署的应用。

Ryan Dahl 自己坦言,从没想到 Node.js 日后会带来这么大的影响。他也将此归功于开发社区的持续改善,才让它越来越成熟。截止到2018年8月,参与 Node.js 的开发者已经超过2千人,十年来的更新发布次数超过 500 次,在 GitHub 上代码的下载次数更是累计超过了10亿次,就连大型科技公司如 PayPal,或顶尖科研机构 NASA 都在使用。

但 Ryan Dahl 在 2012 年开始淡出 Node.js 社区,转而进入 Go、Rust 语言社区,也重回他擅长的数学应用领域,2017 年还申请了 Google 大脑一年的进驻计划,成为 Google 大脑研究团队的一员,担任深度学习工程师,并投入图像处理技术的研究。直到 2018 年 6 月初,就在 Node.js 准备迈入第 10 年之前,JSConf 欧洲大会再次邀请Ryan Dahl 来进行开场演讲。

尽管大受欢迎,但 Node.js 仍有十大技术债

原本 Ryan Dahl 打算在 2018 年的 JSConf 演讲中分享自己这款 JavaScript 版的 IPython 互动式数据分析工具,没想到一直开发到 5 月份,这个工具都还不能用。本来要放弃这次演讲的 Ryan Dahl 念头一转,干脆把他重拾 Node.js 后发现的问题拿出来分享,这就是去年引发全球开发社区热烈讨论的那场演讲,题目是 “我在 Node .js 最后悔的 10 件事”。Ryan Dahl 在演讲中坦言,Node.js 有十大设计错误,甚至可说是他的 10 大悔恨(他刻意用Regret 这个词来形容)!

这些让 Ryan Dahl 懊悔不已的错误,包括了没采用 JavaScript 非同步处理的 Promise 对象、低估了安全性的重要性、采用 gyp 来设计 Build 系统、没有听从社区建议改用 FFI 而继续用 gyp,另外,他也觉得 Node.js 过度依赖 npm 功能(内建到 package.json 支持是个错误)、用 require("")来嵌入任意模块简直太容易了、package.json 容易造成错误的模块观念(让开发者误以为同一目录下的文件就是同一模块)、臃肿复杂的node_module设计以及开发社区抱怨已久的下载黑洞问题(下载npm得花上非常久的等待时间)、require("module")功能没有强制要求注名.js扩展名,以及无用的 index.js 设计。

2012年,Ryan Dahl 离开了Node.js社区,他事后解释,Node.js 的发展已经步入正轨,也达到他最初的目标,因而决定离开,但在2018年这场演讲中,他坦言, Node.js 还有大把问题要修复,所以现在他回来了,要来偿还当年的技术债,挽回 Node.js 的设计错误。

Ryan Dahl 的答案是打造一个全新的服务器端 JavaScript 运行环境,也就是 Deno 项目。

Deno从2018年5月在 Github 开源至今年3月,已有超过100名开发者参与,经常贡献代码的核心开发者也有5名。

让Ryan Dahl懊悔不已的 Node.js 十大技术债

  1. 没用 JavaScript 异步处理的 Promise 对象
  2. 低估了安全的重要性
  3. 使用了 gyp 来设计 Build 系统
  4. 没有听大家的建议提供 FFI 而继续用 gyp
  5. 过度依赖 npm(内建 package.json支持)
  6. 太容易造成 require("任意模块")
  7. package.json 建立了错误的模块概念(在同一目录下的文件就是同一模块)
  8. 臃肿复杂的 node_module 设计和下载黑洞(往往下载 npm 得花上非常久的时间)
  9. require("module") 时没有强制加上 .js 扩展名
  10. 无用的 index.js 设计。

Deno 如何挽回 Node.js 设计上遗留的问题

clipboard.png

这是 Deno 项目的一个范例,是 Unix 系统基本命令 cat 的一个实现。cat 可以从标准输入取得文件, 再逐行把文件内容送出到标准输出上。这个范例反映出 Deno 要将 I/O 抽象化和精简化的意图。

Ryan Dahl 希望通过打造 Deno 这个全新的服务器端 JavaScript 运行环境,来解决 Node.js 的三大问题,包括准确的 I/O 接口、预设安全性(Secure by Default)以及引进一套去中心化的模块系统等,最后一项就是要解决下载过久过慢的老问题。

Ryan Dahl 进一步解释,虽然他所有的时间都是用 C++、Go 或 Rust 这类编译式语言来开发,但是他还是有一些经常要做的事,需要使用动态的脚本程序。例如整理资料、协调测试任务、部署服务器或用户端环境、绘制统计图表、设定构建系统(Build System)的参数或是设计应用雏形等。

可是,这些不同用途的任务,需要切换使用多种不同的脚本语言或工具,如 Bash、Python 或是 Node.js 等才行,相当麻烦。而 2018 年上半年的这个互动式数据分析工具的开发挫折,更让他有一股强烈地念头,能不能有一个通用的脚本工具。

Deno

Deno 架构

打造一款简单、好用的通用脚本工具

Ryan Dahl 在 2018 年 11 月参加台湾年度 JavaScript 开发者大会(JSDC)时,特别提到:“我不喜欢用不同工具来处理不同的事情,我只想要有一个简单,直接可执行,拿了就能用的顺手工具,这正是打造Deno的初衷。”

简单来说,Deno 跟 Node.js 一样都采用了 Chrome 的 JavaScript 引擎 V8,但 Deno 采用了更严格的 JavaScript 语法规范 TypeScript,Deno 等于是一个 TypeScript runtime。第一个版本的 Deno runtime 是用 Go 语言实现的,但是 Ryan Dahl 又重新用 Rust 语言开发了一次 Deno 的 runtime,避免因为重复使用两套垃圾回收器(Go语言一套、V8引擎也内建了一套)而影响效能。另外,Deno runtime 中也内建了 TypeScript 编译器。

Deno的目标是安全、简洁、单一可执行文件

Deno的设计目标是安全、模块简洁、单一可执行文件(简化封装)等,目前已完成的特色之一,就是可以透过URL 来汇入各种模块,另外预设安全性,要存取实体资源或网络时都需要授权,用户的代码只能在安全的沙箱中执行。为什么他最熟悉的 Go 做不到?因为“动态语言仍有其必要。“他强调,尤其要建立一个适当好用的 I/O 处理流程(pipeline)时,动态脚本语言是不可或缺的工具。

而 JavaScript 就是那个他心中的理想动态语言,但是,Node.js 是一项将近 10 年历史的技术,受限于最初的设计架构,他认为,可以重新用 JavaScript 近几年出现的特性,重新思考 Node. js 的根本设计,包括像是可存取原始内存的标准方法 ArrayBuffers、适合弹性组合的 TypeScript Interfaces,以及新兴的非同步机制 Async 和 Await。Ryan Dahl 把这些新的 JavaScipt 功能,放入了 Deno 中,来设计一款新的服务器端 JavaScript 框架。

但是,这一次,他不想重走 Node.js 的老路,将整个 Web 服务器放进框架,Ryan Dahl 决定打造出一支自给自足功能完整的 runtime 程序,容易带着走,而不是有着一套复杂目录和结构的框架。

而且,打包成 runtime 形式,就可以部署到各种环境中,Ryan Dahl 举例,如果在无服务器服务上部署了Deno,就可指定一个网址,就能启动这个无服务器服务的调用,而不用上传一段代码到无服务器服务平台上执行,也可以部署到边缘运算设备中,来完成小型的数据处理工作。

不信任使用者的代码,只能在沙箱执行

另外在安全机制上,Deno 设计了两层权限架构,一个是拥有特权的核心层,另一个是没有特权的用户空间,RyanDahl 解释到,这就像是操作系统的设计一样,不信任使用者的代码,区分出使用者的权限和系统核心的权限等级,使用者的程序,要向使用到关键的资源,必须透过系统调用,由系统核心程序来执行。Deno 也是一样,“不信任用户端的 JavaScript 程序,只能在安全的沙箱中执行。”

所有涉及到敏感资源的处理,如底层文件系统,都需要授权执行,这就是预设安全性的设计,包括网络存取、文件系统写入、环境变量存取、执行等这些敏感的动作,都需要取得授权才能执行。

另外,Deno 的设计还将 I/O 处理抽象化,让 JavaScript 程序不必处理各种不同的输出或输入端配置,改由 runtime 接手,从而无法直接接触实体资源。一来简化各种不同的 I/O 存取方式,不论是本地或远 I/O,都是同样的 read 和 write 指令就可以搞定,二来也可以强化安全性。

另外,Deno 还借鉴了不少 Go 语言的特性,例如 Deno 的 copy() 就参考了 Go 语言的 io.Copy()BufReader() 也参考了 Go 语言的 bufio.Reader 设计等,还有不少测试机制、文件等,Ryan Dahl 也都参考了他熟悉的Go语言。

指定URL,就能嵌入第三方函数库

Deno 第三项设计目标是要打造一个去中心化的模块系统,Ryan Dahl 的设计是,Deno 可以像 JavaScript 一样,通过 URL 网址来嵌入外部的第三方函数库。除此之外,Deno和Rust语言也有不少整合,甚至改用Rust来实作Deno之后,Ryan Dahl透露,将会建立一个桥接机制,让Deno可以很容易地运用Rust中的函数库。

不过他坦言,嵌入外部的第三方函数库的机制,也是 Deno 项目发布后,人们询问最多、也最担心的功能,担心透过 URL 来引入外部函数库后,容易引发安全问题或是中间人攻击等问题。“这就是为什么 Deno 要采取预设安全性的设计的原因,来隔绝来自外部第三方代码的威胁。”

另外,通过 URL 连结到第三方函数库时,Deno 会通过缓存,在本地环境生成一份第三方函数库,第二次再调用到同样的URL时就不需再次下载了,以此来加快执行速度。甚至这个通过外部 URL 来引用函数库的功能,还可以指定版本,就算是改版了,还是可以指向旧版。当然 Ryan Dahl 强调,所有存取外部网络或下载写入到本地文件的动作,都需要取得授权才能执行。

未来会支持 WebGL,Deno 就能调用 GPU 资源

Deno 还有一个与 Node.js 最大的差异,就是未来会支持机器学习。Ryan Dahl 透露,他们正在开发一个数值计算类的外挂模块,最重要的就是要能支持 GPU,这也是机器学习模块最需要的功能。“Deno未来将会原生支持 WebGL,就可以让 JavaScipt 程序调用 GPU 的资源。这是他打造 Deno 的目标之一。”甚至,Ryan Dahl 预告,未来说不定可以把 TensorFlow JS 放上 Deno 来执行,因为 TensorFlow JS 用的也是 WebGL。

Deno未来将瞄准小型机器学习的推理需求

不同于 Nvidia的CUDA 可以用来调度多颗 GPU 资源进行复杂的机器学习训练工作,Ryan Dahl 解释,Deno 想要提供的是简单够用的机器学习能力,可以用来满足只有单颗 GPU,而且是小型或是只需要推理的计算需求,支持WebGL 已经够用了。

Deno 从 2018 年 5 月中放上 Github 网站开源至今年3月,已有超过80名开发者参与,经常贡献代码的核心开发者也有 5 名。目前正把主要精力放在在预设安全性架构的设计功能上。

最后企业能不能用 Deno?Ryan Dahl 坦言 Deno 距离 1.0 还有很长一段路要走,仍旧是一个非常新的技术。不过,“再等我1年,若有企业想用,请Email给我,我会提供技术支持。” 他认真地说。

关于Ryan Dahl

clipboard.png

2009年11月8日,Node. js之 父RyanDahl在欧洲 JSConf 大会,第一次发布了 Node.js ,一鸣惊人,将浏览器端的 JavaScript 技术,带入了服务器端应用领域。不过他从 2012 年开始淡出 Node.js 社区,转而进入Go、Rust语言社区,2017年加入 Google 大脑研究团队,担任深度学习工程师。现为自由开发者。

2018年6月初,Ryan Dahl 在 JSConf 欧洲大会发表了 Node.js 十大悔恨,并推出了新的服务器端 JavaScript runtime 方案 Deno。


本文首发微信公众号:jingchengyideng

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章



欢迎继续阅读本专栏其它高赞文章:

查看原文

我什么都不会 评论了文章 · 2019-03-19

deno 如何偿还 node.js 的十大技术债

根据网络资料整理

“Node现在太难用了!”。Node.js之父 Ryan Dahl 去年初要开发一款 JavaScript 互动式数据分析工具时,忍不住抱怨起自己十年前一手创造的技术。

Ryan Dahl 想要设计出一款类似 IPython 的互动式数据科学分析命令行工具,但改用 JavaScript 语言,要让 JavaScript 也可以像 Python 那样,进行各式各样的数据分析、统计计算以及数据视觉化战士。一度离开 Node.js 开发社区的 Rayn Dahl,再次拿起自己发明的 Node.js 来开发这个新的数据分析工具,但是越用越别扭,他开始思考,有什么方法可以改进 Node.js。

Node.js 是他在 2009 年 11 月 8 日时,在 JavaScript 社区欧洲 JSConf 大会上首度发布的,它把浏览器端的 JavaScript 技术,带入了服务器端应用领域。前端网页工程师从来都没想过,自己也可以成为后端工程师,但 Node.js 让前端技术走出了浏览器,前端工程师甚至可以成为全端工程师,Node.js 改变了前端工程师的世界。

从2009年 Ryan Dahl 设计出这个服务器端的 JavaScript 框架,至今已经发展到了第 10 版。而随 Node.js 而生,另一位开发者 Isaac 设计出的 JavaScript 包管理工具 npm,更成了网页开发者必懂得技术,在 npm 的储存库上,注册了超过 60 万个 Node.js 模块,这更让 Node.js 的应用遍及各类开发或软件需求。

JavaScript是最普及的语言,而Node.js是最受欢迎的框架

根据 Stack OverFlow 在 2018年度的开发者大调查(全球超过 10 万开发者参与),JavaScript 是开发者中最普及的技术,近 7 成开发者都会用,比 HTML 或 CSS 的普及率还要要高,而最多人懂的开发框架排名中,第一名就是 Node.js ,将近5成开发者(49.6%)经常使用,比 2017 年还小幅上升了 2 个百分点,同时使用者还在持续增加,远高于排名第二的 Angular(36.9%),这正是因为 Node.js 是前端和后端工程师都能用的技术。

Node.js 不只是当前的主流技术,也是下一代网页应用架构 Serverless(无服务器)架构的关键技术。负责 Azure Functions 项目的微软资深首席软件工程师 Christopher Anderson 就曾直言,主流无服务器服务商纷纷把宝押在 Node.js,因为看上了 JavaScript 工具的丰富生态,再加上 Node.js 的轻量化、容易分散与水平扩充、各种操作系统都容易运行的特性,将 Node.js 作为无服务器服务优先支持的框架,这也让 Node.js 更适合用于超大规模部署的应用。

Ryan Dahl 自己坦言,从没想到 Node.js 日后会带来这么大的影响。他也将此归功于开发社区的持续改善,才让它越来越成熟。截止到2018年8月,参与 Node.js 的开发者已经超过2千人,十年来的更新发布次数超过 500 次,在 GitHub 上代码的下载次数更是累计超过了10亿次,就连大型科技公司如 PayPal,或顶尖科研机构 NASA 都在使用。

但 Ryan Dahl 在 2012 年开始淡出 Node.js 社区,转而进入 Go、Rust 语言社区,也重回他擅长的数学应用领域,2017 年还申请了 Google 大脑一年的进驻计划,成为 Google 大脑研究团队的一员,担任深度学习工程师,并投入图像处理技术的研究。直到 2018 年 6 月初,就在 Node.js 准备迈入第 10 年之前,JSConf 欧洲大会再次邀请Ryan Dahl 来进行开场演讲。

尽管大受欢迎,但 Node.js 仍有十大技术债

原本 Ryan Dahl 打算在 2018 年的 JSConf 演讲中分享自己这款 JavaScript 版的 IPython 互动式数据分析工具,没想到一直开发到 5 月份,这个工具都还不能用。本来要放弃这次演讲的 Ryan Dahl 念头一转,干脆把他重拾 Node.js 后发现的问题拿出来分享,这就是去年引发全球开发社区热烈讨论的那场演讲,题目是 “我在 Node .js 最后悔的 10 件事”。Ryan Dahl 在演讲中坦言,Node.js 有十大设计错误,甚至可说是他的 10 大悔恨(他刻意用Regret 这个词来形容)!

这些让 Ryan Dahl 懊悔不已的错误,包括了没采用 JavaScript 非同步处理的 Promise 对象、低估了安全性的重要性、采用 gyp 来设计 Build 系统、没有听从社区建议改用 FFI 而继续用 gyp,另外,他也觉得 Node.js 过度依赖 npm 功能(内建到 package.json 支持是个错误)、用 require("")来嵌入任意模块简直太容易了、package.json 容易造成错误的模块观念(让开发者误以为同一目录下的文件就是同一模块)、臃肿复杂的node_module设计以及开发社区抱怨已久的下载黑洞问题(下载npm得花上非常久的等待时间)、require("module")功能没有强制要求注名.js扩展名,以及无用的 index.js 设计。

2012年,Ryan Dahl 离开了Node.js社区,他事后解释,Node.js 的发展已经步入正轨,也达到他最初的目标,因而决定离开,但在2018年这场演讲中,他坦言, Node.js 还有大把问题要修复,所以现在他回来了,要来偿还当年的技术债,挽回 Node.js 的设计错误。

Ryan Dahl 的答案是打造一个全新的服务器端 JavaScript 运行环境,也就是 Deno 项目。

Deno从2018年5月在 Github 开源至今年3月,已有超过100名开发者参与,经常贡献代码的核心开发者也有5名。

让Ryan Dahl懊悔不已的 Node.js 十大技术债

  1. 没用 JavaScript 异步处理的 Promise 对象
  2. 低估了安全的重要性
  3. 使用了 gyp 来设计 Build 系统
  4. 没有听大家的建议提供 FFI 而继续用 gyp
  5. 过度依赖 npm(内建 package.json支持)
  6. 太容易造成 require("任意模块")
  7. package.json 建立了错误的模块概念(在同一目录下的文件就是同一模块)
  8. 臃肿复杂的 node_module 设计和下载黑洞(往往下载 npm 得花上非常久的时间)
  9. require("module") 时没有强制加上 .js 扩展名
  10. 无用的 index.js 设计。

Deno 如何挽回 Node.js 设计上遗留的问题

clipboard.png

这是 Deno 项目的一个范例,是 Unix 系统基本命令 cat 的一个实现。cat 可以从标准输入取得文件, 再逐行把文件内容送出到标准输出上。这个范例反映出 Deno 要将 I/O 抽象化和精简化的意图。

Ryan Dahl 希望通过打造 Deno 这个全新的服务器端 JavaScript 运行环境,来解决 Node.js 的三大问题,包括准确的 I/O 接口、预设安全性(Secure by Default)以及引进一套去中心化的模块系统等,最后一项就是要解决下载过久过慢的老问题。

Ryan Dahl 进一步解释,虽然他所有的时间都是用 C++、Go 或 Rust 这类编译式语言来开发,但是他还是有一些经常要做的事,需要使用动态的脚本程序。例如整理资料、协调测试任务、部署服务器或用户端环境、绘制统计图表、设定构建系统(Build System)的参数或是设计应用雏形等。

可是,这些不同用途的任务,需要切换使用多种不同的脚本语言或工具,如 Bash、Python 或是 Node.js 等才行,相当麻烦。而 2018 年上半年的这个互动式数据分析工具的开发挫折,更让他有一股强烈地念头,能不能有一个通用的脚本工具。

Deno

Deno 架构

打造一款简单、好用的通用脚本工具

Ryan Dahl 在 2018 年 11 月参加台湾年度 JavaScript 开发者大会(JSDC)时,特别提到:“我不喜欢用不同工具来处理不同的事情,我只想要有一个简单,直接可执行,拿了就能用的顺手工具,这正是打造Deno的初衷。”

简单来说,Deno 跟 Node.js 一样都采用了 Chrome 的 JavaScript 引擎 V8,但 Deno 采用了更严格的 JavaScript 语法规范 TypeScript,Deno 等于是一个 TypeScript runtime。第一个版本的 Deno runtime 是用 Go 语言实现的,但是 Ryan Dahl 又重新用 Rust 语言开发了一次 Deno 的 runtime,避免因为重复使用两套垃圾回收器(Go语言一套、V8引擎也内建了一套)而影响效能。另外,Deno runtime 中也内建了 TypeScript 编译器。

Deno的目标是安全、简洁、单一可执行文件

Deno的设计目标是安全、模块简洁、单一可执行文件(简化封装)等,目前已完成的特色之一,就是可以透过URL 来汇入各种模块,另外预设安全性,要存取实体资源或网络时都需要授权,用户的代码只能在安全的沙箱中执行。为什么他最熟悉的 Go 做不到?因为“动态语言仍有其必要。“他强调,尤其要建立一个适当好用的 I/O 处理流程(pipeline)时,动态脚本语言是不可或缺的工具。

而 JavaScript 就是那个他心中的理想动态语言,但是,Node.js 是一项将近 10 年历史的技术,受限于最初的设计架构,他认为,可以重新用 JavaScript 近几年出现的特性,重新思考 Node. js 的根本设计,包括像是可存取原始内存的标准方法 ArrayBuffers、适合弹性组合的 TypeScript Interfaces,以及新兴的非同步机制 Async 和 Await。Ryan Dahl 把这些新的 JavaScipt 功能,放入了 Deno 中,来设计一款新的服务器端 JavaScript 框架。

但是,这一次,他不想重走 Node.js 的老路,将整个 Web 服务器放进框架,Ryan Dahl 决定打造出一支自给自足功能完整的 runtime 程序,容易带着走,而不是有着一套复杂目录和结构的框架。

而且,打包成 runtime 形式,就可以部署到各种环境中,Ryan Dahl 举例,如果在无服务器服务上部署了Deno,就可指定一个网址,就能启动这个无服务器服务的调用,而不用上传一段代码到无服务器服务平台上执行,也可以部署到边缘运算设备中,来完成小型的数据处理工作。

不信任使用者的代码,只能在沙箱执行

另外在安全机制上,Deno 设计了两层权限架构,一个是拥有特权的核心层,另一个是没有特权的用户空间,RyanDahl 解释到,这就像是操作系统的设计一样,不信任使用者的代码,区分出使用者的权限和系统核心的权限等级,使用者的程序,要向使用到关键的资源,必须透过系统调用,由系统核心程序来执行。Deno 也是一样,“不信任用户端的 JavaScript 程序,只能在安全的沙箱中执行。”

所有涉及到敏感资源的处理,如底层文件系统,都需要授权执行,这就是预设安全性的设计,包括网络存取、文件系统写入、环境变量存取、执行等这些敏感的动作,都需要取得授权才能执行。

另外,Deno 的设计还将 I/O 处理抽象化,让 JavaScript 程序不必处理各种不同的输出或输入端配置,改由 runtime 接手,从而无法直接接触实体资源。一来简化各种不同的 I/O 存取方式,不论是本地或远 I/O,都是同样的 read 和 write 指令就可以搞定,二来也可以强化安全性。

另外,Deno 还借鉴了不少 Go 语言的特性,例如 Deno 的 copy() 就参考了 Go 语言的 io.Copy()BufReader() 也参考了 Go 语言的 bufio.Reader 设计等,还有不少测试机制、文件等,Ryan Dahl 也都参考了他熟悉的Go语言。

指定URL,就能嵌入第三方函数库

Deno 第三项设计目标是要打造一个去中心化的模块系统,Ryan Dahl 的设计是,Deno 可以像 JavaScript 一样,通过 URL 网址来嵌入外部的第三方函数库。除此之外,Deno和Rust语言也有不少整合,甚至改用Rust来实作Deno之后,Ryan Dahl透露,将会建立一个桥接机制,让Deno可以很容易地运用Rust中的函数库。

不过他坦言,嵌入外部的第三方函数库的机制,也是 Deno 项目发布后,人们询问最多、也最担心的功能,担心透过 URL 来引入外部函数库后,容易引发安全问题或是中间人攻击等问题。“这就是为什么 Deno 要采取预设安全性的设计的原因,来隔绝来自外部第三方代码的威胁。”

另外,通过 URL 连结到第三方函数库时,Deno 会通过缓存,在本地环境生成一份第三方函数库,第二次再调用到同样的URL时就不需再次下载了,以此来加快执行速度。甚至这个通过外部 URL 来引用函数库的功能,还可以指定版本,就算是改版了,还是可以指向旧版。当然 Ryan Dahl 强调,所有存取外部网络或下载写入到本地文件的动作,都需要取得授权才能执行。

未来会支持 WebGL,Deno 就能调用 GPU 资源

Deno 还有一个与 Node.js 最大的差异,就是未来会支持机器学习。Ryan Dahl 透露,他们正在开发一个数值计算类的外挂模块,最重要的就是要能支持 GPU,这也是机器学习模块最需要的功能。“Deno未来将会原生支持 WebGL,就可以让 JavaScipt 程序调用 GPU 的资源。这是他打造 Deno 的目标之一。”甚至,Ryan Dahl 预告,未来说不定可以把 TensorFlow JS 放上 Deno 来执行,因为 TensorFlow JS 用的也是 WebGL。

Deno未来将瞄准小型机器学习的推理需求

不同于 Nvidia的CUDA 可以用来调度多颗 GPU 资源进行复杂的机器学习训练工作,Ryan Dahl 解释,Deno 想要提供的是简单够用的机器学习能力,可以用来满足只有单颗 GPU,而且是小型或是只需要推理的计算需求,支持WebGL 已经够用了。

Deno 从 2018 年 5 月中放上 Github 网站开源至今年3月,已有超过80名开发者参与,经常贡献代码的核心开发者也有 5 名。目前正把主要精力放在在预设安全性架构的设计功能上。

最后企业能不能用 Deno?Ryan Dahl 坦言 Deno 距离 1.0 还有很长一段路要走,仍旧是一个非常新的技术。不过,“再等我1年,若有企业想用,请Email给我,我会提供技术支持。” 他认真地说。

关于Ryan Dahl

clipboard.png

2009年11月8日,Node. js之 父RyanDahl在欧洲 JSConf 大会,第一次发布了 Node.js ,一鸣惊人,将浏览器端的 JavaScript 技术,带入了服务器端应用领域。不过他从 2012 年开始淡出 Node.js 社区,转而进入Go、Rust语言社区,2017年加入 Google 大脑研究团队,担任深度学习工程师。现为自由开发者。

2018年6月初,Ryan Dahl 在 JSConf 欧洲大会发表了 Node.js 十大悔恨,并推出了新的服务器端 JavaScript runtime 方案 Deno。


本文首发微信公众号:jingchengyideng

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章



欢迎继续阅读本专栏其它高赞文章:

查看原文

我什么都不会 赞了文章 · 2019-03-19

前端监控数据收集(perf)

所谓web,即使你我素未谋面,便知志趣相投;足不出户,亦知世界之大。

这节我们了解一下获取用户访问网页速度和Web应用程序的性能,那如何来获取这些数据呢?我们项目中使用PerformanceTiming 接口来获取当前页面中与时间相关的信息。PerformanceTiming 可以通过只读属性Performance.timing 获得实现该接口的一个对象。

我们先来看一张图,timing API的提供了整个请求的各个阶段的时间信息

图片描述

PerformanceTiming.navigationStart
是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同。

PerformanceTiming.unloadEventStart
是一个无符号long long 型的毫秒数,表征了unload事件抛出时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.unloadEventEnd
是一个无符号long long 型的毫秒数,表征了unload事件处理完成时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.redirectStart
是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.redirectEnd
是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.fetchStart
是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。

PerformanceTiming.domainLookupStart
是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.domainLookupEnd
是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.connectStart
是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。

PerformanceTiming.connectEnd
是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。

PerformanceTiming.secureConnectionStart
是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。

PerformanceTiming.requestStart
是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。

PerformanceTiming.responseStart
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。

PerformanceTiming.responseEnd
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。

PerformanceTiming.domLoading
是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domInteractive
是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventStart
是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventEnd
是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。

PerformanceTiming.domComplete
是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的Unix毫秒时间戳。

PerformanceTiming.loadEventStart
是一个无符号long long 型的毫秒数,返回该文档下,load事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。

PerformanceTiming.loadEventEnd
是一个无符号long long 型的毫秒数,返回当load事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0.

重点来了!!!看了以上参数,我们想要的数据来源有了,现在要做的便是去整理这些数据,将他变成我们项目中使用的数据。我们将要收集的时间归为两类:

1.区间阶段耗时

DNS 解析耗时

dns: timing.domainLookupEnd - timing.domainLookupStart

TCP 连接耗时

tcp: timing.connectEnd - timing.connectStart

SSL 安全连接耗时

ssl: timing.connectEnd - timing.secureConnectionStart

Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准

ttfb: timing.responseStart - timing.requestStart

数据传输耗时

trans: timing.responseEnd - timing.responseStart

DOM 解析耗时

dom: timing.domInteractive - timing.responseEnd

资源加载耗时

res: timing.loadEventStart - timing.domContentLoadedEventEnd

2.关键性能指标

首包时间

firstbyte: timing.responseStart - timing.domainLookupStart

First Paint Time, 首次渲染时间 / 白屏时间

fpt: timing.responseEnd - timing.fetchStart

Time to Interact,首次可交互时间

tti: timing.domInteractive - timing.fetchStart

HTML 加载完成时间, 即 DOM Ready 时间

ready: timing.domContentLoadedEventEnd - timing.fetchStart

页面完全加载时间

load:timing.loadEventEnd - timing.fetchStart

现在这两部分数据有了,我们就可以将这些数据上传到我们后台服务上去了,是不是很简单呢,^_^。对于如何展示我们收集的数据,请前往(http://hubing.online:8083)瞅一眼哦。

不是很明白?那我再附上一些源码吧

    //监听perf
    let performanceTime = function () {
        var timing = performance.timing;
        var loadTime = timing.loadEventEnd - timing.navigationStart;//过早获取时,loadEventEnd有时会是0
        if (loadTime <= 0) {
            // 未加载完,延迟200ms后继续times方法,直到成功
            setTimeout(function () {
                performanceTime();
            }, 200);
            return;
        }
        uploadUserData(1, {
            // 1.区间阶段耗时
            //  DNS 解析耗时 
            dns: formatTime(timing.domainLookupEnd - timing.domainLookupStart),
            // TCP 连接耗时
            tcp: formatTime(timing.connectEnd - timing.connectStart),
            // SSL 安全连接耗时
            ssl: formatTime(timing.connectEnd - timing.secureConnectionStart),
            // Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准
            ttfb: formatTime(timing.responseStart - timing.requestStart),
            // 数据传输耗时
            trans: formatTime(timing.responseEnd - timing.responseStart),
            // DOM 解析耗时
            dom: formatTime(timing.domInteractive - timing.responseEnd),
            // 资源加载耗时
            res: formatTime(timing.loadEventStart - timing.domContentLoadedEventEnd),
            // 2.关键性能指标
            // 首包时间
            firstbyte: formatTime(timing.responseStart - timing.domainLookupStart),
            // First Paint Time, 首次渲染时间 / 白屏时间
            fpt: formatTime(timing.responseEnd - timing.fetchStart),
            // Time to Interact,首次可交互时间
            tti: formatTime(timing.domInteractive - timing.fetchStart),
            // HTML 加载完成时间, 即 DOM Ready 时间
            ready: formatTime(timing.domContentLoadedEventEnd - timing.fetchStart),
            // 页面完全加载时间
            load: function () {
                return formatTime(timing.loadEventEnd - timing.fetchStart);
            }(),
            navt: (function () {
                let type = "";
                switch (performance.navigation.type) {
                    case 0:
                        type = 'NAVIGATE';
                        break;
                    case 1:
                        type = 'RELOAD';
                        break;
                    case 2:
                        type = 'BACK_FORWARD';
                        break;
                    case 255:
                        type = 'RESERVED';
                        break;
                }
                return type;
            })()
        });
    }
​
    window.addEventListener("load", function () {
        performanceTime();
    });


喜欢请点个赞呗

或者去https://github.com/kisslove/w... Star一下

或者打赏一下

再或者……

哈哈,想法有点多了。

图片描述

查看原文

赞 2 收藏 2 评论 0

我什么都不会 赞了文章 · 2019-03-19

前端监控数据收集(perf)

所谓web,即使你我素未谋面,便知志趣相投;足不出户,亦知世界之大。

这节我们了解一下获取用户访问网页速度和Web应用程序的性能,那如何来获取这些数据呢?我们项目中使用PerformanceTiming 接口来获取当前页面中与时间相关的信息。PerformanceTiming 可以通过只读属性Performance.timing 获得实现该接口的一个对象。

我们先来看一张图,timing API的提供了整个请求的各个阶段的时间信息

图片描述

PerformanceTiming.navigationStart
是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同。

PerformanceTiming.unloadEventStart
是一个无符号long long 型的毫秒数,表征了unload事件抛出时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.unloadEventEnd
是一个无符号long long 型的毫秒数,表征了unload事件处理完成时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.redirectStart
是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.redirectEnd
是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.fetchStart
是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。

PerformanceTiming.domainLookupStart
是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.domainLookupEnd
是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.connectStart
是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。

PerformanceTiming.connectEnd
是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。

PerformanceTiming.secureConnectionStart
是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。

PerformanceTiming.requestStart
是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。

PerformanceTiming.responseStart
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。

PerformanceTiming.responseEnd
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。

PerformanceTiming.domLoading
是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domInteractive
是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventStart
是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventEnd
是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。

PerformanceTiming.domComplete
是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的Unix毫秒时间戳。

PerformanceTiming.loadEventStart
是一个无符号long long 型的毫秒数,返回该文档下,load事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。

PerformanceTiming.loadEventEnd
是一个无符号long long 型的毫秒数,返回当load事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0.

重点来了!!!看了以上参数,我们想要的数据来源有了,现在要做的便是去整理这些数据,将他变成我们项目中使用的数据。我们将要收集的时间归为两类:

1.区间阶段耗时

DNS 解析耗时

dns: timing.domainLookupEnd - timing.domainLookupStart

TCP 连接耗时

tcp: timing.connectEnd - timing.connectStart

SSL 安全连接耗时

ssl: timing.connectEnd - timing.secureConnectionStart

Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准

ttfb: timing.responseStart - timing.requestStart

数据传输耗时

trans: timing.responseEnd - timing.responseStart

DOM 解析耗时

dom: timing.domInteractive - timing.responseEnd

资源加载耗时

res: timing.loadEventStart - timing.domContentLoadedEventEnd

2.关键性能指标

首包时间

firstbyte: timing.responseStart - timing.domainLookupStart

First Paint Time, 首次渲染时间 / 白屏时间

fpt: timing.responseEnd - timing.fetchStart

Time to Interact,首次可交互时间

tti: timing.domInteractive - timing.fetchStart

HTML 加载完成时间, 即 DOM Ready 时间

ready: timing.domContentLoadedEventEnd - timing.fetchStart

页面完全加载时间

load:timing.loadEventEnd - timing.fetchStart

现在这两部分数据有了,我们就可以将这些数据上传到我们后台服务上去了,是不是很简单呢,^_^。对于如何展示我们收集的数据,请前往(http://hubing.online:8083)瞅一眼哦。

不是很明白?那我再附上一些源码吧

    //监听perf
    let performanceTime = function () {
        var timing = performance.timing;
        var loadTime = timing.loadEventEnd - timing.navigationStart;//过早获取时,loadEventEnd有时会是0
        if (loadTime <= 0) {
            // 未加载完,延迟200ms后继续times方法,直到成功
            setTimeout(function () {
                performanceTime();
            }, 200);
            return;
        }
        uploadUserData(1, {
            // 1.区间阶段耗时
            //  DNS 解析耗时 
            dns: formatTime(timing.domainLookupEnd - timing.domainLookupStart),
            // TCP 连接耗时
            tcp: formatTime(timing.connectEnd - timing.connectStart),
            // SSL 安全连接耗时
            ssl: formatTime(timing.connectEnd - timing.secureConnectionStart),
            // Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准
            ttfb: formatTime(timing.responseStart - timing.requestStart),
            // 数据传输耗时
            trans: formatTime(timing.responseEnd - timing.responseStart),
            // DOM 解析耗时
            dom: formatTime(timing.domInteractive - timing.responseEnd),
            // 资源加载耗时
            res: formatTime(timing.loadEventStart - timing.domContentLoadedEventEnd),
            // 2.关键性能指标
            // 首包时间
            firstbyte: formatTime(timing.responseStart - timing.domainLookupStart),
            // First Paint Time, 首次渲染时间 / 白屏时间
            fpt: formatTime(timing.responseEnd - timing.fetchStart),
            // Time to Interact,首次可交互时间
            tti: formatTime(timing.domInteractive - timing.fetchStart),
            // HTML 加载完成时间, 即 DOM Ready 时间
            ready: formatTime(timing.domContentLoadedEventEnd - timing.fetchStart),
            // 页面完全加载时间
            load: function () {
                return formatTime(timing.loadEventEnd - timing.fetchStart);
            }(),
            navt: (function () {
                let type = "";
                switch (performance.navigation.type) {
                    case 0:
                        type = 'NAVIGATE';
                        break;
                    case 1:
                        type = 'RELOAD';
                        break;
                    case 2:
                        type = 'BACK_FORWARD';
                        break;
                    case 255:
                        type = 'RESERVED';
                        break;
                }
                return type;
            })()
        });
    }
​
    window.addEventListener("load", function () {
        performanceTime();
    });


喜欢请点个赞呗

或者去https://github.com/kisslove/w... Star一下

或者打赏一下

再或者……

哈哈,想法有点多了。

图片描述

查看原文

赞 2 收藏 2 评论 0

我什么都不会 收藏了文章 · 2019-03-19

前端监控数据收集(perf)

所谓web,即使你我素未谋面,便知志趣相投;足不出户,亦知世界之大。

这节我们了解一下获取用户访问网页速度和Web应用程序的性能,那如何来获取这些数据呢?我们项目中使用PerformanceTiming 接口来获取当前页面中与时间相关的信息。PerformanceTiming 可以通过只读属性Performance.timing 获得实现该接口的一个对象。

我们先来看一张图,timing API的提供了整个请求的各个阶段的时间信息

图片描述

PerformanceTiming.navigationStart
是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同。

PerformanceTiming.unloadEventStart
是一个无符号long long 型的毫秒数,表征了unload事件抛出时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.unloadEventEnd
是一个无符号long long 型的毫秒数,表征了unload事件处理完成时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.redirectStart
是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.redirectEnd
是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.fetchStart
是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。

PerformanceTiming.domainLookupStart
是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.domainLookupEnd
是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.connectStart
是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。

PerformanceTiming.connectEnd
是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。

PerformanceTiming.secureConnectionStart
是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。

PerformanceTiming.requestStart
是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。

PerformanceTiming.responseStart
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。

PerformanceTiming.responseEnd
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。

PerformanceTiming.domLoading
是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domInteractive
是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventStart
是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventEnd
是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。

PerformanceTiming.domComplete
是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的Unix毫秒时间戳。

PerformanceTiming.loadEventStart
是一个无符号long long 型的毫秒数,返回该文档下,load事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。

PerformanceTiming.loadEventEnd
是一个无符号long long 型的毫秒数,返回当load事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0.

重点来了!!!看了以上参数,我们想要的数据来源有了,现在要做的便是去整理这些数据,将他变成我们项目中使用的数据。我们将要收集的时间归为两类:

1.区间阶段耗时

DNS 解析耗时

dns: timing.domainLookupEnd - timing.domainLookupStart

TCP 连接耗时

tcp: timing.connectEnd - timing.connectStart

SSL 安全连接耗时

ssl: timing.connectEnd - timing.secureConnectionStart

Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准

ttfb: timing.responseStart - timing.requestStart

数据传输耗时

trans: timing.responseEnd - timing.responseStart

DOM 解析耗时

dom: timing.domInteractive - timing.responseEnd

资源加载耗时

res: timing.loadEventStart - timing.domContentLoadedEventEnd

2.关键性能指标

首包时间

firstbyte: timing.responseStart - timing.domainLookupStart

First Paint Time, 首次渲染时间 / 白屏时间

fpt: timing.responseEnd - timing.fetchStart

Time to Interact,首次可交互时间

tti: timing.domInteractive - timing.fetchStart

HTML 加载完成时间, 即 DOM Ready 时间

ready: timing.domContentLoadedEventEnd - timing.fetchStart

页面完全加载时间

load:timing.loadEventEnd - timing.fetchStart

现在这两部分数据有了,我们就可以将这些数据上传到我们后台服务上去了,是不是很简单呢,^_^。对于如何展示我们收集的数据,请前往(http://hubing.online:8083)瞅一眼哦。

不是很明白?那我再附上一些源码吧

    //监听perf
    let performanceTime = function () {
        var timing = performance.timing;
        var loadTime = timing.loadEventEnd - timing.navigationStart;//过早获取时,loadEventEnd有时会是0
        if (loadTime <= 0) {
            // 未加载完,延迟200ms后继续times方法,直到成功
            setTimeout(function () {
                performanceTime();
            }, 200);
            return;
        }
        uploadUserData(1, {
            // 1.区间阶段耗时
            //  DNS 解析耗时 
            dns: formatTime(timing.domainLookupEnd - timing.domainLookupStart),
            // TCP 连接耗时
            tcp: formatTime(timing.connectEnd - timing.connectStart),
            // SSL 安全连接耗时
            ssl: formatTime(timing.connectEnd - timing.secureConnectionStart),
            // Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准
            ttfb: formatTime(timing.responseStart - timing.requestStart),
            // 数据传输耗时
            trans: formatTime(timing.responseEnd - timing.responseStart),
            // DOM 解析耗时
            dom: formatTime(timing.domInteractive - timing.responseEnd),
            // 资源加载耗时
            res: formatTime(timing.loadEventStart - timing.domContentLoadedEventEnd),
            // 2.关键性能指标
            // 首包时间
            firstbyte: formatTime(timing.responseStart - timing.domainLookupStart),
            // First Paint Time, 首次渲染时间 / 白屏时间
            fpt: formatTime(timing.responseEnd - timing.fetchStart),
            // Time to Interact,首次可交互时间
            tti: formatTime(timing.domInteractive - timing.fetchStart),
            // HTML 加载完成时间, 即 DOM Ready 时间
            ready: formatTime(timing.domContentLoadedEventEnd - timing.fetchStart),
            // 页面完全加载时间
            load: function () {
                return formatTime(timing.loadEventEnd - timing.fetchStart);
            }(),
            navt: (function () {
                let type = "";
                switch (performance.navigation.type) {
                    case 0:
                        type = 'NAVIGATE';
                        break;
                    case 1:
                        type = 'RELOAD';
                        break;
                    case 2:
                        type = 'BACK_FORWARD';
                        break;
                    case 255:
                        type = 'RESERVED';
                        break;
                }
                return type;
            })()
        });
    }
​
    window.addEventListener("load", function () {
        performanceTime();
    });


喜欢请点个赞呗

或者去https://github.com/kisslove/w... Star一下

或者打赏一下

再或者……

哈哈,想法有点多了。

图片描述

查看原文

我什么都不会 收藏了文章 · 2019-03-19

前端监控数据收集(perf)

所谓web,即使你我素未谋面,便知志趣相投;足不出户,亦知世界之大。

这节我们了解一下获取用户访问网页速度和Web应用程序的性能,那如何来获取这些数据呢?我们项目中使用PerformanceTiming 接口来获取当前页面中与时间相关的信息。PerformanceTiming 可以通过只读属性Performance.timing 获得实现该接口的一个对象。

我们先来看一张图,timing API的提供了整个请求的各个阶段的时间信息

图片描述

PerformanceTiming.navigationStart
是一个无符号long long 型的毫秒数,表征了从同一个浏览器上下文的上一个文档卸载(unload)结束时的UNIX时间戳。如果没有上一个文档,这个值会和PerformanceTiming.fetchStart相同。

PerformanceTiming.unloadEventStart
是一个无符号long long 型的毫秒数,表征了unload事件抛出时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.unloadEventEnd
是一个无符号long long 型的毫秒数,表征了unload事件处理完成时的UNIX时间戳。如果没有上一个文档,or if the previous document, or one of the needed redirects, is not of the same origin, 这个值会返回0.

PerformanceTiming.redirectStart
是一个无符号long long 型的毫秒数,表征了第一个HTTP重定向开始时的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.redirectEnd
是一个无符号long long 型的毫秒数,表征了最后一个HTTP重定向完成时(也就是说是HTTP响应的最后一个比特直接被收到的时间)的UNIX时间戳。如果没有重定向,或者重定向中的一个不同源,这个值会返回0.

PerformanceTiming.fetchStart
是一个无符号long long 型的毫秒数,表征了浏览器准备好使用HTTP请求来获取(fetch)文档的UNIX时间戳。这个时间点会在检查任何应用缓存之前。

PerformanceTiming.domainLookupStart
是一个无符号long long 型的毫秒数,表征了域名查询开始的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.domainLookupEnd
是一个无符号long long 型的毫秒数,表征了域名查询结束的UNIX时间戳。如果使用了持续连接(persistent connection),或者这个信息存储到了缓存或者本地资源上,这个值将和 PerformanceTiming.fetchStart一致。

PerformanceTiming.connectStart
是一个无符号long long 型的毫秒数,返回HTTP请求开始向服务器发送时的Unix毫秒时间戳。如果使用持久连接(persistent connection),则返回值等同于fetchStart属性的值。

PerformanceTiming.connectEnd
是一个无符号long long 型的毫秒数,返回浏览器与服务器之间的连接建立时的Unix毫秒时间戳。如果建立的是持久连接,则返回值等同于fetchStart属性的值。连接建立指的是所有握手和认证过程全部结束。

PerformanceTiming.secureConnectionStart
是一个无符号long long 型的毫秒数,返回浏览器与服务器开始安全链接的握手时的Unix毫秒时间戳。如果当前网页不要求安全连接,则返回0。

PerformanceTiming.requestStart
是一个无符号long long 型的毫秒数,返回浏览器向服务器发出HTTP请求时(或开始读取本地缓存时)的Unix毫秒时间戳。

PerformanceTiming.responseStart
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取)第一个字节时的Unix毫秒时间戳。如果传输层在开始请求之后失败并且连接被重开,该属性将会被数制成新的请求的相对应的发起时间。

PerformanceTiming.responseEnd
是一个无符号long long 型的毫秒数,返回浏览器从服务器收到(或从本地缓存读取,或从本地资源读取)最后一个字节时(如果在此之前HTTP连接已经关闭,则返回关闭时)的Unix毫秒时间戳。

PerformanceTiming.domLoading
是一个无符号long long 型的毫秒数,返回当前网页DOM结构开始解析时(即Document.readyState属性变为“loading”、相应的 readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domInteractive
是一个无符号long long 型的毫秒数,返回当前网页DOM结构结束解析、开始加载内嵌资源时(即Document.readyState属性变为“interactive”、相应的readystatechange事件触发时)的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventStart
是一个无符号long long 型的毫秒数,返回当解析器发送DOMContentLoaded 事件,即所有需要被执行的脚本已经被解析时的Unix毫秒时间戳。

PerformanceTiming.domContentLoadedEventEnd
是一个无符号long long 型的毫秒数,返回当所有需要立即执行的脚本已经被执行(不论执行顺序)时的Unix毫秒时间戳。

PerformanceTiming.domComplete
是一个无符号long long 型的毫秒数,返回当前文档解析完成,即Document.readyState 变为 'complete'且相对应的readystatechange 被触发时的Unix毫秒时间戳。

PerformanceTiming.loadEventStart
是一个无符号long long 型的毫秒数,返回该文档下,load事件被发送时的Unix毫秒时间戳。如果这个事件还未被发送,它的值将会是0。

PerformanceTiming.loadEventEnd
是一个无符号long long 型的毫秒数,返回当load事件结束,即加载事件完成时的Unix毫秒时间戳。如果这个事件还未被发送,或者尚未完成,它的值将会是0.

重点来了!!!看了以上参数,我们想要的数据来源有了,现在要做的便是去整理这些数据,将他变成我们项目中使用的数据。我们将要收集的时间归为两类:

1.区间阶段耗时

DNS 解析耗时

dns: timing.domainLookupEnd - timing.domainLookupStart

TCP 连接耗时

tcp: timing.connectEnd - timing.connectStart

SSL 安全连接耗时

ssl: timing.connectEnd - timing.secureConnectionStart

Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准

ttfb: timing.responseStart - timing.requestStart

数据传输耗时

trans: timing.responseEnd - timing.responseStart

DOM 解析耗时

dom: timing.domInteractive - timing.responseEnd

资源加载耗时

res: timing.loadEventStart - timing.domContentLoadedEventEnd

2.关键性能指标

首包时间

firstbyte: timing.responseStart - timing.domainLookupStart

First Paint Time, 首次渲染时间 / 白屏时间

fpt: timing.responseEnd - timing.fetchStart

Time to Interact,首次可交互时间

tti: timing.domInteractive - timing.fetchStart

HTML 加载完成时间, 即 DOM Ready 时间

ready: timing.domContentLoadedEventEnd - timing.fetchStart

页面完全加载时间

load:timing.loadEventEnd - timing.fetchStart

现在这两部分数据有了,我们就可以将这些数据上传到我们后台服务上去了,是不是很简单呢,^_^。对于如何展示我们收集的数据,请前往(http://hubing.online:8083)瞅一眼哦。

不是很明白?那我再附上一些源码吧

    //监听perf
    let performanceTime = function () {
        var timing = performance.timing;
        var loadTime = timing.loadEventEnd - timing.navigationStart;//过早获取时,loadEventEnd有时会是0
        if (loadTime <= 0) {
            // 未加载完,延迟200ms后继续times方法,直到成功
            setTimeout(function () {
                performanceTime();
            }, 200);
            return;
        }
        uploadUserData(1, {
            // 1.区间阶段耗时
            //  DNS 解析耗时 
            dns: formatTime(timing.domainLookupEnd - timing.domainLookupStart),
            // TCP 连接耗时
            tcp: formatTime(timing.connectEnd - timing.connectStart),
            // SSL 安全连接耗时
            ssl: formatTime(timing.connectEnd - timing.secureConnectionStart),
            // Time to First Byte(TTFB),网络请求耗时 TTFB 有多种计算方式,ARMS 以 Google Development 定义为准
            ttfb: formatTime(timing.responseStart - timing.requestStart),
            // 数据传输耗时
            trans: formatTime(timing.responseEnd - timing.responseStart),
            // DOM 解析耗时
            dom: formatTime(timing.domInteractive - timing.responseEnd),
            // 资源加载耗时
            res: formatTime(timing.loadEventStart - timing.domContentLoadedEventEnd),
            // 2.关键性能指标
            // 首包时间
            firstbyte: formatTime(timing.responseStart - timing.domainLookupStart),
            // First Paint Time, 首次渲染时间 / 白屏时间
            fpt: formatTime(timing.responseEnd - timing.fetchStart),
            // Time to Interact,首次可交互时间
            tti: formatTime(timing.domInteractive - timing.fetchStart),
            // HTML 加载完成时间, 即 DOM Ready 时间
            ready: formatTime(timing.domContentLoadedEventEnd - timing.fetchStart),
            // 页面完全加载时间
            load: function () {
                return formatTime(timing.loadEventEnd - timing.fetchStart);
            }(),
            navt: (function () {
                let type = "";
                switch (performance.navigation.type) {
                    case 0:
                        type = 'NAVIGATE';
                        break;
                    case 1:
                        type = 'RELOAD';
                        break;
                    case 2:
                        type = 'BACK_FORWARD';
                        break;
                    case 255:
                        type = 'RESERVED';
                        break;
                }
                return type;
            })()
        });
    }
​
    window.addEventListener("load", function () {
        performanceTime();
    });


喜欢请点个赞呗

或者去https://github.com/kisslove/w... Star一下

或者打赏一下

再或者……

哈哈,想法有点多了。

图片描述

查看原文

认证与成就

  • 获得 4 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2018-03-27
个人主页被 363 人浏览