美文网首页
react+webpack4搭建前端项目(二)react全家桶的

react+webpack4搭建前端项目(二)react全家桶的

作者: 小猿_Luck_Boy | 来源:发表于2019-08-16 16:29 被阅读0次

    前言

    react+webpack4搭建前端项目分为三个章节。链接如下。目的是实现从零搭建一个react后台管理系统
    1、react+webpack4搭建前端项目(一)基础项目搭建
    2、react+webpack4搭建前端项目(二)react全家桶的使用
    3、react+webpack4搭建前端项目(三)打包优化
    webpack配置的讲解
    4、react+webpack4.x搭建前端项目(四)配置抽取和区分环境
    5、react+webpack4.x搭建前端项目(五)多页面配置
    6、react+webpack4.x多模块打包配置
    这是第二章,react全家桶的使用

    这里小编推荐一个福利,更多精彩内容请点击链接,点击这里

    废话不多说啦。接着上一篇react+webpack4搭建前端项目(一)我们正式进入react全家桶技术篇章,如果对于项目不清楚或者在下面有什么疑惑,建议先看一下上一篇文章熟悉一下项目由来

    使用react-router-dom管理路由,这里使用react-router4.x以后的版本,请注意,和3.x的使用还是有很大的区别

    npm install -S react-router-dom
    

    我们为什么使用react-router-dom呢?

    先简单说下各自的功能:
    react-router: 实现了路由的核心功能
    react-router-dom: 基于react-router,加入了在浏览器运行环境下的一些功能,例如:Link组件,会渲染一个a标签,BrowserRouterHashRouter组件。显而易见react-router-dom功能更丰富,所以选择react-router-dom代替react-router

    下面接着上一篇文章的项目,我们对项目进行改造:
    新建blog,resume,user页面,如下

    QQ截图20190813113835.png
    分别编写blog,resume,user,home(上篇文章已完成)组件

    此处删除home/index.less,修改home/index.js内容:

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

    blog/index.js

    import React from 'react'
    
    export default class BlogIndex extends React.Component {
        render(){
            return (
                <div>
                    <p>BlogIndex</p>
                </div>
            )
        }
    }
    

    resume/index.js

    import React from 'react'
    
    export default class ResumeIndex extends React.Component {
        render(){
            return (
                <div>
                    <p>ResumeIndex</p>
                </div>
            )
        }
    }
    

    user/index.js

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

    以上这些组件是最简单的组件

    新建src/router.js

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

    下面讲一下这些目录的用途

    1、assets是资源目录,放图片,css,js,字体等等
    2、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路径时才会匹配。可以利用模糊匹配路径方式实现多级路由的管理。

    3、home目录暂时没用
    4、layout整个项目的公用布局组件 NavigationBar.js是上边的导航,SlideMenu.js是侧边菜单
    5、resume是立即模块,没有实现二级路由
    6、user用户管理模块,和blog的目录结构一样,实现二级路由
    7、app.less是项目公用的样式文件,这里写了导航和侧边栏的样式
    8、router.config.js是项目的路由和左侧菜单
    9、router.js是项目的路由和整体的布局

    实现的效果图:

    QQ截图20190816085339.png

    注意事项:
    1、你会发现本项目html标签使用class属性来代替className属性。react本身的html标签是不支持class属性,只识别className属性编写类名。这里我们需要安装一个插件

    npm install -D babel-plugin-react-html-attrs
    

    然后再.babelrc文件的pulgins数组添加"react-html-attrs"即可

    2、我们此处用的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+webpack打包优化react+webpack4搭建前端项目(三)打包优化

    相关文章

      网友评论

          本文标题:react+webpack4搭建前端项目(二)react全家桶的

          本文链接:https://www.haomeiwen.com/subject/hlspjctx.html