Node.js

简介

把JavaScript代码放在浏览器运行是前端开发,
JavaScript代码放在node.js运行是后端开发

Node.js中无法调用 DOM、BOM、Ajax等浏览器的内置API
Node.js的js运行环境由 V8引擎 + node的内置API(fs path http)


一、终端打开 node

终端有 :cmd 和 powershell(推荐) 两种方式打开终端

cmd打开终端:window+R 输入cmd打开 / 在当前文档目录下直接打开
powershell打开终端:在当前目录按住 Shift+鼠标右键 用powershell窗口打开

终端的常用快捷键:
在这里插入图片描述

二、fs 文件系统模块

1. fs读取文件 readFile()

// 1. 导入 fs 模块,来操作文件
    const fs1 = require('fs')
// 2. 调用 readFile() 方法读取文件
  //参数1:读取文件的存放路径
  //参数2:读取文件时采用的编码格式,默认utf-8(可省)
  //参数3:回调函数,可拿到读取失败和成功的结果 err dataStr

    fs1.readFile('./fs.txt','utf-8',function(err,dataStr){
      // 打印失败的结果
        // 如果读取成功,err 的值为 null并打印成功的结果
        // 如果读取失败,err 的值为 错误对象,dataStr的值为undefined
        console.log(err);
        console.log('----------');
        
      // 打印成功的结果
        console.log(dataStr);
    })

判断读取文件是否成功:

	const fs = require('fs')
	fs.readFile('./fs.txt','utf8',function(err,dataStr){
	    if (err){ // err != null表示失败
	        return console.log("读取文件失败",err.message);
	    }
	    return console.log("读取文件成功",dataStr);
	})

2. fs写入文件 writeFile()

写入会覆盖文件内原本的内容。

// 1. 导入 fs 模块
    const fs = require('fs')

// 2. 调用 fs.writeFile() 方法 写入文件内容
  //参数1:文件存放的路径
  //参数2:表示要写入的内容
  //参数3:(可省)编码格式 默认:utf8
  //参数4:回调函数
    fs.writeFile('./fs2.txt','Hello fs文件编译',function(err){
        // 如果文件写入成功,则 err 值为null
        // 如果写入失败,err 的值为 错误对象
        console.log(err);
    })

判断写入文件是否成功:

const fs = require('fs')
fs.writeFile('./fs2.txt','Hello fs文件编译',function(err){
    if (err){
        return console.log("写入文件失败",err.message);
    }
    return console.log("写入文件成功");
})

3. fs 的路径 __dirname / __filename

__filename 当前文件路径
__dirname 当前文件夹目录路径

const fs1 = require('node:fs')
const path = require('node:path')
// 1. 出现路径拼接错误问题,是因为提供了 ./ 或 ../ 开头的相对路径(动态)
fs1.readFile('./fs.txt','utf8',function(err,dataStr){
    if (err){
        return console.log("路径错误",err.message);
    }
    return console.log("路径正确",dataStr);
})

// 2. 如果解决这个问题,直接提供绝对路径
// 虽然可以解决路径动态问题,但移植性差 不利于维护
fs1.readFile('C:\\Users\\Mario\\Desktop\\VSCode-HTML\\web\\files\\fs.txt','utf8',function(err,dataStr){
    if (err){
        return console.log("路径错误",err.message);
    }
    return console.log("路径正确",dataStr);
})

// 3. __dirname 表示当前文件所处的目录的绝对路径
console.log(__dirname);
fs1.readFile(path.join(__dirname,'./fs.txt'),'utf8',function(err,dataStr){
    if (err){
        return console.log("读取路径错误",err.message);
    }
    return console.log("读取路径正确",dataStr);
})

4. fs添加文件 appendFile()

如果可以找到要添加文件的目录,会直接在文件内容后面添加内容,不会覆盖。
如果没有找到要添加文件的目录,会创建一个新的文件目录并添加内容。

const fs = require('fs')
const path = require('path')

  //参数1:文件添加的路径
  //参数2:表示要添加的内容
  //参数3:(可省)编码格式 默认:utf8
  //参数4:回调函数
fs.appendFile(path.resolve(__dirname,'./test.txt'),'123',(err,dataStr)=>{
    return console.log(dataStr);
})

5. fs创建路径 fs.mkdir()

fs.rmdir()删除路径

// fs.mkdir() 创建路径
fs.mkdir(path.resolve(__dirname,'./hello'),(err)=>{
    if (err) throw err
})

/* mkdir可以接收一个 配置对象作为第二个参数
    recursive: true;(默认为false)--可以自动创建不存在的上一级目录
*/
fs.mkdir(path.resolve(__dirname,'./hello/abc'),{recursive: true},(err)=>{
    if (err) throw err
})

三、path 路径处理模块

1. path.join() 路径拼接

凡是涉及到路径拼接问题,都要用 path.join() 方法进行处理。(不要直接使用 + 拼接)
path.resolve()方法类似,也可以使用。

// 导入 path 模块
const path = require('path')

// 注意:../会抵消前面的/c
const res = path.join('/a','/b/c','../','./d','./e')
console.log(res); // \a\b\d\e

fs1.readFile(path.join(__dirname,'./fs.txt'),'utf8',function(err,dataStr){
    if (err){
        return console.log("读取路径错误",err.message);
    }
    return console.log("读取路径正确",dataStr);
})

2. path.basename() 获取文件名

可以从一个文件路径中获取到文件的名称部分

// 导入 path 模块
const path = require('path')

// 有一个文件的存放路径:
    const fpath = '/a/b/c/index.html'
// 1. path.basename(fpath) --会拿到:文件名.扩展名
    const fullpath = path.basename(fpath)
    console.log(fullpath); // index.html
    
// 2. path.basename(fpath,'.html') --只会拿到文件名,会移出扩展名
    const fullpath = path.basename(fpath,'.html')
    console.log(fullpath); // index

3. path.extname() 获取扩展名

// 导入 path 模块
const path = require('path')

// 可以从一个文件路径中获取到文件的名称部分
    const fpath = '/a/b/c/index.html'
// path.extname(fpath) --会拿到: .扩展名
    const fullpath = path.extname(fpath)
    console.log(fullpath); // .html

四、http 服务器模块

1. 创建基本web服务器

// 1. 导入 http 模块
    const http = require('http')
// 2. 创建 web 服务器实例
    const server = http.createServer()
    
// 3. 为服务器实例绑定 request 事件,监听客户端的请求
    server.on('request',(req,res)=>{ // request请求 response回应
    // req.url 是客户端请求的 URL 地址
    	const url = req.url
    // req.method 是客户端请求的 method 类型
    	const method = req.method
    	const str = `Your 请求 url is ${url},请求的方法类似是 ${method}`

	// 为了防止中文显示乱码的问题,需要设置响应头 Content-Type 的值为 text/html;charset=utf-8
		res.setHeader('Content-Type','text/html;charset=utf-8')

	// 调用 res.end() 向客户端发送指定的内容,并结束这次请求的处理过程
		res.end(str)
    })
    
// 4. 启动服务器
    server.listen(80,()=>{
        console.log('server running at http://127.0.0.1');
    })

2. 根据不同的 url 响应不同的 html页面 内容

    const http = require('http')
    const server = http.createServer()

// 用户触发服务器
    server.on('request',(req,res)=>{
        const url = req.url // 获取请求的 url 地址
        let content = '<h1>404 Not Found!</h1>' // 默认的响应内容
        if (url === '/' || url === '/index.html'){ // 判断用户请求的页面地址
            content = '<h1>首页</h1>'
        } else if (url === '/about.html'){
            content = '<h1>关于页面</h1>'
        }
        // 防止中文乱码
        res.setHeader('Content-Type','text/html;charset=utf-8')
        res.end(content) // 内容响应给用户
    })
// 启动服务器
    server.listen(80,()=>{
        console.log('server running at http://127.0.0.1');
    })

五、模块化 require()

1. 加载用户自定义模块/作用域

无法访问另个模块内部的变量/函数,防止全局变量污染问题

// 在使用 require 加载用户自定义模块期间
const m1 = require('./fs.js')
//可以省略 .js 后缀名
const m1 = require('./fs')

2. module 对象

在每个 .js 自定义模块中都有一个 module 对象,它里面存储了和当前模块有关的信息

在这里插入图片描述

1. module.exports 对象的使用

向外共享模块域中的成员
使用 require() 方法导入模块时,导入的结果永远以 module.exports 指向的对象为准
在这里插入图片描述

3. exports 对象

简化 module.exports提供的 exports对象。默认情况下,exports 和 module.exports 指向的同一个对象。最终还是以 module.exports 指向的对象为准。

4. exports 和 module.exports 使用误区

时刻谨记:require() 模块时,得到的永远是 module.exports 指向的对象

注意:为了防止混乱,不要在同一个模块中同时使用 exports 和 module.exports
导出单个变量时可以用 exports
导出多个/一组变量时用 module.exports

在这里插入图片描述

5. 模块的加载机制

  1. 模块在第一次加载后会被缓存,意味着多次调用 require()不会导致模块的代码被执行多次。它们会优先从缓存中加载,从而提高加载效率。
  2. 内置模块是由 node.js 官方提供的,它的优先级最高
  3. 如果省略扩展名,node.js会以下面顺序依次加载:
    在这里插入图片描述
    4.如果没有找到对应的第三方模块,则会向上一层父目录中…直到找到根目录

六、包 与 npm

1. 安装 / 卸载包

使用 npm i 安装包所有的依赖,会自动安装新版本的包。如果需要换包的版本或者更新版本,通过 @ 符号指定具体的版本,例:npm i moment@2.23.4
版本号解释:
@2.23.4(大版本.新增功能版本.bug修复版本)

安装包会自动生成 node_modules 文件夹 和 package.json配置文件,里面记录包的版本等相关信息

以 moment 为例:

安装包:npm i moment
全局安装包:npm i 包名 -g (通常安装一些工具)
卸载包:npm uninstall moment
自动生成package.json配置文件:npm init -y
更新包版本:npm i moment@2.23.4

初始化项目

npm init 初始化项目,创建 package.json文件(需要回答问题)
npm init -y 初始化项目,创建 package.json文件(采用默认值,但文件目录的名字必须是英文,不然会出错)

npm镜像源

由于npm是国外地址,下载包非常慢 可以用国内淘宝镜像源
在这里插入图片描述
配置镜像有两种方式:

  1. 在系统中安装cnpm
    npm install -g cnpm --registry=https://registry.npm.taob
  2. 彻底修改npm仓库地址
    npm set registry https://registry.npm.taobao.org/
    • 还原到原版仓库
      npm config delete registry

2.包与npm-格式化时间 moment()

首先下载:npm i moment

// 引入moment模块
    const moment = require('moment')
// 参考官方文档可以自定义时间的格式化
    const dt = moment().format("YYYY-MM-DD HH:mm:ss")
    console.log(dt);

自定义:

// 自定义 日期时间格式函数
    function dataFormat(dataStr){
        const dt = new Date(dataStr)

        const year = padZero(dt.getFullYear())
        const month = padZero(dt.getMonth() + 1);
        const day = padZero(dt.getDate())

        const hours = padZero(dt.getHours());
        const minutes = padZero(dt.getMinutes());
        const seconds = padZero(dt.getSeconds());

        return `${year}-${month}-${day} ${hours}-${minutes}-${seconds}`
    }
    // 补零函数:
        function padZero(n){
            return n > 9 ? n : '0' + n;
        }

// 向外暴露
        module.exports = {
            dataFormat
        }

3. 转义 和 还原html方法

// 转义 和 还原html方法
        // 转义:
    function htmlEscape(str){
        return str.replace(/<|>|&|"/g, match=>{
            switch(match){
                case "<": 
                    return "&lt;"
                case ">":
                    return "&gt;"
                case "&":
                    return "&amp;"
                case '"':
                    return "&quot;"
            }
        })      
    }
        // 还原
    function htmlUnEscape(str){
        return str.replace(/&lt;|&gt;|&amp;|&quot;/g,match=>{
            switch(match){
                case "&lt;": 
                    return "<"
                case "&gt;":
                    return ">"
                case "&amp;":
                    return "&"
                case '&quot;':
                    return '"'
            }
        })
    }


// 向外暴露
        module.exports = {
            htmlEscape,
            htmlUnEscape
        }

4. 发布 / 删除 包

一定要注意发布包要在 最开始的npm上,不能在镜像源上发布,否则会出错。

  • 查看本地npm是不是官方的 :npm config get registry
  • 设置npm官方源:npm config set registry https://registry.npmjs.org

步骤:

  1. 使用 npm login 登录到npm,cd切换目录到你选择发布包的文件下
  2. 使用 npm publish 发布文件,可以登录到自己的npm官方账号查看是否已经存在这个包
  3. 使用 npm unpublish mario-soda --force 删除包
    在这里插入图片描述

七、express

express 是基于 http 更简洁的写 web服务器 和 API服务器

npm i express@4.17.1 安装express

一、express的基本使用

1. express 创建服务器

// 1. 导入 express
    const express = require('express')
// 2. 创建 web 服务器
    const app = express()
// 3. 启动 web 服务器
    app.listen(80,()=>{
        console.log('express server running at http://127.0.0.1');
    })

2. 监听 GET 和 POST 请求

// 1. 导入 express
    const express = require('express')
// 2. 创建 web 服务器
    const app = express()


// 4. 监听客户端的 GET和POST 请求,并向客户端响应具体内容
    app.get('/user',(req,res)=>{
        // 调用express提供的send()方法,向客户端响应一个 JSON 对象
        res.send({name:'ww',age:19,gender:'女'})
    })
    app.post('/user',(req,res)=>{
        res.send('请求成功!!')
    })

    app.get('/',(req,res)=>{
        // 通过 req.query 可以获取到客户端发过来的 查询参数
        // 注意:默认情况下,req.query是一个空对象
        console.log(req.query);
        res.send(req.query)
    })



// 3. 启动 web 服务器
    app.listen(80,()=>{
        console.log('express server running at http://127.0.0.1');
    })
1. param 传参

在路径中以冒号命名的部分我们称为param
param 传参一般不会传递特别复杂的参数

    // :参数名可以随便取,也可以有多个 动态值
    // /user/:id 表示用户访问 /user/XXX 时就会触发
    app.get('/user/:id/:name',(req,res)=>{
        // 通过 req.params 是动态匹配到的URL参数,默认是一个空对象
        console.log(req.params);
        res.send(req.params)  //{"id":"1","name":"zs"}
    })
2. 实现form表单和node的简易交互

form表单登录代码:

<!-- action的路径和后台要一样 -->
    <form action="/login" method="post">
        name:<br>
        <input type="text" name="name"><br>
        password:<br>
        <input type="text" name="password">
        <input type="submit" value="提交">
        <br>
    </form>

form表单注册代码:

<h2>注册</h2>
    <form action="/register" method="post">
        name:<br>
        <input type="text" name="name"><br>
        password:<br>
        <input type="text" name="password"><br>
        确认密码:<br>
        <input type="text" name="repwd">
        <input type="submit" value="注册">
        <br>
    </form>

node.js代码:

// 导入模块
const path = require('path')
const express = require('express')
const app = express()


// 一、1. 配置静态资源路径
app.use(express.static(path.resolve(__dirname,'test')))
// 二、1.引入解析请求体的中间件
app.use(express.urlencoded())

// 用一个数组存放数据
const DATA = [
  {
    name:"anna",
    password: '123456'
  },
  {
    name: 'Mario',
    password: '123'
  }
]

// 2. get请求 --登录
app.get('/login', function(req, res, next) {
    console.log('请求已经收到');
    // 避免乱码
    res.setHeader('Content-Type','text/html;charset=utf-8')
    // 遍历 数组的数据
    const loginUser = DATA.find(item=>{
      return item.name === req.query.name && item.password === req.query.password
    })
    // console.log(loginUser);// 正确会返回 错误会返回 undefined
    if(loginUser){
      res.send(`<h1>欢迎您再次回来!${loginUser.name}</h1>`);
    }else {
      res.send('<h1>登陆失败</h1>')
    }
  });

  // post --注册
app.post('/register',(req,res)=>{
  // 获取用户输入的数据
  // console.log(req.body);
  // 解构赋值
  const {name,password,repwd} = req.body
  // 判断注册是否有重复名
  const user = DATA.find(item=>{
    return item.name === name
  })
  if (!user){
    // 没有找到就可以注册,并把它加入数组
    DATA.push({
      name,
      password
    })
    res.send('<h1>恭喜您,注册成功!</h1>')
  } else {
    res.send('<h1>用户名重复</h1>')
  }
})

3. express.static() 创建静态资源服务器

可以对外提供访问

// 导入模块
const path = require('path')
const express = require('express')
const app = express()


// 创建静态资源服务器 --可以托管多个静态资源目录
// express.static() 方法,快速的对外提供静态资源
    app.use(express.static(path.join(__dirname,'./clock')))
    app.use(express.static(path.join(__dirname,'./files')))
// 如果需要挂载固定的路径前缀
    app.use('/clock',express.static('./clock'))

// 启动服务器
	app.listen(80,()=>{
	    console.log('express server running at http://127.0.0.1');
	})

4. 安装使用 nodemon

  1. 全局安装:npm i nodemon -g
    启动:nodemon 运行index.js
    nodemon XXX 运行指定的js
  2. 在项目中安装:项目打包时不会把它加进去
    npm i nodemon -D
    启动:npx nodemon

由于传统使用 node app.js 命令启动项目,当代码修改后,需要重启命令。

使用 nodemon app.js 命令启动项目,当代码修改后,会被nodemon自动监听到,并自动修改。

二、express的路由

express路由就是 客户端请求和服务器处理 间的一种映射关系。

1. 路由的模块化

  1. 创建路由模块
// 导入 express
let express = require('express')
// 创建路由对象
let router = express.Router()

// 挂载具体路由
router.get('/user/list',(req,res)=>{
    res.send('get request success')
})
router.post('/user/list',(req,res)=>{
    res.send('post request success')
})

// 向外暴露route 路由
module.exports = router
  1. 注册和使用路由模块
const express = require('express')
const app = express()

// 导入路由模块
const route = require('./自定义模块')
// 注册路由模块 app.use() 函数的作用:注册全局中间件
app.use(route)


app.listen(80,()=>{
    console.log('http://127.0.0.1')
})

三、中间件 use()

  • 在express使用 app.use来定义一个中间件
    中间件作用/用法和路由很像
    但路由不需要区分请求方式,只看路径
  • 和路由的区别:
    1. 会匹配所有请求
    2. 路径设置父目录
  • next()的作用:next是回调函数的第三个参数
    如果有多个路由/中间件,只会执行第一个,调用next()会继续执行后面的路由/中间件
    注意:next()不能在响应处理完毕后调用

1. 全局生效中间件

const express = require('express')
const app = express()

// 定义一个简单的中间件
// 将 mw注册为全局生效的中间件
app.use((req,res,next)=>{
    console.log("第一个中间件");
    // 把扭转关系交给下一个中间件或路由
    next()
})
app.use((req,res,next)=>{
    console.log("第二个中间件");
    // 把扭转关系交给下一个中间件或路由
    next()
})

// 请求给路由 会依次触发上面两个中间件
app.get('/',(req,res)=>{
    res.send('home page!')
})


app.listen(80,()=>{
    console.log('http://127.0.0.1')
})

2. 局部生效中间件

const express = require('express')
const app = express()


// 将 mw注册为局部生效的中间件
const mw1 = (req,res,next)=>{
        console.log("第一个中间件");
        // 把扭转关系交给下一个中间件或路由
        next()
    }
const mw2 = (req,res,next)=>{
        console.log("第二个中间件");
        // 把扭转关系交给下一个中间件或路由
        next()
    }

// 请求给路由 会触发上面mw1中间件,不会触发mw2中间件
app.get('/',mw1,(req,res)=>{
    res.send('home page!')
})
app.get('/user',(req,res)=>{
    res.send('home user page!'+ res.startTime)
})


app.listen(80,()=>{
    console.log('http://127.0.0.1')
})

3. 错误级别中间件

考虑到有时路径错误,前面路由都没访问到,表示友好信息,可以在最后放一个错误路由给用户提示!
不写路径,表示匹配所有路由
一定要放在所有路由中间件的后面。 才能捕获前面路由的错误

// 路由
app.get('/',(req,res)=>{
    // 抛出一个自定义的错误
    throw new Error('服务器发生了错误')
    res.send('Home page!')
})
// 错误级别的中间件 4个参数 --顺序不可颠倒
app.use((err,req,res,next)=>{
    console.log('发生了错误:'+ err.message); // 在服务器打印错误信息
    res.send('Error!'+ err.message) // 向客户端打印错误信息
})

四、模板引擎

希望用户在访问students路由时,可以给用户返回一个显示某个信息的页面
由于html属于静态页面,创建时是什么样子,用户看到的就是什么样子,
不会自动跟随服务器数据的变化而变化。

在node中有一个 模板引擎,它长得像网页,且它里面可以嵌入变量
由于node中的模板引擎较多,都各有特色。但习惯使用 ejs

ejs是其中一款模板:
1. 安装ejs
npm i ejs
2. 配置express的模板引擎为ejs
app.set("view engine","ejs")
3. 配置模板路径(可选)
app.set("views",path.resolve(__dirname,"views"))
注意:模板引擎需要被express渲染后才能使用

res.render() 用来渲染一个模板引擎,并将其返回给浏览器

1. ejs 的使用

ejs是运行在服务器的代码。
res.render(“xxx”,{}) 用来渲染一个模板引擎,并将其返回给浏览器(就是把ejs文件转成网页)
通过它可以将render传递进来的数据直接在网页中显示:

  • <%= %> 在ejs中输出内容,它会自动对字符串的特殊字符进行转义,避免 xxs
  • <%- %> 原样输出
  • <% %> 可以在其中直接编辑js代码,js代码会在服务器中执行打印
  • <%# %> 是ejs里面的注释

服务器的代码:

// 配置模板路径
app.set("views",path.resolve(__dirname,"views"))
// 配置express的模板引擎为ejs
app.set("view engine",'ejs')

// 定义一个名字
var name = 'anna'
// get请求 
app.get('/students', (req, res)=> {
    res.render("students",{name})
    
});

// ejs中form表单的数据
app.get('/set-name',(req,res)=>{
  name = req.query.name
  res.send("修改成功!")
})

.ejs代码:

<h1>ejs使用</h1>
    <!--通过它可以将render传递进来的数据直接在网页中显示 -->
     <%=name %>

    <form action="/set-name">
        <input type="text" name="name" placeholder="请输入您的名字">
        <button>提交</button>
    </form>

    <!-- 在服务器打印js代码 -->
    <% 
        let num = 20
        console.log(num)
    %>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值