vue.js 實踐總結(三)Skeleton 骨架屏渲染

應用場景

簡單來講,骨架屏(skeleton screen) 就是一個頁面從html 下載完成到 js 下載完成而且執行數據渲染這兩個時間點之間暫時渲染頁面基本結構的方案。php

就個人理解,骨架屏優化是有必定場景的,包括且不限於如下幾種狀況:html

  • 有懶加載機制的SPA路由
  • 多頁面程序的首頁渲染
  • SPA 中的非懶加載路由,可是數據量很大,徹底load 並渲染數據須要花較長時間

https://huangxuan.me/2017/07/12/upgrading-eleme-to-pwa

上圖形象地解釋了兩個多頁面程序之間的切換,用Skeleton Screen 去優化用戶觀感的方案。前端

Skeleton 渲染是在前端項目編譯時完成的,與之相對應的是組件在瀏覽器runtime實時渲染成DOM,從技術上講就是在服務器端預先把組件佈局和數據渲染成html 字符串而且注入html 文件中。這須要服務器端渲染(ssr) 的支持。如react 和 vue.js 這樣依賴虛擬DOM 的前端框架自然是支持服務器端渲染的。由於只須要一個 JavaScript 在服務器端的執行環境(例如V8 引擎)就能夠輕易建立虛擬Dom而且映射爲DOM tree。基於虛擬dom的服務器端渲染,最先起源於react,能夠參考 strikingly 的技術博客vue

服務器端渲染

服務器端渲染做爲skeleton screen的基本技術棧,能夠參考各前端框架的官方文檔,例如 vue-ssr guide. 主要是依賴 vue-server-renderer 這個庫來實現node

拿vue.js 組件來講,在node.js 中渲染爲 HTML 字符串能夠簡單分爲幾步:python

// 第 1 步:建立一個 Vue 實例
const Vue = require('vue')
const app = new Vue({
// el: 是不須要的,由於一旦設置目標 el,就會涉及document 操做
  template: `<div>Hello World</div>`
})

// 第 2 步:建立一個 renderer
const renderer = require('vue-server-renderer').createRenderer()

// 第 3 步:將 Vue 實例渲染爲 HTML
renderer.renderToString(app, (err, html) => {
  if (err) throw err
  console.log(html)
  // => <div data-server-rendered="true">Hello World</div>
})
複製代碼

上面的官方DEMO 很簡單,直接在nodejs 腳本文件中定義vue 組件。但實際應用中咱們的組件通常都比較複雜,多是一個入口 app.vue,依賴了好幾個組件,因此要用另外的方式去引入 new Vue 產生的 vm 對象. 這個vm 對象做爲 renderer.renderToString 函數的第一個參數是最核心的。實際上經過跟蹤 vue-server-renderer 的源碼,ssr 仍是依靠vue組件的render 函數來完成大部分工做 ,render 函數是 vue 組件的核心函數。react

| installSSRHelpers(component); 
renderToString -> createRenderFunction -> | normalizeRender(component);  // get component render function
                                          | renderNode(component._render(), true, context); 
                                          // call component render to VNode.

複製代碼

幾個坑

  • document not defined

服務器端渲染並不等同於在服務器端啓動一個瀏覽器進程,因此沒法獲取瀏覽器窗體中的window,document 等全局對象。一旦服務器端渲染的js 腳本中涉及到document 操做,就會報這個 document not defined 的錯誤。git

因此要麼就在vue 組件中或者全部依賴包中用到document 的地方都要作檢測,要麼就在服務器端渲染以前給global 對象加上document 定義 github issue程序員

  • unexpected token =

在vue-server-renderer 的執行過程當中,提供了把vue 組件渲染成html 片斷而且插入某個 template HTML 中的API, 在建立Renderer 的時候傳入一個template。後來發現ssr 的 template 中是不能包含相似這種 <%= ddd > 符號的,也就是template 必須得符合vue 的template 語法github

// 最後ssr 就會輸出 Vue 組件的內容,而且注入到template 中 註釋 <!--vue-ssr-outlet--> 的地方

const renderer = createRenderer({
  template: require('fs').readFileSync('./index.template.html', 'utf-8')
})
const context = { title: 'Hello' }
renderer.renderToString(app, context, (err, html) => {
  // 頁面模板中 {{ title }} 將會是 "Hello"
})
複製代碼

具體參考 ssr 使用頁面模板。 由於有的後端程序員習慣了 asp 或者 jsp 的template 語法,以 <%= 等做爲數據插值表達式的開頭,會引發 vue ssr 的編譯失敗。

寫在後面

綜上,此次我只是簡單分析記錄了下 Skeleton 骨架屏實現的第一部分,就是server side render,這是預先渲染的技術前提。實際上,這種後端拼接Html 字符串的活在php年代,python server中,甚至node.js server 中咱們早就幹過了(ejs 或者 jade)。本質上都是拼接Html 字符串,提升SEO 和首屏響應

在Github 上有許多基於服務器端渲染的靜態網站生成器或者 博客工具,能夠實現比較流暢的瀏覽體驗, 例如:vuepress.

關於骨架屏的實現,業內已經有很成熟的方案,既有基於 ssr 的,也有直接基於瀏覽器內核起個進程預渲染的,徹底不用vue 的ssr 技術棧。下一次再具體分享下後者

參考閱讀:

vue ssr guide

餓了麼升級PWA

vue.js 開發系列二:render 函數

essential-guide-to-improve-seo-in-single-page-application-vuejs

相關文章
相關標籤/搜索