我在云栖写代码(三):精益求精

1,692 阅读5分钟

本章作者:@maplor

书接上文

在上一篇文章 我在云栖写代码(二):三维沉浸式体验 中,我们介绍了三维沉浸式展厅的技术解析以及转场动画的实现。今天给大家带来的是系列最后一篇文章:“精益求精”,介绍打造秒开体验、埋点统计,以及社交分享优化的经验。

image.png

>> “混合云体验营” 访问地址

打造秒开体验

访问者对 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>

最终效果是:

image.png

用户在 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`} />

对应的是不同清晰度的视频:

image.png

可以看到加载精简视频资源可以节省许多流量,提升加载速度。

最终效果

最后我们看下整个站点的加载时间图,LCP 在1秒左右,整体访问速度还是非常好的。

image.png

业务埋点统计

为了能统计用户访问网站的信息,我们为网站增加了业务的埋点。这里选择的是 友盟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>

社交分享优化

经过上面的优化,我们的页面已经比较完善了,本以为可以顺利上线推广,但在测试时却发现,分享到聊天软件里显示的效果特别挫:

image.png

而我们希望在分享时卡片能展示核心介绍,经过一番搜索,发现可以通过 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>

最后社交软件的分享卡片就是我们想要的状态啦~

image.png

// 2021-12-31测试,在微信里分享的卡片又回到了很挫的状态...

写在最后

由于各种原因,这篇文章发布时已经过了很久,还好赶在今年结束前发布了。在这里祝大家新年快乐,在学校的学业进步,在工作的事业有成。我们明年见 🎉 🎉 🎉 ~


喜欢文章的朋友们记得持续关注我们哦👇~

image.png