【学习笔记】黑马程序员Node.js全套入门教程 | 基础篇

⛄最近要写一些npm命令发现文件读写和路径API忘记了,索性直接复习了一遍NodeJS,边学边忘真的痛苦。

⛄本文包含以下内容:对NodeJS的基础介绍,NodeJS的内置包的简单介绍,CommonJS模块化介绍,npm包下载与发布介绍。

:本文是对黑马程序员Node.js全套入门教程的学习笔记记录,加入了一些自己的练习改动与思考。

推荐大家去看原视频黑马程序员Node.js全套入门教程

初识NodeJs

思考与认识

JS为什么可以在浏览器中被执行

1
2
3
4
5
====浏览器====
待执行的JS代码

JavaScript解析引擎
====浏览器====

浏览器中含有JavaScript解析引擎负责解析JS代码

  • 不同的浏览器使用不同的JavaScript解析引擎:
    • Chrome => V8
    • Firefox => OdinMonkey(奥丁猴)
    • Safri => JSCore
    • IE浏览器 => Chakra(查克拉)
    • 等…

Chrome浏览器的V8解析引擎性能最好

为什么JavaScript可以操作DOM和BOM

1
2
3
4
5
6
====浏览器====
DOMAPI BOMAPI AjaxAPI

待执行的JS代码(调用WebAPI)

JavaScript解析引擎

每个浏览器都内置了DOM、BOM这样的API函数,因此,浏览器中的JavaScript才可以调用它们。

浏览器中的JavaScript运行环境

  • 运行环境是指代码正常运行所需的必要环境
1
2
3
4
5
====Chrome浏览器运行环境====
V8引擎 内置API
↑ ↓
待执行的JavaScript代码
====Chrome浏览器运行环境====
  1. V8引擎负责解析和执行JavaScript 代码。
  2. 内置API是由运行环境提供的特殊接口,只能在所属的运行环境中被调用。

JavaScript能否做后端开发

  • JS可以在浏览器中运行,我们需要通过Node.js让JS代码在服务端运行

Node.js 简介

什么是Node.js

  • Node.js 是一个基于Chrome V8引擎的JavaScript运行环境。
  • 官网地址:Node.js (nodejs.org)

Node.js中的JavaScript运行环境

1
2
3
4
5
====Node.js运行环境====
V8引擎 内置API(fs path http JS内置对象等)
↑ ↓
待执行的JavaScript代码
====Chrome浏览器运行环境====
  • 浏览器是JavaScript 的前端运行环境。
  • Node.js 是JavaScript的后端运行环境。
  • Node.js 中无法调用DOM和BOM等浏览器内置API。

Node.js 可以做什么

Node,js作为一个JavaScript 的运行环境,仅仅提供了基础的功能和API。然而,基于Node.,js提供的这些基础能,很多强大的工具和框架如雨后春笋,层出不穷,所以学会了Node.js,可以让前端程序员胜任更多的工作和岗位。

安装Node.js

如果希望通过Node.,js 来运行Javascript 代码,则必须在计算机上安装Node.js环境才行。

  1. 区分LTS版本和Current版本的不同

    • LTS为长期稳定版,对于追求稳定性的企业级项目来说,推荐安装LTS版本的Node.js。
    • Current 为新特性尝鲜版,对热衷于尝试新特性的用户来说,推荐安装Current 版本的Node.js。但是,Current 版本中可能存在隐藏的Bug 或安全性漏洞,因此不推荐在企业级项目中使用Current版本的 Node.js
  2. 查看已安装的Node.js版本号

    • 打开终端输入node -v,即可查看node.js版本号
  3. 什么是终端

    • 终端(英文: Terminal)是专门为开发人员设计的,用于实现人机交互的一种方式。
  • 使用node <js文件名>运行js文件

常用模块

fs文件系统模块

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

导入模块

1
2
// Nodejs内置模块 无需额外安装
const fs = require('fs')

读取文件内容

  • 使用fs.readFile()方法,可以读取指定文件中的内容
1
fs.readFile(path[, options], callback)
  • 参数解释:
    1. path:必选参数,字符串,表示文件的路径。
    2. options:可选参数,表示以什么编码格式来读取文件。
    3. callback:必选参数,文件读取完成后,通过回调函数拿到读取的结果,该函数会传入两个参数。
      • err:文件读取错误时发生的报错
      • dataStr:文件内容

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs')

fs.readFile('./test.text', 'utf8', function (err, dataStr) {
// 如果读取成功,则err为null
// 如果读取失败,err的值为错误对象
if(err) {
console.log(err)
}
console.log('+++++++++')
// 打印成功的结果
console.log(dataStr)
})

写入文件内容

  • 使用fs.writeFile0方法,可以向指定的文件中写入内容,语法格式如下
  • 该方法只能创建文件,不能创建目录
1
fs.writeFile(file, data[, options], callback)
  • 参数解释:
    1. file:必选参数,需要指定一个文件路径的字符串,表示文件的存放路径。
    2. data:必选参数,表示要写入的内容。
    3. options:可选参数,表示以什么格式写入文件内容,默认值是utf8。
    4. callback:必选参数,文件写入完成后的回调函数,该函数会传入一个参数。
      • err:文件写入错误时发生的报错

示例代码

1
2
3
4
5
6
7
8
9
const fs = require('fs')

fs.writeFile('./write.text', 'Hello FS Module!', 'utf8', function (err) {
// 如果写入成功,则err为null
// 如果写入失败,err的值为错误对象
if (err) {
console.log(err)
}
})

处理路径问题

  • 在使用fs 模块操作文件时,如果提供的操作路径是以./或../开头的相对路径时,很容易出现路径动态拼接错误的问题。
  • 原因:代码在运行的时候,会队执行node命令时所处的目录,动态拼接出被操作文件的完整路径。(相对于用户所在目录,而不是相对于文件目录)

__dirname

  • __dirname表示当前文件所处的目录,更改后可解决路径问题
1
2
3
4
5
6
7
8
const fs = require('fs')

fs.readFile(__dirname + '/test.text', 'utf8', function (err, dataStr) {
// ...
})
fs.writeFile(__dirname + '/write.text', 'Hello FS Module!', 'utf8', function (err) {
// ...
})

path路径模块

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

导入模块

1
2
// Nodejs内置模块 无需额外安装
const fs = require('fs')

路径拼接

  • 使用path.join(方法,可以把多个路径片段拼接为完整的路径字符串,语法格式如下
1
path.join([...path])
  • 参数解释:
    1. path:路径片段的序列()
1
2
3
4
5
6
7
const path = require('path')

const pathStr = path.join('/a', '/b/c', '../', './d', 'e')
console.log(pathStr) // \a\b\d\e

const pathStr2 = path.join(__dirname, './files/1.text')
console.log(pathStr2) // 将相对路径转化为绝对路径
  • 与 __dirname 使用字符串加法的不同
    • 如果使用字符串加法__dirname + './a'会在路径中多出一个点(.)使用path.join可以解决此问题

获取路径中的文件名

  • 使用path.basename()方法,可以获取路径中的最后一部分,经常通过这个方法获取路径中的文件名,语法格式如下
1
path.basename(path[, ext])
  • 参数解释:
    1. path:必选参数,表示一个路径的字符串
    2. ext:可选参数,表示文件扩展名

使用示例

1
2
3
4
5
6
7
8
9
const path = require('path')

const fpath = '/a/b/c/index.html'

let fullName = path.basename(fpath)
console.log(fullName) // index.html

let nameWithoutExt = path.basename(fpath, '.html')
console.log(nameWithoutExt) // index

获取路径中的文件扩展名

  • 使用path.extname)方法,可以获取路径中的扩展名部分
1
path.extname(path)

参数解释:

  1. path:必选参数,表示一个路径的字符串

使用示例

1
2
3
4
5
6
const path = require('path')

const fpath = '/a/b/c/index.html'

let fext = path.extname(fpath)
console.log(fext) // .html

http模块

  • http模块是Node.js 官方提供的、用来创建 web服务器的模块。通过 http模块提供的 http.createServer()方法,就能方便的把一台普通的电脑,变成一台Web服务器,从而对外提供Web资源服务。

  • 服务器和普通电脑的区别在于,服务器上安装了web服务器软件,例如:IIS、Apache等。通过安装这些服务器软件,就能把一台普通的电脑变成一台web服务器。

  • 在Node.js 中,我们不需要使用IIS、Apache等这些第三方web服务器软件。因为我们可以基于Node,js提供的http模块,通过几行简单的代码,就能轻松的手写一个服务器软件,从而对外提供web服务。

  • 基本四步

    1. 导入http模块
    2. 创建web服务器实例
    3. 为服务器实例绑定request事件,监听客户端请求
    4. 启动服务器

导入模块

1
const http = require('http')

创建web服务器实例

1
const server = http.createServer()

为服务器绑定request事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server.on('request', (req, res) => {
console.log('Someone visit our web server.')
const url = req.url // 请求地址
const method = req.method // 请求url

// 根据路径判断返回不同内容
let content = '<h1>404 Not found!</h1>'
if(url === '/' || url === '/index.html') {
content = '<h1>首页</h1>'
}

res.setHeader('Content-Type', 'text/html; charset=utf-8') // 设置响应头
res.end(content) // 向客户端响应内容
})

启动服务器

1
2
3
server.listen(80, () => {
console.log('http server running at http://127.0.0.1')
})

模块化

模块分类

  • Node.js 中根据模块来源的不同,将模块分为了3大类,分别是
    • 内置模块(内置模块是由Node.js官方提供的,例如fs、path、http等)
    • 自定义模块(用户创建的每个.js文件,都是自定义模块)
    • 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前
      需要先下载)

加载模块

1
2
3
4
5
6
7
8
// 加载内置fs模块
const fs = require('fs')

// 加载用户自定义模块
const custom - require('./custom.js')

// 加载第三方模块
const moment - require('moment')
  • 注意:使用require方法加载其它模块时,会执行被加载模块中的代码。

模块作用域

  • 和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问限制,叫做模块作用域。
  • 该作用域防止了全局变量污染的问题

module对象

  • 在每个.js自定义模块中都有一个module对象,它里面存储了和当前模块有关的信息
    • id,path,exports,parent,filename等…

module.exports

  • 在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用。
  • 外界用require()方法导入自定义模块时,得到的就是 module.exports所指向的对象。

代码示例

1
2
3
4
5
6
// moduleA.js
// 导出变量
module.exports.username = 'zs'
module.exports.sayHello = function() {
console.log('Hello!')
}
1
2
3
4
// main.js
// 导入并使用变量
const moduleA = require('./moduleA.js')
moduleA.sayHello()

exports对象

  • 由于module.exports单词写起来比较复杂,为了简化向外共享成员的代码,Node提供了exports对象。默认情况下,exports和module.exports 指向同一个对象。最终共享的结果,还是以module.exports指向的对象为准。
  • 时刻谨记,require)模块时,得到的永远是 module.exports指向的对象
1
2
3
console.log(module.exports) // {}
console.log(exports) // {}
console.log(module.exports === exports) // true

CommonJS规范

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

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

模块加载机制

模块第一次加载后会被缓存,即多次调用 require() 不会导致模块的代码被执行多次,提高模块加载效率。

内置模块加载

内置模块加载优先级最高。

自定义模块加载

加载自定义模块时,路径要以 ./../ 开头,否则会作为内置模块或第三方模块加载。

导入自定义模块时,若省略文件扩展名,则 Node.js 会按顺序尝试加载文件:

  • 按确切的文件名加载
  • 补全 .js 扩展名加载
  • 补全 .json 扩展名加载
  • 补全 .node 扩展名加载
  • 报错

第三方模块加载

  • 若导入第三方模块, Node.js 会从当前模块的父目录开始,尝试从 /node_modules 文件夹中加载第三方模块。
  • 如果没有找到对应的第三方模块,则移动到再上一层父目录中,进行加载,直到文件系统的根目录

例如,假设在 C:\Users\bruce\project\foo.js 文件里调用了 require('tools'),则 Node.js 会按以下顺序查找:

  • C:\Users\bruce\project\node_modules\tools
  • C:\Users\bruce\node_modules\tools
  • C:\Users\node_modules\tools
  • C:\node_modules\tools

目录作为模块加载

当把目录作为模块标识符进行加载的时候,有三种加载方式:

  • 在被加载的目录下查找 package.json 的文件,并寻找 main 属性,作为 require() 加载的入口
  • 如果没有 package.json 文件,或者 main 入口不存在或无法解析,则 Node.js 将会试图加载目录下的 index.js 文件。
  • 若失败则报错

npm与包

npm

  • npm是Nodejs官方的包管理工具。
  • 初次装包完成后,在项目文件夹下多一个叫做node_ modules的文件夹和package-lockjson的配置文件。
  • node_modules 文件夹用来存放所有已安装到项目中的包。require()导入第三方包时,就是从这个目录中查找并加载包。
  • package-lockjson 配置文件用来记录node modules目录下的每一个包的下载信息,例如包的名字、版本号、下载地址等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 初始化npm配置文件
# -y表示使用配置默认选项
# 执行后出现 package.json 文件
npm init -y

# 下载包
npm install <包名称>
# 删除包
npm uninstall <包名称>
# 切换镜像源加速下载
npm config set registry=https://registry.npm.taobao.org/

# 在执行npm install命令时,如果提供了-g参数,则会把包安装为全局包。
# 全局包会被安装到C:\Users\用户目录VAppData\RoamingInpm\node_modules目录下。
npm install <包名称> -g
  • 上述命令只能在英文的目录下成功运行!所以,项目文件夹的名称一定要使用英文命名,不要使用中文,不能出现空格。
  • 运行npm install命令安装包的时候,npm包管理工具会自动把包的名称和版本号,记录到package.json 中。
1
2
3
4
5
6
7
8
9
10
11
12
13
// packge.json
{
"name": "nodeNpm",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

发布包

  • 新建itheima-tools文件夹,作为包的根目录
  • 在itheima-tools文件夹中,新建如下三个文件:
    • package.json(包管理配置文件)
    • index.js(包的入口文件)
    • README.md(包的说明文档)
1
2
3
4
5
6
7
8
9
10
11
12
13
// packge.json
{
"name": "myTool", // 包名称
"version": "1.0.0", // 包版本
"description": "", // 包的描述
"main": "index.js", // 包的入口文件
"scripts": { // 包的可执行指令
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [], // 搜索关键字,使用那些关键字可以搜索到该作者
"author": "", // 包的作者
"license": "ISC" // 包遵循的开源协议
}
1
2
3
4
5
6
7
// main.js

module.export = {
add(a, b){
return a+b
}
}
  • 包根目录中的README.md文件,是包的使用说明文档。通过它,我们可以事先把包的使用说明,以 markdown的格式写出来,方便用户参考。
  • README文件中具体写什么内容,没有强制性的要求;只要能够清晰地把包的作用、用法、注意事项等描述清楚即可。我们所创建的
  • 这个包的 README.md文档中,会包含以下6项内容:
    • 安装方式、导入方式、格式化时间、转义HTML中的特殊字符、还原HTML中的特殊字符、开源协议

npm发布

注册npm账号

  • 访问https://www.npmjs.com/网站,点击 sign up按钮,进入注册用户界面
  • 填写账号相关的信息:Full Name、Public Email、Username、Password
  • 点击Create an Account按钮,注册账号
  • 登录邮箱,点击验证链接,进行账号的验证

登录npm账号

  • npm账号注册完成后,可以在终端中执行npm login命令,依次输入用户名、密码、邮箱后,即可登录成功。
  • 注意:在运行npm login命令之前,必须先把下包的服务器地址切换为npm的官方服务器。否则会导致发布包失败!
1
2
# 输入账号密码后即可成功登录
npm login

发布

1
2
3
4
5
# 将包发布在npm上
npm publish

# 删除发布的包
npm unpublish <包名> --force
  • npm unpublish命令只能删除72小时以内发布的包。
  • npm unpublish 删除的包,在24小时内不允许重复发布发布包的时候要慎重。
  • 尽量不要往npm上发布没有意义的包!

⛄以上便是基础篇的全部内容了,学习后能让你对NodeJS有一个大致的了解。