學(xué)不完的框架,舔不完的面,Qwik,你到底想干啥
最近我又又雙學(xué)習(xí)了一個(gè)新框架,qwik
真的沒(méi)完沒(méi)了了,
不過(guò)作為"前端娛樂(lè)圈戰(zhàn)地記者",我繼續(xù)幫大家踩雷。
?? 同系列閱讀
- 學(xué)不完的框架,??啄不完的米,SolidJS,你到底愛(ài)誰(shuí)???
- ??超級(jí)簡(jiǎn)單的Svelte,學(xué)不會(huì)你來(lái)打我
- 真心希望不會(huì)有燒不斷地鎖這篇文章!!!
?? 初識(shí)qwik
廢話不多說(shuō),我們先上代碼。一個(gè)簡(jiǎn)單的計(jì)數(shù)器功能
?? useSignal
const App = component$(() => {
const count = useSignal(0);
return (
<>
<button onClick$={() => count.value++}>+</button>
<div>{count.value}</div>
</>
);
});
讓我們給這個(gè)計(jì)數(shù)器加上監(jiān)聽(tīng)事件(后面會(huì)詳細(xì)講)
useVisibleTask$(({ track }) => {
track(() => console.log(count.value))
})
sigal.gif
useSignal,雖然讓我想起來(lái)了熟悉地SolidJS,但是看寫法,這不就是vue3的ref嗎?是的,看起來(lái)很像,那有沒(méi)有類似reactive呢?
?? useStore
當(dāng)然有啊,useSignal是針對(duì)基本變量的。對(duì)于非基本類型可以使用useStore。
const App = component$(() => {
const data = useStore({count: 0})
return (
<>
<button onClick$={() => data.count++}>+</button>
<p>{data.count}</p>
</>
);
});
sigal.gif
useStore可以提供方法,不過(guò)比較復(fù)雜:
import {
$,
type QRL
} from "@builder.io/qwik";
type Store = {
count: number
add: QRL<(this: Store) => void>
}
const App = component$(() => {
const data = useStore<Store>({
count: 0,
add: $(function(this) {
this.count++;
})
})
return (
<>
<button onClick$={() => data.add()}>+</button>
<p>{data.count}</p>
</>
);
}
sigal.gif
?? useComputed$
reactive有了,computed是不是也應(yīng)該有啊,來(lái)了,它就是useComputed$
const capitalizedName = useComputed$(() => {
return count.value + 'mmdctjj';
});
useVisibleTask$(({ track }) => {
track(() => console.log(capitalizedName.value))
})
computer.gif
?? useContext
那依賴注入有沒(méi)有啊?抱歉,沒(méi)有,因?yàn)閝wik是基于jsx的框架,所以只有拿useContext來(lái)將就了
type Store = {
count: number
add: QRL<(this: Store) => void>
}
// ?? 創(chuàng)建全局的上下文
const context = createContextId<Store>('uuid')
const App = component$(() => {
const data = useStore<Store>({
count: 0,
add: $(function(this)
this.count++;
})
})
// ?? provider數(shù)據(jù)
useContextProvider(context, data);
return (
<>
<button onClick$={() => data.add()}>+</button>
<p>{data.count}</p>
</>
);
}
const Child = component$(() => {
// ?? 消費(fèi)上下文
const data = useContext(context)
return (
<>
<Child />
</>
);
}
好吧,接下來(lái)我們說(shuō)些不一樣的
?? useTask$ or useVisibleTask$
上面的例子中,我們所有的監(jiān)聽(tīng)事件都是通過(guò)useVisibleTask$實(shí)現(xiàn)的。雖然它看起來(lái)和useEffect類似,但是卻有著很大的區(qū)別。
這得從qwik的架構(gòu)說(shuō)起,首先qwik是個(gè)服務(wù)端渲染的框架,相當(dāng)于Next.js(基于React服務(wù)端渲染框架)、 Nuxt.js(基于Vue地服務(wù)端渲染框架),換句話說(shuō),它天生支持服務(wù)端渲染的前端框架。所以一個(gè)組件的生命周期是從服務(wù)器開(kāi)始的。
useTask$ -------> RENDER ---> useVisibleTask$
|| --- SERVER or BROWSER --- | ----- BROWSER ----- ||
pause|resume
它們都是用來(lái)注冊(cè)任務(wù)的鉤子函數(shù),這個(gè)任務(wù)在服務(wù)端僅僅執(zhí)行一次,在客戶端可能多次渲染。
下面是具體的區(qū)別
?? useTask$
useTask$首先在服務(wù)端執(zhí)行一次,如果客戶端使用track訂閱依賴了,那么當(dāng)客戶端渲染之后觸發(fā)更新時(shí),任務(wù)會(huì)在客戶端再次執(zhí)行。如果沒(méi)有track,那么僅僅在客戶端執(zhí)行一次。
const App = component$(() => {
const count = useSignal(0);
useTask$(({ track }) => {
track(() => console.log(count.value))
})
return (
<>
<button onClick$={() => count.value++}>+</button>
<div>{count.value}</div>
</>
);
});
task.gif
頁(yè)面刷新后服務(wù)端也執(zhí)行了一次任務(wù)
?? useVisibleTask$
useVisibleTask$:僅僅在瀏覽器執(zhí)行,渲染之后立馬執(zhí)行,當(dāng)訂閱的參數(shù)發(fā)生改變時(shí),任務(wù)會(huì)被再次執(zhí)行。
const App = component$(() => {
const count = useSignal(0);
useVisibleTask$(({ track }) => {
track(() => console.log(count.value))
})
return (
<>
<button onClick$={() => count.value++}>+</button>
<div>{count.value}</div>
</>
);
});
taskvis.gif
它們還有一個(gè)特別重要的參數(shù)cleanup,每次新的任務(wù)被觸發(fā)時(shí),都會(huì)執(zhí)行上次任務(wù)的cleanup。另外組件被移除時(shí)也會(huì)被執(zhí)行。
useVisibleTask$(({ track, cleanup }) => {
// console.log('I am excuted!')
track(() => console.log(count.value))
cleanup(() => console.log('last'))
})
cleanup.gif
我們可以看到,每次任務(wù)觸發(fā)都是先打印last,然后才是最新的count.value
另外,還有一個(gè)重要的配置項(xiàng):{ strategy: 'document-ready' },此時(shí),會(huì)在頁(yè)面加載完畢立馬執(zhí)行。
useVisibleTask$(() => {
// 渲染完畢之后執(zhí)行
console.log(2222)
})
useVisibleTask$(() => {
// document-ready立馬執(zhí)行
console.log(1111)
}, { strategy: 'document-ready' })
此時(shí)console的打印結(jié)果是先1111,然后是2222。
?? 幫它模擬完整的生命周期
綜上,我們可以模擬出一個(gè)完整的生命周期,
唯一的瑕疵是before update會(huì)在組件銷毀時(shí)和unmount一起執(zhí)行一次。
useVisibleTask$(() => {
console.log('before mounted!')
}, { strategy: 'document-ready' })
useVisibleTask$(({ cleanup }) => {
console.log('mounted!')
cleanup(() => console.log('unmount'))
})
useVisibleTask$(({ track, cleanup }) => {
track(() => console.log('updated!', count.value))
cleanup(() => console.log('before update'))
})
?? 總結(jié)
qwik上線一年不到已經(jīng)17.9k地star了,足見(jiàn)它地優(yōu)秀了!