Node.js 基礎(chǔ)入門

一、Node.js 簡介

Node.js 是一個基于 Chrome V8 引擎 的 JavaScript 運行時環(huán)境

安裝與運行

下載

https://nodejs.org/zh-cn/download/
下載

創(chuàng)建項目

mkdir node
cd node
npm init -y

新建 index.js 文件

const { readFile } = require('fs')

readFile('./package.json', { encoding: 'utf-8' }, (err, data) => {
    if (err) {
        throw err;
    }
    console.log(data)
})

輸入 package.json 文件

node index.js

版本管理

在同一個設(shè)備上如何快速切換Node.js 版本?

版本管理工具:

  • n: 一個npm 全局的開源包,是依賴npm 來全局安裝、使用的
  • fnm: 快速簡單,兼容性支持.node-version和.nvmrc文件
  • nvm: 獨立的軟件包,Node Version Manager

特點

特點
異步 I/O

當 Node.js 執(zhí)行 I/O 操作時,會在響應返回并恢復操作,而不是阻塞線程并浪費CPU 循環(huán)等待

異步 I/O
單線程

Node.js 保持了 JavaScript 在瀏覽器中單線程的特點

優(yōu)點:

  • 不用處處在意狀態(tài)同步問題,不會發(fā)生死鎖
  • 沒有線程上下文切換帶來的性能開銷

缺點:

  • 無法利用多核 CPU
  • 錯誤會引起整個應用退出,健壯性不足
  • 大量計算占用導致CPU,無法繼續(xù)執(zhí)行

瀏覽器為例,瀏覽器是多進程,JS 引擎單線程

Browser 進程:瀏覽器主進程,只有一個

插件進程:插件使用時才創(chuàng)建

GPU 進程:最多一個用于3D 繪制

渲染進程:頁面渲染、JS執(zhí)行、事件處理

  • GUI 渲染線程+
  • JS 引擎線程+ V8
  • 事件觸發(fā)線程
  • 定時器觸發(fā)線程
  • 異步請求
跨平臺

兼容Windows 和*nix 平臺,主要得益于在操作系統(tǒng)與Node 上層模塊系統(tǒng)之間構(gòu)建了一層平臺架構(gòu)

跨平臺

應用場景

Node.js 在大部分領(lǐng)域都占有一席之地,尤其是I/O密集型的

  • Web 應用:Express / Koa
  • 前端構(gòu)建: WebpackGUI
  • 客戶端軟件: VSCode / 網(wǎng)易云音樂
  • 其它:實時通訊、爬蟲、CLI 等

二、模塊化機制

  1. 何為模塊化?
    根據(jù)功能或業(yè)務將一個大程序拆分成互相依賴的小文件,再用簡單的方式拼裝起來
  2. 為什么模塊化?
    無模塊化問題所有script 標簽必須保證順序正確,否則會依賴報錯
    全局變量存在命名沖突,占用內(nèi)存無法被回收
    IIFE/namespace 會導致代碼可讀性低等諸多問題

CommonJS規(guī)范

Node.js 支持 CommonJS 模塊規(guī)范,采用同步機制加載模塊

// greeting.js
const prefix = 'hello';
const sayHi = function () {
    return prefix + 'world';
}
module.exports = {
    sayHi
}

// index.js
const { sayHi } = require('./greeting');
sayHi();

exports = module.exports = { }

// greeting.js
const prefix = 'hello';
const sayHi = function () {
    return `${prefix}  world`;
}
exports.sayHi = sayHi;

// index.js
const { sayHi } = require('./greeting');
sayHi();

CommonJS 中 exports、require、module、__filename、__dirname 變量

function (exports, require, module, __filename, __dirname) {
    const m = 1;
    module.exports.m = m;
}

加載方式:

  1. 加載內(nèi)置模塊require('fs')
  2. 加載相對 | | 絕對路徑的文件模塊
    require('/User/.../file.js')
    require('./file.js')
  3. 加載 npm 包 require('lodash')

npm 包查找原則:

require('lodash')

  1. 當前目錄node_modules
  2. 如果沒有,父級目錄的node_modules
  3. 如果沒有,沿著路徑向上遞歸,直到根目錄下node_modules
  4. 找到之后會加載package.json main 指向的文件,如果沒有package.json 則依次查找index.js、index.json、index.node

require.cache

require.cache 中緩存著加載過的模塊,緩存的原因:同步加載

  1. 文件模塊查找耗時,如果每次require 都需要重新遍歷查找,性能會比較差;
  2. 在實際開發(fā)中,模塊可能包含副作用代碼
// 有緩存
const mod1 = require('./foo');
const mod2 = require('./foo');
console.log(mod1 === mod2);     // true

//無緩存
function requireUncached(module) {
    delete require.cache[require.resolve(module)];
    return require(module);
}
const mod3 = requireUncached('./foo');
console.log(mod1 === mod3);     // false

其他模塊化規(guī)范

  • AMD 是 RequireJS 在推廣過程中規(guī)范化產(chǎn)出,異步加載,推崇依賴前置
  • CMD 是 SeaJS 在推廣過程中規(guī)范化產(chǎn)出,異步加載,推崇就近依賴
  • UMD (Universal Module Definition) 規(guī)范,兼容 AMD 和 CommonJS 模式
  • ES Modules (ESM),語言層面的模塊化規(guī)范,與環(huán)境無關(guān),可借助 babel 編譯

常用模塊介紹

文件

var fs = require("fs") // 引入 fs 模塊
fs.readFile(filename, [options], callback); //讀取文件
fs.writeFile(filename, data, [options], callback); // 寫文件
fs.appendFile(filename, data, [options], callback); //以追加的方式寫文件
fs.open(filename, flags, [mode], callback); //打開文件
fs.mkdir(path, [mode], callback); //創(chuàng)建目錄:
fs.readdir(path, callback); //讀取目錄
fs.exists(path, callback); //查看文件與目錄是否存在

Path 模塊

var path = require("path") // 引入 path 模塊
path.basename(path[, ext]); //返回path的最后-部分
path.dirname(path); // 返回path的目錄名
path.normalize(path);//路徑解析,得到規(guī)范路徑
path.isAbsolute(path); //判斷路徑是否是絕對路徑
path.relative(form,to); //方法根據(jù)當前工作目錄返回從from 到to的相對路徑
path.resolve([...paths]); //將路徑或路徑片段的序列解析為絕對路徑

OS 模塊

var os = require("os") //引入 os 模塊
os.cpus(); //返回每個邏輯cpu內(nèi)核信息
os.hostname(); //返回操作系統(tǒng)的主機名
os.platform(); //返回標識操作系統(tǒng)平臺的字符串
os.userInfo([options]); //返回關(guān)于當前有效用戶的信息

三、包管理機制

npm介紹

NPM 是Node.js 中的包管理器,提供了安裝、刪除等其它命令來管理包

常用命令:

  • npm init
  • npm config
  • npm run [cmd]
  • npm install [pkg]
  • npm uninstall [pkg]
  • npm update [pkg]
  • npm info [pkg]
  • npm publish

package.json 文件

  • name 包名稱
  • version 版本號
  • main 入口文件
  • scripts 執(zhí)行腳本
  • dependencies 線上依賴
  • devDependencies 開發(fā)依賴
  • repository 代碼托管地址

更多 package.json 配置

https://docs.npmjs.com/cli/v7/configuring-npm/package-json

依賴

  • dependencies 業(yè)務依賴,應用發(fā)布后正常執(zhí)行所需要的包
  • devDependencies 開發(fā)依賴,只用于開發(fā)環(huán)境
  • peerDependencies 同等依賴,比如一個webpack 插件依賴特定版本的webpack
  • bundledDependencies 打包依賴(npm run pack),必須已經(jīng)在devDep 或者dep聲明過
  • optionalDependencies 可選依賴

私有 npm

其它

  • 并行安裝
  • 扁平管理
  • 鎖文件(lockfile)
  • 緩存優(yōu)化
  • ...
  • npm7 | yarn => lock/扁平/緩存...
  • pnpm => monorepo/硬、符號鏈接/安全性高...

四、異步編程

Callback

目的:讀取 package.json 中main 字段對應的文件內(nèi)容
問題:如何解決回調(diào)地獄?

const fs = require('fs')

fs.readFile('./package.json', { encoding: 'utf-8' }, (err, data) => {
    if (err) throw err;
    const { main } = JSON.parse(data);
    fs.readFile(main, { encoding: 'utf-8' }, (err, data) => {
        if (err) throw err;
        console.log(data)
    })
})

Promise

Promise 是一個具有四個狀態(tài)的有限狀態(tài)機,其中三個核心狀態(tài)為 Pending(掛起),F(xiàn)ulfilled(完成)、Rejected(拒絕),以及還有一個未開始狀態(tài)。

異步

使用 Promise , 實現(xiàn)讀取 package.json 中 main 字段對應的文件內(nèi)容

const { readFile } = require('fs/promises')

readFile('./package.json', { encoding: 'utf-8' }).then(res => {
    return JSON.parse(res);
}).then(data => {
    return readFile(data.main, { encoding: 'utf-8' });
}).then(res => {
    console.log(res);
})

如何將 Callback 轉(zhuǎn)為 Promise 形式?ugl.promisify

function promisify(fn, receiver) {
    return (...args) => {
        return new Promise((resolve, reject) => {
            fn.apply(receiver, [...args, (err, res) => {
                return err ? reject(err) : resolve(res);
            }]);
        });
    };
}
const readFilePromise = promisify(fs.readFile, fs);

await

await 函數(shù)使用 try catch 捕獲異常(注意并行處理)

const { readFile } = require('fs/promises')

async () => {
    const { main } = JSON.parse(await readFile('./package.json', { encoding: 'utf-8' }));
    const data = await readFile(main, { encoding: 'utf-8' });
    console.log(data);
}

Event

發(fā)布訂閱模式,Node.js 內(nèi)置events 模塊

比如 HTTP server on('request') 事件監(jiān)聽

//發(fā)布訂閱模式
const EventEmitter = require('events');

class MyEmitter extends EventEmitter { }
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
    console.log('an event occurred!');
});
myEmitter.emit('event');

// server 監(jiān)聽request 事件
const http = require('http');
const server = http.createServer((req, res) => {
    res.end('hello')
});
server.on('request', (req, res) => {
    console.log(req.url);
});
server.listen(3000);

五、Web 應用開發(fā)

HTTP 模塊

搭建一個最簡單的HTTP 服務?Node.js 內(nèi)置HTTP 模塊

const http = require('http');

http.createServer((req, res) => {
    res.end('hello World\n');
}).listen(3000, () => {
    console.log('App running at http://127.0.0.1:3000/')
})

Koa 介紹

Koa —— 基于 Node.js 平臺的下一代Web 開發(fā)框架 Koa 它僅僅提供了一個輕量優(yōu)雅的函數(shù)庫,使得編寫Web 應用變得得心應手, 不在內(nèi)核方法中綁定任何中間件

const Koa = require('koa');
const app = new Koa();

app.use(async ctx => {
    ctx.body = 'Hello World';
});

app.listen(3000, () => {
    console.log('App running at http://127.0.0.1:3000/')
});

執(zhí)行過程

  • 服務啟動
    • 實例化 application
    • 注冊中間件
    • 創(chuàng)建服務、監(jiān)聽端口
  • 接受/處理請求
    • 獲取請求req、res 對象
    • req -> request、res -> response 封裝
    • request & response -> context
    • 執(zhí)行中間件
    • 輸出設(shè)置到ctx.body 上的內(nèi)容

Koa 中間件

Koa 應用程序是一個包含一組中間件函數(shù)的對象,它是按照洋蔥模型組織和執(zhí)行的

Koa

常用中間件

  • koa-router:路由解析
  • koa-body: request body 解析
  • koa-logger:日志記錄
  • koa-views: 模板渲染
  • koa2-cors :跨域處理
  • koa-session:session 處理
  • koa-helmet:安全防護
  • ...

Koa 中間件繁多,質(zhì)量參差不齊,需要合理選擇,高效組合...

基于Koa 的前端框架

開源:ThinkJS / Egg ...

內(nèi)部:Turbo、Era、Gulu ...

它們做了什么?

  • Koa 對象 response / request / context / application 等擴展
  • Koa 常用中間件庫
  • 公司內(nèi)部服務支持
  • 進程管理
  • 腳手架
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容