要学习熟悉一个框架,我认为最重要的是要学到其中的”道”, 而不是”术”的层面。那么要学习express,有那么多的api,有那么多行源码,源码中又不断引用其他三方的npm组件,很容易就一叶障目,用时很长,但收效甚微。 为什么这么说呢?因为我就是在这个坑中爬了挺长时间才上来,不断摸清了作者的架构思想。 所以,我在这一篇,通过用100行左右的代码,实现一个最简单,但也可用的微框架。
一、原理
在使用express时, 我们主要在处理两类请求,一是对静态资源的请求,而是对非静态资源的请求。
1.1 对静态资源的请求
在express的开头,我们一般都会使用下面代码:
const app = express();
app.use(express.static('public'));
复制代码
我想对express有些了解的同学,都知道这是对静态资源请求进行初始化处理。当后面请求静态资源(css,js,image…)时,会进入处理处理,并把静态资源返回给客户端。
1.2 对非静态资源的请求
在express中,我们会频繁的使用,比如:
// 比如
app.use((req, res, next) => {
// ....
next();
});
// 再比如
app.get('/', (req, res) => {
ejs.renderFile('./views/info.ejs', {
title: 'info页面',
description: 'info描述描述。。。'
},
(err, data) => {
if (err) {
return;
}
res.end(data);
});
});
复制代码
好了,最重要的两个点做了提示,那么就开始分析我的微代码吧~~
二、代码
// 入口文件
const http = require('http');
const app = require('./route');
const ejs = require('ejs');
app.static('public');
app.get('/login', (req, res) => {
// 渲染页面
console.log('进行逻辑处理');
res.end('login success');
});
http.createServer(app).listen(3000);
console.log('http server started on 3000');
复制代码
下面是route.js, 逻辑就在这里面
const url = require('url');
const fs = require('fs');
const mime = require('mime');
const path = require('path');
let G = {
_get: {},
_post: {},
staticPath: 'static'
};
const initStatic = async (req, res, staticPath) => {
let pathname = url.parse(req.url).pathname;
pathname = pathname === '/' ? '/index.html' : pathname;
try {
let data = fs.readFileSync('./' + staticPath + pathname);
if (data) {
let mimes = mime.getType(pathname);
res.writeHead(200, {'Content-Type': '' + mimes + ';charset="utf-8'});
res.end(data);
}
} catch (error) {
}
}
const app = async (req, res) => {
// 当请求来的时候,先去找静态服务下有没有(匹配静态资源)
await initStatic(req, res, G.staticPath);
let pathname = url.parse(req.url).pathname;
let method = req.method.toLowerCase();
// 先只考虑get和post情况
if (G['_' + method][pathname]) {
if (method === 'get') {
G['_' + method][pathname](req, res);
} else {
let postData = '';
req.on('data', chunk => {
postData += chunk;
});
req.on('end', () => {
req.body = postData;
G['_' + method][pathname](req, res);
});
}
} else {
res.writeHead(404, {'Content-Type': 'text/plain;charset="utf-8"'});
res.end('页面不存在');
}
}
app.get = (str, cb) => {
G._get[str] = cb;
}
app.post = (str, cb) => {
G._post[str] = cb;
}
// 配置静态web服务目录
app.static = staticPath => {
G.staticPath = staticPath;
}
module.exports = app;
复制代码
2.1 代码分析
从上面的代码中,可以看到,开始时创建了一个G的变量,这个变量存储了get、post请求以及默认的static目录地址。
当我们使用app.get()或者app.post()进行注册时,会把回调函数push进 _get和_post中。
所以最终的G应该是这样的:
// 伪代码
G = {
_post: [{‘/login’: fn3}, {'/postdata': fn4}],
_get: [{'/': fn1}, {'getname': fn2}],
staticPath: 'static'
}
复制代码
当请求来的时候,那么我们可以从req.method中获取到时get还是post, 那么就取出来执行。
虽然很简单,但是可以很比较准确的把原理体现出来。其中有非常非常多的边界情况没有考虑,但是,这显然不需要在这个开篇中进行分析。 大家能够理解上面的原理,那么看express源码的时候,就会顺手很多。
我不打算在后面的文章中逐句分析express源码,我会通过重写一个express框架,来带给大家精彩内容。 在讲的时候,会抓住主干,舍弃很多边际情况,力争做到代码简洁,逻辑突出。 喜欢就继续查看吧~~ 喜欢的话辛苦点赞~~