nodejs 入門基本操作

2023-05-16 12:00:28

操作fs模組

const path = require("path");
const fs = require("fs");

/*
相對路徑是命令視窗執行的目錄
node 提供了path模組來操作路徑相關的api, 其中__dirname是一個內建的變數,返回當前檔案所在的目錄
*/
const getDirUrl = dir => {
  return path.resolve(__dirname, dir);
};
for (let i = 0; i < 5; i++) {
  fs.writeFileSync(getDirUrl("./create01.text"), i + "、我是測試資料" + i + "\n", {
    flag: "a+",
    encoding: "utf-8"
  });
}

console.log("hello nodejs");

const data = fs.readFileSync(getDirUrl("./create01.text"), {encoding: 'utf-8'}).toString()
console.log('同步讀取')
console.log(data)

console.log('非同步讀取')
fs.readFile(getDirUrl("./create01.text"), (err, data) => {
  if(!err) {
    console.log(data.toString());
  } else {
    console.error(err);
  }
});

在視窗執行對應的目錄即可,我這裡是:

操作http模組

// 1. 匯入http模組
const http = require("http");
const fs = require("fs");
const path = require("path");

const mimes = {
  html: "text/html",
  css: "text/css",
  js: "text/javascript",
  png: "image/png",
  jpg: "image/jpeg",
  gif: "image/gif",
  mp4: "video/mp4",
  mp3: "audio/mpeg",
  json: "application/json"
};
//2. 建立服務物件 create 建立 server 服務
// request 意為請求. 是對請求報文的封裝物件, 通過 request 物件可以獲得請求報文的資料
// response 意為響應. 是對響應報文的封裝物件, 通過 response 物件可以設定響應報文
const server = http.createServer((req, res) => {
  let { url, method } = req; 
  // 資料夾路徑
  const rootDir = __dirname + "/public";
    
  let filePath = rootDir + url;
  if (!fs.existsSync(filePath)) {
    return
  }
  // 讀取內容
  fs.readFile(filePath, (err, data) => {
    if (err) {
      console.log(err);
      //設定字元集
      res.setHeader('content-type','text/html;charset=utf-8');
      //判斷錯誤的代號
      switch(err.code){
        case 'ENOENT':
          res.statusCode = 404;
          res.end('<h1>404 Not Found</h1>');
        case 'EPERM':
          res.statusCode = 403;
          res.end('<h1>403 Forbidden</h1>');
        default:
          res.statusCode = 500;
          res.end('<h1>500 Internal Server Error</h1>');
      }
      return;
    }
    //獲取檔案的字尾名
    let ext = path.extname(filePath).slice(1);
    //獲取對應的型別
    let type = mimes[ext];
    if(type){
      if(ext === 'html'){
        res.setHeader('content-type', type + ';charset=utf-8');
      }else{
        res.setHeader('content-type', type);
      }
    }else{
      //沒有匹配到-預設設定二進位制檔案型別
      res.setHeader('content-type', 'application/octet-stream');
    }
    //響應檔案內容
    res.end(data);
  });
});

//3. 監聽埠, 啟動服務
server.listen(9000, () => {
  console.log("服務已經啟動,9000埠監聽中...");
});

如上最簡單的http 服務起來了,在瀏覽器中 輸入 http://localhost:9000/index.html 得到如下頁面

通過匹配字尾,在public檔案中返回對應的資源,程式碼結構如下

都是一些很簡單的程式碼就不貼了,如果需要留下郵箱即可。

其他模組看看官網的檔案即可,不在記錄。

框架

上面都是通過原始的方式來使用node,其實node的生態也很豐富,有很多的框架讓我們選擇,如 express、koa2、nestjs、midwayjs 等等

express 基本使用

// 1.0 匯入express
const express = require('express')
// 2.0 express 範例
const app = express()
const port = 9000

// 3.0 路由
app.get('/', (req, res) => {
  res.send('基本使用 Hello World!')
})

// 啟動服務
app.listen(port, () => {
  console.log(`啟動服務,埠: ${port}`)
})

通常我們使用腳手架,這樣可以得到統一的專案結構 如 express_ generator,具體檢視express 官網

中介軟體-洋蔥模型

  目前比較流行的 nodejs http 服務架構使用攔截器模式,這種模式將 http 請求響應的過程分為若干切面,每個切面上進行一項或若干項關聯的操作。比如說,我們可以通過不同的攔截切面處理使用者資訊驗證、對談(session)驗證、表單資料驗證、query 解析,或者業務邏輯處理等等。這種架構設計讓切面與切面之間彼此獨立。

有點面向切面程式設計的概念,不知道對不對。

手動實現一個攔截器:實現類似如下效果

async (ctx, next) => {
  do sth...
}

通過呼叫next 執行下一個函數,可以中途return退出,也可以繼續呼叫next直到最後一個函數,然後在一層一層的返回,洋蔥的結構跟這個類似,所以叫洋蔥模型。

這裡的中介軟體其實是一個函數,在外層使用use 注入進來。

執行第一個中介軟體的fn,呼叫next 進入到下一個中介軟體,繼續執行下一個fn,呼叫next 友進入下一個中介軟體,繼續重複上述邏輯,直至最後一箇中介軟體,直至最後一箇中介軟體,就會執行 next 語句後面的程式碼,然後繼續上一個中介軟體的next後置語句,繼續重複上述邏輯,直至執行第一個中介軟體的next後置語句,最後輸出,這個執行的機制,稱為洋蔥模型
 

模擬洋蔥模型

 洋蔥模型關鍵在於怎麼處理next 引數,next是下一個函數的參照, ,可以通過我們索引加閉包,或者累加器的形式來處理,為了方便直接使用累加器的形式即可,如下程式碼:

/*
  這個思路通過利用累加器函數的特性,返回一個函數
*/
class Interceptor {
  aspects = [];
  use (fn) {
    this.aspects.push(fn)
    return this
  }
  async run (context) {
    // 從右往左開始遍歷
    const proc = this.aspects.reduceRight(
      function (a, b) {
        let flag = false
        return async () => {
          // a 上一個fn,也就是呼叫的時傳入的 next
          if (flag) {
            return
          }
          flag = true
          await b(context, a)
        }
    },() => Promise.resolve())
    try {
      // 通過這個reduceRight 讓函數串起來了
      await proc()
    } catch (e) {
      console.error(e);
    }
  }
}

// 測試
const inter = new Interceptor()
inter.use(function a(context, next) {
  console.log("a");
  next();
  console.log("a_after");
});
inter.use(function b(context, next) {
  console.log("b");
  next();
  console.log("b_after");
});
inter.use(function c(context, next) {
  console.log("c");
  next();
  console.log("c_after");
});
inter.use(function d(context, next) {
  console.log("d");
  next();
  console.log("d_after");
});
inter.run();

輸出a、b、c、d、d_after、c_after、b_after、a_after 其中koa2 原始碼中使用了索引加閉包的形式來處理 原始碼

小結

  理解了nodejs 寫起來還是挺順手的,官網檔案也還好。至於其他如sql路由保持對談狀態等後臺基本知識點,看看相關檔案即可,並沒有什麼難度。

  nodejs 入門基本也結束了。