2024年HarmonyOS鸿蒙最新NestJs的根本利用_nestjs命令(2),2024年最新鸿蒙 ...

打印 上一主题 下一主题

主题 874|帖子 874|积分 2626

深知大多数步伐员,想要提升技能,往往是自己探索发展,但自己不成体系的自学结果低效又漫长,而且极易遇到天花板技术停滞不前!



既有适合小白学习的零根本资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部门目录截图出来,全套包含大厂面经、学习条记、源码讲义、实战项目、大纲门路、讲解视频,并且后续会持续更新
需要这份系统化的资料的朋侪,可以戳这里获取
// 浏览器访问:http://localhost:3000/cat/say4/99
// 打印如下: param {id:‘99’}
@Get(‘/say4/:id’)
// Param参数为空
say4(@Param() param: { id: number }) {
console.log(‘param’, param);
return this.catService.say1();
}
// 5.Delete 删除接口
// APIFOX访问:http://localhost:3000/cat/say5/1 接口范例换成elete
// 打印如下: param { id: ‘1’ }
@Delete(‘/say5/:id’)
// Param参数为空
say5(@Param() param: { id: number }) {
console.log(‘param’, param);
return this.catService.say1();
}
// 6.Put 新增接口
// APIFOX访问:http://localhost:3000/cat/say5/1 接口范例换成ut
// 打印如下: param { id: ‘1’ }
@Put(‘/say6/:id’)
// Param参数为空
say6(@Param() param: { id: number }) {
console.log(‘param’, param);
return this.catService.say1();
}
// 7.Post 新增接口
// APIFOX访问:http://localhost:3000/cat 接口范例换成ost
// 参数在接口测试工具的body的json范例加入以下参数: { id: 1, name: ‘萧寂’ }
// 打印如下: cat { id: 1, name: ‘萧寂’ }
@Post()
say7(@Body() cat: any) {
console.log(‘cat’, cat);
return this.catService.say1();
}
}
  1. ## 提供者(也就是Java中的实体类)
  2. **在上面创建的cat文件夹内创建interface文件夹,在文件夹内创建cat.interface.ts文件**  
  3. **在上面创建的cat文件夹内创建dto文件夹,在文件夹内创建create-cat.dto.ts文件**
复制代码
// cat.interface.ts
export interface Cat {
name: string;
age: number;
breed: string;
}
// create-cat.dto.ts
import { Cat } from “…/interface/cat.interface”;
export class CreateCatDto implements Cat{
name: string;
age: number;
breed: string;
}
// cat.controller.ts
import {
Body,
Controller,
Post,
} from ‘@nestjs/common’;
import { CatService } from ‘./cat.service’;
import { CreateCatDto } from ‘./dto/create-cat.dto’;
@Controller(‘cat’)
export class CatController {
// 私有的,只读的service
constructor(private readonly catService: CatService) {}
// APIFOX访问:http://localhost:3000/cat 接口范例换成ost
// 参数在接口测试工具的body的json范例加入以下参数: { id: 1, name: ‘萧寂’ }
// 打印如下: cat { id: 1, name: ‘萧寂’ }
@Post()
say(@Body() cat: CreateCatDto) {
console.log(‘cat’, cat); // 在apiFox打印结果和上面一样,只是加了范例校验,但是由于ts只进行范例束缚,并不要求参数限定必须一致,因此这里请求的参数与dao和接口类不一致也可以拿到参数,并不会报错,只是不存在的参数不会对其范例进行校验而已了
return this.catService.say1();
}
}
  1. ## 中间件
  2. ### 创建中间件
复制代码
nest g mi logger
// 安装完成后在目录中会多出一个logger文件夹
//在app.mudule.ts放入以下内容进行注册绑定中心件
import { MiddlewareConsumer, Module } from ‘@nestjs/common’;
import { AppController } from ‘./app.controller’;
import { AppService } from ‘./app.service’;
import { CatController } from ‘./cat/cat.controller’;
import { CatService } from ‘./cat/cat.service’;
import { LoggerMiddleware } from ‘./logger/logger.middleware’;
@Module({
imports: [],
controllers: [AppController, CatController],
providers: [AppService, CatService],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
// 下面这段代码代表先绑定中心件再应用到路由上,当访问cat大概带cat子接口时会被拦截到
consumer.apply(LoggerMiddleware).forRoutes(‘cat’);
// 由于同一个接口名可能会有get,put,delete,post等方法,因此可以利用下面方式对指定范例的接口进行拦截
consumer.apply(LoggerMiddleware).forRoutes({path:‘cat’,method:RequestMethod.GET}); // 只拦截get请求的cat接口
// 也可以直接将controller作为参数(如许的话,在当前控制器下面全部的请求都会被拦截到的)
consumer.apply(LoggerMiddleware).forRoutes(CatController);
// 如果当前需要拦截的请求较多,可以将controller作为参数,但又不渴望其中个别接口被拦截到,可以利用下面方式忽略需要拦截的接口
consumer.apply(LoggerMiddleware).exclude({path:‘cat/:id’,method:RequestMethod.GET},{path:‘cat’,method:RequestMethod.POST}).forRoutes(CatController);
}
}
// ------------------------------------------------------------------------------------------------
// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from ‘@nestjs/common’;
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
// 每次访问cat接口都会被拦截到,并打印出一下日志
console.log(‘正在访问cat接口’);
next();
}
}
  1. **以上就是中间件基础使用方法了**
  2. ### 函数式中间件
复制代码
// 在logger/lagger.middleware.tss中添加如下内容
import { Injectable, NestMiddleware } from ‘@nestjs/common’;
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log(‘111222’, 111222);
next();
}
}
// 下面这个是函数式中心件(只有函数式中心件可以注册成为全局中心件)
export function logger(req, res, next) {
console.log(‘Request_logger’);
next();
}
  1. **注册成为全局中间件,只有函数式中间件可以注册成为全局中间件**
复制代码
// 在main.ts进行注册(代码如下)
import { NestFactory } from ‘@nestjs/core’;
import { AppModule } from ‘./app.module’;
import { logger } from ‘./logger/logger.middleware’;
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 将中心件注册成为全局中心件
app.use(logger);
await app.listen(3000);
}
bootstrap();
  1. ## 异常处理
  2. ### 异常抛出
  3. **例如我们输入了一个不存在的地址,就会响应一个404,这个是框架给我们默认返回的一个异常,有时候我们需要手动去抛出一个异常**  
  4. **Nest提供了一个内置的HttpException类,为基础异常类,可以很好的帮助我们进行异常的处理**
复制代码
// 在controller内其中一个接口如下:
import {Get,HttpException,HttpStatus} from ‘@nestjs/common’;
@Get(‘/xiaoji’)
xiaoji(): string {
throw new HttpException(‘服务器非常’, HttpStatus.FORBIDDEN);
}
// 浏览器访问:localhost:3000/cat/xiaoji
// 返回值为: {“statusCode”:403,“message”:“服务器非常”}
  1. ### 自定义异常及异常的处理
  2. #### 创建并配置异常处理器
复制代码
// 创建非常过滤器
nest g f http-exception
// 创建完成后目录会多出一个http-exception文件夹
// 在新创建的http-exception.filter.ts文件放入以下内容(已经处理好的)
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
} from ‘@nestjs/common’;
import { Request, Response } from ‘express’;
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
const status = exception.getStatus();
  1. response.status(status).json({
  2.   statusCode: status,
  3.   timestamp: new Date().toISOString(),
  4.   path: request.url,
  5. });
复制代码
}
}
  1. #### 使用异常处理器
  2. **在cat.controller.ts内使用**
复制代码
import {
Body,
Controller,
Get,
HttpException,
HttpStatus,
UseFilters,
} from ‘@nestjs/common’;
import { HttpExceptionFilter } from ‘src/http-exception/http-exception.filter’;
// 在接口利用
@Get(‘/xiaoji’)
// 访问到这个接口直接抛出非常(非常的格式为我们上面定制的格式(状态码,时间和接口路径))
@UseFilters(new HttpExceptionFilter())
xiaoji(): string {
// 如果这里不抛出非常,上面这个非常过滤器不会执行,只有手动抛出非常则非常过滤器才会执行
throw new HttpException(‘服务器非常’, HttpStatus.FORBIDDEN);
}
  1. **全局使用异常过滤器**
复制代码
// 例如有的情况有非常,但是我们不能一个个来抛出,例如全局的404非常,因此需要利用全局非常过滤器,方式如下
// main.ts
import { NestFactory } from ‘@nestjs/core’;
import { AppModule } from ‘./app.module’;
import { logger } from ‘./logger/logger.middleware’;
import { HttpExceptionFilter } from ‘./http-exception/http-exception.filter’;
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 将中心件注册成为全局中心件
app.use(logger);
// 全局注册非常过滤器
app.useGlobalFilters(new HttpExceptionFilter());
//
await app.listen(3000);
}
bootstrap();
// 当访问一个不存在的接口会返回404状态码和时间以及非常的接口路径(改写了默认的样式)
  1. ## 管道
复制代码
管道有两个典型的应用场景:
转换:管道将输入数据转换为所需的数据输出(例如,将字符串转换为整数)
验证:对输入数据进行验证,如果验证乐成继承传递; 验证失败则抛出非常
  1. Nest 自带九个开箱即用的管道,即
  2. * ValidationPipe
  3. * ParseIntPipe
  4. * ParseFloatPipe
  5. * ParseBoolPipe
  6. * ParseArrayPipe
  7. * ParseUUIDPipe
  8. * ParseEnumPipe
  9. * DefaultValuePipe
  10. * ParseFilePipe
  11. ### 转换
  12. **这里以ParseIntPipe为例,进行数据的转化**
复制代码
// 在cat.controller.ts其中一个接口如下内容
import {Get,Param,ParseIntPipe} from ‘@nestjs/common’;
//这个管道可以将吸收的参数转为number范例,否则返回的是{id:“123”},转换之后变成{id:123}
//如果参数给个abc导致不能转化成number,则会抛出400错误的非常,代表转化失败
@Get(‘:id’)
say1(@Param(‘id’, new ParseIntPipe()) id: number) {
return {
id: id,
};
}
  1. ### 验证(守卫)
  2. #### 基础使用
  3. >
  4. > 守卫在所有中间件之后执行,但在拦截器或管道之前执行。
  5. >
  6. >
  7. >
复制代码
// 创建保卫
nest g gu role
// 在新建的role.guard.ts内放入以下内容
import { CanActivate, ExecutionContext, Injectable } from ‘@nestjs/common’;
import { Observable } from ‘rxjs’;
@Injectable()
export class RoleGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise | Observable {
console.log(‘RoleGuard’);
return true;
}
}
// 再新建一个controller
nest g co user
// controller内放入以下内容
import {
Controller,
Get,
Param,
ParseIntPipe,
UseGuards,
} from ‘@nestjs/common’;
import { RoleGuard } from ‘src/role/role.guard’;
@Controller(‘user’)
@UseGuards(RoleGuard) // 利用保卫,当访问下面恣意接口时就会触发保卫,并打印(‘RoleGuard’)
export class UserController {
@Get(‘:id’)
// @UseGuards(RoleGuard) // 利用保卫,当访问当前接口时就会触发保卫,并打印(‘RoleGuard’)
getUserDetial(@Param(‘id’, ParseIntPipe) id: number) {
return {
id,
};
}
}
  1. #### 定义权限,进行路由比对
复制代码
// controller.ts内加入下面代码
import {
Controller,
Get,
Param,
ParseIntPipe,
UseGuards,
SetMetadata,
} from ‘@nestjs/common’;
import { RoleGuard } from ‘src/role/role.guard’;
@Controller(‘user’)
export class UserController {
@Get(‘:id’)
@SetMetadata(‘role’, [‘access1’, ‘access2’])
@UseGuards(RoleGuard) // 利用保卫
getUserDetial(@Param(‘id’, ParseIntPipe) id: number) {
return {
id,
};
}
}
// role.guard.ts加入下面代码
import { CanActivate, ExecutionContext, Injectable } from ‘@nestjs/common’;
import { Reflector } from ‘@nestjs/core’;
import { Observable } from ‘rxjs’;
@Injectable()
export class RoleGuard implements CanActivate {
// 加上反射(可以拿到接口对应相关的一些信息)
constructor(private reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise | Observable {
console.log(‘RoleGuard’);
// 下面的role与controller接口的@SetMetadata(‘role’, [‘access1’,‘access2’])第一个参数保持一致
const roles = this.reflector.get<string[]>(‘role’, context.getHandler());
console.log(‘roles’, roles); // 打印的roles就是[‘access1’,‘access2’],刚刚的第二个参数
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
// 上面那个可以在接口上界说权限,然后拿到界说的权限,这里的user代表用户发请求携带的权限,这里可以进行路由比对,有权限放行,没权限返回false
// 这里是拿到了用户的标识,我们的系统可以查询到用户的权限
console.log(‘user’,user);
return true;
}
}
  1. #### 第二种方式(装饰器模式)
复制代码
// 创建装饰器
nest g d role
// 创建完成后会发现多了个role.decorator.ts文件
// controller.ts
import {
Controller,
Get,
UseGuards,
} from ‘@nestjs/common’;
import { Role } from ‘src/role/role.decorator’; // 引入装饰器
import { RoleGuard } from ‘src/role/role.guard’; // 引入保卫
@Controller(‘user’)
@UseGuards(RoleGuard) // 利用保卫,下面的全部接口都会触发保卫
export class UserController {
@Get(‘user/list’)
@Role(‘access1’, ‘access2’,‘access3’, ‘access4’) // 装饰器内的参数就是权限(个人感觉较麻烦不如上面那个)
getUserList() {
return [1, 2, 3, 4];
}
}
// 访问接口得到-----roles [ ‘access1’, ‘access2’, ‘access3’, ‘access4’ ]
  1. ## 数据库
  2. ### 连接数据库
复制代码
// 安装依赖
yarn add --save @nestjs/typeorm typeorm mysql2
// @nestjs/typeorm这个本人运行上面命令不绝安装不上,只好单独安装一下就乐成了(yarn add @nestjs/typeorm)
// 安装完成后在app.module.ts进行导入
import { Module } from ‘@nestjs/common’;
import { TypeOrmModule } from ‘@nestjs/typeorm’;
@Module({
imports: [
TypeOrmModule.forRoot({
type: ‘mysql’,
host: ‘localhost’,
port: 3306,
username: ‘root’,
password: ‘admin’,
database: ‘shop’,
// entities: [], // 实体类文件,下面创建完curd会讲怎么写的
synchronize: true, //是否将实体同步到数据库
retryDelay:500, // 重试毗连数据库的间隔
retryAttempts: 10, // 重试毗连数据库的次数
autoLoadEntities: true, // 主动加载实体(加上这个就不用上面的entities了)
logging: true, // 是否开启日志
logger: ‘advanced-console’, // 日志
}),
],
})
export class AppModule {}
  1. ### 创建CURD
复制代码
nest g res test
// 选择第一个REST API(遵循rest api的规范)
// 选择y 确定创建curd(增编削查)
// 找到test/entities/test.entity.ts(只要创建了CURD这种文件,一般实体类都是这个位置,那么就可以动态加载实体类了,找到刚刚创建数据库的地方,将entities: [],添补以下内容,用于动态获取实体类),如果autoLoadEntities设置为true了,这个属性可以去掉,autoLoadEntities用于主动加载实体
entities: [__dirname + ‘/**/*.entity{.ts,.js}’],
  1. #### 在创建完的CURD文件内的实体类编写如下代码
  2. **test/entities/test.entity.ts**
复制代码
// 界说实体,界说列,界说自增主键
import { Entity, Column, PrimaryGeneratedColumn } from ‘typeorm’;
@Entity() // 界说实体类
export class Test {
@PrimaryGeneratedColumn()
id: number; // 自增id列
@Column()
name: string; // 列名
@Column()
password: string;
@Column()
age: number;
}
  1. #### 在test/test.model.ts里面编写如下代码
复制代码
import { Module } from ‘@nestjs/common’;
import { TestService } from ‘./test.service’;
import { TestController } from ‘./test.controller’;
// 引入实体
import { Test } from ‘./entities/test.entity’;
// 引入orm框架
import {TypeOrmModule} from ‘@nestjs/typeorm’
@Module({
// 引入注册
imports:[TypeOrmModule.forFeature([Test])],
controllers: [TestController],
providers: [TestService],
})
export class TestModule {}
  1. **保存重启项目会发现MySQL里面的shop这个数据库多了一个test这张表,表内的字段和实体类一一对应**
  2. ### 实体类介绍
复制代码
// 界说实体,界说列,界说自增主键
import { Entity, Column, PrimaryGeneratedColumn,CreateDateColumn,Generated } from ‘typeorm’;
@Entity() // 界说实体类
export class Test {
@PrimaryGeneratedColumn(“uuid”) // 自增的uuid
id: number; // 自增id列
@Column({type:“varchar”,length:255}) // 列范例
name: string;
@Column()
password: string;
@Column()
age: number;
// @CreateDateColumn() // 自增日期
@CreateDateColumn({ type: ‘timestamp’ }) // 时间戳范例
createTime: Date;
@Generated(‘uuid’) // 主动生成列
uuid: string;
@Column({ // 界说枚举范例
type: ‘enum’,
enum: [1, 2, 3, 4],
default: 1,
})
xiaoji: number;
}
  1. [/code] mysql 全部范例
  2. int, tinyint, smallint, mediumint, bigint, float, double, dec, decimal, numeric, date, datetime, timestamp, time, year, char, varchar, nvarchar, text, tinytext, mediumtext, blob, longtext, tinyblob, mediumblob, longblob, enum, json, binary, geometry, point, linestring, polygon, multipoint, multilinestring, multipolygon, geometrycollection
  3. [code]
  4. **列选项**
复制代码
  1. @Column({
  2.     type:"varchar",
  3.     name:"ipaaa", //数据库表中的列名
  4.     nullable:true, //在数据库中使列NULL或NOT NULL。 默认情况下,列是nullable:false
  5.     comment:"注释",
  6.     select:true,  //定义在进行查询时是否默认隐藏此列。 设置为false时,列数据不会显示标准查询,适合用于密码。 默认情况下,列是select:true
  7.     default:"xxxx", //加数据库级列的DEFAULT值
  8.     primary:false, //将列标记为主要列。 使用方式和@ PrimaryColumn相同。
  9.     update:true, //指示"save"操作是否更新列值。如果为false,则只能在第一次插入对象时编写该值。 默认值为"true"
  10.     collation:"", //定义列排序规则。
  11. })
  12. ip:string
复制代码
  1. **simple-array 列类型  
  2. 有一种称为simple-array的特殊列类型,它可以将原始数组值存储在单个字符串列中。 所有值都以逗号分隔**
复制代码
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
  1. @Column("simple-array")
  2. names: string[];
复制代码
}
  1. \*\*simple-json列类型  
  2. 还有一个名为simple-json的特殊列类型,它可以存储任何可以通过 JSON.stringify 存储在数据库中的值。 当你的数据库中没有 json 类型而你又想存储和加载对象,该类型就很有用了。 例如:  
  3. \*\*
复制代码
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
  1. @Column("simple-json")
  2. profile: { name: string; nickname: string };
复制代码
}
  1. ## 数据库的增删改查的使用
  2. ### axios 接口定义
复制代码
import axios from ‘axios’
axios.defaults.baseURL = ‘http://localhost:3000’
// 增
export const addUser = (data) => axios.post(‘/user’,data).then(res => res.data)
// 查
export const getList = (data) => axios.get(‘/user’,{params:data}).then(res => res.data)
// 删
export const delUser = (data) => axios.delete(/user/${data.id}).then(res => res.data)
// 改
export const updateUser = (data) => axios.patch(/user/${data.id},data).then(res => res.data)
  1. ### nest后端代码为
  2. #### 实体类为
复制代码
import { Entity, Column, PrimaryGeneratedColumn } from ‘typeorm’;
@Entity()
export class CreateUserDto {
@Column()
name:string
@Column()
desc:string
}
  1. #### controller代码如下
复制代码
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from ‘@nestjs/common’;
import { UserService } from ‘./user.service’;
import { CreateUserDto } from ‘./dto/create-user.dto’;
import { UpdateUserDto } from ‘./dto/update-user.dto’;
@Controller(‘user’)
export class UserController {
constructor(private readonly userService: UserService) {}
// 增
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
// 查
@Get()
findAll(@Query() query:{keyWord:string,page:number,pageSize:number}) {
return this.userService.findAll(query);
}
// 改
@Patch(‘:id’)
update(@Param(‘id’) id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
// 删
@Delete(‘:id’)
remove(@Param(‘id’) id: string) {
return this.userService.remove(+id);
}
}
  1. ### service代码如下
  2. >
  3. > 1.引入 InjectRepository typeOrm 依赖注入 接受一个实体  
  4. >  2.引入类型 Repository 接受实体泛型  
  5. >  3.Like 用于模糊查询  
  6. >  4.save 保存 find 查询 update 更新 delete 删除
  7. >
  8. >
  9. >
复制代码
import { Injectable } from ‘@nestjs/common’;
import { CreateUserDto } from ‘./dto/create-user.dto’;
import { UpdateUserDto } from ‘./dto/update-user.dto’;
import { Repository, Like } from ‘typeorm’;
import { InjectRepository } from ‘@nestjs/typeorm’;
import { User } from ‘./entities/user.entity’;
@Injectable()
export class UserService {
constructor(@InjectRepository(User) private readonly user: Repository) { }
// 增
create(createUserDto: CreateUserDto) {
const data = new User() // 创建实体类对象
data.name = createUserDto.name // 实体类的name赋值为传来的name
data.desc = createUserDto.desc // 实体类的desc赋值为传来的desc
return this.user.save(data) // 生存(每次生存都相当于添加一次)
}
// 查
async findAll(query: { keyWord: string, page: number, pageSize: number }) {
const data = await this.user.find({ // 查询
where: {
name: Like(%${query.keyWord}%) //模糊查询
},
select:[‘id’,‘name’], // 指定查询出来的字段
order: {
id: “DESC” // 倒序
},
skip: (query.page - 1)* query.pageSize, // skip跳过,从0开始
take:query.pageSize, // 需要展示的每页的数量
})
const total = await this.user.count({ // 总数量
where: {
name: Like(%${query.keyWord}%)
},
})
return {
深知大多数步伐员,想要提升技能,往往是自己探索发展,但自己不成体系的自学结果低效又漫长,而且极易遇到天花板技术停滞不前!



既有适合小白学习的零根本资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部门目录截图出来,全套包含大厂面经、学习条记、源码讲义、实战项目、大纲门路、讲解视频,并且后续会持续更新
需要这份系统化的资料的朋侪,可以戳这里获取
where: {
name: Like(%${query.keyWord}%) //模糊查询
},
select:[‘id’,‘name’], // 指定查询出来的字段
order: {
id: “DESC” // 倒序
},
skip: (query.page - 1)* query.pageSize, // skip跳过,从0开始
take:query.pageSize, // 需要展示的每页的数量
})
const total = await this.user.count({ // 总数量
where: {
name: Like(%${query.keyWord}%)
},
})
return {
深知大多数步伐员,想要提升技能,往往是自己探索发展,但自己不成体系的自学结果低效又漫长,而且极易遇到天花板技术停滞不前!

[外链图片转存中…(img-GSInbE2n-1715631105927)]
[外链图片转存中…(img-KTaI3eXo-1715631105927)]
既有适合小白学习的零根本资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!
由于文件比较多,这里只是将部门目录截图出来,全套包含大厂面经、学习条记、源码讲义、实战项目、大纲门路、讲解视频,并且后续会持续更新
需要这份系统化的资料的朋侪,可以戳这里获取

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

东湖之滨

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表