從壹開始先後端分離 [ vue + .netcore 補充教程 ] 二八║ Nuxt 基礎:面向源碼研究Nuxt.js

前言

哈嘍你們週五好,又是一個開開心心的週五了,接下來就是三天小團圓啦,這裏先祝你們節日快樂咯,但願都沒有加班哈哈,今天公司發了月餅,嗯~時間來不及了,上週應該搞個活動抽中幾個粉絲髮月餅的,下次吧,這裏先預告一下,聖誕節活動,給粉絲送蘋果吧哈哈,不過聽起來好 low 呀,你們有好的想法能夠下邊評論或者來羣裏一塊兒交流喲~javascript

說接上文,昨天我們第一次的接觸到了一個新的框架 Nuxt《二七║ Nuxt 基礎:框架初探》,從概念上,給你們簡單說了下這個框架的產生和應用場景,你們學習這一塊必定要有必定的 vue 基礎,還有就是了解 SSR 服務端渲染的知識和原理,才能作到遊刃有餘,在以前的一系列文章中,我們已經很詳細說明了 vue 的客戶端渲染 SPA 的工做原理,今天呢,我們就重點說一下 Nuxt 的運行機制,雖然說 Vue 已經發展了近兩年,不過網上的資料仍是比較匱乏,全部的教程也都是一篇搞定,直接就是 如何安裝,如何寫頁面,我是不喜歡這樣的,雖然本身懂得的也不是不少,可是仍是想給你們分享下,至少往深層次挖掘一下,這樣你們下次使用的時候,內心纔有一個普。好啦,立刻開始今天的講解,今天沒有涉及到代碼,源碼會在下週開始 Code 。html

 

零、今天要完成綠色的部分

 

1、Nuxt 的初衷 —— 將核心專一於UI渲染

來個官方的說法vue

Nuxt.js is a minimalistic framework for server-rendered Vue applications (inspired by Next.js)java

意思就是:node

Nuxt 是服務器呈現的簡約應用程序的框架,經過對客戶端和服務端基礎架構的抽象,Nuxt.js 可讓開發者更專一於頁面的UI渲染。做用就是在 node.js 上進一步封裝,而後省去咱們搭建服務端環境的步驟,只須要遵循這個庫的一些規則就能輕鬆實現 SSR。webpack

說到這裏你們可能稍微不是很明白,舉個最簡單的栗子( 這裏就不說那個老生常談的 SEO 了,這個原本就不存在於咱們的客戶端渲染中 ):我們以前在用 vue-cli 腳手架搭建項目的時候,每次添加一個頁面,都須要去配置路由 router ,是吧,而後呢,有時候運氣很差了,在多層嵌套的路徑配置時候,還時不時的不起做用,這些原本不該該屬於咱們開發的工做的,Nuxt 就很好的解決了這個問題,Nuxt.js 根據 pages 目錄結構去生成 vue-router 配置,也就是說 pages 目錄的結構直接影響路由結構,仍是實時監聽的方式。web

再舉個栗子:以前我們在配置頁面的時候,都是經過 index.html 提供一個模板,而後再一個 app.vue 的入口程序,而後將全部的 *.vue 組件經過路由填充進去,這個我們第一次使用的時候,感受仍是很嗨皮的,不過長時間的使用的時候,就會發現其實有些問題,好比咱們若是不想讓某些組件路由顯示出來,而後只能在配置裏處理,經過配置路由是否顯示等操做,顯然又多了一個步驟,不過在 nuxt 框架中,咱們有了動態路由,只須要在名字前加上一個下劃線 _ ,好比 _id.vue ,這樣就能輕鬆搞定,是否是很方便,這個我們之後會講到,這裏暫時就不深刻了。vue-router

 

再好比咱們在以前說到組件的時候,用到了這個圖,在入門頁面中,將咱們的全部組件按需加載,可是若是咱們的項目有多個佈局須要怎麼辦?好比咱們商城首頁,和我的中心首頁有時候是不同的,當時若是你必定要保持一個風格也是能夠的,可是感受這麼配置仍是怪怪的。vue-cli

 

不過在 Nuxt.js 框架中,咱們有了新的變化,layouts對應目錄中的layouts文件夾,默認pages下的頁面走的都是 layouts/default.vue 佈局方式,可是若是本身定義的話,也能夠新增新的佈局頁。其中<nuxt/>能夠相似vueslot插槽的概念,(若是對 slot 插槽不是很明白,能夠看以前的文章 《二十║Vue基礎終篇:組件詳解+項目說明》的第四節),pages/**.vue中的內容會插在<nuxt/>內。npm

在我們的第二個項目,基於 Nuxt 的我的博客裏,我就新建了一個 blog.vue 的佈局頁面,定製了一個博客佈局:

 

<template>
  <div class="layout-default">
      <cl-header></cl-header>//定製博客頁頭
      <nuxt class="layout-main"/>//nuxt 插入內容
      <cl-footer></cl-footer>//定製博客頁腳
      <div class="layout-bg"></div>
  </div>
</template>

<script type="text/javascript">
import clHeader from "~/components/layout/header.vue";
import clFooter from "~/components/layout/footer.vue";
export default {
    data () {
        return {};
    },
    mounted () {
        this.$store.dispatch("initUser");
    },
    components: {
        clHeader,
        clFooter
    }
};
</script>

這個就是 nuxt 的分層頁面佈局。

由上邊的栗子能夠看出來,nuxt 是經過封裝,將核心原理隱藏起來,重點實現咱們的UI展現,這麼作的目的就是更有經歷去作 SSR 服務端渲染了,你們還記得我們在講 SSR 的時候麼,須要很複雜的操做配合才能實現,因此,很好的底層封裝,是咱們更好開發 SSR 框架的第一步。

 

2、Nuxt 的工做流程 —— 四步走

仍是老規矩,一言不合就放圖系列

簡單來講,當你訪問一個基於Nuxt.js構建的頁面時,發生了的事情以下:

一、當用戶訪問應用程序, 若是 store 中定義了 nuxtServerInit action,Nuxt.js 將調用它更新 store。

二、將加載即將訪問頁面所依賴的任何中間件。Nuxt 首先從 nuxt.config.js 這個文件中,加載全局依賴的中間件,以後檢測每一個相應頁面對應的佈局文件 ,最後,檢測佈局文件下子組件依賴的中間件。以上是中間件的加載順序。

三、若是要訪問的路由是一個動態路由, 且有一個相應的 validate() 方法路由的validate 方法,講進行路由校驗。

四、Nuxt.js 調用 asyncData() 和 fetch() 方法,在渲染頁面以前加載異步數據。asyncData() 方法用於異步獲取數據,並將 fetch 回來的數據,在服務端渲染到頁面。 用 fetch() 方法取回的將數據在渲染頁面以前填入store。

最後一步, 將全部數據渲染到頁面。

其中核心配置文件 nuxt.config.js 是比較重要的一個文件,整個系統的配置和插件都須要這個文件來執行,感受就像是咱們 vue-cli 腳手架裏的 main.js 入口文件同樣,具體的配置以及使用我們在之後的文章中再說明吧。上邊我們說到了仍是工做流程,那 nuxt 到底封裝了怎麼的工做機制,才能很好的執行這個流程呢,重頭戲來了

 

3、面向源碼簡要分析 Nuxt 的工做機制

一、經過 .nuxt 文件來執行咱們的工做流程

 剛剛我們說到了,nuxt.config.js 就像是一個 main.js 入口同樣,這個時候細心的你必定會發現,欸?爲何 nuxt 框架沒有 router 呢,那咱們的路由該如何配置呢,不要着急,nuxt 已經給咱們封裝了,咱們無需把注意力放到這上邊,這個時候咱們執行 

npm run dev

會發現多了一個文件(由於昨天咱們已經執行過了,如今就不須要執行了)。

 

這個是咱們項目生成的臨時文件,咱們項目運行時候配置的文件都是在這裏,你們能夠看到這裏的路由文件,沒錯,這個就是系統自動給咱們配置的路由文件,根據咱們的 pages 文件夾路徑生成的,你們還能夠看到,由app.js ,client.js 和 server.js 這兩個就是相似咱們的 SSR 中配置的那個 server.js 入口文件,而後還有 middleware.js 中間件文件,其實這個時候咱們大概能懂了,上邊咱們說的工做流程,走的就是這個 臨時文件.nuxt 文件夾中的內容,可是這個文件夾是如何生成的呢,你們請往下看。

二、.nuxt 是如何產生的

本文主要研究nuxt的運行原理,分析它從接收一條nuxt指令,到完成指令背後所發生的一系列事情,在開始本文以前,請讀者務必親自體驗過nuxt.js的使用,而且具有必定的vue.js相關開發經驗。

經過查看nuxt.js工程目錄下的package.json文件,咱們能夠看到下列幾條指令:

"scripts": { 
    "dev": "nuxt",           //    開啓一個監聽3000端口的服務器,同時提供hot-reloading功能
    "build": "nuxt build", //構建整個應用,壓縮合並JS和CSS文件(用於生產環境)
    "start": "nuxt start", //    開啓一個生產模式的服務器(必須先運行nuxt build命令)
    "generate": "nuxt generate" //構建整個應用,併爲每個路由生成一個靜態頁面(用於靜態服務器)
} 

我們還歷來沒有看過咱們的依賴包哈,今天就來看看,打開咱們的 node_modules 文件夾下的 nuxt工程文件夾 進入到到bin目錄,咱們能夠看到5個文件:

我們就說一下 dev 是如何工做的,我們先找到一個片斷,發現基本是執行了如下幾個步驟:

try {
    nuxt = new Nuxt(options)//實例化一個 nuxt 類
    builder = new Builder(nuxt)
    instance = { nuxt, builder }
  } 。。。。

  return (
    Promise.resolve()
      .then(
        () =>
          oldInstance && oldInstance.builder
            ? oldInstance.builder.unwatch()
            : Promise.resolve()
      )
      // Start build 開始執行 build() 方法
      .then(() => builder.build())
      // Close old nuxt after successful build
      .then(
        () =>
          oldInstance && oldInstance.nuxt
            ? oldInstance.nuxt.close()
            : Promise.resolve()
      )
      // Start listening 開啓監聽服務端口
      .then(() => nuxt.listen(port, host))
      // Pass new nuxt to watch chain
      .then(() => instance)
      // Handle errors
      .catch(err => onError(err, instance))
  )
}

三、那什麼是 nuxt() 類,它又是執行了什麼樣的方法呢?

這個時候,咱們繼續看源碼,進入到nuxt/lib目錄,咱們能夠看到以下的文件目錄結構:

├── app                       
├── builder                     
├── common                      
└── core                        
     ├── middleware                           
     │   ├── index.js             
     │   ├── meta.js             
     │   ├── module.js          
     │   ├── muxt.js           
     │   └── render.js          
     └── index.js                

 

 你們看這個熟悉不熟悉?!沒錯,就是和咱們上邊項目執行的時候生成的臨時文件是相似的,請和上邊的 .nuxt 臨時文件夾作對比。

 網上有個很好的總結,如圖:

 

 

上圖中每一步均可以在具體的代碼中自行瀏覽。在用戶輸入指令並實例化了Nuxt()類之後,實例化出來的nuxt對象就會執行圖中打了綠色對勾的幾個方法:build(), render(), renderRoute(), renderAndGetWindow()以及generate()方法。

同時,Nuxt()類也提供了一個close()公有方法,用於關閉其所開啓的服務器。

四、build() 進行編譯,生成 .nuxt 臨時文件

而後就是執行的 build() 方法:

簡單來講,build()方法在判斷完運行條件後,會先初始化產出目錄 .nuxt ,而後經過不一樣目錄下的文件結構來生成一系列的配置,寫入模板文件後輸出到.nuxt目錄。接下來,則會根據不一樣的開發環境來調用不一樣的webpack 配置,運行不一樣的 webpack 構建方案。

 

五、render.js文件 打包輸出渲染

在 nuxt/lib/core 目錄下找到 render.js 文件,它包含着咱們即將要分析的三個方法:render(), renderRoute(), renderAndGetWindow()。

 

經過這張圖片,咱們能夠知道nuxt對於處理「客戶端渲染」與「服務端渲染」的邏輯實際上是很是清晰的。

首先,在render()方法在處理完一系列的路徑問題後,會調用renderRoute()方法,獲取響應所需內容並完成響應。

其中renderRoute()方法會判斷當前響應是否應執行服務端渲染。若是是,則調用vue提供的bundleRenderer()方法,把html內容渲染完畢之後再總體輸出;若是不是,則直接輸出一個<div></div>字符串,交由客戶端渲染。

 // Call renderToString from the bundleRenderer and generate the HTML (will update the context as well)
    let APP = await this.bundleRenderer.renderToString(context)

    if (!context.nuxt.serverRendered) {
      APP = '<div id="__nuxt"></div>'
    }

 

最後,經過renderAndGetWindow()來檢查輸出的html是否存在問題,而後發出通知,代表html可用。

 

 4、結語

 好啦,今天由於時間的問題,暫時就說到這裏吧,其實這裏還有不少知識點沒有說到,那咱們就在以後的項目中,慢慢給你們說吧,請持續關注喲,能夠點個贊👍吧,真的是辛苦哈哈哈

相關文章
相關標籤/搜索