BACK
Featured image of post Vue 2 升級 Vue 3 之全域元件註冊問題

Vue 2 升級 Vue 3 之全域元件註冊問題

Vue 3 改變了元件註冊方式,不再提供全域註冊,必須先 Vue.createApp() 建立實體,app.component("my-component", ...) 註冊或用 components 屬性引用才能使用元件。

參考網站


前情提要

在小專案裡我主要用 Vue.js 來處理 MVVM,用 <script> 載入 vue.js,寫幾行 JavaScript 搞定,走不寫模組,不用 TypeScript,免編譯打包的「輕前端」模式,但常用邏輯還是會寫成元件(Component)方便共用。

在 Vue 2 時代,我習慣在網頁共用的 .js 裡使用 Vue.component("my-component", ...) 註冊元件,註冊一次,各網頁不需宣告就能使用元件。

正式擺脫 IE 後,終於不用再死守 Vue 2,試著將一個小專案升級到 Vue 3,遇到小麻煩。

Vue 3 改變了元件註冊方式,不再提供全域註冊,必須先 Vue.createApp() 建立實體,app.component("my-component", ...) 註冊或用 components 屬性引用才能使用元件。參考:重新認識 Vue.js - 元件的宣告與註冊

全域元件改成區域元件可減少程式間互相干擾,在軟體架構來說是正確的方向,但對簡單應用來說(例如:程式很單純,全域元件打架機率趨於零的場合),這番調整讓元件註冊變得繁瑣。

用個範例來說明。


Vue 2 註冊多個元件

原本 Vue 2 做法是在 my-components-vue2.js 中註冊多個元件:

my-components-vue2.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Vue.component('date-tag', {
  template: '<div>{{date}}</div>',
  data: function () {
    return {
      date: new Date().toJSON().slice(0, 10)
    };
  }
});
Vue.component('time-tag', {
  template: '<div>{{time}}</div>',
  data: function () {
    return {
      time: new Date().toJSON().slice(11, 19)
    };
  }
});
Vue.component('host-tag', {
  template: '<div>{{host}}</div>',
  data: function () {
    return {
      host: location.host
    };
  }
});

如此,數十支 HTML 只需載入 my-components-vue2.js 便能在網頁使用 <date-tag><time-tag> 插入元件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
  <script src="my-components-vue2.js"></script>
</head>

<body>
  <div id="app">
    <date-tag></date-tag>
    <time-tag></time-tag>
    <host-tag></host-tag>
  </div>
  <script>
    var app = new Vue({
      el: '#app'
    });
  </script>
</body>

</html>

升級 Vue 3 之後

升級 Vue 3 之後,元件的 js 跟網頁都要做一些修改:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var dateTag = {
  template: '<div>{{date}}</div>',
  data: function () {
    return {
      date: new Date().toJSON().slice(0, 10)
    };
  }
};
var timeTag = {
  template: '<div>{{time}}</div>',
  data: function () {
    return {
      time: new Date().toJSON().slice(11, 19)
    };
  }
};
var hostTag = {
  template: '<div>{{host}}</div>',
  data: function () {
    return {
      host: location.host
    };
  }
};

建立 app 寫法改為 Vue.createApp,並在宣告中透過 components 列舉要註冊的物件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <script src="https://unpkg.com/vue@3"></script>
  <script src="my-components-vue3-upgrade.js"></script>
</head>

<body>
  <div id="app">
    <date-tag></date-tag>
    <time-tag></time-tag>
    <host-tag></host-tag>
  </div>
  <script>
    var app = Vue.createApp({
      components: {
        'date-tag': dateTag,
        'time-tag': timeTag,
        'host-tag': hostTag
      }    
      // 若元件物件名稱與標籤相符,可簡寫成
      // components: { dateTag, timeTag, hostTag }
    })
    .mount('#app');
  </script>
</body>

</html>

假設我有 30 個網頁,30 個 app 都要加 components: { dateTag, timeTag, hostTag },未來若新增其他元件,所有 components 列舉都要改,這是標準的「Copy & Paste 負面教材」呀,一想就覺得很不 OK 呀。

這種情境,就是套件(Plugin,也有人翻成插件、外掛)上場的時刻。最簡單的套件寫法是寫一個 function,接收 app 及 options 參數,內部呼叫 app.component(...) 逐一註冊元件,options 則是自訂參數,可用來決定要註冊哪些元件或變更元件設定值,提高運用彈性。以下是簡單示範:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function myComponentsPlugin(app, options) {
  app.component('date-tag', {
    template: '<div>{{date}}</div>',
    data: function () {
      return {
        date: new Date().toJSON().slice(0, 10)
      };
    }
  });
  app.component('time-tag', {
    template: '<div>{{time}}</div>',
    data: function () {
      return {
        time: new Date().toJSON().slice(11, 19)
      };
    }
  });
  app.component('host-tag', {
    template: '<div>{{host}}</div>',
    data: function () {
      return {
        host: location.host
      };
    }
  });
}

如此,呼叫端可簡化為 .use(myComponentsPlugin),不需把所有元件列出來,未來要新增元件,修改 my-components-vue3.js 即可,保留原先全域元件的簡單方便:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <script src="https://unpkg.com/vue@3"></script>
  <script src="my-components-vue3.js"></script>
</head>

<body>
  <div id="app">
    <date-tag></date-tag>
    <time-tag></time-tag>
    <host-tag></host-tag>
  </div>
  <script>
    var app = Vue.createApp({
      //...
    }).use(myComponentsPlugin)
    .mount('#app');
  </script>
</body>

</html>


comments powered by Disqus