1. Node开发概述
1.1 为什么要学习服务器端开发基础
- 能够和后端程序员更加紧密的配合
- 网站业务逻辑前置,学习前端技术需要后端技术支持(Ajax)
- 扩宽知识视野,能够站在更高的角度审视整个项目
1.2 服务器端开发要做的事情
- 实现网站的业务逻辑 比如登录
- 数据的增删改查 比如电商平台的购物车
1.3 为什么选择Node
- 使用JavaScript语法开发后端应用
- 一些公司要求前端工程师掌握Node开发
- 生态系统活跃,有大量开源库可以使用
- 前端开发工具大多基于Node开发
1.4 Node是什么
Node是一个基于Chrome V8引擎的JavaScript代码运行环境
Chrome V8引擎: JavaScript代码执行引擎。
1.5 Node.js 组成
- JavaScript由三部分组成,ECMAScript DOM BOM 其中ECMAScript是核心 DOM BOM是浏览器这个运行环境为JavaScript这个语言提供的api
- nodejs是由ECMAScript及node环境提供的一些附加api组成
1.6 Node.js 模块化开发
JavaScript开发的弊端:文件依赖和命名冲突
所以需要模块化,需要的开放出去 不需要的封装掉
软件中的模块化开发:一个功能就是一个模块,多个模块可以组成完整应用,抽离一个模块不会影响其他功能的运行。
比如:
app.js
user.js post.js goods.js
addUser.js deleteUser.js findGood.js addGood.js
node.js 规定一个js文件就是一个模块,模块内部定义的变量和函数默认情况下载外部无法得到
exports对象进行成员导出,使用require方法导入其他模块
exports.version = version; exports.sayhi = sayhi; // b.js let a = require('./b.js'); console.log(a.version);
module.exports.version = version;
底层理解 exports 和 module.exports 都是对象,指向同一地址空间,如果不同的话,以module.export为准
1.7 系统模块fs 文件操作
const fs = require('fs');
读取文件内容:
fs.readFile('文件路径/文件名称'[,'文件编码'],callback);
fs.readFile('../css/main.css','utf-8',(err,doc) => { // 如果文件读取发生错误,参数err的值为错误对象,否则err为null // doc参数为文件内容 if(err ==null){ } })
node 中所有的回调函数第一个参数都是error。错误优先的回调函数
写入文件:
fs.writeFile('文件路径/文件名称','数据',callback)
例如项目中写入错误日志。
const content = ''; fs.writeFile('../index.html',content,err => { if(err != null){ console.log(err); return; } console.log('文件写入成功'); })
系统模块 path 路径操作:
为什么要进行路径拼接??
- 不同操作系统的路径分隔符不统一
- Windows是 \ /
- Linux是/
- Linux系统通常被用作网站的服务器
比如 图片上传功能 存在服务器的某一个硬盘文件夹
路径拼接语法:
path.join('路径','路径',...)
相对路径 vs 绝对路径:
- 大多数情况下使用绝对路径,写相对路径不安全
- 因为执行的时候一般是当前工作目录,所以相对路径不安全
- 使用__dirname(两个下划线)获取当前目录所在绝对路径。
- require时相对路径是相对当前目录,所以require可以使用相对路径
1.8 第三方模块
包,第三方模块,多个文件组成并在一个文件夹中
存在形式
- js文件,提高项目具体功能API接口
- 命令行工具存在
获取第三方模块:
npmjs.com 第三方模块的存储和分发仓库
npm (node package manager): node 第三方模块管理工具
- npm install
- npm uninstall
本地安装和全局安装
- 命令行工具:全局安装
- 库文件:本地安装
1.9 第三方模块 nodemon
在nodejs中每次修改文件都要在命令行中重新执行该文件,非常繁琐
- npm install nodemon -g 下载 全局安装
- 在命令行工具中使用nodemon命令代替node执行
- 用 ctr+C退出
1.10 第三方模块 nrm
nrm (npm registry manager): npm下载地址切换工具
- npm install nrm -g
- 查询 nrm ls
- 切换 nrm use 下载地址名称
1.11 第三方模块 Gulp
基于node平台开发的前端构建工具
将机械化操作编写成任务,想要执行机械化操作时执行一个命令行命令任务就能自动执行了
用机器代替手工,提高开发效率
- npm install gulp 下载gulp库文件
- 在项目根目录下建立 gulpfile.js
- 重构项目的文件夹结构 src目录放源代码 dist目录放构建后文件
- 在gulpfile.js中编写任务
- 命令行工具中执行任务
1.12 package.json文件
package.json的作用:
项目描述文件,记录了当前项目信息,例如项目名称、版本、作者,github地址,当前项目依赖了哪些第三方模块等。使用npm init -y
命令生成(y是全部默认值 (yes))
{ "name": "test", "version": "1.0.0", "description": "test main是项目主入口文件 scripts是命令的别名 ISC是开发源代码协议", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build":"nodemon app.js" }, "keywords": [], "author": "Jiaqi Guo", "license": "ISC" }
在终端写npm run build可以执行nodemon app.js命令
项目依赖——线上线下都需要
开发依赖——开发阶段需要依赖,线上运营阶段不需要依赖的第三方包 使用npm install xxx --save -dev将包添加在package.json文件中的DevDependencies字段中, 比如 gulp
npm install 下载所有的依赖
npm install --production 服务器环境 下载项目依赖
package-lock.json
记录模块之间的依赖关系
1.13 Node.js 中模块的加载机制
模块查找规则:
require('./find.js');
require('./find');
- require方法根据模块路径查找模块,如果是完整路径,直接引入模块
- 如果模块后缀省略,先找同名js文件再找同名js文件夹
- 如果找到了同名文件夹,找文件夹中的index.js
- 如果文件夹中没有index.js 就找当前文件夹package.js中找main选项的入口文件
- 如果入口文件不存在或者没有则报错,模块没有被找到。
模块没有路径也没有后缀
- 系统模块
- 去node_modules文件夹中
- 首先看是否有改名字的js文件
- 文件夹
- 如果是文件夹看是否有index.js
- 如果没有index.js看文件夹中package.json的main选项确定模块入口文件
- 否则找不到报错
2.Node运行环境搭建
2.1 Node.js运行环境安装
官网:https://nodejs.org/en/
中文网:http://nodejs.cn/
关于版本:
LTS = Long Term Support 长期支持版 稳定版
Current 拥有最新特性 实验版
运行中输入:powershell 打开命令行工具
再输入:node -v 可以查看安装版本
2.2 Node环境安装失败的解决办法
1.错误代号2502、2503
失败原因:系统帐户权限不足。
解决办法:
1)以管理员身份运行 powershell 命令行工具
2)输入运行安装包命令 msiexec /package node安装包位置。比如:msiexec /package E:\tools\node-v12.16.2-x64.msi
2.执行命令报错
失败原因:Node安装目录写入环境变量失败
解决办法:将Node安装目录添加到环境变量中
2.3 PATH 环境变量
存储系统中的目录,在命令行中执行命令的时候,系统会自动去这些目录中查找命令的位置。
3 Node.js 快速入门
3.1 Node.js 的组成
JavaScript 由三部分组成,ECMAScript、DOM、BOM。
Node.js 是由 ECMAScript 及 Node 环境提供的一些附加 API 组成的,包括文件、网络、路径等等一些更加强大的 API。
3.2 Node.js 基础语法
所有 ECMAScript 语法在 Node 环境中都可以使用。
运行js文件:在文件当前目录下,输入:
node xxx.js
3.3 Node.js 全局对象 global
在浏览器中全局对象是 window,在 Node 中全局对象是 global。
Node 中全局对象下有以下方法,可以在任何地方是有,global 可以省略:
console.log() 在控制台输出
setTimeout() 设置超时定时器
clearTimeout() 清除超时定时器
setInterval() 设置间歇定时器
clearInterval() 清除间歇定时器
例子:新建 global.js 文件:
global.console.log('我是global对象下面的console.log方法输出的内容'); global.setTimeout(function () { console.log('123'); }, 2000)
在命令行工具中,输入:
node global.js
结果是:先输出“我是global对象下面的console.log方法输出的内容”,然后2秒后输出“123”。
修改下代码,把 global 去掉:
console.log('我是global对象下面的console.log方法输出的内容'); setTimeout(function () { console.log('123'); }, 2000)
在命令行工具输入:
node global.js
结果和上面是一样的,说明 global 是全局对象。
4 Node.js模块化开发
4.1 JavaScript 开发弊端
JavaScript 在使用时存在两大问题:文件依赖和命名冲突。
4.2 软件中的模块化开发
一个功能就是一个模块,多个模块可以组成完整营养,抽离一个模块不会影响其他功能的运行。
4.3 Node.js 中模块化开发规范
Node.js 规定一个 JavaScript 文件就是一个模块,模块内部定义的变量和函数,默认情况下在外部无法得到。
模块内部可以使用 exports 对象进行成员导出,使用 require 方法导入其他模块。
4.4 模块成员导出
例子:
// a.js // 在模块内部定义变量 let version = 1.2; // 在模块内部定义方法 const sayHi = name => `您好,${name}`; // 向模块外部导出数据 exports.version = version; exports.sayHi = sayHi;
4.5 模块成员的导入
例子:
// b.js // 在 b.js 模块中导入模块 a let a = require('./a.js') // 输出 a 模块中的 version 变量 console.log(a.version); // 调用 a 模块中的 sayHi 方法并输出其返回值 console.log(a.sayHi('世界'));
此时在当面目录下的命令行中,输入:node ./b.js
会在控制台打印出:1.2 和 您好,世界
注意:导入模块时后缀可以省略。
比如:在 b.js中 可以写作
let a = require('./a')
4.6 模块成员导出的另一种方式:
module.exports.version = version;
module.exports.sayHi = sayHi;
exports 是 module.exports 的别名(地址引用关系),导出对象最终以 module.exports 为准。
4.7 模块导出两种方式的联系与区别:
当 exports 对象和 module.exports 对象,指向的不是同一个对象时,以 module.exports 为准。
5 系统模块
5.1 什么是系统模块
Node 运行环境提供的 API ,因为这些 API 都是以模块化的方式进行开发的,所以我们又称 Node 运行环境提供的 API 为系统模块。
5.2 系统模块fs 文件操作
f: file, s: system, fs: 文件操作系统
const fs = require('fs');
读取文件内容:
fs.readFile('文件路径/文件名称'[,'文件编码'],callback);
读取文件语法示例:
// 读取上一级 css 目录下的 base.css fs.readFile('../css/base.css','utf-8', (err, doc) => { // 如果文件读取法师错误,参数 err 的值为错误对象,否则 err 的值为 null // doc 参数为文件内容 if (err == null) { // 在控制台中输入文件内容 console.log(doc); } });
例子:新建 readFile.js 文件:
// 1.通过模块的名字 fs 对模块进行引用 const fs = require('fs'); // 2.通过模块内部的 readFile 方法,读取文件内容 fs.readFile('./a.js','utf8', (err, doc) => { // 如果文件读取法师错误,参数 err 的值为错误对象,否则 err 的值为 null // doc 参数为文件内容 if (err == null) { 在控制台中输入文件内容 console.log(doc); } });
在当前目录下的命令行输入:node readFile.js
可以读取出 a.js 的文件内容
写入文件内容:
fs.writeFile('文件路径/文件名', '数据', callback);
写入文件语法示例:
const context = '<h3>正在使用fs.writeFile写入文件内容</h3>'' fs.writeFIle('../index.html', context, err => { if (err != null) { console.log(err); return; } console.log('文件写入成功'); });
例子:新建 writeFile 文件:
const fs = require('fs'); fs.writeFile('./demo.txt', '<h3>Hello world</h3>', err => { if (err != null) { console.log(err); return; } console.log('文件写入成功'); })
注意:如果目录下没有 demo.txt 文件,系统会自动创建该文件;如果目录下有 demo.txt 文件,那么会覆盖替换原文件里面的内容。
此时,在当前目录下的命令行输入:node writeFile.js
命令行工具会显示“文件写入成功”,然后会看到目录下生产一个 demo.txt 文件,里面的内容是“<h3>Hello world</h3>”。
5.3 系统模块 path 路径操作
为什么要进行路径拼接:
不同操作系统的路径分隔符不统一,比如:/public/upload/avatar
Windows 上是:\ 或 /
linux 上是:/
5.4 路径拼接语法:
path.join('路径', '路径',
路径拼接语法示例:
// 导入 path 模块 const path = require('path'); // 路径拼接 let finialPath = path.join('itcast', 'a', 'b', 'c.css'); // 输出结果 itcast\a\b\c.css console.log(finialPath);
例子:新建 path.js 文件
// public/upload/avatar // 导入 path 模块 const path = require('path'); // 路径拼接 let finialPath = path.join('public', 'upload', 'avatar'); // 输出结果 itcast\a\b\c.css console.log(finialPath);
此时,在当前目录下的命令行输入:node path.js
命令行工具会把拼接好的路径打印出来。 public\upload\avatar
5.5 相对路径 VS 绝对路径
大多数情况下使用绝对路径,因为相对路径有时候相对的是命令行工具的当前工作目录
在读取文件或者设置文件路径时都会选择绝对路径
使用 __dirname 获取当前文件所在的绝对路径
例子:新创建 path-xj.js 文件:
const fs = require('fs'); const path = require('path'); console.log(__dirname); // 获取当前文件所在的绝对路径 console.log(path.join(__dirname, 'helloa.js')); fs.readFile(path.join(__dirname, 'helloa.js'), 'utf8', (err, doc) => { console.log(err); console.log(doc); });
在命令行工具中,往上退一级目录。
然后输入:node .\desktop\path-xj.js
可以看到命令行工具中显示:
6 第三方模块
6.1 什么是第三方模块
别人写好的,具有特定功能的、我们能直接使用的模块,即第三方模块。由于第三方模块通常都是由多个文件组成,并且被防止在一个文件夹中,所以又名包。
第三方模块有两种存在形式:
1)以 js 文件的形式存在,提供实现项目具体功能的 API 接口。
2)以命令行工具形式存在,辅助项目开发。
6.2 获取第三方模块
npmjs.com:第三方模块的存储和分发仓库
npm(node package manager):node 的第三方模块管理工具,包管理工具。
下载:npm install 模块名称
卸载:npm uninstall package 模块名称
全局安装与本地安装:
1)命令行工具:全局安装
2)库文件:本地安装
6.3 第三方模块 nodemon
nodemon 是一个命令行工具,用以辅助项目开发。可以监控文件的修改。
在 Node.js 中,每次修改文件都要在命令行工具中重新执行该文件,非常繁琐。
使用步骤:
1)使用 npm install nodemon -g 下载
2)在命令行工具中使用 nodemon 命令替代 node 命令执行文件
6.4 第三方模块 nrm
nrm(npm registry manager):npm 下载地址切换工具
使用步骤:
1)使用 npm install nrm -g 下载
2)查询可用下载地址列表: nrm ls
3)切换 npm 下载地址:nrm use 下载地址名称
安装完成后,在命令行工具,输入:
nrm ls
结果:
*代表的是 npm 当前默认下载地址。
切换淘宝的下载地址,在命令行输入:
nrm use taobao
然后再输入:
nrm ls
可以看到*切换到了 taobao 前面,表示 taobao 地址是当然默认地址了。
6.5 第三方模块 Gulp
基于 node 平台开发的前端构建工具,主要用来设定程序自动处理静态资源的工作。简单的说,gulp就是用来打包项目的。
将机械化操作编写成任务,想要执行机械化操作时执行一个命令行命令任务就能自动执行了。
用机器代替手工,提高开发效率。
中文官网:https://www.gulpjs.com.cn/docs/
7 package.json 文件
7.1 node_modules 文件夹的问题
1、文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,传输速度会很慢很慢。
2、复杂的模块依赖关系需要被记录,确保模块的版本和当前保持一致,否则会导致当前项目运行报错。
7.2 package.json 文件的作用
项目描述文件,记录了当前项目信息,例如项目名称、版本、作者、github地址、当前项目依赖了那些第三方模块等。使用 npm init -y 命令生成。
例子:
1.先新建 description 文件夹,并在命令行工具中切换到 description 目录下
输入:(-y 表示 都用默认值)
npm init -y
效果图:
会发现 description 目录下,生成了 package.json 文件。
2.在命令行工具输入:(可以同时安装多个插件,以空格隔开)
npm install formidable mime
然后可以看到 package.json 文件,多了一个 dependencies 选项:
dependencies 就是项目所依赖的第三方模块。
3.当我们把项目传递给别人时,不会传递 node_modules 文件夹。这里我们假设传递,把 node_modules 文件夹删除掉
别人要做的就是,在命令行工具进入当前项目目录,输入:
npm install
npm 会自动到项目的根目录下找 package.json 文件,然后这个文件中找到 dependencies 选项,再根据这个选项下载第三方模块。
7.3 项目依赖
在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖。
在 npm install 包名 命令下载的文件会默认被添加到 package.json 文件的 dependencies 字段中
{ "dependencies": { "jquery": "^3.3.1" } }
7.4 开发依赖
在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖。
使用 npm install 包名 --save-dev 命令将包添加到 package.json 文件的 dependencies 字段中
{ "devDependencies": { "gulp": "^3.9.1" } }
例如:
npm install gulp --save-dev
package.json 文件中多了一个开发依赖devDependencies:
将项目依赖和开发依赖区分的好处是:可以在不同的运行环境,下载不同的依赖。
比如说在线下开发环境,可以在命令行工具输入:npm install 下载所有依赖,
如果是在线上的运营环境(服务器环境),则可以输入: npm install --production 只会下载 dependencies 选项的依赖。
scripts 选项:
在 package.json 文件中,scripts 选择添加 build:
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "nodemon app.js" },
在项目根目录下新建 app.js 文件:
console.log('app.js 文件被执行了');
正常情况下,我们在命令行工具执行,输入:
nodemon app.js
使用别名执行,输入:
npm run build
使用别名语法:
npm run 别名
7.5 package-lock.json 文件的作用
package-lock.json 文件中包含:模块与模块之间的依赖关系,模块的版本,模块的下载地址
8 Node.js 中的模块加载机制
8.1 模块查找规则-当模块拥有路径但没有后缀时
require('./find.js');
require('./find');
1、require 方法根据模块路径查找模块,如果是完整路径,直接引入模块。
2、如果模块的后缀省略,先找同名JS文件,再找同名JS文件夹
3、如果找到了同名文件夹,找文件夹中的 index.js
4、如果文件夹中没有 index.js ,就会去当前文件夹中的 package.json 文件中查找 main 选项中的入口文件
5、如果找指定的入口文件补存在或者没有指定入口文件,就会报错,模块没有被找到。
例子:
新建项目 modulefindRules ,并创建 require.js 文件:
require('./find.js')
和 find.js 文件
console.log('modulefindRules 文件夹下的 find.js 被引用了');
在当然项目的命令行工具中输入:
node require.js
效果图:
修改 require.js 文件:
// require('./find.js') require('./find');
重新执行:
node require.js
发现结果和刚才是一样的,同样打印出了“modulefindRules 文件夹下的 find.js 被引用了”。
修改 find.js 文件名为 find1.js。
新建 find 文件夹,并创建 index.js 文件:
console.log('find 文件夹下的 index.js 被执行了');
这时在命令行工具输入:
node require.js
打印的结果就是“find 文件夹下的 index.js 被执行了”。
在命令行工具中进入 find 目录,然后输入:
npm init -y
发现在 find 文件夹下,多了一个 package.json 文件,打开该文件,并把入口文件修改为:
"main": "main.js",
在 find 文件夹下,新建 main.js 文件:
console.log('find 文件夹下的 main.js 被执行了');
在命令行工具中返回到项目根目录,重新输入:
node require.js
发现打印的结果是:“find 文件夹下的 main.js 被执行了”。
8.2 模块查找规则-当模块没有路径且没有后缀时
require('find');
1、Node.js 会假设它是系统模块
2、Node.js 会去 node_modules 文件夹中
3、首先看是否有该名字的 JS 文件
4、再看是否 有该名字的文件夹
5、如果是文件夹看里面是否有 index.js
6、如果没有 index.js,查看该文件夹中的 package.json 中的 main 选项确认模块入口文件
7、否则找不到报错
例子:
在根目录下新建 node_modules 文件夹,并创建 find.js 文件:
console.log('node_modules 文件夹下的 find.js 被执行了');
回到命令行工具中,输入:
node require.js
发现结果打印出“node_modules 文件夹下的 find.js 被执行了”。
把 node_modules 目录下的 find.js 文件改名为:finde.js,并新建 find 文件夹,创建 index.js 文件:
console.log('node_modules 文件夹中的 find 文件夹下的 index.js 被执行了');
再回到命令行中输入:
node require.js
此时的打印结果是:“node_modules 文件夹中的 find 文件夹下的 index.js 被执行了”。
修改 node_modules 文件夹中 find 文件夹下的 index.js 文件改名为:index2.js。
并在命令行工具中,进入 modulefindRules\node_modules\find 目录,输入:
npm init -y
然后在 modulefindRules\node_modules\find 目录下,多了一个 package.json 文件,修改入口文件为:
"main": "b.js",
在 modulefindRules\node_modules\find 目录下,新建 b.js 文件:
console.log('b.js 文件被执行了');
回到命令行工具的项目根目录下,输入:
node require.js
此时结果打印为:“b.js 文件被执行了”。