Chrome 提出 PRPL 的模式在自家 Polymer 中的应用状况是什么样的呢?与其他流行框架相比有哪些区别呢?

PRPL 模式

首先简单介绍一下 PRPL 模式:

结合 PWA 的 Service Worker 缓存方案以及 HTTP/2 的资源推送,能够有效减少首屏加载时间,并提升后续访问性能。

借助现有的流行前端框架和构建工具,开发者在应用中实现这一模式已经十分方便,尤其是按需加载路由这部分。

路由懒加载

Webpack 提供的 Code Splitting 代码分离可以将模块分离出来,在运行时异步动态加载:

import(
    /* webpackChunkName: "my-chunk-name" */
    /* webpackMode: "lazy" */
    'module'
);

结合流行框架配套的前端路由,我们就能实现路由的懒加载。例如在 vue-router 中,支持使用异步组件:

const Foo = () => import('./Foo.vue')
const router = new VueRouter({
    routes: [
        { path: '/foo', component: Foo }
    ]
})

这样在路由切换时才会请求所需的路由组件文件。

Polymer 中的做法

首先让我们看一下 Polymer SPA 的项目结构:

Polymer 项目结构

Polymer 项目结构

entrypoint 类似 Webpack 所需的入口文件,应该足够精简,仅包含特性检测之后引入的 polyfill,以及通过 HTML imports 引用的 App Shell。 App Shell 包含了前端路由,全局的导航 UI 等等,以及需要实现动态加载 fragment 的逻辑。 fragment 类似异步路由组件。

Polymer 并没有使用类似 Webpack 这样的构建工具,在自身的配置文件 polymer.json 中指明了这三部分内容:

{
    "entrypoint": "index.html",
    "shell": "src/my-app.html",
    "fragments": [
        "src/my-view1.html",
        "src/my-view2.html",
        "src/my-view3.html",
        "src/my-view404.html"
    ]
}

那么 Polymer 是如何解决异步加载问题的呢?

异步加载路由

Polymer 提供了异步引入的 API:

var resolvedPageUrl = this.resolveUrl('my-' + page + '.html');
this.importHref(resolvedPageUrl,
    null,
    this._importFailedCallback,
    true
);

通过这个 API,开发者能够配合路由组件,实现异步加载,并在出错时跳转到 404 页面。

打包策略

之前我们一直在关注 PRPL 中的 Lazy-load 部分,在 Push 也就是推送资源方面又是怎么考虑的呢?

在 Polymer 中,可以在 polymer.json 中配置不同的打包策略:

"builds": [
    {
       "preset": "es5-bundled"
    },
    {
       "preset": "es6-bundled"
    },
    {
       "preset": "es6-unbundled"
    }
]

es5/6 很好理解,bundled/unbundled 是啥意思呢?

我们知道 HTTP/2 中,服务端在返回 HTML 的同时,可以向浏览器推送所需的静态资源,这样在浏览器解析 HTML 遇到相应的资源时,它们已经在 HTTP 缓存中了。所以针对这一特性,过去打包所有静态资源以减少网络请求数的考量就没有必要了,反而拆分成多个 bundle 更有利于不同页面间共享的缓存。

Polymer 打包策略

Polymer 打包策略

参考资料