<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>

          前端架構探索與實踐

          共 6215字,需瀏覽 13分鐘

           ·

          2020-12-08 10:45

          前文

          從思考、到探索、到腳手架的產生,后面經過一系列的項目開發(fā),不斷優(yōu)化和改良。目前已經成功應用到房產中間頁(改名天貓房產)中。這里,做一下總結。

          ?

          「僅為拋磚,希望看完這個系列的同學可以相互探討學習一下」

          ?

          為什么使用源碼

          目前,我們大多數(shù)頁面,包括搜索頁、頻道頁都是大黃蜂搭建的頁面。至于搭建的優(yōu)點,這里就不多贅述了。而我們使用源碼編寫,主要是基于以下幾點思考:

          • 穩(wěn)定性要求高
          • 頁面模塊多而不定
          • 快速回滾方案
          • 模塊通信復雜

          源碼架構

          架構圖
          ?

          架構圖需要調整。此為稿圖,位置放的有些不合理,表述不清

          ?

          底層技術支撐主要采用 Rax1.0 + TypeScript + Jest 編碼。通過 pmcli生成項目腳手架。腳手架提供基礎的文件代碼組織和組件。包括 ComponentscommonUtilsdocumentmodules等。當然,這些組件最終會被抽離到 puicom group 下。

          再往上,是容器層。容器提供一些可插拔的 hooks 能力。并且根據(jù) component 的配置來渲染不同的組件到頁面中,首屏組件和按需加載組件。最后,支撐到每一個對應的頁面里面。

          分工組織

          對于一個頁面,無論是 react 還是 rax,其實都是 fn(x)=>UI 的過程。所以整理流程無非就是拿到接口屬于渲染到 UI 中。所以對于中間頁的架構而言也是如此。

          首先拿到基本的接口數(shù)據(jù),通過自定義的狀態(tài)管理,掛載到全局 state 對應的組件名下。容器層通過組件的配置文件,渲染對應的組件。最終呈現(xiàn)出完成的一個頁面。當然,其中會有一些額外的容器附屬功能,比如喚起手淘、監(jiān)聽鍵盤彈起等這個按需插入對應 hooks 即可。屬于業(yè)務層邏輯。

          工程目錄

          工程結構


          頁面結構

          image.png

          模塊結構


          ?

          以上結構在之前文章中都有介紹到

          ?

          補充

          ?

          這里補充下動態(tài)加載,以及入口 index 的寫法。理論上這部分,在使用這套架構的同學,無需關心

          ?

          index.tsx

          return?(
          ????<H5PageContainer
          ??????title={PAGE_TITLE}
          ??????showPlaceHolder={isLoading}
          ??????renderPlaceHolder={renderLoadingPage}
          ??????renderHeader={renderHeader}
          ??????renderFooter={renderFooter}
          ??????toTopProps={{
          ????????threshold:?400,
          ????????bottom:?203,
          ??????}}
          ??????customStyles={{
          ????????headWrapStyles:?{
          ??????????zIndex:?6,
          ????????},
          ??????}}
          ????>

          ??????{renderSyncCom(
          ????????asyncComConfig,
          ????????dao,
          ????????dispatch,
          ????????reloadPageNotRefresh,
          ????????reloadTick
          ??????)}
          ??????{renderDemandCom(
          ????????demandComConfig,
          ????????offsetBottom,
          ????????dao,
          ????????dispatch,
          ????????reloadPageNotRefresh,
          ????????reloadTick
          ??????)}
          ??????<BottomAttention?/>
          ????H5PageContainer>

          ??);

          模塊動態(tài)加載

          /**
          ?*?按需按需加載容器組件
          ?*
          ?*?@export
          ?*?@param?{*}?props?按需加載的組件?props+path
          ?*?@returns?需按需加載的子組件
          ?*/

          export?default?function(props:?IWrapperProps)?{
          ??const?placeHolderRef:?any?=?useRef(null);
          ??const?{?offsetBottom,?...otherProps?}?=?props;
          ??const?canLoad?=?useDemandLoad(offsetBottom,?placeHolderRef);
          ??const?[comLoaded,?setComLoaded]?=?useState(false);

          ??//?加入?hasLoaded?回調
          ??const?wrapProps?=?{
          ????...otherProps,
          ????hasLoaded:?setComLoaded,
          ??};

          ??return?(
          ????
          ??????????????x-if={!comLoaded}
          ????????ref={placeHolderRef}
          ????????style={{?width:?750,?height:?500,?marginTop:?20?}}
          ????????source={{?uri:?PLACEHOLDER_PIC?}}
          ????????resizeMode={"contain"}
          ??????/>
          ??????
          ????

          ??);
          }

          /**
          ?*?動態(tài)加載
          ?*?@param?props
          ?*/
          function?ImportWrap(props:?IWrapperProps)?{
          ??const?{?path,?...otherProps?}?=?props;
          ??const?[Com,?error]?=?useImport(path);
          ??if?(Com)?{
          ????return?;
          ??}?else?if?(error)?{
          ????console.error(error);
          ????return?null;
          ??}?else?{
          ????return?null;
          ??}
          }

          use-demand-load.ts

          import?{?useState,?useEffect?}?from?'rax';
          import?{?px2rem?}?from?'@ali/puicom-universal-common-unit';

          /**
          ?*?判斷組件按需加載,即將進去可視區(qū)
          ?*/

          export?function?useDemandLoad(offsetBottom,?comRef):?boolean?{
          ????const?[canLoad,?setCanLoad]?=?useState(false);
          ????const?comOffsetTop?=?px2rem(comRef?.current?.offsetTop?||?0);

          ????useEffect(()?=>?{
          ????????if?(canLoad)?return;
          ????????if?(offsetBottom?>?comOffsetTop?&&?!canLoad)?{
          ????????????setCanLoad(true);
          ????????}
          ????},?[offsetBottom]);

          ????useEffect(()?=>?{
          ????????setCanLoad(comRef?.current?.offsetTop?0));
          ????},?[comRef]);

          ????return?canLoad;
          }

          模塊編寫與狀態(tài)分發(fā)

          模塊編寫

          types

          編寫模塊數(shù)據(jù)類型
          image.png
          掛載到 dao(dataAccessObject) 下
          image.png
          統(tǒng)一導出
          ?

          避免文件引入過多過雜

          ?
          • type/index.d.ts
          image.png

          reducers

          編寫模塊對應reducer

          在 daoReducer 中統(tǒng)一掛載


          數(shù)據(jù)分發(fā)

          image.png

          componentConfig



          ?

          此處 keyName 是 type/dao.d.ts 下聲明的值。會進行強校驗。填錯則分發(fā)不到對應的組件中

          ?
          image.png

          component


          數(shù)據(jù)在 props.dataSource

          狀態(tài)分發(fā)

          • 模塊聲明需要掛載到 type/dao.d.ts
          • reducer ?需要 combine ?到 dao.reduer.ts
          • useDataInitdispatch 對應 Action
          • config ?中配置 (才會被渲染到 UI)

          Demo 演示

          ?

          以彈層為例

          ?


          將所有彈層看做為一個模塊,只是內容不同而已。而內容,即為我們之前說的組件目錄結構中的 components 內容

          定義模塊 Models

          定義模塊類型

          編寫模塊屬于類型

          掛載到 dao 中
          image.png

          reducer

          編寫組件所需的 reducer
          image.png
          ?

          actions 的注釋非常有必要

          ?
          image.png
          combine 到 dao 中
          image.png

          編寫組件



          組件編寫

          carbon.png

          通信

          導入對應 action

          import?{?actions?as?modelActions?}?from?"../../../reducers/models.reducer";
          dispatch
          ?dispatch([modelActions.setModelVisible(true),modelActions.setModelType("setSubscribeType")])
          ?

          觸發(fā) ts 校驗

          ?


          效果


          頁面容器

          ?

          基于拍賣通用容器組件改造

          ?

          改造點「基于 body 滾動」

          因為我們目前頁面都是 h5 頁面了,之前則是 weex 的。所以對于容器的底層,之前使用的 RecycleView :固定 div 高度,基于 overflow 來實現(xiàn)滾動的。

          雖然,在 h5 里面這種滾動機制有些”難受“,但是罪不至”換“。但是尷尬至于在于,iOS 的橡皮筋想過,在頁面滾動到頂部以后,如果頁面有頻繁的動畫或者 setState 的時候,會導致頁面重繪,重新回到頂部。與手動下拉頁面容器的橡皮筋效果沖突,而「倒是頁面瘋狂抖動」。所以。。。。重構。

          舊版容器功能點

          ?

          源碼頁面中使用的部分

          ?

          重構后的使用

          ?

          基本沒有太大改變

          ?

          簡單拆解實現(xiàn)

          type

          import?{?FunctionComponent,?RaxChild,?RaxChildren,?RaxNode,?CSSProperties?}?from?'rax';

          export?interface?IHeadFootWrapperProps?{
          ????/**
          ?????*?需要渲染的子組件
          ?????*/

          ????comFunc?:?()?=>?FunctionComponent?|?JSX.Element;
          ????/**
          ?????*?組件類型
          ?????*/

          ????type:?"head"?|?"foot",
          ????/**
          ?????*?容器樣式
          ?????*/

          ????wrapStyles?:?CSSProperties;
          }

          /**
          ?*?滾動到頂部組件屬性
          ?*/

          export?interface?IScrollToTopProps?{
          ????/**
          ?????*?距離底部距離
          ?????*/

          ????bottom?:?number;
          ????/**
          ?????*?zIndex
          ?????*/

          ????zIndex?:?number;
          ????/**
          ?????*?icon?圖片地址
          ?????*/

          ????icon?:?string;
          ????/**
          ?????*?暗黑模式的?icon?圖片地址
          ?????*/

          ????darkModeIcon?:?string;
          ????/**
          ?????*?icon寬度
          ?????*/

          ????iconWidth?:?number;
          ????/**
          ?????*?icon?高度
          ?????*/

          ????iconHeight?:?number;
          ????/**
          ?????*?滾動距離(滾動多少觸發(fā))
          ?????*/

          ????threshold?:?number;
          ????/**
          ?????*?點擊回滾到頂部是否有動畫
          ?????*/

          ????animated?:?boolean;
          ????/**
          ?????*?距離容器右側距離
          ?????*/

          ????right?:?number;
          ????/**
          ?????*?展示回調
          ?????*/

          ????onShow?:?(...args)?=>?void;
          ????/**
          ?????*?消失回調
          ?????*/

          ????onHide?:?(...args)?=>?void;
          }
          /**
          ?*?內容容器
          ?*/

          export?interface?IContentWrapProps{
          ????/**
          ?????*?children
          ?????*/

          ????children:RaxNode;
          ????/**
          ?????*?隱藏滾動到頂部
          ?????*/

          ????hiddenScrollToTop?:boolean;
          ?????/**
          ?????*?返回頂部組件?Props
          ?????*/

          ????toTopProps?:?IScrollToTopProps;
          ????/**
          ?????*?渲染頭部
          ?????*/

          ????renderHeader?:?()?=>?FunctionComponent?|?JSX.Element;
          ????/**
          ?????*?渲染底部
          ?????*/

          ????renderFooter?:?()?=>?FunctionComponent?|?JSX.Element;
          ????/**
          ?????*?自定義容器樣式
          ?????*/

          ????customStyles?:?{
          ????????/**
          ?????????*?body?容器樣式
          ?????????*/

          ????????contentWrapStyles?:?CSSProperties;
          ????????/**
          ?????????*?頭部容器樣式
          ?????????*/

          ????????headWrapStyles?:?CSSProperties;
          ????????/**
          ?????????*?底部容器樣式
          ?????????*/

          ????????bottomWrapStyle?:?CSSProperties;
          ????};
          ????/**
          ?????*?距離底部多少距離開始觸發(fā)?endReached
          ?????*/

          ????onEndReachedThreshold?:?number;
          }

          export?interface?IContainerProps?extends?IContentWrapProps?{
          ????/**
          ?????*?頁面標題
          ?????*/

          ????title:?string;
          ????/**
          ?????*?頁面?placeHolder
          ?????*/

          ????renderPlaceHolder?:?()?=>?FunctionComponent?|?JSX.Element;
          ????/**
          ?????*?是否展示?placeH
          ?????*/

          ????showPlaceHolder?:?boolean;
          }

          index.tsx

          const isDebug = isTrue(getUrlParam('pm-debug'));
          export default function({
          children,
          renderFooter,
          renderHeader,
          title,
          onEndReachedThreshold = 0,
          customStyles = {},
          toTopProps = {},
          showPlaceHolder,
          renderPlaceHolder,
          hiddenScrollToTop=false
          }: IContainerProps) {
          if (!isWeb) return null;

          // 監(jiān)聽滾動
          useListenScroll();
          // 設置標題
          useSetTitle(title);
          // 監(jiān)聽 error 界面觸發(fā)
          const { errorType } = useListenError();

          return (

          x-if={errorType === "" && !showPlaceHolder}
          renderFooter={renderFooter}
          customStyles={customStyles}
          renderHeader={renderHeader}
          onEndReachedThreshold={onEndReachedThreshold}
          toTopProps={toTopProps}
          hiddenScrollToTop={hiddenScrollToTop}
          >
          {children}

          {renderPlaceHolder && showPlaceHolder && renderPlaceHolder()}



          );
          }

          export { APP_CONTAINER_EVENTS };

          通過 Fragment 包裹,主題是 ContentWrapErrorPageVConsoleHolder放置主體以外。

          ?

          相關 hooks 功能點完全區(qū)分開來

          ?

          廣播事件

          /**
          ?*?Events?以頁面為單位
          ?*/

          export?const?APP_CONTAINER_EVENTS?=?{
          ????SCROLL:?'H5_PAGE_CONTAINER:SCROLL',
          ????TRIGGER_ERROR:?'H5_PAGE_CONTAINER:TRIGGER_ERROR',
          ????END_REACHED:?'H5_PAGE_CONTAINER:END_REACHED',
          ????HIDE_TO_TOP:?'H5_PAGE_CONTAINER:HIDE_TO_TOP',
          ????RESET_SCROLL:?'H5_PAGE_CONTAINER:RESET_SCROLL',
          ????ENABLE_SCROLL:"H5_PAGE_CONTAINER:H5_PAGE_CONTAINER"
          }

          pm-cli

          詳見:pm-cli腳手架,統(tǒng)一阿里拍賣源碼架構

          安裝:tnpm install -g @ali/pmcli

          help

          這里在介紹下命令:

          基本使用

          pmc init

          • 在空目錄中調用,則分兩步工作:
            • 首先調用 tnpm init rax 初始化出來 rax 官方腳手架目錄
            • 修改 package.jsonname 為當前所在文件夾的文件夾名稱
            • 升級為拍賣源碼架構,下載對應腳手架模板:init-project
          • 在已init rax后的項目中調用
            • 升級為拍賣源碼架構,下載對應腳手架模板:init-project
          ?

          注意:經過 pmc 初始化的項目,在項目根目錄下回存有.pm-cli.config.json 配置文件

          ?

          pmc add-page

          在當前 項目中新增頁面,選擇三種頁面類型

          img

          推薦使用 simpleSourcecustomStateManage

          頁面模板地址:add-page

          pmc add-mod

          根據(jù)所選擇頁面,初始化不同類型的模塊

          模塊模板地址為:add-mod

          pmc init-mod

          調用def init tbe-mod,并且將倉庫升級為支持 ts 開發(fā)模式

          pmc publish-init

          發(fā)布端架構初始化,基于 react 應用

          發(fā)布端架構模板地址:publish-project

          pmc publish-add

          添加發(fā)布端模塊

          模塊模板地址:publish-mod

          pmc init-mod

          調用 def init tbe-mod 命令,并同時升級為 ts 編碼環(huán)境。

          ?

          配置環(huán)境、安裝依賴、直接運行

          ?

          相關體驗地址(部分無法訪問)

          • 阿里房產
          • 底層容器 (單獨抽離組件ing)
          • pmCli
          • ts tbeMod


          如果你喜歡探討技術,或者對本文有任何的意見或建議,非常歡迎加魚頭微信好友一起探討,當然,魚頭也非常希望能跟你一起聊生活,聊愛好,談天說地。魚頭的微信號是:krisChans95 也可以掃碼關注公眾號,訂閱更多精彩內容。


          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  亚洲日韩发布在线免费 | 国产成人精品小电影 | 天天cao | 国产极品青青草 | www操操操 |