<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          遠(yuǎn)程組件實(shí)踐

          共 8326字,需瀏覽 17分鐘

           ·

          2023-06-20 10:33

          大廠技術(shù)  高級(jí)前端  Node進(jìn)階

          點(diǎn)擊上方 程序員成長(zhǎng)指北,關(guān)注公眾號(hào)

          回復(fù)1,加入高級(jí)Node交流群

          前言

          從服務(wù)端遠(yuǎn)程下載一個(gè) JS 文件并注冊(cè)成組件。

          一、什么是遠(yuǎn)程組件

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

          二、背景

          1. 項(xiàng)目背景

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

          低代碼平臺(tái)

          需求流程

          這也是遠(yuǎn)程組件的一個(gè)典型場(chǎng)景。

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

          2. 技術(shù)背景

          項(xiàng)目使用的技術(shù)棧為 vue2。我們限定自定義組件開(kāi)發(fā)的技術(shù)棧也是 vue2。

          三、技術(shù)實(shí)現(xiàn)

          1. 流程步驟

          幾個(gè)關(guān)鍵步驟

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

          • 注冊(cè)組件

          • 獲取到組件模塊

          • 渲染組件

          • 響應(yīng)用戶的操作

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

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

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

          下面是規(guī)范的代碼,以及對(duì)應(yīng)的說(shuō)明:

           (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 {
          // 沒(méi)有模塊環(huán)境,直接掛載在全局對(duì)象上
          root.umdModule = factory(root.depModule);
          }
          }(this, function(depModule) {
          // depModule 是依賴模塊
          return {
          name: '我自己是一個(gè)umd模塊'
          }
          }))

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

          在我們的項(xiàng)目中,是直接使用 script 標(biāo)簽引用的。但沒(méi)有走第四個(gè)條件分支。之后會(huì)說(shuō)明原因及做法。

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

          以 Vue CLI 為例。當(dāng)運(yùn)行 vue-cli-service build 時(shí),可以通過(guò) --target 選項(xiàng)指定構(gòu)建目標(biāo)為 庫(kù) 。

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

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

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

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

          在我們的項(xiàng)目中,打包命令為:

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

          我們的 庫(kù) 名是 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: "邊長(zhǎng)",
          name: "width",
          type: "SingleInput",
          value: 200
          }
          ]
          }
          }
          }
          </script>
          4. 組件的注冊(cè)

          當(dāng)使用 Vue CLI 的庫(kù)模式打包時(shí),我們暴露出的 Demo 是個(gè) *.vue 文件。這里是注冊(cè)的關(guān)鍵。Vue CLI 將會(huì)把這個(gè)組件自動(dòng)包裹并注冊(cè)為 Web Components 組件,無(wú)需在 main.js 里自行注冊(cè)。需要注意的是,這個(gè)包依賴了在頁(yè)面上全局可用的 Vue。

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

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

           window.Vue = Vue

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

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

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

          那么,想要使用自定義組件的話,必須要知道 Vue CLI 打包后自動(dòng)注冊(cè)的標(biāo)簽名。

          事實(shí)上,標(biāo)簽名就是 ./packages/demo/index.vue 文件的 name 值,即 demo 。

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

          當(dāng)你使用一個(gè) .js 文件作為入口時(shí),它可能會(huì)包含具名導(dǎo)出,所以庫(kù)會(huì)暴露為一個(gè)模塊。也就是說(shuō)你的庫(kù)必須在 UMD 構(gòu)建中通過(guò) window.yourLib.default 訪問(wèn)。

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

          又有問(wèn)題了,這里我們又必須知道它的庫(kù)名 Demo 才行。

          怎么辦呢?

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

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

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

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

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

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

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

          6.1 動(dòng)態(tài)組件

          主要用于將已知的組件進(jìn)行切換。

          不適用未知的組件。典型場(chǎng)景是在不同組件之間進(jìn)行動(dòng)態(tài)切換,比如在一個(gè)多標(biāo)簽的界面里:

           <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+ 新增如下書(shū)寫方式:

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

          我們?cè)陧?xiàng)目中使用的是新增的這種方式。

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

          下面是項(xiàng)目中將兩種組件結(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 方式獲取到的組件模塊,里面包括:組件的標(biāo)簽、組件的可配置數(shù)據(jù)等。

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

          6.4 渲染函數(shù)

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

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

          它的代碼如下:

           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, [])
          }
          }

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

          7. 遠(yuǎn)程組件數(shù)據(jù)的響應(yīng)

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

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

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

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

          同時(shí),我們還需要監(jiān)聽(tīng)這個(gè)傳進(jìn)來(lái)的屬性值,以便在圖表上做出相應(yīng)的變化。

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

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

          在 5. 獲取組件模塊 的時(shí)候,我們可以通過(guò) module.getDefaultConfig() 獲取到需要響應(yīng)的屬性以及默認(rèn)值,通過(guò) name 獲取到標(biāo)簽名。

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

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

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

          四、待改進(jìn)的地方

          • js、css 不隔離,沒(méi)有沙箱能力

          • 限定技術(shù)棧(項(xiàng)目限定 vue2 ), 對(duì)開(kāi)發(fā)者不友好

          • 如果組件標(biāo)簽相同,會(huì)被覆蓋

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

          方案一:微前端

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

          主流框架有:single-spa 和 qiankun

          主要應(yīng)用場(chǎng)景

          1、跨技術(shù)棧重構(gòu)項(xiàng)目時(shí)。
          2、跨團(tuán)隊(duì)或跨部門協(xié)作開(kāi)發(fā)項(xiàng)目時(shí)。

          微前端拆分的顆粒度為應(yīng)用。

          【第2695期】基于微前端qiankun的多頁(yè)簽緩存方案實(shí)踐

          結(jié)合項(xiàng)目的場(chǎng)景,這個(gè)方案不是很吻合。既不能很好的解決問(wèn)題,也沒(méi)有發(fā)揮微前端的真正能力。

          方案二:微組件

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

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

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

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

          這里有一個(gè)微組件實(shí)踐可供參考。

          方案三:引入 IDE

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

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

             
          Node 社群


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

             “分享、點(diǎn)贊、在看” 支持一波

          瀏覽 58
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本欧美一区不卡少妇 | 国产99自拍 | 五十路老熟女 码视频 | 日皮视频在线入口 | 操骚逼电影 |