追风

我的前端之路


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

express中间件原理浅析

发表于 2020-02-29 | 分类于 Node

再学习express的时候,对其中间件的实现原理狠感兴趣。自己私下模拟了一个简易demo,分享给大家。下面直接上代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
const http = require('http');
const path = require('path');

class MiniExpress {
constructor() {
// 收集app 的中间件 路由执行 函数
this.routes = {
all: [], // 所有的匹配路径的请求 都执行的 中间件函数
get: [], // get 请求 匹配的 中间件执行函数
post: [] // post 请求
// put delete 可自行扩展
}
// 初始中间件
this.use(function (req, res, next) {
// 实现 json 方法
res.json = function (obj = {}) {
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify(obj))
}
next()
})
}
// 第一个参数可能是路径 也可能是函数,并且 支持 多个函数操作
use(...args) {
this.handleLayer('all', ...args)
}

handleLayer(method = 'all', ...args) {
let layer;
if (typeof args[0] == 'string') {
layer = {
path: args.shift(),
stack: args,
}
} else {
layer = {
path: '/',
stack: args
}
}
this.routes[method].push(layer)
}

get(...args) {
this.handleLayer('get', ...args)
}

post() {
this.handleLayer('post', ...args)
}

// 请求方式和路径 匹配 合适 的中间件
match(method, url) {
let midderwares = [];
// 处理浏览器 网页 图标
if (url == '/favicon.ico') {
return midderwares
}

// 处理 对所有请求方式 都生效的中间件
this.routes.all.filter(m => url.indexOf(m.path) == 0).forEach(m => {
midderwares = midderwares.concat(m.stack)
});

// 处理对应请求方式
const res = this.routes[method].filter(m => url.indexOf(m.path) == 0).forEach(m => {
midderwares = midderwares.concat(m.stack)
});
return midderwares;
}

// 处理请求的方法
hanldRequest(req, res) {
// console.log(req)
let method = req.method.toLocaleLowerCase(),
url = req.url;
const stack = this.match(method, url);
// 依次执行 中间件 数组

function next() {
const curMidware = stack.shift();
// 如果是 处理函数
if (curMidware) {
// 传入正常中间件的3个参数
curMidware(req, res, next);
}
}
next()
}

listen(...res) {
const server = http.createServer(this.hanldRequest.bind(this));
server.listen(...res);
}
}

module.exports = MiniExpress;

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const MiniExpress = require('./mini_express');
const app = new MiniExpress();

app.use(function (req, res, next) {
console.log('11111')
next()
})

app.use('/api/list', function (req, res, next) {
console.log('22222222222222')
next()
})

app.get('/api/list', function check(req, res, next) {
console.log('user is pass checked');
next()
}, function (req, res, next) {
res.json({
code: 200,
list: [
{ id: 1, text: 'ddd' },
{ id: 2, text: 'eee' }
]
})
})

app.listen(8000, () => {
console.log('server is listening at port 8000');
})

/*
测试 结果
server is listening at port 8000
11111
22222222222222
user is pass checked

*/

要实现express的中间件这样的效果,我们就需要记录所有的中间处理函数及其对应的路径和请求方式。然后,对每一次请求都匹配对应处理函数,最后就会得到每次请求的 中间件函数队列,然后利用指针来依次执行所有的中间件处理函数。这种思想也可用于异步和动画队列等问题的应用中。

vue路由动态配置

发表于 2020-02-10 | 分类于 Vue

在后台系统这类的项目中,用户角色和权限的区分是不可分割的一部分需求。常见的用户权限区分形式有两种,一是前端请求接口拿到后台配置的用户权限信息;另一种是直接由前端来定义每种用户角色的权限页面,然后在生成对应的路由。今天我们来了解前端配置路由权限的方式。

角色权限页面配置

在配置角色对应的路由页面时,可以参考项目所用的前端框架导航组件的参数,我们以elment ui的menu组件为例:可对审核方(Auditor)、供应商(Supplier)和需求方(Business)做如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  const appMenuitem = {
// 审核方下拉选项(路由)菜单
Auditor: [
{
// icon: "el-icon-view", // operationLog userConfiguration
title: "基本配置",
index: "1",
subs: [
{
index: "dataOverview",
title: "数据概览"
}
]
},
{
// icon: "el-icon-view",
title: "需求审核",
index: "2",
subs: [
{
index: "demendManage",
title: "需求管理"
}
]
}
],
// 业务方下拉选项和路由
Business: [
{
title: "基本配置",
index: "1",
subs: [
{
index: "demandList",
title: "需求列表"
}
]
}
],
// 供应商
Supplier: [
{
title: "需求制作",
index: "1",
subs: [
{
index: "needList",
title: "需求列表"
}
]
}
]
};
阅读全文 »

Koa原理浅析

发表于 2020-01-11 | 分类于 Node

koa是现在我们最常用的node框架,它是一个轻量的web框架,只提供了http的协议的解析和中间件功能。我们要实现路由、静态页面托管和文件上传等功能均需要插件来实现。

koa源码结构


上图是koa的源码结构,lib放着koa的核心文件::application.js、context.js、request.js、response.js。

application.js

application.js是koa的入口文件,它向外到处了Koa类,即函数。Koa继承了node的事件模块event,因此,我们new Koa()的实例app,可以基于事件来实现观察订阅的功能。Koa还有内置了常用的几个函数:listen、use、createContext、toJSON。

listen方法是通过http.createServer开启并监听了http服务,并且它里面还进行了中间件的合并、上下文context的初始化,并且每次请求来的中件合并、context都会重新初始化。

context.js

这部分是对中间件上下对象ctx封装和暴露,里面的重点在delegate,这个就是代理,比如我们要访问ctx.repsponse.status但是我们通过delegate,可以直接访问ctx.status访问到它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 暴露出来的对象
const proto = module.exports = {
toJSON() {
return {
// this.request 是通过application.js 中的createContext 方法将 reques和response对象挂载
request: this.request.toJSON(),
response: this.response.toJSON(),
app: this.app.toJSON(),
originalUrl: this.originalUrl,
req: '<original node req>',
res: '<original node res>',
socket: '<original node socket>'
};
},
get cookies() {
// ....
},

set cookies(_cookies) {
// ....
}
};
// 代理 ctx.reponse 和ctx.request
delegate(proto, 'response')
.method('attachment')
.method('redirect')
// ...

delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
// ...
阅读全文 »

vant pupop弹框锁住背景

发表于 2019-12-20 | 分类于 前端三剑客

我们在做移动端项目开发时,经常会遇到弹框不能锁定背景的背景的问题。比如有一个站视规则并且可以滚动的弹框,这是我们滑动滚动条浏览更多内容的时候,页面会跟随着一起滑动,这种体验不太友好。那么如何解决呢?

html、body设置hidden

我们可以设置html和body的overflow为hidden,

1
2
3
4
.lock{
height: 100%;
overflow: hidden;
}

当弹框展现时,我们可通过js为html和body加上类名,弹框隐藏时,移除类名lock。一些ui框架的弹框也是采用这种方法,如vant的popup弹出层组件。但是这种方法并不能在所有的移动设置生效。

阻止touchmove默认事件

为 body 绑定 touchmove 事件,然后调用 preventDefault() 方法,禁止 touchmove 的默认行为。

1
2
3
4
5
6
7
8
9
function preventDefaultFn(event){
event.preventDefault();
}

// 弹出时
$('body').on('touchmove', preventDefaultFn);

// 隐藏时
$('body').off('touchmove', preventDefaultFn);

如果是vue的项目,有一个更简洁的方式,在弹框的遮罩层加上`@touchmove.prevent`即可

1
<div class="mask" @touchmove.prevent>

但这种方法有一定的局限性,它适合于弹框内的元素不发生滚动,如果弹框的内容需要滚动,那么这种方法就不行了,滚动的内容无法继续滚动了,当然我们可以通过js模拟滚动,不过这有些麻烦。还好我们有第三种方案。

阅读全文 »

js文件处理

发表于 2019-12-08 | 分类于 前端三剑客

下载文件

在后台的管理系统中,数据统计是必不可少的系统功能模块。因此,导出统计数据,下载文件是常见的需求。今天我们来看看实现常用的文件下载的几种方式。

window.open打开新页面下载文件

1
window.open(`url`, '')

这种方式,后台提供文件下载路径,我们调用window.open方法就可以实现文件下载了,简单方便,但是当参数错误时,或其它原因导致接口请求失败,这时无法监听到接口返回的错误信息,需要保证请求必须是正确的且能正确返回数据流,不然打开页面会直接输出接口返回的错误信息,体验不好。

a标签打开新页面下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function exportFile(url,filename) {
var link = document.createElement('a');
var body = document.querySelector('body');
link.href = url;
// 自定义文件名字
link.download = filename;
// 兼容火狐
link.style.display = 'none'
body.appendChild(link);

link.click();
// 移除a标签
body.removeChild(link);
}

a标签下载的方式和window.open一样,非常简单,还可以自定义下载的文件名,但是同样无法监听错误信息。而且,这两种方式在下载.mp3格式或者浏览器支持的音视频文件时,会直接播放,而不是下载这个文件。

阅读全文 »
123…15
陈虎

陈虎

记录学习,记录生活,记录点滴

74 日志
12 分类
27 标签
RSS
© 2020 陈虎
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.4