http 模块位于 /lib/http.js,我们直接看该模块的核心方法 createServer
function createServer(opts, requestListener) {
return new Server(opts, requestListener);
}
createServer 的作用是创建了一个 Server 类,该文件位于 /lib/_http_server.js
function Server(options, requestListener) {
if (!(this instanceof Server)) return new Server(options, requestListener);
if (typeof options === 'function') {
requestListener = options;
options = {};
} else if (options == null || typeof options === 'object') {
options = { ...options };
} else {
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
}
// options 允许传两个参数,分别是 IncomingMessage 和 ServerResponse
// IncomingMessage 是客户端请求的信息,ServerResponse 是服务器响应的数据
// IncomingMessage 和 ServerResponse 允许继承后进行一些自定义的操作
// IncomingMessage 主要负责将 HTTP 报文序列化,将报文头报文内容取出序列化
// ServerResponse 提供了一些快速响应客户端的接口,设置响应报文头报文内容的接口
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
this[kServerResponse] = options.ServerResponse || ServerResponse;
// Server 类仍然是继承于 net.Server 类
net.Server.call(this, { allowHalfOpen: true });
if (requestListener) {
// 触发 request 请求事件后,调用 requestListener 函数
this.on('request', requestListener);
}
// Similar option to this. Too lazy to write my own docs.
// http://www.squid-cache.org/Doc/config/half_closed_clients/
// http://wiki.squid-cache.org/SquidFaq/InnerWorkings#What_is_a_half-closed_filedescriptor.3F
this.httpAllowHalfOpen = false;
this.on('connection', connectionListener);
// 超时时间,默认禁用
this.timeout = 0;
// 对连接超出一定时间不活跃时,断开 TCP 连接,重新建立连接要重新建立三次握手
this.keepAliveTimeout = 5000;
// 最大响应头数,默认不限制
this.maxHeadersCount = null;
this.headersTimeout = 40 * 1000; // 40 seconds
}
我们需要进一步向下剖析,查看一下 net.Server 类是怎么样的,该文件位于 /lib/net.js
function Server(options, connectionListener) {
if (!(this instanceof Server))
return new Server(options, connectionListener);
// 继承于 EventEmitter 类,这个类属于事件类
EventEmitter.call(this);
if (typeof options === 'function') {
connectionListener = options;
options = {};
this.on('connection', connectionListener);
} else if (options == null || typeof options === 'object') {
options = { ...options };
if (typeof connectionListener === 'function') {
this.on('connection', connectionListener);
}
} else {
throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
}
this._connections = 0;
Object.defineProperty(this, 'connections', {
get: deprecate(() => {
if (this._usingWorkers) {
return null;
}
return this._connections;
}, 'Server.connections property is deprecated. ' +
'Use Server.getConnections method instead.', 'DEP0020'),
set: deprecate((val) => (this._connections = val),
'Server.connections property is deprecated.',
'DEP0020'),
configurable: true, enumerable: false
});
this[async_id_symbol] = -1;
this._handle = null;
this._usingWorkers = false;
this._workers = [];
this._unref = false;
this.allowHalfOpen = options.allowHalfOpen || false;
this.pauseonConnect = !!options.pauseOnConnect;
}
Object.setPrototypeOf(Server.prototype, EventEmitter.prototype);
Object.setPrototypeOf(Server, EventEmitter);
从代码可以看出,net.Server 类本身是一个事件分发中心,具体的实现是由其多个内部方法所组成,我们先看一个我们最常用的方法 listen
Server.prototype.listen = function(...args) {
const normalized = normalizeArgs(args);
// 序列化以后 options 里面包含了 port 和 host 字段(如果填写了的话)
var options = normalized[0];
const cb = normalized[1];
if (this._handle) {
throw new ERR_SERVER_ALREADY_LISTEN();
}
if (cb !== null) {
// 给回调函数绑定了一次性事件,当 listening 事件触发时调用回调函数
this.once('listening', cb);
}
// ...
var backlog;
if (typeof options.port === 'number' || typeof options.port === 'string') {
if (!isLegalPort(options.port)) {
throw new ERR_SOCKET_BAD_PORT(options.port);
}
backlog = options.backlog || backlogFromArgs;
// start TCP server listening on host:port
if (options.host) {
// 我们假设 Host 为 localhost, port 为 3000,那么调用的就是 lookupAndListen 方法
lookupAndListen(this, options.port | 0, options.host, backlog,
options.exclusive, flags);
} else { // Undefined host, listens on unspecified address
// Default addressType 4 will be used to search for master server
listenInCluster(this, null, options.port | 0, 4,
backlog, undefined, options.exclusive);
}
return this;
}
// ...
};
function lookupAndListen(self, port, address, backlog, exclusive, flags) {
if (dns === undefined) dns = require('dns');
// 期间使用 dns 模块对 host 进行解析,目的是为了得到一个 ip 地址
dns.lookup(address, function doListen(err, ip, addressType) {
if (err) {
self.emit('error', err);
} else {
addressType = ip ? addressType : 4;
// 拿到 ip 地址后执行了 listenInCluster
listenInCluster(self, ip, port, addressType,
backlog, undefined, exclusive, flags);
}
});
}
function listenInCluster(server, address, port, addressType,
backlog, fd, exclusive, flags) {
exclusive = !!exclusive;
// cluster 是 Node 中的集群模块,可以创建共享服务器端口的子进程
// 在 listen 中的作用是启用一个进程监听 http 请求
if (cluster === undefined) cluster = require('cluster');
if (cluster.isMaster || exclusive) {
// Will create a new handle
// _listen2 sets up the listened handle, it is still named like this
// to avoid breaking code that wraps this method
// 这个方法是最终执行的方法
server._listen2(address, port, addressType, backlog, fd, flags);
return;
}
const serverQuery = {
address: address,
port: port,
addressType: addressType,
fd: fd,
flags,
};
// Get the master's server handle, and listen on it
cluster._getServer(server, serverQuery, listenOnMasterHandle);
function listenOnMasterHandle(err, handle) {
err = checkBindError(err, port, handle);
if (err) {
var ex = exceptionWithHostPort(err, 'bind', address, port);
return server.emit('error', ex);
}
// Reuse master's server handle
server._handle = handle;
// _listen2 sets up the listened handle, it is still named like this
// to avoid breaking code that wraps this method
server._listen2(address, port, addressType, backlog, fd, flags);
}
}
// server._listen2 指向的是一个 setupListenHandle 方法,setupListenHandle 最终指向的是 createServerHandle 方法
// Returns handle if it can be created, or error code if it can't
function createServerHandle(address, port, addressType, fd, flags) {
var err = 0;
// Assign handle in listen, and clean up if bind or listen fails
var handle;
var isTCP = false;
if (typeof fd === 'number' && fd >= 0) {
try {
handle = createHandle(fd, true);
} catch (e) {
// Not a fd we can listen on. This will trigger an error.
debug('listen invalid fd=%d:', fd, e.message);
return UV_EINVAL;
}
err = handle.open(fd);
if (err)
return err;
assert(!address && !port);
} else if (port === -1 && addressType === -1) {
handle = new Pipe(PipeConstants.SERVER);
if (process.platform === 'win32') {
var instances = parseInt(process.env.NODE_PENDING_PIPE_INSTANCES);
if (!Number.isNaN(instances)) {
handle.setPendingInstances(instances);
}
}
} else {
// 最终启动了一个 TCP 服务,当 TCP 端口收到数据时将会推送给 HTTP 进程
// 具体实现可能是 TCP 内部发出了 emit 进行事件通知,同时将字节流转换为 HTTP 协议所能接受的报文格式(协议规范)
handle = new TCP(TCPConstants.SERVER);
isTCP = true;
}
if (address || port || isTCP) {
debug('bind to', address || 'any');
if (!address) {
// Try binding to ipv6 first
err = handle.bind6(DEFAULT_IPV6_ADDR, port, flags);
if (err) {
handle.close();
// Fallback to ipv4
return createServerHandle(DEFAULT_IPV4_ADDR, port);
}
} else if (addressType === 6) {
err = handle.bind6(address, port, flags);
} else {
// 这一步进行主机和端口的绑定
err = handle.bind(address, port);
}
}
return handle;
}
从上面我们可以看出,http.createServer 的调用过程如下:
在日常 Coding 中,码农们肯定少不了对数组的操作,其中很常用的一个操作就是对数组进行遍历,查看数组中的元素,然后一顿操作猛如虎。今天暂且简单地说说在 JavaScript 中 forEach。
克隆项目代码到本地(git应该都要会哈,现在源码几乎都会放github上,会git才方便,不会的可以自学一下哦,不会的也没关系,gitHub上也提供直接下载的链接);打开微信开发者工具;
随着这些模块逐渐完善, Nodejs 在服务端的使用场景也越来越丰富,如果你仅仅是因为JS 这个后缀而注意到它的话, 那么我希望你能暂停脚步,好好了解一下这门年轻的语言,相信它会给你带来惊喜
在 Vue 内部,有一段这样的代码:上面5个函数的作用是在Vue的原型上面挂载方法。initMixin 函数;可以看到在 initMixin 方法中,实现了一系列的初始化操作,包括生命周期流程以及响应式系统流程的启动
nextTick的使用:vue中dom的更像并不是实时的,当数据改变后,vue会把渲染watcher添加到异步队列,异步执行,同步代码执行完成后再统一修改dom,我们看下面的代码。
React更新的方式有三种:(1)ReactDOM.render() || hydrate(ReactDOMServer渲染)(2)setState(3)forceUpdate;接下来,我们就来看下ReactDOM.render()源码
在React中,为防止某个update因为优先级的原因一直被打断而未能执行。React会设置一个ExpirationTime,当时间到了ExpirationTime的时候,如果某个update还未执行的话,React将会强制执行该update,这就是ExpirationTime的作用。
算法对于前端工程师来说总有一层神秘色彩,这篇文章通过解读V8源码,带你探索 Array.prototype.sort 函数下的算法实现。来,先把你用过的和听说过的排序算法都列出来:
extend是jQuery中一个比较核心的代码,如果有查看jQuery的源码的话,就会发现jQuery在多处调用了extend方法。作用:对任意对象进行扩;’扩展某个实例对象
state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!