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

為改則等地區(qū)用戶提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及改則網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站設(shè)計(jì)制作、網(wǎng)站制作、改則網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!
兩部分
有些同學(xué)看到這可能就會(huì)感到疑惑了,哪兩個(gè)部分?Solid、.js?其實(shí)是這樣:大家應(yīng)該都聽說過 Solid.js 是一個(gè)重編譯、輕運(yùn)行的框架吧,所以它可以被分為編譯器和運(yùn)行時(shí)兩個(gè)部分。
那有人可能會(huì)問:你要是這么說的話那豈不是 Vue 也可以被分為兩部分,畢竟 Vue 也有編譯器和運(yùn)行時(shí),為什么從來沒有人說過 Vue 是兩部分組成的呢?是這樣,Vue 的編譯器和運(yùn)行時(shí)全都放在了同一倉(cāng)庫(kù)內(nèi)的 Monorepo 中:
你可以說 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 這樣的寫法過?
.vue 這種單文件組件就只有 Vue 使用,雖說其他框架也有單文件組件的概念并且有著類似的寫法(如:xxx.svelte)但人家 Svelte也不會(huì)去用 Vue 的編譯器去編譯人家的 Svelte 組件。不過 Solid 不一樣,Solid沒自創(chuàng)一個(gè) xxx.solid,而是明智的選擇了 xxx.jsx。
SFC VS JSX
單文件組件和 jsx 各有利弊,不能說哪一方就一定比另一方更好。但對(duì)于一個(gè)聲明式框架作者而言,選擇單文件組件的好處是可以自定義各種語(yǔ)法,并且還可以犧牲一定的靈活性來?yè)Q取更優(yōu)的編譯策略。缺點(diǎn)就是成本太高了,單單語(yǔ)法高亮和 TS 支持度這一方面就得寫一個(gè)非常復(fù)雜的插件才能填平。
好在 Vue 的單文件組件插件 Volar 已經(jīng)可以支持自定義自己的單文件組件插件了,這有效的降低了框架作者的開發(fā)成本。但 Solid 剛開始的時(shí)候還沒有 Volar 呢(可以去看看 Volar 的源碼有多復(fù)雜 這還僅僅只是一個(gè)插件就需要花費(fèi)那么多時(shí)間和精力),甚至直到現(xiàn)在 Volar 也沒個(gè)文檔,就只有 Vue 那幫人在用 Volar(畢竟是他們自己研究的):
并且人家選擇 jsx 也有可能并非是為了降低開發(fā)成本,而是單純的鐘意于 jsx 語(yǔ)法而已。那么為什么選擇 jsx 會(huì)降低開發(fā)成本呢?首先就是不用自己寫 parser、generator 等一堆編譯相關(guān)的東西了,一個(gè) babel 插件就能識(shí)別 jsx 語(yǔ)法。語(yǔ)法高亮、TS 支持度這方面更是不用操心,甚至用戶都不需要為編輯器安裝任何插件(何時(shí)聽過 jsx 插件)。
并且由于 React 是全球占有率最高的框架,jsx 已被廣泛接受(甚至連 Vue 都支持 jsx)但如果選擇單文件組件的話又會(huì)產(chǎn)生有人喜歡這種寫法有人喜歡那種寫法的問題,比方說同樣使用 sfc 的 Vue 和 Svelte,if-else 寫法分別是這樣:
{#if xxx}
{:else}
{/if}
有人喜歡上面那種寫法就有人喜歡下面那種寫法,眾口難調(diào),無(wú)論選擇哪種寫法可能都會(huì)導(dǎo)致另一部分的用戶失望。而 jsx 就靈活的多了,if-else 想寫成什么樣都可以根據(jù)自己的喜好來:
if (xxx) {
return
} else {
return
}
// 或者
return xxx ? :
// 亦或
let Title = 'h1'
if (xxx) Title = 'div'
return 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 沒把 dom-expressions 放在 solidjs/solid 里的重要原因之一,但 Solid.js 又是一個(gè)注重編譯的框架,沒了 dom-expressions 還不行,所以只能說 Solid.js 是由兩部分組成的。
DOM Expressions
DOM Expressions 翻譯過來就是 DOM 表達(dá)式的意思,有人可能會(huì)問那你標(biāo)題為啥不寫成《盤點(diǎn) DOM Expressions 源碼中的那些迷惑行為》?拜托!誰(shuí)知道 DOM Expressions 到底是個(gè)什么鬼!
如果不是我苦口婆心的說了這么多,有幾個(gè)能知道這玩意就是 Solid.js 的編譯器,甭說國(guó)內(nèi)了,就連國(guó)外都沒幾個(gè)知道 DOM Expressions的。你要說 Solid.js 那別人可能會(huì)豎起大拇指說聲 Excellent,但你要說 DOM Expressions 那別人說的很可能就是 What the fuck is that? 了。不信你看它倆的對(duì)比:
再來看看 Ryan 在油管上親自直播 DOM Expressions時(shí)的慘淡數(shù)據(jù):
這都沒我隨便寫篇文章的點(diǎn)贊量高,信不信如果我把標(biāo)題中的 Solid.js 換成了 DOM Expression 的話點(diǎn)贊量都不會(huì)有 Ryan 直播的數(shù)據(jù)好?好歹人家還是 Solid的作者,都只能獲得如此慘淡的數(shù)據(jù),那更別提我了。
言歸正傳,為了防止大家不知道 Solid.js 編譯后的產(chǎn)物與 React 編譯后的產(chǎn)物有何不同,我們先來寫一段簡(jiǎn)單的 jsx:
import c from 'c'
import xxx from 'xxx'
export function Component () {
return (
{}}>
{ 1 + 2 }
{ xxx }
)
}
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(`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)不太易讀,我來給大家寫一段偽代碼,用來幫助大家快速理解 Solid 到底把那段 jsx 編譯成了啥:
import c from 'c';
import xxx from 'xxx';
const template = doucment.createElement('template')
template.innerHTML = '3'
const el = template.content.firstChild.cloneNode(true) // 大家可以簡(jiǎn)單的理解為 el 就是 3
export function Component() {
return (() => {
el.onclick = () => {};
el.setAttribute("c", c);
el.insertBefore(xxx);
return el;
})();
}
這樣看上去就清晰多了吧?直接編譯成了真實(shí)的 DOM 操作,這也是它性能為何能夠如此強(qiáng)悍的原因之一,沒有中間商(虛擬DOM)賺差價(jià)。但大家有沒有感覺有個(gè)地方看起來好像有點(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 但感覺多了一個(gè)脫褲子放屁的步驟
看了源碼才知道,原來看似多此一舉的舉動(dòng)實(shí)則是有苦衷的。因?yàn)槲覀冞@是典型的站在上帝視角來審視編譯后的代碼,源碼的做法是只對(duì) jsx 進(jìn)行遍歷,在剛剛那種情況下所編譯出來的代碼確實(shí)不是最優(yōu)解,但它能保證在各種的場(chǎng)景下都能正常運(yùn)行。
我們來寫一段比較罕見的代碼大家就能明白過來怎么回事了:
if ( {}} />) {
// do something…
}
當(dāng)然這么寫沒有任何的意義,這是為了幫助大家理解為何 Solid 要把它的 jsx 編譯成一段自執(zhí)行函數(shù)才會(huì)寫成這樣的。我們來寫一段偽代碼,實(shí)際上 Solid 編譯出來的并不是這樣的代碼,但相信大家能夠明白其中的含義:
{}} />
// 將會(huì)被編譯成
const el = document.createElement('div')
el.setAttribute('a', value)
el.onclick = () => {}發(fā)現(xiàn)問題所在了么?原本 jsx 只有一行代碼,但編譯過后卻變成三行了。所以如果不加一個(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ù)中那就沒問題了:
if ((() => {
const el = document.createElement('div')
el.setAttribute('a', value)
el.onclick = () => {}
return el
})()) {
// do something…
}
我知道肯定有人會(huì)說把那三行代碼提出去不就得了么:
const el = document.createElement('div')
el.setAttribute('a', value)
el.onclick = () => {}
if (el) {
// do something…
}
還記得我之前說過的那句:我們是站在上帝視角來審判 Solid 編譯后代碼的么?理論上來說這么做確實(shí)可以,但編譯成本無(wú)疑會(huì)高上許多,因?yàn)檫€要判斷 jsx 到底寫在了哪里,根據(jù)上下文的不同來生成不同的代碼,但這樣肯定沒有只編譯 jsx 而不管 jsx 到底是被寫在了哪里來的方便。而且我們上述的那種方式也不是百分百?zèng)]問題的,照樣還是會(huì)有一些意想不到的場(chǎng)景:
for (let i = 0, j; j = , i < 3; i++) {
console.log(j)
}
但假如按照我們那種策略來編譯代碼的話:
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)問題,因?yàn)?nbsp;el 用到了變量 i,而 el 又被提到外面去了所以訪問不到 i變量,所以 el 這幾行代碼必須要在 jsx 的原位置上才行,只有自執(zhí)行函數(shù)能夠做到這一點(diǎn)。由于 js 是一門極其靈活的語(yǔ)言,各種騷操作數(shù)不勝數(shù),所以把編譯后的代碼全都加上一段自執(zhí)行函數(shù)才是性價(jià)比最高并且最省事的選擇之一。
迷之嘆號(hào)?
有次在用 playground.solidjs.com 編譯 jsx 時(shí)驚奇的發(fā)現(xiàn):
不知大家看到這段
Hello, !
時(shí)是什么感受,反正我的第一感覺就是出 bug 了,把我的嘆號(hào) ! 給編譯成 了。但令人摸不著頭腦的是,這段代碼完全可以正常運(yùn)行,沒有出現(xiàn)任何的 bug。隨著測(cè)試的深入,發(fā)現(xiàn)其實(shí)并不是把我的嘆號(hào) ! 給編譯成 了,只是恰巧在那個(gè)位置上我寫了個(gè)嘆號(hào),就算不寫嘆號(hào)也照樣會(huì)有這個(gè) 的:
發(fā)現(xiàn)沒? 出現(xiàn)的位置恰巧就是 {xxx} 的位置,我們?cè)谡{(diào)試的時(shí)候發(fā)現(xiàn)最終生成的代碼其實(shí)是這樣:
12
也就是說當(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í),按理說這個(gè)節(jié)點(diǎn)就已經(jīng)不復(fù)存在了。但每當(dāng)我們打開控制臺(tái)時(shí)就會(huì)看到原本 v-if 的那個(gè)位置變成了這樣:
尤雨溪為何要留下一個(gè)看似沒有任何意義的空注釋節(jié)點(diǎn)呢?廣大強(qiáng)迫癥小伙伴們?nèi)滩涣肆?,趕忙去 GitHub 里開個(gè) issue 問尤雨溪:
尤雨溪給出的答案是這樣:
那 Solid 加一個(gè)這玩意也是和 Vue 一樣的原由么?隨著對(duì)源碼的深入,我發(fā)現(xiàn)它跟 Vue 的 原由并不一樣,我們?cè)賮碛靡欢蝹未a來幫助大家理解 Solid 為什么需要一段空注釋節(jié)點(diǎn):
1{xxx}2
// 將會(huì)被編譯成:
const el = template('12
')
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)的話那就找不到該往哪插了:
1{xxx}2
// 假如編譯成沒有空節(jié)點(diǎn)的樣子:
const el = template('12
')
const el1 = el1.firstChild // 12
const el2 = el2.nextSibling // 沒有兄弟節(jié)點(diǎn)了 只有一個(gè)子節(jié)點(diǎn):12
el.insertBefore(xxx, 特么的往哪插?)
所以當(dāng)大家在 playground.solidjs.com 中發(fā)現(xiàn)有 這種奇怪符號(hào)時(shí),請(qǐng)不要覺得這是個(gè) bug,這是為了留個(gè)占位符,方便 Solid 找到插入點(diǎn)。只不過大多數(shù)人都想不到,把這個(gè) 賦值給 innerHTML 后會(huì)在頁(yè)面上生成一個(gè) 。
迷之 ref
無(wú)論是 Vue 還是 React 都是用 ref 來獲取 DOM 的,Solid 的整體 API 設(shè)計(jì)的與 React 較為相似,ref 自然也不例外:
但它也有自己的小創(chuàng)新,就是 ref 既可以傳函數(shù)也可以傳普通變量。如果是函數(shù)的話就把 DOM 傳進(jìn)去,如果是普通變量的話就直接賦值:
// 偽代碼
// 將會(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 是用來編譯 jsx 上的屬性的:
當(dāng) key 等于 ref 時(shí)需要進(jìn)行一些特殊處理,非常迷的一個(gè)命名就是這個(gè) isFunction,看名字大家肯定會(huì)認(rèn)為這個(gè)變量代表的是屬性值是否為函數(shù)。我來用人話給大家翻譯一下這個(gè)變量賦的值代表什么含義:t.isIdentifier(value.expression)的意思是這個(gè) value 是否為變量名:
比方說 ref={a} 中的 a 就是個(gè)變量名,但如果是 ref={1}、ref={() => {}}那就不是變量名,剩下那倆條件是判斷這個(gè)變量名是否是 const 聲明的。也就是說:
const isFunction = value 是個(gè)變量名 && 是用 const 聲明的
這特么就能代表 value 是個(gè) function 了?
在我眼里看來這個(gè)變量叫 isConst 還差不多,我們?cè)賮硎崂硪幌逻@段邏輯:
// 簡(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)) {
...
}
}
}
接下來就是 if-else 條件判斷里的條件了,再來翻譯下,t.isLVal 代表的是:value 是否可以放在等號(hào)左側(cè),這是什么意思呢?一個(gè)例子就能讓大家明白:
// 此時(shí) key = 'ref'、value = () => {}
{}} />
// 現(xiàn)在我們需要寫一個(gè)等號(hào) 看看 value 能不能放在等號(hào)的左側(cè):
() => {} = xxx // 很明顯這是錯(cuò)誤的語(yǔ)法 所以 t.isLVal(value.expression) 是 false
// 但假如寫成這樣:
a.b.c = xxx // 這是正確的語(yǔ)法 所以 t.isLVal(value.expression) 現(xiàn)在為 true
明白了 t.isLVal 接下來就是 t.isFunction 了,這個(gè)從命名上就能看出來是判斷是否為函數(shù)的。然后就是 t.isCallExpression,這是用來判斷是否為函數(shù)調(diào)用的:
// 這就是 callExpression
xxx()
翻譯完了,接下來咱們就來分析一遍:
當(dāng) value 不是常量并且不能放在等號(hào)左側(cè)時(shí)(這種情況有處理)
當(dāng) value 是常量或者是一個(gè)函數(shù)字面量時(shí)(這種情況有處理)
當(dāng) value 是一個(gè)正在調(diào)用的函數(shù)時(shí)(這種情況有處理)
不知大家看完這仨判斷后有什么感悟,反正當(dāng)我捋完這段邏輯的時(shí)候感覺有點(diǎn)迷,因?yàn)楹孟駢焊鶅壕蜎]覆蓋掉全部情況??!咱們先這么分一下:value 肯定是變量名、字面量以及常量中的其中一種對(duì)吧?是常量的情況下有覆蓋,不是常量時(shí)就有漏洞了,因?yàn)樗昧藗€(gè)并且符號(hào) &&,也就是說當(dāng) value 不是常量時(shí)必須還要同時(shí)滿足不能放在等號(hào)左側(cè)這種情況才會(huì)進(jìn)入到這個(gè)判斷中去,那假如我們寫一個(gè)三元表達(dá)式或者二元表達(dá)式那豈不就哪個(gè)判斷也沒進(jìn)么?不信我們來試一下:
可以看到編譯后的 abc 三個(gè)變量直接變暗了,哪都沒有用到這仨變量,也就是說相當(dāng)于吞掉了這段邏輯(畢竟哪個(gè)分支都沒進(jìn)就相當(dāng)于沒處理)不過有人可能會(huì)感到疑惑,三元表達(dá)式明明能放到等號(hào)左側(cè)?。?/p>
實(shí)際上并不是你想的那樣,等號(hào)和三元表達(dá)式放在一起時(shí)有優(yōu)先級(jí)關(guān)系,調(diào)整一下格式你就明白是怎樣運(yùn)行的了:
const _tmpl$ = /*#__PURE__*/_$template(`Hello`)
a ? b : c = 1
// 實(shí)際上相當(dāng)于
a
? b
: (c = 1)
// 相當(dāng)于
if (a) {
b
} else {
c = 1
}
如果我們用括號(hào)來把優(yōu)先級(jí)放在三元這邊就會(huì)直接報(bào)錯(cuò)了:
二元表達(dá)式也是同理:
我想在 ref 里寫成這樣沒毛病吧:
雖然這種寫法比較少見,但這也不是你漏掉判斷的理由呀!畢竟好多用 Solid.js 的人都是用過 React 的,他們會(huì)把在 React 那養(yǎng)成的習(xí)慣不自覺的帶到 Solid.js 里來,而且這不也是 Solid.js 把 API 設(shè)計(jì)的盡可能與 React 有一定相似性的重要原因之一嗎?
但人家在 React 沒問題的寫法到了你這就出問題了的話,是會(huì)非常影響你這框架的口碑的!而且在文檔里還沒有提到任何關(guān)于 ref 不能寫表達(dá)式的說明:
后來我仔細(xì)想了一下,發(fā)現(xiàn)還真不是他們不小心漏掉的,而是有意為之。至于為什么會(huì)有意為之那就要看它編譯后的產(chǎn)物了:
// 偽代碼
// 將會(huì)被編譯為:
const el = template(``)
typeof a === 'function' ? a(el) : a = el
其中咱們重點(diǎn)看 a = el 這段代碼,a 就是我們寫在 ref 里的,但假如我們給它換成一個(gè)二元表達(dá)式就會(huì)變成:
// 偽代碼
// 將會(huì)被編譯為:
const el = template(``)
a || b = ela || b 不能放在等號(hào)左側(cè),所以源碼中的 isLVal 就是為了過濾這種情況的。那為什么不能編譯成:
(a = el) || (b = el)
這么編譯是錯(cuò)的,因?yàn)榧偃?nbsp;a 為 false,a 就不應(yīng)該被賦值,但實(shí)際上 a 會(huì)被賦值為 el:
所以要把二元編譯成三元:
如果是并且符號(hào)就要編譯成取反:
// 偽代碼
// 將會(huì)被編譯為:
const el = template(``)
!a ? a = el : b = el
然后三元表達(dá)式以及嵌套三元表達(dá)式:
0.5
? refFactory() && refArr[0] && (refTarget1 = refTarget2) && (refTarget1 > refTarget2)
: refTarget1
? refTarget2
: refTarget3
}
/>
當(dāng)然可能并不會(huì)有人這么寫,Solid 那幫人也是這么想的,所以就算了,太麻煩了,如果真要是有復(fù)雜的條件的話可以用函數(shù):
Math.random() > 0.5
? refTarget1 = el
: refTarget2 = el
}
/>
就先不管 isLVal 為 false 的情況了,不過我還是覺得至少要在官網(wǎng)上提一嘴,不然真有人寫成這樣的時(shí)候又搜不到答案的話那多影響口碑?。?/p>
總結(jié)
看過源碼之后感覺有的地方設(shè)計(jì)的很巧妙,但有些地方又不是很嚴(yán)謹(jǐn)。也怪 jsx 太靈活了,不可能做判斷把所有情況都做到面面俱到,當(dāng)你要寫一些在 React 里能運(yùn)行的騷操作可能在 Solid 里就啞火了。
網(wǎng)站題目:盤點(diǎn)Solid.js源碼中的那些迷惑行為
網(wǎng)站地址:http://www.dlmjj.cn/article/coggesc.html


咨詢
建站咨詢
