Node开发概述
目录:
- Node开发概述
- Node运行环境搭建
- Node.js快速入门
为什么要学习服务器端开发基础
- 能够和后端程序员更加精密的配合
- 网站业务逻辑前置,学习前端技术需要后端技术支撑(Ajax)
- 扩宽知识视野,能够站在更高的角度审视整个项目
服务器端开发要做的事情
- 实现网站的业务逻辑(比如用户实现登录,服务器端开发人员要获取到用户输入账号和密码,比对账号是否在页面中是否注册过,再比对输入的密码是否正确,如果全部信息正确,则登录成功,否则登录失败)
- 数据的增删改查(比如购物车的管理页面)
为什么选择Node
后端开发有很多种语言,比如Java,PHP,.net;那为什么要选择Node呢
原因:
- 使用JavaScript语法开发后端应用
- 一些公司要求前端开发工程师掌握Node开发(项目有node开发内容)
- 生态系统活跃,有大量开源库可以使用(插件可以用,大大提升开发效率)
- 前端开发工具大多基于Node开发
Node是什么
Node是基于Chrome V8引擎的JavaScript代码运行环境
什么是运行环境?
- 浏览器(软件)能够运行JavaScript代码,浏览器就是JavaScript代码的运行环境
- Node(软件)能够运行JavaScript代码,Node就是JavaScript代码的运行环境
Node运行环境搭建
Node.js运行环境安装
- LTS = Long Term Support 长期支持版 稳定版(使用)
- Current 拥有最新特性 实验版
在安装目录选择的时候千万不要使用中文名称的文件夹
查看是否安装成功:
打开powershell或者cmd 输入 node -v,只要显示版本号就代表成功了
Node.js环境安装失败解决办法
msi是windows下安装文件的后缀 exec是运行的意思
Path环境变量
存储系统中的目录,在命令行中执行命令的时候,系统会自动去这些目录中查找命令的位置
如果想要直接在控制台中打开一个文件,则必须把这个exe文件所在的目录放入path环境变量中
Node.js快速入门
Node.js的组成
JavaScript 由三部分组成,ECMAScript,DOM,BOM
ECMAScript规定了JS的语法,是核心,而DOM和BOM是针对浏览器的API,以便更好的控制。
Node.js是由ECMAScript及Node环境提供的一些附加API组成的,包括文件、网络、路径等等一些更加强大的API
在这里,ECMAScript依旧是核心,API可以获得操作系统的信息,读取硬盘上的文件内容。
node应用由模块组成,采用的commonjs模块规范。
Node.js的基本语法
所有ECMAScript语法在Node环境中都可以使用
新建js文件
输入内容:
var first = 'hello ,nodejs'
console.log(first);
// 定义一个函数操作
function fn() {
console.log('fn函数被调用了')
}
fn();
// 定义一个for循环
for (var i = 0; i < 5; i++) {
console.log(i);
}
if (true) {
console.log('right');
}
在客户端执行上述代码都是写好html页面然后将js文件引入进去。而在node中,没有html,需要借助命令行工具执行这个工具。
打开powershell或者按“Ctrl+~”弹出终端
输入:
PS C:\Users\deMonlb> cd Desktop\Node.js
进入上述编写的文件的根目录
然后运行文件,运行:PS C:\Users\deMonlb\Desktop\Node.js> node 01-Node.js的基本语法.js
结果成功输出
第二种方法打开powershell
在文件根目录按住shift键再点击右键,打开powershell窗口
这样就可以直接在这个目录中打开
同时打开文件也不需要那么繁琐,直接 输入“node 01”,然后按tab键就可以直接自动补全要打开的文件了
(.\ 表示当前目录)
clear可以直接全部清空,cls也可以
Node.js模块化开发
JavaScript开发弊端
JavaScript在使用时存在两大问题,文件依赖(文件与文件之间依赖不明确的关系)和命名冲突(导致代码覆盖的问题)。
依赖不明确导致重复引包,然后耽误开发效率
两个num不会报错,还而是会直接覆盖,使程序存在潜在的不确定性。应该让文件处于一种半封闭的状态,使一部分的代码暴露出去,而另一部分代码封闭起来。
生活中的模块化开发
组装电脑:
这样做的好处是:若零件损坏只需更换零件,不需整机更换
软件中的模块化开发
一个功能就是一个模块,多个模块可以组成完整应用,抽离一个模块不会影响其他功能的运行
Node.js中模块化开发规范
- Node.js规定JavaScript文件就是一个模块,模块内部定义的变量和函数默认情况下在外部无法得到。
- 模块内部可以使用exports对象进行成员导出,使用require方法导入其他模块。
假如有一个A模块,(a.js)里面有四个方法
假如呢现在有个B模块,也就是b.js,想要在B模块中使用A模块的方法
怎么做呢?
首先将想要导出的方法使用exports导出,因为这些方法在默认情况下在外部无法访问;导出方法:就是将这些方法变成exports的属性值就可以了
导出后,若想在B模块中使用这些方法,就需要在B模块中使用require方法,将A模块进行导入;require方法有返回值,返回的是A模块的exports对象。
使用方法:使用点方法进行调用
模块成员导出
hello ${name} 相当于 ‘hello’+name
模块成员导入
模块成员导入、导出练习
第一步:新建module-a.js和module-b.js
在module-a.js中写入:
const add = (n1, n2) => n1 + n2;
// exports点后的add是exports的属性(自己起),第二个add是函数名
exports.add = add;
在module-b.js中写入:
const a = require('./02-module-a.js')
console.log(a);
console.log(a.add(10, 20));
两个console.log的结果分别为:
注意:
在导入模块的时候后缀可以省略(.js)
模块成员导出的另一种方式
新建两个文件分别为:
在03-module.exports.js中输入:
const greeting = name => `hello ${name}`;
module.exports.greeting = greeting;
若转换为ES6模块
const _greeting = greeting;
export { _greeting as greeting };
在03-module.require.js中输入:
const a = require('./03-module.exports')
console.log(a.greeting('pdd'));
若转换为ES6模块
import { greeting } from './03-module.exports';
console.log(greeting('pdd'));
综上发现,前面加不加module都可以返回模块成员,那么他们之间有什么关系呢?
exports是modules.exports的别名(地址引用关系),导出对象最终以module.exports为准
模块导出两种方式的联系和区别
联系就是:如果两者指向相同,那么输出结果相同
区别就是:如果修改了module.exports的指向,让它指向了一个新的对象,那么exports的指向也会相应改变;但修改exports则不会影响module.exports
若修改为以下代码:
const greeting = name => `hello ${name}`;
const x = 100;
exports.x = x;
module.exports.greeting = greeting;
module.exports = {
name: 'pdd',
}
结果为:
修改之后发现:存储的变量为这个新的对象了
当exports对象和module.exports对象指向的不是同一个对象时,以module.exports为准。
若修改:
const greeting = name => `hello ${name}`;
const x = 100;
exports.x = x;
module.exports.greeting = greeting;
module.exports = {
name: 'pdd',
}
exports = {
name: 'wwk'
}
结果为:
则修改无效,因为以module.exports的导出为准
系统模块
什么是系统模块
Node为运行环境提供API,因为这些API都是以模块化的方式进行开发的,所以我们又称Node运行环境提供的API为 系统模块。
系统模块fs文件操作
f:file文件,s:system系统,文件操作系统
比如说要使用文件操作系统,在使用之前要引入他,比如
const fs = require('fs')
,require里面的参数fs是模块的名字,等号前的fs是require方法的返回值,也就是exports对象,暴露了和文件系统相关的API。
再比如:
读取文件内容:
fs.readFile('文件路径/文件名称'[,'文件编码'],callback);
代码的执行逻辑为:找到文件,读取文件内容,然后返回客户端。
最后一个参数是个回调函数,其作用是:硬盘在读取内容,在读取的过程中呢需要时间,所以不能通过这个API的返回值拿到文件的读取结果,所以要定义一个回调函数,当文件内容读取完成之后呢,硬盘会通知API文件调完了,会将函数的结果以回调函数的参数的形式传递过来。
读取文件内容
读取文件语法的示例
回调函数的第一个参数为err,其他API的函数也都是如此,因此称node.js为错误优先的回调函数。
自我测试如下
新建04-readFile.js
// 1.通过模块的名字fs对模块进行引用
const fs = require('fs');
// 2.通过模块内部的readFile读取文件内容
fs.readFile('./01-Node.js的基本语法.js', 'utf8', (err, doc) => {
// 如果文件读取出错 err是一个对象 包含错误信息
// 如果文件读取正确 err是null
// doc 是文件读取的结果
console.log(err); //null
console.log(doc); // 文件的内容
})
结果:
写入文件内容
fs.writeFile('文件路径/文件名称','数据',callback);
监控网站在运行过程之中是否有报错的情况,但是程序员不会一直盯着电脑的,我们希望当程序出错时能将错误写入错误日志之中,这就是错误日志的作用
语法:
const content = '<h3>正在使用fs.writeFile写入文件内容</h3>'
fs.writeFile('../index.html', content, err => {
if (err != null) {
console.log(err);
return;
}
console.log('文件写入成功');
});
在05-writeFile.js
中写入
const fs = require('fs')
// 如果输出的文件没有的话,则自动创建
fs.writeFile('./demo.txt', '即将要写入的内容', err => {
if (err != null) {
console.log(err);
return;
}
console.log('文件内容写入成功');
})
在终端运行之后,会发现在终端中输出:
然后在文件夹中多了一个demo.txt文件
里面的内容是
相对路径VS绝对路径
- 大多数情况下使用绝对路径,因为相对路径有时候相对的是命令行工具的当前工作目录
- 在读取文件或者设置文件路径时都会选择绝对路径
新建文件relativeOrAbsolute.js
相对路径方式:
const fs = require('fs');
// 相对路径方式
fs.readFile('./01-Node.js的基本语法.js', 'utf-8', (err, doc) => {
console.log(err);
console.log(doc);
})
绝对路径方式:需要使用路径拼接
使用__dirname获取当前文件所在的绝对路径
const fs = require('fs'); // C:\Users\deMonlb\Desktop\Node.js
const path = require('path'); // C:\Users\deMonlb\Desktop\Node.js\01-Node.js的基本语法.js
console.log(__dirname);
console.log(path.join(__dirname, '01-Node.js的基本语法.js'));
// 此处的dirname是当前文件所在的绝对路径也就是Node.js这个目录,然后在这个目录的基础上拼接上 01-Node.js的基本语法.js。拼接之后呢就是01-Node.js的基本语法.js的绝对路径了。
fs.readFile(path.join(__dirname, '01-Node.js的基本语法.js'), 'utf-8', (err, doc) => {
console.log(err);
})
使用绝对路径比较安全,因为无论开始的文件夹是什么,都能正常执行文件
Package.json文件
node_modules文件夹问题
就下载了几个插件,打开这个文件夹发现居然有300+个文件夹
就会有以下两个问题:
1.文件夹以及文件过多过碎,当我们将项目整体拷贝给别人的时候,传输速度会很慢
2.复杂的模块依赖关系需要被记录,确保模块的版本和当前保持一致,否则会导致当前项目运行报错。
那么npm是怎么解决这些问题的呢?
package.json文件的作用
其实node_modules文件夹不需要被传输,只需依赖package.json文件即可,根据他的描述再对应下载依赖项即可
项目描述文件,记录了当前项目信息,例如项目名称、版本、作者、GitHub地址、当前项目依赖了哪些第三方模块等。
生成一个package.json文件
使用npm init -y 命令生成
-y即yes,全部设置使用默认值
第一步:建立description文件夹
第二步:输入命令行
npm init -y
package.json文件内容解读
{
"name": "description",
// 项目名称
"version": "1.0.0",
// 项目版本
"description": "",
// 项目描述,让别人了解当前项目的功能和内容
"main": "index.js",
// 项目的主入口文件
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
// 存储的是命令的别名,命令比较长的时候,很麻烦,给命令一个别名,执行别名即可
"keywords": [],
// 关键字,用其来描述当前的项目
"author": "",
"license": "ISC"
// 项目遵循的协议,ISC是开放源代码的协议
}
只要package.json存在,那么通过npm下载的内容就会自动记录在这个文件之中
第三步:使用npm工具下载一个第三方模块
命令行中输入:
npm i formidable mime
试验一下其显示的内容
下载好之后打开package.json文件发现多了内容:
"dependencies": {
"formidable": "^1.2.2",
"mime": "^2.4.6"
}
因此:
在传递项目的时候不需要将node_modules文件夹一起传递,只需要将项目和package.json文件传递。
之后使用命令:
npm install
则就会自动根据package.json文件中的dependencies自动下载需要的内容
项目依赖
- 在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖
- 使用npm install包名命令下载的文件会默认被添加到package.json文件的dependencies字段中
比如jQuery
开发依赖
- 在项目的开发阶段需要的依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖(比如gulp)
- 使用 npm install 包名 --save -dev 命令将包添加到package.json文件的devDependencies字段中
比如gulp
通过存储的字段不同,来区分项目依赖和开发依赖
以此区分可以让我们在不同的环境下,下载不同的依赖
- 如果是开发环境,使用 npm install 就可以下载项目和开发依赖
- 如果是生产环境(线上服务器运行环境),使用 npm install --production就可以至下载生产环境需要的内容
package-lock.json文件的使用
- 锁定包的版本,确保再次下载时不会因为包版本而产生问题
- 加快下载速度,因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装的时候只需要下载即可,不需要做额外的工作
package.json中scripts文件的作用:
打开package.json文件,在scripts中加入:
"build": "nodemon app.js"
新建app.js文件:
发现无论以哪种方式运行项目都可以成功运行。
起别名的好处:方便使用
Node.js中模块加载机制
模块查找规则-当模块拥有路径但没有后缀时
当查找到的是一个完整路径时,会顺着这个路径找到模块,加载模块
e.g.
require('./find.js')
当查找时,路径完整,但是缺失后缀,此时要怎么查找呢?
e.g.
require('./find')
// 如果模块后缀省略,先找同名JS文件再找同名JS文件夹
// 如果找到了同名文件夹,就找该文件夹中的index.js
// 如果文件夹中没有index.js就会去当前文件夹中package.json文件中查找main选项中的入口文件
// 如果找到指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到
接下来来验证一下:
第一步:到目录文件夹下新建文件夹modulefindRules和两个js文件
第二步:分别在两个js文件中写入内容
如果使用node方法引用require.js能正常输出log里的内容,则说明能够正常引用
第三步:
在终端输入
node require.js
发现正常引用
第四步:
如果尝试将
require('./find.js')
改为
require('./find')
则再使用node命令时,依旧能够正常输出log里的值
如果这时没有find.js,而又有find这个文件夹并且里面有index.js文件,则index.js里的内容会正常输出。
如果又没有find.js,且没有index.js,而有package.json(npm init -y执行生成),其里面将默认执行文件指向main.js,并且main.js存在,则会执行main.js
main选项指向哪个JS文件则说明执行哪个JS文件
模块查找规则-当模块没有路径且没有后缀时
e.g.
require('find');
- Node.js会假设它是系统模块(有则执行,没有则执行下一步)
- Node.js会去node_modules文件夹中
- 首先看是否有该名字的JS文件(有则执行,没有则执行下一步)
- 再看是否有该名字的文件夹(有则执行,没有则执行下一步)
- 再看该文件夹中里面是否有index.js
- 如果没有index.js,则查看该文件夹中的package.json中的main选项确定模块入口文件
- 否则找不到报错