koa项目

news2024/11/12 11:18:37

一.koa起步

1.项目初始化

执行 npm init -y ,生成 package.json

npm init -y

2.安装koa

执行命令

npm install koa

image

3.编写基本app

创建 src/main.js

//1.导入koa包
const Koa = new require("Koa");

//2。实例化app对象
const app = new Koa();

//3.编写中间件
app.use((ctx, next) => {
    ctx.body = "hello,koa!"
})

//4.启动服务器
app.listen(3000,()=>{
    console.log("server is running on http://localhost:3000");
})

4.测试

在终端使用 node src/main.js

二.项目的基本优化

1.自动重启服务

安装 nodemon

npm install nodemon -D

编写 package.json 修改启动文件

"scripts": {
    "dev":"nodemon ./src/main.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

执行npm run dev启动项目

2.读取配置文件

安装 dotenv ,作用:读取根目录中的 .env 文件,将配置写入 process.env

npm install dotenv

创建 根目录/.env 文件

//.env
PORT = 9999

DB_HOST = localhost
DB_PORT = 3306
DB_DATABASE = quinhua
DB_USERNAME = root
DB_PASSWORD = 1234

创建 src/config/config.default.js 导出变量

const dotenv = require("dotenv");
dotenv.config();

//console.log(process.env.PORT);

module.exports = process.env;

执行命令 node src/config/config.default.js 可看到控制台输出 9999

改写 src/main.js

const Koa = new require("Koa");
const { PORT } = require("./config/config.default");

const app = new Koa();

app.use((ctx, next) => {
    ctx.body = "hello,koa!"
})

app.listen(PORT, () => {
    console.log(`server is running on http://localhost:${PORT}`);
})

3. 路径别名

安装 module-alias,为项目中路径设置别名

npm install module-alias

配置 package.json,与 scripts 同级

  "_moduleAliases": {
    //别名:文件夹
    "@": "src",
    "@config":"src/config"
  }

项目中使用

require('module-alias/register')//放置于主文件中所有代码前
const app = require("@/app");//使用别名y

并将项目中所有引入的路径全部替换

三.路由

路由:根据不同的URL,调用对应的处理函数

1.安装 koa-router

执行 npm install koa-router

2.编写路由

创建 src/router/user.route.js文件并写入

const Router = require("koa-router");

const router = new Router({ prefix: "/users" });

//GET /users/
router.get("/", (ctx, next) => {
    ctx.body = "hello,koa!";
});

module.exports = router

改写 src/main.js

const Koa = new require("Koa");
const { PORT } = require("@config/config.default");

const app = new Koa();

const userRouter = require("@router/user.route");

app.use(userRouter.routes());

app.listen(PORT, () => {
    console.log(`server is running on http://localhost:${PORT}`);
});

四. 目录结构优化

1.将 http 服务和 app 业务拆分

创建 src/app/index.js写入

const Koa = new require("Koa");
const app = new Koa();

const userRouter = require("@router/user.route");
app.use(userRouter.routes());

module.exports = app

改写 src/main.js写入

require('module-alias/register')
const app = require("@/app");

const { PORT } = require("@config/config.default");
app.listen(PORT, () => {
    console.log(`server is running on http://localhost:${PORT}`);
});

2. 拆分路由和控制器

路由:解析URL,分发给控制器对应的方法

控制器:处理不同的业务

创建 src/controller/user.controller.js 编写

class UserController {
    async register(ctx, next) {
        ctx.body = "用户注册成功"
    }
}

module.exports = new UserController()

改写 router/user.route.js

const Router = require("koa-router");
const router = new Router({ prefix: "/users" });

const {
    register
} = require("@controller/user.controller")

router.get("/register", register);

module.exports = router

五. 解析body

1.安装 koa-body

npm install koa-body

2.注册中间件

[注意]将 koa-body 注册在所有路由之前

改写 src/app/index.js

const Koa = require("Koa")
const { koaBody } = require("koa-body")

const userRouter = require("@router/user.route")
const app = new Koa()

app.use(koaBody())

app.use(userRouter.routes())

module.exports = app

3.解析请求数据

src/controller/user.controller.js 使用

class UserController {
    async register(ctx, next) {
        console.log(ctx.request.body)
        ctx.body = ctx.request.body
    }
}

module.exports = new UserController()

4.拆分数据层

service层用来做数据库处理

创建 src/service/user.service.js 写入

class UserService{
    async createUser(username,password){
        return "写入数据库成功"
    }
}

module.exports=new UserService()

src/controller/user.controller.js 使用

const { createUser } = require("@service/user.service")

class UserController {
    async register(ctx, next) {
        const { username, password } = ctx.request.body
        const res = await createUser(username, password)
        ctx.body = res
    }
}

module.exports = new UserController()

六.数据库操作

sequelize ORM 数据库工具

ORM: 对象关系映射

  • 数据表映射(对应)一个类
  • 数据表中的数据行(记录)对应一个对象
  • 数据表字段对应对象的属性
  • 数据表的操作对应对象的方法

1.安装

安装 sequlize以及 mysql2

npm install sequelize mysql2

2.连接数据库

创建 src/db/seq.js 写入

const { Sequelize, DataTypes } = require('sequelize');
const {
    DB_HOST,
    DB_PORT,
    DB_DATABASE,
    DB_USERNAME,
    DB_PASSWORD
} = require("../config/config.default")
//这里使用相对路径,使用别名访问不了变量

//设置timezone: '+08:00'变为北京时间
const seq = new Sequelize(DB_DATABASE, DB_USERNAME, DB_PASSWORD, {
    host: DB_HOST,
    dialect: "mysql",
    timezone: '+08:00'
});

//测试是否连接成功
// seq.authenticate().then((res) => {
//     console.log("数据库连接成功")
// }).catch((err) => { console.log(err) })

module.exports = seq

测试数据库是否连接成功

node src/db/seq.js

3.创建数据模型

创建 src/model/user.model.js 写入

//数据模型
const { DataTypes } = require('sequelize')
const seq = require("../db/seq");

//创建名为User的模型,sequelize会自动将User变为Users
//sequelize中id、createdAt(创建-时间戳)、updatedAt(更新-时间戳) 会被自动创建
//allowNull:false设置字段不为空
//unique:true设置字段唯一
//defaultValue设置默认值
//comment注释

const User = seq.define('User', {
    uuid: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4
    },
    username: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: true,
        comment: "用户名,唯一"
    },
    password: {
        type: DataTypes.CHAR(64),
        allowNull: false,
        comment: "密码"
    },
    is_admin: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: 0,
        comment: "是否为管理员,默认值不是管理员,1是0不是"
    }
});

// User.sync() - 如果表不存在,则创建该表(如果已经存在,则不执行任何操作)
// User.sync({ force: true }) - 将创建表,如果表已经存在,则将其首先删除
// User.sync({ alter: true }) - 这将检查数据库中表的当前状态(它具有哪些列,它们的数据类型等),
//然后在表中进行必要的更改以使其与模型匹配.

//User.sync({ force: true })//强制同步数据库(创建数据表),执行当前语句创库之后进行注释

module.exports = User

先将 //User.sync({ force: true }) 取消注释

执行 node src/model/user.model.js 进行创建数据库,可看到数据库输出创库建表语句

[注意] 执行创建数据库后需要将 User.sync({ force: true }) 进行注释掉,否则项目运行一次就创建一次

执行完毕后查看数据库 quinhua.users数据库创建成功

4. 注册接口

使用 User 数据模型注入注册业务中

改写 src/service/user.service.js

const User = require("@model/user.model.js")

class UserService {
    async createUser(username, password) {
        const res = await User.create({ username, password })
        return res.dataValues
    }
}

module.exports = new UserService()

控制层返回数据

改写 src/controller/user.controller.js

const { createUser } = require("@service/user.service")

class UserController {
    async register(ctx, next) {
        const { username, password } = ctx.request.body
        const res = await createUser(username, password)
        ctx.body = {
            code:0,
            message:"用户注册成功",
            result:{
                id:res.id,
                uuid:res.uuid,
                username:res.username
            }
        }
    }

    async login(ctx, next) {
        ctx.body = "用户登录成功"
    }
}

module.exports = new UserController()

5. 查找业务

改写 src/service/user.service.js

const User = require("@model/user.model.js")

class UserService {
    //注册用户
    async createUser(username, password) {
        const res = await User.create({ username, password })
        return res.dataValues
    }

    async getUserInfo({ id, uuid, username, password, is_admin }) {
        //短路算法
        const whereObj = {}
        id && Object.assign(whereObj, { id })
        uuid && Object.assign(whereObj, { uuid })
        username && Object.assign(whereObj, { username })
        password && Object.assign(whereObj, { password })
        is_admin && Object.assign(whereObj, { is_admin })

        const res = User.findOne({
            attributes: ["id", "uuid", "username", "password", "is_admin"],
            where: whereObj
        })
        return res ? res.dataValues : null
    }
}

module.exports = new UserService()

改写 src/controller/user.controller.js

const { createUser, getUserInfo } = require("@service/user.service")

class UserController {
    async register(ctx, next) {
        const { username, password } = ctx.request.body
        if (!username || !password) {
            console.error("用户名或密码为空", ctx.request.body)
            ctx.status = 400
            ctx.body = {
                code: 10001,
                message: "用户名或密码为空",
                result: ""
            }
            return
        }
        if (getUserInfo({ username })) {
            ctx.status = 409
            ctx.body = {
                code: 10002,
                message: "用户已经存在",
                result: ""
            }
            return
        }

        const res = await createUser(username, password)
        ctx.body = {
            code: 0,
            message: "用户注册成功",
            result: {
                id: res.id,
                uuid: res.uuid,
                username: res.username
            }
        }
    }

    async login(ctx, next) {
        ctx.body = "用户登录成功"
    }
}

module.exports = new UserController()

七.错误处理中间件

1.统一错误处理

  • 在出错的地方使用ctx.app.emit提交错误
  • 在 app 中通过app.on监听

编写统一的错误定义文件

创建 src/constant/err.type.js

module.exports = {
    userErrorNameOrPwdNull: {
        code: 10001,
        message: "用户名或密码为空",
        result: ""
    },
    userErrorAlreadHad: {
        code: 10002,
        message: "用户已经存在",
        result: ""
    },
    userErrorRegisterErr: {
        code: 10003,
        message: "用户注册错误",
        result: ""
    },
}

2.统一错误码

创建 src/app/errHandler.js

module.exports = (err, ctx) => {
    let status = 500
    switch (err.code) {
        case 10001:
            status = 400
            break
        case 1002:
            status = 409
            break
        default:
            status = 500
    }
    ctx.status = status
    ctx.body = err
}

改写 src/app/index.js

const Koa = require("Koa")
const { koaBody } = require("koa-body")

const userRouter = require("@router/user.route")
const app = new Koa()

app.use(koaBody())

app.use(userRouter.routes())

//统一的错误处理
const errHandler=require("./errHandler")
app.on("error",errHandler)

module.exports = app

3.错误处理函数

创建 src/middleware/user.middleware.js

const { getUserInfo } = require("../service/user.service")
const {
    userErrorNameOrPwdNull,
    userErrorAlreadHad,
    userErrorRegisterErr
} = require("../constant/err.type")

const userValidatorNameOrPwdNull = async (ctx, next) => {
    const { username, password } = ctx.request.body
    if (!username || !password) {
        console.error('用户名或密码为空', ctx.request.body)
        ctx.app.emit("error", userErrorNameOrPwdNull, ctx)
        return
    }
    await next()
}

const userValidatorAlreadHad = async (ctx, next) => {
    const { username } = ctx.request.body
    try {
        const res = await getUserInfo({ username })
        if (await getUserInfo({ username })) {
            console.error('用户名已经存在', { username })
            ctx.app.emit("error", userErrorAlreadHad, ctx)
            return
        }
    } catch (err) {
        console.error("获取用户信息错误", err)
        ctx.app.emit('error', userErrorRegisterErr, ctx)
        return
    }
    await next()
}

module.exports = {
    userValidatorNameOrPwdNull,
    userValidatorAlreadHad
}

4.错误处理的使用

src/router/user.route.js

const Router = require("koa-router")
const router = new Router({ prefix: "/users" })
const {
    userValidatorNameOrPwdNull, userValidatorAlreadHad
} = require("@middleware/user.middleware.js")
const {
    register, login
} = require("@controller/user.controller")

router.post("/register", userValidatorNameOrPwdNull, userValidatorAlreadHad, register)
router.post("/login", login)

module.exports = router

八.加密

数据加密:在将密码保存到数据库之前, 要对密码进行加密处理

加盐加密

npm install bcryptjs

编写 utils/util/bcrypt.js

const bcrypt = require('bcryptjs')

const hashSync = (data) => {
    const salt=bcrypt.genSaltSync(10)
    const result=bcrypt.hashSync(data, salt);
    return result
};

const compare = (data, hashdata) => {
    const result = bcrypt.compareSync(data, hashdata)
    return result
}

module.exports = {
    hashSync,
    compare
}

src/middleware/user.middleware.js 使用

const { getUserInfo } = require("../service/user.service")
const { hashSync } = require("../utils/util/bcrypt")
const {
    userErrNameOrPwdNull,
    userErrAlreadHad,
    userErrRegisterErr
} = require("../constant/err.type")

//...
const userVerifyHashSync = async (ctx, next) => {
    const { password } = ctx.request.body
    ctx.request.body.password = hashSync(password)
    await next()
}
module.exports = {
    userVerifyNameOrPwdNull,
    userVerifyAlreadHad,
    userVerifyHashSync
}

九.登录验证

改写 src/constant/err.type.js

module.exports = {
    userErrNameOrPwdNull: {
        code: 10001,
        message: "用户名或密码为空",
        result: ""
    },
    userErrAlreadHad: {
        code: 10002,
        message: "用户已经存在",
        result: ""
    },
    userErrRegisterErr: {
        code: 10003,
        message: "用户注册错误",
        result: ""
    },
    userErrNoHad:{
        code: 10004,
        message: "不存在当前用户",
        result: ""
    },
    userErrLoginErr:{
        code: 10005,
        message: "用户登录失败",
        result: ""
    },
    userErrPwdErr:{
        code: 10005,
        message: "用户名或密码错误",
        result: ""
    }
}

src/middleware/user.middleware.js 验证用户

const { getUserInfo } = require("@service/user.service")
const { hashSync,compareSync } = require("@utils/util/bcrypt")
const {
    userErrNameOrPwdNull,
    userErrAlreadHad,
    userErrRegisterErr,
    userErrNoHad,
    userErrLoginErr,
    userErrPwdErr
} = require("../constant/err.type")

//...
const userVerifyHashSync = async (ctx, next) => {
    const { password } = ctx.request.body
    ctx.request.body.password = hashSync(password)
    await next()
}

const userVerifyLogin = async (ctx, next) => {
    const { username, password } = ctx.request.body
    try {
        const res = await getUserInfo({ username })
        if (!res) {
            console.error('用户名不存在', { username })
            ctx.app.emit('error', userErrNoHad, ctx)
            return
        }

        if (!compareSync(password, res.password)) {
            ctx.app.emit('error', userErrPwdErr, ctx)
            return
        }
    } catch (err) {
        console.error(err)
        return ctx.app.emit('error', userErrLoginErr, ctx)
    }

    await next()
}

module.exports = {
    userVerifyNameOrPwdNull,
    userVerifyAlreadHad,
    userVerifyHashSync,
    userVerifyLogin
}

十.用户认证

登录成功后, 给用户颁发一个令牌 token, 用户在以后的每一次请求中携带这个令牌.

jwt: jsonwebtoken

  • header: 头部
  • payload: 载荷
  • signature: 签名
npm install jsonwebtoken

创建 src/utils/util/jwt.js

const jwt = require('jsonwebtoken');
const config = require('../config');

//颁发token
const tokenSign = (data) => {//config.token.jwtExpiresTime
    const result = jwt.sign(data, config.token.jwtSecret, { expiresIn: config.token.jwtExpiresTime })
    return result
}

//验证token
const tokenVerify = (data) => {
    const result = jwt.verify(data, config.token.jwtSecret)
    console.log(result)
    return result
}

module.exports = {
    tokenSign,
    tokenVerify
}

1.登录颁发token

src/controller/user.controller.js 使用

//登录接口   
async login(ctx, next) {
        const { username, password } = ctx.request.body
        try {
            // 从返回结果对象中剔除password属性
            const { password, ...res } = await getUserInfo({ username })

            ctx.body = {
                code: 0,
                message: '用户登录成功',
                result: {
                    token: tokenCarry(res),
                },
            }
        } catch (err) {
            console.error('用户登录失败', err)
        }
    }

2.验证token

改写 src/constant/err.type.js

    tokenExpiredError:{
        code: 10101,
        message: "token已过期",
        result: ""
    },
    invalidToken:{
        code: 10102,
        message: "无效的token",
        result: ""
    }

创建 src/middleware/auth.middleware.js

	const { tokenVerify } = require("@utils/util/jwt")
const {
    tokenExpiredError,
    invalidToken
} = require("@constant/err.type")

const userAuthToken = async (ctx, next) => {
    const { authorization } = ctx.request.header
    const token = authorization.replace('Bearer ', '')
    try {
        ctx.state.user = tokenVerify(token)
    } catch (err) {
        switch (err.name) {
            case 'TokenExpiredError':
                console.error('token已过期', err)
                return ctx.app.emit('error', tokenExpiredError, ctx)
            case 'JsonWebTokenError':
                console.error('无效的token', err)
                return ctx.app.emit('error', invalidToken, ctx)
        }
    }
    await next()
}

module.exports = {
    userAuthToken
}

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

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

相关文章

基于C#+Mysql实现(WinForm)企业的设备管理系统【100010018】

企业的设备管理系统 1 引言 企业的设备管理在企业的生产制造和管理过程之中意义比较重大,明确企业的设备的产权和维护成本对于企业的成本控制和财务管理之中起到了重要的作用。随着市场竞争的加剧,现代企业所处的市场环境发生了深刻的变革,…

JDK19都出来了~是时候梳理清楚JDK的各个版本的特性了【JDK13特性讲解】

JDK各个版本特性讲解-JDK13特性 一、JAVA13概述 2019年9月17日,国际知名的OpenJDK开源社区发布了Java编程语言环境的最新版本OpenJDK13。 Features:总共有5个新的JEP(JDK Enhancement Proposals): http://openjdk.java.net/projects/jdk/13/ Features: …

java基于springboot的人事管理系统-计算机毕业设计

开发环境 开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven 项目介绍 在这个计…

m基于GA遗传优化的三维工程施工设施布局算法matlab仿真,显示二维和三维布局优化效果

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 GA把问题的解表示成“染色体”,在算法中也即是以二进制编码的串。并且,在执行遗传算法之前,给出一群“染色体”,也即是假设解。然后,把…

Matplotlib学习笔记(第二章 2.13 Matplotlib中的图形(三))

图例(Legends) legend()函数,使用MATLAB兼容的图例,放置函数自动生成图形图例。 感谢查尔斯特沃迪对图例功能的投入。 Fig. 19: Legend 文本对象的Tex表示法(TeX-notation for text objects) 下面是Matplotlib内部的mathtext工程支持的许多Tex表达式…

基于C#+SQLServer 2005实现(WinForm)校园卡消费信息系统【100010013】

校园卡消费信息管理系统 一、前言 1.1 选题说明 校园卡消费信息系统是一个实用并且与我们的学校生活密切相关的管理信息系统;如果能够很好的研究、开发并加以利用,校园卡的相关业务会变得更加简单、学生能更便利地进行消费同时准确了解自己的消费情况…

信号包络提取

目录 一、信号包络提取的相关应用: 二、信号包络提取方法 1、希尔伯特变换-Hilbert Transform 1.1 公式原理 1.2 例子说明 2、平方能量包络提取 3、香农能量包络提取 三、3种方法的对比 一、信号包络提取的相关应用: 1)当某一个机械部…

多副本自动化发布——standalone下

一: supervisor 具体这玩意是干嘛的,我就不说了,大家自己看官网: http://www.supervisord.org/ 接下来快速部署一下。 1. pip pip是python的一个包管理器,类似于nuget,如果你的centos上没有安装,那么请执行下面命令。 1 yum -y install epel-release 2 yum -y inst…

经矩形窗截断的信号频谱泄露现象研究-附Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、频谱泄露现象✳️ 二、原因分析以及解决方法✳️ 三、Matlab程序获取与验证✳️ 一、频谱泄露现象 有一个余弦信号,信号频率30Hz,信号为x(t)cos(2π30t),采样频率fs128Hz,样本长度分别取N128和N100&…

代码随想录算法训练营第四天| 24. 两两交换链表中的节点 ,19.删除链表的倒数第N个节点 ,面试题 02.07. 链表相交 ,142.环形链表II

代码随想录算法训练营第四天| 24. 两两交换链表中的节点 ,19.删除链表的倒数第N个节点 ,面试题 02.07. 链表相交 ,142.环形链表II 24. 两两交换链表中的节点 用虚拟头结点,这样会方便很多。 本题链表操作就比较复杂了&#xff…

MicroPython-On-ESP8266——8x8LED点阵模块(4)基于MAX7219滚动显示字符/图案

MicroPython-On-ESP8266——8x8LED点阵模块(4)基于MAX7219滚动显示字符/图案 1. 继续折腾点阵模块 咱们已经学习了点阵屏基础电路与驱动原理,并用74HC595和MAX7219都成功地驱动点阵屏显示了爱心图案。 MicroPython-On-ESP8266——8x8LED点…

python-(6-5-2)爬虫---处理cookie来获取书架数据

文章目录一 需求二 流程分析1 登录2 获取书架的数据三 完整代码一 需求 通过处理cookie来访问自己的书架资源。 二 流程分析 带着cookie,去请求url,得到书架内容。 要将上述的两个操作连续起来,可以使用session。 session是一连串的请求…

自动驾驶之多任务方法调研

1. YOLOP github C TRT TX2 我们提出了一种高效的多任务网络,该网络可以联合处理自动驾驶中的目标检测(车,没有红绿灯)、可驾驶区域分割和车道检测三个关键任务 速度: TX2上23FPS;TAITAN XP上41FPS. 自测结果: 灵活性: 支持…

RNN LSTM GRU

GRU是LSTM的简化结构,而LSTM是RNN的优化结构。 1.RNN RNN对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息. 将网络的输出保存在一个记忆单元中,这个记忆单元的输出经过权重参数调整后和下一次的输入一起进入神经网络中…

队列之王: Disruptor 原理、架构、源码 一文穿透

文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》 持续更新 史上最全 面试必备 2000页 面试必备 大厂必备 涨薪必备 免费赠送 经典…

MySQL innodb引擎架构分析-Redo log

文章目录系列文章目录前言一、Redo log是什么?二、Redo log是怎么工作的Redo log的组成Redo log的配置Redo log何时刷盘总结系列文章目录 1. MySQL innodb引擎架构分析-Buffer Pool 2. MySQL innodb引擎架构分析-Redo log 前言 在MySQL的事物处理过程中&#xff0c…

12 款 yyds 的 IDEA插件,配上18条使用技巧,绝了

工欲善其事,必先利其器。想要提升编程开发效率,必须选择一款顺手的开发工具。 JetBrains 公司提供了一系列功能强大、风格统一的开发工具,深受开发者喜爱。其中,IDEA 是面向 Java 开发的专业 IDE(集成开发环境&#x…

python os.system调用别的系统程序总出现一闪而过的黑框解决办法

python os.system调用别的系统程序总出现一闪而过的黑框解决办法 今天打包了py程序,运行的时候发现老是有个黑框一闪而过。经过多次尝试发现是os.system的问题,调用这个函数会调用cmd,所有会出现短暂的黑框显示。 解决办法1:换用…

HarmonyOS跨端迁移开发代码演示

目录说些废话开源代码环境代码ability_main.xmlconfig.jsonMainAbility.javaMainAbilitySlice.java测试说些废话 官方文档:跨端迁移开发指导(基于java开发)     下面环境里写的两台真机我测试的时候无法正常产生回迁效果,所以…

以太网 DHCP(DHCP的8种报文、DHCP配置、DHCP中继)

2.13.1 以太网 DHCP(DHCP的8种报文、DHCP配置、DHCP中继) DHCP-22.13.1 以太网 DHCP(DHCP的8种报文、DHCP配置、DHCP中继)一、DHCP的8种报文二、DHCP配置接口配置:全局配置:三、DHCP中继配置案例&#xff1…