03_Node.js模块化开发

1 Node.js的基本使用

1.1 NPM

nodejs安装完成后,会跟随着自动安装另外一个工具npm

NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。

2020年3月17日,Github宣布收购npm,GitHub现在已经保证npm将永远免费。可以在终端通过npm -v可以进行版本的查看,能够正确打印出版本,则表示安装成功。

npm版本可以更新,使用npm i -g npm既可进行更新。

设置国内源

因为npm下载的服务器在国外,有时候会导致下载速度很慢,所以一般我们安装完nodejs之后,会修改npm的源(把下载服务器改成国内的服务器)。

我们可以在终端输入下面的命令 npm config set registry [https://registry.npm.taobao.org](https://registry.npm.taobao.org)

回车后,不会有任何的反应。可以通过npm config get registry查看当前源是否是taobao源。

还原官方源

当我们需要用到官方源时,可以通过下面的命令进行还原npm config set registry [https://registry.npmjs.org/](https://registry.npmjs.org/)

1.2 Node.js的组成

JavaScript和Node.js的核心语法都是ECMAScript。

ECMAScript(核心语法):

  • JavaScript
    • 脚本语言,一般运行在客户端
  • Node.js
    • 运行在服务器端的JavaScript

JavaScript的组成:

  • ECMAScript
    • ECMAScript是JavaScript的核心语法。
  • DOM
    • DOM(文档对象模型)是HTML和XML的应用程序接口(API),用于控制文档的内容与结构。
  • BOM
    • BOM(浏览器对象模型)可以对浏览器窗口进行访问和操作。

JavaScript在客户端和服务端实现不同功能:

  • 客户端
    • JavaScript需要依赖浏览器提供的JavaScript引擎解析执行,浏览器还提供了对DOM的解析。
    • 客户端的JavaScript不仅应用了核心语法ECMAScript,而且能操作DOM和BOM。
    • 常见的应用场景如用户交互、动画特效、表单验证、发送Ajax请求等。
  • 服务器端
    • JavaScript不依赖浏览器,而是由特定的运行环境提供的JavaScript引擎解析执行,例如Node.js。
    • 服务器端的JavaScript应用了核心语法ECMAScript,但是不操作DOM和BOM,它常常用来做一些在客户端做不到的事情,例如操作数据库、操作文件等。
    • 另外,在客户端的Ajax操作只能发送请求,而接收请求和做出响应的操作就需要服务端的JavaScript来完成。

1.3 Node.js基础语法

通过node命令解析和执行一个js脚本文件的步骤如下:

  • 根据node命令指定的文件名称,读取js脚本文件。
  • 解析和执行JavaScript代码。
  • 将执行后的结果输出到命令行中。
javascript 复制代码
// step 01
// 创建文件helloworld.js
console.log('hello world');

// step 02
// 打开命令行工具,切换到helloworld.js文件所在的目录
// 并输入"node helloworld.js"命令。

1.4 Node.js全局对象global

全局对象window

在之前使用JavaScript的过程中,在浏览器中默认声明的变量、函数等都属于全局对象window,全局对象中的所有变量和函数在全局作用域内都是有效的。

例如,我们使用console.log()进行值的输出时,console.log()属于window对象的方法,又因为window是全局对象,所以在实际使用中可以省略掉window。

Node.js代码的运行环境存在window对象吗?

在Node.js代码的运行环境中没有DOM和BOM,因此也就不存在window对象。

那么使用的console.log()是来自于哪里呢?

在Node.js中,默认就是模块化的,默认声明的变量、函数都属于当前文件模块,都是私有的,只在当前模块作用域内可以使用。

Node.js中是否只有模块作用域?

答案是否定的,如果想在全局范围内为某个变量赋值,可以应用全局对象global。

Node.js中的global对象类似于浏览器中的window对象,用于定义全局命名空间,所有全局变量(除了global本身以外)都是global对象的属性,在实际使用中可以省略global。

javascript 复制代码
// step 01
// 新建global.js文件
global.console.log('我是global对象中的console.log()方法');
global.setTimeout(() => {
    console.log('123');
}, 2000);

// step 02
// 打开命令行工具,切换到global.js文件所在的目录,并输入"node global.js"命令。

2 Node.js系统模块

2.1 使用fs模块进行文件操作

原生的JavaScript语言无法操作文件,Node.js如何解决文件操作的问题?

Node.js为前端工程师提供了一组文件操作API,解决了前端开发文件操作的问题。

Node.js运行环境提供的APl都是以模块化的方式进行开发的,所以也把Node.js运行环境提供的API称为系统模块。

Node.js运行环境提供了很多系统模块,每一个系统模块中都具有相关性功能。

Node.js的文件操作API由fs(File System)模块提供,fs模块是Node.js的核心模块,不用额外安装就可以使用。

fs模块提供了fs.readFile()读取文件API、fs.writeFile()写入文件API,以及fs.mkdir()创建目录的API等。

模块加载

使用某个模块的API之前,首先需要加载这个模块,fs核心模块的模块标识为"fs"。

javascript 复制代码
// require()方法的返回值
// fs模块的名字
const fs = require('fs');
文件读取

读取已有的文件,Node.js中文件读取的语法如下。

javascript 复制代码
// 要读取文件的路径
// 文件编码(可省略)
// 回调函数
fs.readFile('文件路径/文件名称'[, '文件编码'], callback);

Node.js的文件读取

javascript 复制代码
// step 01
// 在test目录下,新建a.txt作为被加载文件。
// Hello World

// step 02
// 在test目录下新建file.js文件,读取文件。
// 1. 引用fs模块
const fs = require('fs');
// 2. 读取a.txt文件内容
fs.readFile('./a.txt', 'utf8', (err, data) => {
    console.log(err);	// null
    console.log(data);	// Hello World
});

// step 03
// 打开命令行工具,切换到file.js文件所在的目录,并输入"node file.js"命令。
文件写入

文件写入操作常用于网站运行的过程中,如果程序发生错误,将该错误写入到错误日志中,以便程序员对错误进行处理。

javascript 复制代码
// 要写入文件的路径
// 要写入的内容
// 回调函数
fs.writeFile('文件路径/文件名称, 'data', callback);

Node.js的文件写入

javascript 复制代码
// step 01
// 在test目录下,新建writeFile.js文件并编写如下代码向demo.txt文件中写入内容
// 1. 引用fs模块
const fs = require('fs');
// 2. 在demo.txt文件中写入内容
fs.writeFile('./demo.txt', '即将要写入的内容', err => {
    if (err != null) {
        console.log(err);
        return;
    }
    console.log('文件写入内容成功');
});

// step 02
// 打开命令行工具,切换到writeFile.js文件所在的目录,并输入"node writeFile.js"命令。

2.2 使用path模块进行路径操作

在文件操作过程中,经常会遇到路径拼接的问题,那么如何把两个不完整的路径拼接成一个完整的路径?

针对这些路径字符串的操作问题,Node.js的Path模块提供了路径字符操作相关API。

函数 说明
basename(p[,ext]) 获取文件名
dirname§ 获取文件目录
extname§ 获取文件扩展名
isAbsolute(path) 判断是否是绝对路径
join([path1][,path2][,...]) 拼接路径字符串
normalize§ 将非标准路径转换为标准路径
sep 获取操作系统的文件路径分隔符
加载模块

读取已有的文件,Node.js中文件读取的语法如下。

javascript 复制代码
const path = require('path');

在Windows系统中使用path.join()方法拼接路径字符串

javascript 复制代码
// 新建path.js文件
const path = require('path');
// 使用path.join()方法拼接public、uploads、avatar路径字符串
const finalPath = path.join('public', 'uploads','avatar');
// 使用finalPath变量来接收path.join()方法返回结果
console.log(finalPath);

// 运行程序
// 打开命令行工具,切换到path.js文件所在的目录,并输入"node path.js"命令。
// public/uploads/avatar

不同操作系统的路径分隔符是不统一的,示例如下。

  • Windows系统中分隔符可以使用"/"或"\";
  • Linux系统中分隔符为"/"。
相对路径和绝对路径

在Node.js中大多数情况下使用绝对路径,因为相对路径有时候相对的是命令行工具的当前工作目录。

例如文件读取操作API。Node.js中提供了与文件操作相关全局可用变量__dirname__dirname表示当前文件所在的目录,我们可以使用path.join()和__dirname进行路径拼接,从而获取当前文件所在的绝对路径。

Node.js中文件路径的获取

javascript 复制代码
// 新建filename.js
const path = require('path');
console.log(__dirname);
console.log(path.join(__dirname, 'filename.js'));

// 运行程序
// 打开命令行工具,切换到filename.js文件所在的目录,并输入"node filename.js"命令。

3 Node.js第三方模块

3.1 什么是第三方模块

第三方模块就是别人写好的、具有特定功能的、可以直接拿来使用的模块。

由于第三方模块通常都是由多个文件组成并且被放置在一个目录中,所以又称之为包。

第三方模块存在的两种方式:

  • 第1种是以js文件的形式存在,通常都是封装了一些特定的功能,并向外提供实现项目具体功能的API接口供开发者去调用,类似于jQuery。
  • 第2种是以命令行工具形式存在,提供了一些命令用于快速的安装和管理模块,辅助项目开发。

3.2 获取第三方模块

可以使用Node.js的第三方模块管理工具npm提供的命令去下载第三方模块。

npm在Node.js安装完成时就已经被集成,所以可以通过在命令行输入"npm -v"命令,按"Enter"键,来验证npm是否安装成功。

通过npm工具下载安装第三方模块

比如,需要安装的第三方包名称为formidable,就可以在C:\code\chapter02根目录下输入命令npm install formidable,并按"Enter"键等待安装成功即可(注意安装的过程必须联网)。

第三方模块安装成功

安装成功之后,Node.js会自动在项目的当前根目录下(C:\code\chapter02)创建一个node_modules目录,然后把第三方模块自动存放到该目录下。

在开发中使用第三方模块

在C:\code\chapter02目录下的js脚本中通过如下代码来加载formidable模块。

javascript 复制代码
// 加载formidable模块
var formidable = require('formidable');

// 卸载formidable模块
// npm uninstall formidable

在安装或者卸载第三方模块时,还有本地安装和全局安装的选项。

  • 本地安装指的是将模块下载到当前的项目当中,仅供当前项目使用。
  • 全局安装指的是将模块安装到一个公共的目录中,所有的项目都可以使用这个模块。
  • 在实际开发中,通常都会将库文件这种第三方模块进行本地安装,将命令行工具这种第三方模块进行全局安装。

4 Node.js常用开发工具

在Node.js中提供了常用的开发工具来帮助开发人员提高工作效率。

  • nodemon工具可以避免文件每次修改后的重新运行。
  • nrm工具可以快速切换npm的下载地址
  • gulp工具可以自动化处理项目日常任务。

4.1 nodemon工具

在Node.js中,每次修改文件都要在命令行工具中重新执行该文件。第三方模块nodemon解决了这个问题,它是一个用于辅助项目开发的命令行工具,每次保存文件时,代码都会自动运行。

通过npm工具下载安装nodemon工具

javascript 复制代码
// -g全称为global表示全局安装
// npm install nodemon -g

4.2 nrm工具

nrm是npm中的第三方模块下载地址的管理工具,也是一个命令行工具,可以快速切换npm的下载地址。

因为npm中的第三方模块默认的下载地址在国外,在国内下载速度比较慢,有失败的可能。所以为了提高下载速度,在国内有一些公司建立了专门的服务器,用于存储Node.js第三方模块,例如阿里巴巴公司建立的registry.npm.taobao.org服务,它目前每隔10分钟就和npm官网服务做一次同步,可以替代官方的下载地址。

通过npm工具下载安装nrm工具

javascript 复制代码
// -g全称为global表示全局安装
// npm install nrm -g

通过nrm ls命令查询当前可用的下载地址列表

通过nrm use命令切换下载地址列表

4.3 gulp工具

gulp是用JavaScript语言编写的运行在Node.js平台开发的前端构建工具。

gulp是一个JavaScript程序,并且它的指令使用的也是JavaScript语言,所以gulp通常是前端开发人员自动化处理日常任务的首选工具。

gulp工具作用

gulp可以处理日常工作流产生的任务:

  • 项目上线时对HTML、CSS、JavaScript文件合并、压缩。
  • 将ES6语法转换为ES5语法以便代码在较旧的浏览器中运行。

gulp允许开发者将机械化的操作编写成任务,在命令行输入相关的任务名称就能执行机械化操作,从而提高开发效率。

全局安装gulp-cli

gulp-cli是gulp的命令行工具,它需要全局安装,以便gulp能够在命令提示符中直接运行。gulp-cli是本地gulp的全局的入口,负责把所有参数转发到本地gulp,还有显示项目里安装的本地gulp的版本。

全局gulp用于启动各个项目中的本地gulp,换句话说,如果在全局安装了gulp-cli,那么就可以在不同的项目中使用不同的gulp版本。

通过npm工具下载安装gulp-cli

javascript 复制代码
// -g全称为global表示全局安装
// @2.3.0表示全局gulp-cli的版本
// npm install gulp-cli@2.3.0 -g

在项目中安装gulp

本地gulp位于本地项目的node_modules目录下,包含了gulpfile所需的所有函数和API。

本地gulp作用:

  • 加载和运行gulpfile(gulpfile.js)中的构建指令。
  • 另一个是暴露API供gulpfile使用。
javascript 复制代码
// 使用npm初始化项目
// 在项目目录下,初始化项目。
// npm init
// 命令完成之后会创建一个package.json新文件,该文件包存了项目的所有Node.js模块信息和版本。

// 局部安装gulp
// 在项目目录下,执行如下命令。
// npm install gulp@4.0.2 --save-dev
// --save-dev表示将gulp作为devDependencice(开发依赖)保存到package.json文件中
// 命令执行成功后,会在项目根目录中生成一个node_modules目录和package-lock.json文件

// 检测gulp-cli是否识别出了本地的gulp
// 再次在命令行工具中运行"gulp -v"命令检查gulp版本。

构建项目

  • 安装成功后,在项目根目录下建立gulpfile.js文件,注意这个文件名不能随意更改。
  • 重构项目的目录结构。
  • 在gulpfile.js文件中编写构建项目的任务。
javascript 复制代码
// 引用gulp模块
const gulp = require('gulp');
// 使用gulp.task()方法建立任务
gulp.task('first', callback => {
    console.log('第一个gulp任务执行了')
    // 使用gulp.src()获取要处理的文件
    gulp.src('./src/css/base.css')
    // 将处理后的文件输出到dist目录 
        .pipe(gulp.dest('dist/css'));
    callback();
});
  • 在命令行工具中执行gulp任务。
javascript 复制代码
// gulp first
// gulp命令后跟first任务名,表示gulp命令会自动去当前的项目根目录下查找gulpfile.js文件
// 然后在这个文件中再去查找first任务,找到后自动执行first任务的回调函数。
  • first任务的运行结果。

gulp API中的常用方法

方法 说明
gulp.src() 获取任务要处理的文件
gulp.dest() 输出文件
gulp.task() 建立gulp任务
gulp.watch() 监控文件的变化

5 在项目中使用gulp

5.1 gulp中的常用插件

gulp第三方模块仅提供了一些常用的方法,使用这些方法只能实现一些基础的操作。

在gulp中如果我们想要处理文件的合并、压缩等操作,都需要通过哪些插件来实现?

gulp常用插件

插件 说明
gulp-htmlmin 压缩HTML文件
gulp-csso 压缩优化CSS
gulp-babel JavaScrtipt 语法转化
gulp-less Less语法转换
gulp-sass Sass语法转换
gulp-uglify 压缩混淆JavaScript文件
gulp-file-include 公共文件包含
browsersync 浏览器时间实时同步

5.2 压缩并抽取HTML中的公共代码

将HTML文件中的代码进行压缩,并抽取HTML文件中的公共代码

javascript 复制代码
// stpe 01
// 在项目目录下,通过npm工具下载安装gulp-htmlmin插件
// 下载安装压缩HTML文件插件gulp-htmlmin
// npm install gulp-htmlmin

// step 02
// 在gulpfile.js文件中引用gulp-htmlmin插件
// 引用gulp-htmlmin插件
const htmlmin = require('gulp-htmlmin');

// step 03
// 在gulpfile.js文件中调用gulp-htmlmin插件,实现HTML文件中代码的压缩
gulp.task('htmlmin', (callback) => {
    gulp.src('./src/*.html')
    // 压缩html文件中的代码
        .pipe(htmlmin({ collapseWhitespace: true }))
        .pipe(gulp.dest('dist'));
    callback();
});

// step 04
// 查看dist目录结构
// 打开命令行工具,切换到项目目录,运行命令"gulp htmlmin"。
// gulp任务执行成功后,在dist目录可以看到两个压缩后的article.html文件和default.html文件
// 然后打开该文件,可以看到压缩后的代码。

// step 05
// 下载gulp-file-include插件,抽取 HTML中的公共代码
// 在项目目录下,通过npm工具下载安装gulp-file-include插件。
// 下载gulp-file-include插件
// npm install gulp-file-include

// step 06
// 在gulpfile.js文件中引用gulp-file-include插件
// 引用gulp-file-include插件
const fileinclude = require('gulp-file-include');

// step 07
// 在gulpfile.js文件中调用gulp-file-include插件,抽取 HTML中的公共代码
gulp.task('htmlmin', (callback) => {
    gulp.src('./src/*.html')
    // 抽取HTML文件中的公共代码
        .pipe(fileinclude())
    // 压缩HTML文件中的代码
        .pipe(htmlmin({ collapseWhitespace: true }))
        .pipe(gulp.dest('dist'));
    callback();
});

// step 08
// src目录下新建 common 目录
// 然后在common目录下创建 header.html文件
// 并把头部的公共代码粘贴到header.html文件中。

// step 09
// src目录下的default.html和article.html文件的头部代码删除掉,修改为如下代码。
@@include('./common/header.html')
// @@include()语法是由gulp-file-include插件提供的,小括号中是代码片段的路径以及文件的名字。

// step 10
// 打开命令行工具,切换到目录,再次运行命令"gulp htmlmin"。
// gulp任务执行成功后,打开dist目录下的default.html文件和article.html文件
// 查看代码会发现这两个文件中都包含有 header 部分代码。

5.3 压缩并转换Less语法

将CSS文件使用的Less语法转换为CSS语法,并压缩CSS文件中的代码

javascript 复制代码
// step 01
// 在test目录下,通过npm工具下载安装gulp-less插件
// 下载安装Less语法转换插件gulp-less
// npm install gulp-less

// step 02
// 在gulpfile.js文件中引用gulp-less插件
// 引用gulp-less插件
const less = require('gulp-less');

// step 03
// 在gulpfile.js文件中调用gulp-less插件,实现Less语法转换为CSS语法
gulp.task('cssmin', (callback) => {
    // 选择css目录下的所有.less文件
    gulp.src('./src/css/*.less')
    // 将Less语法转换为CSS语法
        .pipe(less())
    // 将处理的结果进行输出
        .pipe(gulp.dest('dist/css'))
    callback();
});

// step 04
// 在 test\src\css 目录下,新建需要编译的a.less 文件。
/* .headers {
width: 100px;
.logo {
height: 200px;
background-color: red;
}
} */

// step 05
// 打开命令行工具,切换到test目录,运行命令"gulp cssmin"。
// gulp任务执行成功后,打开项目下的 dist 目录
// 可以看到 css 目录下新建了一个同名的a.css文件。
// 需要对test\src\css目录下的全部文件进行压缩,然后输出。

/* ---- css目录下有.less文件和.css文件 ---- */
/* ---- 那么如何同时获取这两种类型的文件并对其进行压缩呢? ---- */

// step 06
// 在demo07目录下,通过npm工具下载安装gulp-csso插件。
// 下载gulp-csso插件
// npm install gulp-csso

// step 07
// 在gulpfile.js文件中引用gulp-csso插件
// 引用gulp-csso插件
const csso = require('gulp-csso');

// step 08
// 在gulpfile.js文件中调用gulp-csso插件,对CSS代码进行压缩
gulp.task('cssmin', (callback) => {
    // 选择css目录下的所有.less文件以及.css文件
    gulp.src(['./src/css/*.less', './src/css/*.css'])
    // 将Less语法转换为CSS语法
        .pipe(less())
    // 将CSS代码进行压缩
        .pipe(csso())
    // 将处理的结果进行输出
        .pipe(gulp.dest('dist/css'))
    callback();
});

// step 09
// 打开命令行工具,切换到test目录,运行命令"gulp cssmin"。
// gulp任务执行成功后,打开项目下的 dist 目录,可以看到当前 css 目录结构。
// 打开css目录下的a.css文件,会发现代码已经被压缩了。

5.4 压缩并转换ES6语法

javascript 复制代码
// step 01
// 在test目录下,通过npm工具下载安装gulp-babel插件。
// 下载安装gulp-babel插件,实现ES6语法的转换
// npm install gulp-babel @babel/core @babel/preset-env

// step 02
// 在gulpfile.js文件中引用gulp-babel插件
// 引用gulp-babel插件
const babel = require('gulp-babel');

// step 03
// 在gulpfile.js文件中调用gulp-babel插件,实现ES6语法的转换
gulp.task('jsmin', (callback) => {
    // 选择js目录下的所有JavaScript文件
    gulp.src('./src/js/*.js')
        .pipe(babel({
        // 判断当前代码的运行环境,将代码转换为当前运行环境所支持的代码
        presets: ['@babel/env']
    }))
        .pipe(gulp.dest('dist/js'));
    callback();
});

// step 04
// 在 dtest\src\js目录下,新建需要编译的JavaScript文件(如base.js文件)。
/*
const x = 100;
let y = 200;
const fn = () => {
console.log(1234);
};
*/

// step 05
// 打开命令行工具,切换到test目录,运行命令"gulp jsmin"。
// gulp任务执行成功后,打开项目下的 dist 目录
// 可以看到 js 目录下新建了一个同名的 base.js 文件。

/* ---- 如何对test\src\js目录下的全部文件进行压缩,然后进行输出呢?? ---- */

// step 06
// 下载gulp-uglify插件,对JavaScript文件进行压缩
// 在test目录下,通过npm工具下载安装gulp-uglify插件。
// 下载gulp-uglify插件
// npm install gulp-uglify

// step 07
// 在gulpfile.js文件中引用gulp-uglify插件

// step 08
// 在gulpfile.js文件中调用gulp-uglify插件,对JavaScript代码进行压缩
gulp.task('jsmin', (callback) => {
    // 选择js目录下的所有JavaScript文件
    gulp.src('./src/js/*.js')
        .pipe(babel({
        // 判断当前代码的运行环境,将代码转换为当前运行环境所支持的代码
        presets: ['@babel/env']
    }))
        .pipe(uglify())
        .pipe(gulp.dest('dist/js'));
    callback();
});

// step 09
// 打开命令行工具,切换到test目录,运行命令"gulp jsmin"。
// gulp任务执行成功后,打开项目下的 dist 目录
// 可以看到当前js目录下的base.js文件会压缩了。

5.5 复制目录

对于test项目来说,还缺少src目录下的images目录和lib目录。接下来我们如何把src目录下的images目录和 lib目录,复制到dist目录下呢?

javascript 复制代码
// step 01
// 在gulpfile.js文件中创建copy任务,进行目录复制操作。
gulp.task('copy', (callback) => {
    gulp.src('./src/images/*')
        .pipe(gulp.dest('dist/images'));
    gulp.src('./src/lib/*')
        .pipe(gulp.dest('dist/lib'));
    callback();
});

// step 02
// 打开命令行工具,切换到test目录,运行命令"gulp copy"。
// gulp任务执行成功后,打开项目下的 dist 目录
// 可以看到js目录下新建了一个同名的images文件和lib文件。

5.6 执行全部构建任务

我们通过使用命令"gulp 任务名"去执行单个任务,那么如何实现执行一个任务,其他任务也一起执行呢?

javascript 复制代码
// step 01
// 在gulpfile.js文件中创建default任务
// gulp.series()用于顺序执行任务
gulp.task('default', gulp.series('htmlmin', 'cssmin', 'jsmin', 'copy'));

// step 02
// 打开命令行工具,切换到test目录,运行"gulp default"命令。

6 项目依赖管理

6.1 package.json文件

在进行模块化开发时,项目中需要记录复杂的模块依赖关系。随着时间的推移,项目中的某些模块可能会升级版本,模块提供的API也会发生变化,那么如何对模块进行有效管理呢?

将代码发给其他人或者上传到代码仓库时需要发送node_modules目录吗?

**不建议传输node_modules目录。**原因:node_modules目录中的文件多且零碎,当我们将整个项目进行复制时,传输速度会非常慢。

解决方案:npm工具提供了项目描述文件package.json,该文件中记录当前项目所依赖的第三方模块和对应的版本号,当其他人拿到这个项目的时候会根据package.json文件中所记录的依赖项下载第三方模块,这样项目在他人的计算机上也可以成功运行。

生成package.json文件

javascript 复制代码
// -y表示全部使用默认值
npm init -y

查看package.json文件内容

json 复制代码
{
    "name": "test",	// 表示项目的名称
    "version": "1.0.0",	// 表示项目的版本
    "description": "",	// 表示项目的描述
    "main": "index.js",	// 表示项目的主入口文件
    "scripts": {		// 对象中存储命令的别名
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],	// 表示关键字
    "author": "",	// 表示项目的作者
    "license": "ISC" 	// 表示项目遵循的协议,默认ISC
}

下载第三方模块

json 复制代码
// 使用"npm install 模块名"命令去下载
npm install formidable mime

// 会发现在原来的文件里新增了一个dependencies选项
"dependencies": {
    "formidable": "^1.2.2",
    "mime": "^2.4.6"
}

如果删除项目目录结构中的node_modules目录,并将该项目传输给其他人,传输成功后,该如何运行这个复制的项目呢?

只需要在复制完成的项目中打开命令行工具,输入"npm install"命令,npm工具会自动到项目根目录下去找到package.json文件下的dependencies选项去下载第三方模块。

6.2 查看项目依赖

在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖。

例如,使用"npm install 包名"命令下载的formidable和mime第三方模块,它们会默认被添加到package.json文件的dependencies选项中。

除了项目依赖外,还有开发依赖。开发依赖使用"npm install 包名 --save-dev"命令安装,"--save-dev"参数将包添加到package.json文件的devDependencies选项中。

json 复制代码
{
    "devDependencies": {
        "gulp": "^4.0.2"
    },
}

如何区分项目依赖和开发依赖

一般来说,devDependencies选项下的模块是在开发阶段需要用的,比如项目中使用的gulp模块等。这些模块在项目部署后是不需要的,所以可以使用--save-dev参数去安装。如果像jQuery和Express这些模块是项目运行中必备的,应该安装在dependencies选项中。

项目依赖和开发依赖进行区分的好处?

  • 可以在不同的运行环境中下载不同的依赖。例如在线下的开发环境我们可以使用"npm install"命令下载全部的依赖(包括项目依赖和开发依赖)。
  • 如果在项目上线后的运行环境(服务器环境)可以使用"npm install --production"命令下载dependencies选项(项目依赖),避免下载项目开发依赖。

在下载第三方模块时,npm会同时在demo08项目的根目录下产生一个package-lock.json文件,该文件中会详细记录模块与模块之间的依赖关系、版本信息,以及下载地址。

package-lock.json文件的两个作用

  • 一个作用是锁定包的版本,确保再次下载时不会因为包版本不同而产生问题。
  • 另一个作用是加快下载速度, 因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可,不需要做额外的工作。

7 Node.js模块加载机制

7.1 当模块拥有路径但没有后缀时

传入完整路径

当使用require()方法引入模块时,如果传入的是完整路径,那么程序就会根据模块路径查找模块,并直接引入模块。

javascript 复制代码
require('./find.js');

模块后缀省略时

javascript 复制代码
require('./find');

当模块后缀省略时,模块查找规则:

  • 首先在当前目录下查找find.js同名文件,如果找到就去执行这个同名的文件。
  • 如果当前目录没有找到find.js文件,那么就去查找当前目录下的find目录,如果找到这个目录,就在当前find目录下查找index.js文件,如果找到index.js文件就去执行这个文件。
  • 如果在find目录中没有找到index.js文件,就去当前find目录下的package.json文件中去查找main选项中的入口文件,如果找到入口文件就去执行它。
  • 如果main选项中没有指定入口文件,或者指定的入口文件不存在,程序就会报错。
javascript 复制代码
// step 01
// 在项目目录下,创建test目录,在该目录下创建find.js文件作为被加载模块。
console.log('demo09目录下的find.js被执行了');

// step 02
// 在test目录下,新建require.js文件。
require('./find');

// step 03
// 打开命令行工具,切换到require.js文件所在的目录,并输入"node require.js"命令。

// 如将demo09目录下的find.js重命名为任意一个名字(如nofind.js)
// 回到命令行重新执行"node require.js"命令。

// step 04
// 在test目录下,新建find目录,并且在该目录下新建index.js文件
console.log('find目录下的index.js被执行了');

// 当在test目录下没有找到find.js模块时,它就会去查到test目录下的find目录
// 然后在当前find目录下查找index.js文件,并去执行这个文件。

// step 05
// 回到命令行工具,再次执行"node require.js"命令

// 如果将find目录下的index.js文件,重命名为任意一个名字(如noindex.js)
// 回到命令行,切换到find目录下,执行"npm init -y"命令
// 在生成的package.json文件中将main选项值改为main.js。
// "main": "main.js",

// step 06
// 在find目录下新建main.js文件。
console.log('find目录下的main.js被执行了');

// 回到命令行工具,切换到test目录下,重新执行"node require.js"命令

7.2 当模块没有路径且没有后缀时

当使用require()方法引入模块时,如果只写了模块的名字,没有写模块的后缀,如下所示:

javascript 复制代码
require('find');

当模块没有路径且没有后缀时,模块查找规则:

  • 首先,Node.js 会假设它是系统模块,然后去系统模块中查找有没有find系统模块,如果有就去执行这个模块。
  • 如果没有找到find模块 ,就去node_modules目录中查找有没有同名的.js文件,如果找到了这个同名的.js文件就去执行它。
  • 如果在node_modules目录没有找到同名的.js文件,就在node_modules目录下查找有没有同名的find目录,如果找到这个目录,就在当前find目录下查找index.js文件,如果找到index.js文件就去执行这个文件。
  • 如果find目录 中没有找到index.js文件,就去当前find目录下的package.json文件中去查找main选项中的入口文件,如果找到入口文件就去执行它。
  • 如果main选项 中没有指定入口文件,或者指定的入口文件不存在,程序就会报错。
javascript 复制代码
// STEP 01
// 在项目目录下,创建test目录,在该目录下创建require.js文件。
require('find');

// STEP 02
// 在test目录下,创建node_modules目录,在该目录下新建find.js文件。
console.log('node_modules中的find.js被执行了');

// STEP 03
// 打开命令行工具,切换到require.js文件所在的目录,并输入"node require.js"命令。

// STEP 04
// 在node_modules目录下,新建find目录,并且在该文件下新建index.js文件,编写如下内容。
console.log('node_modules目录中的find目录中的index.js被执行了');

// STEP 05
// 回到命令行工具,再次执行"node require.js"命令。

// STEP 06
// 在find目录下新建main.js文件,编写如下内容。
console.log('find目录下的main.js被执行了');

// STEP 07
// 回到命令行工具,切换到test目录下,重新执行"node require.js"命令
相关推荐
南城巷陌2 小时前
JWT认证机制在Node.js中的详细阐述
node.js·jwt认证机制·前端安全认证
理想不理想v4 小时前
node.js的简单示例
node.js
yrldjsbk4 小时前
使用Node.js搭配express框架快速构建后端业务接口模块Demo
node.js·express
维李设论4 小时前
Node.js的Web服务在Nacos中的实践
前端·spring cloud·微服务·eureka·nacos·node.js·express
CodeChampion6 小时前
60.基于SSM的个人网站的设计与实现(项目 + 论文)
java·vue.js·mysql·spring·elementui·node.js·mybatis
Domain-zhuo6 小时前
如何利用webpack来优化前端性能?
前端·webpack·前端框架·node.js·ecmascript
理想不理想v6 小时前
webpack如何自定义插件?示例
前端·webpack·node.js
斜杠poven10 小时前
为什么加try catch 不会 block 进程?
前端·javascript·node.js
韩俊强1 天前
使用Docker部署一个Node.js项目
docker·容器·node.js
秋沐1 天前
Node Version Manager (nvm) -管理不同版本的 Node.js
node.js