在進入這個主題前,首先需要了解什麼是網頁的生命週期。在以往傳統網頁,每頁都是獨立分開的個體,生命週期的起算,從網頁開始讀取,到DOM(Document Object Model)的產生,至整個頁面加載完畢,及離開網頁的事件。SPA由於是單頁應用,除了一開始加載頁面,內容的切換實際上都在同一頁,所以在切換內容時,不會觸發網頁原本的讀取與離開事件。 為了實現這個方法,前端框架也有所謂的Lifecycle Methods,讓元件可以在發生不同事件當中做要做的事情。
在React的世界裡,每個component有屬於自己的生命週期,我們可以看下面這張圖:
React component完整的生命週期過程包含mounting(掛載) -> updating(更新) -> unmounting(卸載)。可以看到熟悉的render發生在mounting至updating階段,另外在mounting階段的下方有個componentDidMount方法,這代表在DOM加載完成時觸發,看看React對這部份的說明:
在一個 component 被 mount(加入 DOM tree 中)後,componentDidMount() 會馬上被呼叫。需要 DOM node 的初始化應該寫在這個方法裡面。如果你需要從遠端終端點(remote endpoint)請求資料的話, 此處非常適合進行實例化網路請求(network request)。
當要透過操作頁面中的DOM元件來達成一些行為,為了避免DOM還沒有加載進來操作會出現錯誤,就使用componentDidMount這個方法來操作。
所以若是我們想要在SPA中操作Component裡的DOM元件,是否代表在render方法後觸發另一個方法就可以了?
我們可以常會看到一些網站在載入完成時跳出友善擾人的廣告視窗,今天來試試看這樣的效果。首先到Bootstrap官網找到Modal彈窗元件,複製代碼後把它加入Home.js,作為在進入畫面時顯示的提示訊息:
src/pages/Home.js
import { App } from './App'
export const Home = {
render: () => {
const modal = `
<div class="modal" tabindex="-1" id="modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">重要公告</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>假期只剩三天QQ</p>
</div>
</div>
</div>
</div>
`
const content = `
<div class="container">
<h1>Home page</h1>
<div>Welcome to my page!</div>
</div>
${modal}
`
return App.render(content)
},
}
我們宣告modal並放在原本宣告的content裡,因為bootstrap預設Modal是隱藏的,只有在呼叫顯示時才會出現,呼叫的方法為$('#modal').modal('show')。如果在render階段裡使用呼叫,可以預期的是因為還沒完成render,頁面中還沒產生DOM,所以什麼事也沒發生。
為了可以正常運作,應該要在render階段後才對modal使用呼叫方法,所以我們嘗試在Home Component物件中加入一個新的屬性mount,同樣使用callback的arrow function,然後在裡面放入呼叫的方法如下:
src/pages/Home.js
//引入JQuery
import $ from 'jquery'
export const Home = {
mount: () => {
//呼叫modal
$('#modal').modal('show')
},
render: () => {
//...
因為呼叫顯示modal有使用到JQuery,開頭也記得一併引入。
接著我們到原本的Router模組裡,把元件內的mount方法加入進來:
src/routes/Router.js
export const Router = () => {
//...
// 3.將元件內容渲染至畫面
document.querySelector('#wrapper').innerHTML = component.render()
// 4.元件render後呼叫
component.mount()
}
這樣就算完成了,可以看到在render後增加了呼叫元件mount屬性,如此就可以確保在DOM產生後對modal進行呼叫了。
參考資料:
重新認識 JavaScript 番外篇 (6) - 網頁的生命週期