react+webpack4搭建前端項目(二)react全家桶的使用

接着上一篇react+webpack4搭建前端項目(一)咱們正式進入react全家桶技術篇章,若是對於項目不清楚或者在下面有什麼疑惑,建議先看一下上一篇文章熟悉一下項目由來css

使用react-router-dom管理路由,這裏使用react-router4.x之後的版本,請注意,和3.x的使用仍是有很大的區別

npm install -S react-router-dom
複製代碼

咱們爲何使用react-router-dom呢?html

先簡單說下各自的功能: react-router: 實現了路由的核心功能 react-router-dom: 基於react-router,加入了在瀏覽器運行環境下的一些功能,例如:Link組件,會渲染一個a標籤,BrowserRouterHashRouter組件。顯而易見react-router-dom功能更豐富,因此選擇react-router-dom代替react-router前端

下面接着上一篇文章的項目,咱們對項目進行改造: 新建blog,resume,user頁面,以下java

QQ截圖20190813113835.png
分別編寫 blog,resume,user,home(上篇文章已完成)組件

此處刪除home/index.less,修改home/index.js內容:node

import React from 'react'
export default class HomeIndex extends React.Component {
    render(){
        return (
            <div>
                <p>HomeIndex</p>
            </div>
        )
    }
}
複製代碼

blog/index.jsreact

import React from 'react'

export default class BlogIndex extends React.Component {
    render(){
        return (
            <div>
                <p>BlogIndex</p>
            </div>
        )
    }
}
複製代碼

resume/index.jswebpack

import React from 'react'

export default class ResumeIndex extends React.Component {
    render(){
        return (
            <div>
                <p>ResumeIndex</p>
            </div>
        )
    }
}
複製代碼

user/index.jsios

import React from 'react'

export default class UserIndex extends React.Component {
    render(){
        return (
            <div>
                <p>UserIndex</p>
            </div>
        )
    }
}

複製代碼

以上這些組件是最簡單的組件git

新建src/router.jsgithub

import React from "react"
import { Route,BrowserRouter,Link,Switch } from "react-router-dom"
import HomeIndex from "./home"
import BlogIndex from "./blog"
import ResumeIndex from "./resume"
import UserIndex from "./user"
class AppRouter extends React.Component {
    render(){
        return (
            <BrowserRouter>
                <ul>
                    <li><Link to="/home">home</Link></li>
                    <li><Link to="/blog">blog</Link></li>
                    <li><Link to="/resume">resume</Link></li>
                    <li><Link to="/user">user</Link></li>
                </ul>
                <div>
                    {/* Switch只顯示一個組件。加exact表示精確匹配/。若是不加exact,/xxx也會匹配/。  */}
                    <Switch>
                        {/* exact */}
                        <Route path="/home" component={HomeIndex} />
                        <Route exact path="/blog" component={BlogIndex}/>
                        <Route exact path="/resume" component={ResumeIndex}/>
                        <Route exact path="/user" component={UserIndex}/>
                    </Switch>
                </div>
            </BrowserRouter>
        )
    }
}
export default AppRouter;
複製代碼

這裏使用react-router-dom的history模式,簡單寫了一個導航,點擊每一個導航,跳轉到相應的頁面。運行npm run dev,打開http:localhost:8081,效果如圖

1565673235831.gif

到此react-router-dom基本使用已經完成。

由於咱們這裏是配合項目使用,詳細的react-router-dom不過多講解,若是想學習更多基本用法,請查看官方文檔。在後邊隨着項目的複雜,後邊咱們還會說一下嵌套路由,頁面之間的跳轉等等使用方法。

最後咱們須要清除頁面,標籤的默認樣式。代碼能夠去網上找一份,網上隨處可見。 而後在項目根目錄新建static/css/reset.min.css,在index.html模板引入

<link rel="stylesheet" href="/static/css/reset.min.css">
複製代碼

從新運行,你會發現找不到/static/css/reset.min.css。由於這裏只是在index.html中引入了文件,可是並無在webpack中處理靜態文件,咱們須要把static目錄的內容經過webpack插架 編譯構建到包裏;此處須要用到copy-webpack-plugin

npm install -D copy-webpack-plugin
複製代碼

在build/webpack.base.config.js`中添加公用的插件plugins,

plugins:[
    new CopyWebpackPlugin([
        {
            from: utils.resolve('../static'),  // 從哪一個目錄copy
            to: "static", // copy到那個目錄
            ignore: ['.*']
        }
    ])
]
複製代碼

從新運行,你會發現默認樣式清除了!

引入antd

使用教程 這裏已經很詳細了,本項目使用的是按需加載方式,能夠減少打包體積。

使用antd+react-router-dom封裝導航組件

咱們先看一下寫出來項目目錄

QQ截圖20190815182309.png

因爲代碼量愈來愈大,這裏再也不給出詳細代碼,若是須要請點擊 源碼 下載 release 1.0.0 版本

下面講一下這些目錄的用途

一、assets是資源目錄,放圖片,css,js,字體等等 二、blog是博客模塊的頁面 pages目錄是blog模塊下的頁面組件,在這裏新建了兩個頁面add.js添加博客,list.js博客列表 index.js是管理bolg模塊的子路由的組件,代碼以下

import React from 'react'
import BlogListPage from "./pages/list"
import AddBlogPage from "./pages/add"

import { Route } from 'react-router-dom'

export default class BlogIndex extends React.Component {
    render(){
        return (
            <div>
                <p>BlogIndex</p>
                <Route path="/blog/list" component={BlogListPage} />
                <Route path="/blog/add" component={AddBlogPage} />
            </div>
        )
    }
}
複製代碼

這裏使用<Route path="/blog/add" component={AddBlogPage} />添加二級子路由,可是要注意,第一級路由是不要加exact這個屬性,這個屬性表示精確匹配。若是父級路由加了這一屬性,子路由就會匹配不到。

舉個栗子: <Route exact={true} path="/blog" component={BlogIndex}>若是這麼寫,當你輸入/blog/add路徑,會匹配不到任何路由。只有當你輸入/blog路徑時纔會匹配。能夠利用模糊匹配路徑方式實現多級路由的管理。

三、home目錄暫時沒用 四、layout整個項目的公用佈局組件 NavigationBar.js是上邊的導航,SlideMenu.js是側邊菜單 五、resume是當即模塊,沒有實現二級路由 六、user用戶管理模塊,和blog的目錄結構同樣,實現二級路由 七、app.less是項目公用的樣式文件,這裏寫了導航和側邊欄的樣式 八、router.config.js是項目的路由和左側菜單 九、router.js是項目的路由和總體的佈局

實現的效果圖:

QQ截圖20190816085339.png

注意事項: 一、你會發現本項目html標籤使用class屬性來代替className屬性。react自己的html標籤是不支持class屬性,只識別className屬性編寫類名。這裏咱們須要安裝一個插件

npm install -D babel-plugin-react-html-attrs
複製代碼

而後再.babelrc文件的pulgins數組添加"react-html-attrs"便可

二、咱們此處用的class組件來編寫react組件,若是有須要也可使用function組件來編寫react組件。當咱們使用class的時候,再class添加屬性時,也就是下邊的寫法,項目在編譯運行時報錯

export default class BlogIndex extends React.Component {
    state = {
        test:"name"
    }
    click = ()=>{   
    }
}
複製代碼

報錯以下:

QQ截圖20190813152902.png

解決方法是:

npm install -D @babel/plugin-proposal-class-properties
複製代碼

而後再.babelrc文件的pulgins數組添加"@babel/plugin-proposal-class-properties"

使用mobx管理數據

在react中使用mobx,不只須要使用mobx,還須要結合react的插件,那就是mobx-react。 第一先安裝這兩個必須包

npm install -S mobx mobx-react
複製代碼

mobx的基本用法請看這裏mobx mobx-reactmobxreact的結合,提供Provider組件統一管理mobx數據;injectreact組件注入某個mobx實例;observer實現mobx實現react組件和mobx數據的雙向綁定(和react-reduxconnect差很少)等等

建立mobx實例並在react入口文件引入

咱們這裏在user目錄下先建store/UserList.js,建立管理用戶列表頁面的mobx實例

import { observable,action } from "mobx"

class UserListStore {

    @observable name;

    constructor(){
        this.name = "my name is user list;";
    }
    
}

export default new UserListStore();
複製代碼

下面咱們在src下新建store/index.js目錄,統一管理項目的mobx實例:

import UserListStore from "./../user/store/UserList"

const store = {
    UserListStore
}

export default store;
複製代碼

在修改src/index.js,導入文件

import { Provider } from "mobx-react"
import store from "./store"
複製代碼

使用Providerstore

<Provider {...store}>
    <AppRouter />
</Provider>
複製代碼

從新運行項目,不出所料報錯。爲何呢?熟悉mobx的同窗應該都知道,mobx的特點是使用裝飾器來來修飾mobx實例中屬性和方法,以及react-mobx也是經過裝飾器來使用。

裝飾器能夠經過@關鍵字加上相關的方法。來達到爲屬性,方法,class添加其它功能的做用。裝飾器做用的做用其實用很大,好比javaspring運用最普遍,想學習的同窗能夠去查相關資料。

咱們須要在項目配置對裝飾器的支持

安裝npm install -D @babel/plugin-proposal-decorators,在.babelrc文件的pulgins數組添加

["@babel/plugin-proposal-decorators",{"legacy": true}], // 配置對裝飾器的支持
複製代碼

"@babel/plugin-proposal-class-properties"修改爲

["@babel/plugin-proposal-class-properties",{"loose":true}] // 支持類屬性的插件
複製代碼

注意這項配置必定要在@babel/plugin-proposal-decorators以後,否則仍是同樣會報錯。

react組件中使用mobx

經過inject把須要的mobx實例注入到react組件

修改src/user/pages/list.js

import React from 'react'
import {withRouter} from 'react-router-dom'
import {Button} from "antd"
import { inject, observer } from "mobx-react"

@inject("UserListStore")
class UserListPage extends React.Component {

    push = ()=>{
        this.props.history.push("/user/add?name=231");
    }
    render(){
        const {UserListStore} = this.props;
        return (
            <div>
                <p>UserListPage</p>
                <p>組件:{UserListStore.name}</p>
                <Button onClick={this.push}>添加用戶</Button>
            </div>
        )
    }
}

export default withRouter(UserListPage);

複製代碼

發現mobx中的UserListStore實例注入到this.props

QQ截圖20190816104214.png

使用observer實現組件和數據的雙向綁定 在class組件使用@observer修改組件,添加setName方法

setName = ()=>{
    const {UserListStore} = this.props;
    UserListStore.setName("ha ha ha")
}
複製代碼

添加一個修更名稱的按鈕

<Button onClick={this.setName}>修更名字</Button>
複製代碼

點擊按鈕,你會發現{UserListStore.name}變成了ha ha ha。這說明組件個數據雙向綁定已經成功

測試打包,一切正常!

引入mobx代碼請下載 源碼 releases 1.0.1

搭建mock服務(node)

爲了更好的模擬先後端分離場景,新搭建一個服務

項目根目錄建立mock目錄,這裏使用koa搭建一個node服務。koa搭建node服務比較簡單,這裏就不說怎麼去搭建node服務了。若是有須要能夠看我以前寫的react項目整合express+mock實現模擬接口數據,這裏只是express框架換成了koa框架。而後cd mock,執行npm run dev,服務正常啓動。端口好是8082。

重點說一下mock,這裏咱們使用mockjs模擬數據,須要在mock目錄安裝mockjs

npm install -S mockjs
複製代碼

目錄結構:

QQ截圖20190816144713.png

咱們新建一個user模塊。user/data.js模擬數據庫的記錄。這裏模擬產生10條用戶記錄,只要生成了,就不會發生變化 user/data.js

const Mock = require("mockjs")

const data = Mock.mock({
    'list|1-10': [{
        'id|+1': 1,
        'user_id|100-200': 1,
        'status|1': true, // 狀態
        'user_name': '@cname', // 名稱
        'avatar': "@image('150x150', '#4A7BF7', 'img', 'png', 'Tiger')",  // 頭像
        'create_time': '@datetime("yyyy-MM-dd HH:mm:ss")', // 建立日期
    }]
});

// 這裏模擬數據庫裏的用戶記錄

module.exports = data;
複製代碼

user/index.js內容以下

const Router = require("koa-router")
const router = new Router();
const Mock = require("mockjs")
const userlist = require("./data").list;


router.get("/api/user/list",async (ctx)=>{
    ctx.body = {code:0,message:"success",userlist}
});

router.post("/api/user/add",async (ctx)=>{
    let data = ctx.request.body;
    data.id = userlist[userlist.length-1].id + 1;
    const mock = Mock.mock({
        'user_id|100-200': 1,
        'status|1': false, // 狀態
        'avatar': "@image('150x150', '#4A7BF7', 'img', 'png', 'Tiger')",  // 頭像
        'create_time': '@datetime("yyyy-MM-dd HH:mm:ss")', // 建立日期
    })
    data = Object.assign(data,mock);
    userlist.push(data)
    ctx.body = {code:0,message:"success"}
});

module.exports = router;
複製代碼

這裏模擬了請求用戶列表(沒有分頁),添加用戶接口。

postman請求接口數據,以下圖

QQ截圖20190816143728.png

QQ截圖20190816150900.png

react項目請求mock數據

在前端項目安裝

npm install -S axios
複製代碼

由於涉及到跨域,咱們在開發環境須要在webpack.dev.config.js的devServer屬性下添加代理

proxy: {
    // 接口請求代理
    "/api":{
        secure: false,
        target:"http://127.0.0.1:8082"
    }
},
複製代碼

而後須要修改在src/user/store/UserList.js,在這裏獲取請求用戶列表接口,並賦值給實例的userList屬性

import { observable,action } from "mobx"
import axios from "axios"

class UserListStore {

    @observable userList;

    constructor(){
        this.userList = [];
    }

    @action
    async getUserList(){
        const config = {method:"get",url:"/api/user/list"};
        const result = await axios(config);
        if(result.data.code === 0){
            const userList = result.data.data;
            this.userList = userList;
        }
    }
}

export default new UserListStore();
複製代碼

src/user/pages/list.js頁面調用,和渲染用戶列表

componentDidMount(){
    const {UserListStore} = this.props;
    UserListStore.getUserList();
}
render(){
    const {UserListStore} = this.props;
    return (
        <div>
            {
                UserListStore.userList.map((item,index)=><p key={index}>{item.user_name}</p>)
            }
        </div>
    )
}
複製代碼

以下圖:

QQ截圖20190816153402.png

mock+數據請求 源碼 releases 1.0.2

到這裏,react全家桶react+react-router+mobx+axios的使用,mock模擬後端數據已經完成。後邊的事情就是業務邏輯頁面的編輯(這裏省略),打包的優化(下一篇講述)。固然如今項目還有不少不完善,(模塊的組織,目錄結構的劃分,請求實例的封裝,試圖和業務邏輯的抽離,樣式的管理等等,這些和業務邏輯關聯性比較強,這裏不過多說明)

咱們執行一下npm run build,打包成功。可是存在一個問題,打包出來的js高達1M多,太可怕了。如今這個項目還很簡單,隨着業務邏輯複雜和頁面的增多,打包的js會愈來愈大。下一篇文章咱們會一步步優化打包,從路由的懶加載,樣式的抽離,公用第三方包的抽離得等方面優化

下一篇:打包體積優化react+webpack4搭建前端項目(三)打包優化

react+webpack4+react-router5+react-loadable+mobx系列文章請點擊

一、react+webpack4搭建前端項目(一)基礎項目搭建

二、react+webpack4搭建前端項目(二)react全家桶的使用

三、react+webpack4搭建前端項目(三)打包優化

相關文章
相關標籤/搜索