国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

從Dialog管理談到Vue渲染原理

darkbug / 3282人閱讀

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

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

大量的彈窗有什么問題

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

模板過長,且大量冗余

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

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

把每個彈窗抽成模塊

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

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

使用動態(tài)Component

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

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

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

全局Dialog

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

我們在根組件下掛一個Dialog組件,組件內(nèi)容依然使用動態(tài)component,組件的數(shù)據(jù)流轉(zhuǎn),component傳遞等使用Vuex進行。

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

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

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

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

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

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

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

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

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

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

使用簡潔

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

Let"s go.

使用$mount

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

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

// dialog是一個單例,不需要重復(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并手動插入到body
    dialog.$mount()
    document.body.appendChild(dialog.$el)
  }

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

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

生命周期問題

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

注入事件

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

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

ComponentDefinition,就是一個組件的選項對象,new Vue時傳的那個對象

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

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

() => 

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

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

為什么要注入事件

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

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

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

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

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

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

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

在Dialog組件內(nèi),我們觸及不到組件的模板,所以簡單的在動態(tài)component模板上添加 @done 并不能完成事件監(jiān)聽。因為事件監(jiān)聽其實是在render的過程中進行的,而我們的render是通過jsx的方式在調(diào)用$dialog函數(shù)時傳入的,所以只能手動在生成的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)聽了動態(tài)component的donecancel事件,在任一事件觸發(fā)后都會關(guān)閉Dialog,組件$emit("done")表示完成了自己的業(yè)務(wù),$emit("cancel)表示取消了自己的業(yè)務(wù)

主動收集依賴

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

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

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

Vue的渲染流程與依賴收集

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

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

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

所以,粗暴的講,需要把this.text的訪問放在一個render函數(shù)(Vue選項對象的render)中進行。平常用的模板其實也是這樣,因為它最終都被Vue-loader編譯成了render。

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

  // 返回vue選項對象
  const that = this
  return {
    name: "dynamic-wrapper",

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

所以,這就是為什么一定要使用一個返回jsx的函數(shù)作為,而不是直接美滋滋的使用jsx。因為,臣妾實在是做不到響應(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個月的時間了。

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

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/99807.html

相關(guān)文章

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

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

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

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

    wh469012917 評論0 收藏0
  • React造輪系列:對話框組件 - Dialog 思路

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

    qianfeng 評論0 收藏0
  • 面試被問到Vue?想進一步提升?那就停下來看一下吧

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

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

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

    StonePanda 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<