【HarmonyOS】ArkUI - 状态管理

在声明式 UI 中,是以状态驱动视图更新,如图1所示:
图1

图1

其中核心的概念就是状态(State)和视图(View):

  • 状态(State):指驱动视图更新的数据(被装饰器标记的变量)

    @Entry
    @Component
    struct Index {@State message: string = 'Hello World'build() {Column() {Text(this.message).fontSize(50).onClick(() => {this.message = 'Hello ArkTS'})}.width('100%').height('100%')}
    }
    

    Index 组件里定义了 message 变量,而 message 前面就加了 @State 装饰器,如果没有这个装饰器,message 就是一个普通的变量,但是呢,正是我们给它加上了 @State 装饰器,所以,它就变成了一个状态变量。

  • 视图(View):基于 UI 描述渲染得到的用户界面

    @Entry
    @Component
    struct Index {@State message: string = 'Hello World'build() {Column() {Text(this.message).fontSize(50).onClick(() => {this.message = 'Hello ArkTS'})}.width('100%').height('100%')}
    }
    

    build 函数内部就是 UI 的描述,我们这里就描述了一个列式的容器,容器里有一个普通的文本,文本的内容就是 message 的值,所以最终渲染出来的视图就是在屏幕上显示一个 Hello World。

视图渲染好了以后,用户就可以对视图中的页面元素产生交互,比如去触摸、点击、拖拽等事件。这些互动事件就有可能改变状态变量的值,比如说我们这个示例里,给 Text 绑定了一个点击事件,一旦用户点击,就会修改 message 的值,而在 ArkUI 的内部,有一种机制去监控状态变量的值,一旦发现发生了变更,就会触发视图的重新渲染,所以,按照我们这个示例来看,如果现在去点击这个 Hello World 文字,就会触发点击事件,修改 message 的值,把它变成 Hello ArkTS,而一旦这个变量值发生变更,视图重新渲染,于是,屏幕上显示的文字从 Hello World 变成 Hello ArkTS。

所以像这种状态视图之间相互作用的机制,我们就称之为状态管理机制。有了这种机制以后,我们将来开发的时候,不需要自己操作页面,只需要描述页面的结构,然后定义好对应的事件,在事件里面去操作状态,就可以了,这样每当用户去产生互动时,自然就会引起页面的动态刷新。所以一个动态页面就很容易的实现了。这也就是状态管理的好处。

一、@State 装饰器

  1. @State 装饰器标记的变量必须初始化,不能为空值。

    比如上面的示例代码,message 一声明,就给它初始化了一个 Hello World。

  2. @State 装饰器支持的类型是有限制的。

    支持 Object、class、string、number、boolean、enum 类型以及这些类型的数组。

    注:虽然以上这些类型都是允许的,但是有两个特殊场景:

    1. 嵌套类型:@State 修饰的变量是 Object,如果 Object 里面的属性发生了变更其实是能触发视图的更新,但是如果 Object 里面的某个属性它又是一个 Object,也就是 Object 套 Object,那就是嵌套类型,那么内部嵌套的那个 Object 它里面的属性再发生变更,就无法触发视图更新。

    2. 数组:数组中的元素不是简单类型,而是一个对象,那么对象里面的属性发生变更,同样无法触发视图更新。

二、@Prop 和 @Link 装饰器

  1. 首先看一段代码,这是实现任务统计的示例代码:

     // 任务类class Task {static id: number = 1// 任务名称name: string = `任务${Task.id++}`// 任务状态finished: boolean = false}// 统一的卡片样式@Styles function card() {.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })}// 任务完成样式@Extend(Text) function finishedTask() {.decoration({ type: TextDecorationType.LineThrough }).fontColor('#B2B2B1')}@Entry@Componentstruct PropPage {// 总任务数量@State totalTask: number = 0// 已完成任务数量@State finishTask: number = 0// 任务数组@State tasks: Task[] = []build() {Column({ space: 10 }) {// 任务进度卡片Row() {Text('任务进度:').fontSize(30).fontWeight(FontWeight.Bold)Stack() {Progress({value: this.finishTask,total: this.totalTask,type: ProgressType.Ring}).width(100)Row() {Text(this.finishTask.toString()).fontSize(24).fontColor('#0000FF')Text(' / ' + this.totalTask.toString()).fontSize(24)}}}.card().margin({ top: 20, bottom: 10 }).justifyContent(FlexAlign.SpaceEvenly)// 新增任务按钮Button('新增任务').width(200).margin({ top: 10 }).onClick(() => {// 新增任务数据this.tasks.push(new Task())// 更新任务总数量this.totalTask = this.tasks.length})// 任务列表List({ space: 10 }) {ForEach(this.tasks,(item: Task, index) => {ListItem() {Row() {Text(item.name).fontSize(20)Checkbox().select(item.finished).onChange(val => {// 更新当前任务状态item.finished = val// 更新已完成任务数量this.finishTask = this.tasks.filter(item => item.finished).length})}.card().justifyContent(FlexAlign.SpaceBetween)}.swipeAction({ end: this.DeleteButton(index) })})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}.width('100%').height('100%').backgroundColor('#F1F2F3')}@Builder DeleteButton(index: number) {Button() {Image($r('app.media.delete')).fillColor(Color.White).width(20)}.width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5).onClick(() => {this.tasks.splice(index, 1)this.totalTask = this.tasks.lengththis.finishTask = this.tasks.filter(item => item.finished).length})}}
    
  2. 概念

    当父子组件之间需要数据同步时,可以使用 @Prop 和 @Link 装饰器。

    Q:什么是父子组件?什么又是数据同步

    A:看上面这段示例代码,我们会发现代码是从上到下一股脑写的,写了上百行代码,整个代码的可读性是比较差的。要解决这个问题,可以把整个功能分成几个模块,然后按模块封装成一个一个的组件,这样在入口组件(@Entry)当中就不用写太多代码,而是去引用其他模块对应的组件。整个代码结构会更加清晰,复用性也会更好。所以,入口组件就是一个父组件,它引用了其他的组件,那么这些被引用的组件就是子组件。所以这时候组件之间就出现了这种引用关系,而组件之间引用的过程中可能就会有数据传递的需求。比如在父组件里定义了一些数据,然后在子组件里需要用,这时候就需要把父组件的数据传给子组件,单纯的传递还不够,每当数据发生变更,还要去通知子组件,这就叫数据同步。数据同步利用 @State 装饰器是实现不了的,那就需要用 @Prop 和 @Link 装饰器来实现。

  3. @Prop 和 @Link 装饰器对比

    @Prop@Link
    同步类型单向同步双向同步
    允许装饰的变量类型· 父子类型一致:string、number、boolean、enum
    · 父组件是对象类型,子组件是对象属性
    · 不可以是数组、any
    · 父子类型一致:string、number、boolean、enum、object、class,以及它们的数组
    · 数组中元素增、删、替换会引起刷新
    · 嵌套类型以及数组中的对象属性无法触发视图更新
    初始化方式允许子组件初始化父组件传递,禁止子组件初始化
  4. 使用 @Prop 对示例代码进行封装和改造

    假设父组件中的变量采用 @State 装饰器,与之对应的子组件采用 @Prop 装饰器,那这时候就可以实现单项同步,当父组件对 @State 装饰的变量进行任意的修改时,就会立刻把这个数据传递给子组件,但反过来,子组件如果对这个数据进行了修改,是不会反向传递到父组件那里。所以,这种同步被称之为单向同步。实现原理就是拷贝

     ...@Entry@Componentstruct PropPage {// 总任务数量@State totalTask: number = 0// 已完成任务数量@State finishTask: number = 0// 任务数组@State tasks: Task[] = []build() {Column({ space: 10 }) {// 任务进度卡片TaskStatistics({ finishTask: this.finishTask, totalTask: this.totalTask })...}.width('100%').height('100%').backgroundColor('#F1F2F3')}...}@Componentstruct TaskStatistics {@Prop finishTask: number@Prop totalTask: number...}
    
  5. 使用 @Link 对示例代码进行封装和改造

    假设父组件中的变量采用 @State 装饰器,与之对应的子组件采用 @Link 装饰器,此时就是双向同步,当父组件对 @State 装饰的变量进行任意的修改时,就会立刻把这个数据传递给子组件,反过来,子组件如果对这个数据进行了修改,也会把这个数据传递给父组件。所以,这种同步被称之为双向同步。实现原理就是引用

     ...@Entry@Componentstruct PropPage {// 总任务数量@State totalTask: number = 0// 已完成任务数量@State finishTask: number = 0build() {Column({ space: 10 }) {// 任务进度卡片...// 任务列表TaskList({ finishTask: $finishTask, totalTask: $totalTask })}.width('100%').height('100%').backgroundColor('#F1F2F3')}}...@Componentstruct TaskList {// 总任务数量@Link totalTask: number// 已完成任务数量@Link finishTask: number// 任务数组@State tasks: Task[] = []...}
    
  6. 使用数组对示例代码进行封装和改造

     ...// 任务统计信息class StatisticsInfo {totalTask: number = 0finishTask: number = 0}@Entry@Componentstruct PropPage {// 任务统计信息@State info: StatisticsInfo = new StatisticsInfo()build() {Column({ space: 10 }) {// 任务进度卡片TaskStatistics({ finishTask: this.info.finishTask, totalTask: this.info.totalTask })// 任务列表TaskList({ info: $info })}.width('100%').height('100%').backgroundColor('#F1F2F3')}}@Componentstruct TaskStatistics {@Prop finishTask: number@Prop totalTask: number...}@Componentstruct TaskList {@Link info: StatisticsInfo...}
    

    结论:@Prop 不支持对象类型,@Link 支持对象类型。@Prop 和 @Link 该怎么选?如果子组件拿到父组件的值以后,只是用来展示,不做修改,用 @Prop,如果子组件需要修改父组件的值,用 @Link。

四、@Provide 和 @Consume

@Provide 和 @Consume 可以跨组件提供类似于 @State 和 @Link 的双向同步。

使用 @Provide@Consume 对示例代码进行封装和改造:

...// 任务统计信息
class StatisticsInfo {totalTask: number = 0finishTask: number = 0
}@Entry
@Component
struct PropPage {// 任务统计信息@Provide info: StatisticsInfo = new StatisticsInfo()build() {Column({ space: 10 }) {// 任务进度卡片TaskStatistics()// 任务列表TaskList()}.width('100%').height('100%').backgroundColor('#F1F2F3')}
}@Component
struct TaskStatistics {@Consume info: StatisticsInfo...
}@Component
struct TaskList {@Consume info: StatisticsInfo...
}

结论:@Provide@Consume 不需要显示的传参,内部会帮你去实现,但是代价是资源上面的损耗,所以,多数情况下,能用 @State@Prop@Link 就不要用 @Provide@Consume 了,除非是跨组件那种的场景。

五、@Observed 和 @ObjectLink

作用:@Observed 和 @ObjectLink 装饰器用于在涉及嵌套对象数组元素为对象的场景中进行双向数据同步。

  1. 嵌套对象

     class Person {name: stringage: numberfriend: Personconstructor(name: string, age: number, friend?: Person) {this.name = namethis.age = agethis.friend = friend}}
    
     @Entry@Componentstruct Parent {@State p: Person = new Person('Xxx', 20, new Person('Yyy', 20))build() {Column() {Text(`${this.p.friend.name} : ${this.p.friend.age},`).onClick(() => this.p.friend.age++)}}}
    

    通过上面这两段代码可以发现 Xxx 这个对象持有了 Yyy 对象,这就是嵌套对象。利用 Text 去渲染 Xxx 的 Friend 的 name 和 age,当发生点击事件时,去修改 Yyy 的 age,但是我们知道嵌套对象它的属性变更是无法被感知到,因此就无法触发视图的更新。要解决这个问题,需要做两件事:

    (1)需要给嵌套对象它所对应的类型上面加上 @Observed 装饰器

     @Observedclass Person {...}
    

    (2)需要给嵌套对象内部的对象加上 @ObjectLink 装饰器

     @Componentstruct Child {@ObjectLink p: Personbuild() {Column() {Text(`${this.p.name} : ${this.p.age}`)}}}@Entry@Componentstruct Parent {@State p: Person = new Person('Xxx', 20, new Person('Yyy', 20))build() {Column() {Child({ p: this.p.friend }).onClick(() => this.p.friend.age++)}}}
    
  2. 数组元素为对象

     @Observedclass Person {name: stringage: numberfriend: Personconstructor(name: string, age: number, friend?: Person) {this.name = namethis.age = agethis.friend = friend}}@Componentstruct Child {@ObjectLink p: Personbuild() {Column() {Text(`${this.p.name} : ${this.p.age}`)}}}@Entry@Componentstruct Parent {@State p: Person = new Person('Xxx', 20, new Person('Yyy', 20))@State ps: Person[] = [new Person('Aaa', 20), new Person('Bbb', 20)]build() {Column() {Child({ p: this.p.friend }).onClick(() => this.p.friend.age++)Text('==== 朋友列表 ====')ForEach(this.ps,p => {Child({ p: p }).onClick(() => p.age++)})}}}
    

    只要有了 @Observed,然后传递子组件的属性时,加上 @ObjectLink,那么,也能够触发视图的更新了。

  3. 示例代码

     // 任务类@Observedclass Task {static id: number = 1// 任务名称name: string = `任务${Task.id++}`// 任务状态finished: boolean = false}// 统一的卡片样式@Styles function card() {.width('95%').padding(20).backgroundColor(Color.White).borderRadius(15).shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })}// 任务完成样式@Extend(Text) function finishedTask() {.decoration({ type: TextDecorationType.LineThrough }).fontColor('#B2B2B1')}// 任务统计信息class StatisticsInfo {totalTask: number = 0finishTask: number = 0}@Entry@Componentstruct PropPage {// 任务统计信息@Provide info: StatisticsInfo = new StatisticsInfo()build() {Column({ space: 10 }) {// 任务进度卡片TaskStatistics()// 任务列表TaskList()}.width('100%').height('100%').backgroundColor('#F1F2F3')}}@Componentstruct TaskStatistics {@Consume info: StatisticsInfobuild() {Row() {Text('任务进度:').fontSize(30).fontWeight(FontWeight.Bold)Stack() {Progress({value: this.info.finishTask,total: this.info.totalTask,type: ProgressType.Ring}).width(100)Row() {Text(this.info.finishTask.toString()).fontSize(24).fontColor('#0000FF')Text(' / ' + this.info.totalTask.toString()).fontSize(24)}}}.card().margin({ top: 20, bottom: 10 }).justifyContent(FlexAlign.SpaceEvenly)}}@Componentstruct TaskList {@Consume info: StatisticsInfo// 任务数组@State tasks: Task[] = []handleTaskChange() {// 更新任务总数量this.info.totalTask = this.tasks.length// 更新已完成任务数量this.info.finishTask = this.tasks.filter(item => item.finished).length}build() {Column() {// 新增任务按钮Button('新增任务').width(200).margin({ top: 10, bottom: 10 }).onClick(() => {// 新增任务数据this.tasks.push(new Task())// 更新任务总数量this.handleTaskChange()})// 任务列表List({ space: 10 }) {ForEach(this.tasks,(item: Task, index) => {ListItem() {TaskItem({ item: item, onTaskChange: this.handleTaskChange.bind(this) })}.swipeAction({ end: this.DeleteButton(index) })})}.width('100%').layoutWeight(1).alignListItem(ListItemAlign.Center)}}@Builder DeleteButton(index: number) {Button() {Image($r('app.media.delete')).fillColor(Color.White).width(20)}.width(40).height(40).type(ButtonType.Circle).backgroundColor(Color.Red).margin(5).onClick(() => {this.tasks.splice(index, 1)this.handleTaskChange()})}}@Componentstruct TaskItem {@ObjectLink item: TaskonTaskChange: () => voidbuild() {Row() {if (this.item.finished) {Text(this.item.name).finishedTask()} else {Text(this.item.name)}Checkbox().select(this.item.finished).onChange(val => {// 更新当前任务状态this.item.finished = val// 更新已完成任务数量this.onTaskChange()})}.card().justifyContent(FlexAlign.SpaceBetween)}}
    
  4. 运行效果,如图2所示:
    图2

    图2

  5. 总结

    @Observed 和 @ObjectLink 主要用来解决嵌套对象里面,对象属性变更无法触发数组刷新和数组里的元素式对象属性变更无法触发视图更新的问题。解决方案是给对象上面添加 @Observed 装饰器,同时给嵌套的对象或数组元素对象的变量上加 @ObjectLink 装饰器;当子组件调用父组件方法,我们的办法是把父组件的方法作为参数传递进来,但是传递过程中会有 this 的丢失,解决办法是传递这个函数过程当中,用 bind 把这个 this 绑定进去。

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

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

相关文章

【AIGC工具】MonicAi — 可定制的AI学习工具

这是一个集成了多种AI功能的学习平台。Monic.ai提供了诸如测验生成器、闪卡制作器、摘要生成器、与AI对话等功能,旨在帮助学生、教师和专业人士更高效地学习和提升技能。以下是关于Monic.ai的一些亮点: 测验制作器(Quiz maker):您可以轻松地从学习材料中创建测验,以测试您…

第十一届蓝桥杯大赛第二场省赛试题 CC++ 研究生组-子串分值和

solution1&#xff08;通过40%&#xff09; 依次求子串并统计出现过的字母个数 #include<iostream> #include<string> #include<set> using namespace std; int main(){string s, subs;cin >> s;int len s.size(), ans 0;for(int j 1; j < len…

分布式组件 gateway 网关

1.简介 网关作为流量的入口&#xff0c;常用功能包括路由转发、权限校验、限流控制。 spring:cloud:gateway:routes:- id: test_routeuri: http://www.baidu.compredicates: #访问的路径就是 如果是/hello?urlbaidu 就转发到 https://www.baidu.com/hello?urlbaidu- Query…

【LabVIEW FPGA入门】FPGA寄存器(Register)

当您需要从多个时钟域或设计的不同部分访问数据&#xff0c;并且需要编写可重复使用的代码时&#xff0c;可使用寄存器项来存储数据。与 FIFO 相比&#xff0c;寄存器项消耗的 FPGA 逻辑资源更少&#xff0c;而且不消耗块存储器&#xff0c;而块存储器是最有限的 FPGA 资源类型…

2024阿里云2核2G服务器租用价格99元和61元一年

阿里云2核2G服务器配置优惠价格61元一年和99元一年&#xff0c;61元是轻量应用服务器2核2G3M带宽、50G高效云盘&#xff1b;99元服务器是ECS云服务器经济型e实例ecs.e-c1m1.large&#xff0c;2核2G、3M固定带宽、40G ESSD entry系统盘&#xff0c;阿里云活动链接 aliyunfuwuqi.…

微光图像增强算法学习记录(一)

微光图像增强&#xff08;LLIE&#xff09;旨在恢复照明并提高微光图像的可见性&#xff0c;本文对阅读的文献进行记录和分享&#xff0c;帮助回顾和大家建立学习资料。 文献一摘要及前沿摘选主要贡献网络结构实验结论 文献二摘要 文献三摘要主要贡献网络架构实验 文献四摘要实…

THINKPHP仿Word 统计字数的方法

项目需要根据文章字数自动统计字数&#xff0c;该方法和 word大概 一致&#xff0c; word 统计字数&#xff0c;相连的英文和数字&#xff08;半角&#xff09;会计算成一个字&#xff0c;如 good123就会统计成一个字&#xff0c;一下子也不知道该如何进行计算。 示例 functio…

机器学习K-means算法

K-Means 算法&#xff08;K-Means算法、K-Means 中心值计算、K-Means 距离计算公式、K-Means 算法迭代步骤、K-Means算法实例&#xff09; 问题引入 给你如下两种图片&#xff0c;快读回答2个问题&#xff0c;问 图1 中有几类五谷杂粮&#xff1f;问 图2 中有几类五谷杂粮&…

linux源配置:ubuntu、centos;lspci与lsmod命令区别

1、ubuntu源配置 1&#xff09;先查电脑版本型号: lsb_release -c2&#xff09;再编辑源更新&#xff0c;源要与上面型号对应 参考&#xff1a;https://midoq.github.io/2022/05/30/Ubuntu20-04%E6%9B%B4%E6%8D%A2%E5%9B%BD%E5%86%85%E9%95%9C%E5%83%8F%E6%BA%90/ /etc/apt/…

Scala第十一章节(正则表达式和异常处理)

4. 正则表达式 4.1 概述 所谓的正则表达式指的是正确的,符合特定规则的式子, 它是一门独立的语言, 并且能被兼容到绝大多数的编程语言中。在scala中, 可以很方便地使用正则表达式来匹配数据。具体如下: Scala中提供了Regex类来定义正则表达式.要构造一个Regex对象&#xff0…

基于SpringBoot+MyBatis框架的智慧生活商城系统的设计与实现(源码+LW+部署+讲解)

目录 前言 需求分析 可行性分析 技术实现 后端框架&#xff1a;Spring Boot 持久层框架&#xff1a;MyBatis 前端框架&#xff1a;Vue.js 数据库&#xff1a;MySQL 功能介绍 前台功能拓展 商品详情单管理 个人中心 秒杀活动 推荐系统 评论与评分系统 后台功能拓…

Rust 函数体内能定义数据类型或者做其他什么事情吗?

一、可以在函数体内定义数据类型吗&#xff1f; 在 Rust 中&#xff0c;你不能直接在函数体内定义新的数据类型&#xff08;如结构体或枚举&#xff09;。数据类型必须在模块或块的作用域内定义&#xff0c;这通常是在函数外部。然而&#xff0c;你可以在函数体内定义新的类型…

分布式之网关介绍

一、网关简介 1、网关背景 由于微服务“各自为政的特性”使微服务的使用非常麻烦。通常公司会有一个“前台小姐姐”作为统一入口&#xff0c;这就是网关 2、网关作用 统一入口&#xff1a;为服务提供一个唯一的入口&#xff0c;网关起到外部和内部隔离的作用&#xff0c; 保…

Day31 贪心算法

Day31 贪心算法 455.分发饼干 我的思路&#xff1a; 小孩数组g指针一直前移&#xff0c;只有饼干数组s满足条件时&#xff0c;才前移&#xff0c;并且更新num 解答&#xff1a; class Solution {public int findContentChildren(int[] g, int[] s) {Arrays.sort(g);Arrays.…

【Golang星辰图】创造美丽图表,洞察数据:解析Go语言中的数据可视化和数据分析库

解锁数据的力量&#xff1a;深入研究Go语言中的数据可视化和数据分析库 前言 本文将介绍Go语言中几个优秀的数据可视化和数据分析库&#xff0c;以帮助开发者更好地处理和分析数据。这些库提供了丰富的功能和工具&#xff0c;可用于创建漂亮的可视化图表、进行数值计算和数据…

阿里云幻兽帕鲁4核16G和8核32G服务器优惠价格

2024阿里云幻兽帕鲁专用服务器价格表&#xff1a;4核16G幻兽帕鲁专用服务器26元一个月、149元半年&#xff0c;默认10M公网带宽&#xff0c;8核32G幻兽帕鲁服务器10M带宽价格90元1个月、271元3个月。阿里云提供的Palworld服务器是ECS经济型e实例&#xff0c;CPU采用Intel Xeon …

C#,图论与图算法,用于检查给定图是否为欧拉图(Eulerian Graph)的算法与源程序

1 欧拉图 欧拉图是指通过图(无向图或有向图)中所有边且每边仅通过一次通路, 相应的回路称为欧拉回路。具有欧拉回路的图称为欧拉图(Euler Graph), 具有欧拉通路而无欧拉回路的图称为半欧拉图。 对欧拉图的一个现代扩展是蜘蛛图,它向欧拉图增加了可以连接的存在点。 这给…

AJAX-综合

文章目录 同步代码和异步代码回调函数地狱解决回调函数地狱Promise-链式调用async函数和awaitasync函数和await-捕获错误 事件循环宏任务与微任务Promise.all静态方法 同步代码和异步代码 同步代码&#xff1a;逐步执行&#xff0c;需原地等待结果后&#xff0c;才继续向下执行…

英语面试研究

系统架构师面试常用英语词汇和表达详细总结 系统架构师的面试通常会涉及到一些专业的英语词汇和表达&#xff0c;这些词汇和表达有助于更准确地描述架构设计、技术选型、系统优化等方面的内容。以下是一些常用的英语词汇和表达&#xff0c;以及它们在面试中的潜在用途&#xf…

阿里云效流水线—发布公用jar到Maven私仓

后端项目发布 1.选择流水线 2.新建流水线 3.选择模板 4.选择代码仓库 5.调整构建命令 添加mvn install 重新构建项目 6.添加镜像 在wms-app目录下新建Dockerfile文件(Dockerfile文件名中的D一定要是大写的&#xff09;文件&#xff0c;重新推送项目 #基础镜像 FROM openjd…