【Node.JS】koa

news2025/2/1 4:49:00

文章目录

  • 概述
  • koa和express对比
  • koa下载安装
  • 使用
    • 1.创建koa项目文件目录
    • 2. 创建koa服务
    • 3. 添加路由 koa-router
    • 4. 数据库服务 mongodb
    • 5. 添加请求参数json处理 koa-bodyparser
    • 6. 用户接口举例
    • 7.引入koa一些常用插件
    • 8.用户登录验证 koa-jwt
    • 9.webpack生产打包
  • 来源

概述

Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。

koa和express对比

  • Koa采用洋葱模型

通常都会说Koa是洋葱模型,这重点在于中间件的设计。但是按照上面的分析,会发现Express也是类似的,不同的是Express中间件机制使用了Callback 实现,这样如果出现异步则可能会使你在执行顺序上感到困惑,因此如果我们想做接口耗时统计、错误处理Koa的这种中间件模式处理起来更方便些。最后一点响应机制也很重要,
Koa不是立即响应,是整个中间件处理完成在最外层进行了响应,而Express则是立即响应。

  • Koa更轻量

koa不提供内置的中间件;
koa不提供路由,而是把路由这个库分离出来了(koa/router)

  • Context对象

koa增加了一个Context的对象,作为这次请求的上下文对象(在koa2中作为中间件的第一个参数传入)。同时Context上也挂载了Request和Response两个对象。Express类似, 这两个对象都提供了大量的便捷方法辅助开发这样的话对于在保存一些公有的参 数的话变得更加合情合理。

  • 异步流程控制

express采用callback来处理异步,koa采用async/await。
async/await使用同步的写法来处理异步,明显好于callback和promise,

  • 中间件模型

express基于connect中间件,线性模型;
koa中间件采用洋葱模型(对于每个中间件,在完成了-些事情后,可以非常优雅的将控制权传递给下一个中间件,并能够等待它完成,当后续的中间件完成处理后,控制权又回到了自己)
在这里插入图片描述
同步代码
同步方法没有什么区别:

  • 01-express-同步.js
const express = require("express")
const app = express()

app.use((req, res, next) => {
    console.log("111111")
    next()
    console.log("333333")
    res.send("hello world")
})

app.use((req, res, next) => {
    // 同步操作
    console.log("22222")
})

app.listen(3000)

运行输出

111111
22222
333333
  • 01-koa-同步 .js
const Koa = require("koa")
const app = new Koa()

app.use((ctx, next) => {
    console.log("111111")
    next()
    console.log("333333")
    ctx.body("hello world")
})

app.use((ctx, next) => {
    // 同步操作
    console.log("22222")
})

app.listen(3000)

运行输出:

111111
22222
333333

异步代码
next()表示可以执行下一个中间件,当下一个中间件执行完成之后,如果上一个中间件没有执行完,再返回上一个中间件继续执行。

  • 01-express-异步.js
const express = require("express")
const app = express()

app.use(async (req, res, next) => {
    console.log("111111")
    await next()
    console.log("444444")
    res.send("hello world")
})

app.use(async (req, res, next) => {

    console.log("22222")
     // 异步操作
    await delay(1000)
    console.log("33333")
})

function delay(time) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve,time)
    })
}

app.listen(3000)

运行输出:

111111
22222
444444
33333

由于next()返回的不是promise对象因此await不起作用,所以输出不会像我们所想输出

  • 01-koa-异步.js
const Koa = require("koa")
const app = new Koa()

app.use((ctx, next) => {
    console.log("111111")
    next()
    console.log("444444")
})

app.use((ctx, next) => {
    console.log("22222")
    // 异步操作
    delay(1000)
    console.log("33333")
})

function delay(time) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve,time)
    })
}
app.listen(3000)

运行输出:

111111
22222
33333
444444

koa洋葱模型,正常执行。

koa下载安装

npm init
npm i koa

Koa基本框架

const Koa = require("koa")

const app = new Koa()

// ctx=context 相当于res和req的合并
app.use((ctx, next) => {
    
})

app.listen(3000)

使用

1.创建koa项目文件目录

我们学习的第一步就是先搭好项目目录,这里会有我们项目中使用到的任何东西。

// 创建项目文件夹  也可手动创建
mkdir  koa-app

// 进入项目文件
cd  koa-app

// 添加koa依赖    
// 根据自己的包管理器执行自己的命令 我这里以yarn举例
yarn add koa -S

// 新建入口文件
echo >index.js

// 创建变量管理目录   const

mkdir const

// 创建数据库管理目录   database

mkdir database

// 创建中间件管理目录   middlewares

mkdir middlewares

// 创建路由管理目录   router

mkdir router

// 创建静态资源管理目录   static

mkdir static

// 创建工具类管理目录   utils

mkdir utils

// 创建html文件管理目录   view
// 主要用于测试自己接口
mkdir const

// 创建webpack打包文件

echo >webpack.config.js

这时候我们生成的目录结构大致如下

--koa-app
    --const
    --database
    --middlewares
    --router
    --static
    --utils
    --view
    -- index.js
    -- packjson.js
    -- webpack.config.js

2. 创建koa服务

这时候我们就可以创建koa服务,启动后就可以访问服务器目录了。

// index.js

const Koa = require("koa");

const app = new Koa();

app.use(async ctx => {
    ctx.body = "hellO  欢迎使用koa"
})

app.listen(3000);


// 启动koa服务

node  index.js

// 访问服务

在浏览器地址栏输入  localhost:3000

启动服务后我们打开浏览器就能看到"hellO 欢迎使用koa"说明我们koa程序运行成功。

3. 添加路由 koa-router

我们后台服务已经搭建好了,那下一步必不可少的就是路由管理了,这里我们使用koa-router插件

//  /router/index.js

// 引入koa-router
const  Router = require('koa-router');
// 引入user路由对象
const user = require('./user/index.js');
const view = require('./view/index.js')
const goods = require('./goods/index.js');
const category = require('./category/index.js');
const upload = require('./upload/index.js')
const rule = require('./rule/index.js')
const menu = require('./menu/index.js')
const role = require('./role/index.js')
const managerUser = require('./managerUser/index.js')
const attribute = require('./attribute/index.js')

// 生成新的router对象
let router = new Router();

// 添加路由管理
router.use('/api/user', user.routes())
router.use('/view', view.routes())
router.use('/api/goods', goods.routes())
router.use('/api/category', category.routes())
router.use('/api/upload', upload.routes())
router.use('/api/rule', rule.routes())
router.use('/api/menu', menu.routes())
router.use('/api/role', role.routes())
router.use('/api/managerUser', managerUser.routes())
router.use('/api/attribute', attribute.routes())

// 导出路由
module.exports = router

这里我是以自己写好的项目文件直接复制了,如果是测试的话不需要导入那么多路由对象,导入一个自己已经写好的就行了。

接下来我们就需要修改index.js文件与编写user路由

// /router/user/index.js

router.get('/list', async (ctx) => {
    ctx.body =  {
        code: 200,
        message: '访问成功'
    }
})


// index.js

const Koa = require("koa");
const router = require("./router/index.js"); // 路由
const app = new Koa();

// 添加路由中间件
app.use(router.routes()).use(router.allowedMethods());
app.use(async ctx => {
    ctx.body = "hellO  欢迎使用koa"
})

app.listen(3000);

这时候我们重新启动koa服务后,访问localhost/3000/api/user/list 就能获取ctx.body的内容了。 做到这里我们已经实现了自己的第一个接口了。剩下就是去数据库里面获取数据就形成了后台数据服务了。 是不是很棒呢!

4. 数据库服务 mongodb

这里因为学习的是mongodb数据库,所以例子都会是以mongodb数据库为例。其实用mysql的同学也可以自己去看一下mysql的引。

数据库的引入主要是做了2个步骤, 第一连接数据库,第二创建数据model对象,并执行数据库操作。 mongodb使用的是mdb语句去做的查询,mysql则是使用的sql语句。

当然每个数据库特性都不一样,在什么项目中使用什么数据库都需要在搭建项目目录的时候考虑到的,比如mysql, oracle 都是关系型的,在做一些数据关联性强的一些网站上更加适用比如电商,金融,证券,医疗等。 而非关系型的mongodb数据因为数据结构更加多变,适用与一些日记管理,博客,官网等

话不多说,我们来创建我们的数据库服务吧

添加依赖

// 添加依赖
yarn add glob mongoose  -S

创建mongosse文件

// 添加mongoose文件   /database/index.js
// 添加mongosse
const mongoose = require('mongoose')
// 数据库访问地址
const db = "mongodb://127.0.0.1/waimai"
// glob :提供匹配文件路径的方法,可以快速地找 到需要读取的文件
const glob  = require('glob');
const { resolve } = require('path')

// 初始化文档模式
exports.initSchemas = async () => {
    await  glob.sync(resolve(__dirname, './schema', './*.js')).forEach((v) => {
        require(v)
    })
}

exports.connect = () => {
    // 连接数据库
    mongoose.connect(db)

    return new Promise((resolve, reject) => {
        // 添加数据库断开监听事件
        mongoose.connection.on('disconnected', () => {
            console.log('数据库断开---------------')
            mongoose.connect(db)
        })

        // 添加数据库启动监听事件
        mongoose.connection.on('open', () => {
            console.log('数据库连接---------------1')
            mongoose.connect(db)

            resolve();
        })
    })
}

// index.js   引入mongoose文件

// moogose初始化
const { connect, initSchemas } = require("./database/index");

(async () => {
  await connect();
  await initSchemas();
})();

我们重启服务后就能连接到mongodb数据库了, 在conosle里面我们能看到 数据库连接字样

5. 添加请求参数json处理 koa-bodyparser

添加新依赖

yarn add koa-bodyparser -D

更新index.js

const bodyParser = require("koa-bodyparser"); // requeast请求

app.use(bodyParser());

6. 用户接口举例

添加新依赖

yarn add bcrypt -D

创建mongoose.model模型

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let ObjectId = Schema.Types.ObjectId;
// const bcrypt = require('bcrypt');

const SALT_WORK_FACTOR = 10;

// 前台用户表接口
const userSchema = new Schema({
    UserId: ObjectId,
    userName: {
        unique: true,
        type: String
    },
    passWord: String,
    avator: String,
    hashPassword: String,
    nikeName: String,
    address: String,
    isBlack: Boolean,
    sex: String,
    createAt: {
        type: Date,
        default: Date.now(),
    },
    lastLoginAt: {
        type: Date,
        default: Date.now(),
    },
})

// 每次存储时都要执行,加盐加密
userSchema.pre('save', function (next){
    bcrypt.genSalt(SALT_WORK_FACTOR,(err,salt)=>{
        if(err) return next(err)
        bcrypt.hash(this.passWord,salt,(err,hash)=>{
            if(err) return next(err)
            this.hashPassword = hash
            next()
        })
    })
})

// 添加自定义方法
userSchema.methods = {
    // 对比密码一致性
    comparePassword: (_password, hashPassword) => {
        return new Promise((resolve, reject) => {
            // 对比密码方法
            bcrypt.compare(_password, hashPassword, (err, isMatch) => {
                if(!err) resolve(isMatch);

                reject(err)
            })
        }) 
    }
}
// 发布模型
module.exports = mongoose.model('User', userSchema)

添加用户接口

const Router  = require('koa-router');
const mongoose = require('mongoose')
let router = new Router();
const User = require('../../database/schema/User')

// 用户注册
router.post('/register', async (ctx) => {
    const userName = ctx.request.body.userName;
    const passWord = ctx.request.body.passWord;

    let newUser = new User({
        userName,
        passWord,
    })
    .save()
    .then((res) => {
      ctx.body = {
        code: 200,
        message: "添加用户成功",
      };
    })
    .catch((err) => {
      ctx.body = {
        code: 500,
        message: "添加失败" + err,
      };
    });;
})

router.get('/list', async (ctx) => {
    // 引入user模型
    // const User  = mongoose.model('User');
    const uid  = ctx.request.query.uid || '';
    //  分页 page
    let page = ctx.request.body.page || 1;
    //  分页每页数量
    let limit = ctx.request.body.limit || 8;
    // 上一次获取位置
    const start =(page - 1)*limit;
    // console.log( userName, User, 'User')
     const result =  await User.find().exec()

     console.log(result, 'result')
     ctx.body = {
        code: 200,
        data: result.slice((page-1)*limit, page*limit),
        page: {
            page: page,
            limit,
            total: result.length ,
            lastPage: parseInt(result.length / limit)
        }
     }

    // ctx.body = ctx.request.body;
})

module.exports = router;

这时候我们就已经写好了用户添加接口与用户列表接口。因为用户的密码需要保密,我们在这里用了bcrypt去做了加盐加密,考虑到了bcrypt的加密是不可逆的所以我们这里用了passWord对原密码做了保存。

这里我们使用了schema的自定义方法与 schema的钩子函数

  • userSchema.methods

向由该 schema 编译的 model 构造的 document 添加一个实例方法.

  • userSchema.pre

给 schema 定义一个前置钩子 (pre hook)

7.引入koa一些常用插件

处理跨域问题 koa2-cors

yarn add koa2-cors -D


// index,js
const koa2cors = require("koa2-cors"); // 配置跨域

app.use(koa2cors({
    origin: "*",
    maxAge: 5,
    allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
}));

添加静态文件目录 koa-static

yarn add koa-static -D


// index.js

const koaStatic = require("koa-static"); // 静态目录
app.use(koaStatic("./"));

添加websokit服务 koa-websocket

yanr add  koa-websocket -S

// index.js

const websocket = require("koa-websocket"); // socket

// 这时候app需用被websoket包裹
const app = websocket(new Koa());


// 建立socket连接
app.ws.use(async (ctx) => {
  // the websocket is added to the context as `ctx.websocket`.
  ctx.websocket.send("我是服务器");
  ctx.websocket.on("message", function (message) {
    // do something
    const msg = message.toString("utf-8");
    console.log("客户端发来消息", msg);
  });
});

添加xss防御

yarn add xss -S

// index.js
const xss = require('./middlewares/xss.js') // xss

app.use(xss())


// /middlewares/xss.js

const xss = require("xss"); // 需要 npm install xss -S

const xssHandler = () => {
  return async (ctx, next) => {
    try {
      const body = ctx.request.body;
      for (const key in body) {
        if (typeof body[key] === "string") {
          body[key] = xss(body[key]);
        }
      }
      // 一定要添加await
      await next();
    } catch (error) {
      // console.error(error)
      throw error;
    }
  };
};

module.exports = xssHandler;

图片文件处理 koa-multer

yarn add koa-multer -D



//  /router/upload/index.js
const Router  = require('koa-router');
let router = new Router();
const multer = require('koa-multer');

//配置
const storage = multer.diskStorage({
    //配置图片上传的目录
    destination: function (req, file, cb) {
        console.log('destination')
      cb(null, 'static/images/'); //注意路径必须存在
    },
    //图片上传完成重命名
    filename: function (req, file, cb) {
        console.log('filename')
      // 获取后缀名
      var fileFormat = file.originalname.split('.');
      cb(null, Date.now() + '.' + fileFormat[fileFormat.length - 1]);
    },
});
const upload = multer({ storage: storage });
router.post('/img', upload.single('file'), async ctx => {
    console.log(ctx.req.file, 'ctx.req.file')
    ctx.body = {
        code: 200, 
        data: {
            filename: ctx.req.file.filename,//返回文件名 
            path: ctx.req.file.destination + ctx.req.file.filename
        }

    } 
})


module.exports = router;

请求参数验证 Joi

访问接口时会先校验参数是否传对,如果对继续后面的逻辑,如果参数校验不对则会直接返回错误信息给前端。

yarn add Joi -D


// /router/user/index.js

const Joi = require("joi");
const validateSchemaJoi = require("../../middlewares/validateSchemaJoi");
const userSchema = Joi.object({
    userName: Joi.string().min(1).required(),
});

// 用户注册
router.post('/register', validateSchemaJoi("post", userSchema), async (ctx) => {
    // 注册流程
})


// /middlewares/validateSchemaJoi
function validateSchemaJoi(method, schema) {
    async function validateSchema (ctx, next) {
      let data = undefined;
      if (method === 'get') {
        data = ctx.request.query;
      } else {
        data = ctx.request.body;
      }
      const { value, error } = schema.validate(data);
      if (error) {
        ctx.body = {
            code: 400,
            error
        };
      } else {
        next();
      }
    }
    return validateSchema;
  }

  module.exports =  validateSchemaJoi;

8.用户登录验证 koa-jwt

用户验证有3种方式

1。cookie 2. session 3. token

这里我们就以token来做用户验证。

添加依赖

yarn add koa-jwt jsonwebtoken -S

用户登录添加token返回

//  /router/user/index.js

var jwt = require('jsonwebtoken');


router.post('/login', async (ctx) => {
    const userName = ctx.request.body.userName;
    const passWord = ctx.request.body.passWord;

    // 查询用户是否存在
    await User.findOne({ userName: userName }).exec().then(async result => {
        // 如果用户名存在
        if(result) {
            let newUser = new User();
            // 校验用户密码
            await newUser.comparePassword(passWord, result.hashPassword).then(isMatch => {
                // 如果用户校验成功
                if(isMatch) {
                    // 生成token
                    const token = jwt.sign({userName: result.userName}, 'secret', { expiresIn: '2h' });
                    // 返回给前端
                    ctx.body = {
                        code: 200,
                        message: isMatch,
                        data: {
                            token: token,
                            uid: result._id
                        }
                    }
                }else {
                    ctx.body = {
                        code: 500,
                        message: isMatch
                    }
                }
            })
        }else {
            ctx.body = {
                code: 500,
                message: '用户名不存在!'
            }
        }
    }).catch(err => {
        // console.log('result----err')
        ctx.body = {
            code: 500,
            message: err,
        }
    })

})

添加token白名单 不拦截请求

const { jwtWhiteList } = require("./const/jwtWhiteList"); // token白名单

// /const/jwtWhiteList.js

const jwtWhiteList  = [
    /^\/api\/user\/login/,
    /^\/view/,
    /^\/static/,
    "/api/managerUser/login",
    "/api/goods/getGoodsDetailsInfo",
    "/api/upload/img"
]
module.exports = {
    jwtWhiteList
}

添加路由token验证拦截

token路由拦截主要做了以下这几件事

1.koa-jwt对每个请求头部信息进行token校验,如果用户校验失败就返回401,过滤掉白名单的请求。

2.在路由中间件上面添加中间件,当用户token失效后我们就会走401步骤 返回用户token失效信息,让前端去重定向到登录页

3.用户token都是有时效性的,当然时效性越短越好,因为没用数据库去存储token所以在项目重启后可能会有失效问题,没验证过。我这默认是2小时,当小于一半的失效时间时我就会生成新的token交予前端重新生成。也就是所谓的token续存机制。

// index.js

const jwt = require("koa-jwt"); // token验证
const jwtToken = require('jsonwebtoken');

// 路由拦截器中间件
app.use(function (ctx, next) {
  // console.log("ce0", ctx.header.authorization)
  if (ctx.header && ctx.header.authorization) {
    const parts = ctx.header.authorization.split(" ");
    if (parts.length === 2) {
      //取出token
      const scheme = parts[0];
      const token = parts[1];
      if (/^Bearer$/i.test(scheme)) {
        try {
          const decoded = jwtToken.verify(token, 'secret',{ complete: true });
          // iat: 签发时间  exp: 过期时间
          const { iat, exp, userName  } = decoded.payload;
          const nowTime = new Date().getTime()/1000;
          const lastTime  = (exp - nowTime)/60;
          // 当前事件离过期时间还剩一半的时候更新token 如果过期就走401
          if(decoded && 0 < lastTime &&  lastTime< ((exp-iat)/60)/2) {
            // console.log('更新token0')
            const newToken = jwtToken.sign({userName: userName}, 'secret', { expiresIn: '2h' });
            // console.log('更新token1', newToken)
            ctx.res.setHeader('Authorization', newToken)
          }

        } catch (error) {
          console.log("ce3")
          //token过期 
        }
      }
    }
  }

  return next().catch((err) => {
    if (401 == err.status || err.status === 301) {
      ctx.status = 401;
      ctx.body = {
        code: err.status,
        message: "token已经失效!!!!"
      };
      // ctx.body = {error: err.originalError ? err.originalError.message : err.message};
    } else {
      throw err;
    }
  });
});

// 添加token中间件
app.use(jwt({ secret: "secret" }).unless({ path: jwtWhiteList }));

9.webpack生产打包

这里就做了简单的js打包,打包后的文件体积会变小,因为webpack设置mode为生产环境后默认就做了许多处理。

// webpack.config.js
const webpack = require("webpack");
    const path = require("path");
    const { CleanWebpackPlugin } = require("clean-webpack-plugin");
    const nodeExternals = require("webpack-node-externals");
    // const MinifyPlugin = require('babel-minify-webpack-plugin');
    const CopyWebpackPlugin = require('copy-webpack-plugin')

    module.exports = {
      entry: "./index.js",
      mode: "production",
      output: {
        path: path.resolve(__dirname, "./dist"),
        filename: "[name].js",
      },
      target: "node",
      externals: [nodeExternals()], //node 打包可去除一些警告
      module: {
        rules: [
          {
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              {
                loader: "babel-loader",
                options: {
                  presets: ["@babel/preset-env"], //兼容es6,并添加.babelrc
                },
              },
            ],
          },
        ],
      },
      plugins: [
            // 清楚dist
        new CleanWebpackPlugin(),
            // js压缩
            // split切片
            // 复制静态目录
            new CopyWebpackPlugin({
                patterns: [
                    {
                        from: path.resolve(__dirname, './static'),
                        to: path.resolve(__dirname, './dist/static')
                    }
                ]
            })
        // new MinifyPlugin() //压缩js
      ],
    };

// packjson.js  添加启动指令

"build": "webpack --progress  --config webpack.config.js",
"prd_server": "node ./dist/main.js"

来源

你需要的koa入门教学
koa框架

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

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

相关文章

数据结构(六)——图

六、图 6.1 图的基本概念 图的定义 图&#xff1a;图G由顶点集V和边集E组成&#xff0c;记为G (V, E)&#xff0c;其中V(G)表示图G中顶点的有限非空集&#xff1b;E(G) 表示图G中顶点之间的关系&#xff08;边&#xff09;集合。若V {v1, v2, … , vn}&#xff0c;则用|V|…

Jenkins实现CICD

Jenkins实现CICD JenkinsCI简介环境安装新建任务源码管理构建配置发送邮件配置自动化项目定时构建 JenkinsCD简介配置ssh保证其可以免登录接下来配置github的webhook正式实现自动化打包master主分支的代码将前端三剑客代码文件发送到网站服务器对应的tomcat Jenkins面试题 Jenk…

微信小程序的页面制作---常用组件及其属性2

一、标签栏taBar 在全局配置文件app.json中添加taBar配置&#xff0c;可实现标签栏配置。标签栏最少2个&#xff0c;最多5个 &#xff08;1&#xff09;如何配置标签栏&#xff1f; 1》先建多个文件&#xff0c;&#xff08;以我的index&#xff0c;list&#xff0c;myform文…

民航电子数据库:CAEMigrator迁移数据库时总是卡死

目录 一、场景二、异常情况三、排查四、应急方案 一、场景 1、对接民航电子数据库 2、将mysql数据库迁移到cae数据库 3、使用CAEMigrator迁移工具进行数据库迁移时&#xff0c;该工具会卡死&#xff08;不清楚是否是部署cae服务的服务器资源导致&#xff09; 二、异常情况 …

【服务端】node.js详细的配置

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

《剑指 Offer》专项突破版 - 面试题 95、96 和 97 : 和动态规划相关的双序列问题(C++ 实现)

目录 前言 面试题 95 : 最长公共子序列 面试题 96 : 字符串交织 面试题 97 : 子序列的数目 前言 和单序列问题不同&#xff0c;双序列问题的输入有两个或更多的序列&#xff0c;通常是两个字符串或数组。由于输入是两个序列&#xff0c;因此状态转移方程通常有两个参数&am…

持续交付/持续部署流程主要系统构成(CD)

目录 一、概述 二、持续交付/持续部署主要构成 2.1 镜像容器管理系统 2.1.1 镜像分类 2.1.1.1 磁盘镜像 2.1.1.2 镜像容器 2.1.1.2.1 镜像容器分层管理示意图 2.1.2 镜像容器管理系统软件 2.2 配置管理系统 2.2.1 配置管理系统的功能 2.2.1.1 管理操作系统层、中间件…

goland annotate置灰点不动问题解决

goland 项目突然看不到左侧边栏提交记录&#xff0c;annotate按钮灰色不可点击&#xff0c;右键菜单也没有git&#xff0c;尝试各种方法终于解决。 原因是项目使用的非安全模式启动。 C:\Users\用户名\AppData\Roaming\JetBrains\GoLand2022.3\options 路径下的 trusted-path…

java分割回文串(力扣Leetcode131)

分割回文串 力扣原题链接 问题描述 给定一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是回文串。返回 s 所有可能的分割方案。 示例 示例 1: 输入&#xff1a;s “aab” 输出&#xff1a;[[“a”,“a”,“b”],[“aa”,“b”]] 示例 2: 输…

第十四届蓝桥杯省赛C++ B组所有题目以及题解(C++)【编程题均通过100%测试数据】

第一题《日期统计》【枚举】 【问题描述】 小蓝现在有一个长度为100的数组&#xff0c;数组中的每个元素的值都在0到9的范围之内。数组中的元素从左至右如下所示&#xff1a; 5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7 0 5 8 8 …

原生数据开发软件 TablePlus for mac

一款非常好用的本地原生数据开发软件&#xff1a;TablePlus激活版。 软件下载&#xff1a;TablePlus for mac v3.11.0激活版 这款优秀的数据库编辑工具支持 MySQL、SQL Server、PostgreSQL 等多种数据库&#xff0c;具备备份、恢复、云同步等功能。它可以帮助您轻松编辑数据库中…

KUKA机器人安全信号的接入方式

KUKA机器人的安全信号与IO模块是相互独立的&#xff0c;即安全信号不是通过IO信号接入到机器人里。安全信号主要是指:急停、安全门等属于机器人安全控制类的信号。 一、KUKA机器人安全信号的接入方式有以下3种&#xff1a; 1、第一种方式:Profisafe,以软件包的形式安装机器人…

2024最新网络编程 面试题解析

2024最新网络编程 面试题解析 三次握手和四次挥手 三次握手 三次握手是TCP/IP协议中用于建立可靠连接的过程。具体步骤如下&#xff1a; 第一次握手&#xff1a;客户端发送一个带有SYN标志的TCP报文段给服务器&#xff0c;请求建立连接&#xff0c;并进入SYN_SENT状态。 第…

Radash一款JavaScript最新的实用工具库,Lodash的平替!

文章目录 Lodash 的痛点进入正题--Radash特点 举例几个常用的api 一说lodash应该大部分前端同学都知道吧&#xff0c;陪伴我们好多年的JavaScript工具库&#xff0c;但是自从 ES6 出现后就慢慢退出前端人的视线&#xff0c;能ES6写的代码绝对不会用Lodash&#xff0c;也不是完全…

快速上手Spring Cloud 九:服务间通信与消息队列

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

回溯dfs和分支限界bfs

一&#xff1a;拓扑排序 207. 课程表 这道题说白了就是在有向图中找环 拓扑排序实际上应用的是贪心算法。 贪心算法简而言之&#xff1a;每一步最优&#xff0c;全局就最优。 每一次都从图中删除没有前驱的顶点&#xff0c;这里并不需要真正的删除操作&#xff0c;通过设置入度…

centos7配置阿里云的镜像站点作为软件包下载源

目录 1、备份 2、下载新的 CentOS-Base.repo 到 /etc/yum.repos.d/ 3、测试 阿里镜像提供的配置方法&#xff1a;centos镜像_centos下载地址_centos安装教程-阿里巴巴开源镜像站 1、备份 [rootlocalhost ~]# mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentO…

时序预测 | Matlab实现CPO-BP冠豪猪算法优化BP神经网络时间序列预测

时序预测 | Matlab实现CPO-BP冠豪猪算法优化BP神经网络时间序列预测 目录 时序预测 | Matlab实现CPO-BP冠豪猪算法优化BP神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现CPO-BP冠豪猪算法优化BP神经网络时间序列预测&#xff08;完整源码…

大电流电感的作用和特点

大电流电感又称为高功率电感&#xff0c;一般是指绕线型电感&#xff0c; 一、主要作用 1.在低频时&#xff0c;起蓄能和滤高频&#xff1b; 2.在高频时&#xff0c;它的阻抗特性表现的很明显。有耗能发热&#xff0c;感性效应降低等现象。 简单来说就是对交流信号进行隔离、…

Bun安装与使用

Bun安装与使用。 它目前无法在windows上直接安装使用&#xff0c;必须通过虚拟机安装。 在win10虚拟机中安装 # 查看内核版本 $ uname -srm Linux 6.1.0-10-amd64 x86_64# 安装unzip解压工具 $ sudo apt install unzip# 下载安装脚本并开始安装 curl -fsSL https://bun.sh/ins…