Case Study: PWP
本系列文章将以两个实际项目作为研究对象,探讨离线可用这个 PWA 的重要特性在 SSR 架构中的应用思路,最后结合 Vue SSR 进行实际应用。
- 第一部分以 PWA-Directory 为例。
- 本文将研究一个 WordPress 主题项目。这个项目是Google Dev Summit 2017上的一个分享主题。 代码可以通过 Docker 启动。
SW 缓存思路
WordPress 本质上也是一个服务端渲染项目。因此也适用第一部分中提出的缓存思路:
- 改造后端模板以支持返回完整页面和内容片段
- 服务端增加一条针对 App Shell 的路由规则,返回仅包含 App Shell 的 HTML 页面
- 预缓存 App Shell 页面
- SW 拦截所有 HTML 请求,统一返回缓存的 App Shell 页面
- 前端路由负责代码片段的填充,完成前端渲染
按照这个思路,首先需要对模板进行改造。未来将以主题形式发布。其中使用了get_template_part()
分别获取头部和底部模板片段,也就是 App Shell 了。
// index.php
<?php
etag_start();
if(!is_fragment()) get_template_part('header');
?>
<?php if(have_posts()): ?>
<?php while(have_posts()): the_post() ?>
<?php include('fragment-post-preview.php'); ?>
<?php endwhile; ?>
<?php else: ?>
Nothing here :(
<?php endif; ?>
<?php
if(!is_fragment()) get_template_part('footer');
etag_end();
?>
通过 query 中fragment
参数区分完整页面 OR 页面内容片段。
// functions.php
function is_fragment() {
return isset( $_GET['fragment'] ) && 'true' === $_GET['fragment'];
}
在index.php
模板中使用到了etag_start/end()
,顾名思义使用了 Etag 比对浏览器请求头中的 HTTP_IF_NONE_MATCH ,如果发现内容没有改变,直接返回304状态码。
// functions.php
function etag_start() {
global $etag_depth;
if ( $etag_depth == 0 ) ob_start();
$etag_depth++;
}
function etag_end() {
global $etag_depth;
$etag_depth--;
if ( $etag_depth > 0 ) return;
$content = ob_get_clean();
$etag = hash('sha256', $content);
$request = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) && $_SERVER['HTTP_IF_NONE_MATCH'];
if ( $etag == $request ) {
http_response_code(304);
return;
}
header('Etag: ' . $etag);
echo $content;
}