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

資訊專欄INFORMATION COLUMN

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

caozhijian / 1791人閱讀

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

歡迎關注我的公眾號睿Talk,獲取我最新的文章:

一、前言

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

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

今天,我們繼續在之前項目的基礎上擴展功能。在上一篇文章中,介紹了自定義組件的渲染和更新的實現方法。為了驗證setState是否生效,還定義了一個setTimeout方法,5秒后更新state。在現實的項目中,state的改變往往是通過事件觸發的,如點擊事件、鍵盤事件和滾動事件等。下面,我們就將事件處理加入到項目當中。

二、實現事件處理

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

</>復制代碼

  1. // 屬性賦值
  2. function setProps(element, props) {
  3. // 屬性賦值
  4. element[ATTR_KEY] = props;
  5. for (let key in props) {
  6. element.setAttribute(key, props[key]);
  7. }
  8. }
  9. // 比較props的變化
  10. function diffProps(newVDom, element) {
  11. let newProps = {...element[ATTR_KEY]};
  12. const allProps = {...newProps, ...newVDom.props};
  13. // 獲取新舊所有屬性名后,再逐一判斷新舊屬性值
  14. Object.keys(allProps).forEach((key) => {
  15. const oldValue = newProps[key];
  16. const newValue = newVDom.props[key];
  17. // 刪除屬性
  18. if (newValue == undefined) {
  19. element.removeAttribute(key);
  20. delete newProps[key];
  21. }
  22. // 更新屬性
  23. else if (oldValue == undefined || oldValue !== newValue) {
  24. element.setAttribute(key, newValue);
  25. newProps[key] = newValue;
  26. }
  27. }
  28. )
  29. // 屬性重新賦值
  30. element[ATTR_KEY] = newProps;
  31. }

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

</>復制代碼

  1. // 屬性賦值
  2. function setProps(element, props) {
  3. // 屬性賦值
  4. element[ATTR_KEY] = props;
  5. for (let key in props) {
  6. // on開頭的屬性當作事件處理
  7. if (key.substring(0, 2) == "on") {
  8. const evtName = key.substring(2).toLowerCase();
  9. element.addEventListener(evtName, evtProxy);
  10. (element._evtListeners || (element._evtListeners = {}))[evtName] = props[key];
  11. } else {
  12. element.setAttribute(key, props[key]);
  13. }
  14. }
  15. }
  16. function evtProxy(evt) {
  17. this._evtListeners[evt.type](evt);
  18. }
  19. // 比較props的變化
  20. function diffProps(newVDom, element) {
  21. let newProps = {...element[ATTR_KEY]};
  22. const allProps = {...newProps, ...newVDom.props};
  23. // 獲取新舊所有屬性名后,再逐一判斷新舊屬性值
  24. Object.keys(allProps).forEach((key) => {
  25. const oldValue = newProps[key];
  26. const newValue = newVDom.props[key];
  27. // on開頭的屬性當作事件處理
  28. if (key.substring(0, 2) == "on") {
  29. const evtName = key.substring(2).toLowerCase();
  30. if (newValue) {
  31. element.addEventListener(evtName, evtProxy);
  32. } else {
  33. element.removeEventListener(evtName, evtProxy);
  34. }
  35. (element._evtListeners || (element._evtListeners = {}))[evtName] = newValue;
  36. } else {
  37. // 刪除屬性
  38. if (newValue == undefined) {
  39. element.removeAttribute(key);
  40. delete newProps[key];
  41. }
  42. // 更新屬性
  43. else if (oldValue == undefined || oldValue !== newValue) {
  44. element.setAttribute(key, newValue);
  45. newProps[key] = newValue;
  46. }
  47. }
  48. }
  49. )
  50. // 屬性重新賦值
  51. element[ATTR_KEY] = newProps;
  52. }

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

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

</>復制代碼

  1. class MyComp extends Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {
  5. name: "Tina",
  6. count: 1
  7. }
  8. }
  9. elmClick() {
  10. this.setState({name: `Jack${this.state.count}`, count: this.state.count + 1 });
  11. }
  12. render() {
  13. return(
  14. This is My Component! {this.props.count}
  15. name: {this.state.name}
  16. )
  17. }
  18. }

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

三、setState異步更新

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

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

</>復制代碼

  1. class Component {
  2. ...
  3. setState(newState) {
  4. this.state = {...this.state, ...newState};
  5. const vdom = this.render();
  6. diff(this.dom, vdom, this.parent);
  7. }
  8. ...
  9. };

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

</>復制代碼

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

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

</>復制代碼

  1. // 等待渲染的組件數組
  2. let pendingRenderComponents = [];
  3. class Component {
  4. ...
  5. setState(newState) {
  6. this.state = {...this.state, ...newState};
  7. enqueueRender(this);
  8. }
  9. ...
  10. };
  11. function enqueueRender(component) {
  12. // 如果push后數組長度為1,則將異步刷新任務加入到事件循環當中
  13. if (pendingRenderComponents.push(component) == 1) {
  14. if (typeof Promise=="function") {
  15. Promise.resolve().then(renderComponent);
  16. } else {
  17. setTimeout(renderComponent, 0);
  18. }
  19. }
  20. }
  21. function renderComponent() {
  22. // 組件去重
  23. const uniquePendingRenderComponents = [...new Set(pendingRenderComponents)];
  24. // 渲染組件
  25. uniquePendingRenderComponents.forEach(component => {
  26. const vdom = component.render();
  27. diff(component.dom, vdom, component.parent);
  28. });
  29. // 清空待渲染列表
  30. pendingRenderComponents = [];
  31. }

當第一次setState成功后,并不會馬上進行渲染,而是將組件存入待渲染組件列表當中。如果列表是空的,則存入組件后將異步刷新任務加入到事件循環當中。當運行環境支持Promise時,通過微任務運行,否則通過宏任務運行。微任務的運行時間是當前事件循環的末尾,而宏任務的運行時間是下一個事件循環。所以優先使用微任務。

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

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

四、總結

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

這是VD系列的最后一篇文章。本系列從什么是Virtual Dom這個問題出發,講解了VD的數據結構、比較方式和更新流程,并在此基礎上進行功能擴展和性能優化,支持key元素復用、自定義組件,dom事件綁定和setState異步更新。總共三百多行代碼,實現了mvvm庫的核心功能。

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

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

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/97573.html

相關文章

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

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

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

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

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

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

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

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

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

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

    xiongzenghui 評論0 收藏0

發表評論

0條評論

caozhijian

|高級講師

TA的文章

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