Web组件
HarmonyOS 提供了一个 Web 组件,我们可以借助它在我们的应用中内嵌一个浏览器,为开发者提供页面加载、页面交互、页面调试等能力,下图是官网给出的介绍。
该组件的使用场景和功能不多,但常用的一些功能需求还都有,这个还挺好玩的,挺方便的,例如我们有一个集中的固定 Web 用户反馈系统,我们的很多产品都使用该网页反馈系统来接收用户的反馈,那么在 APP 中我们就可以简单方便的直接嵌入该网页。
typescript
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebPage {
webviewController: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Web({src:"https://juejin.cn/user/479835407267336",controller:this.webviewController})
// 允许缩放
.zoomAccess(true)
// 文本缩放
.textZoomAtio(150)
}
}
}
还可以进行本地 html 引入,想要执行页面中的 JavaScript 方法的话需要使用 runJavaScript 方法执行网页中的方法
typescript
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebPage {
webviewController: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Button()
.onClick(()=> {
this.webviewController.runJavaScript("testFunc()");
})
Web({src: $rawfile('index.html'),controller:this.webviewController})
}
}
}
或者将我们在 arkts 中写的方法注入到 html 网页中去执行:javaScriptProxy
再或者我们可以建立一个双方的数据通信通道:createWebMessagePorts
Web 组件支持很多事件,这些事件可以让我们的应用和网页进行更好的交互
例如:onConfirm、onConsole、onDownloadStart、onErrorReceive等等等等
有很多事件我就不一一列出了,甩个链接,我发现我是真的喜欢放链接,太懒了真的:更多事件查看事件列表
就 onConfirm 事件做出一个示例,主要是熟悉了解 HarmonyOS 中的各种开发场景
Web 组件支持 onConfirm 事件,当 html 网页中调用 confirm 告警方法的时候即会调用该方法,触发该回调,该回调又会接受一个event对象参数,包含了:
- url:当前显示弹窗的网页地址
- 弹窗中显示的信息
- 通知 Web 组件用户操作行为
这个事件的作用也就是用户在网页上进行了一些操作,我们需要弹出一个告示框对用户进行一个提示,这时需要我们的应用和 html网页建立一个连接,建立一个数据通信的栈道,拿到数据之后可以在应用中做出相应的操作,下面的代码则是使用 AlertDialog 警告弹框组件将网页上的标题通过弹框展现了出来
typescript
import web_webview from '@ohos.web.webview';
@Entry
@Component
struct WebPage {
webviewController: web_webview.WebviewController = new web_webview.WebviewController();
build() {
Column() {
Button("点我触发事件")
.onClick(()=> {
this.webviewController.runJavaScript("testFunc()");
})
Web({src: $rawfile('index.html'),controller:this.webviewController})
.onConfirm((event) => {
console.log("event.url:" + event.url);
console.log("event.message:" + event.message);
console.log("event.result:" + event.result);
AlertDialog.show({
title: 'title',
message: event.message
})
// 返回值为 true 的时候,可以调用系统的弹窗
return true;
})
}
}
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2 class="text">Hello World!</h2>
</body>
<script>
const testFunc = () => {
console.log(document.querySelector(".text").textContent);
confirm(document.querySelector(".text").textContent);
}
</script>
</html>
数据请求
我们下面会实现这样一个页面功能效果:点击上方按钮,实现数据请求,然后将数据渲染至页面上
获取到数据之后我们再使用弹窗将数据展现出来玩一玩
- 定义一个数据类
typescript
export class MyData {
id:number
address:string
}
- 数据请求(非封装)
有兴趣的朋友可以抽取可变参数进行 全局 Request 的封装
typescript
import http from '@ohos.net.http';
import { MyData } from './MyData'
export default async function getHttpData(): Promise<MyData[]> {
let dataList: MyData[] = []
let httpRequest = http.createHttp();
let response = httpRequest.request(
"https://保密连接",
{
method: http.RequestMethod.GET,
header: {
'Content-Type': 'application/json'
},
expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型
}
);
// 使用await和async,等待请求完成处理数据后返回
await response.then((data) => {
if (data.responseCode == 200) {
// 处理返回结果
const response = data.result + "";
const res = JSON.parse(response).data
for (let i = 0; i < res.length; i++) {
let item = res[i];
dataList.push({
address: item.address,
id:item.id
});
}
} else {
}
}).catch((err) => {
console.info('error:' + JSON.stringify(err));
})
return dataList;
}
- 拿到数据渲染页面
typescript
import getHttpData from './request';
import { MyData } from './MyData'
@Entry
@Component
struct WebPage {
@State data:Array<MyData> = [];
@State rangeList:Array<string> = [];
@State select:number = 0;
@State selectText:string = ""
build() {
Column() {
Button("点击调用请求")
.onClick(()=> {
getHttpData().then((res=> {
this.data = res;
console.log(JSON.stringify(this.data));
for (let i = 0; i < this.data.length; i++) {
this.rangeList.push(this.data[i].address);
}
}))
}
)
Row() {
ForEach(this.data,(item:MyData)=> {
Text(item.address)
.fontSize(32)
.fontColor("#ff197c70")
.fontWeight(FontWeight.Bold)
},item=> item.id)
}
.width("90%")
.margin(32)
.justifyContent(FlexAlign.SpaceBetween)
Text(`您选择的城市为 :${this.selectText}`).margin(64)
Button("选择你的所在地")
.onClick(async ()=> {
await TextPickerDialog.show({
range:this.rangeList,
selected:this.select,
onAccept: (value: TextPickerResult) => {
// 设置select为按下确定按钮时候的选中项index,这样当弹窗再次弹出时显示选中的是上一次确定的选项
this.select = value.index;
this.selectText = value.value;
console.info("TextPickerDialog:onAccept()" + JSON.stringify(value));
},
onCancel: () => {
console.info("TextPickerDialog:onCancel()");
},
onChange: (value: TextPickerResult) => {
console.info("TextPickerDialog:onChange()" + JSON.stringify(value));
}
})
})
}
.width("100%")
.padding(32)
}
}
数据管理-本地数据持久化
数据管理模块包括用户首选项、键值型数据管理、关系型数据管理、分布式数据对象和跨应用数据管理
而本地数据持久化分为三种:
- 用户首选项(Preferences) :通常用于保存应用的配置信息。数据通过文本的形式保存在设备中,应用使用过程中会将文本中的数据全量加载到内存中,所以访问速度快、效率高,但不适合需要存储大量数据的场景。
- 键值型数据库(KV-Store) :一种非关系型数据库,其数据以"键值"对的形式进行组织、索引和存储,其中"键"作为唯一标识符。适合很少数据关系和业务关系的业务数据存储,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。相比于关系型数据库,更容易做到跨设备跨版本兼容。
- 关系型数据库(RelationalStore) :一种关系型数据库,以行和列的形式存储数据,广泛用于应用中的关系型数据的处理,包括一系列的增、删、改、查等接口,开发者也可以运行自己定义的SQL语句来满足复杂业务场景的需要。
用户首选项
它不保证遵循ACID(Atomicity, Consistency, Isolation and Durability)特性,数据之间无关系,进程中每个文件仅存在一个Preferences实例,应用获取到实例后,可以从中读取数据,或者将数据存入实例中。通过调用flush方法可以将实例中的数据回写到文件里
但是因为首选项是存储在内存中的,所以 Preferences 不适合存放过多的数据,这会使内存占用太多
应用首选项的持久化文件保存在应用沙箱内部,可以通过上下文获取其路径,官网为我们提供了一张它的运行机制
- 保存数据(put)
- 获取数据(get)
- 是否包含指定的key(has)
- 删除数据(delete)
- 数据持久化(flush)
我们每次保存数据之后都要进行一次数据持久化操作来保证数据的存储,具体实现我们查看下面的代码:
- 分为两步,首先我们需要获取Preferences实例,然后再读取指定文件,将数据加载到Preferences实例,下面的代码算是做了一个小封装,但是封装的不够彻底,有需要朋友们可以进一步改进
typescript
import DataPreferences from '@ohos.data.preferences';
export class PreferencesUtils {
public static getPreferences(context, name: string): Promise<DataPreferences.Preferences> {
return new Promise<DataPreferences.Preferences>((resolved,rejected) => {
DataPreferences.getPreferences(context, name)
.then((res) => {
resolved(res);
})
.catch(reason => {
rejected(reason);
})
});
}
}
上面这段代码的意思是导出了一个封装工具类,该工具类需要一个当前上下文,以及我们的"数据库"名称,准确来说是一个数据持久化文件名称,创建成功之后会返回一个 Promise 对象,解析为 DataPreferences.Preferences
类型的数据,供我们后续操作使用
- 页面实现 + 实例方法调用
typescript
import {PreferencesUtils} from '../common/preferencesUtil';
@Entry
@Component
struct Index {
@State inputText:string = "";
@State storeText:string = "";
build() {
Column({space:32}) {
TextInput({placeholder:"请输入你的要存储的信息"})
.placeholderColor("#54404040")
.placeholderFont({weight:FontWeight.Bold})
.enterKeyType(EnterKeyType.Done)
.caretColor("#ff1dbf8c")
.maxLength(50)
.onChange((val)=> {
this.inputText = val;
}).margin(64)
Text(`当前页面临时数据: ${ this.inputText }`)
.fontWeight(FontWeight.Bold)
.fontColor("#b9404040")
Button("点击将临时数据本地持久化")
.backgroundColor("#ff13887e")
.fontWeight(500)
.padding({top:12,left:24,bottom:12,right:24})
.onClick(()=> {
PreferencesUtils.getPreferences(globalThis.getContext(), 'data')
.then((preferences) => {
preferences.put('data', this.inputText).then((isPrivacy) => {
AlertDialog.show(
{
title: '提示',
message: '数据存储成功',
}
)
})
})
})
Button("点击获取持久化数据")
.backgroundColor("#ff13887e")
.fontWeight(500)
.fontSize(18)
.padding({top:24,left:42,bottom:24,right:42})
.onClick(()=> {
// PreferencesUtil.getContent("data");
PreferencesUtils.getPreferences(globalThis.getContext(), 'data')
.then((preferences) => {
preferences.get('data', '默认数据', (err, val) => {
if (!err) {
this.storeText = val.toString();
}
return;
})
})
})
Text(`已经本地数据持久化的数据:${this.storeText}`)
.fontWeight(FontWeight.Bold)
.fontColor("#b9404040")
}
}
}
剩下几个我就先晾着先不说了,有这一个也能大体满足我们的需求了,只是存储一些用户端数据,真用到大型或者用户相关数据也不需要这些本地存储了
第三方库的使用
我就单 axios 这个网络库来演示吧,因为原生 http 请求我还不是很习惯
对了,我在下包过程中遇到了一个问题,不过两秒就解决了,就是我们需要配置一下 ohpm 的环境变量,在 Path 中配置就行,文件路径是我们 Ohpm 的 bin 目录,在配置环境变量前跑一下 init.bat 文件初始化一下。
ohpm 安装路径忘了的话,可以去 DevEco Studio 的 设置中找到,如下图
回到正文,我们老规矩先配置网络权限
然后引入相关 api 进行网络请求:
代码如下:
typescript
import axios, { AxiosError, AxiosResponse } from '@ohos/axios';
typescript
axios<string, AxiosResponse<string>, null>({
method: "get",
url: 'https://mock.presstime.cn/mock/649419e43acfaff9262b52ef/example/mock'
}).then((res: AxiosResponse) => {
console.info('result:' + JSON.stringify(res.data));
}).catch((error: AxiosError) => {
console.error(error.message);
})
是不是一下子就简单了哈哈哈
到此为止 HarmonyOS 已经发了三篇文章了,HarmonyO S的入门已经没有任何问题了,我主要是参考官网配合其课程来学习的,官网还是比较好的,就是那个课程有点...不适合我,导致来来回回学了一个星期,其他的学习也就是 API(API 是真的多,知道就行,用的时候再查) 的使用、有趣功能的发现,后续有新的发现(例如有趣 API、奇怪的 BUG、补充说明等)还会继续在该板块中发文章的,之前写过的两篇文章后续也会更加的完善吧,它的社区生态还不是很好这是实话,也算是为了 HarmonyOS 社区贡献了我自己的一份绵薄力,大家可以一起在评论区讨论学习
入门结束 🎉🎉🎉