国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Vue.js 服務(wù)端渲染業(yè)務(wù)入門實(shí)踐

miya / 1625人閱讀

摘要:說起,其實(shí)早在出現(xiàn)之前,網(wǎng)頁就是在服務(wù)端渲染的。沒有涉及流式渲染組件緩存對(duì)的服務(wù)端渲染有更深一步的認(rèn)識(shí),實(shí)際在生產(chǎn)環(huán)境中的應(yīng)用可能還需要考慮很多因素。選擇的服務(wù)端渲染方案,是情理之中的選擇,不是對(duì)新技術(shù)的盲目追捧,而是一切為了需要。

作者:威威(滬江前端開發(fā)工程師)
本文原創(chuàng),轉(zhuǎn)載請(qǐng)注明作者及出處。

背景

最近, 產(chǎn)品同學(xué)一如往常笑嘻嘻的遞來需求文檔, 縱使內(nèi)心萬般拒絕, 身體倒是很誠(chéng)實(shí)。 接過需求,好在需求不復(fù)雜, 簡(jiǎn)單構(gòu)思 后決定用Vue, 得心應(yīng)手。 切好圖, 挽起袖子準(zhǔn)備擼代碼的時(shí)候, SEO同學(xué)不知何時(shí)已經(jīng)站到了背后。

"聽說你要用Vue?"
"恩..."
"SEO考慮了嗎?整個(gè)SPA出來,網(wǎng)頁的SEO咋辦?"
"奧..."

換以前, 估計(jì)只能無奈的換個(gè)實(shí)現(xiàn)方式, 但是Vue 2.0時(shí)代的到來, 給你多了一種可能。 你可以對(duì)SEO工程師說:用Vue沒問題!

想必,很多前端同學(xué)都有類似這樣的經(jīng)歷, 為了SEO,只能放棄得心應(yīng)手的框架。 SEO(Search Engine Optimization)顧名思義就是一系列為了提高 網(wǎng)站收錄排名,吸引精準(zhǔn)用戶的方案。 這么看來,SEO確實(shí)是有舉足輕重的作用。 不過,好消息是,Vue2.0的發(fā)布為SEO提供了可能, 這就是SSR(serve side render)。

說起SSR,其實(shí)早在SPA (Single Page Application) 出現(xiàn)之前,網(wǎng)頁就是在服務(wù)端渲染的。服務(wù)器接收到客戶端請(qǐng)求后,將數(shù)據(jù)和模板拼接成完整的頁面響應(yīng)到客戶端。 客戶端直接渲染, 此時(shí)用戶希望瀏覽新的頁面,就必須重復(fù)這個(gè)過程, 刷新頁面. 這種體驗(yàn)在Web技術(shù)發(fā)展的當(dāng)下是幾乎不能被接受的,于是越來越多的技術(shù)方案涌現(xiàn),力求 實(shí)現(xiàn)無頁面刷新或者局部刷新來達(dá)到優(yōu)秀的交互體驗(yàn)。 比如Vue:

- 在客戶端管理路由,用戶切換路由,無需向服務(wù)器重新請(qǐng)求頁面和靜態(tài)資源,只需要使用 ajax 獲取數(shù)據(jù)在客戶端完成渲染,這樣可以減少了很多不必要的網(wǎng)絡(luò)傳輸,縮短了響應(yīng)時(shí)間。
- 聲明式渲染(告訴 vue 你要做什么,讓它幫你做),把我們從煩人的DOM操作中解放出來,集中處理業(yè)務(wù)邏輯。
- 組件化視圖,無論是功能組件還是UI組件都可以進(jìn)行抽象,寫一次到處用。
- 前后端并行開發(fā),只需要與后端定好數(shù)據(jù)格式,前期用模擬數(shù)據(jù),就可以與后端并行開發(fā)了。
- 對(duì)復(fù)雜項(xiàng)目的各個(gè)組件之間的數(shù)據(jù)傳遞 vue  - Vuex 狀態(tài)管理模式

缺點(diǎn)大家自然猜到了, 對(duì),主要的一點(diǎn)就是不利于SEO,或者說對(duì)SEO不友好。 來看下面兩張圖;

SPA頁面的源代碼

下圖SSR頁面的源代碼

上面兩張圖就是使用了傳統(tǒng)單頁應(yīng)用和SSR的頁面源代碼, 第一張圖中,很明顯頁面的數(shù)據(jù)都是通過Ajax異步獲取,然而搜索引擎度娘家的爬蟲看到這樣空曠的源碼并不會(huì)絲毫留戀. 相反,通過服務(wù)端渲染的頁面,就有很多對(duì)于爬蟲來講有效的連接. 畢竟度娘一家獨(dú)大,看來服務(wù)端渲染確實(shí)有探究的必要了。

Vue.js 的服務(wù)端渲染是怎么回事?

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

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

怎么實(shí)現(xiàn)?

知道了Vue服務(wù)端渲染的大致流程,那怎么用代碼來實(shí)現(xiàn)呢?

1. 創(chuàng)建一個(gè) vue 實(shí)例
2. 配置路由,以及相應(yīng)的視圖組件
3. 使用 vuex 管理數(shù)據(jù)
4. 創(chuàng)建服務(wù)端入口文件
5. 創(chuàng)建客戶端入口文件
6. 配置 webpack,分服務(wù)端打包配置和客戶端打包配置
7. 創(chuàng)建服務(wù)器端的渲染器,將vue實(shí)例渲染成html

首先我們來創(chuàng)建一個(gè) vue 實(shí)例

// app.js

    import Vue from "vue";
    import router from "./router";
    import store from "./store";
    import App from "./components/app";   

    let app = new Vue({
        template: "",
        base: "/c/",
        components: {
            App
        },
        router,
        store
    });

    export {
        app,
        router,
        store
    }

和我們以前寫的vue實(shí)例差別不大,但是我們不會(huì)在這里將app mount到DOM上,因?yàn)檫@個(gè)實(shí)例也會(huì)在服務(wù)端去運(yùn)行,這里直接將 app 暴露出去。

配置 vue 路由

  import Vue from "vue";
  import VueRouter from "vue-router";

  import IndexView from "../views/indexView";
  import ArticleItems from "../views/articleItems";

  Vue.use(VueRouter);

  const router = new VueRouter({
      mode: "history",
      base: "/c/",
      routes: [
          {
              path: "/:alias",
              component: IndexView
          }, {
              path: "/:alias/list",
              component: ArticleItems
          }
      ]
  });

注意這里的 base,在服務(wù)端傳遞 path 給 vue-router 的時(shí)候要注意去掉前面的 "/c/",否則會(huì)匹配不到。

創(chuàng)建視圖組件,這里我們使用單文件組件,下面是 indexView.vue 文件的實(shí)例代碼



  

這里我們暴露一個(gè) fetchServerData 方法用來在服務(wù)端渲染時(shí)做數(shù)據(jù)的預(yù)加載,具體在哪調(diào)用,下面會(huì)講到。 beforeMount 是vue的生命周期鉤子函數(shù),當(dāng)應(yīng)用在客戶端切換到這個(gè)視圖的時(shí)候會(huì)在特定的時(shí)候去執(zhí)行,用于在客戶端獲取數(shù)據(jù)。

使用 vuex 管理數(shù)據(jù),vue2.0 的服務(wù)端官方推薦使用 STORE 來管理數(shù)據(jù),和1.0相比 api 有一些調(diào)整

  import Vue from "vue";
  import Vuex from "vuex";
  import axios from "axios";

  Vue.use(Vuex);

  let apiHost = "http://localhost:3000";

  const store = new Vuex.Store({
      state: {
          alias: "",
          ztData: {},
          courseListItems: [],
          articleItems: []
      },
      actions: {
          FETCH_ZT: ({ commit, dispatch, state }, { alias }) = {
              commit("SET_ALIAS", { alias });
              return axios.get(`${apiHost}/api/zt`)
                          .then(response => {
                              let data = response.data || {};
                              commit("SET_ZT_DATA", data);
                          })
          },
          FETCH_COURSE_ITEMS: ({ commit, dispatch, state }) => {
              return axios.get(`${apiHost}/api/course_items`).then(response => {
                  let data = response.data;
                  commit("SET_COURSE_ITEMS", data);
              });
          },
          FETCH_ARTICLE_ITEMS: ({ commit, dispatch, state }) => {
              return axios.get(`${apiHost}/api/article_items`)
                          .then(response => {
                              let data = response.data;
                              commit("SET_ARTICLE_ITEMS", data);
                          })
          }
      },
      mutations: {
          SET_COURSE_ITEMS: (state, data) => {
              state.courseListItems = data;
          },
          SET_ALIAS: (state, { alias }) => {
              state.alias = alias;
          },
          SET_ZT_DATA: (state, { ztData }) => {
              state.ztData = ztData;
          },
          SET_ARTICLE_ITEMS: (state, items) => {
              state.articleItems = items;
          }
      }
  })

  export default store;

state 使我們應(yīng)用層的數(shù)據(jù),相當(dāng)于一個(gè)倉庫,整個(gè)應(yīng)用層的數(shù)據(jù)都存在這里,與不使用vuex的vue應(yīng)用有兩點(diǎn)不同:

-  Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候,若 store 中的狀態(tài)發(fā)生變化,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新。
-  Vuex 不允許我們直接對(duì) store 中的數(shù)據(jù)進(jìn)行操作。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交(commit) mutations。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用。
action 響應(yīng)在view上的用戶輸入導(dǎo)致的狀態(tài)變化,并不直接操作數(shù)據(jù),異步的邏輯都封裝在這里執(zhí)行,它最終的目的是提交 mutation 來操作數(shù)據(jù)。 mutation vuex 中修改store 數(shù)據(jù)的唯一方法,使用 commit 來提交。

創(chuàng)建服務(wù)端的入口文件 server-entry.js

// server-entry.js
    import {app, router, store} from "./app";

    export default context => {

        const s = Date.now();
        router.push(context.url);
        const matchedComponents = router.getMatchedComponents();
        if(!matchedComponents) {
            return Promise.reject({ code: "404" });
        }

        return Promise.all(
            matchedComponents.map(component => {
                if(component.fetchServerData) {
                    return component.fetchServerData(store);
                }
            })
        ).then(() => {
            context.initialState = store.state;
            return app;
        })
    }

server.js 返回一個(gè)函數(shù),該函數(shù)接受一個(gè)從服務(wù)端傳遞過來的 context 的參數(shù),將 vue 實(shí)例通過 promise 返回。 context 一般包含 當(dāng)前頁面的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ā)送到瀏覽器端與客戶端的vue 實(shí)例進(jìn)行數(shù)據(jù)(狀態(tài))同步。

context.initialState = store.state

創(chuàng)建客戶端入口文件 client-entry.js

// client-entry.js
    import { app, store } from "./app";
    import "./main.scss";
    store.replaceState(window.__INITIAL_STATE__);
    app.$mount("#app");

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

配置 webpack

// webpack.server.config.js
    const base = require("./webpack.base.config"); // webpack 的通用配置
    module.exports = Object.assign({}, base, {
        target: "node",
        entry: "./src/server-entry.js",
        output: {
            filename: "server-bundle.js",
            libraryTarget: "commonjs2"
        },
        externals: Object.keys(require("../package.json").dependencies),
        plugins: [
            new webpack.DefinePlugin({
            "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV || "development"),
            "process.env.VUE_ENV": ""server""
            })
        ]
    })

注意這里添加了 target: "node" 和 libraryTarget: "commonjs2",然后入口文件改成我們的 server-entry.js, 客戶端的 webpack 和以前一樣,這里就不貼了。

分別打包服務(wù)端代碼和客戶端代碼

因?yàn)橛袃蓚€(gè) webpack 配置文件,執(zhí)行 webpack 時(shí)候就需要指定 --config 參數(shù)來編譯不同的 bundle。 我們可以配置兩個(gè) npm script

    "packclient": "webpack --config webpack.client.config.js",
    "packserver": "webpack --config webpack.server.config.js"

然后在命令行運(yùn)行

    npm run packclient
    npm run packserver

就會(huì)生成兩個(gè)文件 client-bundle.js 和 server-bundle.js

創(chuàng)建服務(wù)端渲染器

// controller.js

  const serialize = require("serialize-javascript");
  // 因?yàn)槲覀冊(cè)趘ue-router 的配置里面使用了 `base: "/c"`,這里需要去掉請(qǐng)求path中的 "/c"
  let url = this.url.replace(//c/, "");
  let context = { url: this.url };
  // 創(chuàng)建渲染器
  let bundleRenderer = createRenderer(fs.readFileSync(resolve("./dist/server-bundle.js"), "utf-8"))
  let html = yield new Promise((resolve, reject) => {
      // 將vue實(shí)例編譯成一個(gè)字符串
      bundleRenderer.renderToString(
          context,   // 傳遞context 給 server-bundle.js 使用
          (err, html) => {
              if(err) {
                  console.error("server render error", err);
                  resolve("");
              }
              /**
               * 還記得在 server-entry.js 里面 `context.initialState = store.state` 這行代碼么?
               * 這里就直接把數(shù)據(jù)發(fā)送到瀏覽器端啦
              **/
              html += ``;
              resolve(html);
          }
      )
  })

  yield this.render("ssr", html);

  // 創(chuàng)建渲染器函數(shù)
  function createRenderer(code) {
      return require("vue-server-renderer").createBundleRenderer(code);
  }

在 node 的 views 模板文件中只需要將上面的 html 輸出就可以了

// ssr.html
    {% extends "layout.html" %}
    {% block body %}
        {{ html | safe }}
    {% endblock %}

    

這樣,一個(gè)簡(jiǎn)單的服務(wù)端渲染就結(jié)束了。

限于篇幅,詳細(xì)的代碼請(qǐng)參考 Github代碼庫:https://github.com/ikcamp/vue...

小結(jié)

整個(gè)demo包含了:

vue + vue-router + vuex 的使用

服務(wù)端數(shù)據(jù)獲取

客戶端數(shù)據(jù)同步以及DOM hydration。

沒有涉及:

流式渲染

組件緩存

對(duì)Vue的服務(wù)端渲染有更深一步的認(rèn)識(shí),實(shí)際在生產(chǎn)環(huán)境中的應(yīng)用可能還需要考慮很多因素。

選擇Vue的服務(wù)端渲染方案,是情理之中的選擇,不是對(duì)新技術(shù)的盲目追捧,而是一切為了需要。 Vue 2.0的SSR方案只是提供了一種可能,多了一種選擇,框架本身在于服務(wù)開發(fā)者,根據(jù)不同的場(chǎng)景選擇不同的方案,才會(huì)事半功倍。

文章僅代表個(gè)人觀點(diǎn),有不妥當(dāng)?shù)胤綗┱?qǐng)大家指出,共同進(jìn)步!

iKcamp原創(chuàng)新書《移動(dòng)Web前端高效開發(fā)實(shí)戰(zhàn)》已在亞馬遜、京東、當(dāng)當(dāng)開售。

>> 滬江Web前端上海團(tuán)隊(duì)招聘【W(wǎng)eb前端架構(gòu)師】,有意者簡(jiǎn)歷至:zhouyao@hujiang.com <<

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

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

相關(guān)文章

  • 每周清單第 11 期:Angular 4.1支持TypeScript 2.3,Vue 2.3優(yōu)化

    摘要:斯坦福宣布使用作為計(jì)算機(jī)課程的首選語言近日,某位有年教學(xué)經(jīng)驗(yàn)的斯坦福教授決定放棄,而使用作為計(jì)算機(jī)入門課程的教學(xué)語言。斯坦福官方站點(diǎn)將它們新的課程描述為是最流行的構(gòu)建交互式的開發(fā)語言,本課程會(huì)用講解中的實(shí)例。 前端每周清單第 11 期:Angular 4.1支持TypeScript 2.3,Vue 2.3優(yōu)化服務(wù)端渲染,優(yōu)秀React界面框架合集 為InfoQ中文站特供稿件,首發(fā)地址為...

    warkiz 評(píng)論0 收藏0
  • 每周清單:Node.js服務(wù)實(shí)踐Vue.js 與 GraphQL,Angular 組件技巧

    摘要:前端每周清單第期微服務(wù)實(shí)踐,與,組件技巧,攻防作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。 前端每周清單第 26 期:Node.js 微服務(wù)實(shí)踐,Vue.js 與 GraphQL,Angular 組件技巧,HeadlessChrome 攻防 作者:王下邀月熊 編輯:徐川...

    wall2flower 評(píng)論0 收藏0
  • 資源系列(4)-前學(xué)習(xí)資源分享&前面試資源匯總

    摘要:特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 特意對(duì)前端學(xué)習(xí)資源做一個(gè)匯總,方便自己學(xué)習(xí)查閱參考,和好友們共同進(jìn)步。 本以為自己收藏的站點(diǎn)多,可以很快搞定,沒想到一入?yún)R總深似海。還有很多不足&遺漏的地方,歡迎補(bǔ)充。有錯(cuò)誤的地方,還請(qǐng)斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應(yīng)和斧正,會(huì)及時(shí)更新,平時(shí)業(yè)務(wù)工作時(shí)也會(huì)不定期更...

    princekin 評(píng)論0 收藏0
  • 資源分享-只為更好前

    摘要:一團(tuán)隊(duì)組織網(wǎng)站說明騰訊團(tuán)隊(duì)騰訊前端團(tuán)隊(duì),代表作品,致力于前端技術(shù)的研究騰訊社交用戶體驗(yàn)設(shè)計(jì),簡(jiǎn)稱,騰訊設(shè)計(jì)團(tuán)隊(duì)網(wǎng)站騰訊用戶研究與體驗(yàn)設(shè)計(jì)部百度前端研發(fā)部出品淘寶前端團(tuán)隊(duì)用技術(shù)為體驗(yàn)提供無限可能凹凸實(shí)驗(yàn)室京東用戶體驗(yàn)設(shè)計(jì)部出品奇舞團(tuán)奇虎旗下前 一、團(tuán)隊(duì)組織 網(wǎng)站 說明 騰訊 AlloyTeam 團(tuán)隊(duì) 騰訊Web前端團(tuán)隊(duì),代表作品WebQQ,致力于前端技術(shù)的研究 ISUX 騰...

    zxhaaa 評(píng)論0 收藏0
  • 資源分享-只為更好前

    摘要:一團(tuán)隊(duì)組織網(wǎng)站說明騰訊團(tuán)隊(duì)騰訊前端團(tuán)隊(duì),代表作品,致力于前端技術(shù)的研究騰訊社交用戶體驗(yàn)設(shè)計(jì),簡(jiǎn)稱,騰訊設(shè)計(jì)團(tuán)隊(duì)網(wǎng)站騰訊用戶研究與體驗(yàn)設(shè)計(jì)部百度前端研發(fā)部出品淘寶前端團(tuán)隊(duì)用技術(shù)為體驗(yàn)提供無限可能凹凸實(shí)驗(yàn)室京東用戶體驗(yàn)設(shè)計(jì)部出品奇舞團(tuán)奇虎旗下前 一、團(tuán)隊(duì)組織 網(wǎng)站 說明 騰訊 AlloyTeam 團(tuán)隊(duì) 騰訊Web前端團(tuán)隊(duì),代表作品WebQQ,致力于前端技術(shù)的研究 ISUX 騰...

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

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

0條評(píng)論

miya

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<