本篇目标
- 能够搭建 Node 运行环境
- 掌握 NodeJS 程序的运行方法
- 理解模块化开发
- 理解系统模块和第三方模块
- 理解package.json文件作用
1.Node 开发概述
1.1为什么要学习服务器端开发技术
前端人员为什么要学习服务器端开发技术?
- 能够和后端程序员更加紧密的配合
- 网站业务逻辑前置,学习前端技术需要后端技术支撑(Ajax)
- 扩宽知识视野,能够站在更高的角度审视整个项目
1.2服务器端开发做什么
- 实现网站业务员逻辑
- 数据的增删改查
1.3为什么选择Node作为服务器技术
- 使用熟悉的js语法开发后端技术
- 很多一线城市公司的需求
- 生态系统活跃,有大量开源库可以使用
- 前端开发工具大多基于Node开发
1.4Node是什么
Node官网
Node中文网
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时(运行环境),如浏览器也是js的运行环境,Node 也是JS的运行环境,但是将JS代码的应用场景扩展到了服务器端
2.Node运行环境搭建
2.1安装
打开 Node官网
如果不是你想要的版本,点击页面上的“DOWNLOADS” 链接,打开下载页面
下载完成之后,双击安装程序
下面以截图的方式展现安装过程,基本一路默认即可,需要注意的是安装目录不能是中文
安装过程
注意:安装完成后,桌面上并没有什么快捷方式,因为Node仅仅是一个JS的执行环境
使用Node需要用到命令行工具
如何测试Node是否安装成功呢?
打开命令行工具,这里可以使用传统的 cmd 工具,也可以使用win10中的 powershell
我们这里使用后者
按下键盘上的 windows+S 键,输入 powershell ,会搜索到powershell 工具
或者在任意位置,比如桌面,按下 shift 键同时,按下鼠标右键, 也会显示 powershell 工具
打开命令行后,输入如下命令
node -v
如果能够显示版本号,就表明node安装成功
2.2环境安装失败解决办法
2.2.1 错误代号 2502/2503
表示当前登录系统的用户权限不足。
解决方法:
- 以管理员身份运行 powershell 工具
- 属于运行安装包命令:msiexec /package node安装包位置,如我安装包在如下位置
则输入如下命令进行安装
2.2.2 环境变量问题
在 powershell 中输入 node -v 时,会提示 node 不是可识别的命令类似的错误信息,原因在于,没有将node可执行程序路径添加到环境变量中
环境变量添加方法,这里不再演示
3. Node 入门
3.1 Node.js 组成
- JavaScript 由三部分组成,ECMAScript,DOM,BOM
- Node.js是由ECMAScript及Node 环境提供的一些附加API组成的,包括文件、网络、路径等等一些更加强大的 API
3.2 基础语法和运行
首先,所有前面学习过的 ECMAScript 语法在Node中都可以使用,包括ES6的语法
3.2.1 运行node程序
那么,在Node环境下该如何执行我们的JS代码呢?
按照如下步骤,我们开始第一个node程序的编写和执行
-
打开任意你熟悉的开发工具,如sublime、vs code,或者记事本
-
新建文件 hello.js,注意这里的文件后缀名是 .js ,而不是 .html
-
编写如下测试代码
let ary = [12, 3, 4, 5] ary.forEach(item => console.log(item))
-
找到 hello.js 文件的位置,然后按下 shift 键的同事点击鼠标右键,选择“在此处打开powershell 窗口”
-
在命令行中输入如下命令
node hello.js
敲击回车键后,就可以执行 hello.js 中的代码了
3.2.2 总结
通过上面代码的编写和执行,我们总结如下几点
- node 文件的后缀名都是.js
- 以前的学习中,都是将js文件引入到html中,以运行html文件的方式执行js代码,但node是服务器技术,在node技术中,不存在 dom 和 bom,所以运行方式与以前是不一样的
- 运行node程序的方式是使用 node 命令加上文件名称的方式
- 除了在命令行中运行node程序的方式外,很多开发工具,如vs code内部,也可以运行node程序
3.3 global
以前学习过,console 是 window 的全局对象,但是在 node 中,global 才是全局对象
Node中全局对象下有以下方法,可以在任何地方使用,global可以省略
- console.log() 在控制台中输出
- setTimeout() 设置超时定时器
- clearTimeout() 清除超时时定时器
- setInterval() 设置间歇定时器
- clearInterval() 清除间歇定时器
新建一个 global.js 文件,编写下面代
global.setTimeout(() => {console.log('timeout')
}, 2000);
运行 global.js 文件,进行测试
证明,上面的这些API确实是全局对象 global 中的
4.模块化开发
4.1 现有的弊端
在我们不算长的js代码编写经历中,可能体会到或者体会不到,js开发有如下弊端
- 文件依赖:文件依赖不清楚,需要人为的去分析
- 命名冲突:两个js文件中,可能存在同名的变量或者文件,会出现后面文件覆盖掉前面文件的问题
举例说明:
4.2 软件中的模块化开发
一个功能就是一个模块,多个模块可以组成完整应用,抽离一个模块不会影响其他功能的运行
4.3 Node 中的模块化开发规范
那么到底如何做到模块化开发呢?
- Node.js规定一个JavaScript文件就是一个模块,模块内部定义的变量和函数默认情况下在外部无法得到
- 模块内部可以使用exports对象进行成员导出, 使用require方法导入其他模块。
注意:这里仅仅是Node中的模块化开发规范,ES6也规定了自己的模块化开发规范
代码演示:
新建 m.js,编写如下代码
let name='李白'
var sum=(a,b)=>a+b
exports.name=name
exports.sum=sum
新建 n.js,编写如下代码
const m=require('./m.js')
console.log(m.dd)
console.log(m.ss(4,5))
运行 n.js ,可以看到结果
小编解惑
需要注意如下几点
- 在n.js 中引入 m.js 时,可以省略后面的.js
- 引入 m.js 时,需要使用如下领“./m.js”
- m.js中,exports 后面的属性名称可以使用任何名称
4.4 Node 中两种模块成员导出方法对比
exports.name=name
module.exports.name=name
上面案例用到了 exports.属性=属性值 的方式进行模块成员导出
还可以使用 module.exports.属性=属性值 的方式进行模块成员导出
总结:
exports是module.exports的别名(地址引用关系),导出对象最终以module.exports为准
下面证明这个结论
比如,在m.js 中编写如下代码
let name='李白'
let age=20
exports.name=name
module.exports.age=age
在 n.js 中编写下面代码
const m=require('./m.js')
console.log(m)
输出结果
{ name: '李白', age: 20 }
课件,exports 与 module.exports 确实是一个地址,也就说最后修改的都是 module.exports
再次修改 m.js
let name='李白'
let age=20
module.exports.name=age
exports.name=name
再次运行 n.js
{ name: '李白' }
在 exports 和 module.exports 中都设置了name属性,但一个将值设置成了 age 成员,一个设置成了 name 成员,最后发现还是以 module.exports 为准
这就证明了上面的结论
5.系统模块
5.1 什么是系统模块
Node运行环境提供的API. 因为这些API都是以模块化的方式进行开发的, 所以我们又称Node运行环境提供的API为系统模块
小编解惑
在前面,我们学了很多JS的API,如 querySelector、getElementById、offsetLeft 等,这些都是浏览器提供的跟Dom或者Bom相关的功能,只不过这些API都是没有任何关系的分散的。
Node 中,也提供了很多的API,但是将这些API根据烈性存放在不同的模块中,如文件模块中就包含了很多能够操作磁盘文件的API
所以,我们可以总结如下:
- 所谓模块化,就是将相关API根据类型划分,存放到不同的文件中
- 系统模块,就是 Node 预先已经定义好的一些API的集合
- 除了系统模块,开发者还可以开发第三方模块,其他开发者都可以使用,如我们前面自己定义的m.js也是一个第三方模块
下面通过几个系统模块的讲解,掌握 node 中代码编写的基本规则,以及模块引入和使用的方法
5.2 文件操作
Node.js中文手册
5.2.1 读取文件
语法:
fs.readFile(path[, options], callback)
先看案例
const fs=require('fs')
fs.readFile('m.js','utf8',(err,data)=>{console.log(err)console.log(data)
})
小编解惑
- 系统模块的引入方式也是使用 require
- 模块名称要使用引号包含起来
- readFile是一个异步方法,所以需要回调函数接收读取结果
- 回调函数中,err 获取读取中的错误信息,如果读取过程中没有出现错误,则结果为null;data 为读取的文件内容
所以一般在处理读取的文件之前,先判断err 的值
const fs = require('fs')
fs.readFile('m1.js', 'utf8', (err, data) => {if (err) throw err;console.log(data)
})
5.2.2 写入文件
语法
fs.writeFile(file, data[, options], callback)
案例
const fs = require('fs')
fs.writeFile('./demo.txt', '说点什么呢', err => {if (err) throw err;console.log('文件写入成功')
})
小编解惑
- 回调函数只有一个参数 err
5.3 路径操作
5.3.1 路径拼接
语法
path.join([...paths])
使用平台特定的分隔符作为定界符将所有给定的 path
片段连接在一起,然后规范化生成的路径。
小编解惑
因为不同平台的路径分隔符不统一,为了防止将路径分隔符写死,影响了程序的跨平台特性,所以需要使用路径拼接
案例
const path=require('path')
let res=path.join('public','static','images','time.jpg')
console.log(res)
windows 平台运行结果
public\static\images\time.jpg
linux 平台运行结果
public/static/images/time.jpg
5.3.2 使用相对路径还是绝对路径
- 大多数情况下使用绝对路径,因为相对路径有时候相对的是命令行工具的当前工作目录
- 在读取文件或者设置文件路径时都会选择绝对路径
- 使用__dirname获取当前文件所在的绝对路径
代码演示:
新建文件,编写下代码
const fs=require('fs')
fs.readFile('m.js','utf8',(err,data)=>{if(err) throw errconsole.log(data)
})
在此文件所在目录中,打开命令行工具,并运行此文件,发现没有问题
若在命令行工具中,使用如下命令,向上返回一级目录
cd ..
再次执行命令
所以我们需要在代码中,根据当前编写的文件路径,获取绝对路径,然后将文件路径拼接成绝对路径,就没问题了
小编解惑
命令行工具目录,就是你打开命令行工具时的目录
如你在桌面上打开了命令行工具,那么目录就是
如果在D盘根目录下打开了命令函工具,那么目录就是
但是在代码编写时,我们其实希望以当前正在编写的文件为基础作为相对路径
6. 第三方模块
别人写好的、具有特定功能的、我们能直接使用的模块即第三方模块,由于第三方模块通常都是由多个文件组成并且被放置在一个文件夹中,所以又名包。
第三方模块有两种存在形式:
- 以js文件的形式存在,提供实现项目具体功能的API接口,此种模块多为本地安装
- 以命令行工具形式存在,辅助项目开发,此种模块多为全局安装
6.1 如何获取第三方模块
https://www.npmjs.com/
开发者将开发好的第三方模块,上传到上面网站,其它开发者可以从上面下载
下载方式为命令行方式下载
下面是下载和卸载第三方模块的基本语法
- 下载:npm install 模块名称
- 卸载:npm unintall package 模块名称
根据模块的作用,分为全局安装和本地安装
- 本地安装:模块被下载到命令行工具所在目录下,只能当前项目使用
- 全局安装:模块被下载全局目录下,所有项目都可以使用
6.2 安装演示
6.2.1 本地安装
npm install jquery
安装完成后,会在当前目录下创建 node_modules 目录
卸载
npm uninstall jquery
6.2.2 全局安装
以 nodemon 模块为例
nodemon是一个命令行工具,用以辅助项目开发。
在Node.js中,每次修改文件都要在命令行工具中重新执行该文件,非常繁琐。
安装
npm install nodemon -g
卸载
npm uninstall nodemon -g
6.2.3 安装慢怎么办
因为 npmjs.com 的服务器在国外,所有有时候下载速度很慢,或者总是断开
解决方案就是更换下载源,最好是国内的下载源
解决方案有两个
- 安装 nrm,通过nrm更换下载源,更换之后,仍然使用 npm 命令安装模块
- 安装 cnpm,以后通过cnpm 安装,安装后,使用 cnpm 安装模块
6.2.3.1 nrm
通过安装 nrm 的方式,可以选择和切换下载源
使用步骤
- 使用npm install nrm –g 下载它
- 查询可用下载地址列表 nrm ls
- 切换npm下载地址 nrm use 下载地址名称
- 下载模块 npm install 模块名称
小编解惑
在镜像源中,cnpm 和 taobao 其实是一样的,所以选择 taobao 还是 cnpm 都可以
6.3.2.2 cnpm
除了将下载地址切换为 cnpm 外,还可以直接安装 cnpm
使用 npm 安装 cnpm,同事将下载源更换为taobao
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装成功后,使用 cnpm 命令安装第三方模块,如
cnpm install jquery
说明:cnpm 只是基于npm 的一个小公举
6.3 yarn
上面学习的 npm 是基于node.js 的一个包管理工具,除此之外,还有yarn,现在也非常流行
yarn 是现在非常流行的一个基于node.js的包管理工具,与 npm 可以说是双雄争霸
注意:yarn 与 cnpm 是不同的
6.3.1 yarn 安装
yarn安装
除了下载安装包外,还可以从npm 中安装yarn
下面使用 npm 包安装 yarn(虽然yarn 是与npm 竞争关系,但是npm 还是和开放的允许安装 yarn)
npm install yarn -g
执行下面命令,查询yarn命令列表
yarh -h
查询 yarn 版本
yarn -v
6.3.2 yarn 安装和模块
新建一个项目,然后打开命令行工具,执行如下命令
yarn add jquery
删除模块
yarn remove jquery
小编解惑
- 一个项目中,不要同事使用npm 和 yarn 进行包管理
- npm 和 yarn 的能力,我们仅仅使用了万分之一,更多功能,后面再讲
7. package.json 文件
7.1 node_modules 的问题
我们发现,当使用 npm 安装模块时,会创建 node_modules 目录,此目录中存储下载的模块及其依赖的模块
这个目录中的文件存在两个问题
- 文件夹以及文件过多过碎,当将项目整体拷贝给别人时,传输速度很慢(事实上,node项目的体积主要就是node_modules)
- 复杂的模块依赖关系需要被记录,确保模块的版本和当前保持一致,否则会导致当前项目运行报错
7.2 package.json 解决问题
在个项目拷贝给他人时,删除 node_modules,他人再使用 nmp 命令进行恢复安装
这就需要一个文件能够记录,当前项目都使用了哪些模块,这个文件就是 package.json
创建package.json
默认情况下,是没有此文件的,可以使用命令生成此文件
新建项目,然后在此项目下打开命令行工具,运行如下命令
npm init -y
package.json 文件内容如下
{"name": "package_demo","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "","license": "ISC"
}
安装模块
npm install debug needle
观察 package.json 文件变化
删除node_modules
将项目拷贝给别人时,直接删除 node_modules 目录即可
恢复安装
运行如下命令,即可根据package.json 中的记录恢复安装
npm install
7.3 项目依赖和开发依赖
- 项目依赖
- 在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖
- 使用npm install 包名命令下载的文件会默认被添加到 package.json 文件的 dependencies 字段中
- 开发依赖
- 在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖
- 使用npm install 包名 --save-dev命令将包添加到package.json文件的devDependencies字段中
案例演示:
使用如下命令安装开发依赖包:gulp
npm install gulp --save-dev
查看package.json
删除 node_modules 目录
使用如下命令恢复项目依赖安装
npm install --production
查看 node_modules 中的文件变化
再次删除 node_modules,然后使用如下命令恢复开发依赖安装
npm install
发现node_modules 中多了很多文件
8. package.lock.json
此文件在第一次使用npm下载模块时就会被创建
作用如下:
- 锁定包的版本,确保再次下载时不会因为包版本不同而产生问题
- 加快下载速度,因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可,不需要做额外的工作
9. 模块加载规则
9.1 模块拥有路径时
分成下面两种情况
require('./yhb.js') // 拥有路径和后缀
require('./yhb') // 拥有路径,但没有后缀
- require方法根据模块路径查找模块,如果是完整路径,直接引入模块
- 如果模块后缀省略,先找同名JS文件再找同名JS文件夹
- 如果找到了同名文件夹,找文件夹中的index.js
- 如果文件夹中没有index.js就会去当前文件夹中的package.json文件中查找main选项中的入口文件
- 如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到
9.2 没有路径
require('yhb')
- Node.js会假设它是系统模块
- Node.js会去node_modules文件夹中
- 首先看是否有该名字的JS文件
- 再看是否有该名字的文件夹
- 如果是文件夹看里面是否有index.js
- 如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件
- 否则找不到报错
小编解惑
如果模块没有路径,node会认为这是一个系统模块
如果模块拥有路径,node会认为这是开发者自己定义的模块文件