前言
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
标签,BrowserRouter
和HashRouter
组件。显而易见react-router-dom
功能更丰富,所以选择react-router-dom
代替react-router
下面接着上一篇文章的项目,我们对项目进行改造:
新建blog,resume,user页面,如下
分别编写
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
,效果如图
到此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
封装导航组件
我们先看一下写出来项目目录
由于代码量越来越大,这里不再给出详细代码,如果需要请点击 源码 下载 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
是项目的路由和整体的布局
实现的效果图:
注意事项:
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 = ()=>{
}
}
报错如下:
解决方法是:
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-react
是mobx
和react
的结合,提供Provider
组件统一管理mobx
数据;inject
为react
组件注入某个mobx
实例;observer
实现mobx
实现react
组件和mobx
数据的双向绑定(和react-redux
的connect
差不多)等等
创建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"
使用Provider
,store
<Provider {...store}>
<AppRouter />
</Provider>
重新运行项目,不出所料报错。为什么呢?熟悉mobx
的同学应该都知道,mobx
的特色是使用装饰器来来修饰mobx实例中属性和方法,以及react-mobx
也是通过装饰器来使用。
装饰器可以通过@关键字加上相关的方法。来达到为属性,方法,class添加其它功能的作用。装饰器作用的作用其实用很大,比如java
的spring
运用最广泛,想学习的同学可以去查相关资料。
我们需要在项目配置对装饰器的支持
安装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
中
使用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
目录结构:
我们新建一个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请求接口数据,如下图
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>
)
}
如下图:
mock
+数据请求 源码 releases 1.0.2
到这里,react全家桶react+react-router+mobx+axios
的使用,mock
模拟后端数据已经完成。后边的事情就是业务逻辑页面的编辑(这里省略),打包的优化(下一篇讲述)。当然现在项目还有很多不完善,(模块的组织,目录结构的划分,请求实例的封装,试图和业务逻辑的抽离,样式的管理等等,这些和业务逻辑关联性比较强,这里不过多说明)
我们执行一下npm run build
,打包成功。但是存在一个问题,打包出来的js高达1M多,太可怕了。现在这个项目还很简单,随着业务逻辑复杂和页面的增多,打包的js会越来越大。下一篇文章我们会一步步优化打包,从路由的懒加载,样式的抽离,公用第三方包的抽离得等方面优化
下一篇: react+webpack打包优化react+webpack4搭建前端项目(三)打包优化