【Koa】KOA 基础-掌握基于koa2搭建web应用的基础

news2024/12/28 19:39:31

目录

  • KOA 基础
  • 框架介绍与环境搭建
    • koa2 基本介绍
    • Node.JS 环境安装
    • 创建 Hello World 程序
  • Web 应用开发基础
    • 处理get请求参数
    • 处理post请求参数
    • 响应一个页面
    • 处理静态资源
  • 中间件
    • 基本概念和执行过程
      • 中间件概念理解
      • Koa 中间件执行模型-洋葱圈模型
      • Koa洋葱圈设计理解
    • 用 koa-body 处理 POST 传参
    • 用 koa-views 和 EJS 渲染页面
    • 用 koa-static 处理静态资源
    • 用 koa-body 处理文件上传
    • 用 koa-router 实现后端路由

KOA 基础

课程目标:掌握基于koa2搭建web应用的基础


框架介绍与环境搭建

koa2 基本介绍

本章目标: 认识 KOA

Koa 和 Express 一样,都是基于 node.js 的 Web 应用开发框架。

Koa 2 是 Koa 的 2.0 版本。

image-20210526115054161

koa:下一代的 node.js Web 框架!

  • 由 Express 框架原班人马设计和开发
  • 比 Express 更有表现力、更健壮(异步处理支持 async / await,所以可以用 try / catch 来更好的处理异常)
  • 框架核心默认不带有任何中间件,更精简、更快速(而 Express 默认自带了很多的中间件)
  • 更强大的中间件模型(洋葱圈模型)

典型应用场景:

  • 开发 REST API
  • 开发 WebSocket API
  • 开发服务端渲染的页面

本章问题:

  1. koa 框架可以用来做什么?
  2. koa 框架默认自带中间件吗?

Node.JS 环境安装

本章目标: 完成NodeJS环境安装

开发和运行 koa 必须依赖 node.js,因此需要先安装它

koa 2 支持 async 函数,但是 node.js 7.6 以后才原生支持 async, NodeJS 7.6+

目前 node.js 稳定版已经是 14+ 了,因此大家安装最新的稳定版即可。

node.js 稳定版下载地址

安装步骤

一路下一步即可。推荐不要安装在包含中文的路径下。

安装验证

打开命令行,执行以下命令

node -v

# 如能看到输出一个版本号,如 v14.17.0 即代表安装成功

创建 Hello World 程序

本章目标: 使用 koa 框架编写一个最简单的 API,访问时页面响应 hello world

步骤:

  1. 创建新的工程目录,比如:hello 目录

  2. 安装依赖包

npm i koa
  1. 创建代码文件,比如:app.js 文件
// 1. 引入 koa
const Koa = require('koa')

// 2. 创建 koa 实例
const app = new Koa()

// 3. 创建一个中间件,所有的请求都会执行到这个中间件进行处理
//   Koa 提供一个 Context 对象,表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。
//   通过加工这个对象,就可以控制返回给用户的内容。
app.use(async ctx => {
  // 为方便起见许多上下文的访问器和方法直接委托给它们的 ctx.request 或 ctx.response
  // 例如: ctx.body => ctx.response.body
    
  // 设置响应体  ctx.body = 'xxx'
  ctx.response.body = 'Hello,World'
})

// 4. 启动 koa 实例所关联的 http 服务器,并监听在 3000 端口上向外提供服务
app.listen(3000, () => {
  // http 服务器启动成功后执行本回调函数
  console.log('请访问 http://localhost:3000')
})

  1. 在命令行执行
node server.js
  1. 在浏览器访问 http://localhost:3000

Web 应用开发基础

处理get请求参数

**本章目标: 学习 koa中 如何处理 get 请求参数 **

在中间件中获取 GET 请求发送的查询字符串参数

中间件函数的第一个参数 ctx 中包含了请求和响应相关的操作,我们可以通过它的 query 属性来获取请求的查询字符串参数:

app.use(async ctx => {
  // 方式一:获取对象形式的查询字符串参数
  console.log("查询字符串参数(对象形式)", ctx.request.query)   				
  
  // 方式二:获取原始字符串形式的查询字符串参数
  console.log("查询字符串参数(字符串形式)", ctx.request.querystring)   
  
  // ...
})

处理post请求参数

本节目标: 学会处理 POST 请求参数

在中间件中获取 POST 请求通过请求体发送的参数数据

获取 POST 请求发送的请求体参数,相对较为繁琐,大致思路为:

  1. 监听 node.js 原生请求对象的 data 事件,从请求体中获取数据,并将所有数据拼合起来(因为如果请求体中携带的数据量较大,就会分几次来触发 data 事件,逐步获取)
  2. 监听 node.js 原生请求对象的 end事件,获知请求体数据被完全获取

代码示例:

// ...

app.use(async ctx => {
  let paramStr = ''

  // 1. 监听 node.js 原生 Request 对象的 data 事件,获取请求体数据
  ctx.req.on('data', (data) => {
    // 从请求体中获取数据,并拼接成一整个字符串
    paramStr += data
  })

  // 2. 监听 node.js 原生 Request 对象的 end 事件,结束请求体数据获取
  ctx.req.on('end', () => {
    // paramStr 是查询字符串格式的数据 可以用 new URLSearchParams 解析
    // 语法: var URLSearchParams = new URLSearchParams(init);
    const params = new URLSearchParams(paramStr)
    
    console.log('请求体参数(字符串形式)', paramStr);
    console.log('请求体参数(对象形式)', params);
  })

	// ...
})

URLSearchParams 语法说明:

const params = new URLSearchParams('k=%E5%85%B3%E9%94%AE%E5%AD%97&p=1');
console.log(params.get('k'));   // 返回字符串“关键字”,支持自动 UTF-8 解码
console.log(params.get('p'));   // 返回字符串“1”
console.log(params.get('xxx')); // 如果没有 xxx 这个键,则返回 null
console.log(params.has('xxx')); // 当然也可以通过 has() 方法查询是否存在指定的键
console.log(params.keys());     // 返回一个 ES6 Iterator,内含 ['k', 'p']
console.log(params.values());   // 返一个 ES6 Iterator,内含 ['关键字', '1']

MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams/URLSearchParams

知乎: https://zhuanlan.zhihu.com/p/29581070


响应一个页面

本节目标: 收到请求后,给客户端响应一个 HTML 内容

方式一:直接响应一个 HTML 字符串给客户端

app.use(async ctx => {
  // 为响应体设置 HTML 字符串内容
  ctx.body = `
    <!DOCTYPE html>
    <html lang="en">

    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    	<title>Document</title>
    </head>

    <body>
    	<h1>你好,世界</h1>
    </body>

    </html>
	`
})

方式二:读取 html 文件后,将读取到的 HTML 字符串响应给客户端

<!-- 编写 index.html 文件 -->

<!DOCTYPE html>
<html lang="en">

  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>

  <body>
    <h1>你好,世界</h1>
  </body>

</html>
// 导入文件读取模块
const fs = require('fs')

// 读取 html 文件的工具函数
function getHtmlFile(filePath) {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, (err, data) => {
      if (err) {
        reject(err)
      } else {
	    // 读取的文件数据是 Buffer 形式,要用 toString() 转成字符串
        resolve(data.toString())
      }
    })
  })
}

app.use(async ctx => {
  // 读取 index.html 文件后,设置到响应体
  ctx.body = await getHtmlFile('./index.html')
})

处理静态资源

本节目标: 收到请求后,给客户端响应静态资源内容,比如图片

处理静态资源的思路,和之前响应 html 文件给客户端是类似的,也是先读取文件,再设置给响应体。

以下是响应一张图片的实例:

  1. 先在项目目录中建立一个 static 目录,并放置一张图片文件(本例中是 01.jpg)
  2. 编写下面的代码,实现读取图片并响应给客户端的操作
// 导入文件读取模块
const fs = require('fs')

// 获取静态资源文件的
function getImageFile(filePath) {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, (err, data) => {
      if (err) {
        reject(err)
      } else {
        // 这里保持 Buffer 格式数据,因为图片是二进制数据,不要转成字符串
        resolve(data)
      }
    })
  })
}

app.use(async ctx => {
  // 【重要】正确设置静态资源的 Content-Type 响应头,否则在浏览器中只会下载文件,不能查看到图片
  ctx.set('Content-Type', 'image/jpeg')

  // 在响应体中设置读取到的图片文件数据
  ctx.body = await getImageFile('./static/01.jpg')
})

中间件

基本概念和执行过程

本节目标: 了解什么是中间件,以及它的作用

基本概念

中间件,其实就是一系列用来 对请求进行处理函数

中间件概念理解

为了理解中间件,我们先来看一下我们现实生活中的例子
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

处理污水的这三个中间处理环节,就可以叫做中间件。

Koa 中间件执行模型-洋葱圈模型

本节目标: 理解Koa中间件执行模型 - 洋葱圈模型

在 Koa 中可以为 Koa 实例添加多个中间件,让一个请求可以经过多个中间件函数的处理。

中间件函数的格式为:

function someMiddleware(ctx, next) {
  // ... 对请求进行处理的逻辑 ...
  
}

每个中间件函数都有两个参数:

  • ctx - 请求上下文对象,它包含和请求和响应相关的数据和操作
  • next - 是一个函数,调用后会执行下一个中间件

创建好中间件函数后,可以将它添加到 Koa 实例上:

app.use(someMiddleware)

Koa 中间件的执行模型

6383319-09c1061cf194e0b8

上面为 Koa 框架的中间件模型示意图,看上去像个洋葱,所以我们称它为《洋葱圈模型》。

它的含义就是说:

Koa 中间件的执行就像洋葱一样,最早被 use 的中间件会放在最外层,而后续被 use 的中间件会往里层放;

当接收到一个请求的时候,处理顺序是:

1. 从左到右从洋葱的最外层到最里层,也就是从最早 use 的中间件到最后 use 的中间件依次执行;
2. 在到达最里层中间件后,会继续向右逐层往外执行,直到最外层中间件,然后返回 response

示例代码:

app.use(async (ctx, next) => {
    console.log('>>>>>>>> A 111')
    await next()
    console.log('>>>>>>>> A 222')
})

app.use(async (ctx, next) => {
    console.log('>>>>>>>> B 111')
    await next()
    console.log('>>>>>>>> B 222')
})

当接收请求后,我们可以看到控制台打印如下结果:

>>>>>>>> A 111
>>>>>>>> B 111
>>>>>>>> B 222
>>>>>>>> A 222

由此可知,一般情况下 Koa 的中间件都会执行两次:

  • 调用 next 之前为第一次。在调用 next 后,会把控制权传递给往里层的下一个中间件
  • 当里层不再有任何中间件、或未调用 next 函数时,就开始依次往外层中间件执行,执行的是外层中间件中调用 next 函数之后的代码

本章问题

  1. 中间件函数, 有哪两个形参?
  2. Koa 中中间件执行的模型叫什么?

Koa洋葱圈设计理解

6383319-09c1061cf194e0b8

两个现象:

  • 中间件的执行了两次
  • 执行顺序奇怪,以next函数为分界点:先use的中间件,next前的逻辑先执行,但next后的逻辑反而后执行

思考: 为什么 Koa 要这么设计? 正常不应该是中间件按顺序从开始到结束执行吗?

说明:

  • 如果说使用中间件的场景, 不存在前后依赖的情况,从头到尾按顺序链式调用 => 完全没问题。

  • 但是, 如何存在依赖的情况呢? 比如: 前一个中间件部分代码, 依赖于下一个中间件的处理结果?

    链式一次执行就无法实现了!

结论:

  • next 前的逻辑 : 进行前期处理
  • 调用next,将控制流交给下个中间件,并await其完成,直到后面没有中间件或者没有next函数执行为止
  • 完成后一层层回溯执行各个中间件的后期处理(next 后的逻辑)

用 koa-body 处理 POST 传参

本节目标: 使用 koa-body 中间件 处理 POST 传参

我们自己来处理获取 POST 请求的参数比较繁琐,实际开发中可以使用封装好的开源中间件

我们可以在 Koa 官方wiki 上找到很多优秀的开源中间件。而 koa-body 是一个专门用于获取通过请求体传递的数据的中间件。

使用步骤:

  1. 安装依赖包
npm i koa-body
  1. 引入并使用 koa-body
// 引入 koa-body
const koaBody = require('koa-body')

// ...

// 为 Koa 实例设置 koa-body 中间件
app.use(koaBody())

app.use(async ctx => {
  // 通过 ctx.request.body 获取请求体参数
  console.log('请求体参数', ctx.request.body)
  ctx.body = 'Hello'
})

用 koa-views 和 EJS 渲染页面

本节目标: 在 Koa 中使用模板引擎 EJS 来进行页面的动态渲染

在 Koa 中,我们也可以使用模板引擎,通过模板语法将数据动态渲染成html页面内容,然后发送到客户端去。

koa-views 是一个支持使用多种模板引擎来渲染页面的中间件,在本章中我们要使用 EJS 模板。

使用步骤:

  1. 安装 koa-views 中间件 和 ejs 模板引擎
npm i koa-views ejs
  1. 引入并使用中间件
// 引入 koa-views
var views = require('koa-views');

// 配置和应用 koa-views 中间件(这里配置使用了 ejs 模板引擎,以及模板文件的存放目录)
app.use(views(__dirname + '/views', { extension: 'ejs' }))

// 引入 koa-views 后,就可以使用 ctx.render 函数渲染 ejs 模板文件了
app.use(async ctx => {
  // render 函数的第一个参数是模板文件名;第二个参数是要渲染到模板中的动态数据
  await ctx.render('test', {
    name: '小明',
    age: 18,
    books: ['三国演义', '红楼梦', '西游记', '水浒传']
  })
})

模板文件 views/test.ejs

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>姓名:<%= name %></div>
    <div>年龄:<%= age %></div>
    <ul>
        <% books.forEach(function (book) { %>
            <li><%= book %></li>
        <% }) %>
    </ul>
</body>

</html>

用 koa-static 处理静态资源

本节目标: koa-static中间件, 处理静态资源

我们自己处理静态资源的话,代码会较为繁琐,因此实际开发中可以借助封装好开源中间件,比如 koa-static

查看 koa-static 中间件

使用步骤:

  1. 安装依赖包
npm i koa-static
  1. 创建 static 目录,并放入一些静态资源文件(图片等)

  2. 配置中间件

// 引入 koa-body
const koaStatic = require('koa-static')

// ...

// 调用 koaStatic() 能自定义一些配置,并返回一个真正的 koa-static 中间件函数,然后设置给 Koa 实例使用
app.use(koaStatic('./static'))

用 koa-body 处理文件上传

本节目标: 使用 koa-body 实现文件上传接口, 处理文件上传

通过 koa-body 中间件,从请求体中读取客户端上传的二进制文件数据

通过使用 koa-body 中间件,不光能方便的获取请求体中的普通文本参数,也可获取二进制的文件数据,因此可以处理文件上传的场景。

使用步骤:

  1. 配置 koa-body,使其支持处理文件上传
// 引入 koa-body
const koaBody = require('koa-body')

// 调用 koaBody() 并进行文件上传处理相关的配置
app.use(koaBody({
    // 开启文件上传支持
    multipart: true,
  
    // 上传文件相关配置
    formidable: {
        // 文件上传到的目录
        uploadDir: './upload',
      
        // 保留上传文件的后缀名(默认为 false,所有上传的文件都会被去除后缀名)
        keepExtensions: true
    }
}))
  1. 在我们自己的中间件中,获取客户端通过 FormData 传递过来的文件及其他参数
app.use(async ctx => {
    // 获取上传的文件信息
    console.log('请求体中的文件', ctx.request.files)

    // 获取请求体中的其他参数
    console.log('请求体中的其他参数', ctx.request.body)

    ctx.body = 'Hello'
})

用 koa-router 实现后端路由

本节目标: 使用 koa-router 实现服务端路由

了解如何使用 koa-router 方便的实现服务端路由

自己实现不同的路由示例:

const Koa = require('koa')
const app = new Koa()

app.use(async ctx => {
  // 获取客户端请求的 URL 路径
  const url = ctx.url
  const method = ctx.method

  // 根据路径来判断具体要做的业务逻辑
  if (method === 'GET' && url === '/login') {
    ctx.body = '这是登录页'
  } else if (method === 'POST' && url === '/login') {
    ctx.body = '登录处理成功'
  } else if (method === 'GET' && url === '/register') {
    ctx.body = '这是注册页'
  } else if (method === 'POST' && url === '/register') {
    ctx.body = '注册处理成功'
  } else {
    ctx.body = '404 Not Found'
  }
})

app.listen(3000, () => {
  console.log('请访问 http://localhost:3000')
})

以上做法的弊端是:随着路由路径的增加,中间件代码变得很复杂。

而借助 koa-router 中间件,可以很清晰的创建和管理多个路由。

使用步骤:

  1. 安装依赖包
npm i @koa/router
  1. 引入 koa-router 并创建 Router 实例
// 引入 koa-router
const Router = require('@koa/router')

// 创建 Router 实例
const router = new Router()
  1. 在 Router 实例上创建路由处理器
// 静态路由 GET /login
router.get('/login', async ctx => {
    ctx.body = '这是登录页'
})

// 静态路由 POST /login
router.post('/login', async ctx => {
    ctx.body = '登录处理成功'
})

// 动态路由 GET /articles/123
router.get('/articles/:id', async ctx => {
    const id = ctx.params.id
    console.log(">>>>>>>> 动态路由参数 ID:", id);
    ctx.body = `ID 为 ${id} 的内容`
})
  1. 根据 router 生成路由相关的实际中间件函数,并设置给 Koa 实例
app.use(router.routes())

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

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

相关文章

【掌握C++ string 类】——【高效字符串操作】的【现代编程艺术】

专栏&#xff1a;C学习笔记 上一篇&#xff1a;【C】——【 STL简介】——【详细讲解】 1. 为什么要学习 string 类&#xff1f; 1.1 C 语言中的字符串 在 C 语言中&#xff0c;字符串是以 \0 结尾的字符集合。如下所示&#xff1a; #include <stdio.h>int main() {c…

【第五节】C/C++数据结构之图

目录 一、图的基本概念 1.1 图的定义 1.2 图的其他术语概念 二、图的存储结构 2.1 邻接矩阵 2.2 邻接表 三、图的遍历 3.1 广度优先遍历 3.2 深度优先遍历 四、最小生成树 4.1 最小生成树获取策略 4.2 Kruskal算法 4.3 Prim算法 五、最短路径问题 5.1 Dijkstra算…

springboot+vue人事管理系统 +LW +PPT+源码+讲解

3系统分析 3.1可行性分析 在开发系统之前要进行系统可行性分析&#xff0c;目的是在用最简单的方法去解决最大的问题&#xff0c;程序一旦开发出来满足了员工的需要&#xff0c;所带来的利益也很多。下面我们将从技术、操作、经济等方面来选择这个系统最终是否开发。 3.1.1技…

米国政府呼吁抛弃 C 和 C++

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「C的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 很多观点认为C 或 C永远不可被…

C++那些事之小项目实战-进程间通信

小项目实战之进程间通信 进程间通信是一个非常重要的话题&#xff0c;特别是像一些大型项目都有它的影子&#xff0c;例如&#xff1a;PostgreSQL使用了管道完成copy的进程间通信&#xff0c;那么本节也将基于这个主题&#xff0c;使用C去搭建一个进程间通过管道通信的demo出来…

Angluar 实现pdf页面预览以及编辑

之前用过一个pdf预览的lib&#xff0c;并且还支持在线编辑&#xff0c;和直接下载编辑之后的pdf和直接打印&#xff0c;还不错&#xff0c;记录下 PdfShowcase 首先安装依赖 npm install ngx-extended-pdf-viewer 然后引入 import { NgxExtendedPdfViewerModule } from &q…

软件研发标准化流程文件

为了规范化系统开发流程&#xff0c;我们精心制定了一套详尽的规范文档。该文档旨在通过标准化、系统化的方法来显著提升开发效率与项目质量。流程始于明确需求阶段&#xff0c;通过深入细致的设计规划来确保解决方案既可行又具有前瞻性。随后&#xff0c;我们进入高效的编码实…

【懒删除堆 优先队列】1172. 餐盘栈

本文涉及知识点 懒删除堆 优先队列 LeetCode1172. 餐盘栈 我们把无限数量 ∞ 的栈排成一行&#xff0c;按从左到右的次序从 0 开始编号。每个栈的的最大容量 capacity 都相同。 实现一个叫「餐盘」的类 DinnerPlates&#xff1a; DinnerPlates(int capacity) - 给出栈的最大…

Linux开发讲课29---Linux USB 设备驱动模型

Linux 内核源码&#xff1a;include\linux\usb.h Linux 内核源码&#xff1a;drivers\hid\usbhid\usbmouse.c 1. BUS/DEV/DRV 模型 "USB 接口"是逻辑上的 USB 设备&#xff0c;编写的 usb_driver 驱动程序&#xff0c;支持的是"USB 接口"&#xff1a; US…

向量数据库、主键存储引擎、高速网络 RDMA 框架……DolphinDB 版本更新啦!

盛夏已至&#xff0c;炎热的七月伊始&#xff0c;DolphinDB 也迎来了版本的更新。此次更新的 3.00.1 与 2.00.13 版本从多个维度进行了优化扩展&#xff0c;进一步深化了 DolphinDB 在机器学习、数据分析等领域的尝试与探索。 为了响应用户日益增长的 AI 运算需求&#xff0c;…

XJTUSE-数据结构-homework2

当时写的还挺痛苦的 不过现在看&#xff0c;原老师布置的作业真的有水平 现在来看大二数据结构的作业&#xff0c;真的很锻炼代码能力。有些题目&#xff0c;我现在写也不一定能很快写出来hhhh 当时写的作业感觉还是存在问题的&#xff01; 任务概述 任务 1 &#xff1a;指定的…

JSON字符串中获取一个指定字段的值

一、方式一&#xff0c;引用gson工具 测试报文&#xff1a; {"account":"yanxiaosheng","password":"123456" } 引入pom <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> <dependency><gr…

无人机测绘需要注意什么?

无人机测绘是一项高精度的测量工作&#xff0c;需要注意以下四点&#xff1a; 一、作业前准备&#xff1a;沟通相关事宜&#xff0c;现场勘查&#xff0c;飞行环境检查等&#xff1b; 二、航线规划与像控点布设&#xff1a;航线规划是任务规划的核心内容&#xff0c;需要综合…

【web APIs】快速上手Day05(Bom操作)

目录 Web APIs - 第5天笔记js组成window对象BOM定时器-延迟函数案例-5秒钟之后消失的广告 JS执行机制location对象案例-5秒钟之后跳转的页面 navigator对象histroy对象 本地存储&#xff08;今日重点&#xff09;localStorage&#xff08;重点&#xff09;sessionStorage&#…

交换机需要多大 buffer

有点违背直觉&#xff0c;但是真事儿&#xff0c;交换机过境的流越多&#xff0c;所需 buffer 越小&#xff0c;这是为什么&#xff1f; 范氏(范雅各布森&#xff0c;van jacobson)管道的 aimd 流建议 buffer_size 为 bdp&#xff0c;这很容易理解&#xff0c;因为 aimd 流最小…

适合弱电行业的项目管理软件!找企智汇软件!

随着科技的不断发展&#xff0c;弱电行业对于项目管理的需求日益增强。为满足这一需求&#xff0c;企智汇推出了一款专为弱电行业打造的工程项目管理系统。 企智汇弱电行业工程项目管理系统以其专业性、高效性和智能性&#xff0c;赢得了业界的广泛认可。该系统深入融合了弱电…

pycharm配置conda解释器

假如我新建了一个conda虚拟环境&#xff0c;名为python3.8

Ubuntu设置nacos开机以单机模式自启动

首先&#xff0c;需要安装jdk Ubuntu 安装JDK 创建Systemd服务单元文件 sudo vim /etc/systemd/system/nacos.service按i进入编辑模式&#xff0c;写入下面信息 [Unit] Descriptionnacos server Afternetwork.target[Service] Typeforking Environment"JAVA_HOME/opt/j…

AI 芯片之战:开启智能新时代的关键角逐

在科技发展的浪潮中&#xff0c;一场围绕 AI 芯片的激烈竞争正在全球范围内如火如荼地展开。多家巨头纷纷投身其中&#xff0c;使得这场混战已然进入白热化阶段。 AI 芯片&#xff0c;作为推动人工智能发展的核心硬件&#xff0c;其作用举足轻重。它能够高效地处理海量的数据&a…

IODD简介(1)

目录 1 IODD简介 1.1基本概述 1.2主要结构 1.3 数据类型 1 IODD简介 1.1基本概述 IODD&#xff08;IO Device Description&#xff09;是一组文件&#xff0c;该文件的作用&#xff1a;被工程工具用于PLC或主站。用于识别、配置、定义用于过程数据交换的数据结构参数化和…