成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

caozhijian / 1842人閱讀

摘要:如果列表是空的,則存入組件后將異步刷新任務(wù)加入到事件循環(huán)當(dāng)中。四總結(jié)本文基于上一個版本的代碼,加入了事件處理功能,同時通過異步刷新的方法提高了渲染效率。

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

一、前言

目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DOM技術(shù)提高頁面的渲染效率。那么,什么是Virtual DOM?它是通過什么方式去提升頁面渲染效率的呢?本系列文章會詳細(xì)講解Virtual DOM的創(chuàng)建過程,并實現(xiàn)一個簡單的Diff算法來更新頁面。本文的內(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ù)在之前項目的基礎(chǔ)上擴展功能。在上一篇文章中,介紹了自定義組件的渲染和更新的實現(xiàn)方法。為了驗證setState是否生效,還定義了一個setTimeout方法,5秒后更新state。在現(xiàn)實的項目中,state的改變往往是通過事件觸發(fā)的,如點擊事件、鍵盤事件和滾動事件等。下面,我們就將事件處理加入到項目當(dāng)中。

二、實現(xiàn)事件處理

事件的綁定一般是定義在元素或者組件的屬性當(dāng)中,之前對屬性的初始化和更新沒有考慮支持事件,只是簡單的賦值操作。

// 屬性賦值
function setProps(element, props) {
     // 屬性賦值
    element[ATTR_KEY] = props;

    for (let key in props) {
        element.setAttribute(key, props[key]);
    }
}

// 比較props的變化
function diffProps(newVDom, element) {
    let newProps = {...element[ATTR_KEY]};
    const allProps = {...newProps, ...newVDom.props};

    // 獲取新舊所有屬性名后,再逐一判斷新舊屬性值
    Object.keys(allProps).forEach((key) => {
        const oldValue = newProps[key];
        const newValue = newVDom.props[key];

        // 刪除屬性
        if (newValue == undefined) {
            element.removeAttribute(key);
            delete newProps[key];
        } 
        // 更新屬性
        else if (oldValue == undefined || oldValue !== newValue) {
            element.setAttribute(key, newValue);
            newProps[key] = newValue;
        }
    }
)

    // 屬性重新賦值
    element[ATTR_KEY] = newProps;
}

setProps是在創(chuàng)建元素的時候調(diào)用的,而diffProps則是在diff過程中調(diào)用的。如果需要支持事件綁定,我們需要多做一個判斷。如果屬性名稱是on開頭的話,比如onClick,我們就要在當(dāng)前元素上注冊或刪除一個事件處理。

// 屬性賦值
function setProps(element, props) {
     // 屬性賦值
    element[ATTR_KEY] = props;

    for (let key in props) {
        // on開頭的屬性當(dāng)作事件處理
        if (key.substring(0, 2) == "on") {
            const evtName = key.substring(2).toLowerCase();
            element.addEventListener(evtName, evtProxy);
            (element._evtListeners || (element._evtListeners = {}))[evtName] = props[key];
        } else {
            element.setAttribute(key, props[key]);
        }
    }
}

function evtProxy(evt) {
    this._evtListeners[evt.type](evt);
}

// 比較props的變化
function diffProps(newVDom, element) {
    let newProps = {...element[ATTR_KEY]};
    const allProps = {...newProps, ...newVDom.props};

    // 獲取新舊所有屬性名后,再逐一判斷新舊屬性值
    Object.keys(allProps).forEach((key) => {
        const oldValue = newProps[key];
        const newValue = newVDom.props[key];

        // on開頭的屬性當(dāng)作事件處理
        if (key.substring(0, 2) == "on") {
            const evtName = key.substring(2).toLowerCase();
            if (newValue) {
                element.addEventListener(evtName, evtProxy);
            } else {
                element.removeEventListener(evtName, evtProxy);
            }
            (element._evtListeners || (element._evtListeners = {}))[evtName] = newValue;
        } else {
            // 刪除屬性
            if (newValue == undefined) {
                element.removeAttribute(key);
                delete newProps[key];
            } 
            // 更新屬性
            else if (oldValue == undefined || oldValue !== newValue) {
                element.setAttribute(key, newValue);
                newProps[key] = newValue;
            }
        }
    }
)

    // 屬性重新賦值
    element[ATTR_KEY] = newProps;
}

所有的事件處理函數(shù)都存到dom元素的_evtListeners當(dāng)中,當(dāng)事件觸發(fā)的時候,將事件傳給里面對應(yīng)的方法處理。這樣做的好處是如果以后要對瀏覽器傳入的事件evt做進一步的封裝,就可以在evtProxy函數(shù)里面處理。

接下來,我們在自定義組件里面新增一個onClick事件,在點擊的時候改變state里面的值。

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

    elmClick() {
        this.setState({name: `Jack${this.state.count}`, count: this.state.count + 1 });
    }

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

項目運行的效果是每當(dāng)我點一下MyComp組件的區(qū)域,里面的name就會隨之馬上更新。

三、setState異步更新

用過React的朋友都知道,為了減少不必要的渲染,提高性能,React并不是在我們每次setState的時候都進行渲染,而是將一個同步操作里面的多個setState進行合并后再渲染,給人異步渲染的感覺??催^源碼的都應(yīng)該知道,React是通過事務(wù)的方式來合并多個setState操作的,本質(zhì)來說還是同步的。如果想對其作更深入的學(xué)習(xí),推薦看這篇文章。

為了達到合并操作,減少渲染的效果,最簡單的方式就是異步渲染,下面我們來看看如何實現(xiàn)。在上一個版本里,setState是這么定義的:

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

    ...
};

state更新后直接就進行diff操作,進而更新頁面。如果我們onClick里面的代碼改成這樣:

elmClick() {
        this.setState({name: `Jack${this.state.count}`, count: this.state.count + 1 });
        this.setState({name: `Jack${this.state.count}`, count: this.state.count + 1 });
    }

頁面會渲染2次。如果我們把它改造成下面的樣子:

// 等待渲染的組件數(shù)組
let pendingRenderComponents = [];

class Component {
    ...
        
    setState(newState) {
        this.state = {...this.state, ...newState};
        enqueueRender(this);
    }

    ...
};

function enqueueRender(component) {
    // 如果push后數(shù)組長度為1,則將異步刷新任務(wù)加入到事件循環(huán)當(dāng)中
    if (pendingRenderComponents.push(component) == 1) {
        if (typeof Promise=="function") {
            Promise.resolve().then(renderComponent);
        } else {
            setTimeout(renderComponent, 0);
        }
    }
}

function renderComponent() {
    // 組件去重
    const uniquePendingRenderComponents = [...new Set(pendingRenderComponents)];

    // 渲染組件
    uniquePendingRenderComponents.forEach(component => {
        const vdom = component.render();
        diff(component.dom, vdom, component.parent);
    });

    // 清空待渲染列表
    pendingRenderComponents = [];
}

當(dāng)?shù)谝淮?b>setState成功后,并不會馬上進行渲染,而是將組件存入待渲染組件列表當(dāng)中。如果列表是空的,則存入組件后將異步刷新任務(wù)加入到事件循環(huán)當(dāng)中。當(dāng)運行環(huán)境支持Promise時,通過微任務(wù)運行,否則通過宏任務(wù)運行。微任務(wù)的運行時間是當(dāng)前事件循環(huán)的末尾,而宏任務(wù)的運行時間是下一個事件循環(huán)。所以優(yōu)先使用微任務(wù)。

緊接著進行第二次setState操作,同樣的,將組件存入待渲染組件列表當(dāng)中。此時,主線程的任務(wù)執(zhí)行完了,開始執(zhí)行異步任務(wù)。

當(dāng)異步刷新任務(wù)啟動時,將待渲染列表去重后對里面的組件進行渲染。等渲染完成后再清空待渲染列表。此時,渲染出來的是2次setState合并后的結(jié)果,并且只會進行一次diff操作,渲染一次。

四、總結(jié)

本文基于上一個版本的代碼,加入了事件處理功能,同時通過異步刷新的方法提高了渲染效率。

這是VD系列的最后一篇文章。本系列從什么是Virtual Dom這個問題出發(fā),講解了VD的數(shù)據(jù)結(jié)構(gòu)、比較方式和更新流程,并在此基礎(chǔ)上進行功能擴展和性能優(yōu)化,支持key元素復(fù)用、自定義組件,dom事件綁定和setState異步更新??偣踩俣嘈写a,實現(xiàn)了mvvm庫的核心功能。

有關(guān)VD,如果還有什么想了解的,歡迎留言,有問必答。

P.S.: 想看完整代碼見這里,如果有必要建一個倉庫的話請留言給我:代碼

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

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

相關(guān)文章

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

    摘要:現(xiàn)在流行的前端框架都支持自定義組件,組件化開發(fā)已經(jīng)成為提高前端開發(fā)效率的銀彈。二對自定義組件的支持要想正確的渲染組件,第一步就是要告訴某個標(biāo)簽是自定義組件。下面的例子里,就是一個自定義組件。解決了識別自定義標(biāo)簽的問題,下一步就是定義標(biāo)簽了。 歡迎關(guān)注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、...

    lk20150415 評論0 收藏0
  • 你不知道Virtual DOM(四):key作用

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

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

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

    lavor 評論0 收藏0
  • 你不知道Virtual DOM(二):Virtual Dom更新

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

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

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

    xiongzenghui 評論0 收藏0

發(fā)表評論

0條評論

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