一 项目结构 创建参考
- 文件夹普遍小写
- 文件普遍大写
js
---- src/main/ets
---- constants // 定义宏 枚举之类的
---------- PageConstants
---------- CommonConstants
---------- NetworkState
---- model // 定义模型
---- network // 网络
---- utils // 工具类
---- view // 定义模型
---------- components //UI组件
---------- pages // 页面
---- viewModel // 业务封装
二 签名报错处理
js
// 打开项目根目录的 `build-profile.json5` 文件 ⚠️ 注意不是src/main中的
{
"app": {
"signingConfigs": [], // 清空数组
"products": [
{
"name": "default",
// "signingConfig": "default", // 删除或注释
"targetSdkVersion": "5.1.1(19)",
"compatibleSdkVersion": "5.0.0(12)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"caseSensitiveCheck": true,
"useNormalizedOHMUrl": true
}
}
}
],
// ... 其他配置
}
}
三 修改项目签名
3.1 修改 bundleName**
打开工程根目录下 AppScope/app.json5 文件,修改 bundleName 字段值
js
{
"app": {
"bundleName": "com.example.myapp", // 修改为新的包名
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name"
}
}
3.2 清除签名配置
打开与 AppScope 同级目录下的项目级 build-profile.json5 文件,将 signingConfigs 字段清空
js
{
"app": {
"signingConfigs": [] // 清空签名配置,后续重新生成
}
}
3.3 清理与重新签名
- 在 DevEco Studio 中执行 Build → Clean Project,清理构建缓存6
- 删除真机/模拟器上已安装的旧包名应用6
- 关闭 IDE 后重新打开,重新配置自动签名6
- 重新编译运行,验证应用正常启动
四 写请求
- 两种写法在性能和线程行为上 完全等价:
- 函数可以隐示声明返回值
js
// 写法一:async/await(同步风格,可读性好)
async function fetchData() {
try {
const response = await getArticleList(pageIndex, cid)
return new BaseResult<BaseListBean<ArticleBean[]>>(NetworkState.SUCCESS, response)
} catch (error) {
return new BaseResult<BaseListBean<ArticleBean[]>>(NetworkState.ERROR, undefined, error as BusinessError)
}
}
// 写法二:Promise 链式调用(等价,只是写法不同)
// 注意三个return
function fetchData() {
return getArticleList(pageIndex, cid)
.then((response: BaseBean<BaseListBean<ArticleBean[]>>) => {
return new BaseResult<BaseListBean<ArticleBean[]>>(NetworkState.SUCCESS, response)
})
.catch((error: CommonType) => {
return new BaseResult<BaseListBean<ArticleBean[]>>(NetworkState.ERROR, undefined, error as BusinessError)
})
}
五 断言
undefined和null可以被断言为 任何类型。
六 类型判断
typeof 是一元运算符,用来判断一个值的数据类型,返回一个字符串。
| 值类型 | typeof 返回 | 示例 |
|---|---|---|
| 字符串 | 'string' |
typeof 'hello' |
| 数字 | 'number' |
typeof 123 |
| 布尔值 | 'boolean' |
typeof true |
| undefined | 'undefined' |
typeof undefined |
| 对象 | 'object' |
typeof {} |
| 数组 | 'object' |
typeof [] |
| null | 'object' |
typeof null ⚠️ 历史 bug |
| 函数 | 'function' |
typeof (()=>{}) |
| Symbol | 'symbol' |
typeof Symbol() |
| BigInt | 'bigint' |
typeof 123n |
js
if (typeof arr === 'object') {
console.log('arr 是对象');
}
if (typeof('嘻嘻') =='object') {
console.log('arr 是对象11');
} else {
console.log('嘻嘻是字符串');
}
七 返回 : payload is AxiosError<T, D>;
swift
// ts的语法
// 返回值 通过函数的真实返回类型推断
export function isAxiosError<T = any, D = any>(payload: any): payload is AxiosError<T, D>;
payload is AxiosError 不是返回值 是告诉开发者 这个方法是判断payload 是不是AxiosError错误的函数
八 layoutWeight
- 布局权重: 父组件高度,先减去内部子组件的固定高度,剩余的高度的百分比
- 同时设置固定长度和layoutWeight()权重, 权重生效

js
Row() {
// 权重1,占主轴剩余空间1/3 为:(100% - 30%) * 1/3
// ⚠️ width: '30%'被 .layoutWeight(1)覆盖了, 同时设置 权重生效
Text('layoutWeight(1)')
.size({ width: '30%', height: 110 }).backgroundColor(0xFFEFD5).textAlign(TextAlign.Center)
.layoutWeight(1)
// 权重2,占主轴剩余空间2/3 为:(100% - 30%) * 2/3
Text('layoutWeight(2)')
.size({ width: '30%', height: 110 }).backgroundColor(0xF5DEB3).textAlign(TextAlign.Center)
.layoutWeight(2)
// 未设置layoutWeight属性,组件按照自身尺寸渲染
Text('no layoutWeight')
.size({ width: '30%', height: 110 }).backgroundColor(0xD2B48C).textAlign(TextAlign.Center)
}
.size({ width: '90%', height: 140 })
.backgroundColor(0xAFEEEE)
九 布局
9.1 list
- 组件的子组件总高 小于 父组件高 组件上子组件自适应
如果List组件主轴方向没有设置尺寸,当List子组件主轴方向总尺寸小于List的父组件尺寸时,List主轴方向尺寸自动适应子组件的总尺寸。
- 组件的子组件总高 大于 父组件高 组件按父组件高适应 如果子组件主轴方向总尺寸超过List父组件尺寸时,List主轴方向尺寸适应List的父组件尺寸。
9.2 tabs
- TabContent组件不支持设置通用宽度属性,其宽度默认撑满Tabs父组件。
- TabContent组件不支持设置通用高度属性,其高度由Tabs父组件高度与TabBar组件高度决定。
十 引用传递
NewsData-master
js
// NewsList.ets 页面
@Component
export default struct NewsList {
@State @Watch('newCustom') newsModel: NewsModel = new NewsModel(); // 默认的总模型
newCustom() { // 被监听方法
// ......
}
build() {
Column() {
// ......
}
.width(Const.FULL_WIDTH) // 100%
.height(Const.FULL_HEIGHT)
.justifyContent(FlexAlign.Center)
.onTouch((event: TouchEvent | undefined) => { //监听手指在组件上
// 调用其他页面的公共方法
listTouchEvent(this.newsModel, event);
})
}
}
js
// PullDownRefresh.ets
export function listTouchEvent(that: NewsModel, event: TouchEvent) {
// 接收that
touchUpPullRefresh(that);
}
export function touchUpPullRefresh(that: NewsModel) {
// 网络请求
let self = that;
NewsViewModel.getNewsList(that.currentPage, that.pageSize, Const.GET_NEWS_LIST).then((data: NewsData[]) => {
self.newsData = data; // 重新赋值 非修改某属性
}
}
十一 三方库版本
^1.0.3 和 1.0.3 的核心区别在于:前者是 范围版本号 ,允许安装满足条件的更高版本;后者是 精确版本号,只安装指定版本。
oh-package-lock.json5 对比
js
- "@abner/refresh_v2@^1.0.3": "@abner/refresh_v2@1.0.3",
"packages": {
- "@abner/refresh_v2@1.0.3": {
- "version": "1.0.3",
- "integrity": "sha512-Zv9uXqkaZB1yY+aNOTh4cEEQCgc34EgKkzy6qTx3lCRM1wGmuEZfvuGEw2ql3bNb6nqHvbgt25LMVu5Fs6tyyw==",
- "resolved": "https://ohpm.openharmony.cn/ohpm/@abner/refresh_v2/-/refresh_v2-1.0.3.har",
"registryType": "ohpm"
},
js
+ "@abner/refresh_v2@^1.0.3": "@abner/refresh_v2@1.1.0",
"packages": {
+ "@abner/refresh_v2@1.1.0": {
"name": "@abner/refresh_v2",
+ "version": "1.1.0",
+ "integrity": "sha512-doLWKi0W0vnb459u+NKtpeBye+AY8VEQLVpLrd34HPfMZgm5gcH/0ANV1m09fTULlNk1Dfy+PpJdaa9p2RK2FQ==",
+ "resolved": "https://ohpm.openharmony.cn/ohpm/@abner/refresh_v2/-/refresh_v2-1.1.0.har",
"registryType": "ohpm"
},
十二: px & vp
| vp | Virtual Pixel | 虚拟像素,会根据屏幕像素密度自动缩放,1vp = 1px / 像素密度,适合做响应式布局 |
|---|---|---|
| px | Physical Pixel | 物理像素,屏幕上真实的像素点,不会自动缩放 |
在 ArkUI 中,当 padding、width、height、margin 等布局属性接收 number 类型时,默认单位就是 vp ,因此 10 等价于 10vp。
js
// 写法一:number 类型,默认单位 vp
.padding({ top: 10 }) // 等价于 10vp
// 写法二:string 类型,显式指定 vp
.padding({ top: '10vp' }) // 同样是 10vp
// 写法三:string 类型,显式指定 px
.padding({ top: '10px' }) // 10 物理像素,不会随屏幕密度缩放
// 写法四:string 类型,不指定单位,默认 vp
.padding({ top: '10' }) // 等价于 10vp
js
// 获取布局避让遮挡的区域, 底部导航条高度 并缓存 这里获取的就是 px
// 避让区 导航类型
let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR;
// 获取避让区导航
let avoidArea = windowClass.getWindowAvoidArea(type);
// 获取到导航条区域的高度 单位 px
let bottomRectHeight = avoidArea.bottomRect.height;
// 存储导航高度
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
kotlin
// 在组件中使用需要加上 px 否则会按照vp编译 产生偏差
.padding({ top: this.topRectHeight + 'px' }) //距离 顶部高度
十三 窗口全屏 & 避让区高度(状态栏/下导航) & 主题设置
js
// 主题配置
export class AppColors implements CustomColors {
// 1 文本主题颜色 包括font, input字体颜色
fontPrimary = Color.Red;
// 2 系统组件主题背景色 包括qrcode (感觉有点鸡肋)
backgroundPrimary: ResourceColor = $r('app.color.bg');
// 3 文档没写啥组件生效 大概是获取颜色用吧
compBackgroundPrimary: ResourceColor = $r('app.color.bg');
// 光标主题颜色 包括inpuit光标
brand: ResourceColor = $r('app.color.colorPrimary');
// 使用$r,让一级警示色在深色和浅色模式下,设置为不同的颜色
warning: ResourceColor = $r('app.color.start_window_background');
}
// warning补充说明
/** 深浅模式不同颜色
start_window_background 分别在
resources/base/element/color.json,
resources/dark/element/color.json设置颜色 所以切换深浅色模式可以变换颜色
*/
/** warning主题配置生效的组件有
TipsDialog 提示弹窗 -> (取消/确认 文本的颜色)
AlertDialog 警告弹窗 -> (文本的颜色)
CustomContentDialog 自定义内容弹窗 -> (文本的颜色)
Badge 徽标组件 -> (边框线颜色)
Button 按钮组件 -> (背景颜色)
*/
export class AppTheme implements CustomTheme {
colors?: CustomColors;
constructor(colors: CustomColors) {
this.colors = colors;
}
}
const BlueColorsTheme = new AppTheme(new AppColors());
onWindowStageCreate(windowStage: window.WindowStage): void {
// 获取应用主窗口
let windowClass: window.Window = windowStage.getMainWindowSync();
// 1. 设置窗口全屏
let isLayoutFullScreen = true;
windowClass.setWindowLayoutFullScreen(isLayoutFullScreen).then(() => {
console.info('Succeeded in setting the window layout to full-screen mode.');
}).catch((err: BusinessError) => {
console.error('Failed to set the window layout to full-screen mode. Cause:' + JSON.stringify(err));
});
// 2. 获取避让遮挡的区域
// 2.1 下导航高度并缓存
let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR; // 以导航条避让为例
let avoidArea = windowClass.getWindowAvoidArea(type);
let bottomRectHeight = avoidArea.bottomRect.height; // 获取到导航条区域的高度
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
// 2.2 状态栏高度并缓存
type = window.AvoidAreaType.TYPE_SYSTEM; // 以状态栏避让为例
avoidArea = windowClass.getWindowAvoidArea(type);
let topRectHeight = avoidArea.topRect.height; // 获取状态栏区域高度
AppStorage.setOrCreate('topRectHeight', topRectHeight);
// 2.3. 注册监听函数,动态获取避让区域数据(下导航 / 状态栏)
// 当用户折叠/展开导航条、切换横竖屏等场景下,避让区域会发生变化,需要实时更新全局状态
windowClass.on('avoidAreaChange', (data) => {
if (data.type === window.AvoidAreaType.TYPE_SYSTEM) { // 状态栏
let topRectHeight = data.area.topRect.height;
AppStorage.setOrCreate('topRectHeight', topRectHeight);
} else if (data.type == window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR) { // 下导航
let bottomRectHeight = data.area.bottomRect.height;
AppStorage.setOrCreate('bottomRectHeight', bottomRectHeight);
}
});
//3. 设置全局状态栏内容颜色, 可单独页面更改. 状态栏文字 字体
windowClass.setWindowSystemBarProperties({
statusBarContentColor: '#FFFFFF' // 白色
});
// 4. 设置应用自定义主题
ThemeControl.setDefaultTheme(BlueColorsTheme)
});
}
十四 配置应用 图标 & 名称
14.1 修改应用图标
js
// AppScope/resources/base/media/layered_image,json
// 修改图片地址
{
"layered-image":
{
"background" : "$media:background",
"foreground" : "$media:foreground_2"
}
}
14.2 修改应用名称
js
// 1 配置 $string:app_name
// AppScope/resources/base/element/string.json
{
"string": [
{
"name": "app_name",
"value": "testApp1"
}
]
}
// 2 配置应用名称
// AppScope/app.json5
{
"app": {
"bundleName": "com.testapp.test1",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"buildVersion": "1",
"icon": "$media:layered_image",
"label": "$string:app_name" // 配置应用名称
}
}
⚠️ module.json5 的优先级高于 app.json5,如果这里写错了会覆盖掉 app.json5 的配置
js
// entry/src/main/module.json5
{
"abilities": [
{
"label": "$string:EntryAbility_label"
}
]
}
十五 应用配置信息介绍
js
// AppScope/app.json5
{
"app": {
"bundleName": "com.testapp.test1", // 应用包名
"vendor": "example", // 开发组织名称
"versionCode": 1000000, // 上架的构建版本
"versionName": "1.0.0", // 应用版本号
"buildVersion": "1", // 没啥用
"icon": "$media:layered_image", // 应用图标
"label": "$string:app_name" // 应用名称
}
}
十六 定义函数参数
js
@Component
export struct comNavTitle {
// 定义函数参数
returnButtonClick: () => void = ()=>{ // ⚠️ 改成() => () 不行
HMRouterMgr.pop()
}
build(){
//......
}
}
十七 启动背景颜色及图标修改
js
// entry/src/main/module.json5
"abilities": [
{
"startWindowIcon": "$media:startIcon", // 启动图标
"startWindowBackground": "$color:start_window_background", // 启动背景色
}
]