自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

走進科學(xué)之神秘的拖拽碧油雞

開發(fā) 前端
小朋友,你是不是有很多的問號。學(xué)過的知識點過段時間就忘了,下次遇到的時候還是需要百度。如果你也遇到了這種情況,那么我們就是異父異母的親兄弟啊!!

[[435635]]

一、寫在前面

小朋友,你是不是有很多的問號。學(xué)過的知識點過段時間就忘了,下次遇到的時候還是需要百度。如果你也遇到了這種情況,那么我們就是異父異母的親兄弟啊!!

好兄弟我經(jīng)過多次的坎坷之后,終于找到了一個好辦法!那就是在修 bug 中學(xué)習(xí)。就像彼得-帕克的叔叔對他說的:能力越大,責(zé)任越大。我也得到了屬于我自己的座右銘,那就是:

bug 越多,能力越大。[手動滑稽]

開個玩笑哈。不過話糙理不糙,當(dāng)我們見識過、修復(fù)過大量的稀奇古怪的 bug 之后,我們的知識也會在修復(fù)過程中融會貫通,并且記憶十分深刻。因為這背后都是一個個加班辛勞的夜晚啊!

最近我就遇到了一個很奇怪的 bug,剛開始我一點頭緒都沒有,只能呆坐在工位上痛苦的撓頭撓頭。后續(xù)在解決這個 bug 的過程中,總結(jié)了以前學(xué)習(xí)的知識點,得到了明顯的進步!!因此寫一篇文章,與大家分享一下。

二、碧油雞是什么雞

首先簡單說一下業(yè)務(wù)場景:在一個管理后臺的列表頁有著多條數(shù)據(jù),數(shù)據(jù)之間存在著順序,現(xiàn)在需要每條數(shù)據(jù)能夠拖拽排序。需求看上去很簡單,只涉及到一個排序接口,難點在于對列表數(shù)據(jù)進行拖拽。

我們?nèi)粘S龅酵献ь愋枨箝_發(fā)的時候,當(dāng)然要看看有沒有合適的輪子使用了。以前開發(fā)中我使用過 vue-draggable 做過兩個容器之間數(shù)據(jù)的拖拽。但是這個列表數(shù)據(jù)拖拽沒有那么復(fù)雜 ,有點大材小用。這個時候另一個輕量級的 js 插件走進了我的眼簾:SortableJS。

1. SostableJS 的使用

SortableJS 很輕量,官網(wǎng)上的 demo 很直觀。但是它的配置項說明文檔寫的很差,有些 api 的說明都找不到,讓人排查問題的時候很是費勁。

  1. <template> 
  2.     <div> 
  3.     <!-- 表單 table --> 
  4.       <el-table v-loading="loading" :data="currentLessonList" class="p-course-classes-wrapper--class-table"
  5.         <el-table-column prop="lessonName" label="課時名稱"></el-table-column
  6.         <el-table-column prop="lessonCode" label="課時 ID"></el-table-column
  7.         <el-table-column prop="gmtCreated" label="添加時間"
  8.           <template slot-scope="scope"
  9.             <span>{{ formatTime(scope.row.gmtCreated )}}</span> 
  10.           </template> 
  11.         </el-table-column
  12.         <el-table-column prop="surveyName" label="隨堂測試"
  13.           <template slot-scope="scope"
  14.             <span>{{ scope.row.surveyName || '--'}}</span> 
  15.           </template> 
  16.         </el-table-column
  17.       </el-table
  18.   </div> 
  19. </template> 
  20.  
  21. <script> 
  22.   import Sortable from 'sortablejs' 
  23.  
  24.     export default { 
  25.     ... 
  26.     activated () { 
  27.             // 初始化排序列表 
  28.         this.$nextTick(() => { 
  29.         const that = this 
  30.         const tbody = document.querySelector('.el-table__body-wrapper tbody'
  31.          
  32.         this.sortObj = new Sortable(tbody, { 
  33.         animation: 150, 
  34.         sort: true
  35.         disabled: !that.isCanDrag, 
  36.         onEnd: async function (evt) { 
  37.           // SortableJS 不改變數(shù)據(jù)的實際順序,但是傳遞新舊索引值,需要開發(fā)者手動根據(jù)索引值改變數(shù)據(jù)順序 
  38.           that.currentLessonList.splice(evt.newIndex, 0, that.currentLessonList.splice(evt.oldIndex, 1)[0]) 
  39.           that.currentLessonList = that.currentLessonList.map((item, index) => { 
  40.             return { 
  41.               ...item, 
  42.               sort: index + 1 
  43.             } 
  44.           }) 
  45.  
  46.           await that.updateLessonsOrder() 
  47.         } 
  48.       }) 
  49.       }) 
  50.     } 
  51.   } 
  52. </script> 

如上面的代碼所示,SortableJS 的使用很簡單,只需要在頁面初始化的時候,獲取到指定的 dom 節(jié)點,然后 new 一個 Sortable 的實例即可。需要注意的是當(dāng)你站在拖拽列表數(shù)據(jù)的時候,雖然視圖層面上數(shù)據(jù)的順序發(fā)生改變了,但是模型層上的數(shù)據(jù)順序是沒有改變的。所以需要我們在 onEnd 函數(shù)中,手動改變列表數(shù)據(jù)順序。

2. 遇到的神秘問題

完成了上面的代碼的書寫,我原本以為已經(jīng)結(jié)束了,可以安心提測了。但是這個時候,一個神秘的 bug 出現(xiàn)了。當(dāng)我調(diào)試的時候,出現(xiàn)了詭異的現(xiàn)象:第一行的數(shù)據(jù)和第三行的數(shù)據(jù)拖拽交換位置之后,相應(yīng)的 data 數(shù)組順序和視圖完全不一致。

這是什么鬼?重新審視之前寫的代碼,我們的操作似乎沒啥問題。在 SortableJS 移動了真實的 DOM 后,我們在 onEnd 中也改變了 data 中的列表數(shù)組順序。列表數(shù)組數(shù)據(jù)渲染的順序應(yīng)該和真實 DOM 的順序是一致的,但是為什么詭異不一致呢?

3. 問題分析

任何 bug 的修復(fù)都需要進行全面的問題分析,既然視圖層的順序已經(jīng)改變,模型層的數(shù)據(jù)沒有正確改變。那么問題就出在了模型層列表數(shù)組數(shù)據(jù)的更改上!回顧上面寫的代碼,唯一的對于數(shù)組數(shù)據(jù)順序的操作就在 onEnd 函數(shù)中。那么是不是這里出了問題呢?

然而生活沒有這么一帆風(fēng)順的,在 debugger 了 onEnd 函數(shù)后,發(fā)現(xiàn)我所做的操作是正確的。但是就是最終列表數(shù)組順序沒有跟視圖層保持一致!!太難了啊!!

4. 問題解決

在我痛苦的撓頭了一個下午后,終于在谷歌的幫助下找到了答案。先說問題的最終解決方案:那就是在 el-table 標簽上加一個 key,區(qū)分每一條數(shù)據(jù)的唯一性。

  1. <template> 
  2.     <el-table :data="currentLessonList" :row-key="row => row.pkId"
  3.     .... 
  4.   </el-table
  5. </template> 

難以置信,讓我痛苦了一個下午的問題僅僅就需要一行代碼就解決了。

三、探究神秘 bug 的根源

完成了 bug 的修復(fù)之后,我靜下心來梳理一下這個 bug 的來源。這種詭異并且脫離控制的情況讓我很是好奇,想要弄清楚這背后的原因。閱讀了前輩的博客之后,我知道了這個問題發(fā)生的根本原因:Virtual DOM 和真實 DOM 之間出現(xiàn)了不一致。

1. Virtual DOM 與 真實 DOM

在 Vue 框架興起之前,前端開發(fā)使用的還是原生和封裝的 JQuery 框架。但是其本質(zhì)還是對于頁面 DOM 節(jié)點的操作,頂多就是操作的更加方便了。在這個背景下,前端開發(fā)的步驟繞不開獲取 dom 節(jié)點的過程。等到 Vue,React 等框架的流行之后,前端開發(fā)出現(xiàn)了新的開發(fā)模式:不需要關(guān)注和操作 dom 節(jié)點了。

這個時候一個新的概念誕生了——Virtual Dom。虛擬 DOM 是相對于真實的 DOM 而言的。真實 DOM 就是頁面的 DOM 模型,其中有大量的 dom 節(jié)點。在 JQuery 時代,我們開發(fā)過程就是與這些真實 dom 節(jié)點打交道的過程。

我們回憶一下 Vue 的實現(xiàn)原理,在 Vue2.0 之前是通過 defineProperty 依賴注入和跟蹤的方式實現(xiàn)雙向綁定。針對 v-for 指令,如果指定了唯一的 key,則會通過高效的 Diff 算法計算出數(shù)組內(nèi)元素的差異,進行最少的移動或刪除操作。而 Vue2.0 之后引入了 Virtual Dom之后,子元素的 Dom Diff 算法和前者其實是相似的,唯一的區(qū)別就是,2.0 之前 Diff 直接針對 v-for 指定的數(shù)組對象,2.0 之后則針對的是 Virtual Dom。

2. 具體實例

假設(shè)我們的列表元素數(shù)組是:

  1. let tableData = ['A''B''C''D'

渲染出來后的 DOM 節(jié)點是:

  1. let tableDate_dom = ['$A''$B''$C''$D'

那么 Virtual Dom 對應(yīng)的結(jié)構(gòu)就是:

  1. let tableData_vm = [ 
  2.   { 
  3.     el: '$A'
  4.     data: 'A' 
  5.   }, 
  6.   { 
  7.     el: '$B'
  8.     data: 'B' 
  9.   }, 
  10.   { 
  11.     el: '$C'
  12.     data: 'C' 
  13.   }, 
  14.   { 
  15.     el: '$D'
  16.     data: 'D' 
  17.   }, 

假設(shè)拖拽排序之后,真實的 DOM 變?yōu)椋?/p>

  1. ['$B''$A''$C''$D'

因為 SortableJS 只操作了真實 DOM,改變了它的位置,而 Virtual Dom 的結(jié)構(gòu)并沒有發(fā)生改變,依然是:

  1. let tableData_vm = [ 
  2.   { 
  3.     el: '$A'
  4.     data: 'A' 
  5.   }, 
  6.   { 
  7.     el: '$B'
  8.     data: 'B' 
  9.   }, 
  10.   { 
  11.     el: '$C'
  12.     data: 'C' 
  13.   }, 
  14.   { 
  15.     el: '$D'
  16.     data: 'D' 
  17.   }, 

而我們在實例化 Sortable 實例的時候,在 onEnd 函數(shù)中做了更改數(shù)組數(shù)據(jù)排序的操作,把列表元素也改為和真實 DOM 排序一致:

  1. ['B''A''C''D'

列表元素更改了之后,這個時候會根據(jù) Diff 算法,重新渲染頁面導(dǎo)致了 bug 的發(fā)生。操作路徑可以粗略的理解為:

拖拽移動真實 DOM -> 操作數(shù)據(jù)數(shù)組 -> Patch 算法再更新真實 DOM

3. 更近一步的探究

筆者在寫到這里的時候,感覺到了力有未逮。原本以為自己已經(jīng)理解了這個 bug 的原因,但是隨著文章的書寫,對之前的開發(fā)細節(jié)進行復(fù)盤的時候,卻發(fā)現(xiàn)知識的網(wǎng)絡(luò)還是多有漏洞。更近一步的探究,就需要去學(xué)習(xí) DOM-Diff 算法的細節(jié),才能真正地得知為什么只需要設(shè)置一個唯一的 key,就能解決這個奇怪并且難以排查的 bug。這一點我還欠缺了很多,希望以后工作中能夠多問一個為什么。

四、自省

回顧整個 bug 修復(fù)的過程,我自身的編碼和知識學(xué)習(xí)存在了幾個問題,梳理出來待以后彌補。

1. 日常編碼規(guī)范的不嚴謹

編碼規(guī)范的問題可以說是貫徹了職業(yè)生涯的開始到結(jié)束。這個問題可大可小,但是如果拉長整個時間線到 10 年、20 年的話,它足以對于我們職業(yè)生涯產(chǎn)生重大影響。就拿這次的問題來說,Vue 官方文檔就說了在使用 v-for 指令時,不推薦直接使用數(shù)組數(shù)據(jù)的 index 作為 key 屬性的值。但是回顧我以前的編碼中,都是圖省事直接使用的 index。因為不涉及到真實 DOM 的改變,所以也沒有出現(xiàn)什么問題。而正是這種沒有什么問題才更加縱容我繼續(xù)使用這種不推薦的書寫方式,終于在這個拖拽需求上栽了個跟頭。

糾正并形成嚴謹?shù)木幋a規(guī)范,不僅提前的避免了一些問題的產(chǎn)生,更是培養(yǎng)了開發(fā)者優(yōu)秀的編程思維。日積月累下來,遵循嚴謹?shù)木幋a規(guī)范的開發(fā)者,對于編程的理解潛移默化中都會得到提高。

2. 知其然不知其所以然

知其然很容易做到。當(dāng)我們遇到了一個 bug 時,采用窮舉、詢問的方式都可以找到解決問題的辦法。但是如果只停留在這里,不去更近一步探究問題發(fā)生的根本原因的話。我們始終都是個編碼的工具人,嗚嗚嗚!

正如現(xiàn)在前端流行的框架 Vue 和 React,大部分人包括我自己更多的停留在框架的使用上面。對于框架的原理一知半解,更多的都是遇到問題臨時抱佛腳。這種情況下,我們的知識深度不夠。那么在遇到一些奇怪的 bug 時,我們的思維被局限在一個很小的空間里面,無法透過現(xiàn)象看本質(zhì)的定位到問題的根源。

老話新說,珍貴的東西總是不能輕易得到的??邕^高山,得到的成就感足以讓我們開心很久很久。

五、小結(jié)

前面給大家喂了一波雞湯后,還是要總結(jié)一下本篇文章。本片文章從筆者工作過程中遇到了一個奇怪的拖拽 bug 談起,描述了業(yè)務(wù)場景,然后談到了具體的解決方案。接著探究 bug 發(fā)生的原因:虛擬 DOM 和真實 DOM 的不一致。然后從這個日常開發(fā)過程很少碰到的情況,通過簡單的 demo 描述了發(fā)生的原因。并進一步定位到了問題的根源:Dom-diff 算法。隨后沒有深入講述 diff 算法,留給各位朋友自行學(xué)習(xí)研究。

讓筆者感慨的是,一個普通的 bug 背后牽扯到了各個方面、深度的知識。那么反過來想,我們在學(xué)習(xí)這些知識的時候。如果只是零敲碎打的學(xué)習(xí),而沒有將其納入到一個系統(tǒng)的知識框架中的話。那么我們永遠也無法提升我們的技術(shù)水平。希望本篇文章能夠給大家?guī)硪恍椭院蟮娜兆永锎蠹乙黄饘W(xué)習(xí)進步哈!

六、參考文章

深入淺出 Vue 中的 key 值:https://juejin.cn/post/6844903865930743815

Vue 中使用 SortableJS:https://www.jianshu.com/p/d92b9efe3e6a 

virtual-dom(Vue 實現(xiàn))簡析:https://segmentfault.com/a/1190000010090659

許浩星,微醫(yī)前端技術(shù)部前端工程師。一個認為人生的樂趣一半在靜,一半在動的有志青年!

責(zé)任編輯:武曉燕 來源: 微醫(yī)大前端技術(shù)
相關(guān)推薦

2013-05-09 14:48:26

Windows Blu

2014-01-24 10:35:51

Google

2010-07-05 09:07:42

2010-05-11 10:19:17

VMforceJava云計算

2010-03-16 17:30:14

Java多線程編程

2011-09-08 10:21:50

Node.js

2014-08-21 08:59:44

2011-07-27 22:01:46

Sencha ToucHtml 5

2014-06-16 10:41:18

2015-11-05 13:17:02

互聯(lián)網(wǎng)金融欺詐黑客產(chǎn)業(yè)

2018-03-22 13:34:59

TensorFlow神經(jīng)網(wǎng)絡(luò)

2017-02-24 14:53:16

iOSRunLoop

2019-09-27 17:01:58

2021-08-16 11:05:31

LinuxvimIO

2013-12-05 10:47:13

TechEd2013

2019-10-11 10:23:13

ClassLoaderJavaJVM

2011-08-29 09:59:26

2015-08-04 17:54:45

戴爾anycloud云計算

2009-03-02 10:06:02

軟件工程師面試筆試

2010-05-17 09:13:35

點贊
收藏

51CTO技術(shù)棧公眾號