本章作者:@maplor
书接上文
在上一篇文章 我在云栖写代码(二):三维沉浸式体验 中,我们介绍了三维沉浸式展厅的技术解析以及转场动画的实现。今天给大家带来的是系列最后一篇文章:“精益求精”,介绍打造秒开体验、埋点统计,以及社交分享优化的经验。
打造秒开体验
访问者对 Web 网页的第一个印象必然是打开网页的快慢。等待时间每多一秒,流失率会成倍的提高。所以打开速度是我们最需要优化的点。
用户访问一个站点分为 加载 和 渲染 两个流程。我们的页面托管在阿里云统一对外的平台上,所有的静态资源放在 CDN 上,资源的加载速度已经足够快,所以将重点优化 渲染 部分。
衡量指标
首先我们需要网页性能的衡量指标,实际上网上有很多资料,这里我们使用的是下面这两个:
- LCP(Largest Contentful Paint)最大部分内容已渲染。
- TTI(Time to Interactive)用户可交互时间,值越低越好;
即用户从开始访问到看到屏幕内出现了内容的时间,以及用户可以交互的时间。
审视代码
这个项目使用 React 渲染,默认的 Html 中只有一个空的 div,所有内容都是在 Js 中动态增加的。这会导致白屏时间增加,需要等脚本运行完毕,内容才会渲染。LCP 的时间也就增加了。
同时项目中使用了很多 图片/视频 素材,需要等素材加载完才有完整内容框架,用户可交互时间也不短。
为了提升上面两个指标,我们针对性的使用了 预渲染 和 响应式素材 两种方法。
预渲染
大家所熟知的 SSR 就是一种预渲染技术,但其需要服务端支持。由于平台限制,我们最终使用静态预渲染,就是把希望用户最先看到的内容直接写到 Html 中。
得益于体验营是静态展示型的网站,我们只需要有一种预渲染内容,就是页面的框架和 loading 提示:
<div id="container">
<!-- prerender -->
<div class="orientation-wrapper">
<div class="page-loading-container">
<div class="page-loading-box">
<img class="page-loading-cube" src="//...">
混合云体验营准备中<span><span class="dotting-1">.</span><span class="dotting-2">.</span><span class="dotting-3">.</span></span>
</div>
</div>
</div>
</div>
最终效果是:
用户在 html 和 全局 css 加载完成后就能看到页面的反馈,减少了白屏时间(LCP)。等待脚本加载完成,ReactDom 会替换 div#container
中的内容,完成正常页面的渲染。
响应式素材
所谓的响应式素材,就是根据一些规则,在不同的设备终端上加载不同尺寸的素材,让速度和体验获得平衡。
体积最大的素材就是视频,所以主要针对视频做优化。在第一章中我们提到根据UA识别运行环境,这里在UA的基础上,结合 navigator.connection api,尝试判断当前是否处于 wifi 环境:
const isMobileRes = isMobile();
const ua = window.navigator.userAgent;
const con = window.navigator.connection;
let showFullVideo = true;
// 移动端下如果能判断到 wifi 连接,则使用高清版本,否则降级
if (isMobileRes.phone) {
showFullVideo = false;
// 有些浏览器 ua 有 NetType 属性
if(/NetType/.test(ua)) {
const type = ua.match(/NetType/(\S*)/);
if (type && type[1] && type[1].toLowerCase() === 'wifi') {
showFullVideo = true;
}
} else if(con){
const network = con.type || con.effectiveType;
if (network === 'wifi' || network === '2') {
showFullVideo = true;
}
}
}
如果运行环境是 PC 端,或者能判断是 wifi 链接的移动端,则加载高清视频资源,其它情况下加载体积更小的精简视频资源:
<video src={`//domain/home_${showFullVideo ? 'pc' : 'mobile'}.mp4`} />
对应的是不同清晰度的视频:
可以看到加载精简视频资源可以节省许多流量,提升加载速度。
最终效果
最后我们看下整个站点的加载时间图,LCP 在1秒左右,整体访问速度还是非常好的。
业务埋点统计
为了能统计用户访问网站的信息,我们为网站增加了业务的埋点。这里选择的是 友盟Web 的打点。在 npm 上搜索一下没有 react 适用的包装,所以我们根据官网文档包装了一个 uweb 的工具函数:
// 初始化友盟打点
function init() {
const script = document.createElement('script');
script.src = `https://s4.cnzz.com/z_stat.php?id=${siteId}&web_id=${siteId}`;
script.onload = () => {
if (window._czc) {
// 关闭默认打点
window._czc.push(['_setAutoPageview', false]);
}
};
document.body.appendChild(script);
}
/**
* @param {string} content_url 自定义虚拟PV页面的URL地址 填写以斜杠‘/’开头的相对路径,系统会自动补全域名
* @param {string} referer_url 自定义该受访页面的来源页URL地址。建议填写该异步加载页面的母页面。不填,则来路按母页面的来路计算。填为“空”,即"",则来路按“直接输入网址或书签”计算。
* */
function pv(content_url: string, referer_url?: string) {
const res = ['_trackPageview', `${baseDomain}${content_url}`];
if (referer_url !== undefined) {
res.push(referer_url);
}
window._czc && window._czc.push(res);
}
/**
* @param category 表示事件发生在谁身上,如“视频”、“小说”、“轮显层”等等。
* @param action 表示访客跟元素交互的行为动作,如"播放"、"收藏"、"翻层"等等。
* @param label 用于更详细的描述事件,如具体是哪个视频,哪部小说。
* @param value 用于填写打分型事件的分值,加载时间型事件的时长,订单型事件的价格。
* @param nodeid 填写事件元素的div元素id。
* */
export function event(
category: string,
action: string,
label?: string,
value?: number,
nodeid?: string,
) {
const res: any[] = ['_trackEvent', category, action];
if (label !== undefined) {
res.push(label);
}
if (value !== undefined) {
res.push(value);
}
if (nodeid !== undefined) {
res.push(nodeid);
}
window._czc && window._czc.push(res);
}
我们只要在应用初始化时调用 init
方法初始化,在路由切换时调用 pv
方法打点,在需要触发事件的地方调用 event
方法即可:
// 监听路由跳转
history.listen((location, action) => {
pv(location.pathname);
});
// 点击触发事件
<Link
to="/xxx"
onClick={() => {
event('首页', '开始探索');
}}
>
开启探索
</Link>
社交分享优化
经过上面的优化,我们的页面已经比较完善了,本以为可以顺利上线推广,但在测试时却发现,分享到聊天软件里显示的效果特别挫:
而我们希望在分享时卡片能展示核心介绍,经过一番搜索,发现可以通过 Open Graph Protocol
来设置社交软件分享卡片的内容:
Open Graph Protocol本身是一套Metatags的规范,用来标注页面的类型和描述页面的内容。由Facebook在2010年F8会议上公布。
og标签是一种新的http头部标记,在页面添加这种协议可以让网页成为一个“富媒体对象”,表示同意网页内容可以被其他社会化网站引用等。
详情可以见:ogp.me/
目前大多数社交软件分享时都支持 ogp(目前为止微信安卓版不支持),于是我们在 html meta 中加上以下内容:
<!-- Open Graph data -->
<meta property="og:title" content="让混合云「看得见,摸得着」- 阿里云混合云体验营" />
<meta property="og:url" content="https://apsara-stack.aliyun.com/digital-twin/experience" />
<meta property="og:image" content="https://..." />
<meta property="og:description" content="全产品技术能力的在线互动式体验营 25+标杆客户案例 20+详尽产品介绍 15+真实环境体验 10+行业应用场景 5大实景动态演示 40+全栈解决方案 快速了解阿里云混合云" />
另外还有少数环境可以用 itemprop
属性来设置抓取的内容:
<meta itemprop="name" content="让混合云「看得见,摸得着」- 阿里云混合云体验营">
<meta itemprop="image" content="https://...">
最后是微信,搜到资料说会抓取页面第一个 div 的内容和图片,再结合 SEO 处理,在 body
标签开头加上这一段:
<!-- SEO -->
<div style="display: none;">
<h1>阿里云混合云(Alibaba Cloud Hybrid Cloud)</h1>
阿里云混合云(Alibaba Cloud Hybrid Cloud)源于...
<img src="https://..." />
</div>
最后社交软件的分享卡片就是我们想要的状态啦~
// 2021-12-31测试,在微信里分享的卡片又回到了很挫的状态...
写在最后
由于各种原因,这篇文章发布时已经过了很久,还好赶在今年结束前发布了。在这里祝大家新年快乐,在学校的学业进步,在工作的事业有成。我们明年见 🎉 🎉 🎉 ~
喜欢文章的朋友们记得持续关注我们哦👇~