<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          swc 替代 babel 最佳實踐

          共 4509字,需瀏覽 10分鐘

           ·

          2021-09-08 07:41

          • 原文作者:fre 作者 - 伊撒爾
          • 原文鏈接:https://zhuanlan.zhihu.com/p/395608876

          最近在工作中使用 swc 替代 babel,總結(jié)了一些代碼的最佳實踐,寫一篇文章記錄一下

          最佳實踐一,在 rust 端操作 AST

          如果需要操作 AST,一般來說有兩種可能,一是在 rust 端解析出 AST,然后序列化并傳遞到 node 端,node 端再反序列化修改 AST,再序列化傳回去……

          這樣性能會超級差,因為需要頻繁序列化反序列化一個復(fù)雜的結(jié)構(gòu)

          所以最佳實踐一,在 rust 端操作 AST,生成一個“最小簡單結(jié)構(gòu)”,然后傳到 node 端,避開序列化

          最佳實踐二,使用模式展開

          swc 的結(jié)構(gòu)實在太大了……主要是枚舉太多,如果使用 match 一層層匹配,會嵌套太深,這種時候,有人會使用 if let 來簡化代碼,但這不是不夠的

          pub struct Resolver {
              imported: THashSet,
          }

          impl Visit for Resolver {
              fn visit_jsx_opening_element(&mut self, node: &JSXOpeningElement, _parent: &dyn Node) {
                  if let JSXElementName::Ident(id) = node.name {
                      let name = id.sym.to_string();
                      if name == String::from("import") {
                          for attr in &node.attrs {
                              if let JSXAttrOrSpread::JSXAttr(v) = attr {
                                  if let Some(JSXAttrValue::Lit(Lit::Str(v))) = &v.value {
                                      self.imported.insert(v.value.to_string());
                                  }
                              }
                          }
                      }
                  }
              }
          }
          這是一個收集<import/>依賴的 代碼示例,可以看到,為了兼顧類型,代碼寫的并不優(yōu)雅

          這里面用到了一個模式展開的技巧來簡化代碼

          if let Some(JSXAttrValue::Lit(Lit::Str(v))) = &v.value

          可以說在遍歷 swc 的 AST 的時候,這種技巧得多用,不然代碼會很臃腫

          這是 swc 的類型,枚舉,樹太深導(dǎo)致的,比如我們需要刪除一個節(jié)點的時候,一般來講,我們會把節(jié)點類型設(shè)置為 Option,刪除就是賦值為 None

          struct  Tree {
              node: Option<Box<Tree>>,
          }

          impl Tree {
              fn delete(&mut self){
                  self.node = None;
              }
          }

          可是 swc 沒有!它沒給 Option 類型,這就導(dǎo)致刪除個節(jié)點難于上青天……

          最佳實踐三,生成新樹,而不是修改原先的樹

          因為你改不了呀,啊哈哈哈

          rust 的類型是很嚴(yán)格的,沒有 any,沒有聯(lián)合類型,也就是說,原先的樹是什么類型,你就得改個完全相同的類型

          這可咋整,babal 做的東西一般都是扭曲語義的呢

          // input:
          <For each={list}/>
          // output:
          {list.map()} // this is a Stmt, not Expr.

          我研究了許久,換了很多不同的方案,看了 swc 官方的源碼

          總結(jié)就是,我們盡可能不要修改原先的樹,而是嘗試遞歸生成新的樹,當(dāng)然不一定是一整顆樹,可以是子樹,借助 swc 的 visit_mut 或者 fold

          最佳實踐三,swc 只做標(biāo)記

          這是一個大坑,因為 swc 的結(jié)構(gòu)和類型無法改變,所以我們不得不在原有類型上做標(biāo)記,然后最后一次性做字符串級別的轉(zhuǎn)換

          舉例子:

          <view wx:key=\"111\"></view>

          我們需要將 wx:key 替換為 key,但是前者的類型是 NamespacedName 后者是 AttrName,沒有辦法直接替換

          于是我們不改變原有類型,只改變字符串,將 wx:key改為 $removemeandcolon:key

          然后我們在打包階段就可以做字符串級別的替換了

          code.replace(/removemeandcolon:/,'')

          這也是沒有辦法的事情,我也很絕望

          最佳實踐四,忽略 span 類型

          module.visit_with(&ast::Invalid { span: DUMMY_SP } as _, &mut c);

          這樣會避免一些 panic 錯誤

          最佳實踐五,兩階段遍歷

          比如我現(xiàn)在想要下面的 case

          // input
          <a-component>
            <view slot=header>header</view>
          </a-component>

          // output
          <AComponent $slot$header={<view>header</view>}>
          </AComponent>

          這是一個 slot 轉(zhuǎn)換為 render props 的 case,在 babel 里,我遍歷到 slot 的時候,直接往 parent 引用上塞一個 props 就可以了

          但 swc 就做不到這個,因為它沒有 parent 指針,就算有,也不可變

          所以我重新設(shè)計了整體的遍歷,分兩個階段,第一階段遍歷收集 slot,第二階段通過 mut_visitor 修改 parent

          總結(jié)

          我們看到,如果是修改語義的 AST 轉(zhuǎn)換,實際上在 swc 中改起來是很困難的,因為結(jié)構(gòu)是完全不同的,rust 不像 typescript,還有 any,有聯(lián)合類型,有函數(shù)重載

          rust 的類型是很死板的,所有權(quán)也很嚴(yán)格,不允許寫編譯器不認(rèn)的代碼

          所以不得已,只能適應(yīng)它,然后換用其他方式去設(shè)計遍歷,比如上面提到的,遍歷舊樹,生成新樹,兩階段遍歷等等

          當(dāng)然,大多數(shù)的轉(zhuǎn)譯器不涉及到語義的修改,比如 JSX => h 這種,就不會這么絕望了

          其實押 swc 我有點激進(jìn)了,但實在沒有更好的方案,我還在繼續(xù)寫


          瀏覽 71
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲熟女 综合 | 天天操夜夜操xxxxxx | 亚洲成人综合在线 | 亚洲欧洲AV在线 | 精品欧美无人区乱码毛片 |