Heim  >  Artikel  >  Web-Frontend  >  Implementierung des statischen Ressourcenservers von Node.js (mit Code)

Implementierung des statischen Ressourcenservers von Node.js (mit Code)

不言
不言nach vorne
2019-03-08 17:14:542443Durchsuche

Der Inhalt dieses Artikels befasst sich mit der statischen Ressourcenserverimplementierung von Node.js (mit Code). Ich hoffe, dass er für Sie hilfreich ist.

Dieser Artikel stellt ein einfaches Beispielprojekt für einen statischen Ressourcenserver vor und hofft, Node.js-Anfängern zu helfen. Das Projekt umfasst http, fs, url, path, zlib, process, child_process und deckt eine große Anzahl häufig verwendeter APIs ab. Außerdem umfasst es die Auswahl der Caching-Strategie basierend auf dem http-Protokoll, der Optimierung der gzip-Komprimierung usw.; Veröffentlichen Sie es auf npm und machen Sie es zu einem kleinen Tool, das global installiert und verwendet werden kann. Obwohl der Spatz klein ist, hat er alle inneren Organe. Macht es Sie nicht ein wenig aufgeregt, wenn Sie darüber nachdenken? Kommen wir ohne weitere Umschweife zur Sache.

Die Quellcodeadresse im Artikel befindet sich im letzten Anhang.
Sie können den Projekteffekt zuerst erleben:
Installation: npm i -g hier11
Geben Sie den Befehl an einer beliebigen Ordneradresse ein: hier

Schritt 1 Erstellen Sie ein neues Projekt

Da wir auf npm veröffentlichen möchten, folgen wir zunächst der internationalen Praxis, npm init, los geht's! Sie können in der Befehlszeile die Eingabetaste drücken. Einige Konfigurationen werden in den letzten Veröffentlichungsschritten detailliert beschrieben.

Die Verzeichnisstruktur ist wie folgt:

Implementierung des statischen Ressourcenservers von Node.js (mit Code)

Der Ordner bin speichert unseren Ausführungscode und das Web wird als Testordner mit einigen Webseiten verwendet Es.

Schritt2-Code

Schritt2.1-Prototyp

Statischer Ressourcenserver, für Laien geben wir die Form „http://Domänenname/test/“ in den Browser ein Adressleiste index.html“, der Server findet index.html im entsprechenden Ordner im Stammverzeichnis, liest den Dateiinhalt und gibt ihn an den Browser zurück, und der Browser rendert ihn an den Benutzer.

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

const item = (name, parentPath) => {
    let path = parentPath = `${parentPath}/${name}`.slice(1);
    return `<p><a>${name}</a></p>`;
}

const list = (arr, parentPath) => {
    return arr.map(name => item(name, parentPath)).join("");
}

const server = http.createServer((req, res) => {
    let _path = url.parse(req.url).pathname;//去掉search
    let parentPath = _path;
    _path = path.join(__dirname, _path);
    try {
        //拿到路径所对应的文件描述对象
        let stats = fs.statSync(_path);
        if (stats.isFile()) {
            //是文件,返回文件内容
            let file = fs.readFileSync(_path);
            res.end(file);
        } else if (stats.isDirectory()) {
            //是目录,返回目录列表,让用户可以继续点击
            let dirArray = fs.readdirSync(_path);
            res.end(list(dirArray, parentPath));
        } else {
            res.end();
        }
    } catch (err) {
        res.writeHead(404, "Not Found");
        res.end();
    }
});

const port = 2234;
const hostname = "127.0.0.1";
server.listen(port, hostname, () => {
    console.log(`server is running on http://${hostname}:${port}`);
});

Der obige Code ist unser Kerncode. Wenn Sie ihn lokal ausführen, können Sie sehen, dass das Dateiverzeichnis zurückgegeben wurde, um die entsprechenden Webseiten zu durchsuchen , Bilder und Texte.

Schritt 2.2 Optimierung Die Funktion

wurde implementiert, aber wir können sie in einigen Aspekten optimieren, um die Praktikabilität zu verbessern und mehr APIs zu erlernen (Fähigkeiten unter Beweis zu stellen).

1. Stream

Unser aktueller Vorgang, Dateien zu lesen und an den Browser zurückzugeben, besteht darin, sie einmal auszulesen und sie alle auf einmal über readFile zurückzugeben. Dies kann die Funktion sicherlich erreichen, aber Wir haben eine bessere Methode: Verwenden Sie Stream, um E/A-Vorgänge auszuführen. Stream ist kein einzigartiges Konzept von node.js, sondern die grundlegendste Betriebsform des Betriebssystems. Theoretisch implementiert also jede serverseitige Sprache die Stream-API.

Warum ist die Verwendung von Stream eine bessere Möglichkeit? Da das gleichzeitige Lesen und Bearbeiten großer Dateien Speicher und Netzwerk beansprucht, insbesondere wenn die Anzahl der Benutzerbesuche mit Hilfe von Streams relativ groß ist, können Daten Stück für Stück fließen und verarbeitet werden, wodurch die Leistung verbessert wird. Die Codeänderung ist wie folgt:

if (stats.isFile()) {
    //是文件,返回文件内容
    //在createServer时传入的回调函数被添加到了"request"事件上,回调函数的两个形参req和res
    //分别为http.IncomingMessage对象和http.ServerResponse对象
    //并且它们都实现了流接口
    let readStream = fs.createReadStream(_path);
    readStream.pipe(res);
}

Die Codierungsimplementierung ist sehr einfach. Wenn wir den Dateiinhalt zurückgeben müssen, erstellen wir einen lesbaren Stream und leiten ihn an das res-Objekt weiter.

2. gzip-Komprimierung

Die durch die gzip-Komprimierung erzielte Leistungsverbesserung (Benutzerzugriffserfahrung) ist sehr offensichtlich, insbesondere in der aktuellen Ära beliebter Spa-Anwendungen kann die Aktivierung der gzip-Komprimierung die Leistung erheblich reduzieren Die Größe von Dateiressourcen wie js und css verbessert die Benutzerzugriffsgeschwindigkeit. Als statischer Ressourcenserver müssen wir diese Funktion natürlich hinzufügen.

Es gibt ein zlib-Modul im Knoten, das viele komprimierungsbezogene APIs bereitstellt. Wir verwenden es zur Implementierung von:

const zlib = require("zlib");

if (stats.isFile()) {
    //是文件,返回文件内容

    res.setHeader("content-encoding", "gzip");
    
    const gzip = zlib.createGzip();
    let readStream = fs.createReadStream(_path);
    readStream.pipe(gzip).pipe(res);
}

Mit der Erfahrung in der Verwendung von Stream schauen wir uns diesen Code noch einmal an Dann wird es leichter zu verstehen sein. Leiten Sie den Dateistream zuerst zum gzip-Objekt und dann zum res-Objekt. Darüber hinaus müssen Sie bei der Verwendung der gzip-Komprimierung auf eines achten: Sie müssen die Inhaltskodierung im Antwortheader auf gzip setzen. Andernfalls zeigt der Browser eine Reihe verstümmelter Zeichen an.

3. HTTP-Caching ist etwas, das die Leute lieben und hassen. Bei richtiger Anwendung kann es die Benutzererfahrung verbessern und den Serverdruck verringern Arten von seltsamen Fragen. Im Allgemeinen ist der Browser-HTTP-Cache in einen starken Cache (Nicht-Verifizierungscache) und einen Verhandlungscache (Verifizierungscache) unterteilt.

Was ist starkes Caching? Starkes Caching wird durch zwei Header-Felder gesteuert: Cache-Kontrolle und Ablaufdatum. Heutzutage wird im Allgemeinen die Cache-Kontrolle verwendet. Beispielsweise legen wir den Antwortheader von Cache-Control fest: max-age=31536000, der dem Browser mitteilt, dass diese Ressource einen einjährigen Cache-Zeitraum hat. Innerhalb eines Jahres besteht keine Notwendigkeit, eine Anfrage an den Server zu senden Die Ressource wird direkt aus dem Cache gelesen.

Der ausgehandelte Cache verwendet Header-Felder wie „if-modified-since/last-modified“, „if-none-match/etag“ usw. in Verbindung mit starkem Cache teilt dem Browser „no-cache“ mit), sendet eine Anfrage an den Server, bestätigt die Gültigkeit der Ressource und entscheidet, ob sie aus dem Cache liest oder eine neue Ressource zurückgibt.

Mit den oben genannten Konzepten können wir unsere Caching-Strategie formulieren:

if (stats.isFile()) {
    //是文件,返回文件内容
    
    //增加判断文件是否有改动,没有改动返回304的逻辑
    
    //从请求头获取modified时间
    let IfModifiedSince = req.headers["if-modified-since"];
    //获取文件的修改日期——时间戳格式
    let mtime = stats.mtime;
    //如果服务器上的文件修改时间小于等于请求头携带的修改时间,则认定文件没有变化
    if (IfModifiedSince && mtime <p>这样一套缓存策略在现代前端项目体系下还是比较合适的,尤其是对于spa应用来讲。我们希望index.html能够保证每次向服务器验证是否有更新,而其余的文件统一本地缓存一个月(自己定);通过webpack打包或其他工程化方式构建之后,js、css内容如果发生变化,文件名相应更新,index.html插入的manifest(或script链接、link链接等)清单会更新,保证用户能够实时得到最新的资源。</p><p>当然,缓存之路千万条,适合业务才重要,大家可以灵活制定。</p><h4>4. 命令行参数</h4><p>作为一个在命令行执行的工具,怎么能不象征性的支持几个参数呢?</p><pre class="brush:php;toolbar:false">const config = {
    //从命令行中获取端口号,如果未设置采用默认
    port: process.argv[2] || 2234,
    hostname: "127.0.0.1"
}
server.listen(config.port, config.hostname, () => {
    console.log(`server is running on http://${config.hostname}:${config.port}`);
});

这里就简单的举个栗子啦,大家可以自由发挥!

5. 自动打开浏览器

虽然没太大卵用,但还是要加。我就是要让你们知道,我加完之后什么样,你们就是什么样 :-( duang~

const exec = require("child_process").exec;
server.listen(config.port, config.hostname, () => {
    console.log(`server is running on http://${config.hostname}:${config.port}`);
    exec(`open http://${config.hostname}:${config.port}`);
});

6. process.cwd()

用process.cwd()代替__dirname。
我们最终要做成一个全局并且可以在任意目录下调用的命令,所以拼接path的代码修改如下:

//__dirname是当前文件的目录地址,process.cwd()返回的是脚本执行的路径
_path = path.join(process.cwd(), _path);

step3 发布

基本上我们的代码都写完了,可以考虑发布了!(不发布到npm上何以显示逼格?)

step3.1 package.json

得到一个配置类似下面所示的json文件:

{
    "name": "here11",
    "version": "0.0.13",
    "private": false,
    "description": "a node static assets server",
    "bin": {
        "here": "./bin/index.js"
    },
    "repository": {
        "type": "git",
        "url": "https://github.com/gww666/here.git"
    },
    "scripts": {
        "test": "node bin/index.js"
    },
    "keywords": [
        "node"
    ],
    "author": "gw666",
    "license": "ISC"
}

其中bin和private较为重要,其余的按照自己的项目情况填写。
bin这个配置代表的是npm i -g xxx之后,我们运行here命令所执行的文件,“here”这个名字可以随意起。

step3.2 声明脚本执行类型

在index.js文件的开头加上:#!/usr/bin/env node
否则linux上运行会报错。

step3.3 注册npm账号

勉强贴一手命令,还不清楚自行百度:

没有账号的先添加一个,执行:
npm adduser

然后依次填入
Username: your name
Password: your password
Email: yourmail

npm会给你发一封验证邮件,记得点一下,不然会发布失败。

执行登录命令:
npm login

执行发布命令:
npm publish

发布的时候记得把项目名字、版本号、作者、仓库啥的改一下,别填成我的。
还有readme文件写一下,好歹告诉别人咋用,基本上和文首所说的用法是一样的。

好了,齐活。

step3.4

还等啥啊,赶快把npm i -g xxx 这行命令发给你的小伙伴啊。什么?你没有小伙伴?告辞!

本文项目源码地址:https://github.com/gww666/here

Das obige ist der detaillierte Inhalt vonImplementierung des statischen Ressourcenservers von Node.js (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen