【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务快应用卡片接入指南(下)
- 2022 年 9 月 07 日 北京
本文字数:5679 字
阅读完需:约 19 分钟
一、卡片网络加载优化指导
1. 卡片显示效果优化
1.1 优化说明
(1)首次加载:一般指用户手机上第一次使用卡片,手机本地还没有卡片的相关资源和用户数据,手机会下载卡片 rpk 包并进行安装,安装完成后进行展示。会执行卡片生命周期 onInit->onReady->onShow;
(2)卡片曝光:指卡片对用户可见,手机从息屏再到卡片展示页面,算是一次曝光;会执行卡片生命周期 onShow;
(3)重新加载:首次加载完成以后,手机系统的一些动作会导致负一屏卡片重新进行创建加载,如:用户切换手机字体、主题、重启手机或者手机的一些其他机制,会使卡片重新加载并显示。此时会执行卡片生命周期 onInit->onReady->onShow(卡片对用户可见时执行,不可见不会执行此生命周期);
(4)占位图提示页面:卡片首次加载或者重新加载时,有可能 fetch 请求返回数据较慢,卡片需要先显示文本宫格占位、图标占位或者沉浸式占位的 UI 文案提示页面作为过渡页,样式参见:《快应用卡片设计指南》1.10 空内容卡片样式
(5)无网络失败页面:卡片在首次加载时,如果手机无网络情况下,需要显示“数据加载失败,点击重试”文案提示页面;点击该文案页面时判断:1. 仍然无网络则跳转至卡片落地页处理无网络场景;2. 网络恢复时进行请求数据操作,恢复卡片正常显示。
(6)业务加载失败页面:卡片在首次加载时,如果网络存在,接口失败或者访问网络超时等失败情况时,请自行处理失败页面展示;
(7)缓存方案(可选):如果处在手机无网络或者 fetch 失败,需要获取上次存储的数据(每次 fetch 的数据都使用 storage 存储到本地)进行展示。开发者不必担心使用上次存储的数据显示时图片会无法加载,因为每次图片加载成功后,引擎框架会默认缓存卡片中的图片资源,只要 Image 组件的静态地址不变,图片在无网的情况下也可以正常显示;
注意:卡片的 fetch 不支持直接设置超时参数,所以开发者使用 Promise 自己封装超时处理函数,超时时间固定为 5s(不可以使用其他值),fetch 5s 后返回的数据需要废弃。
(8)卡片刷新场景(可选):为防止卡片影响客户端的流畅度和手机流量、功耗。
需要限制卡片访问网络或者刷新卡片刷新界面(onShow 生命周期)的频率,在刷新管控机制下,卡片在一个时间周期内仅能获取到一次 onShow 生命周期的钩子函数回调,达到限制刷新的目的。onShow 生命周期刷新时间周期建议大于等于 3S。
1.2 卡片首次加载处理
1.3 卡片二次刷新
1.4 示例 demo
(1)HTML 部分
<template> <!-- 卡片最外层容器 --> <div class="card" @click="toH5"> <!-- 加载初始占位布局 --> <div class="layer-box" if="{{showLoadView}}"> <div class="title-box"> <div class="title-logo"></div> <div class="title-txt-box"></div> </div> <div class="content-box"></div> <div class="footer-box"> <div class="footer-txt-box"></div> <div class="footer-img-box"></div> </div> </div> <!-- 数据加载异常布局 --> <div class="network-box" if="{{showLoadFailedView}}" onclick="clickRetry"> <image class="network-img" src="./image/位图备份.png"></image> <text class="network-txt">数据加载失败,点击重试</text> </div> <!-- stack标签用于有需要插入背景图的卡片需求,若无背景图需求无需用stack标签 --> <stack class="stack" if="{{showCardContent}}"> <!-- 卡片stack标签第一个元素,设置百分百宽高,做成背景图效果 --> <div class="bg-img"> <!-- 此处背景图标要用image标签 --> <image src="../Common/res/img_pic.png" forcedark="false" class="icon" ></image> </div> <!-- 卡片stack标签第二个元素,卡片布局容器,设置百分百宽高铺满父元素,若无背景图需求,直接来到此处布局容器 --> <div class="layer"> <!-- 标题区域 --> <div class="title"> <div class="title-logo"> <image forcedark="false" class="logo" src="../Common/res/icon_icon.png" ></image> </div> <text class="title-text">{{ title }}</text> </div> <!-- 内容区域 --> <div class="content"> <text class="content-title">内容区</text> <text class="content-text">{{ cardInfo.content }}</text> </div> <!-- 底部区域 --> <div class="footer"> <div class="footer-box"> <text class="footer-text">{{ cardInfo.fooderName }}</text> </div> </div> </div> </stack> </div></template>(2)CSS 部分
<style lang="less">/* 卡片最外层 最外层容器无需给宽高,由负一屏或桌面设定宽高 */.card { padding: 12dp; /* 此处给宽高是为了让调试器devtools显示内容方便测试效果,实际开发需要去掉宽高 */ height: 152dp; width: 100%; /* 按照最新深色模式规范需要给卡片设置背景色,映衬规范需要#ffffff作为背景色; 若自定义背景色,要满足深色模式则需要通过媒体查询来实现,详见深色模式开发文档 */ background-color: #ffffff;}/* stack标签用于有需要插入背景图的卡片需求,若无背景图需求无需用stack标签*/.stack { width: 100%;}
/*卡片布局容器,设置百分百宽高铺满父元素,若无背景图需求,不需要stack标签结构直接来到此处布局容器*/.layer { width: 100%; height: 100%; justify-content: space-between; flex-direction: column;}
/* 卡片标题区域 */.title { display: flex; height: 16dp; flex-direction: row; .title-logo { width: 16dp; height: 16dp; justify-content: center; align-items: center; flex-shrink: 0; .logo { width: 100%; height: 100%; object-fit: contain; border-radius: 50%; } } .title-text { font-size: 12dp; color: rgba(0, 0, 0, 0.9); padding-left: 8dp; }}
/* 占位图 */.layer-box { width: 100%; height: 100%; flex-direction: column; justify-content: space-between; .title-box { display: flex; height: 16dp; flex-direction: row; align-items: center; /* 占位图 */ .title-logo { width: 16dp; height: 16dp; border-radius: 50%; margin-right: 8dp; background-color: #87ceeb; } .title-txt-box { width: 60%; height: 8dp; background-color: #ccc; border-radius: 8dp; } } .content-box { height: 60dp; background-color: #ccc; } .footer-box { height: 40dp; justify-content: space-between; align-items: flex-end; .footer-txt-box { height: 8dp; width: 50%; background-color: #ccc; } .footer-img-box { height: 40dp; width: 40dp; border-radius: 50%; background-color: #87ceeb; } }}
/* 无网络布局 */.network-box { width: 100%; height: 100%; flex-direction: column; justify-content: center; align-items: center; .network-img { width: 80dp; height: 60dp; object-fit: contain; } .network-txt { font-size: 10dp; color: rgba(0, 0, 0, 0.9); padding-top: 4dp; }}
/*/卡片内容区域 */.content { height: 60dp; flex-direction: column; justify-content: center;}.content-title { font-size: 16dp; color: rgba(0, 0, 0, 0.9); font-weight: 500;}.content-text { font-size: 14dp; color: rgba(0, 0, 0, 0.6); font-weight: 400; /* 注意:卡片内容区域给定高度,内容展示要注意字数限制内容过长采用省略方式,避免导致内容缺失问题 */ lines: 2; text-overflow: ellipsis;}/* 卡片底部区域 */.footer { height: 40dp; flex-direction: row; justify-content: space-between; align-items: flex-end; .footer-box { height: 16dp; flex-direction: row; .footer-text { font-size: 10dp; color: rgba(0, 0, 0, 0.6); } }}
/* 背景图标 */.bg-img { width: 100%; height: 100%; flex-direction: column-reverse; align-items: flex-end;}.icon { width: 40dp; height: 40dp; object-fit: contain;}</style>(3)JS 部分
<script>import fetch from '@system.fetch'import network from '@system.network'import router from '@system.router'
// 封装节流函数 用于限制频繁点击或频繁触发事件的处理function throttle(func, delay = 500) { let timer = null let that = this return function (...arg) { if (timer) return timer = setTimeout(() => { func.apply(that, arg) timer = null }, delay) }}
//封装获取网络状态function getNetwork() { return new Promise((resolve, reject) => { network.getType({ success: function (data) { console.log(`handling success: ${data.type}`) resolve(data.type) }, fail: function (error) { console.error("get network error:" + error); reject(error); } }) })}
export default { private: { title: '标题(2x4)', showLoadView: false, //初始占位 showLoadFailedView: false, //数据加载失败showCardContent: false, //成功渲染hasInited: false, timeout: null, cardInfo: { content: '', fooderName: '' } }, onInit() { this.initDate() // 构建点击无网络失败页面节流函数 防止频繁点击无网络失败页面 this.filedFetchThottle = throttle(this.filedFetch.bind(this), 500) //构建频繁触发updateData数据更新的节流函数 防止频繁触发onShow里数据的更新 this.updateDataThottle = throttle(this.updateData.bind(this), 3000) },
onShow() {if(!this.hasInited) return //防止频繁触发onShow里数据的更新 this.updateDataThottle() },
// 正常卡片状态下的跳转(以跳转百度为示例) toH5() { router.push({ uri: 'hnquick://browser//parameter?url=http://www.baidu.com' }) },
/** * 初始化卡片数据 */ async initDate() { this.showLoadingPage() //初始化显示占位图 const netWorkStatus = await getNetwork() if (netWorkStatus == 'none') { this.showLoadFailedPage() return } const res = await this.fetchData() const { code, data } = res if (code == 200) { this.cardInfo.content = '离开[深圳航站],下一站[广州处理中转站]' this.cardInfo.fooderName = '来自顺丰速运' this.showCard() } else { // 自行处理业务失败逻辑}this.hasInited = true },
//数据更新 async updateData() { const res = await this.fetchData() const { code, data } = res if (code == 200) { this.cardInfo.content = '离开[深圳航站],下一站[广州处理中转站]' this.cardInfo.fooderName = '来自顺丰速运' this.showCard() } },
//接口调用失败或者无网络时跳转去无网络加载失败页面 async filedFetch() { const netWorkStatus = await getNetwork() console.log('netWorkStatus',netWorkStatus) if (netWorkStatus == 'none') { router.push({ uri: 'hnquick://browser//parameter?url=http://www.baidu.com' }) } else { this.updateData() } },
showLoadingPage: function () { this.showLoadFailedView = false this.showCardContent = false this.showLoadView = true },
showLoadFailedPage: function () { this.showCardContent = false this.showLoadView = false this.showLoadFailedView = true }, showCard: function () { this.showLoadFailedView = false this.showLoadView = false this.showCardContent = true },
//网络超时处理 timeoutHandler(promiseInstance) { const timeout = new Promise((_, reject) => { setTimeout(() => { reject('网络超时') }, 5000) }) return Promise.race([timeout, promiseInstance]) },
/** * 发起fetch请求数据 */ fetchData() { return this.timeoutHandler( new Promise((resolve, reject) => { fetch .fetch({ url: 'https://xxx', // 接口链接 method: 'GET', }) .then((response) => { resolve(response.data) }) .catch((error, code) => { console.log(`🐛 request fetchIconUrlData fail, error= ${error}, code = ${code}`) resolve({ code, data: error }) }) }) ) }, // 数据加载失败 clickRetry(evt) { // 阻止事件冒泡 evt.stopPropagation() // 防止频繁点击无网络失败页面 this.filedFetchThottle() },
}</script>
荣耀开发者服务平台
还未添加个人签名 2022.06.28 加入
第一时间发布荣耀开发者服务平台相关资讯,共享行业资源,合作共赢。荣耀开发者服务平台是荣耀面向开发者的统一生态入口,支持一站式接入荣耀软件、硬件生态合作,助力开发者商业成功。









评论