Node.js 入门学习

github地址: Node.js-Study

1. node 基础

  1. Node.js 的特点: 事件驱动、异步I/O(在处理高并发、异步I/O密集场景性能优势明显)。
    Web场景下性能好。
  2. exports 只能使用.语法向外暴露内部变量, 例 exports.test = 100;
    module.exports既可以通过点语法,也可以直接赋值一个对象 例 module.exports.test = 100; module.exports = { test: 100}

exports:首先对于本身来讲是一个变量(对象),它不是module的引用,它是{}的引用,它指向module.exports的{}模块

  1. path: __dirname, __filename 总是返回文件的绝对路径;process.cwd()

2. 初步构建静态资源服务器

  1. 目录结构
    在这里插入图片描述
    说明: index.js 为主入口,helper文件为基本辅助功能,其中router.js 为主要文件,config下为默认配置选项。
  2. 代码片段
    需要下载 npm install chalkhandlebars 安装包。

index.js:入口文件

const http = require('http');
const chalk = require('chalk');
const path = require('path');
const conf = require('./config/defaultConfig.js');
const route = require('./helper/router.js');

const server = http.createServer((req, res) => {
  //  NOTE: 获取用户当前文件夹
  const filePath = path.join(conf.root, req.url);
  route(req, res, filePath);
});

server.listen(conf.port, conf.hostname, () => {
  const addr = `http://${conf.hostname}:${conf.port}/`
  console.log(`Server running at: ${chalk.green(addr)}`);
});
//  supervisor src/index.js 热更新

defaultConfig.js : 默认配置项

const hostname = '127.0.0.1';
const port = 3000;

module.exports = {
  hostname,
  port,
  root: process.cwd(), // 当前工作目录。即命令行的执行时的路径
  compress: /\.(html|js|css|md)/,  // 设置支持的文件后缀
  cache: {
    maxAge: 600,
    expires: true,
    cacheControl: true,
    lastModified: true,
    etag: true,
  }
}

router.js 包含请求的基本处理,压缩,缓存

const fs = require('fs');
const path = require('path');
const Handlebars = require('handlebars');
const promisify = require('util').promisify;
const stat = promisify(fs.stat);
const readdir = promisify(fs.readdir);
const conf = require('../config/defaultConfig.js');
const mime = require('./mime.js');
const compress = require('./compress');
const range = require('./range');
const isFresh = require('./cache');


const tplPath = path.join(__dirname, '../template/dir.tpl'); //  处理路径最好用绝对路径
const source = fs.readFileSync(tplPath);
const template = Handlebars.compile(source.toString()); // source 是Buffer对象

module.exports = async function (req, res, filePath) {
  try {
  //  是文件夹 返回目录,是文件返回内容
    const stats = await stat(filePath);
    if (stats.isFile()) { // 文件
      const contentType = mime(filePath); // 不同后缀实行不同的编译模式
      res.setHeader('Content-Type', contentType);
      //  有缓存并检查缓存是否失效
      if (isFresh(stats, req, res)) {
        res.statusCode = 304;
        res.end()
        return
      }
      let rs;
      const { code, start, end } = range(stats.size, req, res);
      if (code === 200) {
      // 无法处理的范围
        res.statusCode = 200;
        rs = fs.createReadStream(filePath);
      } else {
        res.statusCode = 206;
        rs = fs.createReadStream(filePath, {start, end} );
      }

      if (filePath.match(conf.compress)) { // 符合条件的进行压缩
        rs = compress(rs, req, res);
      }
      rs.pipe(res); //  把文件的内容返回
    } else if (stats.isDirectory()) { // 文件夹
      const files = await readdir(filePath);
      res.statusCode = 200 ;
      res.setHeader('Content-Type', 'text/html');
      const dir = path.relative(conf.root, filePath); // 一个文件相对于另一个文件的路径
      const data = {
        files,
        title: path.basename(filePath),
        dir: dir ? `/${dir}` : '',
      }
      res.end(template(data));
    }
  } catch (ex) {
      console.log('---- 报错了', ex);
      res.statusCode = 404;
      res.setHeader('Content-Type', 'text/plain');
      res.end(`${ex.toString()} is not a directory or file`);
  }
}

cache.js: 缓存文件处理

//  根据key值来更新响应
const { cache } = require('../config/defaultConfig');

function refreshRes (stats, res) {
  const {maxAge, expires, cacheControl, lastModified, etag} = cache;
  if (expires) {
    res.setHeader('expires',new Date(Date.now()+ maxAge * 1000 ).toUTCString())
  }
  if (cacheControl) {
    res.setHeader('Catch-Control',`public, max-age=${maxAge}`) // 公共资源 最大过期时间
  }
  if (lastModified) {
    res.setHeader('Last-Modified', stats.mtime.toUTCString()) // mtime 修改时间
  }
  if (etag) {
    res.setHeader('ETag', `${stats.size}-${stats.mtime}`)
  }
}

module.exports = (stats, req, res) => {
  refreshRes(stats, res);
  const lastModified = req.headers['if-modified-since'];
  const etag = req.headers['if-none-match'];
  if (!lastModified && !etag) { // 第一次请求,没有缓存
    return false
  }
  if (lastModified && lastModified !== res.getHeader('Last-Modified')) { // 有Last-Modified 但是不相等
    return false
  }
  if (etag && etag !== res.getHeader('ETag')) { // 有ETag 但是不相等
    return false
  }
  return true
}

range.js: 文件范围处理

module.exports = (totalSize, req, res) => {
  const range = req.headers['range'];
  if (!range) {
    return { code: 200 }
  }
  const sizes = range.match(/bytes=(\d*)-(\d*)/);
  const start = sizes[1] || totalSize - end;
  const end = sizes[2] || totalSize - 1;
  if (start > end || start < 0 || end > totalSize) {
    return { code: 200 }
  }
  //  可以处理的情况
  res.setHeader('Accept-Ranges','bytes');
  res.setHeader('Content-Range',`bytes ${start}-${end}/${totalSize}`);
  res.setHeader('Content-Length',end - start );
  return {
    code: 206,
    start: parseInt(start),
    end: parseInt(end),
  }
}

// curl -r 0-120 -i http://127.0.0.1:3000/LISTENCETEN

mime.js:类型列表

const path = require('path');
const mimeTypes = {
  'css': 'text/css',
  'gif': 'image/gif',
  'jpg': 'image/jpg',
  'png': 'image/png',
  'js': 'text/javascript',
  'json': 'application/json',
  'pdf': 'application/pdf',
  'txt': 'text/plain',
}

module.exports = (filePath) => {
  let ext = path.extname(filePath)
    .split('.')
    .pop()
    .toLowerCase();  // 取以 . 结尾的最后内容

  if (!ext) {
    ext = filePath
  }
  return mimeTypes[ext] || mimeTypes['txt'];
}

compress.js: 适合的文件进行压缩传送

const {createGzip, createDeflate} = require('zlib');

module.exports = (rs, req, res) => {
  const acceptEncoding = req.headers['accept-encoding']; // 接受的压缩类型
  if (!acceptEncoding || !acceptEncoding.match(/\b(gzip|deflate)\b/)) { // 浏览器不支持压缩 或者 服务器不支持的格式
    return rs;
  } else if (acceptEncoding.match(/\bgzip\b/)) {
    res.setHeader('Content-Encoding','gzip');
    return rs.pipe(createGzip());
  } else if (acceptEncoding.match(/\bdeflate\b/)) {
    res.setHeader('Content-Encoding','deflate');
    return rs.pipe(createDeflate());
  }
}

dir.tpl : 模版文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{title}}</title>
  <style>
    body {
      margin: 30px 30px;
    }
    a {
      display: block;
      font-size: 26px;
      margin-top: 10px;
    }
  </style>
</head>
<body>
  {{#each files}}
    <a href="{{../dir}}/{{this}}">{{this}}</a>
  {{/each}}
</body>
</html>

npm操作

加上执行权限: chmod +x static_Service/bin/any-document
查看: ls -al static_Service/bin/any-document
调用: static_Service/bin/any-document -p 9999

安装:npm install -g nrm
查看npm源:nrm ls
切换源:nrm use [name]

pagckage.json中配置 "bin": { "any-document": "bin/any-document" },
登陆npm: npm login
发布 npm: npm publish

3. glup打包

简单正则匹配

*: 匹配任意字符;
?: 匹配一个字符;
[…]:匹配任意范围内的字符;
!(pattern1 | pattern2) :匹配取反;
?(pattern1 | pattern2) :匹配0或1个;
+(pattern1 | pattern2) :匹配1或多个;
*(a|b|c) 匹配任意个;
@(pattern | pat* | pat?erN) 匹配特定的一个;
** 任意层级匹配;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值