#tech_blog
whu12yz / tech_blog Goto Github PK
View Code? Open in Web Editor NEW一些技术总结和源码解读
一些技术总结和源码解读
#tech_blog
数组里面的很多方法都可以通过reduce来实现,下面使用reduce实现一下map
Array.prototype.map = function(fn, thisArg) {
return this.reduce((result, cur, index, array) => {
thisArg && (fn = fn.bind(thisArg));
result.push(fn(cur, index, array));
return result;
}, []);
}
基于express 4.16.4版本
统一监听入口,利用http模块createServer注入this
, 实现对所有请求的拦截处理
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
模块提供app.set, app.use, app.route, app.enable, app.disable, app.render, app.listen, app.engine等方法
其中app.use, app.get, app.post, app.route等方法实际上就是调用route的相应方法,实现相应路径处理中间件的注册。后面会说到route的这些方法都做了那些事情。
// 源码
var req = Object.create(http.IncomingMessage.prototype)
...
module.exports = req
request 相对比较简单,express以http.IncomingMessage.protoType为原型,创建了一个新的req对象。并在其之上添加一些方法和一些获取头部信息的getter
// 源码
var res = Object.create(http.ServerResponse.prototype)
...
module.exports = res
response 是以http模块ServerResponse.protoType为原型创建的res对象, 添加了很多常用的方法, 具体可以参考api文档, 比如:
typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');
重点说下res.render这个方法, 咱们服务端渲染的利器。render调用了express自定义的render方法,express专门创建了一个view.js来处理render.
view提供了设置engine,查找并解析提供的目录下html文件, 最后通过fs.statSync将文件内容返回到客户端
middleware!
middleware是整个express处理的核心**,整个express router机制都是基于中间件**去处理。
app.lazyrouter = function lazyrouter() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
this._router.use(query(this.get('query parser fn')));
this._router.use(middleware.init(this));
}
};
路由的初始化都会先初始化query,init作为初始执行中间件,其中init添加locals至res对象。query添加query到res对象。保证每个请求进来都会先执行这两个中间件。
router, route, layer结合实现了对路由的中间件处理体系,是整个express的核心部分,先说说layer
每一个中间件的设置都会创建一个layer实例,layer提供match方法,主要用来判断当前path是否与当前layer的path和keys是否匹配,如果匹配则会执行相应的中间件函数
每一个路径创建一个route
route有dispatch,all等方法, route里面每一个中间件对应添加了一个layer实例,设置当前路径为根路径及其对应的的处理函数
// 创建get,post,put, delete等方法
var methods = require('methods');
methods.forEach(function(method){
Route.prototype[method] = function(){
var handles = flatten(slice.call(arguments));
for (var i = 0; i < handles.length; i++) {
var handle = handles[i];
if (typeof handle !== 'function') {
var type = toString.call(handle);
var msg = 'Route.' + method + '() requires a callback function but got a ' + type
throw new Error(msg);
}
debug('%s %o', method, this.path)
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
}
return this;
};
});
每一个方法的调用会创建一个layer挂在当前route的stack属性下
负责处理所有路由过来的请求,设置相应路径的处理中间件,每一个路径都会创建一个Route, 路径下的每一个中间件都会创建一个Layer实例推入当前Route.stack处理栈。当请求进来时命中相应路径时,通过route.dispatch方法遍历stack里面layers,并进行method匹配,匹配上之后就会执行。通过next逐个往后传递。
router里面有几个重要的方法
至此,express的所有模块咱们就说完了。中间件和路由的**是express的核心,整个中间件的执行过程也j借鉴了node.js流式Steam处理的**,从前往后逐个处理修改参数或者返回结果。express的灵活之处在于,可以在请求的任意环节添加任意中间件进行我们想要的处理。后续我会继续写koa的源码解析。一步一步剖析现在主流node.js框架的实现。
function isFunc(func) {
return Object.prototype.toString.call(func) === '[object Function]';
}
function pureFunc(value) {
return value;
}
function pureErrorFunc(err) {
throw err;
}
function isNode() {
return typeof process === 'object' && Object.prototype.toString.call(process) === '[object process]';
}
function schedule(callback) {
if (!isFunc(callback)) return;
// 确保task 异步在microtask中执行
if(isNode()) {
return process.nextTick(callback);
} else if(typeof MutationObserver !== 'undefined') {
const div = document.createElement('div');
const observer = new MutationObserver(() => {
callback();
observer.disconnect();
})
observer.observe(div, {attributes: true});
div.classList.toggle('trigger')
} else if (typeof setImmediate !== 'undefined') {
setImmediate(calback);
} else if (typeof setTimeout !== 'undefined') {
setTimeout(callback);
}
}
function resolvePromise(onResolved, value, resolve, reject) {
schedule(() => {
try{
const result = onResolved(value);
if (result instanceof Promise) {
result.then(resolve, reject);
} else {
resolve(result);
}
}catch(err) {
reject(err);
}
})
}
function rejectPromise(onRejected, value, resolve, reject) {
schedule(() => {
try{
const result = onRejected(value);
if (result instanceof Promise) {
result.then(resolve, reject);
} else {
resolve(result);
}
}catch(err) {
reject(err);
}
})
}
class Promise {
constructor(excutor) {
if (!isFunc(excutor)) {
throw Error('new Promise param must be a function');
}
this.status = 'PENDING';
this.data = ''
this.resolveCallbackList = []; //promise结束之前可能会有多可回调添加上去
this.rejectCallbackList = []; //同理
const resolve = (value) => {
if (this.status === 'PENDING') {
this.status = 'RESOLVED';
this.data = value;
this.resolveCallbackList.forEach((onResolve) => {
schedule(() => {
onResolve(this.data);
})
})
}
}
const reject = (reason) => {
if (this.status === 'PENDING') {
this.status = 'REJECTED';
this.data = reason;
this.rejectCallbackList.forEach((onReject) => {
schedule(() => {
onReject(this.data);
})
})
}
}
try{
excutor(resolve, reject);
}catch(err) {
reject(err);
}
}
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value);
});
}
static reject(err) {
return new Promise((resolve, reject) => {
reject(err);
});
}
then (onResolved, onRejected) {
onResolved = isFunc(onResolved) ? onResolved : pureFunc;
onRejected = isFunc(onRejected) ? onRejected : pureErrorFunc;
if (this.status === 'PENDING') {
return new Promise((resolve, reject) => {
this.resolveCallbackList.push((value) => {
resolvePromise(onResolved, value, resolve, reject);
});
this.rejectCallbackList.push((value) => {
rejectPromise(onRejected, value, resolve, reject);
})
})
}
if ( this.status === 'RESOLVED') {
return new Promise((resolve, reject) => {
resolvePromise(onResolved, this.data, resolve, reject);
})
}
if ( this.status === 'REJECTED') {
return new Promise((resolve, reject) => {
rejectPromise(onRejected, this.data, resolve, reject);
})
}
}
catch(onRejected) {
return this.then(null, onRejected);
}
}
module.exports = Promise;
为了获得dom结构,浏览器会进行一下几个步骤
这整个过程会花费一些时间,尤其是如果html文件比较大,结构比较复杂。所以服务端返回的初始html的大小会影响到初始dom树构建的速度.
和HTML解析一样,css也会以同样的方式构建成CSSOM树
在这个过程中,CSS解析器遍历所有节点获取所有的属性,构建cssom树。css是关键渲染路径上最为重要的元素之一,因为浏览器染直到cssom构建完成才会开始渲染。
如果在任何时候我们必须等待JavaScript执行,那么我们将首先等待CSSOM构造完成。换句话说,JavaScript和CSS之间存在着强烈的依赖关系。这也是为什么我们通常将样式放在顶部,将脚本放在底部
将DOM树和CSSOM树结合生成渲染树,渲染的过程是渐进的过程,为了更好的用户体验,渲染引擎从CSSOM构建完成,开始构建渲染树的时候会同步开始渲染渲染树以最快的速度把已知位置等属性的节点尽快渲染出来。
浏览器的解析渲染过程是一个流式管道处理过程,类似unix的管道处理过程、nodejs的stream流式处理。
这个点很重要,也是为什么我们可以通过服务端渲染来减少白屏时间,加速首屏渲染,提升用户体验。
在服务端渲染中,我们通常会将用户可见的首屏部分在服务端提前解析好相应的html结构,这样到达客户端时,只要解析完css,就会直接渲染出来已有的html结构。而无需像常规的前端渲染一样,等待script脚本加载并解析完成生成新的dom结构再进行渲染。
这个过程浏览器会计算每一个可见元素的尺寸和位置,每一次渲染树的更新或者视口的尺寸变化,浏览器都会进行一次重排。
绘制阶段和排列阶段几乎是并行的,就像前面说的管道式地进行,这个过程中浏览器会拿到前面的排列结果,按像素进行绘制。每种类型的样式绘制的时间是不等的,样式组合的绘制时间也不是简单的1+1=2。有些样式的组合会加倍渲染时间。
关键资源: 可能阻止网页首次渲染的资源。
关键路径长度: 获取所有关键资源所需的往返次数或总时间。
关键字节: 实现网页首次渲染所需的总字节数,它是所有关键资源传送文件大小的总和。我们包含单个 HTML 页面的第一个示例包含一项关键资源(HTML 文档);关键路径长度也与 1 次网络往返相等(假设文件较小),而总关键字节数正好是 HTML 文档本身的传送大小。
关键资源的数量。 关键路径长度。 关键字节的数量。
我这儿更多的是归纳总结,想了解得更加详细透彻建议仔细阅读下面几篇参考资料。
参考资源
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.