NodeJS 案例 记账本
一、构建项目基本功能
1.1 使用Express 搭建项目
express -e accounts // -e 使用 ejs 模板引擎
cd accounts // 切换到项目目录下
npm i // 安装全部依赖
1.2 基本配置路由
在 index.js 路由文件中添加:
// 渲染记账本的列表
router.get('/account', function(req, res, next) {
res.render('list');
});
// 渲染添加记录
router.get('/account/create', function(req, res, next) {
res.render('create');
});注意:
访问静态资源时,要使用绝对路径形式,而不使用相对路径
<script src="" data-missing="jquery.min.js"></script> // 相对路径 访问时可能会出现路径错误
<script src="/js/jquery.min.js"></script> // 绝对路径:拼接当前网页的 协议域名和端口 形成绝对路径,然后去网站的静态目录下寻找资源,不会出错1.3 获取表单数据
使用form表单的提交数据功能,必须为要获取数据的表单组件添加 name 属性值,和设置 form 的属性
添加 name 属性
html<input name="title" type="text" class="form-control" id="item" /> ... <textarea name="remarks" class="form-control" id="remarks"></textarea>设置 form 属性
html<form method="post" action="/account"> // 设置为绝对路径配置获取表单内容的路由
jsrouter.post("/account", (req, res) => { console.log(req.body); // 获取请求体的数据 // req.body 以对象形式获取到表单的数据,name为键名,value为值 res.send("ok"); });
1.4 lowdb介绍与使用
1.4.1 lowdb介绍
Lowdb是一个基于Node.js的轻量级JSON数据库,它允许您使用JavaScript对象和数组来操作数据。
1.4.2 lowdb安装
npm i lowdb@1.0.0
最新版本使用 ES6 语法进行导入,这里安装老版本,使用 node 模块导入
14.3 使用lowdb
初始化:
// 导入 lowdb
const low = require("lowdb");
const FileSync = require("lowdb/adapters/FileSync");
// 将数据存放到 db.json 文件中 会自动创建
const adapter = new FileSync("db.json");
// 获取 db 对象
const db = low(adapter);
// 初始化数据
db.defaults({ posts: [], user: {} }).write();写入数据:
// 顺序写入数据
db.get('posts') // 获取posts 数组
.push({ id: 1, title: 'hello'}) // 向数组中添加数据
.write(); // 写入数据
// 倒序插入数据
db.get("posts").unshift({ id: 2, title: "world" }).write();获取数据:
// 获取指定数据
console.log(db.get("posts").find({ id: 2 }).value());
// 获取全部数据
console.log(db.get('posts').value());删除指定数据:
let res = db.get("posts").remove({ id: 1 }).write(); // 返回被删除的数据
console.log(res); // res 为被删除的数据更新数据:
db.get("posts") // 获取 posts 数组
.find({ id: 2 }) // 查找 id 为 2 的数据
.assign({ title: "good" }) // 更新数据
.write(); // 写入数据1.5 使用lowdb保存账单数据
安装 shortid 用于将保存的 json 数据进行 id 自动编号
npm i shortid
// 导入 lowdb
const low = require("lowdb");
const FileSync = require("lowdb/adapters/FileSync");
// 将数据存放到 db.json 文件中
const adapter = new FileSync(__dirname + "/../data/db.json"); // 绝对路径,保存在data文件夹下的db.json 文件中
// 获取 db 对象
const db = low(adapter);
// 导入 shortid 用于将json数据自动编号
const shortid = require("shortid");
....
// 新增记录/处理获取到的表单数据
router.post("/account", (req, res) => {
console.log(req.body); // 获取请求体的数据
// req.body 以对象形式获取到表单的数据,name为键名,value为值
// 生成 id
let id = shortid.generate();
// 将获取到的数据保存到 db.json中
db.get("accounts")
.unshift({ id: id, ...req.body }) // ...req.body 扩展运算符
.write();
res.send("ok");
});1.6 完善成功提醒功能
在成功添加数据后,跳转到一个新的页面
创建一个 success.ejs 页面用于跳转使用,并接收传递的值
<h1>:) <%= msg%></h1>
<p><a href=<%= url%>>点击跳转</a></p>在路由中渲染页面,并传递参数
res.render("success",{msg: "添加成功~",url:'/account'});1.7 账单列表动态渲染
设置路由:
获取 db.json 中存储的全部账单信息,并使用模板引擎渲染到页面中。
router.get("/account", function (req, res, next) {
// 获取到 db.json 中 accounts 对象存储的所有账单数据
let accounts = db.get('accounts').value();
res.render("list",{accounts}); // 动态渲染到账单列表中
});使用模板引擎渲染账单数据:
<div class="accounts">
<% for(let item of accounts) {%> // 遍历数据
<div class="panel <%= item.type==='-1'? 'panel-danger' : 'panel-success'%>">//三目运算 type = -1 支出 type = 1 收入
<div class="panel-heading"><%= item.time%></div>
<div class="panel-body">
<div class="col-xs-6"><%= item.title %></div>
<div class="col-xs-2 text-center">
<span
class="label <%= item.type==='-1'? 'label-warning' : 'label-success'%>"
><%= item.type==='-1' ? '支出' : '收入'%></span
> // 根据 type 的值判断是收入还是支出
</div>
<div class="col-xs-2 text-right"><%=item.account%></div> // 动态渲染值
<div class="col-xs-2 text-right">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</div>
</div>
</div>
<% }%>
</div>1.8 删除账单功能
删除账单的某条记录,只需在db.json/数据库中将该条记录删除掉。
设置删除路由
jsrouter.get("/account/delete", (req, res) => { // 获取传递来的 id let id = req.query.id; // get 请求使用query // 删除db.json 中的对应 id 的数据 db.get("accounts").remove({ id }).write(); // 找到accounts数组中对应id的值并删除 // 重定向到列表页 即将地址栏的 地址改为 /account 获取列表页面 res.redirect("/account"); // 或渲染一个删除成功的页面 // res.render('success',{msg:"删除成功",url:"/account"}) });在 ejs 模板引擎中 发送请求并传递要删除列表的 id 值
html<!-- 向服务端发送get请求并传递查询字符串--> <a href="/account/delete?id=<%= item.id%>" <!-- /account/delete 使用绝对路径,与地址栏的协议域名端口进行拼接--> ><span class="glyphicon glyphicon-remove" aria-hidden="true"></span ></a>
二、连接mongodb数据库
2.1 创建mongodb数据库连接
在入口文件www 导入db.js
安装 mongoose
npm i mongoose
导入db 并将www 下的全部代码 放在db 的成功回调函数中
在连接数据库成功后,才进行http服务的创建
// 导入 db 函数
const db = require('../db/db');
// 调用 db 函数
db(()=>{ // 连通数据库后,在进行创建http服务的操作
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('accounts:server');
var http = require('http');
.......
})2.2 准备模型文件
在models 文件夹下创建 AccountModels.js 文件
创建账单的模型对象
// 导入 mongoose
const mongoose = require("mongoose");
// 创建文档的结构对象
let AccountSchema = new mongoose.Schema({
title: {
// 标题
type: String,
required: true,
},
time: Date, // 日期
type: {
// 类型
type: Number,
default: -1,
enum: [1, -1],
},
account: {
type: Number,
required: true,
}, // 金额
remarks: String, //备注
});
// 创建模型对象 创建和使用 accounts 集合
let AccountModel = mongoose.model("accounts", AccountSchema);
// 暴露模型对象
module.exports = AccountModel;2.3 插入数据库
2.3.1 moment格式化时间
使用moment 模块将 2024-02-10 格式的日期转换为 new Date格式的日期
安装 moment
npm i monent
使用:
// 1. 2024-02-10 格式转换为 Date()对象格式
moment('2024-02-10').toDate()
// 2. 将Date()对象转换为 2024-02-10 格式
moment(Date).format('YYYY-MM-DD')2.3.2 将数据插入数据库
const AccountModel = require('AccountModel');
router.post("/account", (req, res) => {
console.log(req.body); // 获取请求体的数据
// 插入数据库
AccountModel.create({
...req.body, // ES6 解构
// 修改time属性的值
time:moment(req.body.time).toDate()
}).then((data)=>{
res.render("success", { msg: "添加成功~", url: "/account" });
}).catch((err)=>{
res.render("success", { msg: "添加失败~", url: "/account" });
})
});2.4 读取数据库
读取数据库bilibili 中的 accounts 集合,并将其中的数据渲染到 页面中
router.get("/account", function (req, res, next) {
AccountModel.find()
.sort({ time: -1 }) // 按照添加时间降序排列
.then((data) => {
res.render("list", { accounts:data ,moment});
// 动态渲染到账单列表中 并将momemt对象传入到模板中 对time进行格式化转换
})
.catch((err) => {
res.status(500);
res.render('success',{msg:'出错了',url:'/account/create'})
});
});2.4 删除文档
删除对应的记录时,将数据库中对应的文档删除
router.get("/account/delete", (req, res) => {
// 获取传递来的 id
let id = req.query.id;
// 删除mongodb中的数据
AccountModel.deleteOne({_id:id})
.then((date)=>{
// 重定向到列表页 即将地址栏的 地址改为 /account
res.redirect("/account");
})
.catch((err)=>{
res.render('success',{msg:'删除失败',url:'/account'})
})
});2.6 完善删除功能
使用 js完善删除操作,防止用户误删除
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
let delBtn = $(".delBtn"); // jq选择器返回的是一个jquery对象
delBtn.click(function (e) {
// 这里不需要对其遍历绑定事件
if (confirm("您确定要确认删除吗?")) {
// window.confirm()方法返回true(确定)/false(取消)
return true;
} else {
// 阻止a标签的默认行为
e.preventDefault();
}
});
</script>三、结合 API 功能
3.1 创建接口文件
在routes 文件夹下 创建一个 account.js 用于设置 API 接口 ,用于接收安卓,ios用户上传的数据和返回一个 json 格式的数据,交由前端工程师进行手机端页面的渲染。(前后端分离)
web 文件夹下的 accounts.js 用于响应浏览器发出 http 请求,并由服务器端web渲染页面(服务器端渲染)
设置 api 接口中间件
const accountRouter = require("./routes/api/account");
......
app.use("/api", accountRouter); // 路由前缀加一个 /api使用 RESTful API
3.2 获取账单全部数据接口
router.get("/account", function (req, res, next) {
// 读取mongodb数据库中 accounts集合数据
AccountModel.find()
.sort({ time: -1 }) // 按照添加时间降序排列
.then((data) => {
// 成功获取数据,将json数据返回给客户端
res.json({
// 响应编号
code: "0000", // 成功
// 响应信息
msg: "读取成功",
// 响应的数据
data: data,
});
})
.catch((err) => {
res.json({
// 获取数据失败处理
code: "1001", // 失败
msg: "读取失败",
data: null,
});
});
});请求方式:GET
url:/account
请求体:none
3.3 添加账单接口
router.post("/account", (req, res) => {
// 表单验证,对表单数据进行验证,返回不同的错误信息
........
// 插入到 mongodb 数据库
AccountModel.create({
...req.body, // ES6 解构
// 修改time属性的值
time: moment(req.body.time).toDate(),
})
.then((data) => {
res.json({
code: "0000",
msg: "添加成功",
data: data,
});
})
.catch((err) => {
res.json({
code: "1001",
msg: "添加失败",
data: "null",
});
});
});请求方式:POST
url:/account
请求体:row:{
"title": "买西瓜",
"time": "2022-06-10",
"type": -1,
"account": 30,
"remarks": "天太热了"
}
3.4 删除账单接口
router.delete("/account/delete", (req, res) => {
// 获取传递来的 id
let id = req.query.id;
// 删除mongodb中的数据
AccountModel.deleteOne({ _id: id })
.then((date) => {
res.json({
code: "0000",
msg: "删除成功",
data: date,
});
})
.catch((err) => {
res.json({
code: "1003",
msg: "删除失败",
data: null,
});
});
});请求方法:DELETE
url: /account/delete?id=xxx
请求体:none
3.5 获取单个账单接口
router.get("/account/:id", (req, res) => {
// 获取 id参数
let {id} = req.params; // 对req.params对象进行结构
// 查询数据库
AccountModel.findById(id)
.then((data) => {
res.json({
code: "0000",
msg: "获取成功",
data: data,
});
})
.catch((err) => {
res.json({
code: "1004",
msg: "获取失败",
data: null,
});
});
});请求方法:GET
url: /account/id
请求体:none
3.6 更新账单接口
router.patch("/account/:id", (req, res) => {
// 获取要更新账单的 id 值
let { id } = req.params;
// 更新数据库 将要更新的数据放在请求体中
AccountModel.updateOne({ _id: id }, req.body)
.then((data) => {
// 获取更新后的数据
AccountModel.findById(id).then((data) => {
res.json({
code: "0000",
msg: "更新成功",
data: data,
});
});
})
.catch((err) => {
res.json({
code: "1005",
msg: "更新失败",
data: null,
});
});
});请求方法:PATCH
url: /account/id
请求体:row:{ // 要更新的数据
"account":20000
}
四、注册登录页面&用户校验session
4.1 注册页面
创建auth.js 路由文件
设置路由渲染 注册页面
var express = require("express");
var router = express.Router();
// 导入 moment 用于格式化时间
const moment = require("moment");
// 导入 AccountModel 模型
const AccountModel = require("../../models/AccountModel");
router.get('/reg',(req,res)=>{
// 响应登录页面
res.render('auth/reg');
});
module.exports = router;4.2 注册用户
4.2.1 渲染用户注册页面
登录注册使用同一个 ejs 模板
// 响应注册页面
router.get("/reg", (req, res) => {
// 响应登录页面
res.render("auth/reg", { title: "注册", action: "/reg" });
});添加表单nam属性和设置form表单
4.2.2 创建 UserModel 模型对象
// 导入mongoose
const mongoose = require("mongoose");
// 创建文档的结构对象
const UserSchema = new mongoose.Schema({
// 用户名
username: {
type: String,
required: true,
unique: true,
},
// 密码
password: {
type: String,
required: true,
},
});
// 创建模型对象
let UserModel = mongoose.model("users", UserSchema);
// 暴露模型对象
module.exports = UserModel;4.2.4 密码加密
md5:单向不可逆加密
安装 npm i md5
使用:
// 导入 md5 模块
const md5 = require("md5");
// 使用 md5 对密码进行单向加密处理
password: md5(req.body.password)4.2.3 配置路由
将表单数据添加到数据库中,并做出响应
router.post("/reg", (req, res) => {
// 获取请求体的数据
console.log(req.body);
// 将数据插入到mongodb中 使用md5 将密码单向加密处理
// 在插入前先判断是否存在该用户名,用户名作为唯一标识
UserModel.findOne({ username: req.body.username }).then((data) => {
res.render("success", { msg: "该用户名已存在", url: "/reg" });
});
UserModel.create({ ...req.body, password: md5(req.body.password) })
.then((data) => {
// 响应注册成功页面
res.render("success", { msg: "注册成功", url: "/login" });
})
.catch((err) => {
res.status(500), res.render("success", { msg: "注册失败", url: "/reg" });
});
});4.3 用户登录
4.3.1 渲染登录页面
// 用户登录页面
router.get("/login", (req, res) => {
res.render("auth/reg", { title: "登录", action: "/login" });
});4.3.2 登录路由配置
router.post("/login", (req, res) => {
// 在数据库中查找用户信息判断账号和密码是否匹配
let { username, password } = req.body; // 解构获取 username 和 password
UserModel.findOne({
// 用户名和密码同时匹配
username: username,
password: md5(password),
}).then((data) => {
if (data !== null) {
// 匹配成功 重定向到账单页面
return res.redirect("/account");
}
// 渲染登录失败页面
res.render("success", { msg: "账号或密码错误", url: "/login" });
});
});4.4 写入session
使用session 限制浏览器用户访问账单列表,只能在登录后才能访问。
4.4.1 安装session
npm i express-session connect-mongo
connect-mongo:连接mongodb 数据库将session存储在mongodb数据库中。
4..4.2 导入express-session
在app.js中导入session 和 MongoStore模块
const session = require("express-session");
const MongoStore = require("connect-mongo");4.4.3 设置session 中间件
在 app.js 中设置session中间件
// 导入配置文件
const { DBHOST, DBPORT, DBNAME } = require("./config/config");
app.use(
session({
name: "sid", //设置cookie的name,默认值是:content.sid
secret: "atguigu", //参与加密的字符串(又称签名)
saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储session的is
resave: true, //设置为true表示每次请求都会重新保存session(无感刷新)
store: MongoStore.create({ // 设置存储session的数据库
mongoUrl: `mongodb://${DBHOST}:${DBPORT}/${DBNAME}`,
}),
cookie: {
httpOnly: true, // 开启前端无法通过JS操作cookie
maxAge: 1000 * 60 * 60 * 24 * 7, // 设置有效期7天
},
})
);4.4.4 创建session
当用户名和密码匹配成功,用户登录时将session要存储的用户信息写入数据库,并通过响应体向前端返回sid
req.session.username = data.username; // 写入 用户名
req.session_id = data._id; // 写入 用户_id登录成功后,会在数据库创建一个session集合存储session信息

4.5 登录检测
下次登录时,浏览器会自动携带 sid 作为cookie发送给服务器端,服务器获取到sid 后会在数据库中查找session数据

声明路由中间价,获取 session中的数据判断用户是否已经登录且session未过期
let checkLoginMiddleware = (req,res,next) => {
if(!req.session.username){ // 获取不到该 sid 对应的session数据
return res.redirect("/login") //跳转到登录页面
}
next(); // 执行后面的回调
}为需要登录检测的页面和操作进行登录判断
便于以后其它路由使用该中间件,将其独立成一个文件,并暴露出去,在需要使用的地方进行引入
创建一个 middlewares 文件夹
创建一个 checkLoginMinddleWares.js文件
// 判断是否登录的路由中间件
module.exports = (req,res,next) => {
if(!req.session.username){ // 获取不到该 sid 对应的session数据
return res.redirect("/login") //跳转到登录页面
}
next(); // 执行后面的回调
}- 导入checkLoginMinddleWares中间件
const checkLoginMiddleware = require("../../middlewares/checkLoginMiddleware")- 使用路由中间件
router.get("/account", checkLoginMiddleware, function (req, res, next) {
.......
});4.6 退出登录
退出登录时销毁session数据(将session设置为立即过期且将数据库中的session数据删除,网站请求头携带的sid无法匹配到数据),下次访问时需要重新登录
设置退出登录路由
router.get("/logout", (req, res) => {
req.session.destroy(() => { // 清除session,将数据库中的session删除掉
res.render("success", { msg: "退出登录,bey", url: "/login" });
});
});4.7 跨站请求伪造 CSRF
使用 Live Server 打开attack.html 文件
attack.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="http://127.0.0.1:3000/logout">
</head>
<body>
</body>
</html>刷新 记事本已经登录的页面,将会退出登录,记事本网站就遭受到了攻击。
跨站:由网站B 向网站A 发送请求,且会携带A网站的cookie
A网页会退出登录原因:B网站携带了A网站的cookie,访问了A网站的退出登录路由,导致session被销毁。
解决办法:
将退出登录的请求改为post
在前端通过post请求去退出登录,使用form或ajax
使用ajax会出现跨域,form表单提交数据时,是通过HTTP协议的POST或GET方法直接向服务器发送请求,这个过程并不涉及到JavaScript的同源策略限制,因此不会出现跨域问题。
但本机通过使用post请求去跨站请求伪造还是可以实现
4.8 首页与404页
4.8.1 首页跳转
将根目录 / 重定向到 /account 路由下
// 添加首页的路由规则
router.get("/",(req,res)=>{
//将路由根目录/ 重定向到 /account 页面
res.redirect("/account");
})4.8.2 设置404页面
在app.js 中设置错误处理,渲染404页面
创建一个 404.ejs 模板引擎用于渲染使用

公益404
引入公益404的script标签即可
五、接口token响应与校验
设置token 校验,当移动端app 用户登录后才能访问aip中的数据。浏览器端使用session进行登录验证。
5.1设置登录路由响应token
创建auth.js 路由文件
安装 jsonwebtoken
npm i jsonwebtoken
引入 jwt
const jwt = require("jsonwebtoken");创建当前用户的token
let token = jwt.sign(
{ _id: data._id, username: username }, // 用户数据
"abcde", // 加密字符串
{
expiresIn: 60 * 60 * 24 * 7, // token 7天有效期
}
);用户名和密码与数据库验证成功,响应token
res.json({
code: "0000", // 响应码
msg: "登录成功", // 响应信息
data: token, // 发送token
});设置用户名和密码与数据库验证失败的响应
res.json({
code: "2002",
msg: "用户名或密码错误",
});5.2 配置路由规则
在app.js 中 导入并使用auth.js
// 导入api auth 路由文件
const authAipRouter = require("./routes/api/auth")
.....
// 使用
app.use("/api",authAipRouter)5.3 登录接口测试

5.4 校验token
5.4.1 封装中间件文件
在middlewares文件夹下创建 checkTokenMiddleware.js 文件 用于设置token校验的路由
// 导入 jwt 用于校验token
const jwt = require("jsonwebtoken");
// 声明中间件,用于token的获取和校验
module.exports = (req, res, next) => {
// 获取token
let token = req.get("token");
// 判断 token 是否存在
if (!token) {
return res.json({
// 使用return 语句停止执行后续代码
code: "2003",
msg: "token缺失",
data: null,
});
}
// 校验token 加密字符串
jwt.verify(token, "abcde", (err, data) => {
if (err) {
return res.json({
code: "2004",
msg: "token校验失败", // token过期或不正确
data: null,
});
}
// token 存在且校验成功,继续执行后续中间件
next();
// 此处token只用于检验用户是token是否正确且token不过期,
// 不用于获取用户信息再做数据匹配和获取
});
};5.4.2 引入路由中间件
const checkTokenMiddleware = require("../../middlewares/checkTokenMiddleware");5.4.3 使用路由中间件
router.get("/account", checkTokenMiddleware, function (req, res, next) {
......
});5.5 token功能完善
5.5.1 将token加密字符串添加到配置文件
在token加密和解密时都用到了加密字符串,将token加密字符串添加到配置文件config.js中,便于修改。
config.js
// 配置文件
module.exports={
DBHOST:'127.0.0.1', // 主机名
DBPORT:27017, // 数据库端口号
DBNAME:'bilibili', // 数据库名
secret:"abcde" // token 加密字符串
}导入 config.js 配置文件中的secret加密字符串
const { secret } = require("../config/config");使用 secret
jwt.verify(token, secret, (err, data) => {}5.5.2 保存当前用户信息
当记事本项目为一个多人用户时,需要根据当前用户的信息,去获取响应的数据
在 checkTokenMiddleware.js 的token校验成功的回调中,保存当前用户的信息
req.user = data; // 将当前用户信息,保存到req的user属性中获取在其它路由规则中获取当前用户的信息
userdata = req.user;
console.log(userdata);
/*
{
_id: '65c49ee4b6b61362ee7de935',
username: 'admin',
iat: 1707549503,
exp: 1708154303
}
*/5.5.3 req属性
中间件可以访问操作req和res对象,将一些获取到的数据存储到req请求和res响应对象中
因此可以可以在路由回调中,通过 req.body req.query req.session....... 来获取数据
六、本地域名
6.1 什么是本地域名
本地域名就是 只能在本机使用的域名 一般在开发阶段使用
6.2 操作流程
编辑文件 C:\windows\Ststem32\dirvers\etc\hosts
127.0.0.1 www.xin.com如果修改失败,可以修改该问价的权限,将users的权限设置为完全控制。

6.3 使用本地域名
在浏览器中输入
与http://127.0.0.1:3000/login 效果相同都可以进行访问项目
6.4 设置默认端口号
http 默认端口号为 80
https 默认端口号为 443
修改项目的端口号
在www.js文件中修改 端口号3000为80
// var port = normalizePort(process.env.PORT || '3000');
var port = normalizePort(process.env.PORT || '80');此时就可以通过 www.xin.com 访问项目了
6.5 原理
在地址栏输入 域名 之后,浏览器会先进行 DNS 查询,获取该域名对应的IP地址
请求会发送到 DNS 服务器,可以 根据域名返回IP地址
可以通过 ipconfig/all 查看本机的 DNS 服务器
hosts 文件也可以设置域名与IP 的映射关系,在发送请求之间,可以通过该文件获取域名的IP地址

七、项目上线
7.1 代码上传仓库
7.1.1 点击git初始化仓库
7.1.2 忽略node_modules文件夹
在项目根目录下创建.gitignore文件,并添加以下代码
// 忽略node_modules文件夹下的文件
node_modules
// 忽略upload中的文件
/public/upload当文件夹被忽略后,在项目目录中会变色
可以在vsode中设置全局忽略 node_modules文件夹
7.1.3 提交到本地仓库
输入备注信息:init 点击提交
7.1.4 创建远程仓库
使用码云或github创建远程仓库
复制远程仓库的地址:https://github.com/webXin123/account.git
7.1.5 添加远程存储库

根据提示输入用户名和密码
7.1.6 提交
点击 发布Branch
7.1.7 查看
在github中查看提交的项目
7.2 购买云服务器
7.2.1 打开阿里云官网
产品 -> 云服务器ECS -> 立即购买
7.3 连接云服务器并安装软件
7.4 克隆代码启动服务
git clone url 克隆代码
gti pull 拉取更新