前言
由于這三大框架都是JS驅(qū)動徽千,在JS沒有解析加載完成之前頁面無法展示,會處于長時間的白屏汤锨,帶來了一定的用戶體驗問題
SSR 方式
想到白屏問題,首先想到的解決方案一般都是服務(wù)端渲染百框,在服務(wù)端將渲染邏輯處理好闲礼,然后將處理好的HTML直接返回給前端展示,這樣就可以解決白屏的問題铐维,也可以解決seo的問題柬泽,因為不需要動態(tài)獲取數(shù)據(jù)了
預渲染
這個方案是相對簡單直接的一個解決辦法,嘗試成本也比較低嫁蛇,這里介紹如何用prerender-spa-plugin做預渲染锨并,這樣就可以在瀏覽器進行渲染,而不需要將Vue或者React代碼部署到服務(wù)器上睬棚,以vue-cli3的官方demo為例做配置第煮,看具體的配置文件
const path = require('path')
const PrerenderSPAPlugin = require('prerender-spa-plugin')
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer
module.exports = {
configureWebpack: config => {
let plugins = []
plugins.push(new PrerenderSPAPlugin({
staticDir: path.resolve(__dirname, 'dist'),
routes: ['/', '/about'],
minify: {
collapseBooleanAttributes: true,
collapseWhitespace: true,
decodeEntities: true,
keepClosingSlash: true,
sortAttributes: true
},
renderer: new Renderer({
renderAfterDocumentEvent: 'custom-render-trigger'
})
}))
config.plugins = [
...config.plugins,
...plugins
]
}
}
預渲染有什么缺點
- a、動態(tài)數(shù)據(jù)無法展示抑党,不同的用戶看到的都是同樣的頁面
- b包警、路由很多時,代碼構(gòu)建時間太長
- c底靠、用戶容易誤操作害晦,由于預渲染時js還沒有加載,因此展示出來的內(nèi)容沒有js的交互邏輯暑中,比如按鈕點擊壹瘟,在js加載完成之前用戶點擊是沒有反應的
- d鲫剿、預加載內(nèi)容很少,當頁面有內(nèi)容是依賴動態(tài)數(shù)據(jù)加載時稻轨,在編譯時是無法加載出動態(tài)數(shù)據(jù)的灵莲,因此會導致這部分內(nèi)容編譯不出來
骨架屏
骨架屏的實現(xiàn)原理和預加載類似,都是利用了Puppeteer爬取頁面的功能澄者,Puppeteer是chrome出的一個headlessChromenode庫笆呆,提供了API可以抓取SPA并生成預渲染內(nèi)容,和預加載不太一樣的是骨架屏技術(shù)會在Puppeteer生成內(nèi)容后粱挡,利用算法將生成的內(nèi)容進行替換赠幕,生成骨架頁面,page-skeleton-webpack-plugin是一個用來生成骨架屏的webpack插件询筏,接下來就來看看怎么使用
const SkeletonPlugin = require('page-skeleton-webpack-plugin').SkeletonPlugin
const path = require('path')
module.exports = {
publicPath: '/',
outputDir: 'dist',
configureWebpack: config => {
let plugins = []
plugins.push(new SkeletonPlugin({
pathname: path.resolve(__dirname, './shell'), // pathname為來存儲 shell 文件的地址
staticDir: path.resolve(__dirname, './dist'), // 最好和 `output.path` 相同
routes: ['/', '/about'], // 將需要生成骨架屏的路由添加到數(shù)組中
port: '7890'
}))
config.plugins = [
...config.plugins,
...plugins
]
},
chainWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.plugin('html').tap(opts => {
console.log(opts[0])
opts[0].minify.removeComments = false
return opts
})
}
}
}