iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
0
自我挑戰組

不用前端框架 手把手打造基礎SPA網站系列 第 22

[DAY22]進階應用 - 元件內部共用函式調用

在開發階段通常會建立許許多多的函式來開發功能,為了有效管理程式碼,有時會重複地使用部份函式,所以我們今天要來實做在SPA中,如何管理並使用共用的函式,以下正式開始進入主題。

元件內部共用函式

這裡拿前幾天實做彈出視窗的例子,載入Home頁面時會顯示公告視窗,並且搭配state來控制只讓視窗在第一次出現。這邊想要在介面上做一個按鈕,當按下時視窗會再次跳出。我們試試在render裡加入button:

src/pages/Home.js

export const Home = {
  state: {
    //預設顯示狀態
    show: true,
  },
  mount: function () {
    if (this.state.show === true) {
      //呼叫modal
      $('#modal').modal('show')
    }
    //寫入狀態
    this.state.show = false
  },
  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>這裡是重要公告的內容....blablabla</p>
            </div>
          </div>
        </div>
      </div>
    `
    const content = `
      <div class="container">
        <h1>Home page</h1>
        <div>Welcome to my page!</div>
        <button class="btn btn-primary" id="button">顯示公告</button>
      </div>
      ${modal}
    `
 
    return App.render(content)
  },
}

我們加了一個「顯示公告」的按鈕,並給予id值button,作為綁定監聽事件用。接著在Home裡新增listener屬性,註冊事件類型與handler:

src/pages/Home.js

export const Home = {
  listener: {
    click: function (e) {
      if (e.target.id === 'button') {
        //呼叫modal
        $('#modal').modal('show')
      }
    },
  },
  state: {
  //...

這裡可以看到事件類型是click,由於我們對body進行綁定,所以運用監聽事件的冒泡階段來獲得點擊時的目標,判斷事件目標的id,如果符合才會執行顯示視窗。

重點來了,看到這裡應該就會發現,在listener與mount有重複的彈出視窗程式碼,雖然這邊僅有短短一行,但這畢竟是用jQuery做的示範,真實情況可能會有很多行。如果在元件內有類似這樣的共同函式,該怎麼應用呢?

方法1:在元件外部宣告共用的函式

src/pages/Home.js

const showModal = () => {
  $('#modal').modal('show')
}
 
export const Home = {
//...

第一種是直接在外部直接宣告共用到的函式(showModal),當要調用時直接呼叫showModal()即可。好處是簡單方便,適合單一行為且複雜性低的動作。

方法2:在元件內部增加屬性

src/pages/Home.js

export const Home = {
  func: {
    showModal: () => {
      $('#modal').modal('show')
    },
  },
  listener: {
  //...

第二種相信你們應該也猜到了,在元件新增一個func屬性,裡面在定義屬性與callback,要調用時可以用Home.func.showModal()做呼叫,好處是結構化呈現,在調用上可以明確知道是來自Home元件的函式。

該用什麼方法?

好了,看到這裡其實兩個是差不多的。這兩種方式哪個比較優?沒有明確的標準答案,要視開發複雜程度與習慣而定,若單純在Component內部使用,其實沒有太大的差別,都可以相互調用和帶入參數;但如果要輸出模組給別的Component使用,就會比較推第二種方法(關於這點後面文章會來實做)。

全域共用函式

我們常會看到許多網站在讀取時,會顯示一個圈圈轉呀轉,等到讀取完畢時才會消失,這個東西叫spinner或loader。這個功能幾乎所有頁面都會使用到,但不太可能在SPA裡每頁宣告一樣的spinner函式,所以會需要設定在全域供任何地方調用。這裡該如何應用呢?

方法1:建立在共同函式模組

這個方法跟昨天實做的eventListener.js很相像,首先在src/utils目錄下新增一個func.js,作為共同使用函式的模組:

src/utils/func.js

export const loading = () => {
  const spinners = `
    <div class="d-flex justify-content-center align-items-center bg-light h-100 w-100 fixed-top" id="spinner" style="z-index:1100">
      <div class="spinner-grow text-primary" role="status"></div>
    </div>
  `
 
  //插入HTML至指定元件最後一個子元素之後
  document.querySelector('body').insertAdjacentHTML('beforeend', spinners)
 
  //模擬讀取2秒後移除spinners
  setTimeout(function () {
    document.querySelector('#spinner').remove()
  }, 2000)
}

內容的部份使用到Bootstrap的spinners,因為會讓所有頁面共用所以需要做命名輸出模組。我們在spinner外面套一層div,運用css flex並讓它擴展至全畫面,接著渲染在畫面上使用了Element.insertAdjacentHTML方法,表示會將spinner插入body節點最後一個子元素之後(也就是body標籤結束前面)。最後使用setTimeout這個定時器方法,2秒時間一到就將spinner做移除,來模擬開始到讀取完畢這段存取後端的時間差。

最後記得要在使用的地方做匯入,並呼叫匯入的函式:

import { loading } from '../utils/func'
 
export const Home = {
  //...
  mount: function () {
    //顯示讀取
    loading()
  //...
 

另外這裡有稍微做一點手腳,為了使的讀取畫面會保持在最上層,在spinner的外層容器給予style並設置z-index值高過彈窗modal。可以看到Bootstrap裡modal的z-index設定值為1050。

方法2: 建立單一讀取畫面模組

這個方式其實跟剛剛模式、內容完全一樣,只是建立的位置不一樣。我們可以在src下建立components,這目錄專門用來放共同使用的元件(注意與pages裡的Component做分隔),例如sidebar可以是一個component、spinner也是一個模組...等(使用時記得一樣要匯入函式後呼叫)。

哪個比較好?

如果你問哪種方法好,還是同一句話,要視開發複雜程度與習慣。如果共用函式少,其實不太需要匯出太多模組到components資料夾,在func.js集中做管理就好,因為也就那幾個共用函式。如果共用函式數量可觀,那麼建議可另外輸出專門的模組,名字也比較好認。

小結

不管用什麼方式實做,相信在評估後可以找到適合自己開發的方式,只要可以把共同的函式給管理好,透過模組化輸出匯入開發並且能夠正常使用,選什麼方法也就不是問題了。今天內容就到這裡,我們明天見!


上一篇
[DAY21]進階應用 - 監聽事件的處理(下篇)
下一篇
[DAY23]進階應用 - 將網址參數傳入元件
系列文
不用前端框架 手把手打造基礎SPA網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言