umi

螞蟻金服開源的企業(yè)級React框架,并不是UI框架

  1. 特性
    1. 開箱即用,內(nèi)置react、react-router ...
    2. 類似next.js且功能完備的路由約定呀枢,同時也支持手動配置路由的方式;
    3. 完善的插件體系笼痛,高性能裙秋,通過插件支持PWA、以路由為單元的code splitting等等缨伊;
    4. 支持靜態(tài)頁面導(dǎo)出残吩,適配各種環(huán)境,如中臺業(yè)務(wù)倘核、無線業(yè)務(wù)、egg即彪、支付寶錢包
    5. 開發(fā)啟動快紧唱,支持一鍵開啟dll
    6. 一鍵兼容IE9、基于umi-plugin-polyfills
    7. 支持TypeScript
    8. dva 數(shù)據(jù)流的深入融合隶校,支持duck directory漏益、model的自動加載、code splitting等等
  2. dva 是React應(yīng)用框架深胳,封裝了Redux绰疤、Redux-saga、React-router三個React工具庫舞终,目前React最流行的數(shù)據(jù)流解決放案轻庆;
數(shù)據(jù)流向.jpg
1. State:一個對象癣猾,保存整個應(yīng)用狀態(tài);
2. View:React組件構(gòu)成的視圖層余爆;
3. Action:一個對象纷宇,描述事件
4. connect():綁定```State```到```View```
5. dispatch():發(fā)送```Action```到```State```
  1. dvaumi的約定
    1. src源碼:pages(頁面)components(組件)蛾方、layout(布局)像捶、model(數(shù)據(jù)模型)
    2. config配置
    3. mock數(shù)據(jù)模擬
    4. test測試
  2. 全局安裝腳手架:npm i umi -g

初體驗

  1. 創(chuàng)建一個項目目錄:umidemo
  2. cd umidemo -> npm init:生成package.json
    "scripts": {
        "start": "umi dev",
        "build": "umi build"
    }
    
  3. 創(chuàng)建src目錄,生成pages目錄桩砰,默認使用約定式路由拓春;
    cd src
    umi g page index  // index.js和index.css
    umi g page about  // bout.js和about.css
    
  4. 運行項目:npm start,自動編譯生成頁面配置 /src/pages/.umi目錄亚隅,且項目是熱部署硼莽;
    http://localhost:8000/  --> index.js
    http://localhost:8000/about  --> about.js
    

約定式路由嵌套

  1. 當出現(xiàn) _layout.js 頁面時默認為父組件頁面,通過 {props.children} 顯示子組件內(nèi)容枢步;
  2. 嵌套路由:/users沉删,創(chuàng)建 pages/users 目錄
        umi g page users/_layout    // pages/users/_layout.js、_layout.css
    
    1. pages/users/_layout.js
       import styles from './_layout.css'
       export default function(props) {
           return (
               <div className={styles.normal}>
                   <h1>Page users _layout</h1>
                   <div>{props.children}</div>  //引入子組件醉途,如果沒有創(chuàng)建子組件矾瑰,則會報錯
               </div>
           )
       }
      
    2. _layout.js 創(chuàng)建子組件,users的首頁index.js
       umi g page users/index    // pages/users/index.js隘擎、index.css
      
    3. 訪問嵌套路由:http://localhost:8000/users
  3. 動態(tài)路由文件的命名以 $ 為開頭殴穴,users/$name.js對應(yīng)路由為/users/:name
    1. users目錄中,再創(chuàng)建$name.js货葬、$name.css
    2. 訪問$name.jshttp://localhost:8000/users/xxx
  4. 路由跳轉(zhuǎn)
    1. users/index.js
        import Link from 'umi/Link'
        export default function() {
            const userList = [
                { id:1, name:'Tim' }, { id:2, name:'Jerry' }
            ]
            return (<div>
                <ul>
                    { userList.map(item=>(
                        <li key={item.id}>
                            <Link to={`/users/${item.name}`}>{item.name}</Link>
                        </li>
                    )) }
                </ul>
            </div>)
        }
    
    1. users/$name.js
        export default function(props) {
            return (
                <div>
                    <h2>{props.match.params.name}</h2>
                    <button onClick={()=>props.history.goBack()}>返回</button>
                </div>
            )
        }
    
  5. src/layout目錄中的index.js將成為項目的定級布局頁面采幌,使用{props.children}顯示src/pages目錄中的組件。
    import styles from './index.css'
    export default function(props) {
        return (
            <div  className={styles.normal}>
                <p>我是項目的Layout布局</p>
                <div>{props.children}</div>
            </div>
        )
    }
    

配置式路由

  1. 配置式路由一旦創(chuàng)建震桶,約定式路由自動失效休傍,umi不會再自動創(chuàng)建路由;
  2. 在項目根目錄下創(chuàng)建 config 目錄蹲姐,并創(chuàng)建 config.js 文件磨取;
    export default {
        //路由配置:路徑相對于src/pages
        routes: [
            { path: "/", component: "./index" },
            { path: "/about", component: "./about" },
            { 
                path: "/users",
                component: "./users/_layout",
                routes: [
                    { path: "/users", component: "./users/index" },
                    { path: "/users/:name", component: "./users/$name" },
                ]
            },
            { component: "./notfound" }  // 404頁面,上面的所有路由都沒有匹配時柴墩,則匹配404頁面
        ]
    }
    
  3. 相應(yīng)地忙厌,創(chuàng)建404組件:umi g page notfound
  4. 路由守衛(wèi)
    1. 路由守衛(wèi)組件的路徑相對于項目根目錄,且后綴名不能省略江咳;
    2. 在項目根目錄下創(chuàng)建routes目錄逢净,用于存放路由守衛(wèi)組件;
    3. routes/PriviteRoute.js
        import Redirect from 'umi/redirect'
        export default function(props) {
            if(Math.random()>0.5) {
                return <Redirect to="/login" /> //沒有登錄時,重定向到登錄頁
            }
            //登錄成功時爹土,顯示子路由的頁面組件
            return <div>{props.children}</div>
        }
    
    1. 創(chuàng)建登錄頁umi g page login
    2. config/config.js中配置/login甥雕,并守衛(wèi)/about
        routes: [
            { path: "/login", component: "./login" },
            { 
                path: "/about", 
                component: "./about",
                Routes: ["./routes/PriviteRoute.js"]  //路由守衛(wèi)
            },
            ......
        ]
    
  5. 引入ant design UI庫
    npm i antd -S
    npm i umi-plugin-react -D
    
    1. config/config.js
        export default {
            //路由配置
            routes: [...],
            //插件配置
            plugins: [
                [ "umi-plugin-react", { antd: true } ]
            ]
        }
    
    1. 使用時需要導(dǎo)入組件,因為是按需加載
        import {Button} from 'antd'
        
        <Button type="primary">Primary</Button>
    

引入dva

  1. dva主要是軟件分層的概念
    1. Page負責與用戶直接交互:渲染頁面着饥、接收用戶的操作輸入犀农,側(cè)重于展示型和交互邏輯;
    2. Model負責處理業(yè)務(wù)邏輯宰掉,可以理解成一個維護頁面數(shù)據(jù)狀態(tài)的對象呵哨,為Page做數(shù)據(jù)、狀態(tài)的讀寫等操作轨奄;
        export default {
            namespace: 'goods',  // model的命名空間孟害,區(qū)分多個model
            state: [],  //初始狀態(tài)
            effects: {  //異步操作
            },
            reducers: {}
        }
    
    1. Service主要負責與HTTP做接口對接,跟后端做數(shù)據(jù)交互挪拟,讀寫數(shù)據(jù)挨务;
  2. dva已經(jīng)融合進了umi,在 config/config.js 中打開dva的開關(guān)
    plugins: [
        [ "umi-plugin-react", { antd: true, dva: true } ]
    ]
    

基本用法

    umi g page goods
    npm i axios -S
  1. 路由配置:config/config.js
    routes: [
        { path: "/goods", component: "./goods" },
    ]
    
  2. 在項目根目錄下創(chuàng)建mock/goods.js玉组,模擬接收請求谎柄,響應(yīng)數(shù)據(jù)
    let data = [
        { title: '單頁面' },
        { title: '管理項目' },
    ],
    export default {
        //method url
        "get /api/goods": function(req, res) {
            setTimeout(()=>{
                res.json({result: data})
            }, 1000);
        }
    }
    
  3. Modelsrc/models/goods.js
    import axios from 'axios'
    //調(diào)接口的邏輯應(yīng)該放在 Service 層
    function getGoods() {
        return axios.get('/api/goods')
    }
    
    export default {
        namespace: 'goods',    // 命名空間,如果省略惯雳,則以文件名作為命名空間
        state: [],
        effects: {
            *getList(action, {call, put}) {  //異步操作
                //發(fā)起請求
                const res = yield call(getGoods)
                //派發(fā)異步action: initGoods 
                yield put({type:'initGoods', payload:res.data.result})
            }
        },
        reducers: {
            initGoods(state, action) {
                return action.payload
            },
            addGood(state, action) {  // 添加函數(shù)朝巫,返回一個新的state
                return [...state, {title: action.plyload.title}]
            }
        }
    }
    
  4. pages/goods.js
    import {connect} from 'dva'
    import React, {Component} from 'react'
    
    @connect(
        state=>({
            goodList: state.goods,  // 從指定命名空間內(nèi)獲取state
            loading: state.loading,  // 通過loading命名空間獲取加載的狀態(tài)
        }),
        {
            getList: ()=>{
                {type: 'goods/getList'}  // action的type需要以命名空間為前綴,后跟reducer
            },
            addGood: title=>({
                type:'goods/addGood',
                payload: {title}
            })
        }
    )
    export default class extends Component {
        componentDidMount() {
            this.props.getList()  //觸發(fā)事件石景,發(fā)起請求劈猿,獲取數(shù)據(jù)
        }
        render() {
            if(this.props.loading.models.goods) {
                //命名空間goods 的請求在加載中
                return <div>loading</div>
            }
            return (
                <div>
                    <ul>
                    {
                        this.props.goodList.map((good, index)=>{
                            return <li key={index}>{good.index}</li>
                        })
                    }
                    </ul>
                    <button onClick={()=>this.props.addGood("商品3")}>添加</button>
                </div>
            )
        }
    }
    

升級路由守衛(wèi)

  1. 在項目根目錄下創(chuàng)建mock/login.js
    export default {
        "post /api/login": function(req, res) {
            const {username, password} = req.body
            if(username==="xxx"&&password==="xxx") {
                return res.json({code: 200, data: {token:"xxxx", name:"xxx"}})
            }
            return res.json({code: 404, info:"登錄失敗"})
        }
    }
    
  2. Modelsrc/models/login.js
    import axios from 'axios'
    import router from 'umi/router'
    
    //初始的state
    const initUserInfo = {
        token: "",
        name: ""
    }
    //調(diào)接口的邏輯應(yīng)該放在 Service 層
    function login(data) {
        return axios.post('/api/login', data)
    }
    
    export default {
        namespace: 'login',
        state: initUserInfo,
        effects: {  //異步操作
            *login(action, {call, put}) {
                //發(fā)起請求
                const res = yield call(login, action.payload)
                if(res.data.code===200) {  //登錄成功
                    //派發(fā)異步action
                    yield put({type:'init', payload:res.data.data})
                    //登錄成功,跳轉(zhuǎn)去首頁
                    router.push('/')
                } else {
                    console.log("登陸失敗!")
                }
            }
        },
        reducers: {
            init(state, action) {
                return action.payload
            }
        }
    }
    
  3. pages/login.js
    import {connect} from 'dva'
    import React, {Component} from 'react'
    
    @connect()
    export default class extends Component {
        onSubmit() {
            //派發(fā)action潮孽,發(fā)起請求
            this.props.dispatch({type:"login/login", payload:{username:"xxx",password:"xxx"}})
        }
        render() {
            return (
                <div>
                    <button onClick={onSubmit}>登錄</button>
                </div>
            )
        }
    }
    
  4. routes/PriviteRoute.js
    import React,{Component} from 'react'
    import Redirect from 'umi/redirect'
    import {connect} from 'dva'
    @connect(
        state => ({token:state.login.token})  //從指定命名空間內(nèi)獲取state
    )
    export default class extends Component {
        render() {
            if(this.props.token) {
                //登錄成功時揪荣,顯示子路由的頁面組件
                return <div>{this.props.children}</div>
            }
            //沒有登錄時,重定向到登錄頁
            return <Redirect to="/login" />
        }
    }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末往史,一起剝皮案震驚了整個濱河市仗颈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌椎例,老刑警劉巖挨决,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異粟矿,居然都是意外死亡,警方通過查閱死者的電腦和手機损拢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門陌粹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人福压,你說我怎么就攤上這事掏秩』蛭瑁” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵蒙幻,是天一觀的道長映凳。 經(jīng)常有香客問我,道長邮破,這世上最難降的妖魔是什么诈豌? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮抒和,結(jié)果婚禮上矫渔,老公的妹妹穿的比我還像新娘。我一直安慰自己摧莽,他們只是感情好庙洼,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镊辕,像睡著了一般油够。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上征懈,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天石咬,我揣著相機與錄音,去河邊找鬼受裹。 笑死碌补,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的棉饶。 我是一名探鬼主播厦章,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼照藻!你這毒婦竟也來了袜啃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤幸缕,失蹤者是張志新(化名)和其女友劉穎群发,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體发乔,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡熟妓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了栏尚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片起愈。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抬虽,到底是詐尸還是另有隱情官觅,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布阐污,位于F島的核電站休涤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏笛辟。R本人自食惡果不足惜功氨,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望隘膘。 院中可真熱鬧疑故,春花似錦、人聲如沸弯菊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽管钳。三九已至钦铁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間才漆,已是汗流浹背牛曹。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留醇滥,地道東北人黎比。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像鸳玩,于是被迫代替她去往敵國和親阅虫。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容