找回密码
 立即注册
首页 业界区 业界 Nodejs(含js模块化+npm+express)

Nodejs(含js模块化+npm+express)

左丘雅秀 3 天前
1. 简介

1.1 运行环境


  • 浏览器是 js 的前端运行环境
  • Node.js 是 js 的后端运行环境
  • Node.js 中无法调用 DOM 和 BOM 等浏览器内置 API
1.png
1.2 Node.js 可以做什么


  • 基于 Express 框架可以快速构建 Web 应用
  • 基于 Electron 框架可以快速构建跨平台的桌面应用
  • 基于 restify 框架可以快速构建 API 接口项目
  • 读取和操作数据库,创建实用的命令行工具辅助前端开发
  • ...
1.3 安装与运行


  • 下载稳定版node.js
  • 安装完查看 node.js 的版本
  1. node -v
复制代码

  • 创建测试文件,通过命令行运行(需要切换到文件所在目录)
  1. node test.js
复制代码
2.png

2. fs 文件系统模块

fs 模块是 Node.js 官方提供的用来操作文件的模块,提供了一系列的方法和属性,用来满足用户对文件的操作需求


  • 如果要在 js 代码中使用 fs 模块来操作文件,则需要先导入
  1. const fs = require("fs");
复制代码
2.1 读取指定文件中的内容


  • 使用fs.readFile()读取指定文件中的内容
  1. fs.readFile(path[, options), callback)
复制代码

  • 参数解读

    • path:必选,读取的文件路径(字符串)
    • options:可选,以什么编码格式来读取文件,默认指定utf8
    • callback:必选,文件读取完成后,通过回调函数拿到读取的失败和成功的结果,err 和 dataObj

  • 示例:
  1. const fs = require("fs");
  2. fs.readFile("./files/1.txt", "utf-8", function (err, dataObj) {
  3.         // 读取成功,err为null,否则为错误对象。因此能以此进行判断
  4.         if (err) {
  5.                 return console.log("文件读取失败!" + err.message);
  6.         }
  7.         // 读取成功的结果,失败则为undefined
  8.         console.log("文件读取成功,内容是:" + dataObj);
  9. });
复制代码
3.png

2.2 向指定文件中写入内容


  • 使用fs.writeFile()向指定文件写入内容
  1. fs.writeFile(file, data[, options], callback)
复制代码

  • 参数解读

    • file:必选,文件存放的路径(字符串)
    • data:必选,要写入的内容
    • options:可选,以什么格式写入文件内容,默认utf8
    • callback:必选,文件写入完成后的回调函数

  • 示例
  1. const fs = require("fs");
  2. fs.writeFile("F:/files/2.txt", "hello world", function (err) {
  3.         // 写入成功,err为null,否则为错误对象
  4.         if (err) {
  5.                 return console.log("写入文件失败!" + err.message);
  6.         }
  7.         console.log("文件写入成功!");
  8. });
复制代码
4.png

5.png

2.3 小练习


  • 需求:整理成绩.txt中的数据,并写入成绩-ok.txt
  • 源数据与期望格式数据如下:
6.png


  • 代码实现
  1. const fs = require("fs");
  2. fs.readFile("./files/成绩.txt", function (err, dataObj) {
  3.         if (err) {
  4.                 return console.log("文件读取失败!" + err.message);
  5.         }
  6.         let dataStr = dataObj.toString();
  7.         dataStr = dataStr.replaceAll("=", ":");
  8.         dataStr = dataStr.replaceAll(" ", "\n");
  9.         fs.writeFile("./files/成绩-ok.txt", dataStr, function (err) {
  10.                 if (err) {
  11.                         return console.log("文件写入失败!" + err.message);
  12.                 }
  13.         });
  14. });
复制代码
2.4 路径动态拼接的问题


  • 在使用 fs 模块操作文件时,如果使用相对路径,很容易出现动态路径拼接错误的问题
  • 原因:代码在运行时,会以执行 node 命令所处的目录,动态拼接出被操作文件的完整路径
  • 解决

    • 提供完整路径:移植性差,不利于维护
    • 使用__dirname_ + '/files/data.txt':__dirname表示当前文件所在的目录

  • 使用相对路径,并在文件所在目录上一级执行命令
7.png


  • 优化后的代码
  1. const fs = require("fs");
  2. fs.readFile(__dirname + "/files/data.txt", function (err, dataObj) {
  3.         if (err) {
  4.                 return console.log("文件读取失败!" + err.message);
  5.         }
  6.         console.log(dataObj.toString());
  7. });
复制代码
8.png

3. Path 路径模块

path 模块是 Node.js 官方提供的用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求


  • 如果要在 js 代码中使用 path 模块来处理路径,则需要先导入
  1. const path = require("path");
复制代码
3.1 路径拼接


  • 使用path.join()把多个路径片段拼接为完整的路径字符串
  1. path.join([...paths]);
复制代码

  • 参数解读

    • ...paths:路径片段的序列
    • 返回值:

  • 示例
  1. const fs = require("fs");
  2. const path = require("path");
  3. // ../ 会抵消一级路径
  4. const pathStr = path.join("/a", "/b/c", "../", "./d", "e");
  5. console.log(pathStr);
  6. fs.readFile(path.join(__dirname, "/files/data.txt"), function (err, dataObj) {
  7.         if (err) {
  8.                 return console.log("文件读取失败!" + err.message);
  9.         }
  10.         console.log(dataObj.toString());
  11. });
复制代码

  • 注:以后涉及路径拼接的操作,都要用path.join()进行处理,如果直接使用+进行拼接,可能会有问题,如下图所示
9.png
3.2 获取路径中的文件名


  • 使用path.basename()方法获取路径中的最后一部分,经常用它获取路径中的文件名
  1. path.basename(path[, ext])
复制代码

  • 参数解读

    • path:必选,表示一个路径的字符串
    • ext:可选,表示文件扩展名
    • 返回值:表示路径中的最后一部分

  • 示例
  1. const path = require("path");
  2. // 不加第二个参数,会连扩展名一起输出
  3. const fileName = path.basename("/a/b/c/index.html", ".html");
  4. console.log(fileName);
复制代码
10.png

3.3 获取路径中的文件扩展名


  • 使用path.extname()获取路径中的扩展名
  1. const path = require("path");
  2. const extName = path.extname("/a/b/c/index.html");
  3. console.log(extName);
复制代码

  • 参数解读

    • path:必选,表示路径字符串
    • 返回值:扩展名字符串

  • 示例
  1. const path = require("path");
  2. const extName = path.extname("/a/b/c/index.html");
  3. console.log(extName);
复制代码
3.4 小练习


  • 需求:将Clock.html拆分为三个文件,clock/index.html、clock/index.js、clock/index.css,并引入 css、js 文件(找一个含 html、css、js 的文件进行练习即可)
  • 思路

    • 设置正则表达式匹配和中的内容
    • 使用 fs 模块读取Clock.html文件
    • 编写三个方法处理 css、js、html 内容写入文件中

  • 目录结构
11.png


  • 代码实现
  1. const fs = require("fs");
  2. const path = require("path");
  3. // 先设置正则表达式,提取和的内容
  4. const regStyle = /", "");
  5.         fs.writeFile(path.join(__dirname, "./clock/index.css"), cssStr[0], function (err) {
  6.                 if (err) return console.log("文件写入失败!" + cssStr);
  7.         });
  8.         console.log("css文件写入成功!");
  9. }
  10. // 处理js
  11. function resolveJs(htmlStr) {
  12.         const jsStr = regScript.exec(htmlStr);
  13.         jsStr[0] = jsStr[0].replace("", "");
  14.         fs.writeFile(path.join(__dirname, "./clock/index.js"), jsStr[0], function (err) {
  15.                 if (err) return console.log("文件写入失败!" + jsStr);
  16.         });
  17.         console.log("js文件写入成功!");
  18. }
  19. // 处理html
  20. function resolveHtml(htmlStr) {
  21.         const newStr = htmlStr
  22.                 .replace(regStyle, '<link rel="stylesheet" href="index.css">')
  23.                 .replace(regScript, '');
  24.         fs.writeFile(path.join(__dirname, "./clock/index.html"), newStr, function (err) {
  25.                 if (err) console.log("文件写入失败!" + err.message);
  26.                 console.log("html文件写入成功!");
  27.         });
  28. }
复制代码

  • 两个注意点

    • fs.writeFile()只能用来创建文件,不能用来创建路径
    • 重复调用fs.writeFile()写入同一个文件,新写入的内容会覆盖之前的内容

4. http 模块

4.1 简介

http 模块是 Node.js 官方提供的用来创建 web 服务器的模块


  • 客户端:在网络节点中,负责消费资源的电脑
  • 服务器:负责对外提供网络资源的电脑
  • 服务器和普通电脑的区别在于:服务器上安装了 web 服务器软件,如 IIS、Apache 等,通过安装这些服务器软件,就能把一台普通的电脑变成一台 web 服务器
  • 在 Node.js 中不需要使用 IIS、Apache 等第三方 web 服务器软件,可以基于 Node.js 提供的 http 模块轻松手写一个服务器软件
4.2 创建最基本的 web 服务器


  • 导入
  1. const http = require("http");
复制代码

  • 调用http.createServer()创建 web 服务器实例
  1. const server = http.createServer();
复制代码

  • 为服务器实例绑定request事件,监听客户端的请求
  1. server.on("request", (req, res) => {
  2.         // 只要有客户端来请求服务器,就会触发request事件,从而调用这个事件处理函数
  3.         console.log("Someone visit our web server.");
  4. });
复制代码

  • 调用listen()启动当前 web 服务器实例
  1. server.listen("8080", () => {
  2.         console.log("http server running at http://127.0.0.1:8080");
  3. });
复制代码

  • 运行之后用浏览器访问该地址
12.png

4.3 req 请求对象


  • 只要服务器收到了客户端的请求,就会调用通过server.on()为服务器绑定的request事件处理函数
  • req是请求对象,包含了与客户端相关的数据和属性

    • req.url:客户端请求的 url 地址
    • req.method:客户端的 method 请求类型

  • 示例
  1. const http = require("http");const server = http.createServer();server.on("request", (req, res) => {        console.log(`Your request url is ${req.url}, and request method is ${req.method}`);});server.listen("8080", () => {
  2.         console.log("http server running at http://127.0.0.1:8080");
  3. });
复制代码
4.4 res 响应对象


  • res是响应对象,包含与服务器相关的数据和属性

    • res.end():向客户端发送指定的内容,并结束本次请求的处理过程

  • 示例
  1. const http = require("http");const server = http.createServer();server.on("request", (req, res) => {        const str = `Your request url is ${req.url}, and request method is ${req.method}`;        res.end(str);});server.listen("8080", () => {
  2.         console.log("http server running at http://127.0.0.1:8080");
  3. });
复制代码
13.png


  • 通过一些接口测试软件测试一下其他请求方式,此处使用Apifox
14.png
4.5 解决中文乱码问题

15.png


  • 当调用res.end()向客户端发送中文内容时,会出现乱码,此时需要手动设置内容的编码格式
  1. res.setHeader("Content-Type", "text-html; charset=utf-8");
复制代码

  • 示例
  1. const http = require("http");const server = http.createServer();server.on("request", (req, res) => {        const str = `您的请求地址是:${req.url},请求方式是:${req.method}`;        res.setHeader("Content-Type", "text/html; charset=utf-8");        res.end(str);});server.listen("8080", () => {
  2.         console.log("http server running at http://127.0.0.1:8080");
  3. });
复制代码
16.png

4.6 小练习

4.6.1 根据不同的 url 响应不同的 html 内容


  • 实现步骤

    • 获取请求的 url
    • 路径为/或/index.html,访问的是首页
    • 路径为/about.html,访问的是关于页面
    • 其他则显示404 Not Found
    • 设置Content-Type响应头,防止中文乱码
    • 使用res.end()响应给客户端

  • 代码实现
  1. const http = require("http");const server = http.createServer();server.on("request", (req, res) => {        let content = "[size=6]404 Not Found[/size]
  2. ";        console.log(req.url);        if (req.url === "/" || req.url === "/index.html") {                content = "[size=6]首页[/size]
  3. ";        } else if (req.url === "/about.html") {                content = "[size=6]关于[/size]
  4. ";        }        res.setHeader("Content-Type", "text/html; charset=utf-8");        res.end(content);});server.listen("8080", () => {
  5.         console.log("http server running at http://127.0.0.1:8080");
  6. });
复制代码
4.6.2 实现时钟的 web 服务器


  • 思路:把文件的实际存放路径,作为每个资源的请求 url 地址
17.png


  • 代码实现
  1. const http = require("http");const fs = require("fs");const path = require("path");const server = http.createServer();server.on("request", (req, res) => {        if (req.url !== "/favicon.ico") {                fs.readFile(path.join(__dirname, req.url), function (err, dataObj) {                        if (err) {                                return res.end(`[size=6]404 Not Found[/size]
  2. `);                        }                        res.end(dataObj.toString());                });        }});server.listen("8080", () => {
  3.         console.log("http server running at http://127.0.0.1:8080");
  4. });
复制代码

  • 优化资源请求路径

    • 访问/时默认也访问/clock/index.html
    • 简化路径输入/clock/index.html --> /index.html

  1. const http = require("http");const fs = require("fs");const path = require("path");const server = http.createServer();server.on("request", (req, res) => {        // 优化资源请求路径        let fpath = "";        if (req.url === "/") {                fpath = path.join(__dirname, "./clock/index.html");        } else {                fpath = path.join(__dirname, "/clock", req.url);        }        if (req.url !== "/favicon.ico") {                fs.readFile(fpath, function (err, dataObj) {                        if (err) {                                return res.end(`[size=6]404 Not Found[/size]
  2. `);                        }                        res.end(dataObj.toString());                });        }});server.listen("8080", () => {
  3.         console.log("http server running at http://127.0.0.1:8080");
  4. });
复制代码
5. js 模块化规范

5.1 模块化概述

5.1.1 什么是模块化


  • 将程序⽂件依据⼀定规则拆分成多个⽂件,这种编码⽅式就是模块化的编码方式
  • 拆分出来每个⽂件就是⼀个模块,模块中的数据都是私有的,模块之间互相隔离
  • 同时也能通过一些手段,可以把模块内的指定数据“交出去”,供其他模块使用
5.1.2 为什么需要模块化


  • 随着应用的复杂度越来越高,其代码量和文件数量都会急剧增加,会逐渐引发以下问题:

    • 全局污染问题
    • 依赖混乱问题
    • 数据安全问题

  • 好处

    • 复用性
    • 可维护性
    • 可实现按需加载

5.2 有哪些模块化规范


  • CommonJS——服务端应用广泛
  • AMD(了解)
  • CMD(了解)
  • ES6 模块化——浏览器端应用广泛
5.3 导入和导出的概念

模块化的核心思想就是:模块之间是隔离的,通过导入和导出进行数据和功能的共享


  • 导出(暴露):模块公开其内部的⼀部分(如变量、函数等),使这些内容可以被其他模块使用
  • 导入(引入):模块引入和使用其他模块导出的内容,以重用代码和功能
18.png

5.4 Node.js 中的模块化

5.4.1 分类


  • 根据来源的不同,分为三大类

    • 内置模块:如 fs、path、http 等
    • 自定义模块:用户创建的每个.js文件都是自定义模块
    • 第三方模块:由第三方开发出来的模块,使用前需要提前下载

5.4.2 加载模块
  1. // 1、加载内置的fs模块
  2. const fs = require("fs");
  3. // 2、加载自定义模块,.js后缀可省略
  4. const custom = require("./custom.js");
  5. // 3、加载第三方模块
  6. const moment = require("moment");
复制代码
5.4.3 模块作用域与 module 对象


  • 模块作用域:只能在当前模块内被访问
  • 好处:防止全局变量污染问题
  • module 对象:每个.js自定义模块中都有一个module对象,里面存储了和当前模块有关的信息
19.png
5.5 CommonJS 规范

Node.js 遵循了 CommonJS 模块化规范,CommonJS 规定了模块的特性和各模块之间如何相互依赖


  • CommonJS 规定

    • 每个模块内部,module 变量代表当前模块
    • module 变量是一个对象,其exports属性(即module.exports)是对外的接口
    • 加载某个模块,其实就是加载该模块的module.exports属性,require()方法用于加载模块

5.5.1 初步体验


  • school.js
  1. const name = "尚硅谷";
  2. const slogan = "让天下没有难学的技术!";
  3. function getTel() {
  4.         return "010-56253825";
  5. }
  6. function getCities() {
  7.         return ["北京", "上海", "深圳", "成都", "武汉", "西安"];
  8. }
  9. // 通过给exports对象添加属性的方式,来导出数据
  10. // 此处不导出getCities
  11. exports.name = name;
  12. exports.slogan = slogan;
  13. exports.getTel = getTel;
复制代码

  • student.js
  1. const name = "张三";
  2. const motto = "相信明天会更好!";
  3. function getTel() {
  4.         return "13877889900";
  5. }
  6. function getHobby() {
  7.         return ["抽烟", "喝酒", "烫头"];
  8. }
  9. // 通过给exports对象添加属性的方式,来导出数据
  10. // 此处不导出getHobby
  11. exports.name = name;
  12. exports.motto = motto;
  13. exports.getTel = getTel;
复制代码

  • index.js
  1. // 引入school模块暴露的所有内容
  2. const school = require("./school.js");
  3. // 引入student模块暴露的所有内容
  4. const student = require("./student.js");
  5. console.log(school);
  6. console.log(student);
复制代码
5.5.2 导出数据


  • 在CommonJS标准中,导出数据有两种方式:

    • 第一种方式:module.exports = value
    • 第二种方式:exports.name = value

  • 注:

    • 每个模块内部的:this、exports、modules.exports在初始时,都指向同一个空对象,该空对象就是当前模块导出的数据,如下图:
    20.png


    • 无论如何修改导出对象,最终导出的都是module.exports的值
    • exports是对module.exports的初始引用,仅为了方便给导出添加属性,所以不能用exports={}的形式导出数据,但是可以用module.exports={}导出数据
    • 注:为了防止混乱,建议不要在同一模块中同时使用exports和module.exports

  • school.js
  1. const name = "尚硅谷";
  2. const slogan = "让天下没有难学的技术!";
  3. function getTel() {
  4.         return "010-56253825";
  5. }
  6. function getCities() {
  7.         return ["北京", "上海", "深圳", "成都", "武汉", "西安"];
  8. }
  9. module.exports = { name, slogan, getTel };
  10. // this.c =789
  11. // exports = {a:1}
  12. // exports.b = 2
  13. // module.exports.c = 3
  14. // module.exports = {d:4}        // 最终导出成功的是这个
  15. // console.log(this)
  16. // console.log(exports)
  17. // console.log(module.exports)
  18. // console.log(this === exports && exports === module.exports)
  19. exports.name = name;
  20. exports.slogan = slogan;
  21. exports.getTel = getTel;
复制代码

  • 解释

    • 一开始module.exports和exports指向同一个空对象
    • exports = {a:1}:exports就指向了{a:1}这个新对象,module.exports仍指向空对象
    • exports.b = 2:向exports指向的对象添加属性b
    • module.exports.c = 3:向module.exports指向的对象添加属性c
    • module.exports = {d:4}:module.exports指向了新对象{d:4}
    • 无论如何修改导出对象,最终导出的都是module.exports的值

21.png

5.5.3 导入数据

在 CJS 模块化标准中,使用内置的 require 函数进行导入数据
  1. //直接引入模块
  2. const school = require("./school.js");
  3. //引入同时解构出要用的数据
  4. const { name, slogan, getTel } = require("./school.js");
  5. //引入同时解构+重命名
  6. const { name: stuName, motto, getTel: stuTel } = require("./student.js");
复制代码
5.5.4 扩展理解


  • 一个 JS 模块在执行时,是被包裹在一个内置函数中执行的,所以每个模块都有自己的作用域,可以通过如下方式验证这一说法:
  1. console.log(arguments);
  2. console.log(arguments.callee.toString());
复制代码

  • 内置函数的大致形式如下:
  1. function (exports, require, module, __filename, __dirname){
  2.         /**************************/
  3. }
复制代码
5.5.5 浏览器端运行


  • Node.js 默认是支持 CommonJS 规范的,但浏览器端不支持,所以需要经过编译,步骤如下:

    • 第一步:全局安装 browserify
    1. npm i browserify -g
    复制代码

    • 第二步:编译
    1. browserify index.js -o build.js
    复制代码

    • 注:index.js 是源文件,build.js 是输出的目标文件
    • 第三步:页面中引入使用
    1. [/code]
    2. [/list][size=4]5.6 ES6 模块化规范[/size]
    3. [indent]ES6 模块化规范是一个官方标准的规范,它是在语言标准的层面上实现了模块化功能,是目前最流行的模块化规范,且浏览器与服务端均支持该规范
    4. [/indent][size=3]5.6.1 初步体验[/size]
    5. [list]
    6. [*]school.js
    7. [/list][code]// 导出name
    8. export const name = "尚硅谷";
    9. // 导出slogan
    10. export const slogan = "让天下没有难学的技术!";
    11. // 导出getTel
    12. export function getTel() {
    13.         return "010-56253825";
    14. }
    15. function getCities() {
    16.         return ["北京", "上海", "深圳", "成都", "武汉", "西安"];
    17. }
    复制代码

    • student.js
    1. export const name = "张三";
    2. export const motto = "相信明天会更好!";
    3. export function getTel() {
    4.         return "13877889900";
    5. }
    6. function getHobby() {
    7.         return ["抽烟", "喝酒", "烫头"];
    8. }
    复制代码

    • index.js
    1. // 引入school模块暴露的所有内容
    2. import * as school from "./school.js";
    3. // 引入student模块暴露的所有内容
    4. import * as student from "./student.js";
    复制代码

    • 页面中引入 index.js
    1. [/code][size=3]5.6.2 Node 中运行 ES6 模块[/size]
    2. [list]
    3. [*]Node.js 中运行 ES6 模块代码有两种方式:
    4. [list]
    5. [*]方式一:将 JavaScript 文件后缀从.js改为.mjs,Node 则会自动识别 ES6 模块
    6. [*]方式二:在package.json中设置type属性值为module
    7. [/list]
    8. [/list][align=center] 22.png [/align][size=3]5.6.3 导出数据[/size]
    9. [indent]ES6 模块化提供 3 种导出方式:① 分别导出、② 统一导出、③ 默认导出
    10. [/indent]
    11. [list]
    12. [*]分别导出
    13. [/list][code]// 导出name
    14. export const name = "尚硅谷";
    15. // 导出slogan
    16. export const slogan = "让天下没有难学的技术!";
    17. // 导出getTel
    18. export function getTel() {
    19.         return "010-56253825";
    20. }
    复制代码

    • 统一导出
    1. const name = "尚硅谷";
    2. const slogan = "让天下没有难学的技术!";
    3. function getTel() {
    4.         return "010-56253825";
    5. }
    6. function getCities() {
    7.         return ["北京", "上海", "深圳", "成都", "武汉", "西安"];
    8. }
    9. // 统一导出了:name、slogan、getTel
    10. export { name, slogan, getTel };
    复制代码

    • 默认导出
    1. const name = "尚硅谷";
    2. const slogan = "让天下没有难学的技术!";
    3. function getTel() {
    4.         return "010-56253825";
    5. }
    6. function getCities() {
    7.         return ["北京", "上海", "深圳", "成都", "武汉", "西安"];
    8. }
    9. //默认导出了:name、slogan、getTel
    10. export default { name, slogan, getTel };
    复制代码

    • 注:上述多种导出方式,可以同时使用
    1. // 导出name —— 分别导出
    2. export const name = "尚硅谷";
    3. const slogan = "让天下没有难学的技术!";
    4. function getTel() {
    5.         return "010-56253825";
    6. }
    7. function getCities() {
    8.         return ["北京", "上海", "深圳", "成都", "武汉", "西安"];
    9. }
    10. // 导出slogan —— 统一导出
    11. export { slogan };
    12. // 导出getTel —— 默认导出
    13. export default getTel;
    复制代码
    5.6.4 导入数据

    对于 ES6 模块化来说,使用何种导入方式,要根据导出方式决定

    来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册