摘要:寫(xiě)在最后總體來(lái)說(shuō),是一個(gè)小而美的框架,值得我們來(lái)折騰一下,以上均為本人理解,如有錯(cuò)誤還請(qǐng)指出,不勝感激一個(gè)硬廣我所在團(tuán)隊(duì)工作地點(diǎn)在北京求大量前端社招實(shí)習(xí),有意者可發(fā)簡(jiǎn)歷至
寫(xiě)在前面
沒(méi)錯(cuò),又是一個(gè)新的前端框架,hyperapp非常的小,僅僅1kb,當(dāng)然學(xué)習(xí)起來(lái)也是非常的簡(jiǎn)單,可以說(shuō)是1分鐘入門。聲明式:HyperApp 的設(shè)計(jì)基于Elm Architecture(這也意味著組件更多的是純函數(shù)),支持自定義標(biāo)簽以及虛擬DOM。下面先來(lái)看下怎么使用:
hello worldimport { h, app } from "hyperapp"; app({ state: { count: 0 }, view: (state, actions) => (), actions: { down: state => ({ count: state.count - 1 }), up: state => ({ count: state.count + 1 }) } }); {state.count}
(完整demo可以參見(jiàn)這里)
這樣就完成了一個(gè)Counter,基本由state,view,actions構(gòu)成:
state: 與react中的如出一轍,state的改變會(huì)引起重新渲染
view: 相當(dāng)于react中的render
actions: 對(duì)state進(jìn)行改變
h相當(dāng)于react的createElement,來(lái)看下h接收的參數(shù):
tag: 標(biāo)簽名,或者一個(gè)函數(shù),傳入函數(shù)也就意味著無(wú)狀態(tài)組件
data: 相當(dāng)于react中的props
children: 子節(jié)點(diǎn)
需要注意的一點(diǎn)是,hyperapp并不支持boolean類型,對(duì)于boolean類型會(huì)忽略,使用時(shí)注意將其轉(zhuǎn)化為string類型,如:
Test {true}
// TestTest {String(true)}
// Test true
至于為什么?可以參見(jiàn)源碼
生命周期下面來(lái)看一下其生命周期,對(duì)于hyperapp的整個(gè)運(yùn)行過(guò)程,可以參見(jiàn)下圖:
load:相當(dāng)于react的componentWillMount
update:相當(dāng)于react的componentWillUpdate
render:調(diào)用view函數(shù)之前調(diào)用
action:調(diào)用actions之前,一般用來(lái)進(jìn)行log
resolve:調(diào)用actions之后,對(duì)于一個(gè)異步操作來(lái)說(shuō),actions返回一個(gè)promise,生命周期resolve來(lái)處理,返回一個(gè)函數(shù)update => result.then(update),即框架內(nèi)部調(diào)用update來(lái)更新state,重新渲染
具體代碼可以參考:
// 生命周期: action -> actions[key] -> resolve // 異步請(qǐng)求需要利用resolve emit("action", { name: name, data: data }); var result = emit("resolve", action(appState, appActions, data)); return typeof result === "function" ? result(update) : update(result);
對(duì)于每一個(gè)節(jié)點(diǎn)來(lái)說(shuō),有著三個(gè)特殊的屬性:
oncreate:相當(dāng)于componentDidMount
onupdate:相當(dāng)于componentDidUpdate
onremove:與componentWillUnMount類似,需要注意的是,加入有了這個(gè)屬性,那么當(dāng)節(jié)點(diǎn)需要被移除時(shí),也不會(huì)被移除,需要自己來(lái)從dom中移除,這樣設(shè)計(jì)是為了便于做一些淡入淡出等效果,具體源碼可以參見(jiàn)這里,更多的使用方式以及討論可以參見(jiàn)這里
三個(gè)屬性均為函數(shù),接收一個(gè)參數(shù),就是這個(gè)節(jié)點(diǎn)
自定義組件通過(guò)上面,基本上可以了解hyperapp的基本寫(xiě)法,下面來(lái)看一下如何自定義組件:
“木偶”組件const Header = ({ title, caption }) => (); // 使用 {title} {caption}
無(wú)狀態(tài)組件的寫(xiě)法與react基本一致,hyperapp官方給出的自定義組件的方式僅僅有這種,但是所有的組件都要是無(wú)狀態(tài)的???答案當(dāng)然是否定的,如何實(shí)現(xiàn)“智能組件”是一個(gè)問(wèn)題:
“智能”組件我們通常的期望業(yè)務(wù)組件具有一些基本的功能,比如數(shù)據(jù)獲取展現(xiàn)這種:
const Header = app({ state: { caption: "loading" }, view(state, actions) { return ({state.caption} ); }, actions: { fetchData(state) { return new Promise((resolve) => { // 模擬fetch數(shù)據(jù) setTimeout(() => { state.caption = "ok"; resolve(state); }, 1000); }); } }, events: { load(state, actions) { actions.fetchData(state); }, resolve(state, actions, result) { if (result && typeof result.then === "function") { return update => result.then(update); } } } }); export default Header;
按照如下方式使用:
import Header from "./Header"; ... state: { count: 0 }, view: (state, actions) => (), ... {state.count}
打開(kāi)頁(yè)面,從ui來(lái)看已經(jīng)實(shí)現(xiàn)組件封裝,但是這種是一種”曲線“的實(shí)現(xiàn)方式,為什么說(shuō)它是不正規(guī),可以觀察其dom層級(jí),可能與我們理解和期望的并不相同。我們期望得到的層級(jí)是:
body main header h2
但是事實(shí)上得到的層級(jí)為:
body header main h2
至于為什么會(huì)產(chǎn)生這種情況,需要看一下源碼:
// app接收一個(gè)對(duì)象 function app(props) { ... // appRoot 就是需要掛載到的根節(jié)點(diǎn) var appRoot = props.root || document.body ... // 注意此處,下文會(huì)用到 return emit; ... // 利用raf調(diào)用render渲染ui function render(cb) { element = patch( appRoot, ... ); } ... function patch(parent, ...) { if (oldNode == null) { // 第一次渲染,將節(jié)點(diǎn)插入到appRoot中 // 只要是第一次掛載,element為null element = parent.insertBefore(createElement(node, isSVG), element); } ... } }
所以說(shuō)將Header組件掛載的原因并不是我們通過(guò)jsx寫(xiě)出了這層結(jié)構(gòu),而是在import的時(shí)候,就已經(jīng)將其掛載到了document.body下,main在掛載到document.body時(shí),被插入到子節(jié)點(diǎn)的末尾。
h(tagName, props, children)
來(lái)簡(jiǎn)單的看下h的實(shí)現(xiàn):
function h(tag, data) { // 根據(jù)后續(xù)參數(shù),生成children while (stack.length) { if (Array.isArray((node = stack.pop()))) { // 處理傳入的child為數(shù)組 for (i = node.length; i--; ) { stack.push(node[i]); } } ... } ... return typeof tag === "string" ? { tag: tag, data: data || {}, children: children } : tag(data, children); }
可以得出的是,tag接收函數(shù)傳入,比如木偶組件,tag就是一個(gè)函數(shù),但是對(duì)于
function emit(name, data) { // 一個(gè)不常見(jiàn)的寫(xiě)法,這個(gè)寫(xiě)法會(huì)返回data return ( (appEvents[name] || []).map(function(cb) { var result = cb(appState, appActions, data); if (result != null) { data = result; } }), data ); }
基于目前這兩點(diǎn),可以得出:
h返回的就是children,也就是一個(gè)[]
由于
需要render的節(jié)點(diǎn)的子節(jié)點(diǎn)中根本就沒(méi)有
這種實(shí)現(xiàn)方式可以說(shuō)是非常的不好,局限性也很大,想想可不可以利用其他方法實(shí)現(xiàn):
// 改進(jìn)Header組件 const Header = (root) => app({ root, ...同上 }); // 改進(jìn)引入方式 view: (state, actions) => (), Header(e)}>{state.count}
這種方式,利用了oncreate方法,掛載后,載入組件(可以考慮通過(guò)代碼分割將組件異步加載)
hyperapp支持傳入mixins,既然天然的支持這個(gè),那么將一個(gè)組件進(jìn)行兩方面分割:
view,利用“木偶組件”實(shí)現(xiàn)
feature,利用mixins實(shí)現(xiàn)
組件定義:
export const HeaderView = ({ text }) => ({text} ); export const HeaderMixins = () => ({ state: // 同上 actions: // 同上 events: // 同上 });
使用方式:
import { HeaderView, HeaderMixins } from "./HeaderView"; ... state: { count: 0 }, view: (state, actions) => (), mixins: [ HeaderMixins() ] ... {state.count}
mixins會(huì)將其屬性與本身進(jìn)行一個(gè)并操作,可以理解為Object.assign(key, mixins[key]),對(duì)于events來(lái)說(shuō),為一個(gè)典型的發(fā)布/訂閱模式,events的某一種類型對(duì)應(yīng)一個(gè)數(shù)組,emit時(shí)會(huì)將其全部執(zhí)行。本人認(rèn)為利用這種方式可以實(shí)現(xiàn)出一個(gè)比較符合框架本意的”智能“組件,但是仍然有些問(wèn)題,就是state,在使用這個(gè)組件時(shí)不得不去看一下組件內(nèi)部的state叫什么名字,而且容易造成同名state沖突的情況。
寫(xiě)在最后總體來(lái)說(shuō),hyperapp是一個(gè)小而美的框架,值得我們來(lái)折騰一下,以上均為本人理解,如有錯(cuò)誤還請(qǐng)指出,不勝感激~
一個(gè)硬廣我所在團(tuán)隊(duì)(工作地點(diǎn)在北京)求大量前端(社招 or 實(shí)習(xí)),有意者可發(fā)簡(jiǎn)歷至:[email protected]
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/88804.html
摘要:,大家好,好久不賤呢最近因?yàn)榭戳艘恍┑男≌f(shuō),整個(gè)人都比較致郁就在昨天,我用了一天的時(shí)間寫(xiě)了,又一個(gè)小而美的前端框架可能你覺(jué)得,有了和,沒(méi)必要再寫(xiě)一個(gè)了我覺(jué)得我還是想想辦法尋找一下它的存在感吧先看的組件化方案最先看到的應(yīng)該是。 halo,大家好,好久不賤呢! 最近因?yàn)榭戳艘恍?be 的小說(shuō),整個(gè)人都比較致郁::>__+ {state.count--}}>- ...
摘要:,大家好,好久不賤呢最近因?yàn)榭戳艘恍┑男≌f(shuō),整個(gè)人都比較致郁就在昨天,我用了一天的時(shí)間寫(xiě)了,又一個(gè)小而美的前端框架可能你覺(jué)得,有了和,沒(méi)必要再寫(xiě)一個(gè)了我覺(jué)得我還是想想辦法尋找一下它的存在感吧先看的組件化方案最先看到的應(yīng)該是。 halo,大家好,好久不賤呢! 最近因?yàn)榭戳艘恍?be 的小說(shuō),整個(gè)人都比較致郁::>__+ {state.count--}}>- ...
摘要:專有的內(nèi)容更少,而更多符合標(biāo)準(zhǔn)的成分。當(dāng)前標(biāo)簽實(shí)例的方法被調(diào)用時(shí)當(dāng)前標(biāo)簽的任何一個(gè)祖先的被調(diào)用時(shí)更新從父親到兒子單向傳播。相對(duì)來(lái)說(shuō),微型場(chǎng)景會(huì)更適合,不想要太多的外部依賴,又需要組件化數(shù)據(jù)驅(qū)動(dòng)等更現(xiàn)代化框架的能力。 Riot.js是什么? Riot 擁有創(chuàng)建現(xiàn)代客戶端應(yīng)用的所有必需的成分: 響應(yīng)式 視圖層用來(lái)創(chuàng)建用戶界面 用來(lái)在各獨(dú)立模塊之間進(jìn)行通信的事件庫(kù) 用來(lái)管理URL和瀏覽器回...
摘要:專有的內(nèi)容更少,而更多符合標(biāo)準(zhǔn)的成分。當(dāng)前標(biāo)簽實(shí)例的方法被調(diào)用時(shí)當(dāng)前標(biāo)簽的任何一個(gè)祖先的被調(diào)用時(shí)更新從父親到兒子單向傳播。相對(duì)來(lái)說(shuō),微型場(chǎng)景會(huì)更適合,不想要太多的外部依賴,又需要組件化數(shù)據(jù)驅(qū)動(dòng)等更現(xiàn)代化框架的能力。 Riot.js是什么? Riot 擁有創(chuàng)建現(xiàn)代客戶端應(yīng)用的所有必需的成分: 響應(yīng)式 視圖層用來(lái)創(chuàng)建用戶界面 用來(lái)在各獨(dú)立模塊之間進(jìn)行通信的事件庫(kù) 用來(lái)管理URL和瀏覽器回...
閱讀 1321·2019-08-30 15:44
閱讀 2032·2019-08-30 13:49
閱讀 1664·2019-08-26 13:54
閱讀 3498·2019-08-26 10:20
閱讀 3282·2019-08-23 17:18
閱讀 3306·2019-08-23 17:05
閱讀 2139·2019-08-23 15:38
閱讀 1022·2019-08-23 14:35