前端學習 node 快速入門 系列 —— 簡易版 Apache

彭加李發表於2021-03-14

其他章節請看:

前端學習 node 快速入門 系列

簡易版 Apache

我們用 node 來實現一個簡易版的 Apache:提供靜態資源訪問的能力

實現

直接上程式碼。

- demo
    - static            // 靜態資原始檔夾
        - index.html    // 主頁
        - 1.jpg         
        - 1.css
        - 1.js
    - index.js          // 入口檔案

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="1.css">
</head>
<body>
   <p>歡迎來到首頁</p>
   <img src="1.jpg" alt="">
   <section></section>
   <script src='1.js'></script>
</body>
</html>

1.js 和 1.css:

document.querySelector('section').innerHTML = '我來自js' // js

body{color:red} // css

index.js(入口檔案):

const http = require('http')
const fs = require('fs')
const server = http.createServer()

const requestListener = (req, res) => {
    const url = req.url
    // 如果url是 '/',則返回主頁
    if(url === '/'){
        fs.readFile('./static/index.html', (err, data) => {
            if (err) throw err;
            res.end(data)
        });
        return
    }

    // 如果url不是 '/',則返回資源(找不到資源則報 404)
    fs.readFile('./static/' + url, (err, data) => {
        if (err) {
            res.writeHead(404, {'Content-type':'text/html;charset=utf8'})
            res.end('沒有找到對應的資源')
        }
        res.end(data)
    })
}

server.on('request', requestListener)
server.listen('3000', () => {
    console.log('伺服器已啟動')
})

啟動伺服器:

$ node index

檢驗伺服器是否能提供靜態資源訪問的能力。

1. 瀏覽器輸入 http://localhost:3000/1.css

頁面顯示:body{color:red}

2. 瀏覽器輸入 http://localhost:3000/1.js

頁面顯示:document.querySelector('section').innerHTML = '鎴戞潵鑷猨s'(未指定編碼,所以中文亂碼。不礙事)

3. 瀏覽器輸入 http://localhost:3000/1.jpg
頁面顯示:1.jpg(正常顯示圖片)

3 種靜態資源都能正常響應。

訪問主頁:

瀏覽器輸入:http://localhost:3000/index.html 或 http://localhost:3000/

頁面顯示:
歡迎來到首頁
圖片
我來自js

主頁和其中的圖片、css 以及 js 配合的非常完美。

:中文亂碼的問題沒有出現。因為我們在 html 頁面中 使用了 <meta charset="utf-8"> 來指定編碼。

通常訪問不存在的資源會返回 404。請看:

瀏覽器輸入:http://localhost:3000/2.css

頁面顯示:
沒有找到對應的資源

擴充套件

至此,我們的簡易版 apache 其實已經大功告成。在此基礎之上,我們再擴充套件一下。

我的伺服器我做主

理解這一點很重要:這個伺服器完全由我們做主

現在所有的請求都會進入 requestListener() 方法,如果 url 是 '/',伺服器就返回主頁(index.html),否則就去 static 資料夾中讀取相應的資源,如果沒有找到對應的資源,就做 404 的處理。假如 requestListener() 是一個空方法,則說明我們的伺服器不提供任何服務。

所有的請求都是 url

不要把 http://localhost:3000/1.css 中的 1.css 當成檔案路徑,而要當成 url,因為所有的請求都是 url。請看示例:

const requestListener = (req, res) => {
    ...
    if(url.endsWith('.myCss')){
        url = url.replace('.myCss', '.css') // url 得宣告成 let
    }
    fs.readFile('./static/' + url, (err, data) => { // {1}
        ...
    })
}

在 index.js 的 fs.readFile('./static/'(行{1}) 上面增加三行程式碼。

瀏覽器輸入 http://localhost:3000/1.myCss

頁面顯示:body{color:red}

現在給伺服器傳送請求 http://localhost:3000/1.myCss,伺服器也會返回 1.css 檔案的內容。有些網站的 url 設計的非常優雅。請看:

http://product.dangdang.com/29200520.html

// 更優雅
https://www.douban.com/group/topic/214827461/

重定向

未登入的狀態去訪問頁面,通常會被重定向到首頁。我們來模擬一下:

if(url.endsWith('.myCss')){
    url = url.replace('.myCss', '.css') 
    res.writeHead(302, {'Location':'/'}) // {1}
    // 行{1} 等價於下面兩行
    // res.statusCode = '302'
    // res.setHeader('Location', '/') 
    
}

在 index.js 中增加 res.writeHead(302, {'Location':'/'})(行{1}),瀏覽器輸入 http://localhost:3000/1.myCss,頁面會重定向到主頁。

:沒有後端開發經驗的學習 node 會比較吃力。比如重定向,我們想到的可能是通過呼叫一個重定向的方法來實現,而 node 寫起來更底層。

302 是臨時重定向。301 是永久重定向。請看示例:

// 臨時重定向
Request URL: http://localhost:3000/1.myCss
Request Method: GET
Status Code: 302 Found

Request URL: http://localhost:3000/1.myCss
Request Method: GET
Status Code: 302 Found

// 永久重定向
Request URL: http://localhost:3000/1.myCss
Request Method: GET
Status Code: 301 Moved Permanently

Request URL: http://localhost:3000/1.myCss
Request Method: GET
Status Code: 301 Moved Permanently (from disk cache) // {1}

臨時重定向,瀏覽器每次都會去伺服器那裡;而永久重定向,第二次就不會去伺服器那裡,而是直接在瀏覽器端重定向過去(行{1})

自動重啟服務

每次修改入口檔案,都需要重新啟動伺服器(執行 node index),非常麻煩。

可以通過 nodemon 來幫助我們自動重啟服務。

// 全域性安裝 nodemon。
$ npm install --global nodemon

// 一定要全域性安裝 nodemon。否則執行 nodemon -v 會報錯。
$ nodemon -v

// 執行入口檔案。
$ nodemon index // {1}

使用 nodemon 非常簡單。通過 npm 全域性安裝後,用 nodemon 代替 node(行{1})執行入口檔案即可。

:筆者還嘗試了 supervisor,感覺沒有 nodemon 好用。比如我輸入 let a = (處在一個語法錯誤的狀態)然後儲存,supervisor 會退出服務,而 nodemon 只是報錯,仍會繼續監聽。

其他章節請看:

前端學習 node 快速入門 系列

相關文章