js 加入debug后可以进入controller_写给前端的 Nest.js 教程——10分钟上手后端接口开发

05e6b3d860a28d75ff7801e781c2f6ae.png

前言

沉默了很久,一直都没发文章,有些惭愧。

最近实习结束之后回了学校,提前开始做毕业设计了。对,就是毕业设计

近两个月把 React NativeVue 3.0Nest.js 都摸了一下,大概都摸懂了。

鉴于掘金已经很多优秀的 Vue 3.0 教程了,本人自认为文笔远逊于掘金的大佬们,就没有班门弄斧也写一篇了(本来很想了,后来想想感觉炒冷饭没啥意思,如果有想看我的教程风格的同学可以点赞或者在评论区里留言说一下,也不是不能写)。

很多后端的同学都说:你们前端不就是切个图嘛,凭啥跟我们后端的同学平起平坐啊?

这下前端的同学可以站起来了:你们后端不也就是 CRUD 嘛,凭啥瞧不起我们前端的同学啊?

今天就写一下最近做毕业设计用到的框架:Nest.js 的基础教程吧,简单教大家做一下 CRUD(小白向,大佬轻喷)。

挖个坑,这应该是最基础的第一章吧,如果大家觉得好就多点赞评论,过 200 点赞就加更一些,争取让大家从前端走向全栈吧

这个教程的所有代码我都放在了我的 GitHub 仓库:Nest-CRUD-Demo,欢迎大家点个 Star

同时也欢迎大家关注 「Hello FE」,里面有非常多其他的精品好文,不论是还在学习前端的同学还是已经工作了一段时间的朋友,都可以阅读一下(关注还有小惊喜,链接过期了可以在后台回复,我看到了会回复新的链接)。

框架简介

Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。它使用渐进式 JavaScript,内置并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 编写代码)并结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的元素。
在底层,Nest 使用强大的 HTTP Server 框架,如 Express(默认)和 FastifyNest 在这些框架之上提供了一定程度的抽象,同时也将其 API 直接暴露给开发人员。这样可以轻松使用每个平台的无数第三方模块。

我猜肯定很多同学看不懂这段话,没关系,我也暂时看不懂,但这不影响我们学会用它 CRUD

我们只需要知道它是一款 Node.js 的后端框架,规范化开箱即用的特性使其在国外开发者社区非常流行,社区也非常活跃,GitHub Repo 拥有 31.1k Star

相比于 ExpressKoa 的千奇百怪五花八门,Nest 确实是一股清流。

不过我们国内也有很棒的 Node.js 框架,比如说 Midway,和 Nest 一样,采用的 IoC 的机制,想了解一下的同学可以看我的小伙伴林不渡写的文章:《走近 MidwayJS :初识 TS 装饰器与 IoC 机制》,还可以到 Midway 官网自行探索。

包括在 Nest 当中遇到的装饰器相关的知识,大家也可以到上面林不渡同学的那篇文章中了解。

前置知识

  • HTTP
  • TypeScript/JavaScript

项目环境

  • git
  • mongodb
  • node.js >= 10.13.0

安装 MongoDB

这个章节的教程我就只写 Mac OS 上的安装了,毕竟上了大学就很少用 Windows 了,用 Windows 的同学可以到 ="https://mongodb.com/download-center/community">MongoDB 官网选择对应的系统版本去下载 msi 的安装包,或者搜索引擎里搜索一下,记得限定一下结果的时间,保证能够搜索到最新的教程。

强烈建议使用 Homebrew 来对 Mac OS 的软件包环境进行管理,没有安装的同学可以点击这里下载。

由于目前 MongoDB 已经不开源了,因此我们想要安装 MongoDB 就只能安装社区版本。

brew tap mongodb/brew
brew install mongodb-community

安装好之后我们就可以启动 MongoDB 的服务了:

brew services start mongodb-community

服务启动了就不用管了,如果要关闭的话可以把 start 改成 stop,就能够停止 MongoDB 的服务了。

构建项目

有两种方式,可以自行选择,两者没有区别:

使用 Nest CLI 安装:

npm i -g @nestjs/cli
nest new nest-crud-demo

使用 Git 安装:

git clone https://github.com/nestjs/typescript-starter.git nest-crud-demo

这两条命令的效果完全一致,就是初始化一个 Nest.js 的项目到当前文件夹下,项目的文件夹名字为 nest-crud-demo,两种方式都可以。

当然,我还是建议采用第一种方式,因为后面我们可以直接使用脚手架工具生成项目文件。

启动服务

cd nest-crud-demo
npm run start:dev 或者 yarn run start:dev

就可以以开发模式启动我们的项目了。

这里其实有一个小小的点,就是启动的时候应该以 dev 模式启动,这样 Nest自动检测我们的文件变化,然后自动重启服务

如果是直接 npm start 或者 yarn start 的话,虽然服务启动了,但是我们如果在开发的过程中修改了文件,就要手动停止服务然后重新启动,效率挺低的。

安装依赖

项目中我们会用到 Mongoose 来操作我们的数据库,Nest 官方为我们提供了一个 Mongoose 的封装,我们需要安装 mongoose@nestjs/mongoose

npm install mongoose @nestjs/mongoose --save

安装好之后我们就可以开始编码过程了。

编写代码

创建 Module

我们这次就创建一个 User 模块,写一个用户增删改查,带大家熟悉一下这个过程。

nest g module user server

脚手架工具会自动在 src/server/user 文件夹下创建一个 user.module.ts,这是 Nest 的模块文件,Nest 用它来组织整个应用程序的结构。

// user.module.ts
import { Module } from '@nestjs/common';@Module({})
export class UserModule {}

同时还会在根模块 app.module.ts 中引入 UserModule 这个模块,相当于一个树形结构,在根模块中引入了 User 模块。

执行上面的终端命令之后,我们会惊讶地发现,app.module.ts 中的代码已经发生了变化,在文件顶部自动引入了 UserModule,同时也在 @Module 装饰器的 imports 中引入了 UserModule

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './server/user/user.module'; // 自动引入@Module({imports: [UserModule], // 自动引入controllers: [AppController],providers: [AppService]
})
export class AppModule {}

创建 Controller

nest g controller user server

Nest 中,controller 就类似前端的路由,负责处理客户端传入的请求服务端返回的响应

举个例子,我们如果要通过 http://localhost:3000/user/users 获取所有的用户信息,那么我们可以在 UserController 中创建一个 GET 方法,路径为 users 的路由,这个路由负责返回所有的用户信息。

// user.controller.ts
import { Controller, Get } from '@nestjs/common';@Controller('user')
export class UserController {@Get('users')findAll(): string {return "All User's Info"; // [All User's Info] 暂时代替所有用户的信息}
}

这就是 controller 的作用,负责分发和处理请求响应

当然,也可以把 findAll 方法写成异步方法,像这样:

// user.controller.ts
import { Controller, Get } from '@nestjs/common';@Controller('user')
export class UserController {@Get('users')async findAll(): Promise<any> {return await this.xxx.xxx(); // 一些异步操作}
}

创建 Provider

nest g service user server

provider 我们可以简单地从字面意思来理解,就是服务的提供者

怎么去理解这个服务提供者呢?举个例子,我们的 controller 接收到了一个用户的查询请求,我们不能直接在 controller 中去查询数据库并返回,而是要将查询请求交给 provider 来处理,这里我们创建了一个 UserService,就是用来提供数据库操作服务的。

// user.service.ts
import { Injectable } from '@nestjs/common';@Injectable()
export class UserService {}

当然,provider 不一定只能用来提供数据库的操作服务,还可以用来做一些用户校验,比如使用 JWT 对用户权限进行校验的策略,就可以写成一个策略类,放到 provider 中,为模块提供相应的服务。

挺多文档将 controllerprovider 翻译为控制器提供者,我感觉这种翻译挺生硬的,让人不知所云,所以我们姑且记忆他们的英文名吧。

controllerprovider 都创建完后,我们又会惊奇地发现,user.module.ts 文件中多了一些代码,变成了这样:

// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';@Module({controllers: [UserController],providers: [UserService]
})
export class UserModule {}

从这里开始,我们就要开始用到数据库了~

连接数据库

引入 Mongoose 根模块

连接数据之前,我们要先在根模块,也就是 app.module.ts 中引入 Mongoose 的连接模块:

// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './server/user/user.module';@Module({imports: [MongooseModule.forRoot('mongodb://localhost/xxx'), UserModule],controllers: [AppController],providers: [AppService]
})
export class AppModule {}

这段代码里面的 mongodb://localhost/xxx 其实就是本地数据库的地址,xxx 是数据库的名字。

这时候保存文件,肯定有同学会发现控制台还是报错的,我们看一下报错信息就很容易知道问题在哪里了。

其实就是 mongoose 模块没有类型声明文件,这就很容易解决了,安装一下就好:

npm install @types/mongoose --dev 或者 yarn add @types/mongoose --dev

安装完之后服务就正常重启了。

引入 Mongoose 分模块

这里我们先要创建一个数据表的格式,在 src/server/user 文件夹下创建一个 user.schema.ts 文件,定义一个数据表的格式:

// user.schema.ts
import { Schema } from 'mongoose';export const userSchema = new Schema({_id: { type: String, required: true }, // 覆盖 Mongoose 生成的默认 _iduser_name: { type: String, required: true },password: { type: String, required: true }
});

然后将我们的 user.module.ts 文件修改成这样:

// user.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserController } from './user.controller';
import { userSchema } from './user.schema';
import { UserService } from './user.service';@Module({imports: [MongooseModule.forFeature([{ name: 'Users', schema: userSchema }])],controllers: [UserController],providers: [UserService]
})
export class UserModule {}

好了,现在一切就绪,终于可以开始编写我们的 CRUD 逻辑了!冲冲冲~

CRUD

我们打开 user.service.ts 文件,为 UserService 类添加一个构造函数,让其在实例化的时候能够接收到数据库 Model,这样才能在类中的方法里操作数据库。

// user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateUserDTO } from './user.dto';
import { User } from './user.interface';@Injectable()
export class UserService {constructor(@InjectModel('Users') private readonly userModel: Model<User>) {}// 查找所有用户async findAll(): Promise<User[]> {const users = await this.userModel.find();return users;}// 查找单个用户async findOne(_id: string): Promise<User> {return await this.userModel.findById(_id);}// 添加单个用户async addOne(body: CreateUserDTO): Promise<void> {await this.userModel.create(body);}// 编辑单个用户async editOne(_id: string, body: EditUserDTO): Promise<void> {await this.userModel.findByIdAndUpdate(_id, body);}// 删除单个用户async deleteOne(_id: string): Promise<void> {await this.userModel.findByIdAndDelete(_id);}
}

因为 mongoose 操作数据库其实是异步的,所以这里我们使用 async 函数来处理异步的过程。

好奇的同学会发现,这里突然出现了两个文件,一个是 user.interface.ts,另一个是 user.dto.ts,我们现在来创建一下:

// user.interface.ts
import { Document } from 'mongoose';export interface User extends Document {readonly _id: string;readonly user_name: string;readonly password: string;
}
// user.dto.ts
export class CreateUserDTO {readonly _id: string;readonly user_name: string;readonly password: string;
}export class EditUserDTO {readonly user_name: string;readonly password: string;
}

其实就是对数据类型做了一个定义。

现在,我们可以到 user.controller.ts 中设置路由了,将客户端的请求进行处理,调用相应的服务实现相应的功能:

// user.controller.ts
import {Body,Controller,Delete,Get,Param,Post,Put
} from '@nestjs/common';
import { CreateUserDTO, EditUserDTO } from './user.dto';
import { User } from './user.interface';
import { UserService } from './user.service';interface UserResponse<T = unknown> {code: number;data?: T;message: string;
}@Controller('user')
export class UserController {constructor(private readonly userService: UserService) {}// GET /user/users@Get('users')async findAll(): Promise<UserResponse<User[]>> {return {code: 200,data: await this.userService.findAll(),message: 'Success.'};}// GET /user/:_id@Get(':_id')async findOne(@Param('_id') _id: string): Promise<UserResponse<User>> {return {code: 200,data: await this.userService.findOne(_id),message: 'Success.'};}// POST /user@Post()async addOne(@Body() body: CreateUserDTO): Promise<UserResponse> {await this.userService.addOne(body);return {code: 200,message: 'Success.'};}// PUT /user/:_id@Put(':_id')async editOne(@Param('_id') _id: string,@Body() body: EditUserDTO): Promise<UserResponse> {await this.userService.editOne(_id, body);return {code: 200,message: 'Success.'};}// DELETE /user/:_id@Delete(':_id')async deleteOne(@Param('_id') _id: string): Promise<UserResponse> {await this.userService.deleteOne(_id);return {code: 200,message: 'Success.'};}
}

至此,我们就完成了一个完整的 CRUD 操作,接下来我们来测试一下~

接口测试

接口测试我们用的是 Postman,大家可以去下载一个,非常好用的接口自测工具。

数据库可视化工具我们用的是 MongoDB 官方的 MongoDB Compass,也很不错。

GET /user/users

dc023fe059a4c511acc28901fc0da72f.png

一开始我们的数据库中什么都没有,所以返回了一个空数组,没用用户信息。

POST /user

1bafd3c84e96a455de7fa47659ac0563.png

现在我们添加一条用户信息,服务器返回添加成功。

121a9e133fb3afacc7af7c64a338864c.png

GET /user/:_id

7e8e8b052985058e4b78652c7f2345e0.png

添加完一条用户信息之后再查询,可算是能查询到我的信息了。

PUT /user/:_id

1217cd5426fff01b7b57f210167d8a30.png

现在假如我想修改密码,发送一个 PUT 请求。

c044caba4ba62695562b52f5fe602bb4.png

DELETE /user/:_id

fba1ef5d53c5449835f2a67b26cf723b.png

现在我们删除一下刚才添加的用户信息。

c7de0a76212d8ffbd7dc58779ff51fcf.png

会发现数据库中的内容已经被删除了。

完结撒花

大功告成,CRUD 就这么简单,用这个项目去参加一些学校举行的比赛,拿个奖肯定没什么问题,开箱即用(学校老师们别打我)。

总结

教程还算是用了比较通俗易懂的方式为大家讲解了如何写一个带有 CRUD 功能的后端 Node.js 应用,框架采用的是 Nest.js

相信大家在上面的教程中肯定有非常多不懂的部分,比如说 @Get()@Post()@Param()@Body() 等等的装饰器,再比如说一些 Nest.js 相关的概念。

没关系,我的建议是:学编程先模仿,遇到不懂的地方先记住,等到自己的积累够多了,总有一天你会回过头发现自己茅塞顿开,突然懂了。这也是我个人学习的一个小技巧。

在学习的过程中,也一定会遇到一些问题,学习编程的过程中遇到问题不能自己憋着,一定要学会请教大佬!一定要学会请教大佬!一定要学会请教大佬!重要的事情说三遍。

不过也别很简单的问题就去请教大佬,而且最好给一点小小的报酬,毕竟谁也没有义务帮你解决问题。

我在学习的过程中也请教了一些社区里面的大佬,同时还进入了 Nest.js 的社区答疑群,向国外友人请教学到了不少知识。

当然,这个 Demo 中也有很多可以完善的地方,比如说错误处理

数据库的操作肯定是有可能出现错误的,比如说我们漏传了 required: true 的参数,数据库就会报错。

这个时候我们就要写一个 try/catch 捕获这个异常,或者干脆写一个异常的过滤器,将所有的异常统一处理(Nest.js 支持过滤器)

除此之外,既然有可能出现异常,那么我们就需要一个日志系统去捕获这个异常,方便查错纠错。

如果涉及到登录注册的部分,还有密码加解密的过程,同时还可能有权限校验问题需要进行处理。

所以后端的同学肯定不止 CRUD 啦(可算圆回来了)。

这个教程的所有代码我都放在了我的 GitHub 仓库:Nest-CRUD-Demo,欢迎大家点个 Star

参考资料

  • NestJS - A progressive Node.js framework
  • Nest.js 中文文档

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

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

相关文章

ecplice中class.forname一直报错_A6v5.1升级A6v7.0报错:调用Java代码

一 背景在升级7.0过程中&#xff0c;针对以前SQL2000数据库(主要是以前U3升级上来的账套)会出现java调用失败的情况&#xff0c;导致老版本的账套无法正常升级到最新的版本。错误提示&#xff1a;【com.aisino.a6.update.plugin.ViewsUpdatePlugin】升级业务数据出错升级报错图…

理解 Java 核心基础精髓

1、字符串不变性 下面这张图展示了这段代码做了什么 String s "abcd"; s s.concat("ef"); 2、equals() 方法与 hashCode() 方法的区别 HashCode 被设计用来提高性能。equals() 方法与 hashCode() 方法的区别在于&#xff1a; 如果两个对象相等(equa…

支付退款流程设计_电商订单系统,你该如何设计

文章来自&#xff1a;https://baijiahao.baidu.com/s?id1602959656926168475&wfrspider&forpc作者&#xff1a;人人都是产品经理点击加入&#xff1a;PHP自学中心技术交流微信群商务合作: 请加微信(QQ)&#xff1a;2230304070精选教程推荐技术交流微信群我们在学习中单…

Java 对象之死

如何判断对象“无用”&#xff1f; 关于判断对象是否无用的算法&#xff0c;在JVM的发展过程中出现过两种算法&#xff1a;一种是引用计数和根集算法。 引用计数算法 例如下图中的object1的引用计数是2&#xff0c;GC的时候不回收&#xff0c;object6、object7引用计数为0&a…

图解Java常用数据结构

最近在整理数据结构方面的知识, 系统化看了下Java中常用数据结构, 突发奇想用动画来绘制数据流转过程. 主要基于jdk8, 可能会有些特性与jdk7之前不相同, 例如LinkedList LinkedHashMap中的双向列表不再是回环的. HashMap中的单链表是尾插, 而不是头插入等等, 后文不再赘叙这些…

python中怎么计算_python中的加减乘除运算

python中的加减乘除运算&#xff0c;是学习python入门的基础&#xff0c;是以后进行数学计算的关键部分。包括运算表达式的写法&#xff0c;运算规则&#xff0c;加减乘除&#xff0c;求余&#xff0c;求商等等。 工具/原料 python 电脑 方法/步骤 1 1.python中的加法运算&…

c++ 模拟拖动_2020电工(中级)模拟考试题及电工(中级)模拟考试题库

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序2020电工(中级)模拟考试题及电工(中级)模拟考试题库&#xff0c;包含电工(中级)模拟考试题答案解析及电工(中级)模拟考试题库练习。由安全生产模拟考试一点通公众号结合国家电工(中级)考试最新大纲及电工(中级)考试真题…

12层的bert参数量_只需一个损失函数、一个超参数即可压缩BERT,MSRA提出模型压缩新方法...

来自武汉大学、北京航空航天大学和微软亚洲研究院的这项研究为模型压缩提供了新方向。机器之心报道&#xff0c;参与&#xff1a;魔王。论文链接&#xff1a;https://arxiv.org/pdf/2002.02925.pdf这篇论文提出了一种新型模型压缩方法&#xff0c;能够通过逐步模块替换&#xf…

常用的Java快捷键有哪些

不知道Java常用的快捷键在工作的时候是非常的耽误时间的&#xff0c;本篇文章小编就为大家稍微整理了一些常用的Java快捷键分享给大家。 Ctrl1或F2快速修复 CtrlD快捷删除行 ShiftEnter 快速切换到下一行&#xff0c;在本行的任何位置都可 CtrlF11快速运行代码 Alt上下键 快速移…

bootstrap文件不能被识别_如何使用npm安装bootstrap

经过我好多次的查询和实验&#xff0c;总结了一下在vue项目中使用bootstrap框架&#xff0c;npm安装注意。bootstrap中js插件依赖于jquery&#xff0c;所以在这之前必须安装jquery。jquery安装1、在package.json中添加一行代码&#xff1a;“jquery”: “^2.2.3”"depende…

hm编码工具使用_HM集团旗下最高端品牌ARKET将进驻中国,带你看看真正的“北欧风”...

HM集团旗下最高端的品牌ARKET近日开通了微博&#xff0c;并宣布将在秋季进驻天猫。听到这个消息&#xff0c;钟爱ARKET且一直私心将它当成小众品牌好好藏着的人可能会欢呼——终于不用等待漫长的海购了&#xff01;但也有相当一部分朋友一脸问号——ARKET&#xff1f;是啥&…

Java8特性解决空指针问题

在文章的开头&#xff0c;先说下NPE问题&#xff0c;NPE问题就是&#xff0c;我们在开发中经常碰到的NullPointerException.假设我们有两个类&#xff0c;他们的UML类图如下图所示&#xff1a; 在这种情况下&#xff0c;有如下代码 这种写法&#xff0c;在user为null时&#x…

模拟进程创建、终止、阻塞、唤醒原语_操作系统第二章--进程的描述与控制

操作系统第二章--进程的描述与控制前趋图和程序执行前趋图前趋图是一个有向无循环图DAG&#xff0c;用来描述进程之间执行的前后关系初始结点&#xff1a;没有前趋的结点终止结点&#xff1a;没有后继的结点重量&#xff1a;表示该结点所含有的程序量或结点的执行时间程序执行顺…

对没有标记为安全的activex控件进行初始化和脚本运行_RASP攻防 —— RASP安全应用与局限性浅析...

文&#xff5c;【腾讯安全平台部数据安全团队】 qiye & baz 前言随着Web应用攻击手段变得复杂&#xff0c;基于请求特征的防护手段&#xff0c;已经不能满足企业安全防护需求。在2012年的时候&#xff0c;Gartner引入了“Runtime application self-protection”一词&#x…

java中堆和栈有什么区别?

堆和栈都是Java用来在RAM中存放数据的地方。 一、堆 (1)Java的堆是一个运行时数据区&#xff0c;类的对象从堆中分配空间。这些对象通过new等指令建立&#xff0c;通过垃圾回收器来销毁。 (2)堆的优势是可以动态地分配内存空间&#xff0c;需要多少内存空间不必事先告诉编译…

台式机dp接口_精品导购:你想要的商务台式机 都在这里了!

不少公司都会为员工配备办公电脑&#xff0c;而公司在采购这些电脑时&#xff0c;兴许可能对电脑性能、存储容量等都没有研究过&#xff0c;只是图了价格便宜便批量采购&#xff0c;这就导致电脑在使用一段时间后&#xff0c;会频繁出现死机、运行卡顿和硬盘容量不够等情况的发…

roc曲线怎么绘制_ROC曲线和PR曲线

在机器学习中&#xff0c;ROC曲线被广泛应用于二分类问题中来评估分类器的可信度&#xff0c;当处理一些高度不均衡的数据集时&#xff0c;PR曲线能表现出更多的信息。在二分类问题中&#xff0c;分类器将一个实例的分类标记为正例还是负例&#xff0c;这可以用一个混淆矩阵来表…

华三交换机路由器图标_弱电箱网口不够用,用华三8口千兆交换机搞定

如题&#xff0c;公司租的办公室刚装修完&#xff0c;安了弱电箱&#xff0c;无奈联通只给了一个2口的光猫&#xff0c;如何不自己花钱买光猫的前提下&#xff0c;分配三间屋子的五个有线成了问题。之前考虑过直接路由器内置在弱电箱里&#xff0c;不过电箱金属盒有屏蔽和弱电箱…

i2c hid 触摸板不能用_零基础学硬件(6):I2C总线的用处

I2C总线的用处&#xff0c;什么时候需要用I2C总线这东西太简单了,我还有点不想说..不就是一个start一个STOP一个ACK嘛,,简单得不能在简单.他可以根据器件地址进行通信..当年在学校时看了很多这资料..什么时候需要用I2C总线&#xff0c;你的器件是IIC的器件,我就得用IIC总线..什…

problem a: 简单的整数排序_python里的排序

本篇文章主要讲&#xff1a;自定义规则排序 多字段排序开讲之前&#xff0c;先讲一些简单sorted()或者sort()&#xff0c;两者返回值不同&#xff01;例如&#xff1a;sorted([5, 2, 3, 1, 4]) # 输出&#xff1a;[1, 2, 3, 4, 5]另一个呢a [5, 2, 3, 1, 4] a.sort() a # 输出…