1、一多开发模式简介
①什么是“一多开发模式”
答案:
一多开发模式指的是:一套工程代码,一次开发上架,多段按需部署,本质上是为了让开发者快速高效的开发支持多端设备形态的应用。
两个明显要解决的问题:1、不同设备之间的UI如何适配;2、不同设备之间的系统能力如何适配。
②一多下的页面开发适配
1. 自适应布局
答案:针对常见的开发场景,方舟开发框架提炼了七种自适应布局能力,这些布局可以独立使用,也可多种布局叠加使用
能力类别 | 使用场景 | 实现方式 |
拉伸能力 | 容器组件尺寸发生变化时,增加或减小的空间全部分配给容器组件内指定区域 | Flex布局的flexGrow和flexShrink属性 |
均分能力 | 容器组件尺寸发生变化时,增加或减小的空间均匀分配给容器组件内所有空白区域 | Row组件、Column组件或Flex组件的justifyContent属性设置为FlexAlign.SpaceEvenly |
占比能力 | 子组件的宽或高按照预设的比例,随容器组件发生变化 | 基于通用属性的两种实现方式:
|
缩放能力 | 子组件的宽高按照预设的比例,随容器组件发生变化,且变化过程中子组件的宽高比不变 | 布局约束的aspectRatio属性 |
延伸能力 | 容器组件内的子组件,按照其在列表中的先后顺序,随容器组件尺寸变化显示或隐藏 | 基于容器组件的两种实现方式: |
隐藏能力 | 容器组件内的子组件,按照其预设的显示优先级,随容器组件尺寸变化显示或隐藏。相同显示优先级的子组件同时显示或隐藏 | 布局约束的displayPriority属性 |
折行能力 | 容器组件尺寸发生变化时,如果布局方向尺寸不足以显示完整内容,自动换行 | Flex组件的wrap属性设置为FlexWrap.Wrap |
2. 响应式布局
答案:自适应布局可以保证窗口在一定范围内变化时,页面显示是正常的,但是如果窗口尺寸变化比较大的时候,仅依赖自适应布局有可能出现图片异常放大或者页面内容稀疏、留白过多等问题,此时就需要结合响应式布局调整页面结构。
响应式布局能力 | 简介 |
断点 | 将窗口宽度划分为不同的范围(即断点),监听窗口尺寸变化,当断点改变时同步调整页面布局。 |
媒体查询 | 媒体查询支持监听窗口宽度、横竖屏、深浅色、设备类型等多种媒体特征,当媒体特征发生改变时同步调整页面布局。 |
栅格布局 | 栅格组件将其所在的区域划分为有规律的多列,通过调整不同断点下的栅格组件的参数以及其子组件占据的列数等,实现不同的布局效果。 |
2、一多下的系统能力适配(了解)
①一个前提
功能开发的适配主要体现在需要适配不同范类的应用,比如既要适配手机和平板,也需要适配智能穿戴设备,如果是同泛类产品,系统能力一致,无需考虑多设备上应用功能开发的差异,我们的美寇商城需要适配的是手机和Pad,属于同泛类产品,无需考虑功能开发的差异。以下是常见类型分类:
- 默认设备(一般为手机)、平板
- 车机、智慧屏
- 智能穿戴
②什么是系统能力
系统能力(即SystemCapability,缩写为SysCap)指操作系统中每一个相对独立的特性,如蓝牙,WIFI,NFC,摄像头等,都是系统能力之一。每个系统能力对应多个API,随着目标设备是否支持该系统能力共同存在或消失。
③如何适配系统能力
方法1: 使用canUse接口判断设备是否支持某系统能力
if (canIUse("SystemCapability.Communication.NFC.Core")) {
console.log("该设备支持SystemCapability.Communication.NFC.Core")
} else {
console.log("该设备不支持SystemCapability.Communication.NFC.Core")
}
方法2:通过import动态导入,配合try/catch
import controller from '@ohos.nfc.controller'
try {
controller.enableNfc()
console.log("controller enableNfc success")
} catch (busiError) {
console.log("controller enableNfc busiError: " + busiError)
}
注意:如果某系统能力是应用运行必须的,则要将其写入到应用的要求能力集中,以确保应用不会分发和安装到不符合要求的设备上。如果某系统能力不是应用运行必须的,则可以在运行时做动态判断,这样可以最大程度扩大应用的适用范围。
3、一多下的工程代码结构
一多模式下,官方推荐在开发过程中采用"三层工程架构",其实就是把项目拆分成不同类型的模块,再通过模块之间的引用组合,最终实现应用功能,拆分规范如下:
- common(公共能力层):用于存放公共基础能力合集,比如工具库,公共配置等
- features(基础特性层):用于存放应用中相对独立的各个功能的UI以及业务逻辑实现
- product(产品定制层):用于针对不同设备形态进行功能和特性集成,作为应用入口
- common设计为har包,新建module时选择
Static Library
, 内部存放全局通用的工具函数,公共配置等 - feature设计为har包,新建module时选择
Static Library
, 内部存放相对独立的业务单元,比如购物车
、我的
、分类
、Home
,也就是首页底部Tab切换时的四个核心业务模块 - product为产品层,里面放置phone模块,也就是入口模块,在phone中我们放置入口ability和所有页面级别的组件
以上就是我们本次项目的"三层工程架构" 的基础约定,他们之间存在一定的依赖关系,核心依赖关系如下图所示:
4、环境和通用工具配置
①模块依赖安装使用
模块之间不是孤立的,是可以互相依赖的,如果互相依赖,需要在模块下的oh-package.json5
文件中添加依赖并安装,安装之后才能使用对应模块, 比如phone依赖了features中的所有模块和common模块,可以进行如下配置:
{
"license": "",
"devDependencies": {},
"author": "",
"name": "phone",
"description": "Please describe the basic information.",
"main": "",
"version": "1.0.0",
"dependencies": {
"@meikou/My": "file:../../features/My",
"@meikou/ShopCart": "file:../../features/ShopCart",
"@meikou/common": "file:../../common",
"@meikou/home": "file:../../features/Home",
"@meikou/category":"file:../../features/Category"
}
}
注:key为使用模块时的名字,它只是一个标识,保持规范即可,value为模块的实际地址,不能写错,当我们在dependencies
选项中进行模块配置之后,注意IDE顶部会提示安装模块,点击Sync Now
即可完成安装,安装之后在phone模块中就可以使用 features和common了
②axios工具安装和基础配置
简介:Axios ,是一个基于 promise 的网络请求库,可以运行 node.js 和浏览器中。本库基于Axios 原库v1.3.4版本进行适配,使其可以运行在 OpenHarmony,并沿用其现有用法和特性。
1、安装axios:ohpm install @ohos/axios
2、实例化axios:
import axios, { InternalAxiosRequestConfig, AxiosError, AxiosResponse } from '@ohos/axios'
// 实例化 通用配置
const httpInstance = axios.create({
baseURL: 'https://meikou-api.itheima.net/',
timeout: 5000
})
// 拦截器配置
// 请求拦截器
// token配置等
httpInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
return config
}, (error: AxiosError) => {
return Promise.reject(error)
})
// 添加响应拦截器
// 错误统一处理等
httpInstance.interceptors.response.use((response: AxiosResponse) => {
return response
}, (error: AxiosError) => {
return Promise.reject(error)
})
export { httpInstance }
3、导出axios实例:export { httpInstance } from './src/main/ets/utils/Http'
4、开通网络权限:应用在发送网络请求之前,需要先在module.json5
配置文件中声明网络权限,否则无法发起请求
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
5、示例:
// 基于类型的请求使用方式
const res = await httpInstance.request<null,AxiosResponse<HttpResType<Result[]>,null>,null>({
url:'/home/banner',
method:'GET'
})
5、规范化请求
上面我们简单对axios做了简单测试,存在一些问题,需要我们解决
- 请求直接放到了组件中进行,和组件耦合在一起,没有做统一管理
- 类型写法太过繁琐,使用不方便
①在apis中统一封装业务请求函数
新增apis
文件夹,新建一个banner.ets
业务接口文件,我们以获取Banner接口为例,进行业务接口函数封装:
import { httpInstance } from '@meikou/common'
import { AxiosResponse } from '@ohos/axios'
// 业务接口通用返回类型
interface HttpResType<T> {
code: string
msg: string
result: T
}
// 业务接口特有返回类型
interface Result {
hrefUrl: string;
id: string;
imgUrl: string;
type: string;
}
export function getBannerAPI(){
return httpInstance.request<null,AxiosResponse<HttpResType<Result>,null>,null>({
url:'/home/banner',
method:'GET'
})
}
业务组件中使用:
import { getBannerAPI } from '../apis/banner'
@Entry
@Component
struct Index {
build() {
Button('click').onClick(async ()=>{
// 基于类型的请求使用方式
const res = await getBannerAPI()
console.log('banner:',JSON.stringify(res.data))
})
}
}
②优化类型使用
当前问题:在业务接口函数封装的过程中,request方法传递的类型参数过于复杂,不够直观
解决办法:抽象一个单独的泛型函数,把复杂写法封装到内部,对外保留简单的API,使用样例如下所示:
fetchData<Result>({
url:'/home/banner',
method:'GET'
})
- 新增一个泛型函数fetchData,接收业务接口返回的具体类型传入
- fetchData函数接受一个和axios请求对象一样的参数
- fetchData函数返回值依旧是一个promise对象
封装如下:
import { httpInstance } from '@meikou/common'
import { AxiosRequestConfig, AxiosResponse } from '@ohos/axios'
// 业务接口通用返回类型
interface HttpResType<T> {
code: string
msg: string
result: T
}
// 业务接口特有返回类型
interface Result {
hrefUrl: string;
id: string;
imgUrl: string;
type: string;
}
// 抽象通用泛型
function fetchData<T>(reqData: AxiosRequestConfig):Promise<AxiosResponse<HttpResType<T>,null>>{
return httpInstance.request<null,AxiosResponse<HttpResType<T>,null>,null>(reqData)
}
export function getBannerAPI(){
return fetchData<Result>({
url:'/home/banner',
method:'GET'
})
}
③把fetchData放入common模块
通用类型HttpResType
和 fetchData
函数在很多个业务模块中都需要使用,所以我们把它俩也放置common中,统一导出fetchData函数即可,业务API中只需要引入fetchData使用
// 业务接口通用返回类型
interface HttpResType<T> {
code: string
msg: string
result: T
}
// 抽象通用泛型
function fetchData<T>(reqData: AxiosRequestConfig):Promise<AxiosResponse<HttpResType<T>,null>>{
return httpInstance.request<null,AxiosResponse<HttpResType<T>,null>,null>(reqData)
}
export { httpInstance,HttpResType,fetchData }
6、Logger工具配置
①安装到项目:
ohpm install @abner/log
②全局初始化:
为了让全局生效,可以在EntryAblility
中进行统一配置,这样所有的page都将可用