成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

實(shí)例PK(Vue服務(wù)端渲染 VS Vue瀏覽器端渲染)

GeekGhc / 2366人閱讀

摘要:說(shuō)起,其實(shí)早在出現(xiàn)之前,網(wǎng)頁(yè)就是在服務(wù)端渲染的。二服務(wù)器渲染如此快有部分原因來(lái)自,但是排除這部分,還是比瀏覽器端要快至少幾倍。當(dāng)然,我們可以考慮首屏采用服務(wù)端渲染的方式,因?yàn)橥耆?wù)端渲染會(huì)考慮到很多問(wèn)題,比如復(fù)雜均衡等等等

Vue 2.0 開(kāi)始支持服務(wù)端渲染的功能,所以本文章也是基于vue 2.0以上版本。網(wǎng)上對(duì)于服務(wù)端渲染的資料還是比較少,最經(jīng)典的莫過(guò)于Vue作者尤雨溪大神的 vue-hacker-news。本人在公司做Vue項(xiàng)目的時(shí)候,一直苦于產(chǎn)品、客戶(hù)對(duì)首屏加載要求,SEO的訴求,也想過(guò)很多解決方案,本次也是針對(duì)瀏覽器渲染不足之處,采用了服務(wù)端渲染,并且做了兩個(gè)一樣的Demo作為比較,更能直觀的對(duì)比Vue前后端的渲染。
話(huà)不多說(shuō),我們分別來(lái)看兩個(gè)Demo:(歡迎star 歡迎pull request)

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

2.服務(wù)端渲染Demo:https://github.com/monkeyWang...

兩套代碼運(yùn)行結(jié)果都是為了展示豆瓣電影的,運(yùn)行效果也都是差不多,下面我們來(lái)分別簡(jiǎn)單的闡述一下項(xiàng)目的機(jī)理:

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

首先我們用官網(wǎng)的腳手架搭建起來(lái)一個(gè)vue項(xiàng)目

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

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

1.配置Vue-router

我們需要三個(gè)導(dǎo)航頁(yè):正在上映、即將上映、Top250;一個(gè)詳情頁(yè),一個(gè)搜索頁(yè)。這里我給他們分別配置了各自的路由。在 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
    }

  ]
})

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


   

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

2.引入vuex

Vuex 是一個(gè)專(zhuān)為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。Vuex 也集成到 Vue 的官方調(diào)試工具 devtools extension,提供了諸如零配置的 time-travel 調(diào)試、狀態(tài)快照導(dǎo)入導(dǎo)出等高級(jí)調(diào)試功能。

簡(jiǎn)而言之:Vuex 相當(dāng)于某種意義上設(shè)置了讀寫(xiě)權(quán)限的全局變量,將數(shù)據(jù)保存保存到該“全局變量”下,并通過(guò)一定的方法去讀寫(xiě)數(shù)據(jù)。

Vuex 并不限制你的代碼結(jié)構(gòu)。但是,它規(guī)定了一些需要遵守的規(guī)則:

應(yīng)用層級(jí)的狀態(tài)應(yīng)該集中到單個(gè) store 對(duì)象中。

提交 mutation 是更改狀態(tài)的唯一方法,并且這個(gè)過(guò)程是同步的。

異步邏輯都應(yīng)該封裝到 action 里面。

對(duì)于大型應(yīng)用我們會(huì)希望把 Vuex 相關(guān)代碼分割到模塊中。下面是項(xiàng)目結(jié)構(gòu)示例:

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API請(qǐng)求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我們組裝模塊并導(dǎo)出 store 的地方
    └── moving            # 電影模塊
        ├── index.js      # 模塊內(nèi)組裝,并導(dǎo)出模塊的地方
        ├── actions.js    # 模塊基本 action
        ├── getters.js    # 模塊級(jí)別 getters
        ├── mutations.js  # 模塊級(jí)別 mutations
        └── types.js      # 模塊級(jí)別 types

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

stroe文件夾建好,我們要開(kāi)始在main.js里面引用vuex實(shí)例:

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

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

3.webpack proxyTable 代理跨域

webpack 開(kāi)發(fā)環(huán)境可以使用proxyTable 來(lái)代理跨域,生產(chǎn)環(huán)境的話(huà)可以根據(jù)各自的服務(wù)器進(jìn)行配置代理跨域就行了。在我們的項(xiàng)目config/index.js 文件下可以看到有一個(gè)proxyTable的屬性,我們對(duì)其簡(jiǎn)單的改寫(xiě)

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

這樣當(dāng)我們?cè)L問(wèn)

localhost:8080/api/movie

的時(shí)候 其實(shí)我們?cè)L問(wèn)的是

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

這樣便達(dá)到了一種跨域請(qǐng)求的方案。

至此,瀏覽器端的主要配置已經(jīng)介紹完了,下面我們來(lái)看看運(yùn)行的結(jié)果:

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

run build 后會(huì)都出一個(gè)dist目錄 ,我們可以看到里面有個(gè)index.html,這個(gè)便是我們最終頁(yè)面將要展示的html,我們打開(kāi),可以看到下面:

觀察好的小伙伴可以發(fā)現(xiàn),我們并沒(méi)有多余的dom元素,就只有一個(gè)div,那么頁(yè)面要怎么呈現(xiàn)呢?答案是js append,對(duì),下面的那些js會(huì)負(fù)責(zé)innerHTML。而js是由瀏覽器解釋執(zhí)行的,所以呢,我們稱(chēng)之為瀏覽器渲染,這有幾個(gè)致命的缺點(diǎn):

**1.js放在dom結(jié)尾,如果js文件過(guò)大,那么必然造成頁(yè)面阻塞。用戶(hù)體驗(yàn)明顯不好(這也是我我在公司反復(fù)被產(chǎn)品逼問(wèn)的事情)**

**2.不利于SEO**

**3.客戶(hù)端運(yùn)行在老的JavaScript引擎上**

對(duì)于世界上的一些地區(qū)人,可能只能用1998年產(chǎn)的電腦訪(fǎng)問(wèn)互聯(lián)網(wǎng)的方式使用計(jì)算機(jī)。而Vue只能運(yùn)行在IE9以上的瀏覽器,你可能也想為那些老式瀏覽器提供基礎(chǔ)內(nèi)容 - 或者是在命令行中使用 Lynx的時(shí)髦的黑客

基于以上的一些問(wèn)題,服務(wù)端渲染呼之欲出....

二、服務(wù)器端渲染豆瓣電影

先看一張Vue官網(wǎng)的服務(wù)端渲染示意圖

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

具體實(shí)現(xiàn):

我們需要vuex,需要router,需要服務(wù)器,需要服務(wù)緩存,需要代理跨域....不急我們慢慢來(lái)。

1.建立nodejs服務(wù)

首先我們需要一個(gè)服務(wù)器,那么對(duì)于nodejs,express是很好地選擇。我們來(lái)建立一個(gè)server.js

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

這里用來(lái)啟動(dòng)服務(wù)監(jiān)聽(tīng) 8080 端口。

然后我們開(kāi)始處理所有的get請(qǐng)求,當(dāng)請(qǐng)求頁(yè)面的時(shí)候,我們需要渲染頁(yè)面

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)
})

然后我們需要代理請(qǐng)求,這樣才能進(jìn)行跨域,我們引入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);

這樣我們的服務(wù)端server.js便配置完成。接下來(lái) 我們需要配置服務(wù)端入口文件,還有客戶(hù)端入口文件,首先來(lái)配置一下客戶(hù)端文件,新建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(() => {
  // 開(kāi)始掛載到dom上
  app.$mount("#app")
})

// service worker
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
  navigator.serviceWorker.register("/service-worker.js")
}

客戶(hù)端入口文件很簡(jiǎn)單,同步服務(wù)端發(fā)送過(guò)來(lái)的數(shù)據(jù),然后把 vue 實(shí)例掛載到服務(wù)端渲染的 DOM 上。

再配置一下服務(wù)端入口文件: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 返回一個(gè)函數(shù),該函數(shù)接受一個(gè)從服務(wù)端傳遞過(guò)來(lái)的 context 的參數(shù),將 vue 實(shí)例通過(guò) promise 返回。context 一般包含 當(dāng)前頁(yè)面的url,首先我們調(diào)用 vue-router 的 router.push(url) 切換到到對(duì)應(yīng)的路由, 然后調(diào)用 getMatchedComponents 方法返回對(duì)應(yīng)要渲染的組件, 這里會(huì)檢查組件是否有 fetchServerData 方法,如果有就會(huì)執(zhí)行它。

下面這行代碼將服務(wù)端獲取到的數(shù)據(jù)掛載到 context 對(duì)象上,后面會(huì)把這些數(shù)據(jù)直接發(fā)送到瀏覽器端與客戶(hù)端的vue 實(shí)例進(jìn)行數(shù)據(jù)(狀態(tài))同步。

context.state = store.state

然后我們分別配置客戶(hù)端和服務(wù)端webpack,這里可以在我的github上fork下來(lái)參考配置,里面每一步都有注釋?zhuān)@里不再贅述。

接著我們需要?jiǎng)?chuàng)建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)

/**
 * 創(chuàng)建vue實(shí)例
 * 在這里注入 router  store 到所有的子組件
 * 這樣就可以在任何地方使用 `this.$router` and `this.$store`
 * @type {Vue$2}
 */
const app = new Vue({
  router,
  store,
  render: h => h(App)
})

/**
 * 導(dǎo)出 router and store.
 * 在這里不需要掛載到app上。這里和瀏覽器渲染不一樣
 */
export { app, router, store }

這樣 服務(wù)端入口文件和客戶(hù)端入口文件便有了一個(gè)公共實(shí)例Vue, 和我們以前寫(xiě)的vue實(shí)例差別不大,但是我們不會(huì)在這里將app mount到DOM上,因?yàn)檫@個(gè)實(shí)例也會(huì)在服務(wù)端去運(yùn)行,這里直接將 app 暴露出去。

接下來(lái)創(chuàng)建路由router,創(chuàng)建vuex跟客戶(hù)端都差不多。詳細(xì)的可以參考我的項(xiàng)目...

到此,服務(wù)端渲染配置 就簡(jiǎn)單介紹完了,下面我們啟動(dòng)項(xiàng)目簡(jiǎn)單的看下:


這里跟服務(wù)端界面一樣,不一樣的是url已經(jīng)不是之前的 #/而變成了請(qǐng)求形式 /

這樣每當(dāng)瀏覽器發(fā)送一個(gè)頁(yè)面的請(qǐng)求,會(huì)有服務(wù)器渲染出一個(gè)dom字符串返回,直接在瀏覽器段顯示,這樣就避免了瀏覽器端渲染的很多問(wèn)題。

說(shuō)起SSR,其實(shí)早在SPA (Single Page Application) 出現(xiàn)之前,網(wǎng)頁(yè)就是在服務(wù)端渲染的。服務(wù)器接收到客戶(hù)端請(qǐng)求后,將數(shù)據(jù)和模板拼接成完整的頁(yè)面響應(yīng)到客戶(hù)端。 客戶(hù)端直接渲染, 此時(shí)用戶(hù)希望瀏覽新的頁(yè)面,就必須重復(fù)這個(gè)過(guò)程, 刷新頁(yè)面. 這種體驗(yàn)在Web技術(shù)發(fā)展的當(dāng)下是幾乎不能被接受的,于是越來(lái)越多的技術(shù)方案涌現(xiàn),力求 實(shí)現(xiàn)無(wú)頁(yè)面刷新或者局部刷新來(lái)達(dá)到優(yōu)秀的交互體驗(yàn)。但是SEO卻是致命的,所以一切看應(yīng)用場(chǎng)景,這里只為大家提供技術(shù)思路,為vue開(kāi)發(fā)提供多一種可能的方案。
最后再加一下前后端渲染build后 把netWork切到3G網(wǎng)絡(luò)下進(jìn)行響應(yīng)對(duì)比:
先看看瀏覽器端:加載dom頁(yè)面花了大約800s

在對(duì)比一下服務(wù)端渲染:加載dom頁(yè)面只花了8ms 整整快了10倍,用戶(hù)可能在網(wǎng)絡(luò)比較慢的情況下從遠(yuǎn)處訪(fǎng)問(wèn)網(wǎng)站 - 或者通過(guò)比較差的帶寬。 這些情況下,盡量減少頁(yè)面請(qǐng)求數(shù)量,來(lái)保證用戶(hù)盡快看到基本的內(nèi)容。

補(bǔ)充一下一:一般高復(fù)雜度項(xiàng)目都會(huì)使用webpack的拆分,實(shí)現(xiàn)異步組件,按需加載,這樣可以有效提高時(shí)間。二:服務(wù)器渲染如此快有部分原因來(lái)自serviceworker,但是排除這部分,還是比瀏覽器端要快至少幾倍。 當(dāng)然,我們可以考慮首屏采用服務(wù)端渲染的方式,因?yàn)橥耆?wù)端渲染會(huì)考慮到很多問(wèn)題,比如復(fù)雜均衡等等等

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/82163.html

相關(guān)文章

  • 2017年3月份前資源分享

    平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    ermaoL 評(píng)論0 收藏0
  • 2017年3月份前資源分享

    平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    kamushin233 評(píng)論0 收藏0
  • 2017年3月份前資源分享

    平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    yy736044583 評(píng)論0 收藏0
  • 2017年3月份前資源分享

    平日學(xué)習(xí)接觸過(guò)的網(wǎng)站積累,以每月的形式發(fā)布。2017年以前看這個(gè)網(wǎng)址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進(jìn)擊的 Promise Effective JavaScript leeheys blog -...

    awokezhou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<