哈嘍你們週五好,又是一個開開心心的週五了,接下來就是三天小團圓啦,這裏先祝你們節日快樂咯,但願都沒有加班哈哈,今天公司發了月餅,嗯~時間來不及了,上週應該搞個活動抽中幾個粉絲髮月餅的,下次吧,這裏先預告一下,聖誕節活動,給粉絲送蘋果吧哈哈,不過聽起來好 low 呀,你們有好的想法能夠下邊評論或者來羣裏一塊兒交流喲~javascript
說接上文,昨天我們第一次的接觸到了一個新的框架 Nuxt《二七║ Nuxt 基礎:框架初探》,從概念上,給你們簡單說了下這個框架的產生和應用場景,你們學習這一塊必定要有必定的 vue 基礎,還有就是了解 SSR 服務端渲染的知識和原理,才能作到遊刃有餘,在以前的一系列文章中,我們已經很詳細說明了 vue 的客戶端渲染 SPA 的工做原理,今天呢,我們就重點說一下 Nuxt 的運行機制,雖然說 Vue 已經發展了近兩年,不過網上的資料仍是比較匱乏,全部的教程也都是一篇搞定,直接就是 如何安裝,如何寫頁面,我是不喜歡這樣的,雖然本身懂得的也不是不少,可是仍是想給你們分享下,至少往深層次挖掘一下,這樣你們下次使用的時候,內心纔有一個普。好啦,立刻開始今天的講解,今天沒有涉及到代碼,源碼會在下週開始 Code 。html
來個官方的說法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/>能夠相似vue
中slot
插槽的概念,(若是對 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 框架的第一步。
仍是老規矩,一言不合就放圖系列
簡單來講,當你訪問一個基於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 到底封裝了怎麼的工做機制,才能很好的執行這個流程呢,重頭戲來了
剛剛我們說到了,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.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/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() 方法:
簡單來講,build()方法在判斷完運行條件後,會先初始化產出目錄 .nuxt ,而後經過不一樣目錄下的文件結構來生成一系列的配置,寫入模板文件後輸出到.nuxt目錄。接下來,則會根據不一樣的開發環境來調用不一樣的webpack 配置,運行不一樣的 webpack 構建方案。
在 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可用。
好啦,今天由於時間的問題,暫時就說到這裏吧,其實這裏還有不少知識點沒有說到,那咱們就在以後的項目中,慢慢給你們說吧,請持續關注喲,能夠點個贊👍吧,真的是辛苦哈哈哈