国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

React單頁如何規(guī)劃路由、設(shè)計Store、劃分模塊、按需加載

13651657101 / 2097人閱讀

摘要:所以,我們使用來序列化第二級參數(shù),比如張家界這樣做也有個不好的地方,就是需要,然后特殊字符會變得比較丑。模塊規(guī)劃模塊與無關(guān)劃分模塊可以很好的拆解功能,化繁為簡,并且對內(nèi)隱藏細節(jié),對外暴露少量接口。

本項目地址:react-coat-helloworld

react-coat 同時支持瀏覽器渲染(SPA)服務(wù)器渲染(SSR),本 Demo 僅演示瀏覽器渲染,請先了解一下:react-coat

第一站:Helloworld 安裝
git clone https://github.com/wooline/react-coat-helloworld.git
npm install
運行

npm start 以開發(fā)模式運行

npm run build 以產(chǎn)品模式編譯生成文件

npm run prod-express-demo 以產(chǎn)品模式編譯生成文件并啟用一個 express 做 demo

npm run gen-icon 自動生成 iconfont 文件及 ts 類型

查看在線 Demo

點擊查看在線 Demo

關(guān)于腳手架

采用 webpack 4.0 為核心搭建,無二次封裝,干凈透明

采用 typescript 作開發(fā)語言,使用 Postcss 及 less 構(gòu)建 css

不使用 css module,用模塊化命名空間保證 css 不沖突

采用 editorconfig > prettier 作統(tǒng)一的風格配置,建議使用 vscode 作為 IDE,并安裝 prettier 插件以自動格式化

采用 tslint、eslint、stylelint 作代碼檢查

PeerDependencies

開發(fā)環(huán)境需要很多的 dependencies,你可以自行安裝特定版本,如果特殊要求,建議本站提供的 react-coat-pkg 以及 react-coat-dev-pkg,它們已經(jīng)包含了絕大部分 dependencies。

TS 類型的定義

使用 Typescript 意味著使用強類型,我們把業(yè)務(wù)實體中 TS 類型定義分兩大類:API類型Entity類型

API 類型:指的是來自于后臺 API 輸入的類型,它們可能直接由 swagger 生成,或是機器生成。

Entity 類型:指的是本系統(tǒng)為業(yè)務(wù)實體建模而定義的類型,每個業(yè)務(wù)實體(resource)都會有定義。

理想狀況下,API 類型和 Entity 類型會保持一致,因為業(yè)務(wù)邏輯是同一套,但實際開發(fā)中,可能因為前后端并行開發(fā)、或者前后端視角不同而出現(xiàn)兩者各表。

為了充分的解耦,我們允許這種不一致,我們把 API 類型在源頭就轉(zhuǎn)化為 Entity 類型,而在本系統(tǒng)的代碼邏輯中,不直接使用 API 類型,應(yīng)當使用自已定義的 Entity 類型,以減少其它系統(tǒng)對本系統(tǒng)的影響。
假定項目:旅途 web app 主要頁面:

旅游路線展示

旅途小視頻展示

站內(nèi)信展示(需登錄)

評論展示 (訪客可查看評論,發(fā)表則需登錄)

項目要求

web SPA 單頁應(yīng)用

主要用于 mobile 瀏覽器,也可以適應(yīng)于桌面瀏覽器

無 SEO 要求,但需要能將當前頁面分享給他人

初次進入本站時,顯示 welcome 廣告,并倒計時

路由規(guī)劃

SPA 單頁不就一個頁面么?為什么還需要規(guī)劃路由呢?

其一,為了用戶刷新時盡可能的保持當前展示

其二,為了用戶能將當前展示通過 url 分享給他人

其三,為了后續(xù)的 SEO

path 規(guī)劃

根據(jù)項目需求及 UI 圖,我們初步規(guī)劃主要路由 path 如下:

旅行路線列表 photosList:/photos

旅行路線詳情 photosItem:/photos/:photoId

分享小視頻列表 videosList:/videos

分享小視頻詳情 videosItem:/videos/:videoId

站內(nèi)信列表 messagesList:/messages

參數(shù)規(guī)劃

因為列表頁是有分頁、有搜索的,所以列表類型的路由是有參數(shù)的,比如:

/photos?title=張家界&page=3&pageSize=20

我們估且將這部分查詢列表條件叫"ListSearch",但除了ListSearch之外,也可能會出現(xiàn)別的路由參數(shù),用來控制其它條件(本 demo 暫未涉及),比如:

/photos?title=張家界&page=3&pageSize=20&showComment=true

所以,如果參數(shù)一多,用扁平的一維結(jié)構(gòu)就變得不好表達。而且,利用 URL 參數(shù)存數(shù)據(jù),數(shù)據(jù)將全變成為字符串。比如id=2,你無法知道 2 是數(shù)字型還是字符型,這樣會讓后續(xù)接收處理變得繁重。所以,我們使用 JSON 來序列化第二級參數(shù),比如:

/photos?search={title:"張家界",page:3,pageSize:20}&showComment=true

這樣做也有個不好的地方,就是需要 encodeURI,然后特殊字符會變得比較丑。

路由參數(shù)默認值

為了縮短 URL 長度,本框架設(shè)計了參數(shù)默認值,如果某參數(shù)和默認值相同,可以省去。我們需要做兩項工作:

生成 Url 查詢條件時,對比默認值,如果相同,則省去

原值:{title:"張家界",page:1,pageSize:20} 默認值: {title:"",page:1,pageSize:20},省去后為:{title:"張家界"}

原值:{title:"",page:1,pageSize:20} 默認值: {title:"",page:1,pageSize:20},省去后為:空

收到 Url 查詢條件時,將查詢條件和默認值 merge

/photos?search={page:2} === photos?search={title:"",page:2,pageSize:20}

/photos === photos?search={title:"",page:1,pageSize:20}

處理 null、undefined

由于接收 Url 參數(shù)時,如果某 key 為 undefined,我們會用相應(yīng)的默值將其填充,所以不能將 undefined 作為路由參數(shù)值定義,改為使用 null。也就是說,路由參數(shù)中的每一項,都是必填的,比如:

// 路由參數(shù)定義時,每一項都必填,以下為錯誤示例
interface ListSearch{
  title?:string,
  age?:number
}
// 改為如下正確定義:
interface ListSearch{
  title:string | null,
  age:number | null
}

區(qū)分:原始路由參數(shù)(SearchData) 默認路由參數(shù)(SearchData) 和 完整路由參數(shù)(WholeSearchData)。完整路由參數(shù)(WholeSearchData) = merage(默認路由參數(shù)(SearchData), 原始路由參數(shù)(SearchData))

原始路由參數(shù)(SearchData)每一項都是可選的,用 TS 類型表示為:Partial

完整路由參數(shù)(WholeSearchData)每一項都是必填的,用 TS 類型表示為:Required

默認路由參數(shù)(SearchData)和完整路由參數(shù)(WholeSearchData)類型一致

不直接使用路由狀態(tài)

路由及其參數(shù)本質(zhì)上也是一種 Store,與 Redux Store 一樣,反映當前程序的某些狀態(tài)。但它是片面的,是瞬時的,是不穩(wěn)定的,我們把它看作是 Redux Store 的一種冗余。所以最好不要在程序中直接依賴和使用它,而是控制住它的入口和出口,第一時間在其源頭進行消化轉(zhuǎn)換,讓其成為整個 Redux Store 的一部分,后續(xù)的運行中,我們直接依賴 Redux Store。這樣,我們就將程序與路由設(shè)計解耦了,程序有更大的靈活度甚至可以遷移到無 URL 概念的其它運行環(huán)境中。

模塊規(guī)劃 模塊與 Page 無關(guān)

劃分模塊可以很好的拆解功能,化繁為簡,并且對內(nèi)隱藏細節(jié),對外暴露少量接口。劃分模塊的標準是高內(nèi)聚,低耦合,而不是以 Page 或是 View,一個模塊包含某些完整的業(yè)務(wù)功能,這些功能可能涉及到多個 Page 或多個 View。

所以回過頭,看我們的項目需求和 UI 圖,大體上可以分為三個模塊:

photos //旅游線路展示

videos //分享視頻展示

messages //站內(nèi)消息展示

這三個模塊顯而易見,但是我們注意到:“圖片詳情”和“視頻詳情”都包含“評論展示”,而“評論展示”本身又具有分頁、排序、詳情展示、創(chuàng)建回復(fù)等功能,它具有自已獨立的邏輯,只不過在 view 上被 photoDetail 和 videoDetail 嵌套了,所以將“評論展示”獨立劃分成一個模塊是合適的。

另個,整個程序應(yīng)當有個啟動模塊,它是“上帝視角模塊”,它可以做一些公共事業(yè),必要的時候也可以用來做多個模塊之間的協(xié)調(diào)和調(diào)度,我們叫把它叫做 applicatioin 模塊。

所以最終,本 Demo 被劃分為 5 個模塊:

app // 啟動模塊

photos //旅游線路展示

videos //分享視頻展示

messages //站內(nèi)消息展示

comments //評論展示

為模塊劃分 View

每個模塊可能包含一組 View,View 反映某些特定的業(yè)務(wù)邏輯。View 就是 React 中的 Component,那反過來 Component 就是 View 么?非也,它們之間還是有些區(qū)別的:

view 展現(xiàn)的是 Store 數(shù)據(jù),更偏重于表現(xiàn)特定的具體的業(yè)務(wù)邏輯,所以它的 props 一般是直接用 mapStateToProps connect 到 store。

component 體現(xiàn)的是一個沒有業(yè)務(wù)邏輯上下文的純組件,它的 props 一般來源于父級傳遞。

component 通常是公共的,而 view 通常非公用

回過頭,看我們的項目需求和 UI 圖,大體上劃分以下 view:

app views:Main、TopNav、BottomNav、LoginPop、Welcome、Loading

photos views:Main、List、Details

videos views:Main、List、Details

messages views:Main、List

comments views:Main、List、Details、Editor

目錄結(jié)構(gòu)

經(jīng)過上面的分析,我們有了項目大至的骨架,由于模塊比較少,所以我們就不再用二級目錄分類了:

src
├── asset // 存放公共靜態(tài)資源
│       ├── css
│       ├── imgs
│       └── font
├── entity // 存放業(yè)務(wù)實體TS類型定義
├── common // 存放公共代碼
├── components // 存放React公共組件
├── modules
│       ├── app
│       │     ├── views
│       │     │     ├── TopNav
│       │     │     ├── BottomNav
│       │     │     ├── ...
│       │     │     └── index.ts //導(dǎo)出給其它模塊使用的view
│       │     ├── model.ts //定義ModuleState和ModuleActions
│       │     ├── api //將本模塊需要的后臺api封裝一下
│       │     ├── facade.ts //導(dǎo)出本模塊對外的邏輯接口(類型、Actions、路由默認參數(shù))
│       │     └── index.ts //導(dǎo)出本模塊實體(view和model)
│       ├── photos
│       │     ├── views
│       │     ├── model.ts
│       │     ├── api
│       │     ├── facade.ts
│       │     └── index.ts
│       ├── videos
│       ├── messages
│       ├── comments
│       ├── names.ts //定義模塊名,使用枚舉類型來保證不重復(fù)
│       └── index.ts //導(dǎo)出模塊的全局設(shè)置,如RootState類型、模塊載入方式等
└──index.tsx 啟動入口
facade.ts

其它目錄都好理解,注意到每個 module 目錄中,有一個 facade.ts 的文件,冒似它與 index.ts 一樣都是導(dǎo)出本模塊,那為什么不合并成一個呢?

index.ts 導(dǎo)出的是整個模塊的物理代碼,因為模塊是較為獨立的,所以我們一般希望將整個模塊的代碼打包成一個獨立的 chunk 文件。

facade.ts 僅導(dǎo)出本模塊的一些類型和邏輯接口,我們知道 TS 類型在編譯之后是會被徹底抹去的,而接口僅僅是一個空的句柄。假如在 ModuleA 中需要 dispatch ModuleB 的 action,我們僅需要 import ModuleB 的 facade.ts,它只是一個空的句柄而以,并不會引起兩個模塊代碼的物理依賴。

配置模塊
問:在 react-coat 中怎么配置一個模塊?包括打包、加載、注冊、管理其生命周期等?

答:./src/modules 根目錄下的 index.ts 文件為模塊總的配置文件,增加一個模塊,只需要在此配置一下

// ./src/modules/index.ts

// 一個驗證器,利用TS類型來確保增加一個module時,相關(guān)的配置都同時增加了
type ModulesDefined = T;

// 定義模塊的加載方案,同步或者異步均可
export const moduleGetter = {
  [ModuleNames.app]: () => {
    return import(/* webpackChunkName: "app" */ "modules/app");
  },
  [ModuleNames.photos]: () => {
    return import(/* webpackChunkName: "photos" */ "modules/photos");
  },
  [ModuleNames.videos]: () => {
    return import(/* webpackChunkName: "videos" */ "modules/videos");
  },
  [ModuleNames.messages]: () => {
    return import(/* webpackChunkName: "messages" */ "modules/messages");
  },
  [ModuleNames.comments]: () => {
    return import(/* webpackChunkName: "comments" */ "modules/comments");
  },
};

export type ModuleGetter = ModulesDefined; // 驗證一下是否有模塊忘了配置

// 定義整站Module States
interface States {
  [ModuleNames.app]: AppState;
  [ModuleNames.photos]: PhotosState;
  [ModuleNames.videos]: VideosState;
  [ModuleNames.messages]: MessagesState;
  [ModuleNames.comments]: CommentsState;
}

// 定義整站的Root State
export type RootState = BaseState & ModulesDefined; // 驗證一下是否有模塊忘了配置
路由和加載

本 Demo 直接使用 react-router V4,路由即組件,所以并不需要什么特別的路由配置,直接在./app/views/Main.tsx 中:

const PhotosView = loadView(moduleGetter, ModuleNames.photos, "Main");
const VideosView = loadView(moduleGetter, ModuleNames.videos, "Main");
const MessagesView = loadView(moduleGetter, ModuleNames.messages, "Main");


  
  
  
  
  

使用 loadView()表示異步按需加載一個 View,如果你不想按需加載,完全可以直接 import:

import {Main as PhotosView} from "modules/photos/views"

載入 View 時自動載入其相關(guān)的模塊并初始化 Model。沒有 Model,view 是沒有“靈魂”的,所以在載入 View 時,框架會自動載入其 Model 并完成初始化,這個過程包含 3 步:

1.載入模塊對應(yīng)的 JS Chunk 包

2.初始化模塊 Model,派發(fā) module/INIT Action

3.模塊可以監(jiān)聽自已的 module/INIT Action,作出初始化行為,如獲取遠程數(shù)據(jù)等

Redux Store 結(jié)構(gòu)

module 的劃分不僅體現(xiàn)在工程目錄上,而體現(xiàn)在 Redux Store 中:

  router: { // 由 connected-react-router 生成
    location: {
      pathname: "/photos",
      search: "",
      hash: "#refresh=true",
      key: "gb9ick"
    },
    action: "PUSH"
  },
  app: {...}, // app ModuleState
  photos: { // photos ModuleState
    isModule: true, // 框架自動生成,標明該節(jié)點為一個ModuleState
    listSearch: { // 列表搜索條件
      title: "",
      page: 1,
      pageSize: 10
    },
    listItems: [ // 列表數(shù)據(jù)
      {
        id: "1",
        title: "新加坡+吉隆坡+馬六甲6或7日跟團游",
        departure: "無錫",
        type: "跟團游",
        price: 2499,
        hot: 265,
        coverUrl: "/imgs/1.jpg"
      },
      ...
    ],
    listSummary: {
      page: 1,
      pageSize: 5,
      totalItems: 10,
      totalPages: 2
    }
  },
  messages: {...}, // messages ModuleState
  comments: {...},  // comments ModuleState
}
具體實現(xiàn)

見 Demo 源碼,有注釋

美中不足 路由規(guī)劃的不足

到目前為止,本 Demo 完成了項目要求中的內(nèi)容,接下來,業(yè)務(wù)看了之后提出了幾個問題:

無法分享指定的“評論”,評論是很重要的吸引眼球的內(nèi)容,我們希望分享鏈接時,可以指定評論。

目前可以分享的路由只有 5 種:

- /photos
- /photos/1
- /videos
- /videos/1
- /messages

看樣子,我們得增加:

/photos/1/comments/3  //展示id為3的評論

評論內(nèi)容對以后的 SEO 很重要,我們希望路由能控制評論列表翻頁和排序:

/photos/1?comments-search={page:2,sort:"createDate"}

目前我們的項目主要用于移動瀏覽器訪問,很多 android 用戶習慣用手機下面的返回鍵,來撤消操作,如關(guān)閉彈窗等,能否模擬一下原生 APP?

思考:android 用戶點擊手機下面的返回鍵會引起瀏覽器的后退,后退關(guān)閉彈窗,那就需要在彈出彈窗時增加一條 URL 記錄
結(jié)論:Url 路由不只用來記錄展示哪個 Page、哪個 View,還得標識一些交互操作,完全顛覆了傳統(tǒng)的路由觀念了。

路由效驗的不足
看樣子,路由會越來越復(fù)雜,到目前為止,我們還沒有在 TS 中很好的管理路由參數(shù),拼接 URL 時沒有做 TS 類型的校驗。對于 pathname 我們都是直接用字符串寫死在程序中,比如:
if(pathname === "/photos"){
  ....
}

const arr = pathname.match(/^/photos/(d+)$/);

這樣直接 hardcode 似利不是很好,如果后其產(chǎn)品想換一下名稱怎么搞。

Model 中重復(fù)寫同樣的代碼

注意到,photos/model.ts、videos/model.ts 中,90%的代碼是一樣的,為什么?因為它們兩個模塊基本上功能都是差不多的:列表展示、搜索、獲取詳情...

其實不只是 photos 和 videos,套用 RestFul 的理念,我們用網(wǎng)頁交互的過程就是在對“資源 Resource”進行維護,無外乎“增刪改查”這些基本操作,大部分情況下,它們的邏輯是相似的。由其是在后臺系統(tǒng)中,基本上連 UI 界面也可以標準化,如果將這部分“增刪改查”的邏輯提取出來,模塊可以省去不少重復(fù)的代碼。

下一個 Demo

既然有這么多美中不足,那我們就期待在下一個 Demo 中一步步解決它吧

進階:SPA(單頁應(yīng)用)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/102211.html

相關(guān)文章

  • 【Geek議題】合理的VueSPA架構(gòu)討論(上)

    摘要:下面也是以模塊的模塊集為例,可以發(fā)現(xiàn)和路由有一些不同就是這里為了防止模塊跟全局耦合,運用函數(shù)式編程思想類似于依賴注入,將全局的實例作為函數(shù)參數(shù)傳入,再返回出一個包含的對象,這個導(dǎo)出的對象將會被以模塊名命名,合并到全局的集中。 前言 web前端發(fā)展到現(xiàn)代,已經(jīng)不再是嚴格意義上的后端MVC的V層,它越來越向類似客戶端開發(fā)的方向發(fā)展,已獨立擁有了自己的MVVM設(shè)計模型。前后端的分離也使前端人...

    worldligang 評論0 收藏0
  • 【Geek議題】合理的VueSPA架構(gòu)討論(上)

    摘要:下面也是以模塊的模塊集為例,可以發(fā)現(xiàn)和路由有一些不同就是這里為了防止模塊跟全局耦合,運用函數(shù)式編程思想類似于依賴注入,將全局的實例作為函數(shù)參數(shù)傳入,再返回出一個包含的對象,這個導(dǎo)出的對象將會被以模塊名命名,合并到全局的集中。 前言 web前端發(fā)展到現(xiàn)代,已經(jīng)不再是嚴格意義上的后端MVC的V層,它越來越向類似客戶端開發(fā)的方向發(fā)展,已獨立擁有了自己的MVVM設(shè)計模型。前后端的分離也使前端人...

    pepperwang 評論0 收藏0
  • 基于Redux架構(gòu)的單頁應(yīng)用開發(fā)總結(jié)

    摘要:系統(tǒng)架構(gòu)介紹本項目開發(fā)基于框架,利用進行模塊化構(gòu)建,前端編寫語言是,利用進行轉(zhuǎn)換。單頁是為單頁應(yīng)用量身定做的你可以把拆成很多,這些由路由來加載。前者用來獲取的狀態(tài),后者用來修改的狀態(tài)。 系統(tǒng)架構(gòu)介紹 本項目開發(fā)基于 React + Redux + React-Route 框架,利用 webpack 進行模塊化構(gòu)建,前端編寫語言是 JavaScript ES6,利用 babel進行轉(zhuǎn)換。...

    fish 評論0 收藏0
  • 拒絕Redux文檔“毒害” 一個項目告訴你Redux最新真正哲學

    摘要:之前分享過幾篇關(guān)于技術(shù)棧的原創(chuàng)文章解析前端架構(gòu)學習復(fù)雜場景數(shù)據(jù)設(shè)計干貨總結(jié)打造單頁應(yīng)用一個項目理解最前沿技術(shù)棧真諦一個工程實例今天進一步剖析一個實際案例移動網(wǎng)頁版。目前面臨的問題在于提高產(chǎn)品的各方面性能體驗。 之前分享過幾篇關(guān)于React技術(shù)棧的原創(chuàng)文章: 解析Twitter前端架構(gòu) 學習復(fù)雜場景數(shù)據(jù)設(shè)計 React Conf 2017 干貨總結(jié)1: React + ES next ...

    YuboonaZhang 評論0 收藏0
  • Reactjs、redux的從入門到放棄、刪庫跑路示例

    摘要:我的入門到放棄之路最近看到很多相關(guān)的問題跟討論,越來越多的小伙伴喜歡這個框架了,同時也在看到了有些入門的小伙伴遇到了各種各樣的問題,本人也是框架使用都一枚,公司是騰訊阿里平安三巨頭合資的一家公司,分別上海深圳杭州北京廣州等多個分部,前端人員 showImg(https://segmentfault.com/img/bVbhonB?w=1278&h=722); 我的react入門到放棄之...

    Miracle 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<