0
点赞
收藏
分享

微信扫一扫

【Nodejs】240-有助于理解前端工具的 node 知识

缘起

平时写惯了业务代码之后,如果想要了解下 webpack 或者 vue-cli,好像是件很难上手的事情🙁 。拿 webpack 来说,我们可能会对配置熟悉点,但常常一段时间过后又忘了,感觉看起来不是很好懂。其实类似这种打包工具、构建工具我们最好应该先去学习一下 node 的一些基础知识,然后再回过头来看这些工具,就会有柳暗花明又一村的感觉,因为这些工具是用 node 写出来的🤯。
想想我们是不是时常看到过这种东西: ​​constpath=require('path');​​。假设你学过前端框架但没学过 node,你看到这句话的时候就会一头雾水,好像知道它是弄路径的,但具体这是哪里来的,常用来做什么就不得而知了,我起初看的感觉就是这样🤨。
后来才知道这其实是 node 的内置模块,因为这些构建工具或打包工具是用 node 来执行的,只要我们有装 node,它里面的内置模块就能直接引用,不用另外安装。所以强烈建议大家要是想了解这类工具最好先学习一下 node,不然会总是懵逼的🧐。
言归正传,本篇就来简要讲述一下 node 的一些常用内置模块。

node 初识

node 是什么

首先 node 不是一门后台语言而是一个环境,一个能够让 js 运行在服务器的环境,这个环境就好比是服务器上的浏览器(虽然不是很恰当),但正是因为有了它才使得 js 变成了一门后台语言。

node 遵循的规范

其次 node 遵循的是 CommonJs 规范,什么意思?其实就是规定了导入导出的方式😬,就向下面这样:


1. ​​require('./module')​​
2. ​​module.exports = {​​
3. ​​ a: 1,​​
4. ​​}​​
5. ​​exports.a = 1;​​

这就是 node 的规范,用 ​require​​ 导入、用 ​module.exports​​ 导出。那 node 为什么不支持 ESM(就是用 ​import​​ 导入、用 ​export​​ 导出)规范呢,因为它出现的比较早,仅此而已,然后一时半会儿还改不过来,以后应就会支持了。另外,我们时常在 webpack 里看到 ​require()​​ 字样却没有看见 ​import()​​ 就是因为 webpack 是要用 node 来执行的,而 node 目前只支持 ​require()​​。
这里顺带来一张各种规范图(这种东西容易忘,当作历史看看就行🙄),如下:【Nodejs】240-有助于理解前端工具的 node 知识_字符串

require 寻找依赖

require()​ 里面的参数有两种写法,一种带路径一种不带路径。就像下面这样:


1. ​​require('./module'); // 带相对路径​​
2. ​​require('/module'); // 带绝对路径​​
3. ​​require('module'); // 不带路径​​

这种不带路径的 ​require('module')​​ 引入方式,可能是内置模块,也可能是第三方模块,内置模块优先查找,没有的话就是第三方模块了,它会先从当前目录的 ​node_modules​​ 里面查找,没有的话就到父目录下的 ​node_modules​​ 里面去找,如此向上追溯,直到根目录下的 ​node_modules​​ 目录,要是还没有的话就会到全局里面去找,大概是这么一个搜索过程。
另外一种带路径的方式,就会沿着路径去找,如果没有找到则会尝试将当前目录作一个包来加载。此外,使用绝对路径的速度查找最快,当然了,node 也对路径查找做了缓存机制。

node 模块包装

node 在解析每个模块(js 文件)时,会对每个模块进行包装,就是在代码外面加一个闭包,并且向里传递五个参数,这样就保证了每个模块之间的独立,就像下面这样:


1. ​​(function(exports, require, module, __filename, __dirname) {​​
2. ​​ // module: 表示当前模块​​
3. ​​ // __filename: 当前模块的带有完整绝对路径的文件名​​
4. ​​ // __dirname: 当前模块的完整绝对路径​​
5. ​​ module.exports = exports = this = {};​​
6. ​​ // 我们的代码就在这里...​​
7. ​​ return module.exports;​​
8. ​​})()​​

想想我们平时是不是常在 webpack 里面看到 ​__dirname​ 这种东西,我们既没有引入也没有声明它,为什么能够直接使用呢,就是因为这个原因😮。

node 的应用场景

一般来说,node 主要应用于以下几个方面:

  • 自动化构建等工具
  • 中间层
  • 小项目

第一点对于前端同学来说应该是重中之重了,什么工程化、自动构建工具就是用 node 写出来的,它是前端的一大分水岭之一,是块难啃的骨头,所以我们必须拿下,不然瓶颈很快就到了。如果你能熟练应用 node 的各种模块(系统模块 + 第三方模块),那么恭喜你,你又比别人牛逼了一截😎。

node 的优点

  • 适合前端大大们
  • 基于事件驱动和无阻塞的I/O(适合处理并发请求)
  • 性能较好(别人做过性能分析)

node 内置模块

ok,废话了这么多,咱们赶紧来看看一些常见的 node 基础模块吧。相信掌握这些对你学习 webpack 和 vue-cli 等工具是有很大帮助的✊ 。

http 模块

这是 node 最最基础的功能了,我们用 ​node http.js​​ 运行一下下面的文件就能开启一个服务器,在浏览器中输入 ​http://localhost:8888​ 即可访问,http.js 具体内容如下:

    1. ​​// http.js​​
    2. ​​const http = require('http');​​
    3. ​​http.createServer((req, res) => { // 开启一个服务​​
    4. ​​ console.log('请求来了'); // 如果你打开 http://localhost:8888,控制台就会打印此消息​​
    5. ​​ res.write('hello'); // 返回给页面的值,也就是页面会显示 hello​​
    6. ​​ res.end(); // 必须有结束的标识,否则页面会一直处于加载状态​​
    7. ​​}).listen(8888); // 端口号​​

    fs 文件系统

    由于 js 一开始是用来开发给浏览器用的,所以它的能力就局限于浏览器,不能直接对客户端的本地文件进行操作,这样做的目的是为了保证客户端的信息安全,当然了,通过一些手段也可以操作客户端内容(就像 ​<inputtype='file'>​​),但是需要用户手动操作才行。
    但是当 js 作为后台语言时,就可以直接对服务器上的资源文件进行 I/O 操作了。这也是 node 中尤为重要的模块之一(操作文件的能力),这在自动化构建和工程化中是很常用的。它的主要职责就是读写文件,或者移动复制删除等。fs 就好比对数据库进行增删改查一样,不同的是它操作的是文件。下面我们来具体看看代码用例:


    1. ​​const fs = require('fs');​​
    2.
    3. ​​// 写入文件:fs.writeFile(path, fileData, cb);​​
    4. ​​fs.writeFile('./text.txt', 'hello xr!', err => {​​
    5. ​​ if (err) {​​
    6. ​​ console.log('写入失败', err);​​
    7. ​​ } else {​​
    8. ​​ console.log('写入成功');​​
    9. ​​ }​​
    10. ​​});​​
    11.
    12. ​​// 读取文件:fs.readFile(path, cb);​​
    13. ​​fs.readFile('./text.txt', (err, fileData) => {​​
    14. ​​ if (err) {​​
    15. ​​ console.log('读取失败', err);​​
    16. ​​ } else {​​
    17. ​​ console.log('读取成功', fileData.toString()); // fileData 是二进制文件,非媒体文件可以用 toString 转换一下​​
    18. ​​ }​​
    19. ​​});​​

    需要注意的是 readFile 里面的 fileData 是原始的二进制文件🤨(em...就是计算机才看的懂的文件格式),对于非媒体类型(如纯文本)的文件可以用 ​toString()​​ 转换一下,媒体类型的文件以后则会以流的方式进行读取,要是强行用 ​toString()​​ 转换的话会丢失掉原始信息,所以不能乱转。二进制和 ​toString​​ 的效果就像下面这样:【Nodejs】240-有助于理解前端工具的 node 知识_绝对路径_02另外,和 fs.readFile(异步) 和 fs.writeFile(异步)相对应的还有 fs.readFileSync(同步)和 fs.writeFileSync(同步),fs 的大多方法也都有同步异步两个版本,具体取决于业务选择,一般都用异步,不知道用啥的话也用异步。

    path 路径

    这个模块想必大家应该都并不陌生,🧐瞟过 webpack 的都应该看过这个东东。很显然,path 就是来处理路径相关东西的,我们直接看下面的常见用例就能够体会到:

    嗯,下次看到 path 这个东西就不会迷茫了。

    url 模块

    很显然这是个用来处理网址相关东西的,也是我们必须要掌握的,主要用来获取地址路径和参数的,就像下面这样:


    1. ​​const url = require('url');​​
    2.
    3. ​​let site = 'http://www.xr.com/a/b/index.html?a=1&b=2';​​
    4. ​​let { pathname, query } = url.parse(site, true); // url.parse() 解析网址,true 的意思是把参数解析成对象​​
    5.
    6. ​​console.log(pathname, query);​​
    7. ​​// /a/b/index.html { a: '1', b: '2' }​​

    querystring 查询字符串

    这个主要是用来把形如这样的字符串 ​a=1&b=2&c=3​​(&和=可以换成别的)解析成 ​{a:'1',b:'2',c:'3'}​ 对象,反过来也可以把对象拼接成字符串,上面的 url 参数也可以用 querystring 来解析,具体演示如下:


    1. ​​const querystring = require('querystring');​​
    2.
    3. ​​let query = 'a=1&b=2&c=3'; // 形如这样的字符串就能被解析​​
    4. ​​let obj = querystring.parse(query);​​
    5. ​​console.log(obj, obj.a); // { a: '1', b: '2', c: '3' } '1'​​
    6.
    7. ​​query = 'a=1&b=2&c=3&a=3'; // 如果参数重复,其所对应的值会变成数组​​
    8. ​​obj = querystring.parse(query);​​
    9. ​​console.log(obj); // { a: [ '1', '3' ], b: '2', c: '3' }​​
    10.
    11. ​​// 相反的我们可以用 querystring.stringify() 把对象拼接成字符串​​
    12. ​​query = querystring.stringify(obj);​​
    13. ​​console.log(query); // a=1&a=3&b=2&c=3​​

    assert 断言

    这个我们直接看下面代码就知道它的作用了:


    1. ​​// assert.js​​
    2. ​​const assert = require('assert');​​
    3.
    4. ​​// assert(条件,错误消息),条件这部分会返回一个布尔值​​
    5. ​​assert(2 < 1, '断言失败');​​

    nodeassert.js​​ 运行一下代码就能看到如下结果:【Nodejs】240-有助于理解前端工具的 node 知识_字符串_03上图是断言失败的例子,如果断言正确的话,则不会有任何提示,程序会继续默默往下执行。所以断言的作用就是先判断条件是否正确(有点像 if),如果条件返回值为 ​​false​​ 则阻止程序运行,并抛出一个错误,如果返回值为 ​true​​ 则继续执行,一般用于函数中间和参数判断。
    另外,这里再介绍两种 equal 用法(assert 里面有好多种 equal,这里举例其中的两种):


    1. ​​// assert.js​​
    2. ​​const assert = require('assert');​​
    3.
    4. ​​const obj1 = { a: { b: 1 } };​​
    5. ​​const obj2 = { a: { b: 1 } };​​
    6. ​​const obj3 = { a: { b: '1' } };​​
    7.
    8. ​​// assert.deepEqual(变量,预期值,错误信息) 变量 == 预期值​​
    9. ​​// assert.deepStrictEqual(变量,预期值,错误信息) 变量 === 预期值​​
    10. ​​// 同样也是错误的时候抛出信息,正确的时候继续默默执行​​
    11. ​​assert.deepEqual(obj1, obj2, '不等哦'); // true​​
    12. ​​assert.deepEqual(obj1, obj3, '不等哦'); // true​​
    13. ​​assert.deepStrictEqual(obj1, obj2, '不等哦'); // true​​
    14. ​​assert.deepStrictEqual(obj1, obj3, '不等哦'); // false,这个会抛出错误信息​​

    stream 流

    stream 又叫做流,大家或多或少应该有听过这个概念,那具体是什么意思呢?在这里,你可以把它当做是前面说过的 ​fs.readFile​​ 和 ​fs.writeFile​​ 的升级版。
    我们要知道 ​​readFile​​ 和 ​writeFile​ 的工作流程 是先把整个文件读取到内存中,然后再一次写入,这种方式对于稍大的文件就不适用了,因为这样容易导致内存不足,所以更好的方式是什么呢?就是边读边写啦,业界常说成管道流,就像水流经过水管一样,进水多少,出水就多少,这个水管就是占用的资源(内存),就那么大,这我们样就能合理利用内存分配啦,而不是一口气吃成个胖子,有吃撑的风险(就是内存爆了🤐)。


    1. ​​const fs = require('fs');​​
    2.
    3. ​​// 读取流:fs.createReadStream();​​
    4. ​​// 写入流:fs.createWriteStream();​​
    5. ​​let rs = fs.createReadStream('a.txt'); // 要读取的文件​​
    6. ​​let ws = fs.createWriteStream('a2.txt'); // 输出的文件​​
    7.
    8. ​​rs.pipe(ws); // 用 pipe 将 rs 和 ws 衔接起来,将读取流的数据传到输出流(就是这么简单的一句话就能搞定)​​
    9.
    10. ​​rs.on('error', err => {​​
    11. ​​ console.log(err);​​
    12. ​​});​​
    13. ​​ws.on('finish', () => {​​
    14. ​​ console.log('成功');​​
    15. ​​})​​

    流式操作,就是一直读取,它是个连续的过程,如果一边快一边慢,或者一边出错没衔接上也没关系,它会自动处理,不用我们自己去调整其中的误差,是个优秀的模块没错了👍。另外,我们没有直接使用 stream 模块,是因为 fs 模块引用了它并对其做了封装,所以用 fs 即可。

    zlib 压缩

    这个用法简单,作用也明了,直接看下面的代码就能理解:


    1. ​​const fs = require('fs');​​
    2. ​​const zlib = require('zlib');​​
    3.
    4. ​​let rs = fs.createReadStream('tree.jpg');​​
    5. ​​let gz = zlib.createGzip();​​
    6. ​​let ws = fs.createWriteStream('tree.jpg.gz');​​
    7.
    8. ​​rs.pipe(gz).pipe(ws); // 原始文件 => 压缩 => 写入​​
    9.
    10. ​​rs.on('error', err => {​​
    11. ​​ console.log(err);​​
    12. ​​});​​
    13. ​​ws.on('finish', () => {​​
    14. ​​ console.log('成功');​​
    15. ​​})​​

    小结

    ok👌,以上就是本章要讲的一些 node 知识(比较基础,大家凑合看看)。当然除此之外,还有 util、Buffer、Event、crypto 和 process 等其他内置模块,这里就不一一赘述了,希望大家能够多动手多敲两下代码多实践,毕竟纸上得来终觉浅嘛💪。如果你能用好 node 的各种模块,那么转后端也就拥有了无限可能性😋(其实前端的坑大的超乎你想像😭)。


    【Nodejs】240-有助于理解前端工具的 node 知识_字符串_04

    每一个,都是对我最大的肯定!【Nodejs】240-有助于理解前端工具的 node 知识_字符串_05

    举报

    相关推荐

    0 条评论