Nodejs 学习日记
简介
很多人想了解,是不是学会Nodejs就可以成为全栈工程师了。我的回答是,是也不是。不过学习node能干什么的?首先要知道JS不能干什么。简单的说,JS离开浏览器就什么都干不成了,但是node可以。
为什么要学习nodejs
- 学习nodejs是平滑过渡后端的方式
- 前端工程化的基础
- 可以写js脚本在非浏览器中运行。
- 即时通讯应用开发
Nodejs可以干什么
- 访问系统盘,支持对系统文件的读写
- 进程管理,可以创建多个线程,不是单线程了
- 网络请求没有跨域限制
环境搭建
网站推荐node官网,有最新版本 nodejs.org/en/ 。
查看是否安装成功 在powershell中 输入 node -v
HelloWorld
在vscode 中建立一个 js文件 helloworld.js
console.log("Hello World")
----- 终端 -----
node .\helloworld.js
helloworld
commonJs 模块化开发
node不能使用webpack,所以需要CommonJs进行模块化开发。
默认,一个js就是一个模块,模块里的方法,外部是无法访问的,只能通过暴露和引入的方式访问模块中的属性
//目的:让B.js文件能够访问A.js的内容
--------------------A.js文件--------------------
function test(){
console.log('测试')
}
module.exports = test
//通过module.exports 将test暴露出去
---------------------B.js文件--------------------
var moduleA = require('./A')
moduleA()
-----------------终端---------------------
node .\B.js
测试
//当一个模块中有多个方法的时候如何导入多个方法
-----------------A.js文件----------------------
function test1(){
console.log('测试1')
}
function test2(){
console.log('测试2')
}
module.exports = {
test1,
test2
}
------------------B.js文件---------------------
var ModuleA = require('./A')
ModuleA.test1()
ModuleA.test2()
------------------终端---------------------
node .\B.js
测试1
测试2
Es模块化写法
node 13之后,支持 Es模块化开发
-
开启Es module
-
生成package.json
npm init -
在package.json中添加一个 type 字段
"type":"module" -
可以使用了
----------A.js---------- const Obj1 ={} export default Obj1 ----------B.js------------ // import Obj1 from './A.js' console.log(Obj1) ---------终端------------ node .\B.js {}
使用Es的写法就不可以用CommonJs,建议使用CommonJs,因为引用别人的包可能使用的是CommonJs。
-
自动重启服务器小工具
-
安装&使用
--------------终端--------------- //安装,注意需要-g全局安装 npm i -g nodemon //使用,直接代替原来的node nodemon .\index.js
内置模块
内置模块之http模块
-
创建服务器环境
var http = require("http") http.createServer((req,res)=>{ res.write("hello world") res.end() }).listen(3000,()=>{ console.log("server start") }) //req和res, //结束一定要写end,否则如果超时了,就会返回超时。 //listen 3000 监听3000接口 -
返回json字符串
var http = require("http") http.createServer((req,res)=>{ res.end("[1,2,3]") }).listen(3000,()=>{ console.log("server start") }) //在end中返回json数组 -
返回html标签
var http = require("http") http.createServer((req,res)=>{ res.writeHead(200,{"Content-Type":"text/html;charset=utf-8"}) res.write(` <html> <b>hello world</b> <p>你好 世界</p> </html> `) res.end() }).listen(3000,()=>{ console.log("server start") }) -
简易路由
通过从req中获取url,再使用switch进行判断
var http = require("http") http.createServer((req,res)=>{ res.writeHead(renderStatus(req.url),{"Content-Type":"text/html;charset=utf-8"}) res.write(renderHTML(req.url)) res.end() }).listen(3000,()=>{ console.log("server start") }) function renderStatus(url){ var arr = ["/home","/list","/api/home","/api/list"] return arr.includes(url)?200:400 } function renderHTML(url){ switch(url){ case "/home": render ` <html> <div>home页面</div> </html> ` case "/list": render ` <html> <div>list页面</div> </html> ` case "/api/home": render ` ["list1","list2","list3"] ` case "/list": render ` {name:"ysain"} ` } } -
更加简单易懂的写法
var http = require("http") //1. 创建服务器实例 var server = http.createServer() //服务器监听request,request有变化就调用回调函数 server.on("request",(req,res)=>{ res.writeHead(renderStatus(req.url),{"Content-Type":"text/html;charset=utf-8"}) res.write(renderHTML(req.url)) res.end() }) //服务器监听3000端口号 server.listen(3000,()=>{ console.log("server start") })
内置模块之url模块
-
parse解析url
var url = require("url") -------------重点---------------- //parse: 解析url,生成一个与之相关的对象,里面包含了所有的信息,可以直接用 ------------------------------- //pathname:url对象中的一个字段,可以获取原本的路径,不需要通过截取字符串的方式提取出来 var pathname = url.parse(req.url).pathname //因为用到了req,显而易见是写在回调函数里的。为什么要写这个,后面所有需要url的可以用pathname来代替,这样就不会把传值的路径也添加进去了:如 //原来 res.write(renderHTML(req.url)) //现在 res.write(renderHTML(pathname)) -
将url中的参数截取
---------------浏览器-------------- 地址栏: localhost:1234/home?name:yasin&age=18 ---------------编辑器-------------- var urlObj = url.parse(req.url,true) //第一个参数req.url可以获取到大多数的信息,第二个参数true可以写也可以不写,写的话,传递过来的值就是以json字符串的形式 ---------------终端---------------- query: [Object: null prototype] { name:'yasin',age:'18' } //传来的值是json字符串 -
format 把解析后的对象格式化成一个地址
const urlObj ={ protocal: 'https', slashes: true, ... postname:'www.baidu.com', query: {id: '8',name: 'mouse'}, pathname: '/ad/index.html', ... } const parsedObj = url.format(urlObj) console.log(parsedObj) -------------终端---------------- https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110 -
resolve 对url进行拼接
-------------------1------------------ var a = url.resolve('/one/two/three','four') console.log(a) var b = url.resolve('/one/two/three','/four') console.log(b) ----------------终端--------------------- //a:不加‘/’的会替换掉最后一个 /one/two/four //b:加‘/’的会拼接到后面 /one/two/three/four ----------------------2---------------- var a = url.resolve('http://example.com/aa/bb','/one') ------------------终端--------------------- //无论地址后面有什么,直接全部替换 http://example.com/one
内置模块之QueerString
作用将路径结构转成编码结构
//-1 url参数转obj
let querystring = require('querystring')
let str = 'name=zhangsan&age=18&loaction=admin'
let obj = querystring.parse(str)
console.log(obj);
----------------终端---------------------
[Object: null prototype] {
name: 'zhangsan',
age: '18',
loaction: 'admin'
}
//-2 obj转url参数
//与JSON 不要搞混,但是很相像,可以放在一块记
let obj1 = {
a: 1,
b: 2,
c: 3,
}
let str1 = querystring.stringify(obj1)
console.log(str1);
----------------终端---------------------
a=1&b=2&c=3
内置模块之event模块
实现发布订阅模式
进行事件监听、实现函数解耦
const EventEmitter = require("events")
const event = new EventEmitter()
// 监听
event.on("play", (data) => {
console.log("事件被触发了")
console.log(data)
})
event.on("run", (data) => {
console.log("事件被触发了")
console.log(data)
})
// 触发事件
event.emit("play", "1111我是play参数")
event.emit("run", "1111我是run参数")
内置模块之fs模块
一、引入
const fs = require('fs'); //fs模块引入
二、对目录的操作
| api | 参数 | 功能 |
|---|---|---|
fs.mkdir | (目录,失败的回调) | 创建目录 |
fs.rename | (目录,失败的回调) | 重命名目录 |
fs.rmdir | (目录,失败的回调) | 删除目录,目录下不能有文件 |
fs.readdir | (目录,失败与数据的回调) | 读取目录下文件属性,返回值是数组 |
fs.stat | (目录,回调) | 获取目录下的信息 |
fs.mkdir
// mkdir 创建目录 第二个参数是回调函数,展示错误状态
// 如果不加完整路径,默认在与当前文件同级目录下创建一个文件夹 , 路径也可以写相对路径 如 ../A 或绝对路径 C:\Microsoft\Windows
fs.mkdir('文件夹', (e) => {
if (e && e.code === 'EEXIST') {
console.log('目录已经存在');
}
})
fs.rename
//rename 重命名 错误代码ENOENT目录不存在
fs.rename('./文件夹', './头像1', (e) => {
if (e && e.code === 'ENOENT') {
console.log('目录不存在');
}
})
fs.rmdir
//删除文件夹 删除文件夹前,文件夹必须为空
fs.rmdir('./文件夹', (e) => {
if (e && e.code === 'ENOENT') {
console.log('目录不存在');
}
})
fs.readdir
//读取目录下文件属性,返回值是数组
fs.readdir('./文件夹', (e, data) => {
console.log(data);
})
fs.stat
//获取目录下的信息 里面重点API 判断是否是文件isFile 判断是否是目录isDirectory
fs.stat('./头像', (err, data) => {
console.log(data.isDirectory());
console.log(data.isFile());
})
三、对文件的操作
| api | 参数 | 功能 |
|---|---|---|
fs.writeFile | (路径,内容,回调) | 创建文件,如果没有新建文件,如果有则覆盖 |
fs.appendFile | (路径,内容,回调) | 在已有的文件中追加内容,不进行覆盖 |
fs.readFile | (路径,编码,回调) | 读文件 |
fs.unlink | (路径,回调) | 删除文件 |
fs.existsSync | (路径) | 判断文件是否存在 |
fs.writeFile
//创建文件,如果没有新建文件,如果有则覆盖,第一个参数是路径,第二个参数是内容, 第三个参数是错误
fs.writeFile('./头像/d.md', '# HelloWorld', e => {
console.log(e);
})
fs.appendFile
// 在已有的文件中追加内容,不进行覆盖 通过 转义字符 \n 进行换行
fs.appendFile('./头像/a.md', '\n## 你好', e => {
console.log(e);
})
fs.readFile
// 读文件 两个参数,第一参数是路径 , 第二个是可选择的参数(可以不选)——转码格式,默认是buffer对象,中文可选‘utf-8 , 第三个参数是回调函数,回调函数两个参数,第一个参数是err,第二个是数据
fs.readFile('./头像/a.md', 'utf-8', (e, data) => {
console.log(e);
console.log(data);
})
fs.unlink
//删除文件
fs.unlink('./头像', (e) => {
console.log(e);
})
fs.existsSync
const checkDir = fs.existsSync("./文件夹/a.md");
console.log(checkDir);
四、解决异步问题
将下列代码解决异步问题
const fs = require('fs')
fs.readdir('/文件夹',(err,data)=>{
data.forEach(item=>{
fs.unlink(`/文件夹/${item}`)
})
fs.rmdir('/文件夹')
})
//此时,unlink没有执行完,直接执行了rmdir 会报文件夹不为空的错误
-
Sync
使用内置的同步方法,如unlinkSync
const fs = require('fs') fs.readdir('/文件夹',(err,data)=>{ data.forEach(item=>{ fs.unlinkSync(`/文件夹/${item}`) }) fs.rmdir('/文件夹') })使用
unlinkSync这类以Sync结尾的代码将异步改为同步,同时因为js为单线程,则会影响后面语句的执行。 -
使用
-
Promises 整理异步 可以使用.then的方法
const fs = require('fs').promises /* fs.readdir(('/文件夹').then((data)=>{ data.forEach(item=>{ fs.unlink(`/文件夹/${item}`) }) fs.rmdir('/文件夹') }) */ //但是需要注意,如何等for循环的异步全部执行完再执行下一个 这里建议使用promise.all的方法 //再使用map映射的方式优化代码 fs.readdir(('/文件夹').then( async (data)=>{ await promise.all(data.map(item=>fs.unlink(`/文件夹/${item}`))) await fs.rmdir('/文件夹') })
文件加密
md5加密
在Node.js中使用crypto模块的md5加密数据的步骤如下:
首先需要引入crypto模块,可以使用以下代码进行引入:
const crypto = require('crypto');
然后需要定义需要加密的数据,例如:
const data = '123456';
接着,可以使用crypto模块的createHash方法创建一个md5加密的实例:
const md5 = crypto.createHash('md5');
可以使用update方法向md5实例中添加需要加密的数据:
md5.update(data);
最后,使用digest方法获取加密后的结果:
const result = md5.digest('hex');
其中,hex表示获取加密结果的编码方式,可以使用binary、base64等不同的编码方式。
完整的代码如下:
const crypto = require('crypto');
const data = '123456';
const md5 = crypto.createHash('md5');
md5.update(data);
const result = md5.digest('hex');
console.log(result);