server-similation-toolkit

0.0.7 • Public • Published

每个项目,都会为其编写一个前端模拟服务器端请求的工具,不觉间,已经写了几套类似的工具。

回首一下,实际上就几个板块:

  1. 文件监听
  2. 服务器模拟
  3. 自动重载
  4. 静态资源转发
  5. 动态路由

于是有了此项目,把上传的 上述 板块,各自工具化。

Server

此部分,继承 express,然后拓展出几个常用方法:

const path = require('path');
const Server = require('server-similation-toolkit').Server;
 
// 建立服务的快捷方式,封装了以下几个参数
// 在 process.nextTick 后,将自动调用 listen 方法
const app = Server.create({
  port: 3015,
  // 启动 livereload 功能
  livereload: true,
  // 静态资源访问设置
  static: {
    '/static': path.join(__dirname, '../static')
  }
});

app 实例中,除了 express 现有的 usegetpost 等方法外,还拓展了以下几个方法:

// 配置,并且启用 livereload 功能
app.livereload(true || { type: '"polling" or "eventsource"', url: .., api: .., timeout: .. });
 
// 切换 livereload 的可用状态,仅在 livereload 启动时,生效
app.enableLivereload(true || false);
 
// 刷新所有打开的页面,仅在 livereload 启动时,生效
app.reloadPage();
 
// 配置静态资源访问路径
app.setStatic('/static', 'E://test/static');

Server 类上,除了 Express 可用的方法外,还额外添加了几个方法:

// new Server() 的快捷方式,封装了 listen, livereload, setStatic 等方法
// 同时,添加了 morgan/bodyparser/cookieparser 等中间件
Server.create({ port: xx, livereload: true, static: {} });
 
// 在默认浏览器中,打开某个链接
Server.openBrowser('链接', Function:回调);
 
// 获取当前电脑的 Ip 列表
Server.getIpList();
 
// 一些常用中间件的访问快捷方式
Server.Morgan = require('morgan');
Server.BodyParser = require('body-parser');
Server.CookieParser = require('cookie-parser');

new Server() 中,强制使用了 InjectMiddleware 中间件,给所有 res 对象,添加了 res.inject([]) 方法,具体见 InjectMiddleware 的介绍。

自动重载

PollingReloader

轮询形式的自动刷新,有以下几个方法:

const PollingReloader = require('server-similation-toolkit').PollingReloader;
const reloader = new PollingReloader({ timeout: 3000 });
 
reloader.connect(app);  // 传入 Server 或 Express 的实例
reloader.reload();      // 刷新页面

须配合 InjectMiddleware 使用,同时,需要把 responseContent-Type 设置为 text/html,才会对 html 的内容,进行注入

EventsourceReloader

参考上述 PollingReloader 的使用,客户端层使用 EventSource,进行数据接收。

middleware

InjectMiddleware

又名 注入 中间件,用于给特定内容,注入额外信息,使用如下:

app.use((req, res) => {
  if (true /*满足某个条件*/) {
    // 翻译如下: 如果 content-type 满足 /^text\/html/i,则在 /<head>/ 后面,插入 <script>...</script>
    res.inject([
      function isHit(content, req, res) { return true; },        // 检查当前请求,是否满足要求
      function handle(content, req, res) { return content; }     // 如果满足要求,应该怎么加工内容
    ]);
  }
});

StaticResourcer

同一个访问目录的资源,可能放置在不同的目录,甚至,并不存在于本地的目录,我们迫切需要一个快捷工具、或方法,来满足我们这类的需求:

// 考虑一下,如果我们调用一个方法,就能到本地的几个目录、线上服务器上,去寻找资源,是不是超赞呢?
app.setStatic('/static', [
  'E://test/static',
  'D://test/static',
  'http://res1.test.com',
  'http://res2.test.com'
]);

此工具类,应运而生:

// static-resourcer.js
const StaticResourcer = require('server-similation-toolkit').StaticResourcer;
const staticResourcer = new StaticResourcer(app:Express Instance);
 
staticResourcer.setStatic('/static', [...]);

server.js 中的 setStatic 方法,已经集成了 static-resourcer.js

FileWatcher

业务中,常有监控某些文件,如果特定文件有更变,则需要执行相应的任务,file-watcher.js应运而生,内置了 chokidar 监控库,对日常的监控文件更变、复制文件到某一目录,进行了封装,使用如下:

const Path = require('path');
const FileWatcher = require('server-similation-toolkit').FileWatcher;
 
const fileWatcher = new FileWatcher({
  cwd: Path.resolve(__dirname, './files'),    // 此目录作为根目录,下面的 watch 操作,从此目录去寻找文件
  release: Path.resolve(__dirname, './dist'), // 设置复制的目标目录,如果为 null,而且没有调用 copyTo 方法,则不执行复制操作
  // linkCopy: false                          // 复制的时候,是否使用 “硬链接” 形式,进行复制,默认是 true
});
 
fileWatcher.on('each:change', function(p) {
  // 每次有文件更新时
});
fileWatcher.on('each:add', function(p) {
  // 每次有文件,进入监听列表时
});
fileWatcher.on('each:ready', function() {
  // 每个 .watch() 准备完成时
});
 
// 监控根目录下,所有 index.js
fileWatcher.watch('**/index.js');
// 监控根目录下,所有 样式 文件,并且复制到 目标目录下 
fileWatcher.watch('**/*.css').copyTo('./');

方法介绍:

const watcher = fileWatcher.watch('file, dir, glob, or array', { ignored: 被忽略目录的正则,默认是 /node_modules/ });
 
watcher.on('ready, add, change, unlink 4个事件', callback);
 
watcher.copyTo(String || Function);

注: watcher.copyTo() 如果传入的字符串,带后缀名称,则默认是一个文件。如果传入的是一个函数,那么函数的第一个参数,则是当前监控文件的相对路径,例如:

// 把所有样式,复制到 目标目录下
// 但是,如果是 base.css,那么把 base.css 改名为 base-rename.css
fileWatcher.watch('**/*.css').copyTo(function(p) {
  if (/base\.css/.test(p)) {
    return p.replace(/base/, 'base-rename');
  }
  return './';
});

ReloadRouter

日常开发中,如果不依赖 supervisor 等外部工具,在更改 Express 的路由配置后,需要手动关闭应用,再次重启,才能使新的配置生效。 在关闭、重启之间,耗费了大量的时间,这不是我们所预期的。

这里提供一个折中的方案,我们的配置,都放在路由文件中,当路由文件更改时,我们仅仅重新载入此路由的内容,不再重启应用。

例如:

// router.js
 
// express 的写法
// const express = require('express');
// const router = new express.Router();
 
// Server 是 express 的二次封装,拥有 express 的所有方法、实例
const Server = require('server-similation-toolkit').Server;
const router = new Server.Router();
 
router.get('/user', function(req, res) {
  res.send('hello!');
});
 
module.exports = router;

在入口文件中,如下配置:

// app.js
const express = require('express');
const app = express();
const path = require('path');
const ReloadRouter = require('server-similation-toolkit').ReloadRouter;
 
// 中间件之类的配置....
const reloadRouter = new ReloadRouter(
  path.resolve(__dirname, './router.js'), // 需要重载 的 路由文件 的 绝对路径
  {
    watchFile: true,     // 是否监控文件的变化,此开关方便在生成和开发环境之间切换
    onUpdate: function() {
      // 路由文件每次更新后 的 回调函数
    }
  }
);
// reloadRouter.update(); // 强制更新路由文件
app.use('/', reloadRouter);
 
app.listen(3000);

router.js 上的所有更改,都将在毫秒级别内生效。 甚至 router.js 产生了异常,也会即时抛出,并且访问时动态跳过异常的路由。 直至 router.js 没有异常,才会重新加入队列。

其它常用方法

const Toolkit = require('server-similation-toolkit');
 
/**
 * require 的没缓存版本
 * @param {String} filename 文件名
 * @param {String} dirname 寻址的目录地址
 * @return Object
*/
Toolkit.require1(filename, dirname);
 
/**
 * 根据文件名字,从目录列表中,获取到文件的真实路径,找不到返回 false
 * @param {String} filename 文件名字
 * @param {Array<String>} dirnames 目录列表
 * @return String || Boolean
 */
Toolkit.getFilepathByName(filename, dirnames);
 
/**
 * 从多个目录中,读取文件内容,如果文件不存在,就返回 null
 * @param {String} filename 文件名字
 * @param {Array<String>} dirnames 目录列表
 * @return Buffer
 */
Toolkit.readFileFromDirs(filename, dirnames);
 
/**
 * 把字符串或者字节,以正确的编码读取出来
 * @param {Buffer|String} bytes 需要转码的内容
 * @param {Array|String} charsets bytes可能的编码列表
 * @return {String} 转码后的字符串
*/
Toolkit.decode(bytes, charsets);

(测试,下版本可能删除)DataSpider

简单的源数据爬取,不做太复杂的功能,如要复杂的数据爬取,可配合 puppeteer 或者 testcafe 进行。

看例子:

const Util = require('server-similation-toolkit');
const Spider = Util.DataSpider;
 
Util.co(function* () {
  const url = 'http://www.baidu.com/';
  const ua = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36';
 
  const response = yield Spider.get(url, {
    headers: {
      // 设置 ua,以免被当作简单的爬虫了
      'User-Agent': ua
    }
  });
 
  // response.body 将拿到页面的源码
  // parseDom 封装了 cheerio.load 方法
  const $ = Spider.parseDom(response.body);
  // 获取到标题内容
  console.log($('title').html());
});

方法介绍:

// 把内容,转为 cheerio 对象,方便之后的查询
Spider.parseDom(content: String || Buffer, charsets?: Array);
// Spider 集成 got 库,能使用 got 库的所有方法,包括 get/post/head/patch/put/delete 等
Spider.get(url, options);

(测试,下版本可能删除)Jinja2Template

暂时仅提供 jinja2 运行模板,具体见 npm 上的 node-jinja2-template

Readme

Keywords

Package Sidebar

Install

npm i server-similation-toolkit

Weekly Downloads

0

Version

0.0.7

License

MIT

Unpacked Size

46.5 kB

Total Files

28

Last publish

Collaborators

  • linfenpan