mysql 無論輸入什么都是現實 not found_NotAPanda

activities?page=1

前言

面試競爭力越來越大,是時候擼一波Vue和React源碼啦;

本文從20個層面來對比Vue和React的源碼區別;

如果需要了解API的區別,請戳:

Vue 開發必須知道的 36 個技巧

React 開發必須知道的 34 個技巧

文章源碼:請戳,原創碼字不易,歡迎star!

1.Vue和React源碼區別

1.1 Vue源碼

來張Vue源碼編譯過程圖

activities?page=1

圖片來源:分析Vue源碼實現

1.1.1 掛載

初始化$mounted會掛載組件,不存在 render 函數時需要編譯(compile);

1.1.2 compile

1.compile 分為 parse,optimize 和 generate,最終得到 render 函數;

2.parse 調用 parseHtml 方法,方法核心是利用正則解析 template 的指令,class 和 stype,得到 AST;

3.optimize 作用標記 static 靜態節點,后面 patch,diff會跳過靜態節點;

4.generate 是將 AST 轉化為 render 函數表達式,執行 vm._render 方法將 render 表達式轉化為VNode,得到 render 和 staticRenderFns 字符串;

5.vm._render 方法調用了 VNode 創建的方法createElement// render函數表達式

(function() {

with(this){

return _c('div',{ //創建一個 div 元素

attrs:{"id":"app"} //div 添加屬性 id

},[

_m(0), //靜態節點 header,此處對應 staticRenderFns 數組索引為 0 的 render function

_v(" "), //空的文本節點

(message) //判斷 message 是否存在

//如果存在,創建 p 元素,元素里面有文本,值為 toString(message)

?_c('p',[_v("\n "+_s(message)+"\n ")])

//如果不存在,創建 p 元素,元素里面有文本,值為 No message.

:_c('p',[_v("\n No message.\n ")])

]

)

}

})

1.1.3 依賴收集與監聽

這部分是數據響應式系統

1.調用 observer(),作用是遍歷對象屬性進行雙向綁定;

2.在 observer 過程中會注冊Object.defineProperty的 get 方法進行依賴收集,依賴收集是將Watcher 對象的實例放入 Dep 中;

3.Object.defineProperty的 set 會調用Dep 對象的 notify 方法通知它內部所有的 Watcher 對象調用對應的 update()進行視圖更新;

4.本質是發布者訂閱模式的應用

1.1.4 diff 和 patch

diff 算法對比差異和調用 update更新視圖:

1.patch 的 differ 是將同層的樹節點進行比較,通過唯一的 key 進行區分,時間復雜度只有 O(n);

2.上面將到 set 被觸發會調用 watcher 的 update()修改視圖;

3.update 方法里面調用 patch()得到同級的 VNode 變化;

4.update 方法里面調用createElm通過虛擬節點創建真實的 DOM 并插入到它的父節點中;

5.createElm實質是遍歷虛擬 dom,逆向解析成真實 dom;

1.2 React 源碼

來張React源碼編譯過程圖

activities?page=1

圖片來源:React源碼解析

1.2.1 React.Component

1.原型上掛載了setState和forceUpdate方法;

2.提供props,context,refs 等屬性;

3.組件定義通過 extends 關鍵字繼承 Component;

1.2.2 掛載

1.render 方法調用了React.createElement方法(實際是ReactElement方法);

2.ReactDOM.render(component,mountNode)的形式對自定義組件/原生DOM/字符串進行掛載;

3.調用了內部的ReactMount.render,進而執行ReactMount._renderSubtreeIntoContainer,就是將子DOM插入容器;

4.ReactDOM.render()根據傳入不同參數會創建四大類組件,返回一個 VNode;

5.四大類組件封裝的過程中,調用了mountComponet方法,觸發生命周期,解析出 HTML;

1.2.3 組件類型和生命周期

1.ReactEmptyComponent,ReactTextComponent,ReactDOMComponent組件沒有觸發生命周期;

2.ReactCompositeComponent類型調用mountComponent方法,會觸發生命周期,處理 state 執行componentWillMount鉤子,執行 render,獲得 html,執行componentDidMounted

1.2.4 data 更新 setState

細節請見 3.1

1.2.5 數據綁定

1.setState 更新 data 后,shouldComponentUpdate為 true會生成 VNode,為 false 會結束;

2.VNode會調用 DOM diff,為 true 更新組件;

1.3 對比

React:

1.單向數據流;

2.setSate 更新data 值后,組件自己處理;

3.differ 是首位是除刪除外是固定不動的,然后依次遍歷對比;

Vue:

1.v-model 可以實現雙向數據流,但只是v-bind:value 和 v-on:input的語法糖;

2.通過 this 改變值,會觸發 Object.defineProperty的 set,將依賴放入隊列,下一個事件循環開始時執行更新時才會進行必要的DOM更新,是外部監聽處理更新;

3.differcompile 階段的optimize標記了static 點,可以減少 differ 次數,而且是采用雙向遍歷方法;

2.React 和 Vue 渲染過程區別

2.1 React

1.生成期(掛載):參照 1.2.1

2.更新: 參照1.1.3和 1.1.4

3.卸載:銷毀掛載的組件

2.2 Vue

1.new Vue()初始化后initLifecycle(vm),initEvents(vm),initRender(vm),callHook(vm,beforeCreate),initState(vm),callHook(vm,created);A.initLifecycle, 建立父子組件關系,在當前實例上添加一些屬性和生命周期標識。如:children、refs、_isMounted等;

B.initEvents,用來存放除@hook:生命周期鉤子名稱="綁定的函數"事件的對象。如:$on、$emit等;

C.initRender,用于初始化$slots、$attrs、$listeners;

D.initState,是很多選項初始化的匯總,包括:props、methods、data、computed 和 watch 等;

E.callHook(vm,created)后才掛載實例

2.compileToFunction:就是將 template 編譯成 render 函數;

3.watcher: 就是執行1.2.3;

4.patch:就是執行 1.2.4

3.AST 和 VNode 的異同

1.都是 JSON 對象;

2.AST 是HTML,JS,Java或其他語言的語法的映射對象,VNode 只是 DOM 的映射對象,AST 范圍更廣;

3.AST的每層的element,包含自身節點的信息(tag,attr等),同時parent,children分別指向其父element和子element,層層嵌套,形成一棵樹

  • itemid:{{item.id}}

//轉化為 AST 格式為

{

"type": 1,

"tag": "div",

"attrsList": [

{

"name": "id",

"value": "app"

}

],

"attrsMap": {

"id": "app"

},

"children": [

{

"type": 1,

"tag": "ul",

"attrsList": [],

"attrsMap": {},

"parent": {

"$ref": "$"

},

"children": [

{

"type": 1,

"tag": "li",

// children省略了很多屬性,表示格式即可

}

],

"plain": true

}

],

"plain": false,

"attrs": [

{

"name": "id",

"value": "\"app\""

}

]

}

4.vnode就是一系列關鍵屬性如標簽名、數據、子節點的集合,可以認為是簡化了的dom:{

tag: string | void;

data: VNodeData | void;

children: ?Array;

text: string | void;

elm: Node | void;

ns: string | void;

context: Component | void;

...

}

5.VNode 的基本分類:EmptyVNode,TextVNode,ComponentVNNode,ElementVNNode,CloneVNode

6.創建 VNode方法一:

// 利用createDocumentFragment()創建虛擬 dom 片段

// 節點對象包含dom所有屬性和方法

// html

// js

const element = document.getElementById('ul');

const fragment = document.createDocumentFragment();

const browsers = ['Firefox', 'Chrome', 'Opera', 'Safari', 'Internet Explorer'];

browsers.forEach(function(browser) {

const li = document.createElement('li');

li.textContent = browser;

fragment.appendChild(li);  // 此處往文檔片段插入子節點,不會引起回流 (相當于打包操作)

});

console.log(fragment)

element.appendChild(fragment);  // 將打包好的文檔片段插入ul節點,只做了一次操作,時間快,性能好

方法二:

// 用 JS 對象來模擬 VNode

function Element (tagName, props, children) {

console.log('this',this)

this.tagName = tagName

this.props = props

this.children = children

}

let ElementO =new Element('ul', {id: 'list'}, [

new Element('li', {class: 'item'}, ['Item 1']),

new Element('li', {class: 'item'}, ['Item 2']),

new Element('li', {class: 'item'}, ['Item 3'])

])

// 利用 render 渲染到頁面

Element.prototype.render = function () {

const el = document.createElement(this.tagName) // 根據tagName構建

const props = this.props

for (const propName in props) { // 設置節點的DOM屬性

const propValue = props[propName]

el.setAttribute(propName, propValue)

}

const children = this.children || []

children.forEach(function (child) {

const childEl = (child instanceof Element)

? child.render() // 如果子節點也是虛擬DOM,遞歸構建DOM節點

: document.createTextNode(child) // 如果字符串,只構建文本節點

el.appendChild(childEl)

})

return el

}

console.log('ElementO',ElementO)

var ulRoot = ElementO.render()

console.log('ulRoot',ulRoot)

document.body.appendChild(ulRoot)

4.React 和Vue 的 differ 算法區

4.1 React

1.Virtual DOM 中的首個節點不執行移動操作(除非它要被移除),以該節點為原點,其它節點都去尋找自己的新位置; 一句話就是首位是老大,不移動;

2.在 Virtual DOM 的順序中,每一個節點與前一個節點的先后順序與在 Real DOM 中的順序進行比較,如果順序相同,則不必移動,否則就移動到前一個節點的前面或后面;

3.tree diff:只會同級比較,如果是跨級的移動,會先刪除節點 A,再創建對應的 A;將 O(n3) 復雜度的問題轉換成 O(n) 復雜度;

4.component diff:

根據batchingStrategy.isBatchingUpdates值是否為 true;

如果true 同一類型組件,按照 tree differ 對比;

如果 false將組件放入 dirtyComponent,下面子節點全部替換,具體邏輯看 3.1 setSate

5.element differ:

tree differ 下面有三種節點操作:INSERT_MARKUP(插入)、MOVE_EXISTING(移動)和 REMOVE_NODE(刪除)

請戳

6.代碼實現_updateChildren: function(nextNestedChildrenElements, transaction, context) {

var prevChildren = this._renderedChildren

var removedNodes = {}

var mountImages = []

// 獲取新的子元素數組

var nextChildren = this._reconcilerUpdateChildren(

prevChildren,

nextNestedChildrenElements,

mountImages,

removedNodes,

transaction,

context

)

if (!nextChildren && !prevChildren) {

return

}

var updates = null

var name

var nextIndex = 0

var lastIndex = 0

var nextMountIndex = 0

var lastPlacedNode = null

for (name in nextChildren) {

if (!nextChildren.hasOwnProperty(name)) {

continue

}

var prevChild = prevChildren && prevChildren[name]

var nextChild = nextChildren[name]

if (prevChild === nextChild) {

// 同一個引用,說明是使用的同一個component,所以我們需要做移動的操作

// 移動已有的子節點

// NOTICE:這里根據nextIndex, lastIndex決定是否移動

updates = enqueue(

updates,

this.moveChild(prevChild, lastPlacedNode, nextIndex, lastIndex)

)

// 更新lastIndex

lastIndex = Math.max(prevChild._mountIndex, lastIndex)

// 更新component的.mountIndex屬性

prevChild._mountIndex = nextIndex

} else {

if (prevChild) {

// 更新lastIndex

lastIndex = Math.max(prevChild._mountIndex, lastIndex)

}

// 添加新的子節點在指定的位置上

updates = enqueue(

updates,

this._mountChildAtIndex(

nextChild,

mountImages[nextMountIndex],

lastPlacedNode,

nextIndex,

transaction,

context

)

)

nextMountIndex++

}

// 更新nextIndex

nextIndex++

lastPlacedNode = ReactReconciler.getHostNode(nextChild)

}

// 移除掉不存在的舊子節點,和舊子節點和新子節點不同的舊子節點

for (name in removedNodes) {

if (removedNodes.hasOwnProperty(name)) {

updates = enqueue(

updates,

this._unmountChild(prevChildren[name], removedNodes[name])

)

}

}

}

4.2 Vue

1.自主研發了一套Virtual DOM,是借鑒開源庫snabbdom,

snabbdom地址

2.也是同級比較,因為在 compile 階段的optimize標記了static 點,可以減少 differ 次數;

3.Vue 的這個 DOM Diff 過程就是一個查找排序的過程,遍歷 Virtual DOM 的節點,在 Real DOM 中找到對應的節點,并移動到新的位置上。不過這套算法使用了雙向遍歷的方式,加速了遍歷的速度,更多請戳;

4.代碼實現:updateChildren (parentElm, oldCh, newCh) {

let oldStartIdx = 0, newStartIdx = 0

let oldEndIdx = oldCh.length - 1

let oldStartVnode = oldCh[0]

let oldEndVnode = oldCh[oldEndIdx]

let newEndIdx = newCh.length - 1

let newStartVnode = newCh[0]

let newEndVnode = newCh[newEndIdx]

let oldKeyToIdx

let idxInOld

let elmToMove

let before

while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {

if (oldStartVnode == null) { //對于vnode.key的比較,會把oldVnode = null

oldStartVnode = oldCh[++oldStartIdx]

}else if (oldEndVnode == null) {

oldEndVnode = oldCh[--oldEndIdx]

}else if (newStartVnode == null) {

newStartVnode = newCh[++newStartIdx]

}else if (newEndVnode == null) {

newEndVnode = newCh[--newEndIdx]

}else if (sameVnode(oldStartVnode, newStartVnode)) {

patchVnode(oldStartVnode, newStartVnode)

oldStartVnode = oldCh[++oldStartIdx]

newStartVnode = newCh[++newStartIdx]

}else if (sameVnode(oldEndVnode, newEndVnode)) {

patchVnode(oldEndVnode, newEndVnode)

oldEndVnode = oldCh[--oldEndIdx]

newEndVnode = newCh[--newEndIdx]

}else if (sameVnode(oldStartVnode, newEndVnode)) {

patchVnode(oldStartVnode, newEndVnode)

api.insertBefore(parentElm, oldStartVnode.el, api.nextSibling(oldEndVnode.el))

oldStartVnode = oldCh[++oldStartIdx]

newEndVnode = newCh[--newEndIdx]

}else if (sameVnode(oldEndVnode, newStartVnode)) {

patchVnode(oldEndVnode, newStartVnode)

api.insertBefore(parentElm, oldEndVnode.el, oldStartVnode.el)

oldEndVnode = oldCh[--oldEndIdx]

newStartVnode = newCh[++newStartIdx]

}else {

// 使用key時的比較

if (oldKeyToIdx === undefined) {

oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx) // 有key生成index表

}

idxInOld = oldKeyToIdx[newStartVnode.key]

if (!idxInOld) {

api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el)

newStartVnode = newCh[++newStartIdx]

}

else {

elmToMove = oldCh[idxInOld]

if (elmToMove.sel !== newStartVnode.sel) {

api.insertBefore(parentElm, createEle(newStartVnode).el, oldStartVnode.el)

}else {

patchVnode(elmToMove, newStartVnode)

oldCh[idxInOld] = null

api.insertBefore(parentElm, elmToMove.el, oldStartVnode.el)

}

newStartVnode = newCh[++newStartIdx]

}

}

}

if (oldStartIdx > oldEndIdx) {

before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].el

addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx)

}else if (newStartIdx > newEndIdx) {

removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx)

}

}

4.3 對比

相同點:

都是同層 differ,復雜度都為 O(n);

不同點:

1.React 首位是除刪除外是固定不動的,然后依次遍歷對比;

2.Vue 的compile 階段的optimize標記了static 點,可以減少 differ 次數,而且是采用雙向遍歷方法;

5.React 的 setState和 Vue 改變值的區別

5.1 setState

1.setState 通過一個隊列機制來實現 state 更新,當執行 setState() 時,會將需要更新的 state 淺合并后,根據變量 isBatchingUpdates(默認為 false)判斷是直接更新還是放入狀態隊列;

2.通過js的事件綁定程序 addEventListener 和使用setTimeout/setInterval 等 React 無法掌控的 API情況下isBatchingUpdates 為 false,同步更新。除了這幾種情況外batchedUpdates函數將isBatchingUpdates修改為 true;

3.放入隊列的不會立即更新 state,隊列機制可以高效的批量更新 state。而如果不通過setState,直接修改this.state 的值,則不會放入狀態隊列;

4.setState 依次直接設置 state 值會被合并,但是傳入 function 不會被合并;

讓setState接受一個函數的API的設計是相當棒的!不僅符合函數式編程的思想,讓開發者寫出沒有副作用的函數,而且我們并不去修改組件狀態,只是把要改變的狀態和結果返回給React,維護狀態的活完全交給React去做。正是把流程的控制權交給了React,所以React才能協調多個setState調用的關系// 情況一

state={

count:0

}

handleClick() {

this.setState({

count: this.state.count + 1

})

this.setState({

count: this.state.count + 1

})

this.setState({

count: this.state.count + 1

})

}

// count 值依舊為1

// 情況二

increment(state, props) {

return {

count: state.count + 1

}

}

handleClick() {

this.setState(this.increment)

this.setState(this.increment)

this.setState(this.increment)

}

// count 值為 3

5.更新后執行四個鉤子:shouleComponentUpdate,componentWillUpdate,render,componentDidUpdate

5.2 Vue 的 this 改變

1.vue 自身維護 一個 更新隊列,當你設置 this.a = 'new value',DOM 并不會馬上更新;

2.在更新 DOM 時是異步執行的。只要偵聽到數據變化,Vue 將開啟一個隊列,并緩沖在同一事件循環中發生的所有數據變更;

3.如果同一個 watcher 被多次觸發,只會被推入到隊列中一次;

4.也就是下一個事件循環開始時執行更新時才會進行必要的DOM更新和去重;

5.所以 for 循環 10000次 this.a = i vue只會更新一次,而不會更新10000次;

6.data 變化后如果 computed 或 watch 監聽則會執行;

6. Vue的v-for 或 React 的map 中為什么不要用 index作為 key

6.1 為什么要加 key

6.1.1 React

1.上面的 5.1 講到 React 的 differ 中 element differ 有三種節點操作;

2.場景一不加 key:

新老集合進行 diff 差異化對比,發現 B != A,則創建并插入 B 至新集合,刪除老集合 A;以此類推,創建并插入 A、D 和 C,刪除 B、C 和 D;

都是相同的節點,但由于位置發生變化,導致需要進行繁雜低效的刪除、創建操作,其實只要對這些節點進行位置移動即可;

3.場景二加 key:

新建:從新集合中取得 E,判斷老集合中不存在相同節點 E,則創建新節點 ElastIndex不做處理E的位置更新為新集合中的位置,nextIndex++;

刪除:當完成新集合中所有節點 diff 時,最后還需要對老集合進行循環遍歷,判斷是否存在新集合中沒有但老集合中仍存在的節點,發現存在這樣的節點 D,因此刪除節點 D;

4.總結:

顯然加了 key 后操作步驟要少很多,性能更好;

但是都會存在一個問題,上面場景二只需要移動首位,位置就可對應,但是由于首位是老大不能動,所以應該盡量減少將最后一個節點移動到首位,更多請戳。

6.1.2 Vue

Vue 不加 key 場景分析:

1.場景一不加 key:

也會將使用了雙向遍歷的方式查找,發現 A,B,C,D都不等,先刪除再創建;

2.場景二加 key:雙向遍歷的方式查找只需要創建E,刪除D,改變 B、C、A的位置

6.2 為什么 key 不能為 index

這個問題分為兩個方面:

1.如果列表是純靜態展示,不會 CRUD,這樣用 index 作為 key 沒得啥問題;

2.如果不是const list = [1,2,3,4];

// list 刪除 4 不會有問題,但是如果刪除了非 4 就會有問題

// 如果刪除 2

const listN= [1,3,4]

// 這樣index對應的值就變化了,整個 list 會重新渲染

3.所以 list 最好不要用 index 作為 key

7. Redux和 Vuex 設計思想

7.1 Redux

API:

1.Redux則是一個純粹的狀態管理系統,React利用React-Redux將它與React框架結合起來;

2.只有一個用createStore方法創建一個 store;

3.action接收 view 發出的通知,告訴 Store State 要改變,有一個 type 屬性;

4.reducer:純函數來處理事件,純函數指一個函數的返回結果只依賴于它的參數,并且在執行過程里面沒有副作用,得到一個新的 state;

源碼組成:1.createStore 創建倉庫,接受reducer作為參數

2.bindActionCreator 綁定store.dispatch和action 的關系

3.combineReducers 合并多個reducers

4.applyMiddleware 洋蔥模型的中間件,介于dispatch和action之間,重寫dispatch

5.compose 整合多個中間件

6.單一數據流;state 是可讀的,必須通過 action 改變;reducer設計成純函數;

7.2 Vuex

1.Vuex是吸收了Redux的經驗,放棄了一些特性并做了一些優化,代價就是VUEX只能和VUE配合;

2.store:通過 new Vuex.store創建 store,輔助函數mapState;

3.getters:獲取state,有輔助函數 mapGetters;

4.action:異步改變 state,像ajax,輔助函數mapActions;

5.mutation:同步改變 state,輔助函數mapMutations;

7.3 對比1.Redux: view——>actions——>reducer——>state變化——>view變化(同步異步一樣)

2.Vuex: view——>commit——>mutations——>state變化——>view變化(同步操作)

view——>dispatch——>actions——>mutations——>state變化——>view變化(異步操作)

8.redux 為什么要把 reducer 設計成純函數

1.純函數概念:一個函數的返回結果只依賴于它的參數(外面的變量不會改變自己),并且在執行過程里面沒有副作用(自己不會改變外面的變量);

2.主要就是為了減小副作用,避免影響 state 值,造成錯誤的渲染;

3.把reducer設計成純函數,便于調試追蹤改變記錄;

9.Vuex的mutation和Redux的reducer中為什么不能做異步操作

1.在 vuex 里面 actions 只是一個架構性的概念,并不是必須的,說到底只是一個函數,你在里面想干嘛都可以,只要最后觸發 mutation 就行;

2.vuex 真正限制你的只有 mutation 必須是同步的這一點(在 redux 里面就好像 reducer 必須同步返回下一個狀態一樣);

3.每一個 mutation 執行完成后都可以對應到一個新的狀態(和 reducer 一樣),這樣 devtools 就可以打個 snapshot 存下來,然后就可以隨便 time-travel 了。如果你開著 devtool 調用一個異步的 action,你可以清楚地看到它所調用的 mutation 是何時被記錄下來的,并且可以立刻查看它們對應的狀態;

4.其實就是框架是這么設計的,便于調試追蹤改變記錄

10.雙向綁定和 vuex 是否沖突

1.在嚴格模式中使用Vuex,當用戶輸入時,v-model會試圖直接修改屬性值,但這個修改不是在mutation中修改的,所以會拋出一個錯誤;

2.當需要在組件中使用vuex中的state時,有2種解決方案:在input中綁定value(vuex中的state),然后監聽input的change或者input事件,在事件回調中調用mutation修改state的值;

// 雙向綁定計算屬性

computed: {

message: {

get () {

return this.$store.state.obj.message

},

set (value) {

this.$store.commit('updateMessage', value)

}

}

}

11. Vue的nextTick原理

11.1 使用場景

什么時候會用到?

nextTick的使用原則主要就是解決單一事件更新數據后立即操作dom的場景。

11.2 原理

1.vue 用異步隊列的方式來控制 DOM 更新和 nextTick 回調先后執行;

2.microtask 因為其高優先級特性,能確保隊列中的微任務在一次事件循環前被執行完畢;

3.考慮兼容問題,vue 做了 microtask 向 macrotask 的降級方案;

4.代碼實現:const simpleNextTick = function queueNextTick (cb) {

return Promise.resolve().then(() => {

cb()

})

}

simpleNextTick(() => {

console.log(this.$refs.test.innerText)

})

13. Vue 的data 必須是函數而 React 的 state 是對象

13.1 Vue 的 data 必須是函數

對象是引用類型,內存是存貯引用地址,那么子組件中的 data 屬性值會互相污染,產生副作用;

如果是函數,函數的{}構成作用域,每個實例相互獨立,不會相互影響;

13.2 React 的 state 是對象

因為 state 是定義在函數里面,作用域已經獨立

14.Vue 的合并策略

1.生命周期鉤子:合并為數組function mergeHook (

parentVal,

childVal

) {

return childVal

? parentVal // 如果 childVal存在

? parentVal.concat(childVal) // 如果parentVal存在,直接合并

: Array.isArray(childVal) // 如果parentVal不存在

? childVal // 如果chilidVal是數組,直接返回

: [childVal] // 包裝成一個數組返回

: parentVal // 如果childVal 不存在 直接返回parentVal

}

// strats中添加屬性,屬性名為生命周期各個鉤子

config._lifecycleHooks.forEach(function (hook) {

strats[hook] = mergeHook // 設置每一個鉤子函數的合并策略

})

2.watch:合并為數組,執行有先后順序;

3.assets(components、filters、directives):合并為原型鏈式結構,合并的策略就是返回一個合并后的新對象,新對象的自有屬性全部來自 childVal, 但是通過原型鏈委托在了 parentVal 上function mergeAssets (parentVal, childVal) { // parentVal: Object childVal: Object

var res = Object.create(parentVal || null) // 原型委托

return childVal

? extend(res, childVal)

: res

}

config._assetTypes.forEach(function (type) {

strats[type + 's'] = mergeAssets

})

4.data為function,需要合并執行后的結果,就是執行 parentVal 和 childVal 的函數,然后再合并函數返回的對象;

5.自定義合并策略:Vue.config.optionMergeStrategies.watch = function (toVal, fromVal) {

// return mergedVal

}

14.Vue-router 的路由模式

1.三種:"hash" | "history" | "abstract";

2.hash(默認),history 是瀏覽器環境,abstract是 node 環境;

3.hash: 使用 URL hash 值來作路由,是利用哈希值實現push、replace、go 等方法;

4.history:依賴 HTML5 History API新增的 pushState() 和 replaceState(),需要服務器配置;

5.abstract:如果發現沒有瀏覽器的 API,路由會自動強制進入這個模式。

15.Vue 的事件機制class Vue {

constructor() {

// 事件通道調度中心

this._events = Object.create(null);

}

$on(event, fn) {

if (Array.isArray(event)) {

event.map(item => {

this.$on(item, fn);

});

} else {

(this._events[event] || (this._events[event] = [])).push(fn); }

return this;

}

$once(event, fn) {

function on() {

this.$off(event, on);

fn.apply(this, arguments);

}

on.fn = fn;

this.$on(event, on);

return this;

}

$off(event, fn) {

if (!arguments.length) {

this._events = Object.create(null);

return this;

}

if (Array.isArray(event)) {

event.map(item => {

this.$off(item, fn);

});

return this;

}

const cbs = this._events[event];

if (!cbs) {

return this;

}

if (!fn) {

this._events[event] = null;

return this;

}

let cb;

let i = cbs.length;

while (i--) {

cb = cbs[i];

if (cb === fn || cb.fn === fn) {

cbs.splice(i, 1);

break;

}

}

return this;

}

$emit(event) {

let cbs = this._events[event];

if (cbs) {

const args = [].slice.call(arguments, 1);

cbs.map(item => {

args ? item.apply(this, args) : item.call(this);

});

}

return this;

}}

16.keep-alive 的實現原理和緩存策略

1.獲取keep-alive第一個子組件;

2.根據include exclude名單進行匹配,決定是否緩存。如果不匹配,直接返回組件實例,如果匹配,到第3步;

3.根據組件id和tag生成緩存組件的key,再去判斷cache中是否存在這個key,即是否命中緩存,如果命中,用緩存中的實例替代vnode實例,然后更新key在keys中的位置,(LRU置換策略)。如果沒有命中,就緩存下來,如果超出緩存最大數量max,刪除cache中的第一項。

4.keep-alive是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出現在父組件鏈中;

5.LRU算法:根據數據的歷史訪問記錄來進行淘汰數據,其實就是訪問過的,以后訪問概率會高;

6.LRU 實現:

新數據插入到鏈表頭部;

每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部;

當鏈表滿的時候,將鏈表尾部的數據丟棄。

17.Vue 的 set 原理

1.由于 Object.observe()方法廢棄了,所以Vue 無法檢測到對象屬性的添加或刪除;

2.原理實現:

判斷是否是數組,是利用 splice 處理值;

判斷是否是對象的屬性,直接賦值;

不是數組,且不是對象屬性,創建一個新屬性,不是響應數據直接賦值,是響應數據調用defineReactive;export function set (target: Array | Object, key: any, val: any): any {

// 如果 set 函數的第一個參數是 undefined 或 null 或者是原始類型值,那么在非生產環境下會打印警告信息

// 這個api本來就是給對象與數組使用的

if (process.env.NODE_ENV !== 'production' &&

(isUndef(target) || isPrimitive(target))

) {

warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target: any)}`)

}

if (Array.isArray(target) && isValidArrayIndex(key)) {

// 類似$vm.set(vm.$data.arr, 0, 3)

// 修改數組的長度, 避免索引>數組長度導致splcie()執行有誤

target.length = Math.max(target.length, key)

// 利用數組的splice變異方法觸發響應式, 這個前面講過

target.splice(key, 1, val)

return val

}

// target為對象, key在target或者target.prototype上。

// 同時必須不能在 Object.prototype 上

// 直接修改即可, 有興趣可以看issue: https://github.com/vuejs/vue/issues/6845

if (key in target && !(key in Object.prototype)) {

target[key] = val

return val

}

// 以上都不成立, 即開始給target創建一個全新的屬性

// 獲取Observer實例

const ob = (target: any).__ob__

// Vue 實例對象擁有 _isVue 屬性, 即不允許給Vue 實例對象添加屬性

// 也不允許Vue.set/$set 函數為根數據對象(vm.$data)添加屬性

if (target._isVue || (ob && ob.vmCount)) {

process.env.NODE_ENV !== 'production' && warn(

'Avoid adding reactive properties to a Vue instance or its root $data ' +

'at runtime - declare it upfront in the data option.'

)

return val

}

// target本身就不是響應式數據, 直接賦值

if (!ob) {

target[key] = val

return val

}

// 進行響應式處理

defineReactive(ob.value, key, val)

ob.dep.notify()

return val

}

https://juejin.im/post/5e04411f6fb9a0166049a073#heading-18

18.簡寫 Reduxfunction createStore(reducer) {

let state;

let listeners=[];

function getState() {

return state;

}

function dispatch(action) {

state=reducer(state,action);

listeners.forEach(l=>l());

}

function subscribe(listener) {

listeners.push(listener);

return function () {

const index=listeners.indexOf(listener);

listeners.splice(inddx,1);

}

}

dispatch({});

return {

getState,

dispatch,

subscribe

}

}

19 react-redux是如何來實現的

源碼組成:

1.connect 將store和dispatch分別映射成props屬性對象,返回組件

2.context 上下文 導出Provider,,和 consumer

3.Provider 一個接受store的組件,通過context api傳遞給所有子組件

20. react16 的 fiber 理解

1.react 可以分為 differ 階段和 commit(操作 dom)階段;

2.v16 之前是向下遞歸算法,會阻塞;

3.v16 引入了代號為 fiber 的異步渲染架構;

4.fiber 核心實現了一個基于優先級和requestIdleCallback循環任務調度算法;

5.算法可以把任務拆分成小任務,可以隨時終止和恢復任務,可以根據優先級不同控制執行順序,更多請戳;

總結

文章源碼:請戳,原創碼字不易,歡迎star!

您的鼓勵是我持續創作的動力!查看原文

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/258162.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/258162.shtml
英文地址,請注明出處:http://en.pswp.cn/news/258162.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

這五張PPT告訴你,如何打造無人駕駛“最強大腦”

“英特爾在談無人駕駛&#xff0c;會成為汽車制造商嗎?NO!我們要為無人駕駛提供從車、連接到云的‘最強大腦’。” 在昨天于北京舉行的英特爾無人駕駛分享會上&#xff0c;英特爾無人駕駛事業部中國區市場總監徐偉杰在主旨演講中開門見山。 這也是英特爾無人駕駛事業部去年11月…

javascript之ua與urlSchema

我們首先拿到瀏覽器ua: 1 var ua navigator.userAgent; 1 if (ua.indexOf("MicroMessenger") > -1) { 2 alert("微信瀏覽器"); 3 } 1 if (ua.indexOf("iPhone") > -1) { 2 alert("iphone"); 3 } 其…

ezdpl Linux自動化部署實戰

最近把ezdpl在生產環境中實施了&#xff0c;再加上這段時間的一些修改&#xff0c;一并介紹一下。再次申明&#xff1a; ezdpl不是開箱即用的&#xff0c;需要根據自己的應用環境定制。對初學者來說使用起來反倒困難更多、風險更大。它不是一個通用的項目&#xff0c;更多的是提…

無法打開輸入文件mysql_錯誤LNK1181,pip安裝“無法打開輸入文件”mysqlclient.lib'...

我是Python新手&#xff0c;正在嘗試安裝mysql模塊&#xff0c;但是在解決了其他5個問題之后&#xff0c;我現在遇到了一個問題&#xff0c;當我嘗試安裝該模塊時&#xff0c;會出現以下日志&#xff1a;PS C:\Users\poste> pip install mysqlCollecting mysqlUsing cached …

俄羅斯將封殺LinkedIn 推動個人數據本地化

北京時間11月11日上午消息&#xff0c;莫斯科一家法院本周四支持了在俄羅斯封殺職業社交網站LinkedIn的決定。 俄羅斯聯邦通信監管局&#xff08;Roskomnadzor&#xff09;之前要求國內外企業從2015年9月開始&#xff0c;必須將所有俄羅斯用戶的個人數據存儲在該國境內。Linked…

python的datetime舉例_Python datetime模塊的使用示例

1、獲取當前年月日時分秒# -*- encodingutf-8 -*-import datetimenow datetime.datetime.now()print("now:{}".format(now))year now.yearprint("year:{}".format(year))month now.monthprint("month:{}".format(month))day now.dayprint(&q…

vs2015 去除 git 源代碼 綁定,改成向tfs添加源碼管理

除了下文的方法是將源碼管理從git改成tfs之外&#xff0c;還要做以下幾步即可 向tfs添加源碼 打開源碼管理(管理連接)&#xff0c;雙擊打開你要向其中添加的tfs連接選中該解決方案&#xff0c;右鍵 將解決方案添加到源碼管理嵌入完畢vs2015 去除 git 源代碼 綁定 第一次碰到這個…

HDU 4609 FFT

題目大意 給定n條邊的邊值&#xff0c;求任意取三條邊能組成三角形的概率 這里概率 P valid/tot tot (n-2)*(n-1)*n/6是沒問題的 valid表示合法的方式 先考慮&#xff0c;任意兩條邊組合形成方法的總數 因為邊值在100000的范圍內&#xff0c;這里組合用fft計算 得到最后形成和…

《日志管理與分析權威指南》一2.3 良好日志記錄的標準

本節書摘來華章計算機《日志管理與分析權威指南》一書中的第2章 &#xff0c;第2.3節&#xff0c;&#xff08;美&#xff09; Anton A. Chuvakin Kevin J. Schmidt Christopher Phillips 著 姚 軍 簡于涵 劉 暉 等譯更多章節內容可以訪問云棲社區“華章計算機”公眾號查…

Python【01】【基礎部分】- A

一、WHATS PYTHON ? 1、python 簡介 Python&#xff08;英語發音&#xff1a;/?pa?θ?n/&#xff09;, 是一種面向對象、解釋型計算機程序設計語言&#xff0c;由Guido van Rossum于1989年發明&#xff0c;第一個公開發行版發行于1991年。Python是純粹的自由軟件&#xff0…

java的自增自減_Java中自增和自減操作符(++/--)的那些事

自增()和自減(--)運算符在JAVA語言中存在著很多運算符&#xff0c;但是在實際開發中我們或許很少用到它們&#xff0c;在初次學習中卻時常出現它們的身影&#xff0c;對于這些運算符的含義和用法&#xff0c;是否還記得呢&#xff1f;1. 概述自增操作符()和自減操作符(--)是對變…

Finished yeah!

終于到了最后的博客階段&#xff0c;這時候才知道博客此時此刻是多么的愜意&#xff0c;它成了書寫心聲的自由平臺&#xff01;耗時一天完成這作業說起來也是蠻辛苦的&#xff0c;編譯器需要新裝&#xff0c;IDE需要熟悉&#xff0c;當然最主要的是之前淺入淺出的C功底在此次作…

《Python語言程序設計》——1.6 開始學習Python

本節書摘來自華章計算機《Python語言程序設計》一書中的第1章&#xff0c;第1.6節,作者&#xff1a;&#xff3b;美&#xff3d;梁勇&#xff08;Y. Daniel Liang&#xff09; 更多章節內容可以訪問云棲社區“華章計算機”公眾號查看。 1.6 開始學習Python 關鍵點&#xff1a;…

Tomcat性能調優

1、集成apache 雖然Tomcat也可以作web服務器&#xff0c;但是處理靜態html的速度比不上apache&#xff0c;且其作為web服務器的功能遠不如Apache&#xff0c;因此把apache和tomcat集成起來&#xff0c;講html和jsp功能部分進行明確的分工&#xff0c;讓tomcat只處理jsp部分&…

【轉】sip中的subscribe和notify擴展應用技術

http://blog.csdn.net/hwz119/article/details/3965322轉載于:https://www.cnblogs.com/matthew-2013/p/4917207.html

再讀《被神化的框架》

開發框架&#xff0c;構件&#xff0c;組件非常地多&#xff0c;而且&#xff0c;趨勢是越來越多&#xff0c;特別是在java中。當然也不是說其它平臺的少。而特別是框架越來越被神化了&#xff0c;似乎用之解決一切問題&#xff0c;不用就要敲壞鍵盤。對于老衲這樣的打字員來說…

河南推出近萬億PPP投資計劃 鄭州實現智慧城市全覆蓋

1 近萬億PPP項目啟動 眼下&#xff0c;國內財經新聞的熱點聚焦在PPP開發上&#xff0c;這與PPP支撐國內經濟平衡運行的一支強勁力量正被政府看好。就連二級市場也出現了PPP概念的搶籌現象。 9月27日&#xff0c;股市再一次遭遇拋售&#xff0c;大盤創出階段性新低&#xff0c;然…

java基礎實例代碼_Java基礎實例

打印等腰三角形代碼public class ForForTest{public static void main(String []args){for(int x0;x<5;x){for(int yx1;y<5;y){System.out.print(" ");}for(int z0;zSystem.out.print("* ");}System.out.println();}}}折半查找代碼&#xff1a;//練習…

###《Effective STL》--Chapter3

點擊查看Evernote原文。 #author: gr #date: 2014-09-13 #email: forgeruigmail.com Chapter3 關聯容器 Topic 22: 切勿直接修改set或multiset中的鍵 修改元素的值可以通過下面五步操作&#xff0c;避免作類型轉換。 struct IDNumberLess : public binary…

如何獲取網絡資源?

# encodingutf-8 #python 2.7.10 #xiaodeng #如何獲取網絡資源&#xff1f; #HTTP權威指南 26頁#url就是因特網資源的標準化名稱&#xff0c;他指向每一條電子信息&#xff0c;告訴你他們位于何處&#xff0c;以及如何與之交互。 #URL是瀏覽器尋找信息時所需的資源位置。 #一個…