Nodejs--Express框架使用

目录

一.概念

二.项目目录结构

 三.app.js

四.项目需要的中间件

五.Mysql连接

六.日志配置

七.实体模型配置

 八.统一结果封装

九.app.js的详细配置

十.自定义登录拦截器

十一.route路由配置

十二.controller处理

十二:静态页面:

十三:总结


一.概念

Express是一个使用Node.js编写的灵活且最受欢迎的Web应用程序框架。它提供了一组用于构建Web应用程序和API的工具和功能,使开发者能够轻松地处理路由、请求和响应、中间件、模板引擎等。

以下是一些Express框架的特点和用法:

  1. 快速搭建Web应用程序:Express提供了一个简洁的API,使您能够快速搭建一个完整的Web应用程序。通过使用Express,您可以轻松地定义路由、处理HTTP请求和响应、设置和使用中间件等。

  2. 简化的路由功能:Express提供了一种简单而强大的方式来定义和处理路由。您可以使用Express的路由功能来定义不同URL路径的处理函数,并将它们链接在一起以构建完整的Web应用程序。

  3. 中间件支持:Express使用中间件来处理请求和响应。中间件是一个函数,它可以在请求到达目标处理函数之前或之后执行,并对请求或响应进行处理。Express提供了许多内置的中间件,也支持使用第三方中间件。

  4. 数据库集成:Express与多个数据库提供商(如MongoDB、MySQL等)兼容,并提供了方便的数据库集成。您可以使用适当的数据库驱动程序和中间件来连接和操作数据库。

  5. 模板引擎支持:Express支持多种模板引擎,如EJS、Pug等。模板引擎允许您在服务器端生成HTML,并将动态数据注入到模板中。

总的来说,Express是一个简单而强大的Web应用程序框架,它使开发者能够快速构建可靠的Web应用程序和API。无论是构建小型项目还是大型应用程序,Express都是一个理想的选择。

二.项目目录结构

 

说明:

 1.app.js : 该文件是启动服务器的主文件,类似于Springboot中的Appliction,

2.routes/ :该目录进行了路由的相关配置

3.public/ :该目录是静态文件目录,页面放在该目录下

4.config/ :该目录放我们项目的一些配置文件

5.models/ : 该目录放实体类模型

6.controller/ :该目录类似java的controller,mvc模式

7.interceptor/ :该目录放一些拦截器的相关配置

8.logs/ :该目录放日志文件

9.package.json :该文件是我们项目的说明和依赖,类似于pom.xml

 三.app.js

express基于nodejs标准库中的http模块构建,下面我们看一下app.js文件

 我们先看一下最核心最简单的配置:

const express = require('express')//构建应用对象
const app = express()app.get('/',(req,res)=>{res.send("hello,world")
})//启动服务器
app.listen(8081,()=>{console.log("访问地址: http://localhost:8081");
})

 当我们访问8081这个端口时,就能返回hello,world,

四.项目需要的中间件

我们的项目需要满足一些基本的要求,例如:连接mysql,会话管理,静态资源解析,等,下面我们看看我们需要安装哪些中间件:

{"name": "untitled","version": "0.0.0","private": true,"scripts": {"start": "node ./bin/www"},"dependencies": {"axios": "^1.6.2","body-parser": "^1.20.2","cookie-parser": "~1.4.4","cors": "^2.8.5","debug": "~2.6.9","express": "~4.16.1","express-session": "^1.17.3","http-errors": "~1.6.3","morgan": "~1.9.1","pug": "2.0.0-beta11","sequelize": "^6.35.0","session-memory-store": "^1.0.0","static": "^2.0.0","winston": "^3.11.0"}
}

 我们来解释一下:

  1.axios : 这个不用多说了,就是发请求的

  2.body-parser: 这个是用来解析请求体的,

  3.cookie-parser: 这个是用来解析cookie获取的

 4.cors: 这个是用来配置跨域的

5.debug: express自带的

6.express-session: 负责session管理

7.sequelize: 这个是orm框架,类似于mybatis

8.session-memory-store: 这个是用来将session保存到内存中

9.static: 这个是负责解析静态资源目录的

10.winston: 这个是日志框架,类似于log4j,slf4j等

五.Mysql连接

  在config/目录下创建db.js文件

const Sequelize = require('sequelize')const sequelize = new Sequelize('mybatis','root','123456',{host: 'localhost',dialect: 'mysql',timezone: '+08:00',     // 这里是东八区,默认为0时区// 使用连接池pool: {max: 5,min: 0,acquire: 30000,idle: 10000,},
})module.exports = sequelize;

 创建sequelize对象,前三个参数分别是:数据库名称用户名密码, 然后是地址类型

 时区,以及连接池配置

最后不要忘记吧对象导出

六.日志配置

在config/目录下创建log.js文件

const winston = require('winston')
const path = require('path');
//上一级目录
const dirname = path.dirname(__dirname)
// 设置日志文件输出位置
const logFilePath = path.join(dirname, 'logs', 'logs.txt');const util = require('util');// 创建日志记录器
const logger = winston.createLogger({level: 'info', // 设置日志级别(可选)//format: winston.format.json(), // 设置日志格式(可选)format: winston.format.combine(winston.format.timestamp({format: 'YYYY-MM-DD HH:mm:ss' // 设置时间格式}),winston.format.printf(info => {// 检查是否传递了user参数if (info.message && info.user) {return `${info.timestamp} ${info.level}: ${info.message} ${info}`;}return `${info.timestamp} ${info.level}: ${info.message}`;})),transports: [new winston.transports.Console(), // 将日志输出到控制台new winston.transports.File({ filename: logFilePath }) // 将日志输出到文件]
});function logInfo(...args){logger.info(getStr(args))
}
function logError(...args){logger.error(getStr(args))
}function getStr(...args){var str = ''for(let i=0;i<args.length;i++){var s = util.inspect(args[i])str += s}return str;
}module.exports = {logInfo,logError};

我这里使用了util模块的inspect()方法,他可以吧任意类型的值转化为字符串

七.实体模型配置

我们先看一下数据库:

tb_user表:

tb_student表:

 

然后我们分别在models/目录下创建User.jsStudent.js文件

 

const {Model,DataTypes} = require('sequelize')const sequelize = require('../config/db')/*** 创建user模型,指定表为tb_user*/
const User = sequelize.define('User',{id:{type:DataTypes.INTEGER,primaryKey:true,autoIncrement:true} ,username: DataTypes.STRING,password: DataTypes.STRING
},{tableName:"tb_user",timestamps: false // 关闭自动插入 timestamps 字段
})module.exports = User;
const {Model,DataTypes} = require('sequelize')const sequelize = require('../config/db')/*** 创建user模型,指定表为tb_user*/
const Student = sequelize.define('Student',{id:{type:DataTypes.INTEGER,primaryKey:true,autoIncrement:true} ,stuid:DataTypes.INTEGER,stuname:DataTypes.STRING,sex:DataTypes.STRING,myClass:DataTypes.STRING,address:DataTypes.STRING,tel:DataTypes.STRING,sorce:DataTypes.INTEGER},{tableName:"tb_student",timestamps: false // 关闭自动插入 timestamps 字段
})module.exports = Student;

说明一下:

 每个字段可以规定为mysql的那种类型,type:指定类型,primaryKey:指定主键,autoIncremnt:指定自增等...后面我会在出一期详细介绍sequlize框架的文章

 八.统一结果封装

 像java中那样封装结果:code,msg,data三个:

在models/目录下创建Result.js文件


const httpStatus = require('./HttpStatus')class Result {constructor(code, msg, data) {this.code = code;this.msg = msg;this.data = data;}/*** 默认的成功返回方法*/static ok(httpStatus,data) {return new Result(httpStatus.code,httpStatus.msg,data)}/*** 默认的失败返回方法* @returns {Result}* @param httpStatus*/static error(httpStatus) {return new Result(httpStatus.code,httpStatus.msg,null);}
}module.exports = Result

然后在创建HttpStatus.js文件

const HttpStatus = {OK: { code: 200, msg: "success" },ERROR: { code: 500, msg: "failure" }
};module.exports = HttpStatus;

这是要给字面量对象,我们可以使用它来模拟java中的枚举类型

九.app.js的详细配置

const express = require('express')
const bodyParser = require('body-parser')
//导入用户路由
const userRoutes = require('./routes/user')
const stuRoutes = require('./routes/student')
//导入会话管理
const session = require('express-session')
const MemoryStore = require('session-memory-store')(session);
//导入拦截器
const {checkRequest} = require('./intercepter/intercepter')
//导入cookie解析器
const cookieParser = require('cookie-parser');
//导入跨域配置
const cors = require('cors')
//导入static静态资源模块
const static = require('static')//构建应用对象
const app = express()
//设置跨域
app.use(cors())
//启用static模块
app.use(express.static('public'))//解析请求体
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extend: true }))//设置会话中间件
app.use(session({sotre: new MemoryStore(), //使用内存管理会话secret: 'ikun123',resave: false,saveUninitialized: true,cookie: { secure: true,maxAge: 1800000 } //30分钟
}))//使用cookie解析器
app.use(cookieParser())//使用拦截器
app.use(checkRequest)//使用路由
app.use('/user',userRoutes)
app.use('/stu',stuRoutes)//启动服务器
app.listen(8080,()=>{console.log("访问地址: http://localhost:8080");
})

我们通过以上配置可以基本实现一些主要的功能

十.自定义登录拦截器

 我们可以写一个类似springmvc那样的拦截器,来判断是否登录

在interceptor/目录下创建intercerptor.js文件:

// AuthMiddleware.js
const whitelist = ['/user/login','/user/register'];
//const blacklist = ['/user/list','/user/logout','/user/add'];
//导入会话管理
const session = require('express-session')
const MemoryStore = require('session-memory-store')(session);const checkRequest = (req, res, next) => {const url = req.originalUrl;if (whitelist.includes(url)) {next();}else{//如果不是白名单,那么就需要验证了console.log("黑名单拦截...");//  console.log(req.cookies.sessionID);//黑名单if(req.cookies.sessionID != null){const cookieSessionID = req.cookies.sessionIDconst acticeSessions = req.sessionStore.sessions;//  console.log(acticeSessions);for(let sessionID in acticeSessions){// console.log(sessionID);//比较二者值if(sessionID === cookieSessionID){//相等,放行,跟新session时间acticeSessions[sessionID].validity = 30*60console.log("放行...");return next()}}//遍历完没有找到seesionID,说明回话过期return res.status(401).json({message:'会话过期!,请重新登录'})}else{return res.status(401).json({message:'未登录!'})}}
};module.exports = {checkRequest
};

这里我们先创建了一个白名单,来默认放行不需要拦截的路径,然后其他的路径需要拦截,

 我们可以看到它传入三个参数:req,res.next,这个next它是express框架内置的一个用来负责函数执行顺序的,也就是自带的,我们可以在任何函数中使用它,当放行之后,我们使用next()方法来继续执行下一个函数,

 我们先尝试获取请求头中的cookie中的sessionID,如果有,我们拿着这个sessionID和我们服务端保存的session集合中遍历,对比是否有相等的,如果有,就放行,没有,返回会话过期提示

为什么是遍历呢?因为我们的服务端保存了很多用户的session,不可能是单个session,所以要遍历对比

十一.route路由配置

我们在route/目录下分别创建user.js和student.js,

const express = require('express')
const UserController = require('../controller/UserController')const router = express.Router()//GET /user/login
router.post('/login',UserController.login)//GET /user/list --获取用户列表
router.get('/list',UserController.list)//POST /user --添加用户
router.post('/add',UserController.addUser)//GET /user/test 测试路径
router.get('/test',UserController.test)//GET /user/logout 退出登录
router.post('/logout',UserController.logout)module.exports = router
const express = require('express')
const StuController = require('../controller/StuController')const router = express.Router()router.get('/list',StuController.list)router.get('/byid/:id',StuController.searchByid)module.exports = router

类似于vue中的路由配置,

十二.controller处理

我们在controller/目录下分别创建UserController.js和StutController.js

const User = require('../models/User')
const {logInfo,logError} = require('../config/log')
//事务需要用
const Sequelize = require('../config/db')
const Result = require("../models/Result");
const HttpStatus = require("../models/HttpStatus");//导入类型约束module.exports = {//登录async login(req,res){try {//避免重复登录const sinId = req.cookies.sessionIDif(sinId !=null){//获取所有sessionsconst acticeSessions = req.sessionStore.sessions;//遍历for(let sID in acticeSessions){if(sID === sinId){//说明有sessionID,返回重复登录错误return res.status(200).json({message:'重复登录!'})}}}//都没有说明可以继续登录了const {username,password} = req.body;//假设if(username === 'lisi' && password === '123'){//服务端设置sessionreq.session.username = username;//设置cookieres.cookie('sessionID', req.sessionID, { httpOnly: true, maxAge: 3600000 });//   res.cookie('username', username, {//     expires: new Date(Date.now() + 1800000), // 设置 cookie 过期时间//     httpOnly: true, // 禁止客户端 javascript 访问 cookie//     secure: false, // 如果使用 HTTPS,请设置为 true// });//返回响应logInfo('用户登录:',{username})//res.status(200).json({message:'登录成功!'})res.status(200).json(Result.ok(HttpStatus.OK,username))}else{res.status(401).json({messge:'用户名或者密码错误'})}} catch (err) {logError('登录错误:',{err})res.status(500).json({msg:'服务器错误'+err})}},//查询所有用户async list(req,res){try {const users = await User.findAll()return res.json(users)} catch (error) {res.status(500).json({msg:'服务器错误'})}},/***  添加用户*/async addUser(req,res){const { username,password } = req.body;try {await Sequelize.transaction(async (t)=>{await User.create({username,password},{transaction:t})//记录日志logInfo('添加用户:',username,password)res.status(200).json({msg:'添加成功'})}).then(()=>{console.log("事务已经提交");})} catch (err) {logError('添加用户失败',{err})res.status(500).json({msg:'服务器错误'})}},/*** //退出登录*/async logout(req,rese){console.log("退出登录。。。。");try {//将session销毁var sessionID =  req.cookies.sessionID//获取所有sessionsconst acticeSessions = req.sessionStore.sessions;//遍历for(let sID in acticeSessions){if(sID == sessionID){//销毁sessionsessionStore.destroy(sID, (err) => {if (err) {console.error('Failed to destroy session:', err);res.status(500).json({message:err})} else {console.log('Session destroyed successfully.');//销毁cookiereq.clearCookie('sessionID')res.status(200).json({message:'退出成功'})}});}}} catch (error) {res.status(500).json({msg:'服务器错误'})}},//测试async test(req,res){try {console.log("测试....");res.json({test:"测试"})} catch (error) {res.status(500).json({msg:'服务器错误'})}}}

读者自行看上面的代码和注释思考登录和注销的流程

注意:我们在添加用户时开启了事务,当成功添加后提交事务

StuController.js


const Student = require('../models/Student')
const {logInfo,logError} = require('../config/log')
//事务需要用
const Sequelize = require('../config/db')module.exports = {//查询所有学生async list(req,res) {try {const stuList = await Student.findAll()//   console.log(stuList);res.status(200).json(stuList)} catch (error) {res.status(500).json({msg: '服务器错误'})}},//根据id查询学生async searchByid(req,res){const id = req.params.idconsole.log(id)console.log("hhhh")try{const stu = await  Student.findOne({where:{id:id}})if(stu){res.status(200).json(stu)}else {res.status(200).json({message:"无此用户"})}}catch (error){res.status(500).json({msg: '服务器错误'})}}
}

十二:静态页面:

下面看一下我们的静态页面:

public/目录下的index.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>
</head><div id="app"><div>账号:<input type="text" v-model="username"></div><div>密码:<input type="text" v-model="password"></div><button @click="login">登录</button></div><script src="js/vue.js"></script><script src="js/axios.js"></script><script>new Vue({el:"#app",data:{return(){username:""password:""}},methods:{//登录login(){axios.post('http://localhost:8080/user/login',{"username":this.username,"password":this.password}).then(res=>{if(res.data.code===200){alert("登录成功")location.href = './html/home.html'}})}}})</script><body></body>
</html>

public/html/下的home.html:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>系统</title><link href="../css/home.css" rel="stylesheet" type="text/css">
</head>
<body><div id="app"><h1>学生管理</h1><input type="text" v-model="id"><button @click="search">搜索</button><table><tr><th>学号</th><th>姓名</th><th>性别</th><th>班级</th><th>地址</th><th>电话</th><th>分数</th><th>操作</th></tr><tr v-for="(item,index) in students" :key="index" class="stu"><th>{{ item.stuid }}</th><th>{{item.stuname}}</th><th>{{item.sex}}</th><th>{{item.myClass}}</th><th>{{item.address}}</th><th>{{item.tel}}</th><th>{{item.sorce}}</th><th><a href="javascript:;" class="edit">编辑</a><a  href="javascript:;" class="del">删除</a></th></tr></table><div>hello</div></div><script src="../js/axios.js"></script><script src="../js/vue.js"></script><script src="../js/config.js"></script><script>new Vue({el:"#app",data(){return{students:[],id:''}},created(){this.getStuList()},methods:{//查询所有学生getStuList(){axios.get(url + '/stu/list').then(res=>{this.students = res.data;})},//根据id查询学生search(){axios.get(url+`/stu/byid/${this.id}`).then(res=>{this.students = res.data})}},watch:{students:function (newVal,oldVal){this.students =}}})</script></body>
</html>

下面我们运行一下项目:

可以看到我们项目成功启动了!

我们先访问一下home.html中的/user/list看看如果不登录会咋样:

 

可以看到为登录,下面我们先登录, 

这里返回的格式两个不一样因为我写的不完善,没有每一个都是三个那种Result格式的,请见谅!

可以看到,登录成功了,下面我们在访问一下/user/list接口:

 

可以看到成功查询到了学生列表!,

下面我们来新增一个学生:

 

可以看到添加成功了!

看一下控制台输出:

Executing (072a3295-6d06-424e-915d-8aa19e702c03): INSERT INTO `tb_user` (`id`,`username`,`password`) VALUES (DEFAULT,?,?);
2023-11-19 21:46:16 info: [ '添加用户:', 'jjh', '123' ]
Executing (072a3295-6d06-424e-915d-8aa19e702c03): COMMIT;
事务已经提交

 看到事务已经提交!

再看一下日志:

2023-11-19 21:44:01 info: [ '用户登录:', { username: 'lisi' } ]
2023-11-19 21:46:16 info: [ '添加用户:', 'jjh', '123' ]

可以看到日志已经成功记录了!

下面我们看一下前端页面:

可以看到成功登录!

如果我们继续访问该接口:

 

可以看到重复登录!!!

登录成功后页面会跳转到home.html:

 

可以看到学生成功的查询出来了!

十三:总结

 好了,以上就是nodejs的express框架的基本使用了,读者可以在此基础上实现根据id查询用户,分页查询,批量增加删除用户等功能,我们下期再见!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/149212.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pytest自动化测试框架:mark用法---测试用例分组执行

pytest中的mark&#xff1a; mark主要用于在测试用例/测试类中给用例打标记(只能使用已注册的标记名)&#xff0c;实现测试分组功能&#xff0c;并能和其它插件配合设置测试方法执行顺序等。 如下图&#xff0c;现在需要只执行红色部分的测试方法&#xff0c;其它方法不执行&am…

辅助笔记-Jupyter Notebook的安装和使用

辅助笔记-Jupyter Notebook的安装和使用 文章目录 辅助笔记-Jupyter Notebook的安装和使用1. 安装Anaconda2. conda更换清华源3. Jupter Notebooks 使用技巧 笔记主要参考B站视频“最易上手的Python环境配置——Jupyter Notebook使用精讲”。 Jupyter Notebook (此前被称为IPyt…

学习教授LLM逻辑推理11.19

学习教授LLM逻辑推理 摘要1 引言2前言2.1事件关系提取2.2 演绎推理 3 揭示逻辑推理中的LLMS3.1 LLM如何执行任务3.1.1数据源3.1.2实验装置3.1.3 分析 3.2 LLM如何执行抽象多跳推理&#xff1f;3.2.1数据来源3.2.2 实验装置。3.2.3 分析。 4 逻辑推理教学4.1 LLM的上下文学习4.2…

国外客户发开发信怎么发?写外贸邮件方法?

如何给国外客户发开发信&#xff1f;给老外发外贸开发信的技巧&#xff1f; 与国外客户保持联系并积极开展业务合作变得至关重要。一种有效的方式是通过发展信件&#xff0c;这可以帮助建立关系、传递信息和促成商机。蜂邮将介绍如何向国外客户发开发信&#xff0c;以确保您的…

金融市场数据至上:QuestDB 为您的数据提供最优解 | 开源日报 No.81

vlang/v Stars: 34.7k License: MIT V 是一个开源项目&#xff0c;它是一种简单、易于学习的编程语言。该项目具有以下核心优势和主要功能&#xff1a; 简洁性&#xff1a;可以在周末内掌握这门语言。快速编译&#xff1a;使用 Clang 后端约为 110k loc/s&#xff0c;本地和…

MR素数测试及 pycryptodome库下 已知MR伪素数以及强伪证 生成指定伪随机数生成器绕过素性检测

MR素数测试在密码学库中应用广泛&#xff0c;通常作为BSPW的一部分来进行素数测试&#xff0c;由于在其算法中&#xff0c;有随机数的使用&#xff08;选择一个随机的base&#xff09;&#xff0c;若一个MR伪素数 n n n&#xff0c;已知其在某一个强伪证 a a a&#xff08;随机…

Leetcode—3.无重复字符的最长子串【中等】

2023每日刷题&#xff08;三十二&#xff09; Leetcode—3.无重复字符的最长子串 实现代码 class Solution { public:int lengthOfLongestSubstring(string s) {unordered_set<char> smap;int maxlen 0;int left 0;for(int i 0; i < s.size(); i) {while(smap.fi…

【精选】JSP技术知识点大合集

JSP简介 JSP介绍 JSP(全称Java Server Pages)Java服务端页面技术&#xff0c;是JavaEE平台下的技术规范。它允许使用特定的标签在HTML网页中插入Java代码&#xff0c;实现动态页面处理&#xff0c;所以JSP就是HTML与Java代码的复合体。JSP技术可以快速的实现一个页面的开发&am…

C++大神之路——环境篇

序 在我还在做后端的时候&#xff0c;当时程序员圈里就有个梗很火&#xff0c;说的是当时几种常用编程语言的鄙视链&#xff1a;做C的鄙视做Java的&#xff0c;做Java的鄙视做C#的&#xff0c;而我很不幸&#xff0c;当时在鄙视链最底层。一开始只是当个笑话听听就算了&#x…

MIB 6.1810实验Xv6 and Unix utilities(3)pingpong

Mit6.S081-实验1-Xv6 and Unix utilities-pingpong问题_Isana_Yashiro的博客-CSDN博客 Write a user-level program that uses xv6 system calls to ping-pong a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to…

00后如何组织双十一大促看这一篇就够了! | 京东云技术团队

引言 大家好&#xff0c;我是王蒙恩&#xff0c;一名“整顿职场”的00后。作为一名去年刚刚加入京东的校招生&#xff0c;我有幸成为本次CDP平台的11.11备战负责人。虽然早在实习的时候就经历过大促&#xff0c;但是真正组织整个部门的备战还是很难忘的。于是提起笔&#xff0…

深信服AC流量管理技术

拓扑图 一.保证通道针对修仙部&#xff0c;访问网站&#xff0c;邮件&#xff0c;DNS&#xff0c;IM&#xff0c;办工 OA&#xff0c;微博论坛网上银行等常见应用保证带宽最低 50%&#xff0c;最高 100% 1. 先新建线路带宽 2.新增流量管理通道&#xff08;保证关键应用&#x…

PPT基础:表格

目录 表格基本功能底纹框线单元格表格美化 表格基本功能 底纹 所在位置&#xff1a;插入图表>>>表设计>>>底纹 底纹&#xff1a;相当于每个单元格的“颜色填充”如何用表格拆分图片 &#xff08;1&#xff09;生成一个表格>>>插入一张图片>>…

我的创作纪念日——365天

机缘 最开始我写博客没有什么特别的原因&#xff0c;主要是因为以下几点&#xff1a; 练习自己的语言组织能力 记录自己学习生活中学到的知识 主要还是想找一个好的保存 Markdown 笔记的平台。 最终我选择了 CSDN&#xff0c;一来是因为 CSDN 对 Markdown 语法的支持较为全面…

计算机视觉的应用17-利用CrowdCountNet模型解决人群数量计算问题(pytorch搭建模型)

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用17-利用CrowdCountNet模型解决人群数量计算问题(pytorch搭建模型)。本篇文章&#xff0c;我将向大家展示如何使用CrowdCountNet这个神奇的工具&#xff0c;以及它是如何利用深度学习技术来解决复杂…

Taro.navigateTo 使用URL传参数和目标页面参数获取

文章目录 1. Taro.navigateTo 简介2. 通过 URL 传递参数3. 目标页面参数获取4. 拓展与分析4.1 拓展4.2 URL参数的类型4.3 页面间通信 5. 总结 &#x1f389;欢迎来到Java学习路线专栏~Taro.navigateTo 使用URL传参数和目标页面参数获取 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x…

OpenAI前CEO萨姆·阿尔特曼可能重返CEO职位;用LoRA微调LLM的实用技巧

&#x1f989; AI新闻 &#x1f680; OpenAI前CEO萨姆阿尔特曼可能重返CEO职位 摘要&#xff1a;据报道&#xff0c;OpenAI前CEO萨姆阿尔特曼有望重新担任CEO职位&#xff0c;并对公司董事会进行重大改变。微软等投资人正努力恢复阿尔特曼的职位&#xff0c;尽管董事会仍然是…

Sql Server 2017主从配置之:发布订阅

使用发布订阅模式搭建Sql Server 2017主从同步&#xff0c;类似事件通知机制&#xff0c;基本可以做到准实时同步&#xff0c;可以同时做到一对多的数据同步。 不过发布订阅模式&#xff0c;只能同时数据&#xff0c;不能同步表结构。在创建发布的时候&#xff0c;需要选择需要…

亚马逊卖家参与活动:提升产品排名的神秘法宝?

对于许多亚马逊卖家来说&#xff0c;提升产品排名是一项持续且充满挑战的任务。有很多方法可以帮助提升产品在亚马逊平台上的排名&#xff0c;其中之一就是参与亚马逊的Woot活动。那么&#xff0c;通过Woot活动真的可以提升产品排名吗&#xff1f;让我们深入了解。 什么是Woot活…

数字IC前端学习笔记:时钟切换电路

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 有些时候我们需要在系统运行时切换系统时钟&#xff0c;最简单的方法就是使用一个MUX&#xff08;数据选择器&#xff09;选择输出的时钟&#xff0c;如下代码片所…