偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

盤點(diǎn) Solid.js 源碼中的那些迷惑行為

開發(fā) 前端
看過(guò)源碼之后感覺(jué)有的地方設(shè)計(jì)的很巧妙,但有些地方又不是很嚴(yán)謹(jǐn)。也怪 jsx? 太靈活了,不可能做判斷把所有情況都做到面面俱到,當(dāng)你要寫一些在 ?React? 里能運(yùn)行的騷操作可能在 ?Solid? 里就啞火了。?

前言

我研究 Solid.js 源碼已經(jīng)有一段時(shí)間了,在鉆研的過(guò)程中我發(fā)現(xiàn)了其中的一些迷惑行為,在搞懂之后終于恍然大悟,忍不住想要分享給大家。不過(guò)這么說(shuō)其實(shí)也不太準(zhǔn)確,因?yàn)樵趪?yán)格意義上來(lái)講 Solid.js 其實(shí)是被劃分為了兩個(gè)部分的。我只認(rèn)真鉆研了其中一個(gè)部分,所以也不能說(shuō)鉆研 Solid.js 源碼,因?yàn)榱硗庖粋€(gè)部分壓根就不叫 Solid。

兩部分

有些同學(xué)看到這可能就會(huì)感到疑惑了,哪兩個(gè)部分?Solid、.js?其實(shí)是這樣:大家應(yīng)該都聽(tīng)說(shuō)過(guò) Solid.js 是一個(gè)重編譯、輕運(yùn)行的框架吧,所以它可以被分為編譯器和運(yùn)行時(shí)兩個(gè)部分。

那有人可能會(huì)問(wèn):你要是這么說(shuō)的話那豈不是 Vue 也可以被分為兩部分,畢竟 Vue 也有編譯器和運(yùn)行時(shí),為什么從來(lái)沒(méi)有人說(shuō)過(guò) Vue 是兩部分組成的呢?是這樣,Vue 的編譯器和運(yùn)行時(shí)全都放在了同一倉(cāng)庫(kù)內(nèi)的 Monorepo 中:

圖片

圖片

你可以說(shuō) Vue2 和 Vue3 是兩個(gè)部分,因?yàn)樗鼈z被放在了兩個(gè)不同的倉(cāng)庫(kù)中:

圖片

雖然它倆已經(jīng)是兩個(gè)不同的倉(cāng)庫(kù)了,但好歹也都是 vuejs 名下的吧:

圖片

而 Solid.js 的兩部分不僅不在同一個(gè)倉(cāng)庫(kù)內(nèi),甚至連組織名都不一樣:

圖片

圖片

一個(gè)是 solidjs/solid:

圖片

而另一個(gè)則是 ryansolid/dom-expressions:

圖片

ryan 是 Solid.js 作者的名字,所以 ryan + solid = ryansolid(有點(diǎn)迷,為啥不放在 solidjs 旗下非要單獨(dú)開一個(gè) ryansolid)

這個(gè) dom-expressions 就是 Solid.js 的編譯器,那為啥不像 Vue 編譯器似的都放在同一個(gè)倉(cāng)庫(kù)內(nèi)呢?因?yàn)?nbsp;Vue 的編譯器就是專門為 Vue 設(shè)計(jì)的,你啥時(shí)候看非 Vue項(xiàng)目中使用 xxx.vue 這樣的寫法過(guò)?

.vue 這種單文件組件就只有 Vue 使用,雖說(shuō)其他框架也有單文件組件的概念并且有著類似的寫法(如:xxx.svelte)但人家 Svelte也不會(huì)去用 Vue 的編譯器去編譯人家的 Svelte 組件。不過(guò) Solid 不一樣,Solid沒(méi)自創(chuàng)一個(gè) xxx.solid,而是明智的選擇了 xxx.jsx。

SFC VS JSX

單文件組件和 jsx 各有利弊,不能說(shuō)哪一方就一定比另一方更好。但對(duì)于一個(gè)聲明式框架作者而言,選擇單文件組件的好處是可以自定義各種語(yǔ)法,并且還可以犧牲一定的靈活性來(lái)?yè)Q取更優(yōu)的編譯策略。缺點(diǎn)就是成本太高了,單單語(yǔ)法高亮和 TS 支持度這一方面就得寫一個(gè)非常復(fù)雜的插件才能填平。

好在 Vue 的單文件組件插件 Volar 已經(jīng)可以支持自定義自己的單文件組件插件了,這有效的降低了框架作者的開發(fā)成本。但 Solid 剛開始的時(shí)候還沒(méi)有 Volar 呢(可以去看看 Volar 的源碼有多復(fù)雜 這還僅僅只是一個(gè)插件就需要花費(fèi)那么多時(shí)間和精力),甚至直到現(xiàn)在 Volar 也沒(méi)個(gè)文檔,就只有 Vue 那幫人在用 Volar(畢竟是他們自己研究的):

圖片

并且人家選擇 jsx 也有可能并非是為了降低開發(fā)成本,而是單純的鐘意于 jsx 語(yǔ)法而已。那么為什么選擇 jsx 會(huì)降低開發(fā)成本呢?首先就是不用自己寫 parser、generator 等一堆編譯相關(guān)的東西了,一個(gè) babel 插件就能識(shí)別 jsx 語(yǔ)法。語(yǔ)法高亮、TS 支持度這方面更是不用操心,甚至用戶都不需要為編輯器安裝任何插件(何時(shí)聽(tīng)過(guò) jsx 插件)。

并且由于 React 是全球占有率最高的框架,jsx 已被廣泛接受(甚至連 Vue 都支持 jsx)但如果選擇單文件組件的話又會(huì)產(chǎn)生有人喜歡這種寫法有人喜歡那種寫法的問(wèn)題,比方說(shuō)同樣使用 sfc 的 Vue 和 Svelte,if-else 寫法分別是這樣:

<template>
  <h1 v-if="xxx" />
  <div v-else />
</template>
{#if xxx}
  <h1 />
{:else}
  <div />
{/if}

有人喜歡上面那種寫法就有人喜歡下面那種寫法,眾口難調(diào),無(wú)論選擇哪種寫法可能都會(huì)導(dǎo)致另一部分的用戶失望。而 jsx 就靈活的多了,if-else 想寫成什么樣都可以根據(jù)自己的喜好來(lái):

if (xxx) {
  return <h1 />
} else {
  return <div />
}
// 或者
return xxx ? <h1 /> : <div />
// 亦或
let Title = 'h1'
if (xxx) Title = 'div'
return <Title />

jsx 最大程度的融合了 js,正是因?yàn)樗鼘?duì) js 良好的兼容性才導(dǎo)致它的適用范圍更廣,而不是像 Vue、Svelte 那樣只適用于自己的框架。

畢竟每種模板語(yǔ)言的 if-else、循環(huán)等功能寫法都不太一樣,當(dāng)然 jsx 里的 if-else 也可以有各種千奇百怪的寫法,但畢竟還是 js 寫法,而不是自創(chuàng)的 ng-if、v-else、{:else if} {% for i in xxx %}等各種不互通的寫法。

正是由于 jsx 的這個(gè)優(yōu)勢(shì)導(dǎo)致了很多非 React 框架(如:Preact、Stancil、Solid 等)用 jsx 也照樣用的飛起,那么既然 jsx 可以不跟 React 綁定,那 Ryan 自創(chuàng)的 jsx編譯策略也同樣可以不跟 Solid 綁定啊對(duì)不對(duì)?

這是一款可以和 Solid.js 搭配使用的 babel 插件,也同樣是一款可以和 MobX、和 Knockout、和 S.js、甚至和 Rx.js 搭配使用的插件,只要你有一款響應(yīng)式系統(tǒng),那么 dom-expressions 就可以為你提供 jsx 服務(wù)。

Solid.js

所以這才是 Ryan 沒(méi)把 dom-expressions 放在 solidjs/solid 里的重要原因之一,但 Solid.js 又是一個(gè)注重編譯的框架,沒(méi)了 dom-expressions 還不行,所以只能說(shuō) Solid.js 是由兩部分組成的。

DOM Expressions

DOM Expressions 翻譯過(guò)來(lái)就是 DOM 表達(dá)式的意思,有人可能會(huì)問(wèn)那你標(biāo)題為啥不寫成《盤點(diǎn) DOM Expressions 源碼中的那些迷惑行為》?拜托!誰(shuí)知道 DOM Expressions 到底是個(gè)什么鬼!

如果不是我苦口婆心的說(shuō)了這么多,有幾個(gè)能知道這玩意就是 Solid.js 的編譯器,甭說(shuō)國(guó)內(nèi)了,就連國(guó)外都沒(méi)幾個(gè)知道 DOM Expressions的。你要說(shuō) Solid.js 那別人可能會(huì)豎起大拇指說(shuō)聲 Excellent,但你要說(shuō) DOM Expressions 那別人說(shuō)的很可能就是 What the fuck is that? 了。不信你看它倆的??對(duì)比:

圖片

圖片

再來(lái)看看 Ryan 在油管上親自直播 DOM Expressions時(shí)的慘淡數(shù)據(jù):

圖片

這都沒(méi)我隨便寫篇文章的點(diǎn)贊量高,信不信如果我把標(biāo)題中的 Solid.js 換成了 DOM Expression 的話點(diǎn)贊量都不會(huì)有 Ryan 直播的數(shù)據(jù)好?好歹人家還是 Solid的作者,都只能獲得如此慘淡的數(shù)據(jù),那更別提我了。

言歸正傳,為了防止大家不知道 Solid.js 編譯后的產(chǎn)物與 React 編譯后的產(chǎn)物有何不同,我們先來(lái)寫一段簡(jiǎn)單的 jsx:

import c from 'c'
import xxx from 'xxx'

export function Component () {
  return (
    <div a="1" b={2} c={c} onClick={() => {}}>
      { 1 + 2 }
      { xxx }
    </div>
  )
}

React 編譯產(chǎn)物:

import c from 'c';
import xxx from 'xxx';
import { jsxs as _jsxs } from "react/jsx-runtime";
export function Component() {
  return /*#__PURE__*/_jsxs("div", {
    a: "1",
    b: 2,
    c: c,
    onClick: () => {},
    children: [1 + 2, xxx]
  });
}

Solid 編譯產(chǎn)物:

import { template as _$template } from "solid-js/web";
import { delegateEvents as _$delegateEvents } from "solid-js/web";
import { insert as _$insert } from "solid-js/web";
import { setAttribute as _$setAttribute } from "solid-js/web";
const _tmpl$ = /*#__PURE__*/_$template(`<div a="1" b="2">3`);
import c from 'c';
import xxx from 'xxx';
export function Component() {
  return (() => {
    const _el$ = _tmpl$(),
      _el$2 = _el$.firstChild;
    _el$.$$click = () => {};
    _$setAttribute(_el$, "c", c);
    _$insert(_el$, xxx, null);
    return _el$;
  })();
}
_$delegateEvents(["click"]);

Solid 編譯后的產(chǎn)物乍一看有點(diǎn)不太易讀,我來(lái)給大家寫一段偽代碼,用來(lái)幫助大家快速理解 Solid 到底把那段 jsx 編譯成了啥:

import c from 'c';
import xxx from 'xxx';

const template = doucment.createElement('template')
template.innerHTML = '<div a="1" b="2">3</div>'
const el = template.content.firstChild.cloneNode(true) // 大家可以簡(jiǎn)單的理解為 el 就是 <div a="1" b="2">3</div>

export function Component() {
  return (() => {
    el.onclick = () => {};
    el.setAttribute("c", c);
    el.insertBefore(xxx);
    return el;
  })();
}

這樣看上去就清晰多了吧?直接編譯成了真實(shí)的 DOM 操作,這也是它性能為何能夠如此強(qiáng)悍的原因之一,沒(méi)有中間商(虛擬DOM)賺差價(jià)。但大家有沒(méi)有感覺(jué)有個(gè)地方看起來(lái)好像有點(diǎn)多此一舉,就是那個(gè)自執(zhí)行函數(shù):

export function Component() {
  return (() => {
    el.onclick = () => {};
    el.setAttribute("c", c);
    el.insertBefore(xxx);
    return el;
  })();
}

為何不直接編譯成這樣:

export function Component() {
  el.onclick = () => {};
  el.setAttribute("c", c);
  el.insertBefore(xxx);
  return el;
}

效果其實(shí)都是一樣的,不信你試著運(yùn)行下面這段代碼:

let num = 1
console.log(num) // 1

num = (() => {
  return 1
})()
console.log(num) // 還是 1 但感覺(jué)多了一個(gè)脫褲子放屁的步驟

看了源碼才知道,原來(lái)看似多此一舉的舉動(dòng)實(shí)則是有苦衷的。因?yàn)槲覀冞@是典型的站在上帝視角來(lái)審視編譯后的代碼,源碼的做法是只對(duì) jsx 進(jìn)行遍歷,在剛剛那種情況下所編譯出來(lái)的代碼確實(shí)不是最優(yōu)解,但它能保證在各種的場(chǎng)景下都能正常運(yùn)行。

我們來(lái)寫一段比較罕見(jiàn)的代碼大家就能明白過(guò)來(lái)怎么回事了:

if (<div a={value} onClick={() => {}} />) {
  // do something…
}

當(dāng)然這么寫沒(méi)有任何的意義,這是為了幫助大家理解為何 Solid 要把它的 jsx 編譯成一段自執(zhí)行函數(shù)才會(huì)寫成這樣的。我們來(lái)寫一段偽代碼,實(shí)際上 Solid 編譯出來(lái)的并不是這樣的代碼,但相信大家能夠明白其中的含義:

<div a={value} notallow={() => {}} />
// 將會(huì)被編譯成
const el = document.createElement('div')
el.setAttribute('a', value)
el.onclick = () => {}

發(fā)現(xiàn)問(wèn)題所在了么?原本 jsx 只有一行代碼,但編譯過(guò)后卻變成三行了。所以如果不加一個(gè)自執(zhí)行函數(shù)的話將會(huì)變成:

if (const el = document.createElement('div'); el.setAttribute('a', value); el.onclick = () => {}) {
  // do something…
}

這很明顯是錯(cuò)誤的語(yǔ)法,if 括號(hào)里根本不能寫成這樣,會(huì)報(bào)錯(cuò)的!但如果把 if 括號(hào)里的代碼放在自執(zhí)行函數(shù)中那就沒(méi)問(wèn)題了:

if ((() => {
  const el = document.createElement('div')
  el.setAttribute('a', value)
  el.onclick = () => {}
  return el
})()) {
  // do something…
}

我知道肯定有人會(huì)說(shuō)把那三行代碼提出去不就得了么:

const el = document.createElement('div')
el.setAttribute('a', value)
el.onclick = () => {}
if (el) {
  // do something…
}

還記得我之前說(shuō)過(guò)的那句:我們是站在上帝視角來(lái)審判 Solid 編譯后代碼的么?理論上來(lái)說(shuō)這么做確實(shí)可以,但編譯成本無(wú)疑會(huì)高上許多,因?yàn)檫€要判斷 jsx 到底寫在了哪里,根據(jù)上下文的不同來(lái)生成不同的代碼,但這樣肯定沒(méi)有只編譯 jsx 而不管 jsx 到底是被寫在了哪里來(lái)的方便。而且我們上述的那種方式也不是百分百?zèng)]問(wèn)題的,照樣還是會(huì)有一些意想不到的場(chǎng)景:

for (let i = 0, j; j = <div a={i} />, i < 3; i++) {
  console.log(j)
}

但假如按照我們那種策略來(lái)編譯代碼的話:

const el = document.createElement('div')
el.setAttribute('a', i)
for (let i = 0, j; j = el, i < 3; i++) {
  console.log(j)
}

此時(shí)就會(huì)出現(xiàn)問(wèn)題,因?yàn)?nbsp;el 用到了變量 i,而 el 又被提到外面去了所以訪問(wèn)不到 i變量,所以 el 這幾行代碼必須要在 jsx 的原位置上才行,只有自執(zhí)行函數(shù)能夠做到這一點(diǎn)。由于 js 是一門極其靈活的語(yǔ)言,各種騷操作數(shù)不勝數(shù),所以把編譯后的代碼全都加上一段自執(zhí)行函數(shù)才是性價(jià)比最高并且最省事的選擇之一。

迷之嘆號(hào)??

有次在用 playground.solidjs.com 編譯 jsx 時(shí)驚奇的發(fā)現(xiàn):

圖片

不知大家看到這段 <h1>Hello, <!>!</h1> 時(shí)是什么感受,反正我的第一感覺(jué)就是出 bug 了,把我的嘆號(hào) ! 給編譯成 <!> 了。

但令人摸不著頭腦的是,這段代碼完全可以正常運(yùn)行,沒(méi)有出現(xiàn)任何的 bug。隨著測(cè)試的深入,發(fā)現(xiàn)其實(shí)并不是把我的嘆號(hào) ! 給編譯成 <!> 了,只是恰巧在那個(gè)位置上我寫了個(gè)嘆號(hào),就算不寫嘆號(hào)也照樣會(huì)有這個(gè) <!>的:

圖片

發(fā)現(xiàn)沒(méi)?<!> 出現(xiàn)的位置恰巧就是 {xxx} 的位置,我們?cè)谡{(diào)試的時(shí)候發(fā)現(xiàn)最終生成的代碼其實(shí)是這樣:

<h1>1<!---->2</h1>

也就是說(shuō)當(dāng)我們 .innerHTML = '<!>' 的時(shí)候其實(shí)就相當(dāng)于 .innerHTML = '' 了,很多人看到這個(gè)空注釋節(jié)點(diǎn)以后肯定會(huì)聯(lián)想到 Vue,當(dāng)我們?cè)?nbsp;Vue 中使用 v-if="false" 時(shí),按理說(shuō)這個(gè)節(jié)點(diǎn)就已經(jīng)不復(fù)存在了。但每當(dāng)我們打開控制臺(tái)時(shí)就會(huì)看到原本 v-if 的那個(gè)位置變成了這樣:

圖片

尤雨溪為何要留下一個(gè)看似沒(méi)有任何意義的空注釋節(jié)點(diǎn)呢?廣大強(qiáng)迫癥小伙伴們?nèi)滩涣肆?,趕忙去 GitHub 里開個(gè) issue 問(wèn)尤雨溪:

圖片

尤雨溪給出的答案是這樣:

圖片

那 Solid 加一個(gè)這玩意也是和 Vue 一樣的原由么?隨著對(duì)源碼的深入,我發(fā)現(xiàn)它跟 Vue 的  原由并不一樣,我們?cè)賮?lái)用一段偽代碼來(lái)幫助大家理解 Solid 為什么需要一段空注釋節(jié)點(diǎn):

<h1>1{xxx}2</h1>
// 將會(huì)被編譯成:
const el = template('<h1>12</h1>')
const el1 = el.firstChild  // 1
const el2 = el1.nextSibling  // 
const el3 = el2.nextSibling  // 2

// 在空節(jié)點(diǎn)之前插入 xxx 而空節(jié)點(diǎn)恰好就在 1 2 之間 所以就相當(dāng)于在 1 2 之間插入了 xxx
el.insertBefore(xxx, el2)

看懂了么,Solid 需要在 1 和 2 之間插入 xxx,如果不加這個(gè)空節(jié)點(diǎn)的話那就找不到該往哪插了:

<h1>1{xxx}2</h1>
// 假如編譯成沒(méi)有空節(jié)點(diǎn)的樣子:
const el = template('<h1>12</h1>')
const el1 = el1.firstChild  // 12
const el2 = el2.nextSibling  // 沒(méi)有兄弟節(jié)點(diǎn)了 只有一個(gè)子節(jié)點(diǎn):12

el.insertBefore(xxx, 特么的往哪插?)

所以當(dāng)大家在 playground.solidjs.com 中發(fā)現(xiàn)有 <!> 這種奇怪符號(hào)時(shí),請(qǐng)不要覺(jué)得這是個(gè) bug,這是為了留個(gè)占位符,方便 Solid 找到插入點(diǎn)。只不過(guò)大多數(shù)人都想不到,把這個(gè) <!> 賦值給 innerHTML 后會(huì)在頁(yè)面上生成一個(gè) <!---->。

迷之 ref

無(wú)論是 Vue 還是 React 都是用 ref 來(lái)獲取 DOM 的,Solid 的整體 API 設(shè)計(jì)的與 React 較為相似,ref 自然也不例外:

圖片

但它也有自己的小創(chuàng)新,就是 ref 既可以傳函數(shù)也可以傳普通變量。如果是函數(shù)的話就把 DOM 傳進(jìn)去,如果是普通變量的話就直接賦值:

// 偽代碼
<h1 ref={title} />
// 將會(huì)編譯成:
const el = document.createElement('h1')
typeof title === 'function'
  ? title(el)
  : title = el

但在查看源碼時(shí)發(fā)現(xiàn)了一個(gè)未被覆蓋到的情況:

// 簡(jiǎn)化后的源碼
transformAttributes () {
  if (key === "ref") {
    let binding,
        isFunction =
      t.isIdentifier(value.expression) &&
      (binding = path.scope.getBinding(value.expression.name)) &&
      binding.kind === "const";

    if (!isFunction && t.isLVal(value.expression)) {
      ...
    } else if (isFunction || t.isFunction(value.expression)) {
      ...
    } else if (t.isCallExpression(value.expression)) {
      ...
    }
  }
}

稍微給大家解釋一下,這個(gè) transformAttributes 是用來(lái)編譯 jsx 上的屬性的:

圖片

當(dāng) key 等于 ref 時(shí)需要進(jìn)行一些特殊處理,非常迷的一個(gè)命名就是這個(gè) isFunction,看名字大家肯定會(huì)認(rèn)為這個(gè)變量代表的是屬性值是否為函數(shù)。我來(lái)用人話給大家翻譯一下這個(gè)變量賦的值代表什么含義:t.isIdentifier(value.expression)的意思是這個(gè) value 是否為變量名:

圖片

比方說(shuō) ref={a} 中的 a 就是個(gè)變量名,但如果是 ref={1}、ref={() => {}}那就不是變量名,剩下那倆條件是判斷這個(gè)變量名是否是 const 聲明的。也就是說(shuō):

const isFunction = value 是個(gè)變量名 && 是用 const 聲明的

這特么就能代表 value 是個(gè) function 了?

在我眼里看來(lái)這個(gè)變量叫 isConst 還差不多,我們?cè)賮?lái)梳理一下這段邏輯:

// 簡(jiǎn)化后的源碼
transformAttributes () {
  if (key === "ref") {
    const isConst = value is 常量

    if (!isConst && t.isLVal(value.expression)) {
      ...
    } else if (isConst || t.isFunction(value.expression)) {
      ...
    } else if (t.isCallExpression(value.expression)) {
      ...
    }
  }
}

接下來(lái)就是 if-else 條件判斷里的條件了,再來(lái)翻譯下,t.isLVal 代表的是:value 是否可以放在等號(hào)左側(cè),這是什么意思呢?一個(gè)例子就能讓大家明白:

// 此時(shí) key = 'ref'、value = () => {}
<h1 ref={() => {}} />

// 現(xiàn)在我們需要寫一個(gè)等號(hào) 看看 value 能不能放在等號(hào)的左側(cè):
() => {} = xxx // 很明顯這是錯(cuò)誤的語(yǔ)法 所以 t.isLVal(value.expression) 是 false

// 但假如寫成這樣:
<h1 ref={a.b.c} />

a.b.c = xxx // 這是正確的語(yǔ)法 所以 t.isLVal(value.expression) 現(xiàn)在為 true

明白了 t.isLVal 接下來(lái)就是 t.isFunction 了,這個(gè)從命名上就能看出來(lái)是判斷是否為函數(shù)的。然后就是 t.isCallExpression,這是用來(lái)判斷是否為函數(shù)調(diào)用的:

// 這就是 callExpression
xxx()

翻譯完了,接下來(lái)咱們就來(lái)分析一遍:

當(dāng) value 不是常量并且不能放在等號(hào)左側(cè)時(shí)(這種情況有處理)

當(dāng) value 是常量或者是一個(gè)函數(shù)字面量時(shí)(這種情況有處理)

當(dāng) value 是一個(gè)正在調(diào)用的函數(shù)時(shí)(這種情況有處理)

不知大家看完這仨判斷后有什么感悟,反正當(dāng)我捋完這段邏輯的時(shí)候感覺(jué)有點(diǎn)迷,因?yàn)楹孟駢焊鶅壕蜎](méi)覆蓋掉全部情況??!咱們先這么分一下:value 肯定是變量名、字面量以及常量中的其中一種對(duì)吧?是常量的情況下有覆蓋,不是常量時(shí)就有漏洞了,因?yàn)樗昧藗€(gè)并且符號(hào) &&,也就是說(shuō)當(dāng) value 不是常量時(shí)必須還要同時(shí)滿足不能放在等號(hào)左側(cè)這種情況才會(huì)進(jìn)入到這個(gè)判斷中去,那假如我們寫一個(gè)三元表達(dá)式或者二元表達(dá)式那豈不就哪個(gè)判斷也沒(méi)進(jìn)么?不信我們來(lái)試一下:

圖片

可以看到編譯后的 abc 三個(gè)變量直接變暗了,哪都沒(méi)有用到這仨變量,也就是說(shuō)相當(dāng)于吞掉了這段邏輯(畢竟哪個(gè)分支都沒(méi)進(jìn)就相當(dāng)于沒(méi)處理)不過(guò)有人可能會(huì)感到疑惑,三元表達(dá)式明明能放到等號(hào)左側(cè)?。?/p>

圖片

實(shí)際上并不是你想的那樣,等號(hào)和三元表達(dá)式放在一起時(shí)有優(yōu)先級(jí)關(guān)系,調(diào)整一下格式你就明白是怎樣運(yùn)行的了:

const _tmpl$ = /*#__PURE__*/_$template(`<h1>Hello`)
a ? b : c = 1
// 實(shí)際上相當(dāng)于
a
  ? b
  : (c = 1)
// 相當(dāng)于
if (a) {
  b
} else {
  c = 1
}

如果我們用括號(hào)來(lái)把優(yōu)先級(jí)放在三元這邊就會(huì)直接報(bào)錯(cuò)了:

圖片

二元表達(dá)式也是同理:

圖片

我想在 ref 里寫成這樣沒(méi)毛病吧:

<h1 ref={a || b} />

雖然這種寫法比較少見(jiàn),但這也不是你漏掉判斷的理由呀!畢竟好多用 Solid.js 的人都是用過(guò) React 的,他們會(huì)把在 React 那養(yǎng)成的習(xí)慣不自覺(jué)的帶到 Solid.js 里來(lái),而且這不也是 Solid.js 把 API 設(shè)計(jì)的盡可能與 React 有一定相似性的重要原因之一嗎?

但人家在 React 沒(méi)問(wèn)題的寫法到了你這就出問(wèn)題了的話,是會(huì)非常影響你這框架的口碑的!而且在文檔里還沒(méi)有提到任何關(guān)于 ref 不能寫表達(dá)式的說(shuō)明:

圖片

后來(lái)我仔細(xì)想了一下,發(fā)現(xiàn)還真不是他們不小心漏掉的,而是有意為之。至于為什么會(huì)有意為之那就要看它編譯后的產(chǎn)物了:

// 偽代碼
<div ref={a} />
// 將會(huì)被編譯為:
const el = template(`<div>`)
typeof a === 'function' ? a(el) : a = el

其中咱們重點(diǎn)看 a = el 這段代碼,a 就是我們寫在 ref 里的,但假如我們給它換成一個(gè)二元表達(dá)式就會(huì)變成:

// 偽代碼
<div ref={a || b} />
// 將會(huì)被編譯為:
const el = template(`<div>`)
a || b = el

a || b 不能放在等號(hào)左側(cè),所以源碼中的 isLVal 就是為了過(guò)濾這種情況的。那為什么不能編譯成:

(a = el) || (b = el)

這么編譯是錯(cuò)的,因?yàn)榧偃?nbsp;a 為 false,a 就不應(yīng)該被賦值,但實(shí)際上 a 會(huì)被賦值為 el:

圖片

所以要把二元編譯成三元:

圖片

如果是并且符號(hào)就要編譯成取反:

// 偽代碼
<div ref={a && b} />
// 將會(huì)被編譯為:
const el = template(`<div>`)
!a ? a = el : b = el

然后三元表達(dá)式以及嵌套三元表達(dá)式:

<div
  ref={
    Math.random() > 0.5
      ? refFactory() && refArr[0] && (refTarget1 = refTarget2) && (refTarget1 > refTarget2)
      : refTarget1
        ? refTarget2
        : refTarget3
  }
/>

當(dāng)然可能并不會(huì)有人這么寫,Solid 那幫人也是這么想的,所以就算了,太麻煩了,如果真要是有復(fù)雜的條件的話可以用函數(shù):

<div
  ref={
    el => Math.random() > 0.5
      ? refTarget1 = el
      : refTarget2 = el
  }
/>

就先不管 isLVal 為 false 的情況了,不過(guò)我還是覺(jué)得至少要在官網(wǎng)上提一嘴,不然真有人寫成這樣的時(shí)候又搜不到答案的話那多影響口碑??!

總結(jié)

看過(guò)源碼之后感覺(jué)有的地方設(shè)計(jì)的很巧妙,但有些地方又不是很嚴(yán)謹(jǐn)。也怪 jsx 太靈活了,不可能做判斷把所有情況都做到面面俱到,當(dāng)你要寫一些在 React 里能運(yùn)行的騷操作可能在 Solid 里就啞火了。

責(zé)任編輯:武曉燕 來(lái)源: 前端學(xué)不動(dòng)
相關(guān)推薦

2022-08-12 08:02:11

Solid.jsReact

2020-06-17 09:01:37

C語(yǔ)言代碼開發(fā)

2012-12-20 12:24:33

2022-06-30 09:00:23

Vue.js版本名稱

2021-09-04 07:56:44

Pythonos模塊

2020-05-20 13:24:28

MySQL優(yōu)化數(shù)據(jù)庫(kù)

2022-03-27 20:52:41

Chrome插件開發(fā)

2010-09-14 11:36:24

上網(wǎng)行為管理網(wǎng)絡(luò)安全網(wǎng)康科技

2021-08-30 10:25:48

JavaScript進(jìn)階操作前端

2021-10-19 07:41:45

React組件前端

2021-08-26 10:25:04

JavaScript進(jìn)階操作 前端

2014-12-17 14:41:21

云計(jì)算互聯(lián)網(wǎng)混合云

2022-01-11 06:53:23

IPO開發(fā)容器

2017-04-20 14:58:16

2020-05-14 09:15:52

設(shè)計(jì)模式SOLID 原則JS

2013-08-28 10:18:48

2023-01-31 16:35:34

JavaScript測(cè)試框架

2023-02-15 09:00:49

2021-04-28 10:01:06

Python基礎(chǔ)項(xiàng)目

2021-04-07 10:02:51

Python字典Python基礎(chǔ)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)