vue 启动时卡死_十分钟浅入Vue 原理
vue原理
引用眾所周知vue是一個MVVM 漸進式框架,MVVM是vue的設計模式,在vue框架中數據會自動驅動視圖。
1、MVVM設計模式
?
解釋View是視圖,就是DOM;對應視圖也就是HTML部分--代表UI組件,它負責將數據模型轉化成UI展現出來。 Model是模型,就是vue組件里的data,或者說是vuex里的數據;--代表數據模型,也可以在Model中定義數據修改和操作的業務邏輯。 ViewModel--監聽模型數據也就是data的的改變和控制視圖行為、處理用戶交互,簡單理解就是一個同步View和Model的對象,連接Model和View。
總結在MVVM架構下,View和Model之間并沒有直接的聯系,而是通過ViewMode進行交互,Model和ViewModel之間的交互是雙向的,因此View數據的變化會同步到Model中,而Model數據的變化也會立即反應到View上。
ViewModel通過雙向數據綁定把View層和Model層連接了起來,而View和Model之間的同步工作完全是自動的,無需人為干涉,因此開發者只需關注業務邏輯,不需要手動操作DOM,不需要關注數據狀態的同步問題,復雜的數據狀態維護完全由MVVM來統一管理。
由此,我們可以引出vue是響應式的
2、響應式
說明Vue 的響應式原理是核心是通過 ES5 的保護對象的 Object.defindeProperty中的訪問器屬性中的 get和 set方法,data 中聲明的屬性都被添加了訪問器屬性,當讀取 data 中的數據時自動調用 get 方法,當修改 data 中的數據時,自動調用 set 方法,檢測到數據的變化,會通知觀察者 Wacher,觀察者 Wacher自動觸發重新render 當前組件(子組件不會重新渲染),生成新的虛擬 DOM 樹,Vue 框架會遍歷并對比新虛擬 DOM 樹和舊虛擬 DOM 樹中每個節點的差別,并記錄下來,最后,加載操作,將所有記錄的不同點,局部修改到真實 DOM 樹上。
2.1 雙向數據綁定原理
檢測data變化的核心APIObject.defindeProperty基本使用
const data = {}; let name = "張三";Object.defineProperty(data,'name',{get:function(){console.log('觸發get')return name},set:function(newVal){console.log('觸發set')name=newVal} })//測試 console.log(data.name) // 觸發get 張三 data.name = '李四' // 觸發set這樣就是可以實現數據的獲取和賦值的監聽
2.2 接下來看看vue是如何監聽data變化的
//觸發更新視圖 function updateView() {console.log('視圖更新') } //重新定義數組原型 const oldArrayProperty = Array.prototypo //創建新對象,原型指向oldArrayProperty,在拓展新的方法(這樣不會影響原型) let arrayProto = Array.prototype let methods = ['pop', 'shift', 'unshift', 'sort', 'reverse', 'splice', 'push'] methods.forEach(methodName => {arrayProto[methodName] = function () {updateView ()oldArrayProperty[methodName].call(this,...arguments)} }) //監聽對象屬性 function observer(target){if(typeof target !=='object' || target === null) {//不是對象或者數組return target}//重新定義數組原型if (Array.isArray(target)) {target.__proto__ = arrProto}//重新定義各個屬性(for in 對象/數組都可以遍歷)for(let key in target) {defineReactive(target,key,target[key])} } //重新定義屬性,監聽起來 function defineReactive (target, key, value){//遞歸深度監聽observer(value)//核心API Object.defineProperty(target,key,{get(){return value},set(newValue){if(newValue !== value) {// 深度監聽observer(newValue)//設置新值value = newvalue//觸發更新視圖updateView()}}}) } // 準備數據 const data = {name: 'zhangsan',age: 20,info: {address: '北京' // 需要深度監聽},nums: [10, 20, 30] }data.name = 'lisi' //視圖更新data.age = 21 //視圖更新console.log('age', data.age) //age 21data.x = '100' // 新增屬性,監聽不到 —— 所以有 Vue.setdelete data.name // 刪除屬性,監聽不到 —— 所以有 Vue.detedata.info.address = '上海' // 深度監聽data.nums.push(4) // 視圖更新 缺點- 深度監聽obj,需要遞歸到底,一次性計算量大,如果數據過大頁面,頁面可能會卡死
- 無法監聽新增屬性/刪除屬性(所以vue提供了Vue.set Vue.delete)
- 無法原生監聽數組,需要做特殊處理
3、vdom和diff
背景DOM操作是非常耗時的,Vue 和React 是數據驅動視圖,就是 通過 虛擬DOM(vdom)來解決的這個問題
3.1 vdom
vdom就是一段js形式的html代碼
用 js 模擬 DOM 結構
<div id ='app' class='box'><p>p標簽的文本</p><ul style='font-size:20px'><li>li標簽文本</li></ul> </div>{tag: 'div',props: {id: 'app',className: 'box'},children: [{tag: 'p',children: 'p標簽的文本'},{tag: 'ul',props: {style: 'font-size:20px'},children: [{tag: 'li',children:'li標簽文本'}]}]}- tag 標簽
- props 屬性(包括 id、className 、 style、事件等)
- children 子元素,數組或者字符串
可以通過學習snabbdom 進一步了解
var snabbdom = require('snabbdom'); var patch = snabbdom.init([ // Init patch function with chosen modulesrequire('snabbdom/modules/class').default, // makes it easy to toggle classesrequire('snabbdom/modules/props').default, // for setting properties on DOM elementsrequire('snabbdom/modules/style').default, // handles styling on elements with support for animationsrequire('snabbdom/modules/eventlisteners').default, // attaches event listeners ]); var h = require('snabbdom/h').default; // helper function for creating vnodesvar container = document.getElementById('container');var vnode = h('div#container.two.classes', {on: {click: someFn}}, [h('span', {style: {fontWeight: 'bold'}}, 'This is bold'),' and this is just normal text',h('a', {props: {href: '/foo'}}, 'I'll take you places!') ]); // Patch into empty DOM element – this modifies the DOM as a side effect patch(container, vnode);var newVnode = h('div#container.two.classes', {on: {click: anotherEventHandler}}, [h('span', {style: {fontWeight: 'normal', fontStyle: 'italic'}}, 'This is now italic type'),' and this is still just normal text',h('a', {props: {href: '/bar'}}, 'I'll take you places!') ]); // Second `patch` invocation patch(vnode, newVnode); // Snabbdom efficiently updates the old view to the new state 解析- h 是一個函數,接收三個參數(標簽或選擇器,屬性,子節點數組),返回一個vnode結構;
- patch 補丁的意思;
- patch(containerro/容器, vnode/虛擬dom) ,表示把vnode渲染到DOM結構中
- patch(vnode, newVnode); 表示更新已有的內容(具體怎么計算更新對應的內容就是使用diff算法)
- patch(containerro, null); 清空DOM結構
3.2 diff算法,新舊vnode對比,計算出最小的更新范圍
核心- 同層級比較(只比較同一層級,不跨級比較)
- tag 不相同,則直接刪除重建,不在深度比較
- tag 和 key,兩個都相同,則認為是相同節點,不在深度比較
再此推薦兩篇文章
- 解析vue2.0的diff算法
- VirtualDOM與diff(Vue實現)
4、渲染過程
vue組件渲染/更新過程(異步渲染)
- 初次渲染過程
- 更新過程
4.1初次渲染過程
- 解析模板為render函數(一般在開發環境已經完成,vue-loader)4
- 觸發響應式,監聽data屬性getter setter(模板中使用到的變量會觸發getter)
- 執行render函數(觸發getter),生成vnode,patch(elem,vnode)渲染到頁面上
? 如果模板中沒有用的data數據就不會觸發getter,因為和視圖沒關系(vue里面的優化)
4.2 更新過程
- 修改data的數據,觸發setter(此前data數據在getter中已被監聽)
- 重新執行render函數,生成newVnode(新的虛擬dom)
- 使用 patch(vnode,newVnode)更新到頁面上
- 1、編譯模板生成render函數,生成vdom
- 2、執行render函數,觸發data中的getter
- 3、getter方法收集依賴(通俗點就是,在模板里面觸發了哪個變量的getter,就把哪個變量觀察起來)
- 4、在依賴中setter修改data中的數據的時候,Notify看下修改的那個數據是不是之前被觀察起來的
- 5、如果是之前觀察起來的,就重新渲染( re-render),重新生成render函數,生成newVdom形成一個閉環
5、路由
vue分為hash(默認)以及 history 兩個路由模式
解析- protocol - 協議
- hostname - 主機名
- port - 端口
- pathname - url 路徑
- search - ?號之后的參數
- hash - #號之后的部分
5.1 hash
特點- hash 變化會觸發頁面跳轉,即瀏覽器的前進,后退
- hash 變化不會刷新頁面,SPA(單頁面)必須的特點
- hash 永遠不會提交到server 端
vue中就是通過hash 的變化觸發路由的變化,來觸發視圖的渲染
js 實現hash<body><p>hash路由</p><button id='btn'>修改 hash</button> </body><script>//hash 變化 包括;//a. js 修改URL//b. 手動修改url的hash//c.瀏覽器的前進、后退//頁面初次加載獲取hashwindow.addEventListener ('DOMCintentLoaded',() =>{console.log('hash',location.hash)})//hash變化觸發window.onhashchange = (event) =>{console.log('hash',location.hash)}//js 修改 urldocument.getElementById('btn').addEventListener('click',()=>{location.href = '#/user'}) </script>5.2 history
h5 history 主要是通過 history.pushState 跳轉 和 window.onpopstate 監聽頁面的前進和后退
<body><p>historyh路由</p><button id='btn'>修改 url</button> </body><script>//頁面初次加載獲取hashwindow.addEventListener ('DOMCintentLoaded',() =>{console.log('load',location.pathname)})//js 修改 urldocument.getElementById('btn').addEventListener('click',()=>{//pushState 有三個參數//第一個參數是個js對象,可以放任何的內容,可以在onpostate事件中(后面介紹)獲取到便于做相應的、處理。//第二個參數是頁面標題:目前所有瀏覽器都不支持,填空字符串即可//第三個參數是個字符串,就是保存到history中的url。let state= {title:'新頁面'}history.pushState(state,'','user')})//監聽瀏覽器的前進、后退window.onpostate = (event) => {console.log(event.state) // {title:'新頁面'}console.log(location.pathname)} </script>上面的代碼如果放在本地html 文件中運行 js代碼會報錯,需要放在web服務器
注意history 模式需要后端配合,就是無論用戶訪問什么路由,所有路由的切換都由前端來做,后端只需要返回index.html的文件,如果后面沒有配置兼容,當訪問user這個路由,點擊刷新,就會報user頁面找不到
以上內容純屬個人理解,若有不對,請留言糾正!
關注【前端知識小冊】,第一時間獲取前端優質文章!
總結
以上是生活随笔為你收集整理的vue 启动时卡死_十分钟浅入Vue 原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 五味子多少钱啊?
- 下一篇: 查表法实现反正切_关于python实现C