前端在複雜性日增的今天,三個框架被咱們熟知,Vue、React、Angular,三個常常常常被咱們拿來討論,對比,好比學習哪一個?php
在我入門的時候,第一個學習的就是Vue,基本跳過了後臺php、jsp結合JQuery,三個框架成爲了開發三選一的問題,在後面的工做和學習中,我逐漸意識到前框框架解決的核心問題在於數據和視圖同步,我也發現挺多人意識到了這一點,而且在向大衆普及這個前端須要解決的核心問題html
咱們說目前前端的核心問題是數據和視圖的同步,這裏我姑且採用了一個我的以爲更適合的名詞,數據到視圖的映射,那麼框架們是以怎麼樣的思路思考這個問題的呢,咱們這裏開展一個功能實現的討論,首先是JQuery實現,咱們須要更新輸入框值前端
// 當前用戶名的值
let curUsername = 'cj'
// 找到類名爲username的元素
let username = $('.username')
// 設置username爲cj
username.val(curUsername)
複製代碼
按照如下步驟,便可完成咱們的小需求java
定義修改的值node
找到須要修改的元素算法
修改元素的值爲咱們給定的值編程
問題性能優化
這裏找到元素、設置元素的值,明顯是很命令式的寫法,若是咱們須要屢次操做,一個簡單的方法就是屢次重複這段代碼前端框架
function updateUsername(username) {
let usernameInput = $('.username')
usernameInput.val(username)
}
let curUsername = 'cj'
updateUsername(curUsername)
複製代碼
從上述代碼能夠抽象出,咱們只須要關心改變的值和改變的元素,其餘細節是咱們不須要知道的,中間選取元素、修改元素在過程當中能夠認爲是徹底抽離成一個新的抽象,下面就是基於這一點的思考閉包
既然咱們關注的點在於目標元素、數據,這二者能夠在編譯時肯定,只要咱們更新這個數據,系統就更新對應引用這個數據的元素,從而將咱們從指定元素、修改元素的重複勞動中脫離出來,這是一個基本的思路,固然Vue採用的是上述的思路,而React採用的是暴力diff的方式
Vue綁定數據和節點在一塊兒,在數據更新時,更新對應的元素
React比較直接,直接對整個組件diff,找到先後不一樣的地方,內部根本不須要知道數據和節點的對應關係
Angular,接觸不多,目前好像是髒檢查和proxy結合
目前來講,Virtual DOM 和配套的diff算法,逐漸出如今你們面前,這裏咱們爲何要引入這兩套技術?,你們其實看的也多了,解決DOM更新緩慢,跨平臺等,那麼它引入的初心是什麼,這還得看React爲何要引入它
Virtual DOM是由React推出時引入的技術,我的理解是,React系統不知道數據對應的節點是哪些,因此按照常規方式來講,確定是要更新所有節點,React須要一種在操做節點以前用於性能優化的一套方案,這就是React引入Virtual DOM技術的初心
目前不少Vue響應式基本實現會像如下同樣:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id='app'>
<h3>姓名</h3>
<p>{{name}}</p>
<h3>年齡</h3>
<p>{{age}}</p>
</div>
</body>
</html>
<script>
document.addEventListener('DOMContentLoaded', function(){
let opt = {el:'#app', data:{name:'檢索中...', age:30}}
let vm = new Vue(opt)
setTimeout(() => {
opt.data.name = '王永峯'
}, 2000);
}, false)
class Vue{
constructor(opt){
this.opt = opt
this.observe(opt.data)
let root = document.querySelector(opt.el)
this.compile(root)
}
// 爲響應式對象 data 裏的每個 key 綁定一個觀察者對象
observe(data){
Object.keys(data).forEach(key => {
let obv = new Observer()
data["_"+key] = data[key]
// 經過 getter setter 暴露 for 循環中做用域下的 obv,閉包產生
Object.defineProperty(data, key, {
get(){
Observer.target && obv.addSubNode(Observer.target);
return data['_'+key]
},
set(newVal){
obv.update(newVal)
data['_'+key] = newVal
}
})
})
}
// 初始化頁面,遍歷 DOM,收集每個key變化時,隨之調整的位置,以觀察者方法存放起來
compile(node){
[].forEach.call(node.childNodes, child =>{
if(!child.firstElementChild && /\{\{(.*)\}\}/.test(child.innerHTML)){
let key = RegExp.$1.trim()
child.innerHTML = child.innerHTML.replace(new RegExp('\\{\\{\\s*'+ key +'\\s*\\}\\}', 'gm'),this.opt.data[key])
Observer.target = child
this.opt.data[key]
Observer.target = null
}
else if (child.firstElementChild)
this.compile(child)
})
}
}
// 常規觀察者類
class Observer{
constructor(){
this.subNode = []
}
addSubNode(node){
this.subNode.push(node)
}
update(newVal){
this.subNode.forEach(node=>{
node.innerHTML = newVal
})
}
}
</script>
複製代碼
上述代碼是Vue1的核心實現簡化,每一個數據知道引用本身的精確節點,Vue的實現是在每一個節點編譯,引用數據時都爲其生成一個Watcher,顯而易見,爲每個引用數據的節點都要生成一個Watcher,是有比較大的內存壓力,如何解決,很簡單,將粒度提高到組件級別,將低粒度的diff交給Virtual DOM
框架解決的核心是選擇節點,操做節點這一過程的抽象,將其隱藏在框架實現以內,咱們只須要關注數據和節點的對於關係便可,其他技術是在這個大背景下的優化。
數據驅動方案決定了其性能以及後續性能與優化方向,數據驅動是前端的一大潮流,已經成爲了框架必備特性了,由於從前面的討論得知,操做節點、修改節點已經能夠很好地進行抽象,優化。這裏簡單討論一下三個框架的數據驅動方案
Vue2的數據驅動仍是數據劫持和Virtual DOM方案結合,即保持了較爲精準的更新能力又保持了內存使用的低水平,Vue這種方式有着自然的性能優點,因此你們在對組件的認知上不只僅要知道組件複用才被抽取,其實,組件能夠進行良好的代碼組件,維持適當大小的組件規模,還能夠利用Vue2的數據驅動方案進行一個自然的性能優化,關於這一點具體的狀況,後面我會研究分離組件和總體的性能差別
Vue使用模板時,還能夠得到靜態優化的優點,在Vue3更加優化了模板編譯
React更新採用了Virtual DOM,解決了數據更新時,徹底替換的問題,實現了局部更新的可能,可是對於數據更新這一層薄弱,或者說,徹底不知道數據變了,須要咱們主動setState,這就產生了後續須要在生命週期交由開發者避免沒必要要的渲染優化,這裏還有一個重要的點,就是時間分片,React推出了Fiber,用於優化視圖更新時的性能問題,這裏多說一句,Vue3原本也實現了時間分片,考慮到收益不大,又移除了,這應該是自己Vue的數據驅動方案的優點形成時間分片的收益較低
因爲採用了JSX,無法提供足夠的信息在編譯時進行優化,可是能夠嘗試在JSX 編譯成 React.createElement 的整個過程進行優化,目前 FaceBook 推出了Prepack 用於這個過程的優化
angular9目前採用了髒檢測和proxy結合的方式,基本沒使用,不予評價
Angular支持模板,隨着V9版本發佈,ivy渲染引擎推出,用於優化,也算是一個彌補
實現數據驅動的方式有不少,Vue和Angular走得是類MVVM的形式,React實質上不能夠稱爲MVVM框架,就如它所說的,他就是一個視圖層框架,數據驅動的不一樣方式各有優劣,我的能力水平有限,也說不出誰好誰壞,可是能夠看出的是,每一個數據驅動的方式都有存在一點缺陷,框架自己也針對這些缺陷在優化
組件化除了數據驅動另一大前端趨勢,框架如何設計組件也是一個關注點
定義
Vue.component("my-component", {
props:['props1'],
template: ` <div @click="clickHandler"> <slot><slot> </div> `,
methods: {
clickHandler() {
this.$emit('click')
}
}
});
複製代碼
使用
<my-component @click="clickHandler" :props1="props1" data-x="dataX">
<template #default>slot</template>
</my-component>
複製代碼
Vue在組件上劃分了props和attrs,組件內部提供了props選項來捕獲特色的attr轉換爲props,其他會成爲attrs直接附加到組件根節點上,實質上,這是一種默認大於配置的想法,默認無關的屬性應該附加到根節點,可是總會有少數狀況,因此後續Vue提供了inheritAttrs來控制attr做用元素
Vue在組件以前的事件採用了發佈訂閱模式EventEmitter來實現,這個模式比起直接傳遞迴調函數,有如下好處
Vue提供了slot機制,用於提供擴展組件內部節點,值得一提的是,scopedSlot機制
定義
function Square(props) {
return (
<button onClick={props.onClick}> {props.props1} </button>
<div>props.slot<div>
);
}
複製代碼
使用
<Square value={} onClick={() => this.handlerClick(i) slot={}}/>
複製代碼
React沒有區分Props和attrs,只是將數據解析好,徹底交給開發者去控制
React採用了將父組件直接傳入子組件的方式,我的認爲像Vue那樣的EventEmitter對於開發者更加友好
React沒有爲slot提供特殊語法,能夠直接將slot以普通屬性的方式傳遞進來,剩下的交由組件內部處理
定義
@Component({
// 省略
})
export class ProductAlertsComponent implements OnInit {
// 定義數據入口
@Input() product;
// 定義外部事件
@Output() notify = new EventEmitter();
constructor() { }
}
複製代碼
<div>
<button @click="notify.emit()"></button>
<ng-content></ng-content>
</div>
複製代碼
使用
<component (notify)="onNotify()" [product]="product">
<div>slot</div>
</component>
複製代碼
Angular也只是提供了一個數據入口
Angular相似Vue同樣,使用了發佈訂閱模式來管理組件之間的事件
Angular提供了相似slot的機制來擴展組件節點能力
毫無疑問,組件化必定是將來的持續保持的前端框架特性,如何設計一個良好的組件,這是值得思考的,三個框架也有本身的思考,,這裏不予評價
這一點我想只是從個人認知來說,框架除去數據驅動和組件化設計以外,它對開發者帶來哪些特性,能力有限,這裏分析不是很深刻
Vue 是適應開發者,讓開發者怎麼爽怎麼來,Vue不少特性都是基於開發者角度去開發的,好比attr和props,Angular和React就不會這樣處理,徹底交給開發者去處理細節,因此就致使,Vue好像看起來沒啥框架上的特色,實質上,Vue將一些對開發者認知不友好的處理,內置到了框架以內
React 設計是改變開發者,提供強大而複雜的機制,固然這麼作,就會社區很是繁榮,後續也能夠吸取一些良好的範式,目前我也在學習React,不得不說,React更像是學院派,社區可以誕生不少不錯的Idea
Angular給我最深的印象是Service和DI的機制,它也爲開發者考慮了不少,不少庫直接用官方的就能夠了,徹底不須要考慮用哪一個,Angular的腳手架用起來仍是挺舒服的
Service將與視圖渲染無關的邏輯提取到單獨代碼中
DI解決了對象管理混亂的問題
更加貼近java那套OOP
從前端框架解決的問題出發,咱們反思了數據驅動、組件化等框架必備特性,脫去這兩點,剩餘的框架特性,推崇的編程範式,也是值得咱們品味的,好比Angular視圖渲染邏輯和數據分離,在React、Vue咱們也能夠借鑑,三大框架將來在保持數據驅動和組件化特性下,框架特性這一塊,極可能仍是要統一走一套,好比Vue3的hook和React的hook,這一點是對開發者友好的,學習、認知成本沒那麼高,有人會在這種文章上說,誰抄誰這種話,我認爲試試不妥的,但願你們可以以一個更高層的視覺看到目前前端的發展。