成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

你不知道的Virtual DOM(五):自定義組件

lk20150415 / 3587人閱讀

摘要:現(xiàn)在流行的前端框架都支持自定義組件,組件化開發(fā)已經(jīng)成為提高前端開發(fā)效率的銀彈。二對(duì)自定義組件的支持要想正確的渲染組件,第一步就是要告訴某個(gè)標(biāo)簽是自定義組件。下面的例子里,就是一個(gè)自定義組件。解決了識(shí)別自定義標(biāo)簽的問(wèn)題,下一步就是定義標(biāo)簽了。

歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:

一、前言

目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DOM技術(shù)提高頁(yè)面的渲染效率。那么,什么是Virtual DOM?它是通過(guò)什么方式去提升頁(yè)面渲染效率的呢?本系列文章會(huì)詳細(xì)講解Virtual DOM的創(chuàng)建過(guò)程,并實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Diff算法來(lái)更新頁(yè)面。本文的內(nèi)容脫離于任何的前端框架,只講最純粹的Virtual DOM。敲單詞太累了,下文Virtual DOM一律用VD表示。

這是VD系列文章的第五篇,以下是本系列其它文章的傳送門:
你不知道的Virtual DOM(一):Virtual Dom介紹
你不知道的Virtual DOM(二):Virtual Dom的更新
你不知道的Virtual DOM(三):Virtual Dom更新優(yōu)化
你不知道的Virtual DOM(四):key的作用
你不知道的Virtual DOM(五):自定義組件
你不知道的Virtual DOM(六):事件處理&異步更新

今天,我們繼續(xù)在之前項(xiàng)目的基礎(chǔ)上擴(kuò)展功能。現(xiàn)在流行的前端框架都支持自定義組件,組件化開發(fā)已經(jīng)成為提高前端開發(fā)效率的銀彈。下面我們就將自定義組件功能加到項(xiàng)目中去,目標(biāo)是正確的渲染和更新自定義組件。

二、JSX對(duì)自定義組件的支持

要想正確的渲染組件,第一步就是要告訴JSX某個(gè)標(biāo)簽是自定義組件。這個(gè)實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,只要標(biāo)簽名的首字母大寫就可以了。下面的例子里,MyComp就是一個(gè)自定義組件。

普通標(biāo)簽

經(jīng)過(guò)JSX編譯后,是下面這個(gè)樣子。

h(
    "div",
    null,
    h(
        "div",
        null,
        "u666Eu901Au6807u7B7E"
    ),
    h(MyComp, null)
);

當(dāng)首字母大寫當(dāng)時(shí)候,JSX會(huì)將標(biāo)簽名當(dāng)作變量處理,而不是像普通標(biāo)簽一樣當(dāng)字符串處理。解決了識(shí)別自定義標(biāo)簽的問(wèn)題,下一步就是定義標(biāo)簽了。

三、定義基類Component

在React中,所有自定義組件都要繼承Component基類,它為我們提供了一系列生命周期方法和修改組件的方法。我們也對(duì)應(yīng)的定義一個(gè)自己的Component類:

class Component {
    constructor(props) {
        this.props = props;
        this.state = {};
    }
    
    setState(newState) {
        this.state = {...this.state, ...newState};
        const vdom = this.render();
        diff(this.dom, vdom, this.parent);
    }

    render() {
        throw new Error("component should define its own render method")
    }
};

如果用一句話描述Component,那就是屬性和狀態(tài)的UI表達(dá)。我們先不考慮生命周期函數(shù),先定義一個(gè)最精簡(jiǎn)版的Component。首先在初始化的時(shí)候,需要傳入props屬性,然后提供一個(gè)setState方法來(lái)改變組件的狀態(tài),最后就是子類必須要實(shí)現(xiàn)的render函數(shù)。如果子類沒(méi)有實(shí)現(xiàn),就會(huì)沿著原型鏈查找到Component類,然后會(huì)拋出一個(gè)錯(cuò)誤。

有了Component基類后,我們就可以定義自己的組件了。我們來(lái)定義一個(gè)最簡(jiǎn)單的顯示屬性和狀態(tài)信息的組件。

class MyComp extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: "Tina"
        }
    }

    render() {
        return(
            
This is My Component! {this.props.count}
name: {this.state.name}
) } }

定義好組件后,就要考慮渲染的邏輯了。

四、組件渲染邏輯

在對(duì)VD進(jìn)行diff操作的時(shí)候,要對(duì)tag為函數(shù)類型(自定義組件)的節(jié)點(diǎn)做特殊處理,同時(shí)對(duì)新建的節(jié)點(diǎn),也要加入一些額外的邏輯。

function diff(dom, newVDom, parent, componentInst) {
    if (typeof newVDom == "object" && typeof newVDom.tag == "function") {
        buildComponentFromVDom(dom, newVDom, parent);
        return false;
    }
    
    // 新建node
    if (dom == undefined) {
        const dom = createElement(newVDom);

        // 自定義組件
        if (componentInst) {
            dom._component = componentInst;
            dom._componentConstructor = componentInst.constructor;
            componentInst.dom = dom;
        }

        parent.appendChild(dom);
        return false;
    }
    ...
}

function buildComponentFromVDom(dom, vdom, parent) {
    const cpnt = vdom.tag;
    if (!typeof cpnt === "function") {
        throw new Error("vdom is not a component type");
    }

    const props = getVDomProps(vdom);
    let componentInst = dom && dom._component;

    // 創(chuàng)建組件
    if (componentInst == undefined) {
        try {
            componentInst = new cpnt(props);
            setTimeout(() => {componentInst.setState({name: "Dickens"})}, 5000);
        } catch (error) {
            throw new Error(`component creation error: ${cpnt.name}`);
        }
    } 
    // 組件更新 
    else {
        componentInst.props = props;
    }
    
    const componentVDom = componentInst.render();
    
    diff(dom, componentVDom, parent, componentInst);
}

function getVDomProps(vdom) {
    const props = vdom.props;
    props.children = vdom.children;

    return props;
}

如果是自定義組件,會(huì)調(diào)用buildComponentFromVDom方法。先通過(guò)getVDomProps方法獲取vdom最新的屬性,包括children。如果dom對(duì)象有_component屬性,說(shuō)明是組件更新的過(guò)程,否則為組件創(chuàng)建的過(guò)程。如果是創(chuàng)建過(guò)程則直接實(shí)例化一個(gè)對(duì)象,setTimeout部分主要為了驗(yàn)證setState能不能正常工作,可以先忽略。如果是更新過(guò)程,則傳入最新的props。最后通過(guò)組件的render方法得到最新的vdom后,再進(jìn)行diff操作。

diff多了一個(gè)componentInst的參數(shù),在新建dom節(jié)點(diǎn)的時(shí)候,如果有這個(gè)參數(shù),說(shuō)明是自定義組件創(chuàng)建的節(jié)點(diǎn),需要用_component_componentConstructor做一下標(biāo)識(shí)。其中_component上面就用到了,用來(lái)判斷是組件更新過(guò)程還是組件創(chuàng)建過(guò)程。_componentConstructor用在組件更新過(guò)程中判斷組件的類型是否相同。

function isSameType(element, newVDom) {
    if (typeof newVDom.tag == "function") {
        return element._componentConstructor == newVDom.tag;
    }
    ...
}

到此為止,自定義組件的被動(dòng)更新過(guò)程已經(jīng)完成了,下面來(lái)看看主動(dòng)更新的邏輯。

五、setState

setState的邏輯很簡(jiǎn)單,就是更新state后再render一次,獲取到最新的vdom,再走一遍diff的過(guò)程。setState的前提是組件已經(jīng)實(shí)例化并且已經(jīng)渲染出來(lái)了,this.dom就是組件渲染出來(lái)的dom的頂級(jí)節(jié)點(diǎn)。

setState(newState) {
    this.state = {...this.state, ...newState};
    const vdom = this.render();
    diff(this.dom, vdom, this.parent);
}

function buildComponentFromVDom(dom, vdom, parent) {
    ...
    // 創(chuàng)建組件
    if (componentInst == undefined) {
        ...
        setTimeout(() => {componentInst.setState({name: "Dickens"})}, 5000);
    ...
}

為了驗(yàn)證setState能否按預(yù)期運(yùn)行,在創(chuàng)建組件的時(shí)候我們?cè)?秒后更新一下state,看看名字能否正確更新。我們的頁(yè)面是長(zhǎng)這個(gè)樣子的:

function view() {
    const elm = arr.pop();

    // 用于測(cè)試能不能正常刪除元素
    if (state.num !== 9) arr.unshift(elm);

    // 用于測(cè)試能不能正常添加元素
    if (state.num === 12) arr.push(9);

    return (
        
Hello World
    { arr.map( i => (
  • 第{i}
  • )) }
); }

剛開始渲染出來(lái)是這個(gè)樣子:

5秒之后是這個(gè)樣子:

可以看到propsstate都得到了正確都渲染。

六、總結(jié)

本文基于上一個(gè)版本的代碼,加入了對(duì)自定義組件的支持,大大提高代碼的復(fù)用性?;诋?dāng)前這個(gè)版本的代碼還能做怎樣的優(yōu)化呢,請(qǐng)看下一篇的內(nèi)容:你不知道的Virtual DOM(六):事件處理&異步更新。

P.S.: 想看完整代碼見這里,如果有必要建一個(gè)倉(cāng)庫(kù)的話請(qǐng)留言給我:代碼

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

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

相關(guān)文章

  • 你不知道Virtual DOM(六):事件處理&異步更新

    摘要:如果列表是空的,則存入組件后將異步刷新任務(wù)加入到事件循環(huán)當(dāng)中。四總結(jié)本文基于上一個(gè)版本的代碼,加入了事件處理功能,同時(shí)通過(guò)異步刷新的方法提高了渲染效率。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DO...

    caozhijian 評(píng)論0 收藏0
  • 你不知道Virtual DOM(四):key作用

    摘要:最后里面沒(méi)有第四個(gè)元素了,才會(huì)把蘋果從移除。四總結(jié)本文基于上一個(gè)版本的代碼,加入了對(duì)唯一標(biāo)識(shí)的支持,很好的提高了更新數(shù)組元素的效率。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DOM技術(shù)提高頁(yè)面的渲染...

    DirtyMind 評(píng)論0 收藏0
  • 你不知道Virtual DOM(一):Virtual Dom介紹

    摘要:不同的框架對(duì)這三個(gè)屬性的命名會(huì)有點(diǎn)差別,但表達(dá)的意思是一致的。它們分別是標(biāo)簽名屬性和子元素對(duì)象。我們先來(lái)看下頁(yè)面的更新一般會(huì)經(jīng)過(guò)幾個(gè)階段。元素有可能是數(shù)組的形式,需要將數(shù)組解構(gòu)一層。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約...

    lavor 評(píng)論0 收藏0
  • 你不知道Virtual DOM(三):Virtual Dom更新優(yōu)化

    摘要:經(jīng)過(guò)這次優(yōu)化,計(jì)算的時(shí)間快了那么幾毫秒?;诋?dāng)前這個(gè)版本的代碼還能做怎樣的優(yōu)化呢,請(qǐng)看下一篇的內(nèi)容你不知道的四的作用。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DOM技術(shù)提高頁(yè)面的渲染效率。那么,什...

    xiongzenghui 評(píng)論0 收藏0
  • 你不知道Virtual DOM(二):Virtual Dom更新

    摘要:變化的只有種更新和刪除。頁(yè)面的元素的數(shù)量隨著而變。四總結(jié)本文詳細(xì)介紹如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的算法,再根據(jù)計(jì)算出的差異去更新真實(shí)的。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React 和 Vue,都不約而同的借助 Virtual DOM 技術(shù)提高頁(yè)面的渲染...

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

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

0條評(píng)論

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