Git Product home page Git Product logo

tech_blog's Introduction

#tech_blog

tech_blog's People

Stargazers

 avatar

Watchers

 avatar

tech_blog's Issues

使用reduce实现map

数组里面的很多方法都可以通过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源码解析

express原理分析

基于express 4.16.4版本


主要从以下几个方面进行详细讲解:
  • application
  • request
  • response
  • middleware
  • layer
  • route
  • router

application

统一监听入口,利用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的这些方法都做了那些事情。

request

// 源码
var req = Object.create(http.IncomingMessage.prototype)
...
module.exports = req

request 相对比较简单,express以http.IncomingMessage.protoType为原型,创建了一个新的req对象。并在其之上添加一些方法和一些获取头部信息的getter

  • 方法,比如:req.accepts(设置请求头Accept), req.acceptsEncodings, req.acceptsCharsets, req.acceptsLanguages, req.param(已废弃), req.range(解析头部Range), req.is(解析请求数据的Content-Type和给定参数类型进行对比)
  • getter,比如:req.hostname, req.path, req.xhr, req.host, req.ip, req.subdomains...

response

// 源码
var res = Object.create(http.ServerResponse.prototype)
...
module.exports = res

response 是以http模块ServerResponse.protoType为原型创建的res对象, 添加了很多常用的方法, 具体可以参考api文档, 比如:

  • res.send 返回数据到客户端,本质上是调用http模块的end方法,传递数据
  • res.get 获取头部信息
  • res.json 设置头部Content-Type: application/json 并stringify数据,调用send返回
  • res.jsonp 根据头部的jsonp callback name返回typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');
  • res.sendFile 返回文件

重点说下res.render这个方法, 咱们服务端渲染的利器。render调用了express自定义的render方法,express专门创建了一个view.js来处理render.
view提供了设置engine,查找并解析提供的目录下html文件, 最后通过fs.statSync将文件内容返回到客户端

middleware

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对象。保证每个请求进来都会先执行这两个中间件。

layer

router, route, layer结合实现了对路由的中间件处理体系,是整个express的核心部分,先说说layer

每一个中间件的设置都会创建一个layer实例,layer提供match方法,主要用来判断当前path是否与当前layer的path和keys是否匹配,如果匹配则会执行相应的中间件函数


layer实例主要有如下属性:
  • path匹配路径
  • handle该路径的处理函数
layer实例主要的方法:
  • handle_request 执行handle方法
  • handle_error 处理错误请求
  • match 匹配当前请求路径和当前layer实例路径path和keys是否一致

route

每一个路径创建一个route
route有dispatch,all等方法, route里面每一个中间件对应添加了一个layer实例,设置当前路径为根路径及其对应的的处理函数

route实例对应的属性
  • path 路径
  • stack 中间件栈
route实例对应的方法
  • dispatch 分发执行当前route下所有method对应的layer处理函数
  • post, get, put, all等注入中间件方法
// 创建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属性下

router

负责处理所有路由过来的请求,设置相应路径的处理中间件,每一个路径都会创建一个Route, 路径下的每一个中间件都会创建一个Layer实例推入当前Route.stack处理栈。当请求进来时命中相应路径时,通过route.dispatch方法遍历stack里面layers,并进行method匹配,匹配上之后就会执行。通过next逐个往后传递。


router里面有几个重要的方法

  • router.use
  • router.route
  • router.handle
  • router.get/post/put/delete/all等等衍生调用调用route的相应方法

至此,express的所有模块咱们就说完了。中间件和路由的**是express的核心,整个中间件的执行过程也j借鉴了node.js流式Steam处理的**,从前往后逐个处理修改参数或者返回结果。express的灵活之处在于,可以在请求的任意环节添加任意中间件进行我们想要的处理。后续我会继续写koa的源码解析。一步一步剖析现在主流node.js框架的实现。

ok实现一个promise

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渲染机制

DOM渲染机制

浏览器渲染引擎工作原理

  1. 解析DOM
  2. 解析CssOM
  3. 构建渲染树
  4. 排列
  5. 绘制

解析DOM

为了获得dom结构,浏览器会进行一下几个步骤

  1. 将字节转换为字符集
  2. 识别标签
  3. 将标签转换为节点
  4. 生成节点关系,构建DOM树

这整个过程会花费一些时间,尤其是如果html文件比较大,结构比较复杂。所以服务端返回的初始html的大小会影响到初始dom树构建的速度.

解析CSSOM

和HTML解析一样,css也会以同样的方式构建成CSSOM树

  1. 将字节转换为字符集
  2. 识别标签
  3. 将标签转换为样式节点
  4. 构建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 文档本身的传送大小。

关键渲染路径优化方式

关键资源的数量。 关键路径长度。 关键字节的数量。

  • 对关键路径进行分析和特性描述:资源数、字节数、长度。
  • 最大限度减少关键资源的数量:删除它们,延迟它们的下载,将它们标记为异步等。
  • 优化关键字节数以缩短下载时间(往返次数)。
  • 优化其余关键资源的加载顺序:您需要尽早下载所有关键资产,以缩短关键路径长度。

加速页面关键渲染路径

  1. 消除阻塞渲染的js和css
  2. 优化js的使用
    a. 尽量使用异步js资源
    b. 延迟解析js,延迟非必须脚本
    c. 避免运行时间长的js
  3. 优化css的使用
    a. 将css置于文档head标签内尽早加载
    b. 内联阻塞渲染的css

我这儿更多的是归纳总结,想了解得更加详细透彻建议仔细阅读下面几篇参考资料。

参考资源

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.