從壹開始前后端分離 [ vue + .netcore 補充教程 ] 三十║ Nuxt實戰(zhàn):動態(tài)路由+同構(gòu)

上期回顧

說接上文《二九║ Nuxt實戰(zhàn):異步實現(xiàn)數(shù)據(jù)雙端渲染》编兄,昨天咱們通過項目二的首頁數(shù)據(jù)處理桐经,簡單了解到了 nuxt 異步數(shù)據(jù)獲取的作用契吉,以及親身體驗了幾個重要頁面的意義漠其,整篇文章也一直在往如何實現(xiàn)服務端渲染的方向講解,因為我個人感覺這個是一個重點瑞驱,如果是只會如何使用的話,大家就可以走馬觀花的看看就行了窄坦,昨天呢唤反,遺留了幾個問題,我也想了想鸭津,還沒有想好如何通過淺顯的話來概括彤侍,如果要是搬出來教科書似的講解,感覺又不是很清晰逆趋,我就在以后的領(lǐng)悟中補充吧盏阶,這里就先說下其中的三個問題:

1、我們通過 dev 編譯闻书,生成的 .nuxt 臨時文件夾(我個人感覺他就像我們 .net core 中的 bin 文件夾)名斟,.nuxt 目錄為 npm run dev或者是npm run build 后才生成,兩個操作都執(zhí)行了 build() 方法魄眉,用于存放 Nuxt.js 的核心庫文件砰盐,如果你將一個老項目的 .nuxt 文件夾覆蓋一個新項目的 .nuxt 文件夾,新項目正常運行坑律,按照老的項目路由規(guī)則之類的都可以正常訪問岩梳。例如,你可以在這個目錄下找到 server.js 文件,描述了 Nuxt.js 進行服務端渲染的邏輯冀值,流程是:調(diào)用 nuxtServerInit 方法也物,當請求打入時,最先調(diào)用的即是 nuxtServerInit 方法列疗,可以通過這個方法預先將服務器的數(shù)據(jù)保存滑蚯,如已登錄的用戶信息等。另外作彤,這個方法中也可以執(zhí)行異步操作膘魄,并等待數(shù)據(jù)解析后返回。Middleware 層竭讳,經(jīng)過第一步后创葡,請求會進入 Middleware 層,在該層中有三步操作:讀取 nuxt.config.js 中全局 middleware 字段的配置绢慢,并調(diào)用相應的中間件方法 匹配并加載與請求相對應的 layout 調(diào)用 layoutpage 的中間件方法灿渴。調(diào)用 validate 方法,在這一步可以對請求參數(shù)進行校驗胰舆,或是對第一步中服務器下發(fā)的數(shù)據(jù)進行校驗骚露,如果校驗失敗,將拋出 404 頁面缚窿。

調(diào)用 fetchasyncData 方法棘幸,這兩個方法都會在組件加載之前被調(diào)用,它們的職責各有不同倦零, asyncData 用來異步的進行組件數(shù)據(jù)的初始化工作误续,而 fetch 方法偏重于異步獲取數(shù)據(jù)后修改 Vuex 中的狀態(tài)。


2扫茅、每次修改文件蹋嵌,都會觸發(fā)熱 webpack 的[HMR] 熱加載,因為 Nuxt.js集成了如下模塊: Vue-Router, Vue-Meta 和 Vuex (僅在使用 Vuex 狀態(tài)樹配置項 時引入)葫隙。 這樣的好處在于栽烂,不需要手工配置依賴,每次當我們修改文件恋脚,webpack 就會自動保存腺办,Nuxt.js 使用 Webpack 和 vue-loader 、 babel-loader 來處理代碼的自動化構(gòu)建工作(如打包糟描、代碼分層菇晃、壓縮等等)。

image

4蚓挤、在 network 中磺送,當有一個請求過來時驻子,服務器會新建一個vue實例,渲染(render)出需要顯示的頁面的html估灿,把得到的頁面以字符串的形式返回給客戶端崇呵。同時把相關(guān)的js文件也返回(首次請求時返回vue的runtime、webpack的runtime和app.js等文件馅袁,非首次請求返回按需加載的js文件)域慷,返回的js文件和單頁面應用(SPA)返回的差不多

app.js:基本就是你實際編寫的那個app.vue(.vue或.js),沒這個頁面跑不起來,該頁面應該提供了跟app應用相關(guān)的公共方法汗销,腳本里也明確配置了跟路由相關(guān)的信息

vendor.js:vue-cli全家桶默認配置里面這個chunk就是將所有從node_modules/里require(import)的依賴都打包到這里犹褒,所以這個就是所有node_modules/下的被require(import)的js文件

manifest.js: 最后一個chunk,被注入了webpackJsonp的定義及異步加載相關(guān)的定義(webpack調(diào)用CommonsChunkPlugin處理后模塊管理的核心,因為是核心,所以要第一個進行加載,不然會報錯)弛针,該文件確定是跟路由相關(guān)的配置信息叠骑,其中明確包含了路由的路徑,和版本號削茁,但是暫時不明白為何前端輸出會保留該配置(大概是做一些頁面動態(tài)切換效果或者是預加載的時候使用宙枷,但是頁面的預加載已經(jīng)在ssr 輸出的html 已經(jīng)包含了)

然后還有一些 pages_index.js文件,布局 layouts_blog.js文件等:default.js(跟dis/layout/default.js一致茧跋,是載入了使用的layout)

慰丛。瀏覽器接收到這些文件后,通過js文件把靜態(tài)頁面的字符串hydrate成可以交互的應用瘾杭。和SPA相比诅病,SSR返回的數(shù)據(jù)就是多了個靜態(tài)頁面(字符串形式)。

image

我又一次老生常談的說了一遍粥烁,還是感覺不是很清晰贤笆,看來自己的功底還是不行呀,如果有愛好 nuxt 或者 做過 SSR 的小伙伴页徐,歡迎聯(lián)系,咱們一起討論下银萍,今天呢变勇,接著昨天的工作,把詳情頁渲染出來吧~~~

零贴唇、今天要完成紫色的部分

image

一搀绣、動態(tài)路由實現(xiàn)詳情頁布局設計

經(jīng)過昨天的首頁渲染,大家不知道使用起來怎么樣戳气,不僅可以配置每一頁的 head 信息( TDK head)链患,還可以對整體進行配置,雖然中間引入了 plugins 插件機制瓶您,不過也是很好的做了封裝麻捻,特別是路由這一塊纲仍,大家是不是發(fā)現(xiàn)已經(jīng)完全不用配置了,Nuxt.js 依據(jù) pages 目錄結(jié)構(gòu)自動生成 vue-router 模塊的路由配置贸毕,為我們減少了很大的工作量郑叠,今天咱們就繼續(xù)對詳情頁進行配置。

什么是動態(tài)路由

昨天呢明棍,咱們開發(fā)了首頁乡革,通過地址直接可以訪問,但是在開發(fā)過程中摊腋,肯定會有這樣的頁面:通過不同的 id 加載不同的詳情頁面沸版,這些頁面雖然是一個,但是 URL 地址卻是多個兴蒸,所以我們就說這個路由是動態(tài)的视粮,還記得咱們在第一個項目中的時候,是怎么配置的么类咧?我們通過頁面接收參數(shù)來實現(xiàn)動態(tài)路由

 {
      path: "/Content/:id",
      name: "Content",
      component: Content
    },
image

在 Nuxt.js 里面定義帶參數(shù)的動態(tài)路由馒铃,需要創(chuàng)建對應的以下劃線作為前綴的 Vue 文件 或 目錄。

以下目錄結(jié)構(gòu):

pages/
--| _slug/
-----| comments.vue -----| index.vue --| users/
-----| _id.vue --| index.vue

Nuxt.js 生成對應的路由配置表為:

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue' },
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue' } ]
}

你會發(fā)現(xiàn)名稱為 users-id 的路由路徑帶有 :id? 參數(shù)痕惋,表示該路由是可選的区宇。如果你想將它設置為必選的路由,需要在 users/_id 目錄內(nèi)創(chuàng)建一個 index.vue 文件值戳。

添加博客詳情頁

1议谷、在 pages 文件夾中,添加 blog 文件夾堕虹,然后添加 _id.vue 頁面

這個時候卧晓,我們看我們的臨時編譯文件 .nuxt 中 router.js 已經(jīng)動態(tài)的增加上了上邊添加的路由

 return new Router({
    mode: 'history', base: '/',
    linkActiveClass: 'nuxt-link-active',
    linkExactActiveClass: 'nuxt-link-exact-active',
    scrollBehavior,
    routes: [
        {
            path: "/blog/:id?",
            component: _66cb1a63,
            name: "blog-id" },
        {
            path: "/",
            component: _70e72bdd,
            name: "index" }
    ],
image

2、編輯 _id.vue 文件赴捞,實現(xiàn)數(shù)據(jù)獲取

<template>
  <div class="post-page">
    <h1 class="title">{{data.btitle}}</h1>
    <p class="createTime">{{data.bCreateTime }}</p>
    <div  v-html="data.bcontent"  ></div>
  </div>
</template>

<script> import Vue from "vue";
  export default {
    layout: "blog",
    validate ({ params }) { // 校驗文章id是否為數(shù)字
      return /^\d+$/.test(params.id);
    }, async asyncData ({ params, error }) { // 獲取文章詳情
      let data = {}; try {
        data = await Vue.http.get(`blog/${params.id}`); return {
          data: data
        };
      } catch (e) { //error({ statusCode: 404, message: "出錯啦" });
 }
    },
    fetch ({ store, params }) {},
    data () { return {
        comments: []
      };
    },
    head () {//設置頁面 head 信息
      return {
        title: `${this.data.btitle}`,
        meta: [
          {
            name: "description",
            content: this.data.btitle
          }
        ]
      };
    },
    filters: {
      timeFormat: function (time) { if (!time) return ""; return time;
      }
    },
    mounted () {},
    components: {
    }
  }; </script>
//導入樣式
<style lang="css"> @import "../../static/vue-blog-sq.css"; </style>

是不是很簡單逼裆,直接添加頁面內(nèi)容,就可以實現(xiàn)路由渲染赦政,直接就可以訪問了胜宇,不過這里可能會有一個坑,如果你運氣好的話恢着,會碰上桐愉,運氣不好,就過去了掰派。

3从诲、刷新頁面查看結(jié)果

image

蒼天呀,不是吧靡羡,報錯了系洛?俊性!如果你看到這個錯誤,恭喜你比較幸運碎罚,可能你會進一步的了解到 nuxt 是如何渲染的磅废。

4、點擊 瀏覽器后退 荆烈,返回到首頁拯勉,發(fā)現(xiàn)更加崩潰

image

不僅剛剛的詳情頁不見了,就連我們的首頁數(shù)據(jù)也出錯了憔购!雖然這上面有數(shù)據(jù)宫峦,但是這個是瀏覽器緩存的,而不是我們真實的數(shù)據(jù)玫鸟,這個時候著急的小伙伴导绷,一定會很著急,穩(wěn)住屎飘,我們能贏妥曲!

首次運行服務端渲染,然后開始客戶端渲染

這個時候钦购,如果你刷新首頁檐盟,發(fā)現(xiàn)一切正常,不僅如何押桃,如果你刷新詳情頁葵萎,數(shù)據(jù)也能出現(xiàn),不信你可以試試唱凯,那這是為什么呢羡忘?

原因就在于我們刷新頁面,或者新窗口打開等等磕昼,都是新開了一個服務卷雕,我們的頁面為了實現(xiàn) SEO 先進行的是服務端渲染,講整個頁面的字符串發(fā)送過來票从,然后點擊鏈接去詳情頁的時候漫雕,我們就開始走客戶端渲染了,之所以頁面會報錯纫骑,就是我們存在跨域的問題蝎亚。

你可能會問九孩,問什么第一次不存在先馆,因為第一次是服務端渲染呀,服務端是不存在跨域問題的躺彬,只有 js 請求才會存在跨域的問題煤墙,到這里梅惯,通過這個錯誤你是不是了解到了一點兒,這個錯誤也是我故意放出來的仿野,就是為了讓大家更清楚的了解到 nuxt 是如何進行渲染的铣减。這也能說的通,為什么第一次刷新首頁有數(shù)據(jù)脚作,從詳情頁返回過來葫哗,報錯的原因了,因為第二次渲染已經(jīng)交給客戶端了球涛。

image

解決辦法很簡單劣针,還是在我們 .net core api 中 CORS 跨域配置我們的端口就行,然后一切正常了亿扁。

image

相信這個時候你對 nuxt 的渲染有了一點理解了吧捺典,如果還不是很清晰干旁,請往下看

二部念、SSR 同構(gòu)知多少

SSR 用通過同構(gòu)的方法解決了上面問題。我們先說一下 SSR 的具體表現(xiàn)厦画,比如我們現(xiàn)在有一個列表頁牍陌,列表中每一行對應一個詳情頁擎浴,那么如果直接用瀏覽器訪問列表頁時,服務器返回數(shù)據(jù)和 html 融合后的頁面呐赡,瀏覽器拿到頁面直接渲染退客,這就省去了先請求 js 再由 js 發(fā)起數(shù)據(jù)請求的過程,頁面渲染的同時請求js链嘀,js加載完成后綁定事件萌狂;從列表頁中點擊某一條到詳情頁的時候,和普通的全棧 Ajax 一樣怀泊,先請求 js 再由 js 發(fā)起數(shù)據(jù)請求茫藏,然后填充數(shù)據(jù)渲染頁面。如果將詳情頁的鏈接復制出來霹琼,直接在新瀏覽中訪問务傲,那么詳情頁會直接返回數(shù)據(jù)和 html 融合后的頁面(服務端渲染),渲染的同時請求詳情頁 js枣申,最后再綁定事件售葡。這個“服務器端拼接 html 和 html 是由同樣的頁面和組件完成的,這種前后端采用同樣的結(jié)構(gòu)在不同的環(huán)境中產(chǎn)出同樣的 html 的方案稱之為“同構(gòu)”忠藤。

什么叫前后端同構(gòu)挟伙?

為了解決某些問題(比如SEO、提升渲染速度等)vue 提供了2個方法在服務端生成一個HTML文本格式的字符串模孩。在得到了這個HTML格式的字符串之后尖阔,通常會將其組裝成一個頁面直接返回給用戶的瀏覽器贮缅。

到這里,服務端的活已經(jīng)干完了介却,然后就是瀏覽器這邊干活谴供。

瀏覽器拿到HTML文本后,立刻進行渲染將內(nèi)容呈現(xiàn)給用戶齿坷。然后加載頁面所需的 .js 文件桂肌,然后執(zhí)行 JavaScript 腳本,然后開始初始化 vue 組件

到這里問題就來了永淌。vue 初始化組件后會執(zhí)行組件內(nèi)所有 render () 方法轴或,然后生成虛擬DOM的樹形結(jié)構(gòu),然后在適當?shù)臅r候?qū)⑻摂Mdom寫到瀏覽器的真實 dom 中仰禀。因為 vue 總是根據(jù)虛擬 dom 來生成真實dom照雁,所以最后會把服務器端渲染好的HTML全部替換掉。

上面這個事情說不是問題確實也不是問題答恶,無非就是用戶看到頁面然后“閃現(xiàn)”一下饺蚊。說是問題還真是個問題,產(chǎn)品會拿著這毛病從用戶體驗的角度在各種場合和你死磕半個月悬嗓∥酆簦磕累了你索性把服務端渲染關(guān)了,然后運營又拿著SEO的問題準備和你開始撕逼了包竹。

為了解決這些問題燕酷,他們在 .renderToString(element) 方法中提供了一個 checksum 機制。前后端同構(gòu)就是保證前端和后端的dom結(jié)構(gòu)一致周瞎,不會發(fā)生重復渲染苗缩。

什么叫 首屏渲染?

簡單的說就是 vue 在瀏覽器內(nèi)存中第一次生成的虛擬 dom 樹声诸。切記是虛擬 dom 酱讶,而不是瀏覽器的dom。

了解 vue 的應該知道彼乌,所有 vue組件都有一個 render() 方法(如果使用function方式編寫的組件會把function里的所有代碼都塞到 render() 方法中去)泻肯。當 render( element, container, [callback] )方法執(zhí)行時,會執(zhí)行以下步驟:

1. 所有組件的會先進行初始化(es6執(zhí)行構(gòu)造函數(shù))慰照。
2. 所有組件的 render () 方法會被調(diào)用一次灶挟,完成這個過程后會得到一顆虛擬的 dom 樹。
3. vue 會將虛擬dom轉(zhuǎn)換成瀏覽器dom毒租,完成后調(diào)用組件的 componentDidMount() 方法告訴你已經(jīng)裝載到瀏覽器上了稚铣。

在上面這個過程成中,步驟2完成后即為完成 vue 的首屏渲染。結(jié)合 checksum 機制步驟3有可能不會執(zhí)行榛泛。

當組件狀態(tài)發(fā)生變更時( setState() 生命周期函數(shù)被調(diào)用)或者 父組件渲染時(父組件的 render() 方法被調(diào)用),當前組件的 render() 方法都會被執(zhí)行噩斟,都有可能會導致虛擬dom變更曹锨,但是這些變更和首屏渲染沒任何關(guān)系了。

在我們的項目中剃允,查看是如何渲染的

1沛简、在我們的首頁中,首次加載斥废,在 network 中椒楣,查看我們都加載了那些文件

image

這些文件咱們在文章頂部都講到了,這里說下 初始頁面牡肉,它是直接將 html 返回給我們的前端渲染捧灰,這個很好理解

2、點擊到詳情頁

image

我們發(fā)現(xiàn)這個我們的網(wǎng)絡請求统锤,并沒有繼續(xù)打包 build 走服務端渲染毛俏,而是僅僅請求了一個接口,返回了 json 數(shù)據(jù)饲窿,從這里大家應該就能看的處理煌寇,這就是所謂的雙端渲染模式。

三逾雄、總結(jié)

好啦阀溶,今天就暫時說到這里了,通過詳情頁的添加鸦泳,大家會切身體會到 nuxt 的渲染模式银锻,是如何在服務端和客戶端之間來回切換渲染的,這三篇文章大家要多看看做鹰,才能了解其中的內(nèi)涵徒仓,加油鴨~~

四、Github

https://github.com/anjoy8/Blog.Vue.Nuxt

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末誊垢,一起剝皮案震驚了整個濱河市掉弛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喂走,老刑警劉巖殃饿,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芋肠,居然都是意外死亡乎芳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奈惑,“玉大人吭净,你說我怎么就攤上這事‰鹊椋” “怎么了寂殉?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長原在。 經(jīng)常有香客問我友扰,道長,這世上最難降的妖魔是什么庶柿? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任村怪,我火速辦了婚禮,結(jié)果婚禮上浮庐,老公的妹妹穿的比我還像新娘甚负。我一直安慰自己,他們只是感情好审残,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布腊敲。 她就那樣靜靜地躺著,像睡著了一般维苔。 火紅的嫁衣襯著肌膚如雪碰辅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天介时,我揣著相機與錄音没宾,去河邊找鬼。 笑死沸柔,一個胖子當著我的面吹牛循衰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播褐澎,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼会钝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了工三?” 一聲冷哼從身側(cè)響起迁酸,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎俭正,沒想到半個月后奸鬓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡掸读,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年串远,在試婚紗的時候發(fā)現(xiàn)自己被綠了宏多。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡澡罚,死狀恐怖伸但,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情留搔,我是刑警寧澤更胖,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站催式,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏避归。R本人自食惡果不足惜荣月,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梳毙。 院中可真熱鬧哺窄,春花似錦、人聲如沸账锹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奸柬。三九已至生年,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廓奕,已是汗流浹背抱婉。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留桌粉,地道東北人蒸绩。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像铃肯,于是被迫代替她去往敵國和親患亿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容