整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          「vue基礎」手把手教你編寫一個簡單的 Vue 組件

          「vue基礎」手把手教你編寫一個簡單的 Vue 組件

          家好,本篇文章我將帶著大家一起學習如何編寫自定義組件(Components),通過「vue基礎」新手快速入門篇(一)這篇文章的學習,我們知道了 Vue 設計的目的就是為了方便我們創建基于組件UI的項目。基于這個設計原則,我們可以更專注于設計可重用、各自獨立、基于接口方式的組件,就好比樂高玩具一樣,我們可以基于組件組裝我們的項目。

          首先我們來看一個簡單的例子,比如我們有一個頭像的組件(avatar)用來展示用戶的頭像,我們只需要創建一次,我們就可以隨意將此組件添加至項目需要用到的地方,十分方便。比如如下的集成方式:

          從上述代碼我們可以看出,在項目中集成組件十分方便,就類似一個 HTML 標簽,這個標簽我們可以自定義,比如這個例子,我們定義的是 <avatar>, 除了自定義標簽,你還可以自定義任何屬性,同時可利用屬性進行傳值,比如這個例子我們自定義了src屬性。

          自定義組件

          接下來,我們來學習如何編寫Vue自定義組件。

          首先我們來先了解下,在 「vue基礎」新手快速入門篇(一)這篇文章我們通過引入JS文件快速熟悉了Vue,基于這種方式我們如何自定義組件呢。我們可以使用 Vue 這個全局對象聲明組件,聲明的函數接受兩個參數,一個是組件的名稱,一個是組件設置相關的參數對象,基于這種方式,我們可以用如下的方式,快速自定義組件:

          關于組件的命名,你可以選擇kebab case(短橫線命名)的方式,例如 my-custom-componen,或者使用 pascal case(帕斯卡命名)首字母大寫的方式進行命名,例如 MyCustomComponent,個人比較推薦使用 pascal case。

          通過這種方式注冊的組件可以在項目中的任何位置進行使用,并可以嵌套在其他組件的模板里,這樣大大提高了我們開發的靈活性。

          在本系列的上一篇文章里「vue基礎」Vue相關構建工具和基礎插件簡介

          家好,本篇文章我將帶著大家一起學習如何編寫自定義組件(Components),通過「vue基礎」新手快速入門篇(一)這篇文章的學習,我們知道了 Vue 設計的目的就是為了方便我們創建基于組件UI的項目。基于這個設計原則,我們可以更專注于設計可重用、各自獨立、基于接口方式的組件,就好比樂高玩具一樣,我們可以基于組件組裝我們的項目。

          首先我們來看一個簡單的例子,比如我們有一個頭像的組件(avatar)用來展示用戶的頭像,我們只需要創建一次,我們就可以隨意將此組件添加至項目需要用到的地方,十分方便。比如如下的集成方式:

          從上述代碼我們可以看出,在項目中集成組件十分方便,就類似一個 HTML 標簽,這個標簽我們可以自定義,比如這個例子,我們定義的是 <avatar>, 除了自定義標簽,你還可以自定義任何屬性,同時可利用屬性進行傳值,比如這個例子我們自定義了src屬性。

          自定義組件

          接下來,我們來學習如何編寫Vue自定義組件。

          首先我們來先了解下,在 「vue基礎」新手快速入門篇(一)這篇文章我們通過引入JS文件快速熟悉了Vue,基于這種方式我們如何自定義組件呢。我們可以使用 Vue 這個全局對象聲明組件,聲明的函數接受兩個參數,一個是組件的名稱,一個是組件設置相關的參數對象,基于這種方式,我們可以用如下的方式,快速自定義組件:

          關于組件的命名,你可以選擇kebab case(短橫線命名)的方式,例如 my-custom-componen,或者使用 pascal case(帕斯卡命名)首字母大寫的方式進行命名,例如 MyCustomComponent,個人比較推薦使用 pascal case。

          通過這種方式注冊的組件可以在項目中的任何位置進行使用,并可以嵌套在其他組件的模板里,這樣大大提高了我們開發的靈活性。

          在本系列的上一篇文章里「vue基礎」Vue相關構建工具和基礎插件簡介

          在一些特殊的場景中(比如低代碼、類似于APP的熱更新),我們需要從服務端動態加載.vue文件,然后將動態加載的遠程vue組件渲染到我們的項目中。今天這篇文章我將帶你學會,在vue3中如何去動態加載遠程組件。

          defineAsyncComponent異步組件

          想必聰明的你第一時間就想到了defineAsyncComponent方法。我們先來看看官方對defineAsyncComponent方法的解釋:

          定義一個異步組件,它在運行時是懶加載的。參數可以是一個異步加載函數,或是對加載行為進行更具體定制的一個選項對象。

          defineAsyncComponent方法的返回值是一個異步組件,我們可以像普通組件一樣直接在template中使用。和普通組件的區別是,只有當渲染到異步組件時才會調用加載內部實際組件的函數。

          我們先來簡單看看使用defineAsyncComponent方法的例子,代碼如下:

          import { defineAsyncComponent } from 'vue'
          
          const AsyncComp=defineAsyncComponent(()=> {
            return new Promise((resolve, reject)=> {
              // ...從服務器獲取組件
              resolve(/* 獲取到的組件 */)
            })
          })
          // ... 像使用其他一般組件一樣使用 `AsyncComp`

          defineAsyncComponent方法接收一個返回 Promise 的回調函數,在Promise中我們可以從服務端獲取vue組件的code代碼字符串。然后使用resolve(/* 獲取到的組件 */)將拿到的組件傳給defineAsyncComponent方法內部處理,最后和普通組件一樣在template中使用AsyncComp組件。

          從服務端獲取遠程組件

          有了defineAsyncComponent方法后事情從表面上看著就很簡單了,我們只需要寫個方法從服務端拿到vue文件的code代碼字符串,然后在defineAsyncComponent方法中使用resolve拿到的vue組件。

          第一步就是本地起一個服務器,使用服務器返回我們的vue組件。這里我使用的是http-server,安裝也很簡單:

          npm install http-server -g

          使用上面的命令就可以全局安裝一個http服務器了。

          接著我在項目的public目錄下新建一個名為remote-component.vue的文件,這個vue文件就是我們想從服務端加載的遠程組件。remote-component.vue文件中的代碼如下:

          <template>
            <p>我是遠程組件</p>
            <p>
              當前遠程組件count值為:<span class="count">{{ count }}</span>
            </p>
            <button @click="count++">點擊增加遠程組件count</button>
          </template>
          
          <script setup>
          import { ref } from "vue";
          const count=ref(0);
          </script>
          
          <style>
          .count {
            color: red;
          }
          </style>

          從上面的代碼可以看到遠程vue組件和我們平時寫的vue代碼沒什么區別,有templateref響應式變量、style樣式。

          接著就是在終端執行http-server ./public --cors命令啟動一個本地服務器,服務器默認端口為8080。但是由于我們本地起的vite項目默認端口為5173,所以為了避免跨域這里需要加--cors ./public的意思是指定當前目錄的public文件夾。

          啟動了一個本地服務器后,我們就可以使用 http://localhost:8080/remote-component.vue鏈接從服務端訪問遠程組件啦,如下圖:

          從上圖中可以看到在瀏覽器中訪問這個鏈接時觸發了下載遠程vue組件的操作。

          defineAsyncComponent加載遠程組件

          const RemoteChild=defineAsyncComponent(async ()=> {
            return new Promise(async (resolve)=> {
              const res=await fetch("http://localhost:8080/remote-component.vue");
              const code=await res.text();
              console.log("code", code);
              resolve(code);
            });
          });

          接下來我們就是在defineAsyncComponent方法接收的 Promise 的回調函數中使用fetch從服務端拿到遠程組件的code代碼字符串應該就行啦,代碼如下:

          同時使用console.log("code", code)打個日志看一下從服務端過來的vue代碼。

          上面的代碼看著已經完美實現動態加載遠程組件了,結果不出意外在瀏覽器中運行時報錯了。如下圖:

          在上圖中可以看到從服務端拿到的遠程組件的代碼和我們的remote-component.vue的源代碼是一樣的,但是為什么會報錯呢?

          這里的報錯信息顯示加載異步組件報錯,還記得我們前面說過的defineAsyncComponent方法是在回調中resolve(/* 獲取到的組件 */)。而我們這里拿到的code是一個組件嗎?

          我們這里拿到的code只是組件的源代碼,也就是常見的單文件組件SFC。而defineAsyncComponent中需要的是由源代碼編譯后拿的的vue組件對象,我們將組件源代碼丟給defineAsyncComponent當然會報錯了。

          看到這里有的小伙伴有疑問了,我們平時在父組件中import子組件不是也一樣在template就直接使用了嗎?

          子組件local-child.vue代碼:

          <template>
            <p>我是本地組件</p>
            <p>
              當前本地組件count值為:<span class="count">{{ count }}</span>
            </p>
            <button @click="count++">點擊增加本地組件count</button>
          </template>
          
          <script setup>
          import { ref } from "vue";
          const count=ref(0);
          </script>
          
          <style>
          .count {
            color: red;
          }
          </style>

          父組件代碼:

          <template>
            <LocalChild />
          </template>
          
          <script setup lang="ts">
          import LocalChild from "./local-child.vue";
          console.log("LocalChild", LocalChild);
          </script>

          上面的import導入子組件的代碼寫了這么多年你不覺得怪怪的嗎?

          按照常理來說要import導入子組件,那么在子組件里面肯定要寫export才可以,但是在子組件local-child.vue中我們沒有寫任何關于export的代碼。

          答案是在父組件import導入子組件觸發了vue-loader或者@vitejs/plugin-vue插件的鉤子函數,在鉤子函數中會將我們的源代碼單文件組件SFC編譯成一個普通的js文件,在js文件中export default導出編譯后的vue組件對象。

          這里使用console.log("LocalChild", LocalChild)來看看經過編譯后的vue組件對象是什么樣的,如下圖:

          從上圖可以看到經過編譯后的vue組件是一個對象,對象中有rendersetup等方法。defineAsyncComponent方法接收的組件就是這樣的vue組件對象,但是我們前面卻是將vue組件源碼丟給他,當然會報錯了。

          最終解決方案vue3-sfc-loader

          從服務端拿到遠程vue組件源碼后,我們需要一個工具將拿到的vue組件源碼編譯成vue組件對象。幸運的是優秀的vue不光暴露出一些常見的API,而且還將一些底層API給暴露了出來。比如在@vue/compiler-sfc包中就暴露出來了compileTemplatecompileScriptcompileStyleAsync等方法。

          如果你看過我寫的 vue3編譯原理揭秘 開源電子書(點擊閱讀原文跳轉到電子書),你應該對這幾個方法覺得很熟悉。

          • compileTemplate方法:用于處理單文件組件SFC中的template模塊。
          • compileScript方法:用于處理單文件組件SFC中的script模塊。
          • compileStyleAsync方法:用于處理單文件組件SFC中的style模塊。

          vue3-sfc-loader包的核心代碼就是調用@vue/compiler-sfc包的這些方法,將我們的vue組件源碼編譯為想要的vue組件對象。下面這個是改為使用vue3-sfc-loader包后的代碼,如下:

          import * as Vue from "vue";
          import { loadModule } from "vue3-sfc-loader";
          
          const options={
            moduleCache: {
              vue: Vue,
            },
            async getFile(url) {
              const res=await fetch(url);
              const code=await res.text();
              return code;
            },
            addStyle(textContent) {
              const style=Object.assign(document.createElement("style"), {
                textContent,
              });
              const ref=document.head.getElementsByTagName("style")[0] || null;
              document.head.insertBefore(style, ref);
            },
          };
          
          const RemoteChild=defineAsyncComponent(async ()=> {
            const res=await loadModule(
              "http://localhost:8080/remote-component.vue",
              options
            );
            console.log("res", res);
            return res;
          });

          loadModule函數接收的第一個參數為遠程組件的URL,第二個參數為options。在options中有個getFile方法,獲取遠程組件的code代碼字符串就是在這里去實現的。

          我們在終端來看看經過loadModule函數處理后拿到的vue組件對象是什么樣的,如下圖:

          從上圖中可以看到經過loadModule函數的處理后就拿到來vue組件對象啦,并且這個組件對象上面也有熟悉的render函數和setup函數。其中render函數是由遠程組件的template模塊編譯而來的,setup函數是由遠程組件的script模塊編譯而來的。

          看到這里你可能有疑問,遠程組件的style模塊怎么沒有在生成的vue組件對象上面有提現呢?

          答案是style模塊編譯成的css不會塞到vue組件對象上面去,而是單獨通過options上面的addStyle方法傳回給我們了。addStyle方法接收的參數textContent的值就是style模塊編譯而來css字符串,在addStyle方法中我們是創建了一個style標簽,然后將得到的css字符串插入到頁面中。

          完整父組件代碼如下:

          <template>
            <LocalChild />
            <div class="divider" />
            <button @click="showRemoteChild=true">加載遠程組件</button>
            <RemoteChild v-if="showRemoteChild" />
          </template>
          
          <script setup lang="ts">
          import { defineAsyncComponent, ref, onMounted } from "vue";
          import * as Vue from "vue";
          import { loadModule } from "vue3-sfc-loader";
          import LocalChild from "./local-child.vue";
          
          const showRemoteChild=ref(false);
          
          const options={
            moduleCache: {
              vue: Vue,
            },
            async getFile(url) {
              const res=await fetch(url);
              const code=await res.text();
              return code;
            },
            addStyle(textContent) {
              const style=Object.assign(document.createElement("style"), {
                textContent,
              });
              const ref=document.head.getElementsByTagName("style")[0] || null;
              document.head.insertBefore(style, ref);
            },
          };
          
          const RemoteChild=defineAsyncComponent(async ()=> {
            const res=await loadModule(
              "http://localhost:8080/remote-component.vue",
              options
            );
            console.log("res", res);
            return res;
          });
          </script>
          
          <style scoped>
          .divider {
            background-color: red;
            width: 100vw;
            height: 1px;
            margin: 20px 0;
          }
          </style>

          在上面的完整例子中,首先渲染了本地組件LocalChild。然后當點擊“加載遠程組件”按鈕后再去渲染遠程組件RemoteChild。我們來看看執行效果,如下圖:

          從上面的gif圖中可以看到,當我們點擊“加載遠程組件”按鈕后,在network中才去加載了遠程組件remote-component.vue。并且將遠程組件渲染到了頁面上后,通過按鈕的點擊事件可以看到遠程組件的響應式依然有效。

          vue3-sfc-loader同時也支持在遠程組件中去引用子組件,你只需在options額外配置一個pathResolve就行啦。pathResolve方法配置如下:

          const options={
            pathResolve({ refPath, relPath }, options) {
              if (relPath===".")
                // self
                return refPath;
          
              // relPath is a module name ?
              if (relPath[0] !=="." && relPath[0] !=="/") return relPath;
          
              return String(
                new URL(relPath, refPath===undefined ? window.location : refPath)
              );
            },
            // getFile方法
            // addStyle方法
          }

          其實vue3-sfc-loader包的核心代碼就300行左右,主要就是調用vue暴露出來的一些底層API。如下圖:

          總結

          這篇文章講了在vue3中如何從服務端加載遠程組件,首先我們需要使用defineAsyncComponent方法定義一個異步組件,這個異步組件是可以直接在template中像普通組件一樣使用。

          但是由于defineAsyncComponent接收的組件必須是編譯后的vue組件對象,而我們從服務端拿到的遠程組件就是一個普通的vue文件,所以這時我們引入了vue3-sfc-loader包。vue3-sfc-loader包的作用就是在運行時將一個vue文件編譯成vue組件對象,這樣我們就可以實現從服務端加載遠程組件了。


          主站蜘蛛池模板: 亚洲视频一区二区三区四区| 台湾无码一区二区| 色婷婷av一区二区三区仙踪林| 亚洲日韩精品无码一区二区三区 | 无码AⅤ精品一区二区三区| 亚洲一区二区三区成人网站 | 日韩成人一区ftp在线播放| 成人一区二区三区视频在线观看| 精品国产天堂综合一区在线| 国产高清在线精品一区二区| 美女视频在线一区二区三区| 亚洲一区二区三区高清在线观看| 在线观看国产一区二区三区| 国产自产V一区二区三区C| 国产日韩一区二区三区| 国产一区韩国女主播| 日韩电影一区二区三区| 91精品乱码一区二区三区| 精品视频在线观看一区二区| 亚洲第一区香蕉_国产a| 国产小仙女视频一区二区三区| 亚洲国产精品一区二区第一页免| 无码人妻精品一区二区三18禁| 韩国精品福利一区二区三区| 内射女校花一区二区三区| 国产精品乱码一区二区三 | 久久精品综合一区二区三区| 国产91久久精品一区二区| 亚洲国产一区视频| 亚洲一区精彩视频| 国产综合精品一区二区三区| 在线精品自拍亚洲第一区| 久久精品无码一区二区三区| 国产一区二区三区视频在线观看| 久久久综合亚洲色一区二区三区| 精品国产一区二区三区久久影院| 激情一区二区三区| 亚洲一区二区精品视频| 国内精品视频一区二区八戒| 无码一区二区三区视频| 冲田杏梨高清无一区二区|