Try   HackMD

Vue3 - 進階篇(Nuxt)

tags: Vue Nuxt HiSKIO

SPA v.s. SSR

SPA (Single Page Application,單頁式應用)

所有畫面由前端 JS 操控:HTML 渲染,讀到 .js檔案 後再把相對應的 html 丟到畫面上。
只有一個頁面,用起來卻像 app 一樣。
右鍵 -> 檢視網頁原始碼 -> 內容幾乎是空的。
只更新部分畫面,而不是暴力的每次都砍掉重練。

優點

  1. 使用者體驗佳 (利用 ajax,著名例子:Gmail)
  2. 必要場景 (例如:音樂播放網站,不會因為換頁而停掉音樂)

缺點

  1. SEO 差:
    因為 index.html 幾乎是空的,google 爬蟲效果有待討。
    網頁初始畫面在 API 回來之前不存在有意義的資料,搜尋引擎爬蟲抓不到網頁的內容。
  2. JS 打包檔大

SSR (Server Side Rendering,伺服器端渲染)

所有畫面為伺服器端收到請求後,解析出完整的 HTML 後,再傳給使用者端。

優點

  1. SEO 佳
  2. 首頁加載速度快

缺點

  1. 使用者體驗差

綜合兩者 SSR + SPA:

第一個頁面由 Server side render,之後的操作還是由 Client side render

MVC 就是因為 code 變得越來越亂,所以將職責區分清楚的一種設計模式。SPA 就是因為想增進使用者體驗,而出現的一種在前端利用 Ajax 達成不換頁的方法。SSR 就是因為要解決 SPA 的 SEO 問題而出現的解法。

看使用情境決定決定使用哪種方式:
例如:音樂播放網站(不換頁)、後台系統網站(不需要 SEO),就適合使用 SPA;反之,產品網站(需要 SEO),較適合使用 SSR。

參考資料

What is Nuxt ?

詳見官網介紹:Nuxt.js - The Intuitive Vue Framework
Nuxt.js 是一個基於 Vue.js 的通用應用框架。

  1. SERVER SIDE RENDERED:預設利用 Vue.js 開發伺服器端渲染(SSR)的應用所需要的各種配置。
  2. STATICALLY GENERATED:提供了一種命令叫:nuxt generate,為基於 Vue.js 的應用提供生成對應的靜態站點的功能。

How Nuxt Run ?

Nuxt.js 是建構在 Node.js 環境之上。
Nuxt 將專案打包成兩份,一份是給 Client 端,一份是給 Server 端。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Install Nuxt

npx create-nuxt-app <project-name>

npm v.s npx

npm:是套件管理工具,可以把想要的 node 套件,透過 npm 安裝在 local 專案位置或是 global 全局性整台電腦的環境下。

npx:npx 是在 npm v5.2.0 之後內建的指令,也是一種安裝工具,讓我們可以更方便的安裝或是管理依賴(dependencies)。

差別一:npm 永久安裝 v.s. npx 安裝後即移除

差別二:npx 更方便的運行本地套件

差別三:npx 直接執行 Github 檔案

差別四:npx 可以指定 node 版本、命令的版本

主要特點:

  1. 臨時安裝可執行依賴包,不用全局安裝,不用擔心長期的污染。
  2. 可以執行依賴包中的命令,安裝完成自動運行。
  3. 自動加載 node_modules 中依賴包,不用指定$PATH。
  4. 可以指定 node 版本、命令的版本,解決了不同項目使用不同版本的命令的問題。

參考資料:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Nuxt 架構解析

Directory Structure - NuxtJS

pages & layouts & components: .vue

pages

動態頁面

:question: Pages directory - NuxtJS

layouts

預設頁面 Default Layout

  • 為所有頁面所共用。其中 <Nuxt /> 即為 pages 中的 .vue檔內容 (很像 Vue CLI 中的 router-view )
<template> <div> <TheHeader /> <Nuxt /> <TheFooter /> </div> </template>

客製化頁面 Custom Layout

  • 為特定頁面共用相同頁面

例如:

layouts/blog.vue

<template> <div> <div>My blog navigation bar here</div> <Nuxt /> </div> </template>

pages component 中指定要呈現的 layouts
pages/posts.vue

<script> export default { layout: 'blog', // OR layout (context) { return 'blog' } } </script>

錯誤頁面 Error Page

當有錯誤(e.g. 404, 500)發生時呈現的頁面。

雖然該檔案位於 layouts 之下,但屬於 pages components!
因此不會有 <Nuxt /> 在其中。

layouts/error.vue

<template> <div class="container"> <h1 v-if="error.statusCode === 404">Page not found</h1> <h1 v-else>An error occurred</h1> <NuxtLink to="/">Home page</NuxtLink> </div> </template> <script> export default { props: ['error'], layout: 'blog' // you can set a custom layout for the error page } </script>

components

  • Components directory - NuxtJS
  • 放所有共用的元件。
  • v2.13,以後可以自動引入進任何 pages,layoutscomponents 中,無需手動 import。

nuxt.config.js 中設定:

export default { components: true }

詳細設定請見官網

動態載入 Dynamic Imports

動態載入即所謂的懶加載。只要在 template 中加入 Lazy 前綴詞。

layouts/default.vue

<template> <div> <TheHeader /> <Nuxt /> <LazyTheFooter /> </div> </template>

當事件被觸發時,動態載入元件。
pages/index.vue

<template> <div> <h1>Mountains</h1> <LazyMountainsList v-if="show" /> <button v-if="!show" @click="show = true">Show List</button> </div> </template> <script> export default { data() { return { show: false } } } </script>

巢狀路徑 Nested Directories

若元件放置位置如下:

components/
  base/
      foo/
         Button.vue

則該元件的名稱則包含其路徑位置,即為 <BaseFooButton />
若不想包含特定路徑在元件名稱中,則需要在 nuxt.config.js 中指明:

components: { dirs: [ '~/components', '~/components/base' ] }

則原本的 <BaseFooButton /> 就可以改成 <FooButton />

assets & static: 靜態檔案

  • assets : 會經過 Nuxt 編譯壓縮打包。例如:第三方套件的 css 檔。
  • static : 不會經過 Nuxt 編譯壓縮打包。例如:json 檔。

middleware & store & plugins

  • middleware : 進入頁面前需要處理的事。例如:驗證。
  • store : 放 Vuex 的資料。
  • plugins: 自定義 Nuxt 的套件。從 nuxt.config.js 中做引入,供全局環境使用。

Nuxt 生命週期

Nuxt Lifecycle - NuxtJS

asyncData v.s. fetch

asyncData

頁面被渲染以前,在 server 端執行的生命週期函式。
例如:非同步處理,做 SEO。

  1. asyncData 只會執行一次!
  2. asyncData 只能在 pages 資料夾底下的 .vue 中使用!
  3. asyncData return 的值會覆蓋掉 data 中取相同名稱的值。可以直接 return 值到 template。
  4. 無法使用與 window, document 等瀏覽器相關的 api。例如:alert
  5. 沒有 this ! (this 是指 Vue 實體)

fetch

:warning: 只能在 Nuxt 2.12 之後的版本使用。

頁面被渲染以前,在 server 端及 client 端執行的生命週期函式。

  • server 端:當 route 被渲染時,初始頁面被渲染前。
  • client 端:當進行導向時(navigating)。

asyncData 不同的地方:

  1. 可以在任何 .vue component 中使用 (不限於 pages 資料夾)。
  2. 可以取用 this。因為在 created 之後,Vue 的實體就會被 new 出來。
  3. 不行 return 值到 template。

快捷 shortcuts

提供 $fetchState 在非同步資料取得時使用

  • $fetchState.pending: Boolean。讓你在 client 端判斷 fetch 執行完沒。可以在 pending 為 true 時,放 loading 的顯示。
<div v-if="$fetchState.pending">Loading...</div>
  • $fetchState.error: null or Error ({Obj})。判斷 fetch 是否執行出錯,可將 Error 呈現出來。
<div v-if="$fetchState.error">Error {{ $fetchState.error }}</div>
  • $fetchState.timestamp: Integer。最後一次執行 fetch 的時間。適合與 keep-alive 的暫存功能搭配使用。

keep-alive

keep-alive 可以保存已造訪過頁面的 fetch 執行結果。

<Nuxt /><NuxtChild /> 中加入 keep-alive 指令。

<template> <Nuxt keep-alive /> </template>

可以使用 keep-alive-props 來指定要傳入的 props

<template> <Nuxt keep-alive :keep-alive-props="{ max: 10 }" /> </template>

activated 生命週期

只有在距上次呼叫 fetch 後的 30 秒,才會再次執行 fetch
使用 this.$fetch() 可手動執行 fetch 生命週期。

<template> ... </template> <script> export default { data() { return { posts: [] } }, activated() { // Call fetch again if last fetch more than 30 sec ago if (this.$fetchState.timestamp <= Date.now() - 30000) { this.$fetch() } }, async fetch() { this.posts = await fetch('https://api.nuxtjs.dev/posts').then(res => res.json() ) } } </script>

參數 Options

  • fetchOnServer : Boolean or Function (預設為 true)

fetchOnServer: false,則 fetch 只會在 client 端執行。

export default { data() { return { posts: [] } }, async fetch() { this.posts = await fetch('https://api.nuxtjs.dev/posts').then(res => res.json() ) }, // call fetch only on client-side fetchOnServer: false }

監聽 $route.query

當 router 的 query string 改變時,是不會執行 fetch 生命週期。可以使用 watch 進行監聽。

export default { watch: { '$route.query': '$fetch' }, async fetch() { // Called also on query changes } }

beforeCreate & created

beforeCreatecreated 在 server 端與 client 端都會被執行。

export default { asyncData () { console.log('asyncData') }, beforeCreate () { console.log('beforeCreate') }, created () { console.log('created') }, fetch () { console.log('fetch') }, beforeMount () { console.log('beforeMount') }, mounted () { console.log('mounted') } }

Meta tags & SEO

Meta Tags and SEO - NuxtJS