React騷操作——jsx遇到template-directive

?“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能做的遠遠不止如此,無聊的時候可以好好玩一玩暑竟。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末斋射,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子但荤,更是在濱河造成了極大的恐慌绩鸣,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纱兑,死亡現場離奇詭異呀闻,居然都是意外死亡,警方通過查閱死者的電腦和手機潜慎,發(fā)現死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門捡多,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蓖康,“玉大人,你說我怎么就攤上這事垒手∷夂福” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵科贬,是天一觀的道長泳梆。 經常有香客問我,道長榜掌,這世上最難降的妖魔是什么优妙? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮憎账,結果婚禮上套硼,老公的妹妹穿的比我還像新娘。我一直安慰自己胞皱,他們只是感情好邪意,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著反砌,像睡著了一般雾鬼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上宴树,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天呆贿,我揣著相機與錄音,去河邊找鬼森渐。 笑死做入,一個胖子當著我的面吹牛,可吹牛的內容都是我干的同衣。 我是一名探鬼主播竟块,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼耐齐!你這毒婦竟也來了浪秘?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤埠况,失蹤者是張志新(化名)和其女友劉穎耸携,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體辕翰,經...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡夺衍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了喜命。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沟沙。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡河劝,死狀恐怖,靈堂內的尸體忽然破棺而出矛紫,到底是詐尸還是另有隱情赎瞎,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布颊咬,位于F島的核電站务甥,受9級特大地震影響,放射性物質發(fā)生泄漏喳篇。R本人自食惡果不足惜敞临,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杭隙。 院中可真熱鬧,春花似錦因妙、人聲如沸痰憎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铣耘。三九已至,卻和暖如春以故,著一層夾襖步出監(jiān)牢的瞬間蜗细,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工怒详, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炉媒,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓昆烁,卻偏偏與公主長得像吊骤,于是被迫代替她去往敵國和親跑芳。 傳聞我的和親對象是個殘疾皇子懦铺,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內容