亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

從Dialog管理談到Vue渲染原理

darkbug / 3404人閱讀

摘要:在組件內(nèi),我們觸及不到組件的模板,所以簡(jiǎn)單的在動(dòng)態(tài)模板上添加并不能完成事件監(jiān)聽。簡(jiǎn)單來說,依賴收集是在渲染函數(shù)渲染的函數(shù)中進(jìn)行的,在中一旦通過使用了這個(gè)變量,通過這個(gè)變量的就收集到了正在執(zhí)行的渲染函數(shù)這一個(gè)依賴。

作為一個(gè)中后臺(tái)表單&表格工程師,經(jīng)常需要在一個(gè)頁(yè)面中處理多個(gè)彈窗。我自己的項(xiàng)目中,一個(gè)復(fù)雜的審核頁(yè)面中的彈窗數(shù)量超過了30個(gè),如何管理大量的彈窗就成為了一個(gè)需要考慮的問題。

大量的彈窗有什么問題

假設(shè)你有一個(gè)彈窗組件,類似于element-ui的Dialog,如果簡(jiǎn)單粗暴的每一個(gè)彈窗都寫一個(gè)dialog,那么會(huì)有以下問題:

模板過長(zhǎng),且大量冗余

命名困難,每一個(gè)彈窗需要一個(gè)變量去控制顯示,通常每一個(gè)彈窗里面也是一個(gè)表單,又需要一個(gè)變量保存表單數(shù)據(jù),每個(gè)彈窗也有自己的邏輯(method),都要寫在這個(gè)頁(yè)面,要絞盡腦汁去取名

非常的不優(yōu)雅,簡(jiǎn)直就是Repeat yourself反模式的示范。。。

把每個(gè)彈窗抽成模塊

一個(gè)很容易想到的優(yōu)化方法就是把一個(gè)彈窗作為一個(gè)組件抽離出去,每個(gè)彈窗的邏輯多帶帶寫在組件中。

這樣通過組件拆分做很好的解決了模板過長(zhǎng)的問題,也基本解決了命名困難的問題,不過還是需要很多的變量去控制每個(gè)組件的顯示。

使用動(dòng)態(tài)Component

第一個(gè)辦法本質(zhì)上并沒有減少重復(fù)的代碼和邏輯(彈窗顯示/關(guān)閉),只是把代碼放在了不同的文件當(dāng)中。

顯然,我并不需要寫那么多的Dialog,Dialog本身并沒有變,作為一個(gè)「包裹」組件,變的只是內(nèi)容。

所以,只需要寫一個(gè)dialog,配合Vue的動(dòng)態(tài)組件Component,切換不同的組件就行了。

全局Dialog

使用Component,我們做到了一個(gè)頁(yè)面只需要一個(gè)Dialog,但其實(shí)整個(gè)網(wǎng)頁(yè),也只需要一個(gè)全局的Dialog。

我們?cè)诟M件下掛一個(gè)Dialog組件,組件內(nèi)容依然使用動(dòng)態(tài)component,組件的數(shù)據(jù)流轉(zhuǎn),component傳遞等使用Vuex進(jìn)行。

使用函數(shù)創(chuàng)建組件

作為單個(gè)項(xiàng)目的解決方案,全局Dialog加動(dòng)態(tài)Component其實(shí)已經(jīng)足夠好了,使用一個(gè)函數(shù)調(diào)用就可以顯示彈窗。

this.$dialog({
  title: "我是彈窗",
  component: Test,
  props: { props }, // Test的props通過這樣傳遞
})

但是想要作為通用解決方案,還不夠:

引入不方便,需要手動(dòng)在跟組件下引入并寫上封裝好的彈窗組件

必須使用Vuex進(jìn)行數(shù)據(jù)流轉(zhuǎn),而并不是每個(gè)Vue項(xiàng)目都使用Vuex的

沒法監(jiān)聽事件,只能傳入回調(diào)

props的傳遞方式不夠優(yōu)雅,不夠聲明式

在我心中,一個(gè)理想的彈窗組件,需要是這樣的:

引入方便,Vue.use(Dialog)就行了

使用簡(jiǎn)潔

  this.$dialog({
    title: "哎呀不錯(cuò)哦",
    component: () => 
  })

Let"s go.

使用$mount

Vue作為一個(gè)視圖層的框架,核心其實(shí)就是渲染函數(shù),所以一定有一個(gè)辦法,可以把一個(gè)Vue組件渲染成一個(gè)DOM,這個(gè)方法就是$mount。

// 這個(gè)Dialog組件就是寫好的彈窗組件
import Dialog from "./Dialog"

// dialog是一個(gè)單例,不需要重復(fù)創(chuàng)建
let dialog
export default function createDialog(Vue, { store = {}, router = {} }, options) {
  if (dialog) {
    dialog.options = {
      ...options,
    }

    dialog.$children[0].visible = true
  } else {
    dialog = new Vue({
      name: "Root-Dialog",
      router,
      store,
      data() {
        return {
          options: { ...options },
        }
      },
      render(h) {
        return h(Dialog, {
          props: this.options,
        })
      },
    })

    // 渲染出DOM并手動(dòng)插入到body
    dialog.$mount()
    document.body.appendChild(dialog.$el)
  }

  // 暴露close方法
  return {
    close: () => dialog.$children[0].close(),
  }
}
Dialog組件

基于element-ui的Dialog組件二次封裝,在原有的props之外,添加一個(gè)component,使用動(dòng)態(tài)Component渲染上去就行了。
思路很簡(jiǎn)單,但是有幾個(gè)問題需要考慮。

生命周期問題

如果不做任何處理,當(dāng)彈窗消失的時(shí)候component并不會(huì)銷毀;當(dāng)再次顯示彈窗時(shí),會(huì)傳入一個(gè)新的組件,這個(gè)時(shí)候,上一個(gè)組件才銷毀,這非常不合理。所以我們需要在彈窗消失的時(shí)候手動(dòng)銷毀傳入的component。

注入事件

Vue的動(dòng)態(tài)Component組件的is屬性接受的值有3種類型:

string,在當(dāng)前組件內(nèi)注冊(cè)過的組件的名稱

ComponentDefinition,就是一個(gè)組件的選項(xiàng)對(duì)象,new Vue時(shí)傳的那個(gè)對(duì)象

ComponentConstructor,返回一個(gè)ComponentDefinition的函數(shù),比如動(dòng)態(tài)import函數(shù)

而我們希望的調(diào)用形式里,component是一個(gè)返回jsx的函數(shù),而它會(huì)被babel插件babel-plugin-transform-vue-jsx轉(zhuǎn)換為調(diào)用createElement函數(shù)的結(jié)果,也就是說

() => 

這個(gè)函數(shù)最終返回的是一個(gè)Virtual Node。
而Vue的選項(xiàng)里面,render最終返回的也是一個(gè)VNode。
也就是說,() => 這個(gè)函數(shù)可以作為一個(gè)Vue組件的render選項(xiàng),所以,我們需要構(gòu)造一個(gè)完整的Vue選項(xiàng)對(duì)象,然后將這個(gè)對(duì)象作為動(dòng)態(tài)component的is屬性,這樣就可以渲染出這個(gè)Test組件了。

在這個(gè)過程中,我們可以在這個(gè)Vnode里面做一些有趣的事情,比如注入事件。

為什么要注入事件

首先,這里有一個(gè)剛需:彈窗內(nèi)的組件需要可以關(guān)閉彈窗,也就是它的父組件。
通常有兩個(gè)辦法可以做到:

通過props接收一個(gè)函數(shù),調(diào)用它可以關(guān)閉彈窗

主動(dòng)拋出一個(gè)事件,dialog組件監(jiān)聽這個(gè)事件,然后把自己關(guān)了

略微比較一下就可以發(fā)現(xiàn),拋出事件的方法優(yōu)于回調(diào)函數(shù)的辦法(通常來說,「事件」都優(yōu)于「回調(diào)」):

代碼少, $emit("complete")就行了,使用回調(diào)需要添加一個(gè)props,調(diào)用的時(shí)候還需要判斷它是否存在

通用性更好,這個(gè)組件可能不僅僅只在彈窗內(nèi)調(diào)用,它可以在其它任何地方被調(diào)用,使用事件只需要簡(jiǎn)單的拋出一個(gè)事件,表示我完成了,調(diào)用它的組件根據(jù)自身的邏輯來進(jìn)行接下來的工作,這樣組件本身做到了低耦合。

但是,拋出事件的實(shí)現(xiàn)卻要比傳入回調(diào)難很多,需要對(duì)VNode比較熟悉。

在Dialog組件內(nèi),我們觸及不到組件的模板,所以簡(jiǎn)單的在動(dòng)態(tài)component模板上添加 @done 并不能完成事件監(jiān)聽。因?yàn)槭录O(jiān)聽其實(shí)是在render的過程中進(jìn)行的,而我們的render是通過jsx的方式在調(diào)用$dialog函數(shù)時(shí)傳入的,所以只能手動(dòng)在生成的VNode上添加事件監(jiān)聽:

在 vNode.componentOptions.listeners中,添加我們需要監(jiān)聽的事件和事件處理函數(shù):

let listeners = vNode.componentOptions.listeners

if (!listeners) {
  listeners = {}
  vNode.componentOptions.listeners = listeners
}

// 添加done
const orginDoneHandler = listeners.done
listeners.done = function () {
  if (orginDoneHandler) orginDoneHandler()
  doneHandler()
}

// 添加cancel
const orginCancelHandler = listeners.cancel
listeners.cancel = function () {
  if (orginCancelHandler) orginCancelHandler()
  cancelHandler()
}

在Dialog中,監(jiān)聽了動(dòng)態(tài)component的donecancel事件,在任一事件觸發(fā)后都會(huì)關(guān)閉Dialog,組件$emit("done")表示完成了自己的業(yè)務(wù),$emit("cancel)表示取消了自己的業(yè)務(wù)

主動(dòng)收集依賴

到這里,還有一個(gè)問題沒有解決:這個(gè)組件還不是響應(yīng)式的,比如說,你在一個(gè)index組件中通過$dialog顯示一個(gè)彈窗

this.$dialog({
  title: "響應(yīng)式",
  component: () => 
})

當(dāng)text更新時(shí),彈窗中的內(nèi)容并沒有更新,也就說,組件沒有重新渲染。

Vue的渲染流程與依賴收集

這里就要涉及到一些Vue的原理了,比如說渲染流程,依賴收集,一兩句話也講不清楚,我試著大概的說一下:

首先,頁(yè)面上顯示的數(shù)據(jù)變了,一定是觸發(fā)了重新渲染,this.text = "新的text" 之所以會(huì)更新頁(yè)面,可以理解為一個(gè)渲染函數(shù)在this.text的setter中執(zhí)行了。

那么,this.text的getter怎么樣才能知道要執(zhí)行哪些函數(shù),就是通過所謂的依賴收集。簡(jiǎn)單來說,依賴收集是在渲染函數(shù)(渲染Vnode的函數(shù))中進(jìn)行的,在createElement中一旦通過this.text使用了這個(gè)變量,通過這個(gè)變量的getter就收集到了正在執(zhí)行的渲染函數(shù)這一個(gè)依賴。

所以,粗暴的講,需要把this.text的訪問放在一個(gè)render函數(shù)(Vue選項(xiàng)對(duì)象的render)中進(jìn)行。平常用的模板其實(shí)也是這樣,因?yàn)樗罱K都被Vue-loader編譯成了render。

_component() {
  // 這一步很重要,讓component收集到了這個(gè)計(jì)算屬性的依賴,否則當(dāng)component變化時(shí)不會(huì)重新渲染組件
  const fn = this.component
  let vNode

  // 返回vue選項(xiàng)對(duì)象
  const that = this
  return {
    name: "dynamic-wrapper",

    render() {
      // fn的運(yùn)行一定要在render函數(shù)中,也是為了掛載依賴
      vNode = fn()
      ...
    }
}

所以,這就是為什么一定要使用一個(gè)返回jsx的函數(shù)作為,而不是直接美滋滋的使用jsx。因?yàn)?,臣妾?shí)在是做不到響應(yīng)式呀~

this.$dialog({
  title: "臣妾做不到啊~",
  component: ,
})

等于

// this.text的值為text
this.$dialog({
  title: "臣妾做不到啊~",
  component: createElement(
    Text,
    props: {
      text: "text",
    }
  )
})

完整代碼,拍著胸脯保證可用,已經(jīng)在生產(chǎn)環(huán)境大量使用超過3個(gè)月的時(shí)間了。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/99807.html

相關(guān)文章

  • 前端每周清單第 48 期:Slack Webpack 構(gòu)建優(yōu)化,CSS 命名規(guī)范與用戶追蹤,Vue.

    摘要:發(fā)布是由團(tuán)隊(duì)開源的,操作接口庫(kù),已成為事實(shí)上的瀏覽器操作標(biāo)準(zhǔn)。本周正式發(fā)布,為我們帶來了,,支持自定義頭部與腳部,支持增強(qiáng),兼容原生協(xié)議等特性變化。新特性介紹日前發(fā)布了大版本更新,引入了一系列的新特性與提升,本文即是對(duì)這些變化進(jìn)行深入解讀。 showImg(https://segmentfault.com/img/remote/1460000012940044); 前端每周清單專注前端...

    sean 評(píng)論0 收藏0
  • 一個(gè)基于Vue.js+Mongodb+Node.js的博客內(nèi)容管理系統(tǒng)

    摘要:三更新內(nèi)容在原來項(xiàng)目的基礎(chǔ)上,做了如下更新數(shù)據(jù)庫(kù)重新設(shè)計(jì),改成以用戶分組的數(shù)據(jù)庫(kù)結(jié)構(gòu)應(yīng)數(shù)據(jù)庫(kù)改動(dòng),所有接口重新設(shè)計(jì),并統(tǒng)一采用和網(wǎng)易立馬理財(cái)一致的接口風(fēng)格刪除原來游客模式,增加登錄注冊(cè)功能,支持彈窗登錄。 這個(gè)項(xiàng)目最初其實(shí)是fork別人的項(xiàng)目。當(dāng)初想接觸下mongodb數(shù)據(jù)庫(kù),找個(gè)例子學(xué)習(xí)下,后來改著改著就面目全非了。后臺(tái)和數(shù)據(jù)庫(kù)重構(gòu),前端增加了登錄注冊(cè)功能,僅保留了博客設(shè)置頁(yè)面,但是...

    wh469012917 評(píng)論0 收藏0
  • React造輪系列:對(duì)話框組件 - Dialog 思路

    摘要:本文是造輪系列第二篇。實(shí)現(xiàn)方式事件處理跟差不多,唯一多了一步就是當(dāng)點(diǎn)擊或者的時(shí)候,如果外部有回調(diào)就需要調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)。 本文是React造輪系列第二篇。 1.React 造輪子系列:Icon 組件思路 本輪子是通過 React + TypeScript + Webpack 搭建的,至于環(huán)境的搭建這邊就不在細(xì)說了,自己動(dòng)手谷歌吧。當(dāng)然可以參考我的源碼。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳Git...

    qianfeng 評(píng)論0 收藏0
  • 面試被問到Vue?想進(jìn)一步提升?那就停下來看一下吧

    摘要:兩個(gè)對(duì)象鍵名沖突時(shí),取組件對(duì)象的鍵值對(duì)。允許聲明擴(kuò)展另一個(gè)組件可以是一個(gè)簡(jiǎn)單的選項(xiàng)對(duì)象或構(gòu)造函數(shù),而無(wú)需使用。這主要是為了便于擴(kuò)展單文件組件。 Vue作為最近最炙手可熱的前端框架,其簡(jiǎn)單的入門方式和功能強(qiáng)大的API是其優(yōu)點(diǎn)。而同時(shí)因?yàn)槠銩PI的多樣性和豐富性,所以他的很多開發(fā)方式就和一切基于組件的React不同,如果沒有對(duì)Vue的API(有一些甚至文檔都沒提到)有一個(gè)全面的了解,那么在...

    andot 評(píng)論0 收藏0
  • vue2 中如何實(shí)現(xiàn)動(dòng)態(tài)表單增刪改查

    摘要:最近項(xiàng)目中遇到的需求是要操作大量的表單,之前的項(xiàng)目中有做過這方的研究,只不過是用來操作。添加操作上面的只是其中一個(gè)動(dòng)態(tài)列表。 最近項(xiàng)目中遇到的需求是要操作大量的表單,之前的項(xiàng)目中有做過這方的研究,只不過是用jquery來操作。 項(xiàng)目A 先簡(jiǎn)單說說以前項(xiàng)目A中的應(yīng)用場(chǎng)景,可能有小伙伴兒也遇到相同的需求。A項(xiàng)目是公司的OA系統(tǒng)中有的項(xiàng)目,是用java的jsp渲染的頁(yè)面,需求是要改成:嵌入A...

    StonePanda 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<