首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

鸿蒙Harmony(八)ArkUI--状态管理器之@Prop 和 @Link & @Provide 和 @Consume & @ObjectLink 和@Observed

  • 24-03-18 03:42
  • 3029
  • 13183
blog.csdn.net

承接@State的状态管理器学习

@Prop 和 @Link

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

@Prop@Link
同步类型单向同步双向同步
允许装饰的变量类型@Prop只支持string、number、boolean、enum类型 ,父组件对象类型,自组件对象属性不可以是数组、any父子类型一致:string、number、boolean,enum,object,class以及他们的数组。数组中元素增、删、替换会引起刷新。嵌套类型以及数组中的对象属性无法触发页面更新
初始化方式不允许子组件初始化父组件传递,禁止子组件初始化

代码示例

在这里插入图片描述

class Task {
  static id: number = 1
  name: string = '任务' + Task.id++
  isDone: boolean = false
}

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}

@Entry
@Component
struct TaskPage {
  // 总任务数量
  @State totalTask: number = 0
  @State finishTask: number = 0
  @State taskList: Array<Task> = []

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView({ totalTask: this.totalTask, finishTask: this.finishTask })
      // 2.任务列表  @Link 因为传递的是引用,必须使用$ 
      TaskListView({ totalTask: $totalTask, finishTask: $finishTask, taskList: $taskList })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  // @Prop:禁止初始化,单向变更,父组件变动,引起子组件刷新
  @Prop totalTask: number
  @Prop finishTask: number

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.finishTask, type: ProgressType.Ring, total: this.totalTask }).width(120)
        Text(this.finishTask + "/" + this.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Link totalTask: number
  @Link finishTask: number
  @Link taskList: Array<Task>

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            this.TaskItemView(item)
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder TaskItemView(task: Task) {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (task.isDone) {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(task.isDone).onChange((isChecked) => {
        task.isDone = isChecked
        this.finishTask = this.taskList.filter(item => item.isDone).length
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.finishTask = this.taskList.filter(item => item.isDone).length
      this.totalTask = this.taskList.length
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

将totalTask与finishedTask封装为对象依旧生效

class Task {
  static id: number = 1
  name: string = '任务' + Task.id++
  isDone: boolean = false
}

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}
// 封装为对象
class StatInfo {
  totalTask: number = 0
  finishTask: number = 0
}

@Entry
@Component
struct TaskPage {
  @State stat: StatInfo = new StatInfo()

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView({ totalTask: this.stat.totalTask, finishTask: this.stat.finishTask })
      // 2.任务列表  @Link 因为传递的是引用,必须使用$
      TaskListView({ stat: $stat })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  // @Prop:禁止初始化,单向变更,父组件变动,引起子组件刷新
  @Prop totalTask: number
  @Prop finishTask: number

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.finishTask, type: ProgressType.Ring, total: this.totalTask }).width(120)
        Text(this.finishTask + "/" + this.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Link stat: StatInfo
  @State taskList: Array<Task> = []

  handleStatistics() {
    this.stat.finishTask = this.taskList.filter(item => item.isDone).length
    this.stat.totalTask = this.taskList.length
  }

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.stat.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            this.TaskItemView(item)
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder TaskItemView(task: Task) {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (task.isDone) {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(task.isDone).onChange((isChecked) => {
        task.isDone = isChecked
        this.handleStatistics()
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.handleStatistics()
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

@Provide和@Consume

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

代码示例

class Task {
  static id: number = 1
  name: string = '任务' + Task.id++
  isDone: boolean = false
}

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}

class StatInfo {
  totalTask: number = 0
  finishTask: number = 0
}

@Entry
@Component
struct TaskPage {
  // @Provide 跨组件传递且被@Provide装饰器修饰的组件不需要被传递
  @Provide stat: StatInfo = new StatInfo()

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView()
      // 2.任务列表  @Link 因为传递的是引用,必须使用$
      TaskListView()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  @Consume stat: StatInfo

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.stat.finishTask, type: ProgressType.Ring, total: this.stat.totalTask }).width(120)
        Text(this.stat.finishTask + "/" + this.stat.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Consume stat: StatInfo
  @State taskList: Array<Task> = []

  handleStatistics() {
    this.stat.finishTask = this.taskList.filter(item => item.isDone).length
    this.stat.totalTask = this.taskList.length
  }

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.stat.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            this.TaskItemView(item)
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder TaskItemView(task: Task) {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (task.isDone) {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(task.isDone).onChange((isChecked) => {
        task.isDone = isChecked
        this.handleStatistics()
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.handleStatistics()
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

@ObjectLink 和@Observed

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

使用说明

1.嵌套对象使用@Observed 修饰,比如下面

@Observed
class Task {
  static id: number = 1
  name: string = '任务' + Task.id++
  isDone: boolean = false
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2.需要根据嵌套对象属性的变更而刷新视图的组件,通过传入参数且该参数需要被@ObjectLink装饰

@Component
struct TaskItemView {
  @ObjectLink task: Task
  onTaskChange: () => void

  build() {}
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

代码示例

实现点击后任务名称样式变更,如下图所示,

在这里插入图片描述

@Observed
class Task {
  static id: number = 1
  name: string = '任务' + Task.id++
  isDone: boolean = false
}

@Extend(Text) function finishedTask() {
  .decoration({ type: TextDecorationType.LineThrough })
  .fontColor('#B1B2B1')
}

@Styles function commonCardStyle() {
  .width('95%')
  .margin({ left: 10, right: 10, top: 10 })
  .borderRadius(20)
  .backgroundColor('#ffffff')
  .shadow({ radius: 6, color: '#1f000000', offsetX: 2, offsetY: 4 })
}

class StatInfo {
  totalTask: number = 0
  finishTask: number = 0
}

@Entry
@Component
struct TaskPage {
  // @Provide 跨组件传递且被@Provide装饰器修饰的组件不需要被传递
  @Provide stat: StatInfo = new StatInfo()

  build() {
    Column() {
      // 1.顶部任务统计部分
      TaskStatisticsView()
      // 2.任务列表  @Link 因为传递的是引用,必须使用$
      TaskListView()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#eeeeee')
    .justifyContent(FlexAlign.Start)
  }
}

@Component
struct TaskStatisticsView {
  @Consume stat: StatInfo

  build() {
    Row() {
      Text("任务进度:")
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ right: 40 })
      Stack() {
        Progress({ value: this.stat.finishTask, type: ProgressType.Ring, total: this.stat.totalTask }).width(120)
        Text(this.stat.finishTask + "/" + this.stat.totalTask)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
      }
    }.commonCardStyle()
    .height(200)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.Center)
  }
}

@Component
struct TaskListView {
  @Consume stat: StatInfo
  @State taskList: Array<Task> = []

  handleStatistics() {
    this.stat.finishTask = this.taskList.filter(item => item.isDone).length
    this.stat.totalTask = this.taskList.length
  }

  build() {
    Column() {
      // 2.新增任务
      Button("新增任务").onClick(() => {
        this.taskList.push(new Task())
        this.stat.totalTask = this.taskList.length
      }).width("60%")
        .margin({ top: 10 })
      // 3.任务列表
      List() {
        ForEach(this.taskList, (item, index) => {
          ListItem() {
            // 变更,传入函数引用
            TaskItemView({ task: item,onTaskChange:this.handleStatistics.bind(this) })
          }.swipeAction({ end: this.getDeleteButton(index) }) // 向左滑动,出现删除按钮
        })
      }.layoutWeight(1) // 高度权重
      .width('100%')
      .alignListItem(ListItemAlign.Center)
    }
  }

  @Builder getDeleteButton(index: number) {
    Button({ type: ButtonType.Circle }) {
      Image($r('app.media.del'))
    }
    .onClick(() => {
      this.taskList.splice(index, 1)
      this.handleStatistics()
    })
    .width(50)
    .height(50)
    .padding(10)
    .margin({ right: 5 })
    .backgroundColor('#ffffff')
  }
}

@Component
struct TaskItemView {
  @ObjectLink task: Task
  onTaskChange: () => void

  build() {
    Row() {
      // 这里不生效,原因:state 数组对象嵌套不刷新视图
      if (this.task.isDone) {
        Text(this.task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
          .finishedTask()
      } else {
        Text(this.task.name)
          .fontSize(15)
          .fontWeight(FontWeight.Bold)
          .margin({ right: 40 })
      }
      Checkbox()
        .select(this.task.isDone).onChange((isChecked) => {
        this.task.isDone = isChecked
        this.onTaskChange()
      })
    }.commonCardStyle()
    .height(100)
    .padding({ left: 20, right: 20 })
    .justifyContent(FlexAlign.SpaceBetween)
  }
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
注:本文转载自blog.csdn.net的danfengw的文章"https://danfeng.blog.csdn.net/article/details/135240309"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top