UmiJS簡單介紹及使用UmiJS開發結構淺析


   UmiJS官方文檔:https://umijs.org/zh-CN

一、什么是Umi.js

  umi,中文可發音為烏米,是一個可插拔的企業級 react 應用框架。你可以將它簡單的理解為一個專注性能的類 next.js 前端框架,並通過約定、自動生成和解析代碼等方式來輔助開發,減少我們開發者的代碼量。

  其特點:

1、插件化umi的整個生命周期都是插件化的,甚至其內部實現就是由大量插件組成,比如:pwa、按需加載、一鍵切換 preact、一鍵兼容 ie9 等等,都是由插件實現。

2、開箱即用 :你只需一個umi依賴就可啟動開發,無需安裝reactpreactwebpackreact-routerbabeljest等等。

3、約定式路由 :類似next.js的約定式路由,無需再維護一份冗余的路由配置,支持權限、動態路由、嵌套路由等等。

二、為什么使用Umi.js?

  我們做react開發的時候會不會遇到以下問題?:

1、項目做大的時候,開發調試的啟動和熱更新時間會變得很長。

2、大應用下,網站打開很慢,有沒有辦法基於路由做到按需加載。

3、dva的model每次都要手寫載入,能否一開始就同項目初始化好?

  使用烏米,即可解決以上問題,並且還能提供如下優勢:

  • 開箱即用,內置 react、react-router 等
  • 類 next.js 且功能完備的路由約定,同時支持配置的路由方式
  • 完善的插件體系,覆蓋從源碼到構建產物的每個生命周期
  • 一鍵兼容到 IE9
  • 完善的 TypeScript 支持
  • 與 dva 數據流的深入融合

三、UmiJS快速上手

1、node環境安裝:nodeJS版本需>=8.10

2、需要全局安裝Umi:

npm install -g umi
# 使用yarn安裝umi yarn global add umi

(1)如果你要使用 yarn 安裝,那就需要先安裝 yarn:

  可以把yarn看做了優化了的npm,其中tyarn使用的是npm.taobao.org的源,速度要快一些。平常使用的話使用tyarn即可。

npm i yarn tyarn -g --registry=https://registry.npm.taobao.org

  然后使用tyarn安裝umi

tyarn global add umi

(2)查看是否安裝成功:umi -v

  如果出現 'umi' 不是內部或外部命令,也不是可運行的程序 或批處理文件或者提示 umi: command not found

  解決方案:

3、快速上手:先新建個空目錄myapp

# 新建應用 $ mkdir myapp && cd myapp # 新建頁面 $ umi generate page index # 本地開發 $ umi dev # 構建上線 $ umi build

4、如果是拿到別人的項目是使用 Umi 開發的,那么需要怎么做?

(1)首先,全局安裝 UmiJS:npm install -g umi

(2)其次,安裝項目所需依賴:npm install

(3)其次,啟動項目:根據 package.json 里的啟動命令 : umi dev

四、項目工程目錄簡介

  一個簡單的demo工程,列下工程下的文件功能描述

public                  // 公共文件 可以放一些第三方字體 樣式庫等
mock                    // mock文件
src |-- components        // 公共組件目錄 當業務需要拆分組件的時候,可以在對應的業務文件夾下單獨創建一個components文件夾
  |-- layouts           // 項目結構文件
  |-- locales           // 規划文件
  |-- models            // 公共model存放位置
    |-- public.js       // 公共model文件 可以多個
  |-- services          // 公共api存放
  |-- pages             // 容器組件
    |-- demo-umi            // 業務容器 相對路由/demo ***不可以有任何大寫字母
      |-- index.js      // 業務入口 入口文件只識別index.js 后綴必須是js
      |-- index.less    // 業務樣式
      |-- modules       // 業務model目錄
        |-- demo-m.js     // 業務model文件 可以有多個 自動加載
      |-- service       // 業務api目錄
        |-- demo-s.js     // 業務api文件 可以有多個
  |-- utils             // 工具
  |-- theme |-- cofnig.js       // 覆蓋antd樣式文件
    |-- vars.less       // 全局變量
  |-- global.less       // 公共樣式 覆蓋樣式
.eslintignore           // eslint過濾文件清單
.eslintrc.js            // eslint配置
.gitignore package.json README.md 

1、約定式路由

  啟動 umi dev 后,大家會發現 pages 下多了個 .umi 的目錄。

  請不要直接在這里修改代碼,umi 重啟或者 pages 下的文件修改都會重新生成這個文件夾下的文件,約定 pages 下所有的 (j|t)sx? 文件即路由

五、使用 dva

  在 umi 項目中,你可以使用 dva 來處理數據流,以響應一些復雜的交互操作。

  在 umi@^2 中要使用 dva 的功能很簡單,只要使用 umi-plugin-react 插件並配置 dva:true 即可。

  修改配置的文件:./umirc.js
// ref: https://umijs.org/config/
export default { plugins: [ // ref: https://umijs.org/plugin/umi-plugin-react.html
    ['umi-plugin-react', { antd: true, dva: true, // 在此處啟用 dva
      dynamicImport: false, title: 'hero', dll: false, routes: { exclude: [], }, hardSource: false, }], ], }

  在dva中,處理數據流的文件統一放在 models 文件夾下,每一個文件默認導出一個對象,里面包含數據和處理數據的方法,通常我們稱之為 model 。

  如以下count.js,model結構一般是如此:

// ./src/models/count.js
export default { namespace: 'count', // 默認與文件名相同
  state: 'count', subscriptions: { setup({ dispatch, history }) { }, }, reducers: { update(state) { return `${state}_count`; }, }, effects: { *fetch({ type, payload }, { put, call, select }) { }, }, }

  關於reducers,effects,subscriptions的詳細介紹,可參考dva.js官方文檔:Dva.js

1、在項目頁面中使用model

  我們需要導入connect將頁面和model綁定在一起。

import { connect } from 'dva'; function CountPage(props) { //從props屬性中打印namespace為count的model的state數據 
 console.log(props.count); return ( <div className={styles.normal}>
      <h1>數量大小</h1>
      <h2>This is {props.count}</h2>
    </div> ); } export default connect(({ count }) => ({ count }))(CountPage);  

  如果使用es7的裝飾器,我們可以改成這樣的寫法:

import { connect } from 'dva'; // 裝飾器 
@connect(({ count }) => ({ count })) function CountPage(props) { //從props屬性中打印namespace為count的model的state數據 
 console.log(props.count); return ( <div className={styles.normal}>
      <h1>數量大小</h1>
      <h2>This is {props.count}</h2>
    </div> ); } export default CountPage;

六、分層開發

1、過程圖示

  上圖中,左邊是用戶,中間為前端,右邊為后端。我們對前端進行分層,可以分為PageModelService3層。

(1)Page 負責與用戶直接打交道:渲染頁面、接受用戶的操作輸入,側重於展示型交互性邏輯。

(2)Model 負責處理業務邏輯,為 Page 做數據、狀態的讀寫、變換、暫存等。

(3)Service 負責與 HTTP 接口對接,進行純粹的數據讀寫。

  其中

  Page層通過UmiJSumi-plugin-react插件的dva功能,可以調用Model層定義的數據和方法;

  Model層通過import定義的異步請求函數request.js來調用Service層;

  而Service層就是去后端請求數據。

 2、開始開發

// 1、添加umi的依賴
tyarn add umi --dev // 2、添加umi-plugin-react插件
tyarn add umi-plugin-react --dev // 3、添加.gitignore文件
node_modules dist .umi // 4、在config/config.js配置中引入umi-plugin-react插件
export default { plugins: [ ['umi-plugin-react', { dva: true, antd: true }] ] }; 

3、antd基本布局

  添加基本布局和樣式:在layouts文件目錄下創建index.js文件,在index.js中我們寫入:

import { Component } from 'react'; import { Layout } from 'antd'; // Header, Footer, Sider, Content組件在Layout組件模塊下
const { Header, Footer, Sider, Content } = Layout; class BasicLayout extends Component { render() { return ( <Layout>
                <Sider width={256} style={{ minHeight: '100vh', color: 'white' }}> Sider </Sider>
                <Layout >
                    <Header style={{ background: '#fff', textAlign: 'center', padding: 0 }}>Header</Header>
                    <Content style={{ margin: '24px 16px 0' }}>
                        <div style={{ padding: 24, background: '#fff', minHeight: 360 }}> {this.props.children} </div>
                    </Content>
                    <Footer style={{ textAlign: 'center' }}>Ant Design ©2018 Created by Ant UED</Footer>
                </Layout>
            </Layout> ) } } export default BasicLayout;

  上面代碼中,我們創建了一個三部分的基本布局:Header 、Content 、Footer。

  然后我們將 Content 替換成 { this.props.children },這樣之后我們設置的路由會通過替換 children 變量實現內容的切換。

  上面需要引入的組件都安裝好了之后,我們就可以來編寫我們的前端代碼了。

  我們可以根據上面的圖,按照從下到上的順序進行編寫,也就是ServiceModelPage來進行。

4、Service異步請求數據

  在 src 目錄下創建 utils 目錄, 創建 request.js 文件

function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } const error = new Error(response.statusText); error.response = response; throw error; } /** * Requests a URL, returning a promise. * * @param {string} url The URL we want to request * @param {object} [options] The options we want to pass to "fetch" * @return {object} An object containing either "data" or "err" */ export default async function request(url, options) { const response = await fetch(url, options); checkStatus(response); return await response.json(); } 

5、Mock模擬數據

  在 mock目錄下創建UserList.js文件,用來模擬數據。因為有了umi默認集成了mock功能,所以只要編寫mock數據即可。

export default { 'get /ds/list' : function (req, res) { res.json({ data: ['zhangsan','lisi','wangwu'] }) } } 

6、Model層中引入該 js 文件用於異步請求

import request from '../../utils/request' export default { namespace: 'user', //該模型中的一些屬性
 state: { data: [] }, //一些正常的同步方法
 reducers: { //state是原先的數據,result是effets中異步調用返回的數據
 save(state, result){ //如果 result.data中存在數據,表示該函數是被異步調用初始化。直接返回
            if (result.data){ return result.data; } let list = [...state.data, 'freeman']; //返回更新后的state對象
 return { data: list } } }, effects: { // 這里定義異步方法
        *initData(params, sagaEffects) { //定義異步方法
            const {call, put} = sagaEffects; //獲取到call、put方法
            const url = "/ds/list"; // 定義請求的url
            let result = yield call(request, url); //執行請求
            yield put({ // 調用reducers中的方法
                type : "save", //指定方法名
                data : result  //傳遞ajax回來的數據, 注意 put 會指定調用的同步方法[reducers 中定義的方法], //該調用的方法會在定義的方法的入參添加一個參數(result), 使用該參數才能獲取到put方法,取到的值
 }); } } }

  DVA 的 model 對象有幾個基本的屬性,需要大家了解。

  • namespacemodel 的命名空間,只能用字符串。一個大型應用可能包含多個 model,通過namespace區分。
  • state:當前 model 狀態的初始值,表示當前狀態。
  • reducers:用於處理同步操作,可以修改 state,由 action 觸發。reducer 是一個純函數,它接受當前的 state 及一個數據體(payload)作為入參,返回一個新的 state
  • effects:用於處理異步操作(例如:與服務端交互)和業務邏輯,也是由 action 觸發。但是,它不可以修改 state,要通過觸發 action 調用 reducer 實現對 state 的間接操作。
  • action:是 reducerseffects 的觸發器,一般是一個對象,形如{ type: 'add', payload: todo },通過 type 屬性可以匹配到具體某個reducer 或者 effectpayload 屬性則是數據體,用於傳送給 reducereffect

7、Page層引入Model層的數據和方法

  dva是基於 redux、redux-saga 和 react-router 的輕量級前端框架,官網:https://dvajs.com/

(1)@connect(mapStateToProps, mapDispatchToProps) 需要2個參數:

  mapStateToProps:是一個方法,該方法的返回值是一個屬性對象{},它的作用是將這個包含state屬性的對象注入到this.props中。組件通過this.props.xx的方式即可獲取到model中的數據。

  • umi框架啟動,會自動讀取models目錄下所有model文件,(如:user/List.js中的數據 )
  • 這些model數據 會進入到mapStateToProps方法中
  • 在全局的數據中,會有很多,所以需要通過namespace進行區分,所以通過state[namespace]進行獲取數據
  • 拿到model數據中的data,也就是['zhangsan','lisi','wangwu']數據,進行包裹{}后返回
  • 返回的數據,將被封裝到this.props中,所以通過this.props.listData即可獲取到 model中的數據。

(2)mapDispatchToProps: 是一個方法,它的返回值是一個函數對象{}, 它的作用是將這些函數注入到this.props中。

  • 所以可以把Model中暴露的方法綁定到當前的組件中,定義一個方法接收,然后可以綁定到onClick事件上,就可以實現點擊操作;也可以在頁面加載完的時候拿到數據對頁面進行渲染的操作(只需要綁定到生命周期函數上即可)。
  • dispatch 函數就是和 dva model 打交道的唯一途徑。 dispatch 函數接受一個 對象 作為入參,在概念上我們稱它為 action,唯一強制要包含的是 type 字段,string 類型,用來告訴 dva 我們想要干什么。我們可以選擇給 action 附着其他字段,這里約定用 payload字段表示額外信息。

  在 pages目錄下新建user/List.js(或List.jsx)頁面,使用快捷鍵rccrccp可以快速生成react組件。

import React, {Component} from 'react'; import { connect } from 'dva'; const namespace = 'user'; const mapStateToProps = (state)=>{ let listData = state[namespace].data; return {listData} }; const mapDispatchToProps = (dispatch) => { // 定義方法,dispatch是內置函數
    return { //返回的這個對象將綁定到this.props對象中
        addUser : () =>{ // 定義方法
            dispatch({ // 通過調用dispatch()方法,調用model中reducers的方法
                type: `${namespace}/save` // 指定方法,格式: namespace/方法名
 }); }, userList : () => { //新增初始化方法的定義
 dispatch({ type: `${namespace}/initData`
 }); } } } @connect(mapStateToProps, mapDispatchToProps) class List extends Component { componentDidMount() { this.props.userList(); } render() { return ( <div>
                <ul> { this.props.listData.map( (v, i) => {return <li key={i}>{v}</li>}
 ) } </ul>
                <button onClick={() => {this.props.addUser()}}> 添加 </button>
            </div>
 ); } } export default List; 

8、umi-plugin-react插件升級

  在運行 umi devumi build 運行或部署應用是,有時候會出現 “Path must be a string”錯誤。

  解決方法:按照官網升級umi-plugin-react的版本。

// 1、 package.json文件
 { "devDependencies": { -   "umi-plugin-react": "^1"
+   "@umijs/preset-react": "^1" } } // 2、config/config.js文件
 export default { - plugins: [ -   ['umi-plugin-react', { - dva: {}, - antd: {}, - ... - }] - ], + dva: {}, + antd: {}, + ... }

  可以參考:https://umijs.org/docs/upgrade-to-umi-3#升級-umi-plugin-react-為-umijspreset-react

參考文章:https://www.cnblogs.com/zhaoxxnbsp/p/12672652.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM