前端理論面試-vue主題

一.什么是vue框架

Vue是一套用于構(gòu)建用戶界面的漸進式框架。與其它大型框架不同的是,Vue 被設(shè)計為可以自底向上逐層應(yīng)用。Vue 的核心庫只關(guān)注視圖層,不僅易于上手,還便于與第三方庫或既有項目整合。另一方面,當(dāng)與現(xiàn)代化的工具鏈以及各種支持類庫結(jié)合使用時,Vue 也完全能夠為復(fù)雜的單頁應(yīng)用提供驅(qū)動。(vue官方描述)
(1)下面來解釋下,何為漸進式框架,
  如果只使用Vue最基礎(chǔ)的聲明式渲染的功能,則完全可以把Vue當(dāng)做一個模板引擎來使用
  如果想以組件化開發(fā)方式進行開發(fā),則可以進一步使用Vue里面的組件系統(tǒng)
  如果要制作SPA(單頁應(yīng)用),則使用Vue里面的客戶端路由功能
  如果組件越來越多,需要共享一些數(shù)據(jù),則可以使用Vue里的狀態(tài)管理
  如果想在團隊里執(zhí)行統(tǒng)一的開發(fā)流程或規(guī)范,則使用構(gòu)建工具
  所以,可以根據(jù)項目的復(fù)雜度來自主選擇使用Vue里面的功能

(2)Vue是一個基于MVVM模式數(shù)據(jù)驅(qū)動頁面的框架,它將數(shù)據(jù)綁定在視圖上。屬于實現(xiàn)單頁面應(yīng)用的技術(shù)。.總結(jié)起來的幾大特點:
1) 簡潔 2) 輕量 3)快速 4) 數(shù)據(jù)驅(qū)動 5) 模塊友好 6) 組件化

vue靠數(shù)據(jù)驅(qū)動雙向綁定使我們開發(fā)頁面更簡單,開發(fā)者不需要手動的去修改dom。Vue通過數(shù)據(jù)雙向綁定是一切變得更簡單。它的數(shù)據(jù)驅(qū)動雙向綁定,底層是通過Object.defineProperty() 定義的數(shù)據(jù) set、get 函數(shù)原理實現(xiàn)。

(3)、組件化開發(fā),讓項目的可拓展性、移植性更好,代碼重用性更高。

(4)、單頁應(yīng)用的體驗,局部組件更新界面,讓用戶體驗更快速省時。

單頁應(yīng)用也稱為SPA是將所有的活動局限于一個Web頁面中,僅在該Web頁面初始化時加載相應(yīng)的HTML、JavaScript 和 CSS。加載完成,頁面不在重新加載或跳轉(zhuǎn),僅僅是里面的組件或模塊通過hash,或者h(yuǎn)istory api來進行交互和跳轉(zhuǎn),并通過ajax拉取數(shù)據(jù)來實現(xiàn)響應(yīng)功能,整個應(yīng)用就一個html,所以叫單頁面!

(5)、js的代碼無形的規(guī)范,團隊合作開發(fā)代碼可閱讀性更高

二.vue框架實現(xiàn)原理

對數(shù)據(jù)代理、數(shù)據(jù)劫持、模板解析、變異數(shù)組方法、雙向綁定

1).數(shù)據(jù)代理

簡單介紹數(shù)據(jù)代理
正常情況下,我們都會把數(shù)據(jù)寫在data里面

var vm = new Vue({
 el:'#app',
 data:{
    title: 'hello world'    
  }
methods:{
 changeTitle:function(){          
    this.title ='hello vue'        
  }  
}
})
console.log(vm.title)

如果沒有 數(shù)據(jù)代理,而我們又要修改data里面的title的話,methods里面的changeTitle只能這樣修改成 this.data.title='hello vue', 下面的console也只能改成 console.log(vm.data.title),數(shù)據(jù)代理就是這樣的功能。

2). 實現(xiàn)原理

通過遍歷data里面的屬性,將每個屬性通過object.defineProperty()設(shè)置getter和setter,將data里面的每個屬性都復(fù)制到與data同級的對象里。
觸發(fā)的getter將會觸發(fā)data里面對應(yīng)屬性的getter,觸發(fā)這里的setter將會觸發(fā)data里面對應(yīng)屬性的setter,從而實現(xiàn)代理。實現(xiàn)代碼如下:

var self = this;
 // this為vue實例, 即vm

Object.keys(this.data).forEach(function(key){ 
Object.defineProperty(this,key,{
    // this.title, 即vm.title
  enumerable:false,
  configurable:true,        
  get: function getter (){            
    return self.data[key];
  //觸發(fā)對應(yīng)data[key]的getter
  },      
  set:function setter (newVal){
     self.data[key]=newVal;
  //觸發(fā)對應(yīng)data[key]的setter     
  } 
});
}

3).雙向綁定

數(shù)據(jù)變動 ---> 視圖更新

視圖更新(input、textarea) --> 數(shù)據(jù)變動

視圖更新-->數(shù)據(jù)變動這個方向的綁定比較簡單,主要通過事件監(jiān)聽來改變數(shù)據(jù),比如input可以監(jiān)聽input事件,一旦觸發(fā)input事件就改變data。下面主要來理解一下 數(shù)據(jù)變動--->視圖更新這個方向的綁定。

4). 數(shù)據(jù)劫持

不妨讓我們自己思考一下,如何實現(xiàn)數(shù)據(jù)變動,對應(yīng)綁定數(shù)據(jù)的視圖就更新呢?

答案還是object.defineProperty,通過object.defineProperty遍歷設(shè)置this.data里面所有屬性,在每個屬性的setter里面去通知對應(yīng)的回調(diào)函數(shù),這里的回調(diào)函數(shù)包括dom視圖重新渲染的函數(shù)、使用$watch添加的回調(diào)函數(shù)等,這樣我們就通過object.defineProperty劫持了數(shù)據(jù),當(dāng)我們對數(shù)據(jù)重新賦值時,如 this.title='hello vue',就會觸發(fā)setter函數(shù),從而觸發(fā)dom視圖重新渲染的函數(shù),實現(xiàn)數(shù)據(jù)變動,對應(yīng)視圖更新。

5). 發(fā)布-訂閱模式

那么問題來了,我們?nèi)绾卧趕etter里面觸發(fā)所有綁定該數(shù)據(jù)的回調(diào)函數(shù)呢?

既然綁定該數(shù)據(jù)的回調(diào)函數(shù)不止一個,我們就把所有的回調(diào)函數(shù)放在一個數(shù)組里面,一旦觸發(fā)該數(shù)據(jù)的setter,就遍歷數(shù)組觸發(fā)里面所有的回調(diào)函數(shù),我們把這些回調(diào)函數(shù)稱為 訂閱者。數(shù)組最好就定義在setter函數(shù)的最近的上級作用域中,如下面實例代碼所示。

Object.keys(this.data).forEach(function(key){
  var subs = []; // 在這里放置添加所有訂閱者的數(shù)組 
  Object.defineProperty(this.data, key,{ // this.data.title
     enumerable:false,
     configurable:true,
     get:function getter (){
         console.log('訪問數(shù)據(jù)啦啦啦')          
        return this.data[key]; //返回對應(yīng)數(shù)據(jù)的值        
      },
    set:function setter (newVal){
      if(newVal === this.data[key]){
            return;  // 如果數(shù)據(jù)沒有變動,函數(shù)結(jié)束,不執(zhí)行下面的代碼
      }
      this.data[key]=newVal; //數(shù)據(jù)重新賦值
      subs.forEach(function(){
       // 通知subs里面的所有的訂閱者       
      })       
    }   
 });
}

那么問題又來了,怎么把綁定數(shù)據(jù)的所有回調(diào)函數(shù)放到一個數(shù)組里面呢?

我們可以在getter里面做做手腳,我們知道只要訪問數(shù)據(jù)就會觸發(fā)對應(yīng)數(shù)據(jù)的getter,那我們可以先設(shè)置一個全局變量target,如果我們要在data里面title屬性添加一個訂閱者(changeTitle函數(shù)),我們可以先設(shè)置target = changeTitle,把changeTitle函數(shù)緩存在target中,然后訪問this.title去觸發(fā)title的getter,在getter里面把target這個全局變量的值添加到subs數(shù)組里面,添加完成后再把全局變量target設(shè)置為null,以便添加其他訂閱者。實例代碼如下:

Object.keys(this.data).forEach(function(key){
  var subs = []; // 在這里放置添加所有訂閱者的數(shù)組 
  Object.defineProperty(this.data, key,{ // this.data.title
     enumerable:false,
     configurable:true,
     get:function getter (){
         console.log('訪問數(shù)據(jù)啦啦啦') 
         if (target) {
           subs.push(target);            
         }         
        return this.data[key]; //返回對應(yīng)數(shù)據(jù)的值        
      },
    set:function setter (newVal){
      if(newVal === this.data[key]){
            return;  // 如果數(shù)據(jù)沒有變動,函數(shù)結(jié)束,不執(zhí)行下面的代碼
      }
      this.data[key]=newVal; //數(shù)據(jù)重新賦值
      subs.forEach(function(){
       // 通知subs里面的所有的訂閱者       
      })       
    }   
 });
}

上面的代碼為了方便理解都是通過簡化的,實際上我們把訂閱者寫成一個構(gòu)造函數(shù)watcher,在實例化訂閱者的時候去訪問對應(yīng)的數(shù)據(jù),觸發(fā)相應(yīng)的getter,詳細(xì)的代碼可以閱讀DMQ的自己動手實現(xiàn)MVVM

6). 模板解析

通過上面的兩個步驟我們已經(jīng)實現(xiàn)一旦數(shù)據(jù)變動,就會通知對應(yīng)綁定數(shù)據(jù)的訂閱者,接下來我們來簡單介紹一個特殊的訂閱者,也就是視圖更新函數(shù),幾乎每個數(shù)據(jù)都會添加對應(yīng)的視圖更新函數(shù),所以我們就來簡單了解一下視圖更新函數(shù)。

假如說有下面這一段代碼,我們怎么把它解析成對應(yīng)的html呢?

<input v-model="title">
<h1>{{title}}</h1>
<button v-on:click="changeTitle">change title<button>

先簡單介紹視圖更新函數(shù)的用途,
比如解析指令 v-model="title", v-on:click="changeTitle",還有把{{title}}替換為對應(yīng)的數(shù)據(jù)等。

回到上面那個問題,如何解析模板?我們只要去遍歷所有dom節(jié)點包括其子節(jié)點,

如果節(jié)點屬性含有 v-model,視圖更新函數(shù)就為把input的value設(shè)置為title的值

如果節(jié)點為文本節(jié)點,視圖更新函數(shù)就為先用正則表達式取出大括號里面的值'title',再設(shè)置文本節(jié)點的值為data['title']

如果節(jié)點屬性含有 v-on:xxxx,視圖更新函數(shù)就為先用正則獲取事件類型為click,然后獲取該屬性的值為changeTitle,則事件的回調(diào)函數(shù)為this.methods['changeTitle'],接著用addEventListener監(jiān)聽節(jié)點click事件。

我們要知道視圖更新函數(shù)也是data對應(yīng)屬性的訂閱者,如果不知道如何觸發(fā)視圖更新函數(shù),可以把上面的發(fā)布-訂閱模式再看一遍。

可能有的小伙伴可能還有個疑問,如何實現(xiàn)input節(jié)點的值變化后,下面的h1節(jié)點的title值也發(fā)生變化?在遍歷所有節(jié)點后,如果節(jié)點含有屬性 v-model,就用addEventListener監(jiān)聽input事件,一旦觸發(fā)input事件,改變data['title']的值,就會觸發(fā)title的setter,從而通知所有的訂閱者。

7). 監(jiān)聽數(shù)組變化

無法監(jiān)控每個數(shù)組元素
如果讓我們自己實現(xiàn)監(jiān)聽數(shù)組的變化,我們可能會想到用object.defineProperty去遍歷數(shù)組每個元素并設(shè)置setter,但是vue源碼里面卻不是這樣寫的,因為對每一個數(shù)組元素defineProperty帶來代碼本身的復(fù)雜度增加和代碼執(zhí)行效率的降低。

8). 變異數(shù)組方法

既然無法通過defineProperty監(jiān)控數(shù)組的每個元素,我們可以重寫數(shù)組的方法(push, pop, shift, unshift, splice, sort, reverse)來改變數(shù)組。

vue文檔中是這樣寫的:

Vue 包含一組觀察數(shù)組的變異方法,所以它們也將會觸發(fā)視圖更新。這些方法如下:

push()

pop()

shift()

unshift()

splice()

sort()

reverse()

變異數(shù)組方法的缺陷

vue文檔中變異數(shù)組方法的缺陷

由于 JavaScript 的限制, Vue 不能檢測以下變動的數(shù)組:

當(dāng)你利用索引直接設(shè)置一個項時,例如: vm.items[indexOfItem] = newValue

當(dāng)你修改數(shù)組的長度時,例如: vm.items.length = newLength 同時文檔中也介紹了如何解決上面這兩個問題。

最后

以上是自己對vue一些基本原理的理解。
你無法猜測將要面臨什么樣的問題,那只好記錄更多的答案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • vue理解淺談 一 理解vue的核心理念 使用vue會讓人感到身心愉悅,它同時具備angular和react的優(yōu)點...
    ndxs2008閱讀 24,404評論 2 18
  • 一:什么是閉包?閉包的用處? (1)閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。在本質(zhì)上,閉包就 是將函數(shù)內(nèi)部和函數(shù)外...
    xuguibin閱讀 10,037評論 1 52
  • vue概述 在官方文檔中,有一句話對Vue的定位說的很明確:Vue.js 的核心是一個允許采用簡潔的模板語法來聲明...
    li4065閱讀 7,624評論 0 25
  • vue是什么? vue是構(gòu)建數(shù)據(jù)驅(qū)動的web界面的漸進式框架。Vue.js 的目標(biāo)是通過盡可能簡單的 API 實現(xiàn)...
    九四年的風(fēng)閱讀 8,818評論 2 131
  • VUE介紹 Vue的特點構(gòu)建用戶界面,只關(guān)注View層簡單易學(xué),簡潔、輕量、快速漸進式框架 框架VS庫庫,是一封裝...
    多多醬_DuoDuo_閱讀 2,855評論 1 17

友情鏈接更多精彩內(nèi)容