<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    遠程組件實踐

    共 8326字,需瀏覽 17分鐘

     ·

    2023-06-20 10:33

    大廠技術  高級前端  Node進階

    點擊上方 程序員成長指北,關注公眾號

    回復1,加入高級Node交流群

    前言

    從服務端遠程下載一個 JS 文件并注冊成組件。

    一、什么是遠程組件

    這里是指在生產(chǎn)環(huán)境中,從服務端遠程下載一個 JS 文件并注冊成組件,使其在生產(chǎn)環(huán)境中能夠使用。

    二、背景

    1. 項目背景

    我們的項目是個低代碼平臺,它內(nèi)置了一些常用組件,可供用戶使用。但內(nèi)置組件不能夠完全滿足用戶的需求,我們希望能夠提供一個入口,用戶自己上傳自定義組件。這樣可以極大的增加項目的可拓展性。

    低代碼平臺

    需求流程

    這也是遠程組件的一個典型場景。

    【第2837期】低代碼在數(shù)據(jù)分析場景的應用

    2. 技術背景

    項目使用的技術棧為 vue2。我們限定自定義組件開發(fā)的技術棧也是 vue2。

    三、技術實現(xiàn)

    1. 流程步驟

    幾個關鍵步驟

    • 用戶按照 UMD 模塊規(guī)范開發(fā)組件

    • 注冊組件

    • 獲取到組件模塊

    • 渲染組件

    • 響應用戶的操作

    2. 什么是 UMD 模塊規(guī)范呢?

    所謂 UMD (Universal Module Definition),就是一種 javascript 通用模塊定義規(guī)范,讓你的模塊能在 javascript 所有運行環(huán)境中發(fā)揮作用。

    簡言之就是能兼容主流 javascript 模塊的規(guī)范,如 CommonJS, AMD, CMD 等。

    下面是規(guī)范的代碼,以及對應的說明:

     (function(root, factory) {
    if (typeof module === 'object' && typeof module.exports === 'object') {
    // 這是 commonjs 模塊規(guī)范,nodejs 環(huán)境
    var depModule = require('./umd-module-depended')
    module.exports = factory(depModule);
    } else if (typeof define === 'function' && define.amd) {
    // 這是 AMD 模塊規(guī)范,如 require.js
    define(['depModule'], factory)
    } else if (typeof define === 'function' && define.cmd) {
    // 這是 CMD 模塊規(guī)范,如sea.js
    define(function(require, exports, module) {
    var depModule = require('depModule')
    module.exports = factory(depModule)
    })
    } else {
    // 沒有模塊環(huán)境,直接掛載在全局對象上
    root.umdModule = factory(root.depModule);
    }
    }(this, function(depModule) {
    // depModule 是依賴模塊
    return {
    name: '我自己是一個umd模塊'
    }
    }))

    如果在 html 中直接使用 script 標簽引用 umd 格式的 js 文件。就會走到第四個條件分支,即 直接掛載在全局對象上 。這個全局對象指的就是 window。

    在我們的項目中,是直接使用 script 標簽引用的。但沒有走第四個條件分支。之后會說明原因及做法。

    3. 如何打包 UMD 規(guī)范的組件文件

    以 Vue CLI 為例。當運行 vue-cli-service build 時,可以通過 --target 選項指定構(gòu)建目標為 庫 。

    上圖中 庫 的名字為 myLib 。[entry] 為需要構(gòu)建的入口文件。構(gòu)建一個庫會輸出一些文件,需要我們關注的是下面兩個:

    • dist/myLib.umd.js:一個直接給瀏覽器或 AMD loader 使用的 UMD 包

    • dist/myLib.umd.min.js:壓縮后的 UMD 構(gòu)建版本

    可見,使用 Vue CLI 打包 UMD 規(guī)范文件是十分方便的。

    在我們的項目中,打包命令為:

     vue-cli-service build --target lib --name Demo ./index.js

    我們的 庫 名是 Demo 。

    ./index.js 文件的內(nèi)容為:

     import Demo from './packages/demo/index.vue'

    export default {
    version: '1.0.0',
    Demo
    }

    ./packages/demo/index.vue 文件的內(nèi)容為:

     <template>
    <div :style="`width: ${config.width}px;`"></div>
    </template>

    <script>
    export default {
    name: "demo",
    title: "demo演示組件",
    props: {
    config: {
    type: Object,
    }
    },
    watch: {
    config: {
    handler: function (_, newConfig) {
    this.width = newConfig.width
    },
    deep: true
    }
    },
    mounted() {
    this.width = this.config.width
    },
    getDefaultConfig() {
    return {
    defaultProperties: [
    {
    title: "邊長",
    name: "width",
    type: "SingleInput",
    value: 200
    }
    ]
    }
    }
    }
    </script>
    4. 組件的注冊

    當使用 Vue CLI 的庫模式打包時,我們暴露出的 Demo 是個 *.vue 文件。這里是注冊的關鍵。Vue CLI 將會把這個組件自動包裹并注冊為 Web Components 組件,無需在 main.js 里自行注冊。需要注意的是,這個包依賴了在頁面上全局可用的 Vue。

    在 Vue CLI 的庫模式中,Vue 是外置的。這意味著包中不會有 Vue。輸出代碼里會使用一個全局的 Vue 對象。主項目無論使用什么輸出格式,都需要將自己系統(tǒng)內(nèi)的 Vue 對象暴露到 window 上。

    所以,在項目中我們需要將 Vue 暴露到 window 上。需要在 main.js 文件添加代碼:

     window.Vue = Vue

    當這個腳本被引入網(wǎng)頁時,你的組件就可以以普通 DOM 元素的方式被使用了。

     <script src="demo.umd.js"></script>

    <!-- 可在普通 HTML 中或者其它任何框架中使用 -->
    <demo></demo>
    5. 獲取組件模塊

    那么,想要使用自定義組件的話,必須要知道 Vue CLI 打包后自動注冊的標簽名。

    事實上,標簽名就是 ./packages/demo/index.vue 文件的 name 值,即 demo 。

    在 3. 如何打包 UMD 規(guī)范的組件文件 中,我們的打包入口是 ./index.js 。它暴露出了 './packages/demo/index.vue' 。那么拿到 ./index.js 就拿到了標簽名。

    當你使用一個 .js 文件作為入口時,它可能會包含具名導出,所以庫會暴露為一個模塊。也就是說你的庫必須在 UMD 構(gòu)建中通過 window.yourLib.default 訪問。

    也就是我們可以通過 window.Demo.default 拿到 ./index.js 。

    又有問題了,這里我們又必須知道它的庫名 Demo 才行。

    怎么辦呢?

    我們回到上文中的 UMD 模塊規(guī)范的代碼觀察。commonjs 模塊規(guī)范使用了 module.exports ,它是可以將模塊直接暴露出來的,而不是掛載在 window 上。

    那我們就模擬下 node 環(huán)境,這樣不需要知道 庫 名,就能拿到模塊。

     // 模擬 node 環(huán)境
    window.module = {}
    window.exports = {}

    // 模擬 node 環(huán)境獲取模塊
    const module = window.module.exports

    這樣就不必像官網(wǎng)那樣具名訪問模塊了。

     // 官網(wǎng)獲取掛載的模塊
    const module = window.Demo.default
    6. 渲染組件

    拿到了組件模塊,下一步就是將它渲染出來。項目里我們使用 動態(tài)組件 + 異步組件 + 渲染函數(shù) 的組合來完成。下面分別回顧一下這幾個知識點,然后將他們相結(jié)合。

    6.1 動態(tài)組件

    主要用于將已知的組件進行切換。

    不適用未知的組件。典型場景是在不同組件之間進行動態(tài)切換,比如在一個多標簽的界面里:

     <template>
    <component v-bind:is="currentTabComponent"></component>
    </template>

    <script>
    import Home from '../components/Home'
    import Posts from '../components/Posts'
    import Archive from '../components/Archive'
    export default {
    components: {
    Home,
    Posts,
    Archive
    },
    data () {
    return {
    currentTabComponent
    }
    }
    }
    </script>

    6.2 異步組件

    vue2 官網(wǎng)是這樣描述的:

    Vue 2.3.0+ 新增如下書寫方式:

     const AsyncComponent = () => ({
    // 需要加載的組件 (應該是一個 `Promise` 對象)
    component: import('./MyComponent.vue'),
    // 異步組件加載時使用的組件
    loading: LoadingComponent,
    // 加載失敗時使用的組件
    error: ErrorComponent,
    // 展示加載時組件的延時時間。默認值是 200 (毫秒)
    delay: 200,
    // 如果提供了超時時間且組件加載也超時了,
    // 則使用加載失敗時使用的組件。默認值是:`Infinity`
    timeout: 3000
    })

    我們在項目中使用的是新增的這種方式。

    6.3 動態(tài)組件 + 異步組件

    下面是項目中將兩種組件結(jié)合的代碼:

     <template>
    <component v-bind:is="componentFile" :model="model"></component>
    </template>

    <script>
    export default defineComponent({
    name: 'AsyncComponent',
    props: {
    model: {
    type: Object,
    default: () => {}
    }
    },
    setup() {
    const AsyncComponent = () => ({
    component: import('./anonymous.vue'),
    delay: 200,
    timeout: 3000
    })

    return {
    componentFile: AsyncComponent,
    }
    },
    })
    </script>
    • model 是 umd 方式獲取到的組件模塊,里面包括:組件的標簽、組件的可配置數(shù)據(jù)等。

    • componentFile 是需要異步加載的組件。

    6.4 渲染函數(shù)

    Vue 推薦在絕大多數(shù)情況下使用模板來創(chuàng)建你的 HTML。然而在一些場景中,你真的需要 JavaScript 的完全編程的能力。這時你可以用渲染函數(shù),它比模板更接近編譯器。

    項目中的 anonymous.vue 文件就非使用 渲染函數(shù) 不可。畢竟,我們都不知道標簽的名字是什么。

    它的代碼如下:

     export default {
    name: 'Anonymous',
    props: {
    model: {
    type: Object,
    default: () => {}
    }
    },
    render(h) {
    const tagName = this.model.tagName

    const param = {
    "props": {
    config: this.model.config
    }
    }
    return h(tagName, param, [])
    }
    }

    以上就是渲染遠程組件的具體步驟。下面簡單梳理一下遠程組件的數(shù)據(jù)是如何響應的。

    7. 遠程組件數(shù)據(jù)的響應

    想要數(shù)據(jù)獲得響應,需要給組件開發(fā)者和接入者約定好規(guī)范。

    7.1 組件開發(fā)者規(guī)范

    在用 Vue CLI 打包的入口文件 ./index.js 中暴露的 Demo (./packages/demo/index.vue)組件中,我們將組件需要響應的屬性以及默認值以 getDefaultConfig() 的形式導出。

     getDefaultConfig() {
    return {
    defaultProperties: [
    {
    title: "邊長",
    name: "width",
    type: "SingleInput",
    value: 200
    }
    ]
    }
    }

    同時,我們還需要監(jiān)聽這個傳進來的屬性值,以便在圖表上做出相應的變化。

     props: {
    config: {
    type: Object,
    }
    }
     watch: {
    config: {
    handler: function (_, newConfig) {
    this.width = newConfig.width
    },
    deep: true
    }
    }

    7.2 組件接入者規(guī)范

    在 5. 獲取組件模塊 的時候,我們可以通過 module.getDefaultConfig() 獲取到需要響應的屬性以及默認值,通過 name 獲取到標簽名。

    在 6.4 渲染函數(shù) 步驟中,將屬性的默認值、標簽名、props (也就是 config) 傳給渲染函數(shù)。就可以完成數(shù)據(jù)的響應 了。

     render(h) {
    // 這里獲得了標簽名
    const tagName = this.model.tagName

    const param = {
    // 這里傳入屬性
    "props": {
    config: this.model.config
    }
    }
    return h(tagName, param, [])
    }

    四、待改進的地方

    • js、css 不隔離,沒有沙箱能力

    • 限定技術棧(項目限定 vue2 ), 對開發(fā)者不友好

    • 如果組件標簽相同,會被覆蓋

    五、對未來優(yōu)化方向的調(diào)研

    方案一:微前端

    微前端借鑒了微服務的架構(gòu)理念,核心在于將一個龐大的前端應用拆分成多個獨立靈活的小型應用,每個應用都可以獨立開發(fā)、獨立運行、獨立部署,再將這些小型應用融合為一個完整的應用。

    主流框架有:single-spa 和 qiankun

    主要應用場景

    1、跨技術棧重構(gòu)項目時。
    2、跨團隊或跨部門協(xié)作開發(fā)項目時。

    微前端拆分的顆粒度為應用。

    【第2695期】基于微前端qiankun的多頁簽緩存方案實踐

    結(jié)合項目的場景,這個方案不是很吻合。既不能很好的解決問題,也沒有發(fā)揮微前端的真正能力。

    方案二:微組件

    這里我們把一些基于 Web Components 的輕量級的微前端框架,稱為微組件。

    框架有:micro-app、magic-microservices 等。

    這種解決方案更適合當前的場景。它可以解決 js、css 不隔離的問題,并且不再限定組件開發(fā)者的技術棧。

    對于組件數(shù)據(jù)的響應與通訊,則需要進一步的調(diào)研和實踐。

    這里有一個微組件實踐可供參考。

    方案三:引入 IDE

    以上兩個方案可以解決前兩個問題,但如果要解決三個問題的話就需要引入 IDE 。

    這個可能是終極方案,可以極大優(yōu)化開發(fā)者體驗,對應的成本也是最高的。這里就不做贅述了。

       
    Node 社群


    我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。

       “分享、點贊、在看” 支持一波

    瀏覽 58
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    日本大香蕉网 | 看90后操逼 | 久久久久久av | 香蕉伊人网 | 欧美 国产 综合 |