node.js知识积累
node.js
node.js基础
1. Node.js 简介
Node.js 是一个开源与跨平台的 JavaScript 运行时环境。 它是一个可用于几乎任何项目的流行工具!
Node.js 在浏览器外运行 V8 JavaScript 引擎(Google Chrome 的内核)。 这使 Node.js 表现得非常出色。
Node.js 应用程序运行于单个进程中,无需为每个请求创建新的线程。 Node.js 在其标准库中提供了一组异步的 I/O 原生功能(用以防止 JavaScript 代码被阻塞),并且 Node.js 中的库通常是使用非阻塞的范式编写的(从而使阻塞行为成为例外而不是规范)。
当 Node.js 执行 I/O 操作时(例如从网络读取、访问数据库或文件系统),Node.js 会在响应返回时恢复操作,而不是阻塞线程并浪费 CPU 循环等待。
这使 Node.js 可以在一台服务器上处理数千个并发连接,而无需引入管理线程并发的负担(这可能是重大 bug 的来源)。
Node.js 具有独特的优势,因为为浏览器编写 JavaScript 的数百万前端开发者现在除了客户端代码之外还可以编写服务器端代码,而无需学习完全不同的语言。
在 Node.js 中,可以毫无问题地使用新的 ECMAScript 标准,因为不必等待所有用户更新其浏览器,你可以通过更改 Node.js 版本来决定要使用的 ECMAScript 版本,并且还可以通过运行带有标志的 Node.js 来启用特定的实验中的特性。
学习 Node.js 不是学习一种新的语言,而是一门技术
node 利用 JavaScript 语言作为基础,并提供了一些模块,开发者使用JavaScript 调用这些模块中的方法,就可以实现例如读写文件、操作数据库等功能
比如 unity3d 这门技术利用了早已经诞生的 c# 语言作为开发语言…
大量的库
npm 的简单结构有助于 Node.js 生态系统的激增,现在 npm 仓库托管了超过 1,000,000 个可以自由使用的开源库包。
2. Node.js安装
官网下载:https://nodejs.org/zh-cn/download/
根据自己的电脑型号选择安装包下载
下载后双击安装程序
然后根据以下截图一直按下一步操作就行了
安装过程:
安装完成:
注意:安装完成后,桌面上并没有什么快捷方式,因为Node仅仅是一个JS的执行环境,我们可以通过Windows PowerShell进行验证
输入命令:
node -v
当出现安装的版本时,就说明安装成功了
3. 配置全局变量
当我们安装成功后我们要经行全局变量的配置
Node.js 入门
1. Node.js 组成
- JavaScript 由三部分组成,ECMAScript,DOM,BOM
- Node.js是由ECMAScript及Node 环境提供的一些附加API组成的,包括文件、网络、路径等等一些 更加强大的 API
Dom: 操作页面中的元素
Bom: 操作浏览器
2. 基础语法和运行
首先,所有前面学习过的 ECMAScript 语法在Node中都可以使用,包括ES6的语法
1. 运行 node 程序
编写一个文件名为 hello.js的文件,编写内容
let a= 10
console.log(a)
打开终端,输入命令
// node 文件名.js
node hello.js
在此注意,如果你的文件名比较长,你害怕写错,可以先输入 node
然后输入空格,输入h+tab会自动补全文件名,敲击回车键后,就可以执行 hello.js 中的代码了
如果不喜欢在终端中打开,我们还可以在命令窗口打开
-
打开文件所在的文件夹
-
按住Shift,右击,选择在此处打开 PowerShell 窗口,输入node 打开文件命令也可以运行文件
总结:
通过上面代码的编写和执行,我们总结如下几点 :
- node 文件的后缀名都是.js
- 以前的学习中,都是将js文件引入到html中,以运行html文件的方式执行js代码,但node是服务器 技术,在node技术中,不存在 dom 和 bom,所以运行方式与以前是不一样的
- 运行node程序的方式是使用 node 命令加上文件名称的方式
- 除了在命令行中运行node程序的方式外,很多开发工具,如vs code内部,也可以运行node程序
3. npm
由于我们每次输入都要执行一次命令来刷新程序,会导致代码运行特别麻烦,所以我们需要一个自动刷新浏览器和运行的工具
而在node.js中有一个非常厉害的工具叫npm,他是一个包管理工具
NPM的全称是Node Package Manager,是一个NodeJS包管理和分发工具,已经成为了非官方的发布Node模块(包)的标准。
Nodejs自身提供了基本的模块,但是开发实际应用过程中仅仅依靠这些基本模块则还需要较多的工作。幸运的是,Nodejs库和框架为我们提供了帮助,让我们减少工作量。但是成百上千的库或者框架管理起来又很麻烦,有了NPM,可以很快的找到特定服务要使用的包,进行下载、安装以及管理已经安装的包。
我们可以从它的官网上 https://www.npmjs.com/ 搜索你所需要安装的工具包,里面会有他的安装命令和方法,例如搜索jquery:
找到node,就可以看到他的安装命令
我们把命令输入到终端中,就可以安装上了
当我们安装完毕之后,目录下会出现一个 node_modules文件夹,这个文件夹里放着我们所安装的所有安装包,打开会看到你会看到刚刚安装的jquery文件夹。
而我们正需要就是nodemon工具
nodemon工具
Nodemon是一种帮助开发基于node.js的应用程序的工具,方法是在检测到目录中的文件更改时自动重新启动节点应用程序。
Nodemon不会对你的代码产生额外的更改,它只是node命令的替代品。因为当你修改源文件后,如果你用的是原来的node 则必须手动重启 你的更改才会有效。但是如果用的是nodemon,则你不需要手动操作,它会检测你的代码改动自动重启。
安装
nodemon支持局部的和全局的配置文件。它们通常被命名为nodemon.js.可以位于当前的工作文件夹内,也可以位于你的home文件夹内(项目依赖)。一个配置文件可以被–config <file>
命令来指定。
-
全局安装:
npm install/i -g nodemon //加上 -sava/ -S,下载依赖信息
-
局部安装
npm install/i nodemon
运行
运行命令:
nodemon 文件名.js
安装好之后我们可以使用 nodemon 来运行命令
终止运行
当我们不需要继续运行文件时,我们可以 Ctrl + C 来终止运行
卸载
当我们不需要这个软件时,我们也可以使用命令来卸载它
卸载也有两种卸载方式,一种卸载全局安装的,一种卸载局部安装的
-
卸载全局
npm uninstall -g nodemon //加上--save / 删除包括依赖信息
-
卸载局部
npm uninstall nodemon
启动nodemon时出错
启动nodemon时出错 无法加载文件C:\Users\gxf\AppData\Roaming\npm\nodemon.ps1,因为在此系统上禁止运行脚本
解决方案:
-
以管理员身份打开powerShell
-
输入命令
set-ExecutionPolicy RemoteSigned
-
先择Y或者A,就可以了
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 中的
模块化开发
node.js的核心模块
JavaScript和CommonJS的对比
JavaScript的局限性
- 没有模块系统
- 系统提供的接口较少,比如:缺少操作文件、I/O流等常用的接口
- 没有标准接口,缺少如web服务器、数据库等统一接口
- 缺乏管理系统导师JavaScript应用中基本没
CommonJS规范
- Node.js开发之初遵守了CommonJS规范
- 使JavaScript达到像Java、Python、PHP等语言一样有开发大型应用的基本能力
- CommonJS规范规定每一个模块都有一个单独的作用域
- CommonJS规范规定每个模块对外公布的成员使用module.exports或者exports
- 有了模块化系统之后,Node.js提供了许多系统模块:文件、Buffer、I/O流、Socket等
核心模块
path模块
-
作用:操作文件的路径,为文件操作服务
-
引入模板 :
const path = require('path')
-
常用的几个函数:
path.join(第一个路径,第二个路径) : 拼接路径
-
导入模块
var path = require("path");
-
basename() 获取文件名+后缀
path.basename("/笔记大全/伸缩布局代码/align-content.html") //第二个参数,去掉获取的文件名中的相同部分 path.basename("E:/笔记大全/伸缩布局代码/align-content.html","html")
-
dirname() 获取目录
path.dirname("/笔记大全/伸缩布局代码/align-content.html")
-
extname() 获取文件的扩展名(尾缀名)
path.extname("/笔记大全/伸缩布局代码/align-content.html")
-
join() 合并路径
var p1 = "E:/笔记大全"; var p2 = "/伸缩布局代码"; console.log(path.join(p1,p2));
-
parse() 把路径转换为一个对象
解析对象的路径为组成其的片段:
root
: 根路径。dir
: 从根路径开始的文件夹路径。base
: 文件名 + 扩展名name
: 文件名ext
: 文件扩展名
path.parse("E:/笔记大全/伸缩布局代码/align-content.html","html") { root:'E:/', dir:'E:/笔记大全/伸缩布局代码', base:'align-content.html', ext:'.html', name:'align-content' }
-
format() 把一个路径对象转换成一个路径字符串
var obj = { root:'E:/', dir:'E:/笔记大全/伸缩布局代码', base:'align-content.html', ext:'.html', name:'align-content' } console.log(path.format(obj));
-
delimiter 环境变量的分隔符,可以跨平台 windows下是; 其它平台 :
- path.sep 路径的分隔符 windows下是\ 其它下是/
- isAbsolute() 是否是绝对路径
-
relative 接受 2 个路径作为参数
require('path').relative('/Users/joe', '/Users/joe/test.txt') //'test.txt' require('path').relative('/Users/joe', '/Users/joe/something/test.txt') //'something/test.txt'
URL模块
-
作用:
处理客户端请求过来的URL
-
代码步骤:
- 导入 URL 核心模块
- 导入 HTTP 核心模块
- 监听客户端的请求
在这中间处理客户端请求过来的URL
- 处理客户端的请求
- 开启服务器
-
导入模块var url=requre(“url”)
-
parse()把字符串的路径传换成对象
var uri = "http://www.baidu.com:8080/images/1.jpgversion=1.0&time=1123#abcd"; console.log(url.parse(uri));
-
format() 把路径对象转换成字符串
var obj = { protocol: 'http:', slashes: true, auth: null, host: 'www.baidu.com:8080', port: '8080', hostname: 'www.baidu.com', hash: '#abcd', search: '?version=1.0&time=1123', query: 'version=1.0&time=1123', pathname: '/images/1.jpg', path: '/images/1.jpg?version=1.0&time=1123', href: 'http://www.baidu.com:8080/images/1.jpg?version=1.0&time=1123#abcd' }; var str = url.format(obj); console.log(str);
query string模块
-
作用:处理客户端通过
get/post
请求传递过来的参数 -
使用关键点:
- 需要导入 ‘querystring’ 这个核心模块
get
请求时 querystring 一般是配合 url 核心模块一起使用的get/post
请求最终都需要调用 querystring.parse方法,
将请求传递过来的键值对字符串转成js对象,方便操作
-
注意:
- get/post的请求方式是不一样的,客户端传递过来时,参数放在的地方是不一样的,所以服务器端处理方式也不太一样
-
导入模块 var querystring = require(“querystring”);
-
parse() 把参数字符串解析成对象
var obj = querystring.parse("version=1.0&time=123"); console.log(obj);
-
stringify() 把一个对象转换成一个字符串
-
escape() url进行编码
-
unescape() url进行解码
File System 模块(fs)
前提:以后的所有静态资源(html,css,js,图片等)都是放在服务端的,如果浏览器需要这些html,css,js,图片等资源,则需要先将其读取到node.exe的内容中,然后再返回给浏览器
作用:
- 在服务端来操作文件,可能是需要将浏览器上传的图片保存到服务器,也可能是需要将服务器的资源读取之后返回给浏览器
代码步骤:
-
导入 fs 核心模块
const fs = require("fs");
-
使用相应的方法来写入文件、读取文件等操作
注意点:
- 目录 fs它是不会自动帮我们创建的需要我们自己手工创建
- 文件如果不存在,调用writeFile与appendFile方法会自动帮我们创建
HTTP模块
作用:
- 处理客户端的网络请求
代码步骤:
- 导入 HTTP 核心模块
- 监听客户端的请求
- 处理客户端的请求
- 开启服务器
引入http 模块
// 引入 http 模块
const http = require("http");
Global模块
作用:全局共享的,不需要导入模块即可以使用
常用的属性:
-
__dirname : 文件所在的文件夹路径
-
__filename : 文件所在的路径
-
require() : 导入需要的模块
-
module : 自定义模块时用到
-
exports : 自定义模块时用到
现有的弊端
在我们不算长的js代码编写经历中,可能体会到或者体会不到,js开发有如下弊端
- 文件依赖:文件依赖不清楚,需要人为的去分析
- 命名冲突:两个js文件中,可能存在同名的变量或者文件,会出现后面文件覆盖掉前面文件的问题
Node 中的模块化
模块化规范
在实际的发展中,js模块化大体有如下几种规范
- AMD
- CMD
- CommonJS
- ES6 模块化-官网标准,正规军
node.js中使用的就是 commonjs 模块化规范
那么到底如何做到模块化开发呢?
- Node.js规定一个JavaScript文件就是一个模块,每一个模块都是一个单独的作用域,模块内部定义的变量和函数默认情况下在外部无法得到
- 模块内部可以使用exports对象进行成员导出, 使用require方法导入其他模块。
exports
Node使用CommonJS规范 ,它有四个重要的环境变量:module、exports、require、global。实际使用时,module变量代表当前模块,exports是module的属性,表示对外输出的接口,加载某个模块,实际上是加载该模块的module.exports属性。用require加载模块(同步)。
Node为每隔模块提供了一个exports变量,指向module.exports,这等同于每个模块头部有这样的一行代码:
var exports = module.exports
exports只是module.exports的一个引用,指向module.exports对象所在的地址
注意:
- 与引入 fs、path 等模块不同,引入自定义模块时,要写上路径 ‘./a.js’,而不能使用 ‘a.js’,否则会认为是一个系统模块,进而出现无法发现模块的错误
- 模块可以省略后缀
const a_module=require('./a.js')
也可以定义成员的时候就直接向外暴漏
exports.b=20
也可以定义函数,并向外暴漏
function add(a,b){
return a+b
}
exports.add=add
模块外部如下调用
const a_module=require('./a')
console.log(a_module.add(5,6));
如果模块内成员较多,也可以在定义成员后,一次性向外暴漏所有需要的成员
module.exports
a.js
module.exports.a=10
module.exports.b=20
module.exports.add=(a,b)=>a+b
b.js
const a_module=require('./a')
console.log(a_module.a);
console.log(a_module.b);
console.log(a_module.add(4,5));
a.js
function sum(a, b) {
return a + b
}
function mul(a, b) {
return a * b
}
let salary=1000
// exports.sum=sum //exorts={sum:sum}
// exports.mul=mul // exorts={sum:sum,mul:mul}
// module.exports 是根本,exports只是它的一个别名
// 改造
// module.exports.sum=sum
// module.exports.mul=mul
// 继续改造
module.exports = {
sum, // 使用函数sum的名字作为对象中的属性,使用函数sum作为属性的值 =>sum:sum
mul,
salary // 使用变量salary的名字作为属性名称,使用变量salary 的值作为属性的值 salary:salary
}
b.js
//a={ sum: [Function: sum], mul: [Function: mul],salary:1000 }
// const a=require('./a')
// console.log(a)
// 使用解构赋值
let { sum, mul,salary } = require('./a')
console.log(sum(4, 5))
console.log(mul(4, 5))
console.log(salary)
总结:
- module.exports
- 属性的简洁表示方法
- 解构赋值
Node 中两种模块成员导出方法对比
exports.name=name
module.exports.name=name
1、exports是module.exports的别名(地址引用关系),导出对象最终以module.exports为准
上面案例用到了 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
exports只能使用语法来向外暴露内部变量,module.exports既可以通过语法,也可以直接赋值一个对象
let name='李白'
let age=20
exports={
name:name,
age:age
}
const a_module=require('./a')
console.log(a_module);
输出结果为 {}
如果使用 module.exports
let name='李白'
let age=20
module.exports={
name:name,
age:age
}
const a_module=require('./a')
console.log(a_module);
输出结果如下
{ name: '李白', age: 20 }
属性的简写表示
上面的
module.exports={
name:name,
age:age
}
可以简写成
module.exports={
name,
age
}
也就是说,如果属性名称与值的名称一致,可以省略值
NodeJS模块与ES6模块系统
Node
Node使用CommonJS规范 ,它有四个重要的环境变量:module、exports、require、global。实际使用时,module变量代表当前模块,exports是module的属性,表示对外输出的接口,加载某个模块,实际上是加载该模块的module.exports属性。用require加载模块(同步)。
Node为每隔模块提供了一个exports变量,指向module.exports,这等同于每个模块头部有这样的一行代码:
var exports = module.exports
exports只是module.exports的一个引用,指向module.exports对象所在的地址
ES6
在ES6模块化中,使用 import 引入模块,通过 export导出模块,但需要babel编译为浏览器可以识别的代码。
1.export与export default均可用于导出常量/函数/文件/模块等;
2.在一个文件或模块中,export/import可以有多个,export default只有一个;
3.通过export方式导出,在导入时需要加{},export default不需要;
4.export能导出变量/表达式,export default不可以。
区别
-
CommonJS模块输出是一个值的拷贝,ES6模块输出是值的引用。
-
CommonJS模块是运行时加载,ES6模块是编译时输出接口。
-
CommonJS模块无论require多少次,都只会在第一次加载时运行一次,然后保存到缓存中,下次在require,只会去从缓存取。
-
module.exports与exports ,是CommonJS的规范,被使用于Node.js中。
-
export与export default ,是ES6规范,被使用于React或Vue中。
-
Polyfill : 解决浏览器对API的兼容问题的。
-
Babel : Babel 是一个广泛使用的 ES6 转码器,可以将 ES6 代码转为 ES5 代码。
node 和ES6 对照
引入方式
引入系统模块
// CommonJS 模块
// 引入 http 模块
const http = require("http");
// ES6 模块
// 引入 http 模块
import http from 'http'
引入自定义模块
// CommonJS 模块
//引入 blog.js 模块
const blog_module = require("./src/router/blog");
// ES6 模块
//引入 blog.js 模块
import { handlerBlog } from './src/router/blogs.js';
在此注意,引入自定义模块时,CommonJS 模块可以自定义方法名,而 ES6 模块引入时,需要使用{}语法,变量名 只能是暴漏出来那个的方法名,并且引入时不光要写文件名,还必须把文件后缀名写上,引入模块不能不能和原生的模块一起引入,会发生冲突
暴漏方式
// CommonJS 模块
// 暴漏 handlerBlog 方法
module.exports={
handlerBlog
}
// ES6 模块
// 暴漏 handlerBlog 方法
export{
handlerBlog
}
自定义模块的书写
ES6 每个值都要用return 去返回,CommonJS 可以只返回一次
CommonJS 模块
const handlerBlog=(req)=>{
// 使用 URL 模块对 req.url 进行封装
let myUrl = new URL(req.url, "http://127.0.0.1:3000/");
let method = req.method;
let pathname = myUrl.pathname;
// console.log(pathname);
let msgResult = null;
if (pathname == "/api/blog/list" && method == "GET") {
msgResult = { msg: "博客列表" };
} else if (pathname == "/api/blog/detail" && method == "GET") {
msgResult = { msg: "博客详情" };
} else if (pathname == "/api/blog/new" && method == "POST") {
msgResult = { msg: "新增博客" };
} else if (pathname == "/api/blog/update" && method == "POST") {
msgResult = { msg: "更新博客" };
} else if (pathname == "/api/blog/del" && method == "POST") {
msgResult = { msg: "删除博客" };
}
return msgResult;
}
// 暴漏 handlerBlog 方法
module.exports={
handlerBlog
}
ES6 模块
const handlerBlog = (req)=>{
// 使用 URL 模块对 req.url 进行封装
let myUrl = new URL(req.url,"http://127.0.0.1:3000/");
let method = req.method;
let pathname = myUrl.pathname;
// console.log(pathname);
let msgResult = null;
if(pathname == "/api/blog/list" && method == "GET") {
return msgResult = {msg : "博客列表"}
}else if(pathname == "/api/blog/detail" && method == "GET"){
return msgResult = {msg : "博客详情"}
}else if(pathname == "/api/blog/new" && method == "POST"){
return msgResult = {msg : "新增博客"}
}else if(pathname == "/api/blog/update" && method == "POST"){
return msgResult = {msg : "更新博客"}
}else if(pathname == "/api/blog/del" && method == "POST"){
return msgResult = {msg : "删除博客"}
}else{
return msgResult = {msg : "sorry,地址错误"};
}
}
// 暴漏 handlerBlog 方法
export{
handlerBlog
}
package.json
在使用ES6模块的时候,在项目的package.json文件中,指定type字段为module
一旦设置了以后,该目录里面的 JS 脚本,就被解释用 ES6 模块。
如果没有type字段,或者type字段为commonjs,则.js脚本会被解释成 CommonJS 模块。
http://naotu.baidu.com/file/f03f6830a9a0de7bafa2a987cc065e46?token=35d035cc963f2d82
核心模块存在位置
- 核心模块存储在node.exe中,当node.exe运行的时候,核心模块会被加载,require的时候会加载到内存
- 在github上可以找到源代码,lib文件夹下
- 核心模块的执行速度比较快
系统模块
什么是系统模块
Node运行环境提供的API. 因为这些API都是以模块化的方式进行开发的, 所以我们又称Node运行环境提 供的API为系统模块
在前面,我们学了很多JS的API,如 querySelector、getElementById、offsetLeft 等,这些都是浏览器 提供的跟Dom或者Bom相关的功能,只不过这些API都是没有任何关系的分散的。
Node 中,也提供了很多的API,但是将这些API根据烈性存放在不同的模块中,如文件模块中就包含了 很多能够操作磁盘文件的API
所以,我们可以总结如下:
- 所谓模块化,就是将相关API根据类型划分,存放到不同的文件中
- 系统模块,就是 Node 预先已经定义好的一些API的集合
- 除了系统模块,开发者还可以开发第三方模块,其他开发者都可以使用,如我们前面自己定义的 m.js也是一个第三方模块
文件操作
1.读取文件
同步读取
var fs=require('fs')
// 同步读取
var res = fs.readFileSync('./a.txt','utf8')
console.log(res)
异步读取
使用回调函数
const fs = require('fs')
fs.readFile('a.txt','utf8',(err,data)=>{
console.log(err)
console.log(data)
})
- 系统模块的引入方式也是使用 require
- 模块名称要使用引号包含起来
- readFile是一个异步方法,所以需要回调函数接收读取结果
- 回调函数中,err 获取读取中的错误信息,如果读取过程中没有出现错误,则结果为null;data 为 读取的文件内容
- fs 是 node 中内置的一个系统模块(文件的操作模块)
- 其实模块就是一个js文件,叫做fs.js,当我们在安装node.js时,它会自动安装一个fs.js文件
- 我们的所用到的 readFileSync 就是 fs.js 中提供的一个方法
所以一般在处理读取的文件之前,先判断err 的值
const fs = require('fs')
fs.readFile('m1.js', 'utf8', (err, data) => {
if (err) throw err;
console.log(data)
})
补充知识点:箭头函数
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this
,arguments
,super
或new.target
。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。
箭头函数可以把function函数给省略掉,然后再参数和主体之间加一个 胖箭头=>
就行了
// 箭头函数是对匿名函数的应用
var f1 = (a,b)=>{
console.log(a+b)
}
f1(5,6)
//如果只有一个参数,小括号也可以省略掉
var f2=a=>{
console.log(a);
}
f2(10)
//如果函数体只有一行代码,可以把花括号去掉
var f3=a=>console.log(a);
f3(20)
2.写入文件
语法:
fs.writeFile(file, data[, options], callback)
同步写入:
var fs=require('fs')
//同步的
// 写入文件
fs.writeFileSync('a.txt','今天是美好的一天','utf8')
异步写入
var fs=require('fs')
//异步的
// 写入文件
fs.writeFile('a.txt','每一天都是美好的一天','utf8',err=>{
console.log('文件保存成功');
})
- 用 writeFile 写入文件会直接把原文件里的内容给替代
- 回调函数只有一个参数 err
默认情况下,此 API 会替换文件的内容(如果文件已经存在)。
可以通过指定标志来修改默认的行为:
fs.writeFile('a.txt','今天是美好的一天', { flag: 'a+' }, err => {})
可能会使用的标志有:
r+
打开文件用于读写。w+
打开文件用于读写,将流定位到文件的开头。如果文件不存在则创建文件。a
打开文件用于写入,将流定位到文件的末尾。如果文件不存在则创建文件。a+
打开文件用于读写,将流定位到文件的末尾。如果文件不存在则创建文件。
(可以在 http://nodejs.cn/api/fs.html#fs_file_system_flags 中查看更多标志)
追加文件
将内容追加到文件末尾的便捷方法是 fs.appendFile()
(及其对应的 fs.appendFileSync()
):
var fs=require('fs')
fs.appendFileSync('a.txt','今天是美好的一天','utf8')
引用流
所有这些方法都是在将全部内容写入文件之后才会将控制权返回给程序(在异步的版本中,这意味着执行回调)。
在这种情况下,更好的选择是使用流写入文件的内容。
相对于使用其他的数据处理方法,流基本上提供了两个主要优点:
- 内存效率: 无需加载大量的数据到内存中即可进行处理。
- 时间效率: 当获得数据之后即可立即开始处理数据,这样所需的时间更少,而不必等到整个数据有效负载可用才开始。
3.操作文件夹
检查文件夹是否存在
使用 fs.access()
检查文件夹是否存在以及 Node.js 是否具有访问权限。
创建新的文件夹
使用 fs.mkdir()
或 fs.mkdirSync()
可以创建新的文件夹。
读取目录的内容
使用 fs.readdir()
或 fs.readdirSync()
可以读取目录的内容。
重命名文件夹
使用 fs.rename()
或 fs.renameSync()
可以重命名文件夹。 第一个参数是当前的路径,第二个参数是新的路径
删除文件夹
使用 fs.rmdir()
或 fs.rmdirSync()
可以删除文件夹。
删除包含内容的文件夹可能会更复杂。
在这种情况下,最好安装 fs-extra
模块,该模块非常受欢迎且维护良好。 它是 fs
模块的直接替代品,在其之上提供了更多的功能。
使用以下命令安装
npm install fs-extra
4.操作文件
先引入 fs 模板
const fs = require('fs')
这样就可以访问其所有的方法
fs.access()
: 检查文件是否存在,以及 Node.js 是否有权限访问。fs.appendFile()
: 追加数据到文件。如果文件不存在,则创建文件。fs.chmod()
: 更改文件(通过传入的文件名指定)的权限。相关方法:fs.lchmod()
、fs.fchmod()
。fs.chown()
: 更改文件(通过传入的文件名指定)的所有者和群组。相关方法:fs.fchown()
、fs.lchown()
。fs.close()
: 关闭文件描述符。fs.copyFile()
: 拷贝文件。fs.createReadStream()
: 创建可读的文件流。fs.createWriteStream()
: 创建可写的文件流。fs.link()
: 新建指向文件的硬链接。fs.mkdir()
: 新建文件夹。fs.mkdtemp()
: 创建临时目录。fs.open()
: 设置文件模式。fs.readdir()
: 读取目录的内容。fs.readFile()
: 读取文件的内容。相关方法:fs.read()
。fs.readlink()
: 读取符号链接的值。fs.realpath()
: 将相对的文件路径指针(.
、..
)解析为完整的路径。fs.rename()
: 重命名文件或文件夹。fs.rmdir()
: 删除文件夹。fs.stat()
: 返回文件(通过传入的文件名指定)的状态。相关方法:fs.fstat()
、fs.lstat()
。fs.symlink()
: 新建文件的符号链接。fs.truncate()
: 将传递的文件名标识的文件截断为指定的长度。相关方法:fs.ftruncate()
。fs.unlink()
: 删除文件或符号链接。fs.unwatchFile()
: 停止监视文件上的更改。fs.utimes()
: 更改文件(通过传入的文件名指定)的时间戳。相关方法:fs.futimes()
。fs.watchFile()
: 开始监视文件上的更改。相关方法:fs.watch()
。fs.writeFile()
: 将数据写入文件。相关方法:fs.write()
。
关于 fs
模块的特殊之处是,所有的方法默认情况下都是异步的,但是通过在前面加上 Sync
也可以同步地工作。
例如:
fs.rename()
fs.renameSync()
fs.write()
fs.writeSync()
试验一下 fs.rename()
方法。 异步的 API 会与回调一起使用:
const fs = require('fs')
fs.rename('before.json', 'after.json', err => {
if (err) {
return console.error(err)
}
//完成
})
同步的 API 则可以这样使用,并使用 try/catch 块来处理错误:
const fs = require('fs')
try {
fs.renameSync('before.json', 'after.json')
//完成
} catch (err) {
console.error(err)
}
此处的主要区别在于,在第二个示例中,脚本的执行会阻塞,直到文件操作成功。
第三方模块
1. 如何获取第三方模块
https://www.npmjs.com/
node 官方不可能将项目开发中用到的所有功能都封装成模块,在我们安装 node 的时候提供给我们,事实上,用的最多的是其他开发者开发的一些模块,这些模块叫做第三方模块
别人写好的、具有特定功能的、我们能直接使用的模块即第三方模块,由于第三方模块通常都是由多个文件组成并且被放置在一个文件夹中,所以又名包。
第三方模块有两种存在形式:
- 以js文件的形式存在,提供实现项目具体功能的API接口,此种模块多为本地安装
- 以命令行工具形式存在,辅助项目开发,此种模块多为全局安装
开发者将开发好的第三方模块,上传到上面网站,其它开发者可以从上面下载
下载方式为命令行方式下载
下面是下载和卸载第三方模块的基本语法
-
下载:
npm install 模块名称
-
卸载:
npm unintall package 模块名称
根据模块的作用,分为全局安装和本地安装
- 本地安装:模块被下载到命令行工具所在目录下,只能当前项目使用
- 全局安装:模块被下载全局目录下,所有项目都可以使用
2. 安装演示
1. 本地安装:
例如安装 jquery
npm install jquery
安装完成后,会在当前目录下创建 node_modules 目录
卸载:
npm uninstall jquery
2. 全局安装:
以 nodemon 模块为例
nodemon是一个命令行工具,用以辅助项目开发。
在Node.js中,每次修改文件都要在命令行工具中重新执行该文件,非常繁琐。
npm install nodemon -g
卸载:
npm uninstall nodemon -g
3. 安装慢的问题
因为 npmjs.com 的服务器在国外,所有有时候下载速度很慢,或者总是断开
解决方案就是更换下载源,最好是国内的下载源
解决方案有两个:
- 安装 nrm,通过nrm更换下载源,更换之后,仍然使用npm 命令安装模块
- 安装 cnpm,以后通过cnpm 安装,安装后,使用 cnpm 安装模块
1. nrm
通过安装 nrm 的方式,可以选择和切换下载源
使用步骤:
-
使用npm install nrm –g 下载它
-
查询可用下载地址列表 nrm ls
-
切换npm下载地址 nrm use 下载地址名称
-
下载模块 npm install 模块名称
对于我们国内,建议使用淘宝来下载
在镜像源中,cnpm 和 taobao 其实是一样的,所以选择 taobao 还是 cnpm 都可以
2. cnpm
除了将下载地址切换为 cnpm 外,还可以直接安装 cnpm
使用 npm 安装 cnpm,同时将下载源更换为taobao
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装成功后,使用 cnpm 命令安装第三方模块,如
cnpm install mysql
说明:cnpm 只是基于npm 的一个小公举
总结:
- 无论使用 nrm 还是 cnpm 都需要使用 npm 下载包
- 使用 nrm 切换下载源之后,仍然使用 nmp 命令安装包
- 安装 cnpm 后,需要使用 cnpm 命令安装包,才会从淘宝镜像下载,如果仍然使用 npm 命令,仍然会从默认的下载源安装
或者这两种方式都不用,而是使用安装包的时候指定下载源
npm install mysql --registry=https://registry.npm.taobao.org
3. yarn
上面学习的 npm 是基于node.js 的一个包管理工具,除此之外,还有yarn,现在也非常流行
yarn 是现在非常流行的一个基于node.js的包管理工具,与 npm 可以说是双雄争霸
注意:yarn 与 cnpm 是不同的
1 yarn 安装
除了下载安装包外,还可以从npm 中安装yarn
下面使用 npm 包安装 yarn(虽然yarn 是与npm 竞争关系,但是npm 还是和开放的允许安装 yarn)
npm install yarn -g
执行下面命令,查询yarn命令列表
yarh -h
查询 yarn 版本
yarn -v
2 yarn 安装和模块
新建一个项目,然后打开命令行工具,执行如下命令
yarn add jquery
删除模块
yarn remove jquery
- 一个项目中,不要同事使用npm 和 yarn 进行包管理
- npm 和 yarn 的能力,我们仅仅使用了万分之一,更多功能,后面再讲
package.json 文件
每个项目的根目录下面,一般都有一个package.json文件,定义了这个项目所需要的各种模块,以及项目的配置信息。
1. node_modules 的问题
我们发现,当使用 npm 安装模块时,会创建 node_modules 目录,此目录中存储下载的模块及其依赖的模块
这个目录中的文件存在两个问题
- 文件夹以及文件过多过碎,当将项目整体拷贝给别人时,传输速度很慢(事实上,node项目的体积主要就是node_modules)
- 复杂的模块依赖关系需要被记录,确保模块的版本和当前保持一致,否则会导致当前项目运行报错
2. package.json 解决问题
在个项目拷贝给他人时,删除 node_modules,他人再使用 nmp 命令进行恢复安装
这就需要一个文件能够记录,当前项目都使用了哪些模块,这个文件就是 package.json
创建package.json
默认情况下,是没有此文件的,可以使用命令生成此文件
新建项目,然后在此项目下打开命令行工具,运行如下命令
npm init -y
package.json 文件中的内容如下
{
"name": "blog",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
安装mysql文件
npm install mysql
我们会发现 package.json 文件会多出东西
删除node_modules
将项目拷贝给别人时,直接删除 node_modules 目录即可
恢复安装
运行如下命令,即可根据package.json 中的记录恢复安装
npm install
总结:
- 创建项目后首先执行 npm init -y 命令,创建 package.json 文件
- 将项目发送给别人之前,先删除 node-modules 目录
- 别人接受并打开项目后,运行 npm install 命令,根据 package.json 中的 dependencies 属性安装相关的包
3. 项目依赖和开发依赖
- 项目依赖
- 在项目的开发阶段和线上运营阶段,都需要依赖的第三方包,称为项目依赖
- 使用npm install 包名命令下载的文件会默认被添加到 package.json 文件的 dependencies 字段中
- 开发依赖
- 在项目的开发阶段需要依赖,线上运营阶段不需要依赖的第三方包,称为开发依赖
- 使用npm install 包名 --save-dev命令将包添加到package.json文件的devDependencies字段 中
使用如下命令安装开发依赖包:mysql
npm install mysql
删除 node_modules 目录
使用如下命令恢复项目依赖安装
npm install --production
查看 node_modules 中的文件变化
再次删除 node_modules,然后使用如下命令恢复开发依赖安装
npm install
发现node_modules 中多了很多文件
package.lock.json
此文件在第一次使用npm下载模块时就会被创建
作用如下:
- 锁定包的版本,确保再次下载时不会因为包版本不同而产生问题
- 加快下载速度,因为该文件中已经记录了项目所依赖第三方包的树状结构和包的下载地址,重新安装时只需下载即可,不需要做额外的工作
关于 package-lock.json 的一个关键细节,那就是它不能被发布。如果你在项目根目录以为的地方找到这个文件,它都会被忽略。
当某人尝试通过 npm install 初始化项目时,下载的依赖包可能和项目初期是不同的,尤其是 package.json 中的版本没有固定,可以更新时,虽然补丁版本或次版本不应该引起重大改变,但还是有概率引入 bug。
package-lock.json 会固化当前安装的每个依赖包的版本,每当 npm install 时,npm 会使用这些确切的版本。
- version : 软件包的版本号。
- resolved : 指向软件包位置。
- integrity : 校验软件包。
- requries : 需要的其他软件包。
- dependencies : 该包的依赖包。
模块加载规则
require加载规则
- 优先从缓存加载。 //不会重复加载,提高效率
- 路径形式的文件模块,必须加 ./ (当前目录)或…/(上一级目录) 。 //否则会当成核心模块处理,造成错误
- 第三方模块。既不是核心模块、 也不是路径形式的模块
凡是第三方模块在当前文件目录下通过 npm下载,通过require(‘包名’)加载
自定义模块加载
模块加载分为两种情况,一种是加后缀名,一种是不加后缀名
const blog_module = require("./src/router/blog");
const blog_module = require("./src/router/blog.js");
- require方法根据模块路径查找模块,如果是完整路径,直接引入模块
- 如果模块后缀省略,先找同名JS文件再找同名JS文件夹
- 如果找到了同名文件夹,找文件夹中的index.js
- 如果文件夹中没有index.js就会去当前文件夹中的package.json文件中查找main选项中的入口文件
- 如果找指定的入口文件不存在或者没有指定入口文件就会报错,模块没有被找到
系统模块加载
当模块引入没有添加路径,自动默认成是系统模块
const http = require("http");
- Node.js会假设它是系统模块
- Node.js会去node_modules文件夹中
- 首先看是否有该名字的JS文件
- 再看是否有该名字的文件夹
- 如果是文件夹看里面是否有index.js
- 如果没有index.js查看该文件夹中的package.json中的main选项确定模块入口文件
- 否则找不到报错
node.js对于MYSQL数据库的操作
1.配置环境
在对数据库进行操作时,先使用终端安装驱动
安装驱动的命令:
npm install mysql
引入模块
var mysql = require('mysql');
2.连接数据库
使用以下代码根据你的实际配置修改数据库,用户名,密码及数据库名
test.js 文件代码:
var mysql = require('mysql');
// 连接数据库的配置
var connection = mysql.createConnection({
// 主机名称,一般是本机
host : 'localhost',
// 数据库的端口号,如果不设置,默认是3306
port: 3306,
// 创建数据库时设置用户名
user: 'root',
// 创建数据库时设置的密码
password: 'root',
// 查询的数据库
database : 'test'
});
// 与数据库建立连接
connection.connect();
// 查询数据库
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results[0].solution);
});
执行以下命令:
node test.js
输出结果为:
The solution is: 2
再次要注意两点:
- 文件名必须是英文,使用中文名容易报错
- 终端打开的文件和编写文件是否是同一个文件夹
数据库连接参数说明:
参数 | 描述 |
---|---|
host | 主机地址 (默认:localhost) |
user | 用户名 |
password | 密码 |
port | 端口号 (默认:3306) |
database | 数据库名 |
charset | 连接字符集(默认:‘UTF8_GENERAL_CI’,注意字符集的字母都要大写) |
localAddress | 此IP用于TCP连接(可选) |
socketPath | 连接到unix域路径,当使用 host 和 port 时会被忽略 |
timezone | 时区(默认:‘local’) |
connectTimeout | 连接超时(默认:不限制;单位:毫秒) |
stringifyObjects | 是否序列化对象 |
typeCast | 是否将列值转化为本地JavaScript类型值 (默认:true) |
queryFormat | 自定义query语句格式化方法 |
supportBigNumbers | 数据库支持bigint或decimal类型列时,需要设此option为true (默认:false) |
bigNumberStrings | supportBigNumbers和bigNumberStrings启用 强制bigint或decimal列以JavaScript字符串类型返回(默认:false) |
dateStrings | 强制timestamp,datetime,data类型以字符串类型返回,而不是JavaScript Date类型(默认:false) |
debug | 开启调试(默认:false) |
multipleStatements | 是否许一个query中有多个MySQL语句 (默认:false) |
flags | 用于修改连接标志 |
ssl | 使用ssl参数(与crypto.createCredenitals参数格式一至)或一个包含ssl配置文件名称的字符串,目前只捆绑Amazon RDS的配置文件 |
更多说明可参见:https://github.com/mysqljs/mysql
3.数据库操作
在进行数据库操作前,你需要在Navicat 中创建好你所需要进行操作的数据库文件,然后进行操作
1. 建立连接
配置好环境并且连接好你所需要操作的数据库
var mysql = require('mysql');
// 连接数据库的配置
var connection = mysql.createConnection({
// 主机名称,一般是本机
host : 'localhost',
// 数据库的端口号,如果不设置,默认是3306
port: 3306,
// 创建数据库时设置用户名
user: 'root',
// 创建数据库时设置的密码
password: 'root',
// 查询的数据库
database : 'test'
});
// 与数据库建立连接
connection.connect();
基于以上操作完成后,我们进行对数据库的操作
2. 查询数据
//查询数据
var sql = 'SELECT * FROM nemu';
//查
/**
* query 方法是一个异步方法
* sql:要执行的sql语句
* 回调函数的两个参数
* err:如果执行 sql 语句过程中发生错误,会将错误信息赋值给 err
* result:如果执行 sql 语句成功,会将结果赋值给result
*/
connection.query(sql,function (err, result) {
if(err){
console.log('[SELECT ERROR] - ',err.message);
return;
}
console.log('--------------------------SELECT----------------------------');
console.log(result);
console.log('------------------------------------------------------------\n\n');
});
3.添加数据
//增加数据
var addSql = 'INSERT INTO nemu(id,name,age) VALUES(NULL,?,?)';
var addSqlParams = ['独一', 10];
//增
/**
* query 方法是一个异步方法
* sql:要执行的sql语句
* 回调函数的两个参数
* err:如果执行 sql 语句过程中发生错误,会将错误信息赋值给 err
* result:如果执行 sql 语句成功,会将结果赋值给result
*/
connection.query(addSql,addSqlParams,function (err, result) {
if(err){
console.log('[INSERT ERROR] - ',err.message);
return;
}
//console.log('INSERT ID:',result.insertId);
console.log('INSERT ID:',result);
});
4. 修改数据
//修改数据
var modSql = 'UPDATE nemu SET name = ?,age = ? WHERE id = ?';
var modSqlParams = ['无二', '10',6];
//改
connection.query(modSql,modSqlParams,function (err, result) {
if(err){
console.log('[UPDATE ERROR] - ',err.message);
return;
}
console.log('--------------------------UPDATE----------------------------');
console.log('UPDATE affectedRows',result.affectedRows);
console.log('-----------------------------------------------------------------\n\n');
});
5. 删除数据
//删除数据
var delSql = 'DELETE FROM nemu where id=6';
//删
connection.query(delSql,function (err, result) {
if(err){
console.log('[DELETE ERROR] - ',err.message);
return;
}
console.log('--------------------------DELETE----------------------------');
console.log('DELETE affectedRows',result.affectedRows);
console.log('-----------------------------------------------------------------\n\n');
});
6.关闭数据库
// 关闭连接
connection.end();