缓存这个东西让人又爱又恨,用得好,可以提升用户体验,减轻服务器压力;用得不好,可能就会面临各种各样奇奇怪怪的问题。一般来讲浏览器http缓存分为强缓存(非验证性缓存)和协商缓存(验证性缓存)。
什么叫强缓存呢?强缓存是由cache-control和expires两个首部字段控制的,现在一般用cache-control。比如我们设置了cache-control: max-age=31536000的响应头,就是告诉浏览器这个资源有一年的缓存期,一年内不用向服务端发送请求,直接从缓存中读取资源。
而协商性缓存是使用if-modified-since/last-modified、if-none-match/etag等首部字段,配合强缓存,在强缓存没有命中(或告知浏览器no-cache)的时候,向服务器发送请求,确认资源的有效性,决定从缓存中读取或是返回新的资源。
有了以上概念,我们便可以制定我们的缓存策略:
if (stats.isFile()) { //是文件,返回文件内容 //增加判断文件是否有改动,没有改动返回304的逻辑 //从请求头获取modified时间 let IfModifiedSince = req.headers["if-modified-since"]; //获取文件的修改日期——时间戳格式 let mtime = stats.mtime; //如果服务器上的文件修改时间小于等于请求头携带的修改时间,则认定文件没有变化 if (IfModifiedSince && mtime <= new Date(IfModifiedSince).getTime()) { //返回304 res.writeHead(304, "not modify"); return res.end(); } //第一次请求或文件被修改后,返回给客户端新的修改时间 res.setHeader("last-modified", new Date(mtime).toString()); res.setHeader("content-encoding", "gzip"); let reg = /\.html$/; //不同的文件类型设置不同的cache-control if (reg.test(_path)) { //我们对html文件执行每次必须向服务器验证资源有效性的策略 res.setHeader("cache-control", "no-cache"); } else { //我们对其余的静态资源文件采取强缓存策略,一个月内无需向服务器索取 res.setHeader("cache-control", `max-age=${1 * 60 * 60 * 24 * 30}`); } //执行gzip压缩 const gzip = zlib.createGzip(); let readStream = fs.createReadStream(_path); readStream.pipe(gzip).pipe(res); }
这样一套缓存策略在现代前端项目体系下还是比较合适的,尤其是对于spa应用来讲。我们希望index.html能够保证每次向服务器验证是否有更新,而其余的文件统一本地缓存一个月(自己定);通过webpack打包或其他工程化方式构建之后,js、css内容如果发生变化,文件名相应更新,index.html插入的manifest(或script链接、link链接等)清单会更新,保证用户能够实时得到最新的资源。
当然,缓存之路千万条,适合业务才重要,大家可以灵活制定。
4. 命令行参数
作为一个在命令行执行的工具,怎么能不象征性的支持几个参数呢?
const config = { //从命令行中获取端口号,如果未设置采用默认 port: process.argv[2] || 2234, hostname: "127.0.0.1" } server.listen(config.port, config.hostname, () => { console.log(`server is running on ${config.hostname}:${config.port}`); });
这里就简单的举个栗子啦,大家可以自由发挥!
5. 自动打开浏览器
虽然没太大卵用,但还是要加。我就是要让你们知道,我加完之后什么样,你们就是什么样 :-( duang~
const exec = require("child_process").exec; server.listen(config.port, config.hostname, () => { console.log(`server is running on ${config.hostname}:${config.port}`); exec(`open ${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