高度兼容低版本的 antd 的动态主题方案

  • Post author:
  • Post category:其他


本文立足于代码,通过

实现自定义事件、使用流读写数据以及导入导出模块

,抛砖引玉,希望能对大家理解事件驱动结构、流和模块这几个

Node.js

中的核心概念有所帮助



事件驱动架构



是什么



有什么用

  • 模块之间更加独立
  • 可以对同一个事件多次响应



代码说明

  • 自定义事件

    const EventEmitter = require("events"); // events模块// 继承class Sales extends EventEmitter {constructor() {super();}}// 创建实例const myEmitter = new Sales();// listener1myEmitter.on("newSale", () => {console.log("There was a new sale");});// listener2myEmitter.on("newSale", (store) => {console.log(`There are now ${store} items`);});// emittermyEmitter.emit("newSale", 10);

    * 使用内置的

    http

    模块

    const http = require("http");// 创建实例const server = http.createServer();// listener1server.on("request", (req, res) => {console.log("Request received!"); // 显示在控制台res.end("Request received!"); // 显示在浏览器});// listener2server.on("request", (req, res) => {console.log("Another request received!");});// emitter(启动服务器)server.listen(8000, "127.0.0.1", () => {console.log("Waiting for request...");});

    ### 需要注意的地方

  • 设置多个事件监听器时,其执行顺序的规则与同步代码一样

  • 编写自定义事件时,注意

    emitter



    listener

    的顺序






是什么

官网的描述是:

A stream is an abstract interface for working with streaming data in Node.js.

也就是说,流是用于处理流式数据的抽象接口???好家伙,用自己解释自己!!!

那就不管了,先把概念封装起来,直接看

API


  • Node.js

    中的流有四种| 流 | 作用 | 案例 | 重要事件 | 重要函数 || — | — | — | — | — || ⭐可读流 | 读取数据 |

    http

    请求,

    fs

    读取文件 |

    data

    ,

    end

    |


    pipe()


    ,

    read()

    || ⭐可写流 | 写入数据 |

    http

    响应,

    fs

    写入文件 |

    drain

    ,

    finish

    |

    write()

    ,

    end()

    || 双工流 | 即可读又可写 |

    net

    模块的

    web socket

    | / | / || 转换流 | 在读写的时候转换数据 |

    zlib

    模块的

    Gzip creation

    | / | / |*


    Node.js

    中的

    Stream

    本质上是

    EventEmitter

    的实例

    ,所以也可以发射和监听事件



有什么用

  • 用于读取或修改数据/文件片段,从而不需要在内存中保存全部资源
  • 适合用于处理海量的数据,比如流媒体



代码说明

首先来设想一个需求:从磁盘读取文件,然后发给客户端

so easy,直接调用

fs

模块和

http

模块,一顿操作:

// 从磁盘读取text文件,发给客户端
const fs = require("fs");
const server = require("http").createServer();

server.on("request", (req, res) => {fs.readFile("test-file.txt", (err, data) => {if (err) console.log(err);res.end(data);});
});

server.listen(8000, "127.0.0.1", () => {console.log("Listening request...");
}); 

所读取的

test-file.txt

文件的内容是:100万行“Node.js is the best!”

启动

Node

程序,并在浏览器输入

127.0.0.1:8000

,如期看到文本内容:

看到这里,相信大家也发现问题了:

  • 使用上面的代码读取的是全部的文本内容,但有时候我们只需要获取部分数据就能满足需求
  • 使用

    fs.readFile()

    读取文件时,本质上是

    先将文件的内容存在变量

    data

    中,等全部读取完再发送给客户端

    ,这就给

    Node

    应用带来了内存压力,当文件很大时程序就崩溃了

👉使用流可以解决以上问题,代码如下:

const fs = require("fs");
const server = require("http").createServer();

server.on("request", (requ, res) => {// 创建可读流实例const readable = fs.createReadStream("test-file.txt");// 读取文件时readable.on("data", (chunk) => {res.write(chunk); // 使用可写流的write()方法, chunk为每次传输的数据});// 文件读取完毕readable.on("end", () => {res.end();});// 读取出错readable.on("error", (err) => {console.log(err);res.statusCode(500);res.end("File not found!");});
});

server.listen(8000, "127.0.0.1", () => {console.log("Listening request...");
}); 


使用流读取数据的思路

是:

readable

是一个可读流实例,在

readable.on()

中监听

data

事件,当不断读取数据时,

data

事件就会一直被触发,此时将读取的数据,不借助变量直接写入

res

中,文件全部读取完后便会触发

end

事件。

所以可以简单地将



理解为

分段传输数据

但上面的思路其实存在一个由读写的速度带来漏洞,如果可读流从磁盘读入数据的速度,远远大于写入

res

的速度,即接收到的这一波数据还来不及发送出去,下一波就来了,这就会造成内存溢出,也就是

背压

👉可读流的

pipe()

方法可以解决这个问题,它可以自动处理数据的读写速度:

const fs = require("fs");
const server = require("http").createServer();

server.on("request", (requ, res) => {const readable = fs.createReadStream("test-file.txt");readable.pipe(res); // 优雅永不过时
});

server.listen(8000, "127.0.0.1", () => {console.log("Listening request...");
}); 



模块



是什么


  • Node.js

    中,每个

    .js

    文件都是一个模块

  • Node.js

    模块的导入导出使用的是

    CommonJS

    规范*

    require()

    :引入模块*


    exports

    :导出多个变量;只能为模块添加属性

    *


    module.exports

    :导出单一变量,比如类或函数;可以为模块添加属性或赋值到新对象




require('module')

的过程

graph LR
加载模块 --> 封装模块--> 执行模块--> 返回--> 缓存 

  • 加载



    require

    加载模块的优先级:1.核心模块:

    require('http')

    2.自定义模块:

    require('./utils')

    3.第三方模块(

    npm

    安装):

    require('express')

    完整的过程是:* 首先按照名称查找有无对应的核心模块* 如果以

    ./



    ../

    开头,就按照路径查找自定义的模块;没找到就去找同名文件夹下的

    index.js

    文件* 如果按照名称没有找到核心模块,就去

    node_modules

    目录下找;所以出现重名,就会优先加载核心模块

  • 封装



    Node.js

    封装模块时使用的函数封装器:

    (function(exports, require, module, __filename, __dirname) {// 模块的代码实际在这里});// require:加载模块// exports:从模块中导出对象// module:当前模块文件// __filename:当前模块文件的绝对路径// __dirname:当前模块文件据所在目录的绝对路径

    事实上,在任何一个

    .js

    文件中,输入

    console.log(arguments)

    就可以看到当前模块的上述参数值;此外,

    Node.js

    中有个核心模块就叫

    module

    ,我们同样可以使用它来查看上面的函数封装器

    // index.jsconsole.log(arguments);console.log('------------------------')console.log(require("module").wrapper);

    控制台的输出结果如下:

    [Arguments] {'0': {}, // require'1': // exports{ [Function: require]resolve: { [Function: resolve] paths: [Function: paths] },main: Module { id: '.', exports: {}, parent: null, filename: 'E:\\index.js', loaded: false, children: [], paths: [Array] },extensions: [Object: null prototype] { '.js': [Function], '.json': [Function], '.node': [Function] }, cache: [Object: null prototype] { 'E:\\index.js': [Module] } },'2': // moduleModule {id: '.',exports: {},parent: null,filename: 'E:\\index.js',loaded: false,children: [],paths: [ 'E:\\node_modules' ] },'3': 'E:\\index.js', // __filename'4': 'E:\\' } // __dirname------------------------[ '(function (exports, require, module, __filename, __dirname) { ','\n});' ]

    * ⭐

    返回

    :*


    require

    返回的是模块的导出,这些导出存储在

    module.exports

    这个对象中,而

    exports

    只是指向

    mudule.exports



代码说明



module.js

  • 使用

    module.exports

    导入

    test-module-1.js````// module.js// 使用module.exportsconst C = require("./test-module-1");const calc1 = new C();console.log(calc1.add(3, 4));// 使用exportsconst { add, print } = require("./test-module-2"); // 导出的是对象(函数封装器中的exports),所以可以用解构赋值console.log(add(5, 5)); print(); ``````// test-module-1.jsmodule.exports = class {add(a, b) {return a + b;}multiply(a, b) {return a * b;}divide(a, b) {return a / b;}}; ```* 使用

    exports

    导入

    test-module-2.js

    // test-module-2.jsexports.add = (a, b) => a + b;exports.multiply = (a, b) => a * b;exports.divide = (a, b) => a / b;module.exports.print = () => {console.log("--------");}; ```* 下面做些有意思的事情在`test-module-2.js`添加一行代码,然后直接运行`node test-module-2.js

    // test-module-2.jsexports.add = (a, b) => a + b;exports.multiply = (a, b) => a * b;exports.divide = (a, b) => a / b;module.exports.print = () => {console.log(“——–”);};console.log(arguments);

    这里截取控制台输出的一小段

    [Arguments] { ‘0’: // require { add: [Function], multiply: [Function], divide: [Function], print: [Function] }, … ‘2’: Module {id: ‘E:\Resourses\Node\complete-node-bootcamp\2-how-node-works\starter\test-module-2.js’,exports: // module.exports { add: [Function], multiply: [Function], divide: [Function], print: [Function] },parent:… }

    我们已经知道这一项表示的是`require`,下面在`module.js`中也增加一行代码`console.log(require('test-module-2.js'))`,对应的输出结果如下:

    { add: [Function],multiply: [Function],divide: [Function],print: [Function] } “`现在就比较清晰了,也验证了我们在上一节中的描述,



    require

    返回的其实就是

    Node.js

    内部

    module.exports

    这个对象

    总结


  • http

    模块,

    fs

    模块等,都是基于事件驱动架构所创建的

  • 使用可读流读取数据可以不经过变量,直接将读取到的数据发送给客户端,减小了

    Node.js

    的内存压力

  • 可读流的

    pipe()

    方法可以解决背压问题:

    可读流实例.pipe(可写流实例)


  • exports

    用于导出多个变量,但只能为模块添加属性,不能赋值到新对象;

    module.exports

    用于导出单一变量,比如类或函数;也可以为模块添加属性或赋值到新对象;名称相同时,优先使用

    module.exports



最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。








有需要的小伙伴,可以点击下方卡片领取,无偿分享



版权声明:本文为web22050702原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。