Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 久久精品国产国产,亚洲一级毛片中文字幕,亚洲成人在线视频

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          大廠二面讓我談談Vue.js框架,我慌了,我不知道啊

          Vue.js 是什么

          Vue (讀音 /vju?/,類似于 view) 是一套用于構建用戶界面的漸進式框架。

          Vue 的核心庫只關注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合。另一方面,當與現代化的工具鏈以及各種支持類庫結合使用時,Vue 也完全能夠為復雜的單頁應用提供驅動。

          初始Vue.js

          創建Demo.html
          
          <!-- id標識vue作用的范圍 -->
          <div id="app">
              <!-- {{}} 插值表達式,綁定vue中的data數據 -->
              {{ message }}
          </div>
          <!--導入vue.js庫-->
          <script src="vue.min.js"></script>
          <script>
              // 創建一個vue對象
              new Vue({
                  el: '#app',//綁定vue作用的范圍
                  data: {//定義頁面中顯示的模型數據
                      message: 'Hello Vue!'
                  }
              })
          </script>

          這就是聲明式渲染:Vue.js 的核心是一個允許采用簡潔的模板語法來聲明式地將數據渲染進 DOM 的系統

          這里的核心思想就是沒有繁瑣的DOM操作,例如jQuery中,我們需要先找到div節點,獲取到DOM對象,然后進行一系列的節點操作

          創建代碼片段

          在vs code中創建代碼片段:

          文件 => 首選項 => 用戶代碼片段 => 新建全局代碼片段/或文件夾代碼片段:vue-html.code-snippets

          注意:制作代碼片段的時候,字符串中如果包含文件中復制過來的“Tab”鍵的空格,要換成“空格鍵”的空格

          {
              "vue htm": {
                  "scope": "html",
                  "prefix": "vuehtml",
                  "body": [
                      "<!DOCTYPE html>",
                      "<html lang=\"en\">",
                      "",
                      "<head>",
                      "    <meta charset=\"UTF-8\">",
                      "    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">",
                      "    <meta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\">",
                      "    <title>Document</title>",
                      "</head>",
                      "",
                      "<body>",
                      "    <div id=\"app\">",
                      "",
                      "    </div>",
                      "    <script src=\"vue.min.js\"></script>",
                      "    <script>",
                      "        new Vue({",
                      "            el: '#app',",
                      "            data: {",
                      "                $1",
                      "            }",
                      "        })",
                      "    </script>",
                      "</body>",
                      "",
                      "</html>",
                  ],
                  "description": "my vue template in html"
              }
          }

          分享目標

          了解 Vue.js 的組件化機制了解 Vue.js 的響應式系統原理了解 Vue.js 中的 Virtual DOM 及 Diff 原理

          Vue.js概述

          Vue 是一套用于構建用戶界面的漸進式MVVM框架。那怎么理解漸進式呢?漸進式含義:強制主張最少。

          Vue.js包含了聲明式渲染、組件化系統、客戶端路由、大規模狀態管理、構建工具、數據持久化、跨平臺支持等,但在實際開發中,并沒有強制要求開發者之后某一特定功能,而是根據需求逐漸擴展。 Vue.js的核心庫只關心視圖渲染,且由于漸進式的特性,Vue.js便于與第三方庫或既有項目整合。

          組件機制

          定義:

          組件就是對一個功能和樣式進行獨立的封裝,讓HTML元素得到擴展,從而使得代碼得到復用,使得開發靈活,更加高效。

          與HTML元素一樣,Vue.js的組件擁有外部傳入的屬性prop和事件,除此之外,組件還擁有自己的狀態data和通過數據和狀態計算出來的計算屬性computed,各個維度組合起來決定組件最終呈現的樣子與交互的邏輯。

          數據傳遞

          每一個組件之間的作用域是孤立的,這個意味著組件之間的數據不應該出現引用關系,即使出現了引用關系,也不允許組件操作組件內部以外的其他數據。Vue中,允許向組件內部傳遞prop數據,組件內部需要顯性地聲明該prop字段,如下聲明一個child組件:

          <!-- child.vue -->
          <template>
              <div>{{msg}}</div>
          </template>
          <script>
          export default {
              props: {
                  msg: {
                      type: String,
                      default: 'hello world' // 當default為引用類型時,需要使用 function 形式返回
                  }
              }
          }
          </script>

          父組件向該組件傳遞數據:

          <!-- parent.vue -->
          <template>
              <child :msg="parentMsg"></child>
          </template>
          <script>
          import child from './child';
          export default {
              components: {
                  child
              },
              data () {
                  return {
                      parentMsg: 'some words'
                  }
              }
          }
          </script>

          事件傳遞

          Vue內部實現了一個事件總線系統,即EventBus。在Vue中可以使用 EventBus 來作為溝通橋梁的概念,每一個Vue的組件實例都繼承了 EventBus,都可以接受事件on和發送事件emit

          如上面一個例子,child.vue組件想修改 parent.vue組件的 parentMsg 數據,怎么辦呢?為了保證數據流的可追溯性,直接修改組件內propmsg 字段是不提倡的,且例子中為非引用類型 String,直接修改也修改不了,這個時候需要將修改 parentMsg的事件傳遞給child.vue,讓 child.vue 來觸發修改parentMsg 的事件。如:

          <!-- child.vue -->
          <template>
              <div>{{msg}}</div>
          </template>
          <script>
          export default {
              props: {
                  msg: {
                      type: String,
                      default: 'hello world'
                  }
              },
              methods: {
                  changeMsg(newMsg) {
                      this.$emit('updateMsg', newMsg);
                  }
              }
          }
          </script>

          父組件:

          <!-- parent.vue -->
          <template>
              <child :msg="parentMsg" @updateMsg="changeParentMsg"></child>
          </template>
          <script>
          import child from './child';
          export default {
              components: {
                  child
              },
              data () {
                  return {
                      parentMsg: 'some words'
                  }
              },
              methods: {
                  changeParentMsg: function (newMsg) {
                      this.parentMsg = newMsg
                  }
              }
          }
          </script>

          父組件parent.vue 向子組件child.vue傳遞了updateMsg事件,在子組件實例化的時候,子組件將updateMsg事件使用on函數注冊到組件內部,需要觸發事件的時候,調用函數this.emit來觸發事件。

          除了父子組件之間的事件傳遞,還可以使用一個 Vue 實例為多層級的父子組件建立數據通信的橋梁,如:

          const eventBus = new Vue();
          
          // 父組件中使用$on監聽事件
          eventBus.$on('eventName', val => {
              //  ...do something
          })
          
          // 子組件使用$emit觸發事件
          eventBus.$emit('eventName', 'this is a message.');

          除了on和emit以外,事件總線系統還提供了另外兩個方法,once和off,所有事件如下:

          • $on:監聽、注冊事件。
          • $emit:觸發事件。
          • $once:注冊事件,僅允許該事件觸發一次,觸發結束后立即移除事件。
          • $off:移除事件。
          • 內容分發

          Vue實現了一套遵循 Web Components 規范草案 的內容分發系統,即將元素作為承載分發內容的出口。

          插槽slot,也是組件的一塊HTML模板,這一塊模板顯示不顯示、以及怎樣顯示由父組件來決定。實際上,一個slot最核心的兩個問題在這里就點出來了,是顯示不顯示和怎樣顯示。

          插槽又分默認插槽、具名插槽。

          默認插槽

          又名單個插槽、匿名插槽,與具名插槽相對,這類插槽沒有具體名字,一個組件只能有一個該類插槽。

          如:

          <template>
          <!-- 父組件 parent.vue -->
          <div class="parent">
              <h1>父容器</h1>
              <child>
                  <div class="tmpl">
                      <span>菜單1</span>
                  </div>
              </child>
          </div>
          </template>
          <template>
          <!-- 子組件 child.vue -->
          <div class="child">
              <h1>子組件</h1>
              <slot></slot>
          </div>
          </template>

          如上,渲染時子組件的slot標簽會被父組件傳入的div.tmpl替換。

          具名插槽

          匿名插槽沒有name屬性,所以叫匿名插槽。那么,插槽加了name屬性,就變成了具名插槽。具名插槽可以在一個組件中出現N次,出現在不同的位置,只需要使用不同的name屬性區分即可。

          如:

          <template>
          <!-- 父組件 parent.vue -->
          <div class="parent">
              <h1>父容器</h1>
              <child>
                  <div class="tmpl" slot="up">
                      <span>菜單up-1</span>
                  </div>
                  <div class="tmpl" slot="down">
                      <span>菜單down-1</span>
                  </div>
                  <div class="tmpl">
                      <span>菜單->1</span>
                  </div>
              </child>
          </div>
          </template>
          <template>
              <div class="child">
                  <!-- 具名插槽 -->
                  <slot name="up"></slot>
                  <h3>這里是子組件</h3>
                  <!-- 具名插槽 -->
                  <slot name="down"></slot>
                  <!-- 匿名插槽 -->
                  <slot></slot>
              </div>
          </template>

          如上,slot標簽會根據父容器給 child標簽內傳入的內容的slot 屬性值,替換對應的內容。

          其實,默認插槽也有name屬性值,為default,同樣指定 slotname值為 default,一樣可以顯示父組件中傳入的沒有指定slot的內容。

          作用域插槽

          作用域插槽可以是默認插槽,也可以是具名插槽,不一樣的地方是,作用域插槽可以為slot標簽綁定數據,讓其父組件可以獲取到子組件的數據。

          如:

          <template>
              <!-- parent.vue -->
              <div class="parent">
                  <h1>這是父組件</h1>
                  <current-user>
                      <template slot="default" slot-scope="slotProps">
                          {{ slotProps.user.name }}
                      </template>
                  </current-user>
              </div>
          </template>
          <template>
              <!-- child.vue -->
              <div class="child">
                  <h1>這是子組件</h1>
                  <slot :user="user"></slot>
              </div>
          </template>
          <script>
          export default {
              data() {
                  return {
                      user: {
                          name: 'xiehuangbao1123'
                      }
                  }
              }
          }
          </script>

          如上例子,子組件 child在渲染默認插槽slot的時候,將數據 user傳遞給了 slot 標簽,在渲染過程中,父組件可以通過slot-scope屬性獲取到user數據并渲染視圖。

          slot 實現原理:

          當子組件vm實例化時,獲取到父組件傳入的 slot 標簽的內容,存放在vm.slot中,默認插槽為vm.slot.default,具名插槽為vm.slot.xxx,xxx為插槽名,當組件執行渲染函數時候,遇到<slot>標簽,使用slot中的內容進行替換,此時可以為插槽傳遞數據,若存在數據,則可曾該插槽為作用域插槽。

          至此,父子組件的關系如下圖:

          模板渲染

          Vue.js 的核心是聲明式渲染,與命令式渲染不同,聲明式渲染只需要告訴程序,我們想要的什么效果,其他的事情讓程序自己去做。而命令式渲染,需要命令程序一步一步根據命令執行渲染。如下例子區分:

          var arr = [1, 2, 3, 4, 5];
          
          // 命令式渲染,關心每一步、關心流程。用命令去實現
          var newArr = [];
          for (var i = 0; i < arr.length; i++) {
              newArr.push(arr[i] * 2);
          }
          
          // 聲明式渲染,不用關心中間流程,只需要關心結果和實現的條件
          var newArr1 = arr.map(function (item) {
              return item * 2;
          });

          Vue.js實現了iffor、事件、數據綁定等指令,允許采用簡潔的模板語法來聲明式地將數據渲染出視圖。

          模板編譯

          為什么要進行模板編譯?實際上,我們組件中的 template語法是無法被瀏覽器解析的,因為它不是正確的 HTML 語法,而模板編譯,就是將組件的 template編譯成可執行的 JavaScript代碼,即將 template 轉化為真正的渲染函數。

          模板編譯分三個階段,parseoptimizegenerate,最終生成render函數。

          parse階段:

          使用正在表達式將template進行字符串解析,得到指令、classstyle等數據,生成抽象語法樹 AST

          optimize階段:

          尋找 AST 中的靜態節點進行標記,為后面 VNodepatch 過程中對比做優化。被標記為static 的節點在后面的diff 算法中會被直接忽略,不做詳細的比較。

          generate階段:

          根據 AST 結構拼接生成render函數的字符串。

          預編譯

          對于 Vue 組件來說,模板編譯只會在組件實例化的時候編譯一次,生成渲染函數之后再也不會進行編譯。因此,編譯對組件的 runtime 是一種性能損耗。而模板編譯的目的僅僅是將template轉化為render function,而這個過程,正好可以在項目構建的過程中完成。

          比如webpack的vue-loader依賴了vue-template-compiler模塊,在 webpack 構建過程中,將template預編譯成 render 函數,在 runtime 可直接跳過模板編譯過程。

          回過頭看,runtime 需要是僅僅是 render 函數,而我們有了預編譯之后,我們只需要保證構建過程中生成 render 函數就可以。與 React 類似,在添加JSX的語法糖編譯器babel-plugin-transform-vue-jsx之后,我們可以在 Vue 組件中使用JSX語法直接書寫 render 函數。

          <script>
          export default {
              data() {
                  return {
                      msg: 'Hello JSX.'
                  }
              },
              render() {
                  const msg = this.msg;
                  return <div>
                      {msg}
                  </div>;
              }
          }
          </script>

          如上面組件,使用 JSX 之后,可以在 JS 代碼中直接使用 html 標簽,而且聲明了 render 函數以后,我們不再需要聲明 template。當然,假如我們同時聲明了 template 標簽和 render 函數,構建過程中,template 編譯的結果將覆蓋原有的 render 函數,即 template 的優先級高于直接書寫的 render 函數。

          相對于 template 而言,JSX 具有更高的靈活性,面對與一些復雜的組件來說,JSX 有著天然的優勢,而 template 雖然顯得有些呆滯,但是代碼結構上更符合視圖與邏輯分離的習慣,更簡單、更直觀、更好維護。

          需要注意的是,最后生成的 render 函數是被包裹在with語法中運行的。

          小結

          Vue 組件通過 prop 進行數據傳遞,并實現了數據總線系統EventBus,組件集成了EventBus進行事件注冊監聽、事件觸發,使用slot進行內容分發。

          除此以外,實現了一套聲明式模板系統,在runtime或者預編譯是對模板進行編譯,生成渲染函數,供組件渲染視圖使用。

          響應式系統

          Vue.js 是一款 MVVM 的JS框架,當對數據模型data進行修改時,視圖會自動得到更新,即框架幫我們完成了更新DOM的操作,而不需要我們手動的操作DOM。可以這么理解,當我們對數據進行賦值的時候,Vue 告訴了所有依賴該數據模型的組件,你依賴的數據有更新,你需要進行重渲染了,這個時候,組件就會重渲染,完成了視圖的更新。

          數據模型 、計算屬性 和監聽器

          在組件中,可以為每個組件定義數據模型data、計算屬性computed、監聽器watch。

          數據模型:Vue 實例在創建過程中,對數據模型data的每一個屬性加入到響應式系統中,當數據被更改時,視圖將得到響應,同步更新。data必須采用函數的方式 return,不使用 return 包裹的數據會在項目的全局可見,會造成變量污染;使用return包裹后數據中變量只在當前組件中生效,不會影響其他組件。

          計算屬性:computed基于組件響應式依賴進行計算得到結果并緩存起來。只在相關響應式依賴發生改變時它們才會重新求值,也就是說,只有它依賴的響應式數據(data、prop、computed本身)發生變化了才會重新計算。那什么時候應該使用計算屬性呢?模板內的表達式非常便利,但是設計它們的初衷是用于簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。對于任何復雜邏輯,你都應當使用計算屬性。

          監聽器:監聽器watch作用如其名,它可以監聽響應式數據的變化,響應式數據包括 data、prop、computed,當響應式數據發生變化時,可以做出相應的處理。當需要在數據變化時執行異步或開銷較大的操作時,這個方式是最有用的。

          響應式原理

          在 Vue 中,數據模型下的所有屬性,會被 Vue 使用Object.defineProperty(Vue3.0 使用 Proxy)進行數據劫持代理。響應式的核心機制是觀察者模式,數據是被觀察的一方,一旦發生變化,通知所有觀察者,這樣觀察者可以做出響應,比如當觀察者為視圖時,視圖可以做出視圖的更新。

          Vue.js 的響應式系統以來三個重要的概念,Observer、Dep、Watcher。

          發布者-Observer

          1. Observe 扮演的角色是發布者,他的主要作用是在組件vm初始化的時,調用defineReactive函數,使用Object.defineProperty方法對對象的每一個子屬性進行數據劫持/監聽,即為每個屬性添加getter和setter,將對應的屬性值變成響應式。
          2. 在組件初始化時,調用initState函數,內部執行initState、initProps、initComputed方法,分別對data、prop、computed進行初始化,讓其變成響應式。
          3. 初始化props時,對所有props進行遍歷,調用defineReactive函數,將每個 prop 屬性值變成響應式,然后將其掛載到_props中,然后通過代理,把vm.xxx代理到vm._props.xxx中。
          4. 同理,初始化data時,與prop相同,對所有data進行遍歷,調用defineReactive函數,將每個 data 屬性值變成響應式,然后將其掛載到_data中,然后通過代理,把vm.xxx代理到vm._data.xxx中。
          5. 初始化computed,首先創建一個觀察者對象computed-watcher,然后遍歷computed的每一個屬性,對每一個屬性值調用defineComputed方法,使用Object.defineProperty將其變成響應式的同時,將其代理到組件實例上,即可通過vm.xxx訪問到xxx計算屬性。

          調度中心/訂閱器-Dep

          1. Dep 扮演的角色是調度中心/訂閱器,在調用defineReactive將屬性值變成響應式的過程中,也為每個屬性值實例化了一個Dep,主要作用是對觀察者(Watcher)進行管理,收集觀察者和通知觀察者目標更新,即當屬性值數據發生改變時,會遍歷觀察者列表(dep.subs),通知所有的 watcher,讓訂閱者執行自己的update邏輯。
          2. 其dep的任務是,在屬性的getter方法中,調用dep.depend()方法,將觀察者(即 Watcher,可能是組件的render function,可能是 computed,也可能是屬性監聽watch)保存在內部,完成其依賴收集。在屬性的setter方法中,調用dep.notify()方法,通知所有觀察者執行更新,完成派發更新。

          觀察者-Watcher

          Watcher 扮演的角色是訂閱者/觀察者,他的主要作用是為觀察屬性提供回調函數以及收集依賴,當被觀察的值發生變化時,會接收到來自調度中心Dep的通知,從而觸發回調函數。

          而Watcher又分為三類,normal-watchercomputed-watcherrender-watcher

          • normal-watcher:在組件鉤子函數watch中定義,即監聽的屬性改變了,都會觸發定義好的回調函數。
          • computed-watcher:在組件鉤子函數computed中定義的,每一個computed屬性,最后都會生成一個對應的Watcher對象,但是這類Watcher有個特點:當計算屬性依賴于其他數據時,屬性并不會立即重新計算,只有之后其他地方需要讀取屬性的時候,它才會真正計算,即具備lazy(懶計算)特性。
          • render-watcher:每一個組件都會有一個render-watcher, 當data/computed中的屬性改變的時候,會調用該Watcher來更新組件的視圖。

          這三種Watcher也有固定的執行順序,分別是:computed-render -> normal-watcher -> render-watcher。這樣就能盡可能地保證,在更新組件視圖的時候,computed 屬性已經是最新值了,如果 render-watcher 排在 computed-render 前面,就會導致頁面更新的時候 computed 值為舊數據。

          小結

          Observer 負責將數據進行攔截,Watcher 負責訂閱,觀察數據變化, Dep 負責接收訂閱并通知 Observer 和接收發布并通知所有 Watcher。

          Virtual DOM

          在 Vue 中,template被編譯成瀏覽器可執行的render function,然后配合響應式系統,將render function掛載在render-watcher中,當有數據更改的時候,調度中心Dep通知該render-watcher執行render function,完成視圖的渲染與更新。

          整個流程看似通順,但是當執行render function時,如果每次都全量刪除并重建 DOM,這對執行性能來說,無疑是一種巨大的損耗,因為我們知道,瀏覽器的DOM很“昂貴”的,當我們頻繁的更新 DOM,會產生一定的性能問題。

          為了解決這個問題,Vue 使用 JS 對象將瀏覽器的 DOM 進行的抽象,這個抽象被稱為 Virtual DOM。Virtual DOM 的每個節點被定義為VNode,當每次執行render function時,Vue 對更新前后的VNode進行Diff對比,找出盡可能少的我們需要更新的真實 DOM 節點,然后只更新需要更新的節點,從而解決頻繁更新 DOM 產生的性能問題。

          VNode

          VNode,全稱virtual node,即虛擬節點,對真實 DOM 節點的虛擬描述,在 Vue 的每一個組件實例中,會掛載一個$createElement函數,所有的VNode都是由這個函數創建的。

          比如創建一個 div:

          // 聲明 render function
          render: function (createElement) {
              // 也可以使用 this.$createElement 創建 VNode
              return createElement('div', 'hellow world');
          }
          // 以上 render 方法返回html片段 <div>hellow world</div>

          render 函數執行后,會根據VNode Tree將 VNode 映射生成真實 DOM,從而完成視圖的渲染。

          Diff

          Diff 將新老 VNode 節點進行比對,然后將根據兩者的比較結果進行最小單位地修改視圖,而不是將整個視圖根據新的 VNode 重繪,進而達到提升性能的目的。

          patch

          Vue.js 內部的 diff 被稱為patch。其 diff 算法的是通過同層的樹節點進行比較,而非對樹進行逐層搜索遍歷的方式,所以時間復雜度只有O(n),是一種相當高效的算法。

          首先定義新老節點是否相同判定函數sameVnode:滿足鍵值key和標簽名tag必須一致等條件,返回true,否則false。

          在進行patch之前,新老 VNode 是否滿足條件sameVnode(oldVnode, newVnode),滿足條件之后,進入流程patchVnode,否則被判定為不相同節點,此時會移除老節點,創建新節點。

          patchVnode

          patchVnode 的主要作用是判定如何對子節點進行更新,

          1. 如果新舊VNode都是靜態的,同時它們的key相同(代表同一節點),并且新的 VNode 是 clone 或者是標記了 once(標記v-once屬性,只渲染一次),那么只需要替換 DOM 以及 VNode 即可。
          2. 新老節點均有子節點,則對子節點進行 diff 操作,進行updateChildren,這個 updateChildren 也是 diff 的核心。
          3. 如果老節點沒有子節點而新節點存在子節點,先清空老節點 DOM 的文本內容,然后為當前 DOM 節點加入子節點。
          4. 當新節點沒有子節點而老節點有子節點的時候,則移除該 DOM 節點的所有子節點。
          5. 當新老節點都無子節點的時候,只是文本的替換。

          updateChildren

          Diff 的核心,對比新老子節點數據,判定如何對子節點進行操作,在對比過程中,由于老的子節點存在對當前真實 DOM 的引用,新的子節點只是一個 VNode 數組,所以在進行遍歷的過程中,若發現需要更新真實 DOM 的地方,則會直接在老的子節點上進行真實 DOM 的操作,等到遍歷結束,新老子節點則已同步結束。

          updateChildren內部定義了4個變量,分別是oldStartIdx、oldEndIdx、newStartIdx、newEndIdx,分別表示正在 Diff 對比的新老子節點的左右邊界點索引,在老子節點數組中,索引在oldStartIdx與oldEndIdx中間的節點,表示老子節點中為被遍歷處理的節點,所以小于oldStartIdx或大于oldEndIdx的表示未被遍歷處理的節點。同理,在新的子節點數組中,索引在newStartIdx與newEndIdx中間的節點,表示老子節點中為被遍歷處理的節點,所以小于newStartIdx或大于newEndIdx的表示未被遍歷處理的節點。

          每一次遍歷,oldStartIdx和oldEndIdx與newStartIdx和newEndIdx之間的距離會向中間靠攏。當 oldStartIdx > oldEndIdx 或者 newStartIdx > newEndIdx 時結束循環。

          在遍歷中,取出4索引對應的 Vnode節點:

          • oldStartIdx:oldStartVnode
          • oldEndIdx:oldEndVnode
          • newStartIdx:newStartVnode
          • newEndIdx:newEndVnode

          diff 過程中,如果存在key,并且滿足sameVnode,會將該 DOM 節點進行復用,否則則會創建一個新的 DOM 節點。

          首先,oldStartVnode、oldEndVnode與newStartVnode、newEndVnode兩兩比較,一共有 2*2=4 種比較方法。

          情況一:當oldStartVnode與newStartVnode滿足 sameVnode,則oldStartVnode與newStartVnode進行 patchVnode,并且oldStartIdx與newStartIdx有移動。

          情況二:與情況一類似,當oldEndVnode與newEndVnode滿足 sameVnode,則oldEndVnode與newEndVnode進行 patchVnode,并且oldEndIdx與newEndIdx左移動。

          情況三:當oldStartVnode與newEndVnode滿足 sameVnode,則說明oldStartVnode已經跑到了oldEndVnode后面去了,此時oldStartVnode與newEndVnode進行 patchVnode 的同時,還需要將oldStartVnode的真實 DOM 節點移動到oldEndVnode的后面,并且oldStartIdx右移,newEndIdx左移。

          情況四:與情況三類似,當oldEndVnode與newStartVnode滿足 sameVnode,則說明oldEndVnode已經跑到了oldStartVnode前面去了,此時oldEndVnode與newStartVnode進行 patchVnode 的同時,還需要將oldEndVnode的真實 DOM 節點移動到oldStartVnode的前面,并且oldStartIdx右移,newEndIdx左移。

          當這四種情況都不滿足,則在oldStartIdx與oldEndIdx之間查找與newStartVnode滿足sameVnode的節點,若存在,則將匹配的節點真實 DOM 移動到oldStartVnode的前面。

          若不存在,說明newStartVnode為新節點,創建新節點放在oldStartVnode前面即可。

          當 oldStartIdx > oldEndIdx 或者 newStartIdx > newEndIdx,循環結束,這個時候我們需要處理那些未被遍歷到的 VNode。

          當 oldStartIdx > oldEndIdx 時,說明老的節點已經遍歷完,而新的節點沒遍歷完,這個時候需要將新的節點創建之后放在oldEndVnode后面。

          當 newStartIdx > newEndIdx 時,說明新的節點已經遍歷完,而老的節點沒遍歷完,這個時候要將沒遍歷的老的節點全都刪除。

          此時已經完成了子節點的匹配。下面是一個例子 patch 過程圖:

          總結

          借用官方的一幅圖:

          Vue.js 實現了一套聲明式渲染引擎,并在runtime或者預編譯時將聲明式的模板編譯成渲染函數,掛載在觀察者 Watcher 中,在渲染函數中(touch),響應式系統使用響應式數據的getter方法對觀察者進行依賴收集(Collect as Dependency),使用響應式數據的setter方法通知(notify)所有觀察者進行更新,此時觀察者 Watcher 會觸發組件的渲染函數(Trigger re-render),組件執行的 render 函數,生成一個新的 Virtual DOM Tree,此時 Vue 會對新老 Virtual DOM Tree 進行 Diff,查找出需要操作的真實 DOM 并對其進行更新。

          于使用 webpack 的配置,我們可以從以下幾個方面來進行了解與認識:

          簡單配置

          調整webpack配置的最簡單方法是為 configureWebpack 選項提供對象vue.config.js

          module.exports = {
          	configureWebpack: {
          		plugins: [
          			new MyAwesomeWebpackPlugin()
          		]
          	}
          }
          

          修改加載程序的選項

          添加一個新的加載器

          檢測項目的webpack配置

          輸出重定向到配置好的文件中便于檢查:

          vue inspect > output.js
          

          通過指定路徑檢查配置子集:

          vue inspect module.rule.0
          

          以命名規則或插件為目標:

          vue inspect --rule vue
          vue inspect --plugin html
          

          列出所有命名的規則和插件:

          vue inspect --rules
          vue inspect --plugins
          

          將解析的配置用作文件,使用以下路徑

          <projectRoot>/node_modules/@vue/cli-service/webpack.config.js
          

          文件可以動態解析并導出 vue-cli-service命令中使用的完全相同的webpack配置,包括來自插件甚至自定義配置的配置。

          鏈接:https://www.9xkd.com/


          主站蜘蛛池模板: 国产在线观看91精品一区| 99无码人妻一区二区三区免费| 成人区人妻精品一区二区不卡视频| 波多野结衣一区视频在线| 久久久久国产一区二区三区| 色欲AV无码一区二区三区| 成人精品一区二区电影| 在线中文字幕一区| 无码AV天堂一区二区三区| 一区二区在线电影| 亚洲一区二区三区高清| 久久久精品人妻一区二区三区蜜桃 | 国产乱码精品一区二区三区麻豆| 中文字幕一区二区三区人妻少妇| 亚洲日韩一区二区三区| 久久精品国内一区二区三区| 天美传媒一区二区三区| 福利一区二区三区视频午夜观看| 精品一区二区AV天堂| 久久无码精品一区二区三区| 麻豆精品久久久一区二区| 久久精品国产第一区二区三区 | 天天爽夜夜爽人人爽一区二区 | 日韩视频免费一区二区三区| 在线观看一区二区三区视频| 日本强伦姧人妻一区二区| 成人一区二区免费视频| 精品无码AV一区二区三区不卡 | 天堂一区人妻无码| 日韩三级一区二区三区| 国产免费播放一区二区| 久久国产一区二区三区| 麻豆AV无码精品一区二区| 国语对白一区二区三区| 一区二区国产精品| 一区二区在线电影| 成人一区二区三区视频在线观看| 一区二区精品视频| 无码精品黑人一区二区三区| 精品国产日韩一区三区| 本免费AV无码专区一区|