91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析

發布時間:2021-09-15 11:29:06 來源:億速云 閱讀:124 作者:柒染 欄目:web開發

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

本人在做Vue項目的時候,一直苦于產品、客戶對首屏加載要求,SEO的訴求,也想過很多解決方案,本次也是針對瀏覽器渲染不足之處,采用了服務端渲染,并且做了兩個一樣的Demo作為比較,更能直觀的對比Vue前后端的渲染。

talk is cheap,show us the code!話不多說,我們分別來看兩個Demo:

1.瀏覽器端渲染Demo: https://github.com/monkeyWangs/doubanMovie

2.服務端渲染Demo:https://github.com/monkeyWangs/doubanMovie-SSR

兩套代碼運行結果都是為了展示豆瓣電影的,運行效果也都是差不多,下面我們來分別簡單的闡述一下項目的機理:

一、瀏覽器端渲染豆瓣電影

首先我們用官網的腳手架搭建起來一個vue項目

npm install -g vue-cli
vue init webpack doubanMovie
cd doubanMovie
npm install
npm run dev

這樣便可以簡單地打起來一個cli框架,下面我們要做的事情就是分別配置 vue-router, vuex,然后配置我們的webpack proxyTable 讓他支持代理訪問豆瓣API。

1.配置Vue-router

我們需要三個導航頁:正在上映、即將上映、Top250;一個詳情頁,一個搜索頁。這里我給他們分別配置了各自的路由。在 router/index.js 下配置以下信息:

import Vue from 'vue'
import Router from 'vue-router'
import Moving from '@/components/moving'
import Upcoming from '@/components/upcoming'
import Top250 from '@/components/top250'
import MoviesDetail from '@/components/common/moviesDetail'
import Search from '@/components/searchList'
Vue.use(Router)
/**
 * 路由信息配置
 */
export default new Router({
 routes: [
 {
 path: '/',
 name: 'Moving',
 component: Moving
 },
 {
 path: '/upcoming',
 name: 'upcoming',
 component: Upcoming
 },
 {
 path: '/top250',
 name: 'Top250',
 component: Top250
 },
 {
 path: '/search',
 name: 'Search',
 component: Search
 },
 {
 path: '/moviesDetail',
 name: 'moviesDetail',
 component: MoviesDetail
 }
 ]
})

這樣我們的路由信息配置好了,然后每次切換路由的時候,盡量避免不要重復請求數據,所以我們還需要配置一下組件的keep-alive:在app.vue組件里面。

<keep-alive exclude="moviesDetail">
 <router-view></router-view>
</keep-alive>

這樣一個基本的vue-router就配置好了。

2.引入vuex

Vuex 是一個專為 Vue.js 應用程序開發的狀態管理模式。它采用集中式存儲管理應用的所有組件的狀態,并以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。

簡而言之:Vuex 相當于某種意義上設置了讀寫權限的全局變量,將數據保存保存到該“全局變量”下,并通過一定的方法去讀寫數據。

Vuex 并不限制你的代碼結構。但是,它規定了一些需要遵守的規則:

應用層級的狀態應該集中到單個 store 對象中。

提交 mutation 是更改狀態的唯一方法,并且這個過程是同步的。

異步邏輯都應該封裝到 action 里面。

對于大型應用我們會希望把 Vuex 相關代碼分割到模塊中。下面是項目結構示例:

├── index.html
├── main.js
├── api
│ └── ... # 抽取出API請求
├── components
│ ├── App.vue
│ └── ...
└── store
 ├── index.js  # 我們組裝模塊并導出 store 的地方
 └── moving  # 電影模塊
 ├── index.js # 模塊內組裝,并導出模塊的地方
 ├── actions.js # 模塊基本 action
 ├── getters.js # 模塊級別 getters
 ├── mutations.js # 模塊級別 mutations
 └── types.js # 模塊級別 types

所以我們開始在我們的src目錄下新建一個名為store 的文件夾 為了后期考慮 我們新建了moving 文件夾,用來組織電影,考慮到所有的action,getters,mutations,都寫在一起,文件太混亂,所以我又給他們分別提取出來。

stroe文件夾建好,我們要開始在main.js里面引用vuex實例:

import store from './store'
new Vue({
 el: '#app',
 router,
 store,
 template: '<App/>',
 components: { App }
})

這樣,我們便可以在所有的子組件里通過 this.$store 來使用vuex了。

3.webpack proxyTable 代理跨域

webpack 開發環境可以使用proxyTable 來代理跨域,生產環境的話可以根據各自的服務器進行配置代理跨域就行了。在我們的項目config/index.js 文件下可以看到有一個proxyTable的屬性,我們對其簡單的改寫

proxyTable: {
 '/api': {
 target: 'http://api.douban.com/v2',
 changeOrigin: true,
 pathRewrite: {
  '^/api': ''
 }
 }
 }

這樣當我們訪問

localhost:8080/api/movie

的時候 其實我們訪問的是

http://api.douban.com/v2/movie

這樣便達到了一種跨域請求的方案。

至此,瀏覽器端的主要配置已經介紹完了,下面我們來看看運行的結果:

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析

為了介紹瀏覽器渲染是怎么回事,我們運行一下npm run build 看看我們的發布版本的文件,到底是什么鬼東西....

run build 后會都出一個dist目錄 ,我們可以看到里面有個index.html,這個便是我們最終頁面將要展示的html,我們打開,可以看到下面:

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析

觀察好的小伙伴可以發現,我們并沒有多余的dom元素,就只有一個div,那么頁面要怎么呈現呢?答案是js append,對,下面的那些js會負責innerHTML。而js是由瀏覽器解釋執行的,所以呢,我們稱之為瀏覽器渲染,這有幾個致命的缺點:

js放在dom結尾,如果js文件過大,那么必然造成頁面阻塞。用戶體驗明顯不好(這也是我我在公司反復被產品逼問的事情)

不利于SEO

客戶端運行在老的JavaScript引擎上

對于世界上的一些地區人,可能只能用1998年產的電腦訪問互聯網的方式使用計算機。而Vue只能運行在IE9以上的瀏覽器,你可能也想為那些老式瀏覽器提供基礎內容 - 或者是在命令行中使用 Lynx的時髦的黑客

基于以上的一些問題,服務端渲染呼之欲出....

二、服務器端渲染豆瓣電影

先看一張Vue官網的服務端渲染示意圖

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析

從圖上可以看出,ssr 有兩個入口文件,client.js 和 server.js, 都包含了應用代碼,webpack 通過兩個入口文件分別打包成給服務端用的 server bundle 和給客戶端用的 client bundle. 當服務器接收到了來自客戶端的請求之后,會創建一個渲染器 bundleRenderer,這個 bundleRenderer 會讀取上面生成的 server bundle 文件,并且執行它的代碼, 然后發送一個生成好的 html 到瀏覽器,等到客戶端加載了 client bundle 之后,會和服務端生成的DOM 進行 Hydration(判斷這個DOM 和自己即將生成的DOM 是否相同,如果相同就將客戶端的vue實例掛載到這個DOM上, 否則會提示警告)。

具體實現:

我們需要vuex,需要router,需要服務器,需要服務緩存,需要代理跨域....不急我們慢慢來。

1.建立nodejs服務

首先我們需要一個服務器,那么對于nodejs,express是很好地選擇。我們來建立一個server.js

const port = process.env.PORT || 8080
app.listen(port, () => {
 console.log(`server started at localhost:${port}`)
})

這里用來啟動服務監聽 8080 端口。

然后我們開始處理所有的get請求,當請求頁面的時候,我們需要渲染頁面

app.get('*', (req, res) => {
 if (!renderer) {
 return res.end('waiting for compilation... refresh in a moment.')
 }
 const s = Date.now()
 res.setHeader("Content-Type", "text/html")
 res.setHeader("Server", serverInfo)
 const errorHandler = err => {
 if (err && err.code === 404) {
 res.status(404).end('404 | Page Not Found')
 } else {
 // Render Error Page or Redirect
 res.status(500).end('500 | Internal Server Error')
 console.error(`error during render : ${req.url}`)
 console.error(err)
 }
 }
 renderer.renderToStream({ url: req.url })
 .on('error', errorHandler)
 .on('end', () => console.log(`whole request: ${Date.now() - s}ms`))
 .pipe(res)
})

然后我們需要代理請求,這樣才能進行跨域,我們引入http-proxy-middleware模塊:

const proxy = require('http-proxy-middleware');//引入代理中間件
/**
 * proxy middleware options
 * 代理跨域配置
 * @type {{target: string, changeOrigin: boolean, pathRewrite: {^/api: string}}}
 */
var options = {
 target: 'http://api.douban.com/v2', // target host
 changeOrigin: true,  // needed for virtual hosted sites
 pathRewrite: {
 '^/api': ''
 }
};
var exampleProxy = proxy(options);
app.use('/api', exampleProxy);

這樣我們的服務端server.js便配置完成。接下來 我們需要配置服務端入口文件,還有客戶端入口文件,首先來配置一下客戶端文件,新建src/entry-client.js

import 'es6-promise/auto'
import { app, store, router } from './app'
// prime the store with server-initialized state.
// the state is determined during SSR and inlined in the page markup.
if (window.__INITIAL_STATE__) {
 store.replaceState(window.__INITIAL_STATE__)
}
/**
 * 異步組件
 */
router.onReady(() => {
 // 開始掛載到dom上
 app.$mount('#app')
})
// service worker
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
 navigator.serviceWorker.register('/service-worker.js')
}

客戶端入口文件很簡單,同步服務端發送過來的數據,然后把 vue 實例掛載到服務端渲染的 DOM 上。

再配置一下服務端入口文件:src/entry-server.js

import { app, router, store } from './app'
const isDev = process.env.NODE_ENV !== 'production'
// This exported function will be called by `bundleRenderer`.
// This is where we perform data-prefetching to determine the
// state of our application before actually rendering it.
// Since data fetching is async, this function is expected to
// return a Promise that resolves to the app instance.
export default context => {
 const s = isDev && Date.now()
 return new Promise((resolve, reject) => {
 // set router's location
 router.push(context.url)
 // wait until router has resolved possible async hooks
 router.onReady(() => {
 const matchedComponents = router.getMatchedComponents()
 // no matched routes
 if (!matchedComponents.length) {
 reject({ code: 404 })
 }
 // Call preFetch hooks on components matched by the route.
 // A preFetch hook dispatches a store action and returns a Promise,
 // which is resolved when the action is complete and store state has been
 // updated.
 Promise.all(matchedComponents.map(component => {
 return component.preFetch && component.preFetch(store)
 })).then(() => {
 isDev && console.log(`data pre-fetch: ${Date.now() - s}ms`)
 // After all preFetch hooks are resolved, our store is now
 // filled with the state needed to render the app.
 // Expose the state on the render context, and let the request handler
 // inline the state in the HTML response. This allows the client-side
 // store to pick-up the server-side state without having to duplicate
 // the initial data fetching on the client.
 context.state = store.state
 resolve(app)
 }).catch(reject)
 })
 })
}

server.js 返回一個函數,該函數接受一個從服務端傳遞過來的 context 的參數,將 vue 實例通過 promise 返回。context 一般包含 當前頁面的url,首先我們調用 vue-router 的 router.push(url) 切換到到對應的路由, 然后調用 getMatchedComponents 方法返回對應要渲染的組件, 這里會檢查組件是否有 fetchServerData 方法,如果有就會執行它。

下面這行代碼將服務端獲取到的數據掛載到 context 對象上,后面會把這些數據直接發送到瀏覽器端與客戶端的vue 實例進行數據(狀態)同步。

context.state = store.state

然后我們分別配置客戶端和服務端webpack,這里可以在我的github上fork下來參考配置,里面每一步都有注釋,這里不再贅述。

接著我們需要創建app.js:

import Vue from 'vue'
import App from './App.vue'
import store from './store'
import router from './router'
import { sync } from 'vuex-router-sync'
import Element from 'element-ui'
Vue.use(Element)
// sync the router with the vuex store.
// this registers `store.state.route`
sync(store, router)
/**
 * 創建vue實例
 * 在這里注入 router store 到所有的子組件
 * 這樣就可以在任何地方使用 `this.$router` and `this.$store`
 * @type {Vue$2}
 */
const app = new Vue({
 router,
 store,
 render: h => h(App)
})
/**
 * 導出 router and store.
 * 在這里不需要掛載到app上。這里和瀏覽器渲染不一樣
 */
export { app, router, store }

這樣 服務端入口文件和客戶端入口文件便有了一個公共實例Vue, 和我們以前寫的vue實例差別不大,但是我們不會在這里將app mount到DOM上,因為這個實例也會在服務端去運行,這里直接將 app 暴露出去。

接下來創建路由router,創建vuex跟客戶端都差不多。詳細的可以參考我的項目...

到此,服務端渲染配置 就簡單介紹完了,下面我們啟動項目簡單的看下:

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析

這里跟服務端界面一樣,不一樣的是url已經不是之前的 #/而變成了請求形式 /

這樣每當瀏覽器發送一個頁面的請求,會有服務器渲染出一個dom字符串返回,直接在瀏覽器段顯示,這樣就避免了瀏覽器端渲染的很多問題。

說起SSR,其實早在SPA (Single Page Application) 出現之前,網頁就是在服務端渲染的。服務器接收到客戶端請求后,將數據和模板拼接成完整的頁面響應到客戶端。 客戶端直接渲染, 此時用戶希望瀏覽新的頁面,就必須重復這個過程, 刷新頁面. 這種體驗在Web技術發展的當下是幾乎不能被接受的,于是越來越多的技術方案涌現,力求 實現無頁面刷新或者局部刷新來達到優秀的交互體驗。但是SEO卻是致命的,所以一切看應用場景,這里只為大家提供技術思路,為vue開發提供多一種可能的方案。

為了更清晰的對比兩次渲染的結果,我做了一次實驗,把兩個想的項目build后模擬生產環境,在瀏覽器netWork模擬網速3g環境,先來看看服務端渲染的結果:

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析

可以看到整體加載dom一共花了832ms;用戶可能在網絡比較慢的情況下從遠處訪問網站 - 或者通過比較差的帶寬。 這些情況下,盡量減少頁面請求數量,來保證用戶盡快看到基本的內容。

Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析

然后我們可以看到其中有一個vendor.js 達到了563KB,整體的加載時間達到了了8.19s,這是因為單頁面文件的原因,會把所有的邏輯代碼打包到一個js里面。可以用分webpack拆分代碼避免強制用戶下載整個單頁面應用,但是,這樣也遠沒有下載個單獨的預先渲染過的HTML文件性能高。

關于Vue服務端渲染和Vue瀏覽器端渲染性能對比的示例分析問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

澄江县| 平乐县| 稷山县| 丹寨县| 年辖:市辖区| 太谷县| 宜宾市| 乌兰浩特市| 特克斯县| 金湖县| 民和| 乌审旗| 曲水县| 新丰县| 齐齐哈尔市| 黑山县| 佛冈县| 永平县| 德惠市| 崇阳县| 日喀则市| 延吉市| 福州市| 兰坪| 徐水县| 乐平市| 和龙市| 金华市| 齐河县| 徐闻县| 吉水县| 嘉善县| 大洼县| 蒙自县| 宜丰县| 泊头市| 广安市| 驻马店市| 平和县| 高尔夫| 若尔盖县|