Skip to content

Commit

Permalink
✨ some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Copyes committed Mar 26, 2018
1 parent ebd9538 commit 97f9982
Show file tree
Hide file tree
Showing 10 changed files with 674 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.history
.vscode
node_modules
yarn-error.log
171 changes: 171 additions & 0 deletions brower.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#### 浏览器相关知识点总结

> 1、浏览器进程
* 主进程:只有一个主要是负责各个进程之间的调控
* gpu 进程:只要是用于 3d 渲染
* 后台修复进程
* tab 进程(浏览器渲染进程):开多个 tab 的时候相互之间不会受到影响。控制各个 tab 自己的渲染,脚本执行,事件处理等等事情。
* 插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建

> 2、多线程的浏览器渲染进程
* GUI 渲染线程
* JS 引擎线程(单线程)
* 事件触发线程
* 定时器线程
* 网络请求线程

> 3、网络请求都是单独的线程
每次网络请求时都需要开辟单独的线程进行,譬如如果 URL 解析到 http 协议,就会新建一个网络线程去处理资源下载

因此浏览器会根据解析出得协议,开辟一个网络线程,前往请求资源(这里,暂时理解为是浏览器内核开辟的,如有错误,后续修复)

> 4、tcp/ip 并发限制
浏览器在同一域名下的并发 tcp 链接限制一般是 2 到 10 个。

> 5、performance
```js
// 计算加载时间
function getPerformanceTiming() {
var performance = window.performance

if (!performance) {
// 当前浏览器不支持
console.log('你的浏览器不支持 performance 接口')
return
}

var t = performance.timing
var times = {}

//【重要】页面加载完成的时间
//【原因】这几乎代表了用户等待页面可用的时间
times.loadPage = t.loadEventEnd - t.navigationStart

//【重要】解析 DOM 树结构的时间
//【原因】反省下你的 DOM 树嵌套是不是太多了!
times.domReady = t.domComplete - t.responseEnd

//【重要】重定向的时间
//【原因】拒绝重定向!比如,http://example.com/ 就不该写成 http://example.com
times.redirect = t.redirectEnd - t.redirectStart

//【重要】DNS 查询时间
//【原因】DNS 预加载做了么?页面内是不是使用了太多不同的域名导致域名查询的时间太长?
// 可使用 HTML5 Prefetch 预查询 DNS ,见:[HTML5 prefetch](http://segmentfault.com/a/1190000000633364)
times.lookupDomain = t.domainLookupEnd - t.domainLookupStart

//【重要】读取页面第一个字节的时间
//【原因】这可以理解为用户拿到你的资源占用的时间,加异地机房了么,加CDN 处理了么?加带宽了么?加 CPU 运算速度了么?
// TTFB 即 Time To First Byte 的意思
// 维基百科:https://en.wikipedia.org/wiki/Time_To_First_Byte
times.ttfb = t.responseStart - t.navigationStart

//【重要】内容加载完成的时间
//【原因】页面内容经过 gzip 压缩了么,静态资源 css/js 等压缩了么?
times.request = t.responseEnd - t.requestStart

//【重要】执行 onload 回调函数的时间
//【原因】是否太多不必要的操作都放到 onload 回调函数里执行了,考虑过延迟加载、按需加载的策略么?
times.loadEvent = t.loadEventEnd - t.loadEventStart

// DNS 缓存时间
times.appcache = t.domainLookupStart - t.fetchStart

// 卸载页面的时间
times.unloadEvent = t.unloadEventEnd - t.unloadEventStart

// TCP 建立连接完成握手的时间
times.connect = t.connectEnd - t.connectStart

return times
}
```

> 6、性能优化
* DNS 查询 ============

1、控制域名数量,推荐是两个。

2、使用缓存 Last-Modified,If-Modified-Since,ETag,If-None-Match,Expires,Cache-Control。

3、使用 CDN,提高缓存命中率。

4、服务根据需要设置合理的 TTL。

5、DNS 的预解析。

* 建立连接=================

1、合并请求,减少请求次数。

2、持久连接 keep-alive,避免重新建立连接。

3、避免重定向。

* 发送请求 ===================

1、请求数据最小化。

2、避免重定向。

* 接收数据================

1、使用缓存 Last-Modified,If-Modified-Since,ETag,If-None-Match,Expires,Cache-Control

2、降低传输数据量,避免冗余的数据,适当的压缩后再进行传输。

3、传输过程中的压缩。

4、使用 CDN,缩短传输链路。

* 解析 DOM 树 ===================

1、简化 DOM 结构,嵌套不要太深。

2、css 放头部,js 放底部(?有争议~

3、延迟或者异步加载资源。

4、按照规范书写 html 文档,浏览器会有很多容错机制,但是这些会带来一些额外的消耗。所以没有必要的情况下,请严格按照 html 规范来。

5、同步加载的 js 除了会阻塞页面的解析,如果其中有修改 DOM 结构的相关代码,还会导致 DOM 重新解析,非常划不来。

* DOMContentLoaded 事件耗时

1、避免多余操作,延迟加载或者按需加载。

2、精简代码。

3、提高执行效率。



优化方向

优化手段

请求数量

合并脚本和样式表,CSS Sprites,拆分初始化负载,划分主域

请求带宽

开启GZip,精简JavaScript,移除重复脚本,图像优化

缓存利用

使用CDN,使用外部JavaScript和CSS,添加Expires头,减少DNS查找,配置ETag,使AjaX可缓存

页面结构

将样式表放在顶部,将脚本放在底部,尽早刷新文档的输出

代码校验

避免CSS表达式,避免重定向
196 changes: 196 additions & 0 deletions koa2/application.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
#### 关于 koa2 的源码学习总结

通过一个简单的例子来看看 koa2 的源码
**示例**

原生 http 请求方式

```js
const http = require('http')

const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('Hello World\n')
})

server.listen(3000)
```

koa2 使用方式

```js
const Koa = require('koa')
const app = new Koa()

app.use(ctx => {
ctx.body = 'Hello Koa'
})

app.listen(3000)
```

> 1、构造函数 `application.js`
这个文件里面主要是在做`new` 一个`koa`实例的初始化工作,以及收集中间件等操作。关键操作就是下面这几步。

```js
this.middleware = [] // 用于存储中间件的数组
this.context = Object.create(context) // 创建上下文
this.request = Object.create(request) // 创建request
this.response = Object.create(response) // 创建response
```

> 2、注册中间件
```js
use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-');
this.middleware.push(fn);
return this;
}
```

初始化构造函数完成后,如果我们有很多中间件的话,那么这个时候就是调用 use 来注册中间件了。上面就是注册中间件的方法。这个方法里面主要就是判断,传进来的是不是一个函数,如果是一个迭代器函数,那么会调用 convert 方法,将迭代器函数转化成为普通的函数,当然还会提示你不要用迭代器了。最后就是将传进来的函数存在构造函数里面声明的 middleware 数组中。

> 3、调用 listen 方法
```js
listen(...args) {
debug('listen');
const server = http.createServer(this.callback());
return server.listen(...args);
}
```

在构造函数里面初始化完成后,按照我们的使用方式就是注册中间件,然后在最后的时候调用 listen 方法。这个方法很简单,就是以向原生 http.createServer 传入一个函数的形式来创建自身的一个实例。listen 方法就做了这么简单的一个事。

> 4、在看 this.callback()
看到这里我们就明白我们实际上最关心的就是这个 this.callback 函数。其实这个也是 koa2 的核心所在。

```js
callback() {
const fn = compose(this.middleware);
// 这里是调用Emitter类里面的方法
if (!this.listeners('error').length) this.on('error', this.onerror);
// 包装函数,将ctx和中间件和并函数传给内部
const handleRequest = (req, res) => {
// 基于req和req封装出我们使用的ctx对象。
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};

return handleRequest;
}
```

this.callback 执行的结果是一个函数,这个函数的主要作用就是根据 req 获取请求信息,然后向 res 中写入返回内容。具体做法就是在一开始的时候合并中间件返回一个函数。然后基于 res 和 req 封装出我们平时使用的 ctx 对象。接着就是调用 koa 自己的 handleRequest 方法,将合并好的中间件函数和刚生成的 ctx 对象传入。

> 5、创建 ctx---createContext
前面已经说过了 ctx 这个对象就是基于 res 和 res 封装来的,接下来就看看是怎么封装的。

```js
createContext(req, res) {
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.response);
context.app = request.app = response.app = this;
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
context.originalUrl = request.originalUrl = req.url;
context.cookies = new Cookies(req, res, {
keys: this.keys,
secure: request.secure
});
request.ip = request.ips[0] || req.socket.remoteAddress || '';
context.accept = request.accept = accepts(req);
context.state = {};
return context;
}
```

上面的主要操作就是创建了三个对象 context,request,response

> 6、关于 handleRequest 函数
上面我们知道传进 handleRequest 方法的参数就是经过封装的 ctx 和合并后的中间件函数。并且将他们的原型指定为我们 app 中对应的对象,然后将原生的 req 和 res 赋值给相应的属性,就完成了。

```js
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err => ctx.onerror(err);
// response 辅助函数
const handleResponse = () => respond(ctx);
// onFinished 是确保一个流在关闭、完成和报错时都会执行相应的回调函数
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
```

这里做的主要是将封装的 ctx 传给合并后的中间件函数 fnMiddleware,中间件函数返回的是一个 promise。resolve 的话就调用 handleResponse,reject 的话就调用 onerror。handleResponse 里面主要做的操作就是通过 ctx 中的信息向 res 中写入信息。

> 7、respond 的分析
respond 方法就是一个辅助方法,主要作用就是根据 ctx 中的相关信息向 res 中写入信息。

```js
function respond(ctx) {
// allow bypassing koa
if (false === ctx.respond) return

const res = ctx.res
if (!ctx.writable) return

let body = ctx.body
const code = ctx.status

// ignore body
if (statuses.empty[code]) {
// strip headers
ctx.body = null
return res.end()
}

if ('HEAD' == ctx.method) {
if (!res.headersSent && isJSON(body)) {
ctx.length = Buffer.byteLength(JSON.stringify(body))
}
return res.end()
}

// status body
if (null == body) {
body = ctx.message || String(code)
if (!res.headersSent) {
ctx.type = 'text'
ctx.length = Buffer.byteLength(body)
}
return res.end(body)
}

// responses
if (Buffer.isBuffer(body)) return res.end(body)
if ('string' == typeof body) return res.end(body)
if (body instanceof Stream) return body.pipe(res)

// body: json
body = JSON.stringify(body)
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body)
}
res.end(body)
}
```
Loading

0 comments on commit 97f9982

Please sign in to comment.