iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 25
1
Modern Web

RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄系列 第 25

Day 25. 說好的 window 和 document 呢?

  • 分享至 

  • xImage
  •  

在傳統許多 JS 套件都是和畫面渲染以及 HTML Dom 有關。然而 Nuxt 是 SSR,因此在使用套件 (或是自己開發) 的過程中,可能會遇到「window 和 document 沒有定義」這樣的錯誤訊息,接下來會介紹如何排除錯誤!

no-ssr & process.client

在 Nuxt 預設 component 裡面有一個 <no-ssr>,此 component 將在 Nuxt 3.X 中捨棄,所以我們依照官方提示改用 <client-only>

<client-only> 顧名思義,在這個 component 底下的內容都會在 client 端渲染。通常會使用 Nuxt 多半都與 SSR 和 SEO 兩大需求脫離不了關係,所以如果要讓部分畫面由 client 端渲染,要稍微注意是否跟原始需求有衝突!

下面是一段很單純的 code,我們透過 process.clientprocess.server 來確定 <client-only> 底下內容的渲染時機。

<template>
    <div>
        <div>
            A 區塊是由 {{renderSide()}} 渲染
        </div>
        <client-only>
            B 區塊是由 {{renderSide()}} 渲染
        </client-only>
    </div>
</template>

<script>
export default {
    name: 'index',
    methods: {
        renderSide() {
            if (process.server) {
                return 'Server 端';
            } else if (process.client) {
                return 'Client 端';
            }
        },
    }
}
</script>

什麼!! 你說 A、B 區塊都是顯示 Client 端 嗎? 別緊張我們看看 console:

https://ithelp.ithome.com.tw/upload/images/20190926/20112580gFeoXwm0Gn.png

看吧! A 區塊確實是在 server 端渲染好的,同時 B 區塊也只在 client 端的時候才渲染。

window is not defined

就像開頭說的,有些套件可能會用到 window 或是 document 這類前端及 browser 的物件,因此在引用、使用套見的時候就會出錯。

  1. 假設我們有個 JS module 匯出 Post 類別
// 我知道 alert 訊息很詭異,但只是個範例 ><"
window.alert('Using Post ...');

export class ITPost {
    constructor(title, content) {
        this.title = title;
        this.content = content;
    }

    setTitle(title) {
        this.title = title;
    }

    setContent(content) {
        this.content = content;
    }

    getTitle() {
        return this.title;
    }

    getContent() {
        return this.content;
    }
}
  1. 我們建立一個 post component 並利用 ITPost 類別
<template>
    <div>
        <div>{{itPost.getTitle()}}</div>
        <div>{{itPost.getContent()}}</div>
    </div>
</template>

<script>
import { ITPost } from '../helpers/ITPost';

export default {
    name: 'Post',
    props: {
        post: {
            required: true,
            type: Object,
        }
    },
    data() {
        return {
            itPost: undefined,
        };
    },
    mounted() {
        this.itPost = new ITPost(post.title, post.content);
    }
}
</script>
  1. 最後我們在 page component 上使用。
<template>
    <div>
        <it-post :post="post"></it-post>
    </div>
</template>

<script>
import ItPost from '../components/ItPost';

export default {
    name: 'test',
    components: {ItPost},
    data() {
        return {
            post: {
                title: 'Day 23. Vuex 和 Cookie 哪個好? 小朋友才做決定,我兩個都要',
                content: '...'
            }
        };
    }
}
</script>
  1. 一切看似沒問題,實際跑起來卻出現「window is not defined」
    https://ithelp.ithome.com.tw/upload/images/20190926/201125804RZqTIeraa.png

  2. 好的,那我們在 page component 加上 <client-only>

<template>
    <div>
        <client-only>
            <it-post :post="post"></it-post>
        </client-only>
    </div>
</template>

結果還是錯RRR!

  1. 回到 ItPost.vue,我們將 import 改為 process.client + require() 就好了!
<template>
    <div>
        <!-- 確保 itPost 是存在的 -->
        <template v-if="itPost">
            <div>{{itPost.getTitle()}}</div>
            <div>{{itPost.getContent()}}</div>
        </template>
    </div>
</template>

<script>
export default {
    name: 'Post',
    props: {
        post: {
            required: true,
            type: Object,
        }
    },
    data() {
        return {
            itPost: undefined,
        };
    },
    mounted() {
        // 只有在 client 端才執行
        if (process.client) {
            const { ITPost } = require('../helpers/ITPost');
            this.itPost = new ITPost(this.post.title, this.post.content);
        }
    }
}
</script>

我們從今天的範例可以看到,如果套件或是一些 Vue component 有用到 (更精確的說法是有執行到) 一些屬於前端和瀏覽器才有的相關物件與變數,我們可以透過 <client-only> 搭配 process.client 調整某些套件或是 component 要使用的時機!

Nuxt 的介紹到這邊告一個段落,明天開始要來寫寫幾個常用到的範例 (總不能連 Hello World 都沒有吧) ! 但是在結束之前,我們回顧 Nuxt lifecycle

https://ithelp.ithome.com.tw/upload/images/20190926/20112580A08rj9zkVE.png

Nuxt lifecycle 圖源自於官網

不知道在最後看到這張圖有沒有感覺呢?

首先透過 nuxtServerInit 將 request 中的 cookie 重新塞回 Vuex state;接著先透過 route 對應上的所有 component 中的 middleware 過濾與驗證,接著可以透過 validate() 確認 route 上的變數是否合法,通過驗證之後,藉由 asyncData()fetch() 串接 API 取得資料,必要時可以存入 Vuex state 當中,最後我們渲染畫面。這就是一個基礎的 page component 完整 lifecycle。

個人覺得從 Vue 轉到 Nuxt 沒有太多困擾 (當然也可能因為沒有寫到 scale 很大的案子),但是就學習曲線而言應該算是平易近人的,不知道大家覺得如何呢?

那今天就先這樣,我們明天見!


上一篇
Day 24. Plugin - 懶人救星
下一篇
Day 26. 手把手造個輪子 - Form 表單 (文長慎入)
系列文
RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言