?“React 和 Vue 哪個更好?” 論壇上經澈Q看到這樣的問題,然后評論區(qū)就直接開戰(zhàn)了舱殿。也有朋友轉行做前端奥裸,問我該學React還是Vue。幾年前沪袭,可能確實有必要考慮下到底該選擇哪一個湾宙,畢竟前端圈子這么亂,誰又知道Vue能走多遠冈绊?React會不會不維護了呢侠鳄?可現在兩者生態(tài)都很不錯,Vue確實好用死宣,React學習成本也沒有傳聞中那么高伟恶,template很好用,jsx也更靈活毅该〔╋可以兩者都去玩玩,根據個人喜好和項目需要來選擇用哪個鹃骂。而如果能夠結合兩者的優(yōu)點台盯,那豈不是很有趣?
?我剛轉前端的時候畏线,用的是vue版本好像還是1.0静盅,那時候的感覺就是數據綁定比jquery操作dom省事兒太多了。后來又接觸了React寝殴,之后的項目大部分用React寫的蒿叠,現在偶爾也用vue,總體感覺就是vue單文件組件結構比較清晰蚣常,模板指令也很好用市咽,而jsx更加靈活,之前有在react狀態(tài)管理部分做一些嘗試抵蚊,可以像普通function一樣去更新狀態(tài)施绎,也一直想在jsx中加上類似vue里面的模板指令溯革,直到前幾天比較閑,總算實現了 “條件渲染”和 “列表渲染”谷醉,結果還算不錯致稀,過程也挺有趣。
?先來看看結果吧俱尼,以前要根據不同狀態(tài)來控制模塊是否顯示抖单,我們大概要寫這樣的代碼:
render(){
const visible = true
return(
<div>
{
visible ? <div>content<div>
: ''
}
</div>
)
}
?現在可以這么玩:
render(){
const visible = true
return(
<div>
<div r-if = {visible}>content</div>
</div>
)
}
?另一種常見的場景就是根據一個數組來渲染出一個列表,一般是這么寫:
render(){
const list = [1, 2, 3, 4, 5]
return(
<div>
{
list.map((item,index)=>(
<div key={index}>{item}</div>
))
}
</div>
)
}
?現在可以更簡潔:
render(){
const list = [1, 2, 3, 4, 5]
return(
<div>
<div r-for = {item in list}>{item}</div>
</div>
)
}
?以上代碼會自動設置key遇八,值為當前元素的索引矛绘。如果你想要自定義key,也可以加上刃永,改成
<div r-for = {(item,index) in list} key = {index+1}>{item}</div>
?結果還算不錯吧货矮,代碼更簡短,語義也比較明確斯够,體驗也不必vue里面的模板指令差次屠,個人感覺在“”里面寫js有點奇怪。而在{}里面寫就很自然雳刺,就是普通的js代碼塊嘛劫灶。
?至于實現方法嘛,其實很簡單掖桦,總共才幾十行代碼本昏,就是寫了一個babel插件。
?我們寫的jsx也是通過babel轉譯成普通js代碼的枪汪,然后才能在瀏覽器中運行涌穆,而babel編譯主要分為三個階段:解析、轉換雀久、生成目標代碼宿稀。解析部分就是將源代碼解析成抽象語法樹ast,轉換過程是對ast做一些處理赖捌,而生成目標代碼部分就是將ast再轉換成js代碼祝沸。babel-plugin就是在轉換部分做一些工作。
?比如對于以下jsx:
<div r-if = { visible }>{content}</div>
?轉換成的ast結構大概是:
{
type: 'CallExpression',
callee: {},
arguments: {
properties: []
}
}
?目標代碼為:
React.createElement(
'div',
{'r-if': visible},
content
)
?React.createElement()方法調用對應ast中的CallExpression, React和createElement在callee中可以找到越庇,可以以此來找出createElement(), r-if 等屬性以及第三個參數content在arguments的properties數組中能找到罩锐。有了這些信息,我們就可以遍歷ast卤唉,找到那些callee為React.createElement的CallExpression, 然后判斷arguments中如果出現了r-if, 就對ast做以下修改:首先移除r-if屬性涩惑,避免死循環(huán);然后在CallExpression對應的節(jié)點外面再套一層ifStatement, 如此一來桑驱,轉換后的ast生成的目標代碼大致如下:
if(visible){
React.createElement(
'div',
{'r-if': visible},
content
)
}
?至此竭恬,我們的目標就已經達到了跛蛋,至于r-for列表渲染,原理類似痊硕,先找出有r-for屬性的CallExpression, 然后構造一個map方法對應的CallExpression, 當前CallExpression作為參數傳給map方法即可问芬。需要注意的是,在同一次遍歷中解析出 r-if 和 r-for 的話寿桨,需要把map放在外層,ifStatement放在里面强戴,如果覺得這樣做結構比較混亂亭螟,可以拆分成不同的插件。
?最后再說一下babel-plugin的寫法骑歹,其實也就是一個方法:
module.exports = function ({ types: t }) {
return {
visitor: {
CallExpression(path) {
// 在這里通過修改path來修改ast预烙。
},
Identifier(path) {
}
}
}
}
?types類型為babel-types, 提供了一些類似loadash的操作方法,比如做一些判斷道媚、構造節(jié)點扁掸。visitor里面寫對應類型節(jié)點的遍歷方法, 比如遍歷標識符類型的就寫在Identifier中,方法調用就寫在CallExpression中最域。
?本文中提到的r-if 和 r-for 已經寫成了一個插件谴分,可以在github倉庫中找到:https://github.com/evolify/babel-plugin-react-directive 同時也發(fā)布到了npm倉庫,可以直接安裝:
yarn add --dev babel-plugin-react-directive
?然后在.babelrc中配置即可:
{
"plugins": [
"react-directive"
]
}
?我想要的目的已經達到了镀脂,但這并未結束牺蹄,才剛剛開始,還可以實現其他的一些指令薄翅,比如r-if 是模塊渲染或者不渲染的沙兰,我們經常也會遇到這種需求:只是單純的控制元素可見或者不可見,但元素還是占用空間的翘魄,也就是控制visibility, 這也可以寫成一個指令鼎天。而babel能做的遠遠不止如此,無聊的時候可以好好玩一玩暑竟。