写点什么

【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务快应用卡片接入指南(下)

  • 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 加入

第一时间发布荣耀开发者服务平台相关资讯,共享行业资源,合作共赢。荣耀开发者服务平台是荣耀面向开发者的统一生态入口,支持一站式接入荣耀软件、硬件生态合作,助力开发者商业成功。

评论

发布
暂无评论
【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务快应用卡片接入指南(下)_前端_荣耀开发者服务平台_InfoQ写作社区