前端上手纯血鸿蒙开发初体验,鸿蒙相关语法跟TS是一样的,提供的包也是丰富,也就意味着需要了解熟悉的API也是很多,可以慢慢在开发过程中去熟悉你所需要的API运用,不然干看记不住也不知道实际如何使用。
在这开发过程中进行了分享记录开发过程,如何更快速的投入业务实现中,还有更多可拓展探讨的空间学习,以下是我在业务开发过程想要分享的内容。
类型问题
比起我们写TS,鸿蒙是强制类型约束提醒,只要声明都需要进行类型定义。可快速类型定义:
// 提示: Object literal must correspond to some explicitly declared class or interface (arkts-no-untyped-obj-literals) <ArkTSCheck>
const previewProfile = {
width: 1080, // 根据实际设备调整
height: 1920 // 建议使用设备支持的分辨率
};
// 修改后
const previewProfile:Object = Object({
width: 1080, // 根据实际设备调整
height: 1920 // 建议使用设备支持的分辨率
});
全局事件通知实现
需求: 监听软件回到前后台,将事件传给到离线H5的包 (通过watch的方式实现)
// EntryAbility.ets - 获取到监听的方法
import { UIAbility } from '@kit.AbilityKit'
import { appStorage } from '../appStorage/AppStorage'
export default class EntryAbility extends UIAbility {
onForeground(): void {
// 应用从后台进入前台
// 通过事件中心触发全局事件
appStorage.set('isAppForeground',true)
}
}
// AppStorage.ets - 数据进行store化存储
class StorageList {
deviceList: ProductItem[] = []
resetPhones: ProductItem[] = []
set<T>(key:string, value:T){
AppStorage.setOrCreate(key, value)
}
}
export const appStorage = new StorageList()
// 通知信息监听 - 通过watch的方式去监听数据的变化
@StorageLink('isAppForeground') @Watch('handleForegroundChange') isForeground: boolean = false;
handleForegroundChange():void {
if(this.isForeground) {
this.webController.runJavaScript('onForegroundHarmonyToH5()')
}
}
内嵌离线包H5如何实现
技术点: 1. 如何让原有H5的window事件调用(摄像头等)生效 2. 桥接实现
import WebView from '@ohos.web.webview'
import { webview } from '@kit.ArkWeb';
import router from '@ohos.router'
import window from '@ohos.window'
import common from '@ohos.app.ability.common'
import { audio } from '@kit.AudioKit'
import deviceInfo from '@ohos.deviceInfo';
// 桥接的方法处理
webController: WebView.WebviewController = new WebView.WebviewController()
this.webController.runJavaScript(`changeVolume(${false})`)
Web({
// todo 切换成正式本地启动包
src: "",
controller: this.webController
})
// 这种方式开启才可以去调浏览器原生方法window.navigator等
.javaScriptAccess(true) // 启用 JavaScript
.domStorageAccess(true)
.fileAccess(true)
.mediaPlayGestureAccess(true)
.enableNativeMediaPlayer({
enable: true, // 启用本地媒体接管
shouldOverlay: false
})
// 调用摄像头需要监听对应的权限返回
.onPermissionRequest((event) => { // WebView 内部权限处理
if (event.request.getAccessibleResource().includes('TYPE_VIDEO_CAPTURE')) {
// 弹窗询问用户
promptAction.showDialog({
title: '权限申请',
message: '需要访问摄像头',
buttons: [{ text: '拒绝' , color: '#333'}, { text: '允许', color: '#333' }]
}).then((result) => {
if (result.index === 1) {
// 授予摄像头权限
event.request.grant(['TYPE_VIDEO_CAPTURE']);
} else {
event.request.deny();
}
});
}
})
.onControllerAttached(async () => {
// 加载离线包
this.webController.loadUrl(`resource://rawfile/cloud/index.html#/main?cloudId=${this.cloudId}&token=${this.token}&baseUrl=${this.baseUrl}&package=${this
.package}&devicePwd=${this.devicePwd}¤tPhoneName=${this.currentPhoneName}&userId=${this.userId}&channel=${getDefaultChannel()}`)
// 加载本地H5的项目可进行联调
// this.webController.loadUrl(`http://172.16.80.72:5173/#/main?cloudId=${this.cloudId}&token=${this.token}&baseUrl=${this.baseUrl}&package=${this
// .package}&devicePwd=${this.devicePwd}¤tPhoneName=${this.currentPhoneName}&userId=${this.userId}&channel=${getDefaultChannel()}`)
开发页面过程
页面开发预览
- 编辑器自带的预览器
- 设备下载的虚拟手机
- 物理机器。 这种需要在
文件 - 项目结构 - project - signing Config进行添加config配置,插入物理机器后可以直接生成的,后面打包成测试包、生产包也是需要在这对应进行配置
页面开发
import getSystemBarHeight from '../utils/GetSystemBarHeight'
import { NavHeader } from '../common/NavHeader'
import { deleteUserLoginOut } from '../apis/app'
import { showAlert } from '../common/AlertDialog'
import authStore from '../stores/auth/index'
import router from '@ohos.router'
import { authListData, authLIST } from '../views/AboutPage/utils/list'
import {PermissionUtil } from '../views/AboutPage/utils/uploadInformation'
import UserStore from '../stores/UserStore'
import { common } from '@kit.AbilityKit'
import { abilityAccessCtrl } from '@kit.AbilityKit';
import { camera } from '@kit.CameraKit';
@Entry
@Component
struct Auth {
// @Consume isLogin: boolean
@StorageProp('authListStr') authListStr: AuthOpenStatus = {}
@State authList:AuthOpenStatus = {}
private isManual: boolean = false; // 标记是否为用户操作
// 顶部导航栏高度
@State titleBarPadding:string = '0'
@State myAuthListData: authLIST[] = authListData
async aboutToAppear(): Promise<void> {
// 获取系统顶部导航栏的高度
this.titleBarPadding = getSystemBarHeight()
this.authList = await authStore.getAuthList() as AuthOpenStatus || {}
this.handleAuthOpen('privacy', true)
}
async handleAuthOpen(key: string, value: boolean):Promise<void> {
this.authList[key] = value
await authStore.setAuthList(this.authList)
}
build() {
Column() {
NavHeader({ title: '权限管理' })
Column(){
ForEach(this.myAuthListData, (item: authLIST, index: number) => {
Row() {
Text(item.text)
.fontSize(15)
.fontColor(Color.Black)
Toggle({ type: ToggleType.Switch, isOn: this.authList[item.type] })
.hitTestBehavior(HitTestMode.None) // 阻止默认事件 - Block
.onChange(async (isOn: boolean) => {
console.info('此处不会触发,因为已禁用点击事件');
return
})
}
.onClick(() => {
if (!this.authList[item.type] !== (item.type === 'privacy')) { // 仅在开启操作时触发弹窗
// 如果是退出登录
showAlert({
title: item.tip,
message: item.message,
autoCancel: false,
cancelBtnText: '确认',
confirmBtnText: '取消',
confirmFunc: () => {
if(item.type !== 'privacy') {
this.handleAuthOpen(item.type, false)
}
},
cancelFunc: async () => {
if(item.type === 'gps') {
let location = await PermissionUtil.request(
getContext(this) as common.UIAbilityContext,
['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']
);
if(location) { this.handleAuthOpen(item.type, true)}
}
if (item.type === 'camera') {
let cameraData = await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(this) as common.UIAbilityContext, ['ohos.permission.CAMERA']);
const grantCameraDataStatus = cameraData.authResults;
if (grantCameraDataStatus.every(status => status === 0)) {
this.handleAuthOpen(item.type, true)
}
}
if (item.type === 'micro'){
const microData = await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(this) as common.UIAbilityContext, ['ohos.permission.MICROPHONE']);
const grantMicroStatus = microData.authResults;
if (grantMicroStatus.every(status => status === 0)) {
this.handleAuthOpen(item.type, true)
}
}
if(item.type === 'privacy') {
// 退出登录
// ToDo: 埋点
await deleteUserLoginOut()
// localStore.clearUserInfo()
UserStore.clearUserInfo()
// 登陆成功后存储登录状态
await UserStore.setLoginStatus(false)
router.back()
// this.isLogin = false
}
},
})
}
if(this.authList[item.type]) {
this.handleAuthOpen(item.type, false)
}
})
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.padding({
top: '21vp',
bottom: '21vp',
left: '12vp',
right: '12vp'
})
.backgroundColor('#FFFFFF')
.margin({bottom: '20vp'})
})
}
.backgroundColor('#F9F9F9')
.height('100%')
.padding(16)
}
.height('100%')
.width('100%')
.padding({
top: this.titleBarPadding
})
}
}
工具介绍
运用插件CodeGenie,挺好用的,围绕都是鸿蒙的点去回答