一个完整项目解读

 新闻中心     |      2020-03-28 08:12

前言:此文需要有一定react,redux基础,具体学习资料请科学上网。

一个完成的react项目

github地址
demo地址(请调成手机模式观看)


使用create-react-app脚手架

技术栈: webpack + react + react-router + redux + sass + es6

配合antd组件实现的管理系统demo,线上地址

1.下载

git clone git@github.com:toniqian/react-redux-router.git
cd react-redux-router
npm install (安装依赖模块)
npm install webpack -g (没有安装webpack的需要安装)

开发前反思

2.运行

1. 按需加载

开发环境
npm run vendor (提取公共js)
npm run start(启动服务)

webpack的 import 动态加载的模块的函数,import,参数为模块地址。

生产环境
npm run prod

注意: import 后会返回一个promise对象。

3.文件目录

  • dist文件是打包的生产代码
  • node_modules 是开发需要的插件
  • src是开发目录
  • package.json 插件配置,运行命令的配置
  • server.js 开发环境的服务配置
  • webpack.config.js 开发环境的配置
  • webpack.dll.config.js 提取开发环境的公共js
  • webpack.prod.config.js 生产环境的配置
  • webpack.prod.dll.config.js 提取生产环境的公共js

import.then(mud => { dosomething;

4.项目目录

  • action 组件改变状态的行为
  • components 公共组件库
  • containers 首页
  • image 图片库
  • lib 公共js库
  • reducers reducer库
  • router 路由的汇总
  • routers 每个单独的页面,包含页面的路由配置
  • scss 样式库
  • index.html 开发环境页面模板
  • index.js 应用入口文件

本demo构建了异步加载组件Bundle,具体代码请见

5.命名规范

class Bundle extends Component { constructor; this.state = { mod: null }; } unmount = false componentDidMount = () => { // 加载组件时,打开全局loading this.props.dispatch this.load } componentWillUnmount = () => { this.unmount = true } componentWillReceiveProps { if (nextProps.load !== this.props.load) { this.load } } load { if  { return true } //注意这里,使用Promise对象; mod.default导出默认 props.load => { if  { // 离开组件时,不异步执行setState this.props.dispatch return false } this.setState({ mod: mod.default ? mod.default : mod }, _ => { // 组件加载完毕,关闭loading this.props.dispatch; }); } render() { return this.state.mod ? this.props.children : null; }}

 {Comp => { return Comp ?  : 加载中... }} 
整个项目以模块为单位

比如建立一个帮助中心模块,取名为help

(1)image存在help模块,公共icon放入icon目录


(2)reducers 存在help模块,reducer里面写help模块下的页面reducer,
index.js把help模块下reducer统一汇总


(3)routers 存在help模块,demo1,demo2为help模块下面的页面,
index.js把help模块下面的路由统一汇总


(4)scss下面index.scss汇总项目的scss

animate为动画样式,common为公共样式,

component为组件样式

icon为图标样式,page为每个页面的样式汇总,

reset为重置样式,

variable为项目变量


2. 全局loading

6.项目功能

项目是一个spa应用,包括首页,还有2个案例页,一个是表单验证页,

一个图片懒加载页,利用路由进行页面跳转,通过webpack做了按需加载,

每个页面只会请求当前页面的js,提高了页面的打开性能


项目状态树图

状态树图


首页是banner组件,还有btn组件组成

首页


表单验证页,是input组件,还有btn组件组成,加上逻辑组件getInput组件

表单验证页


图片懒加载,是image组件,建议所有的图片采用image组件

图片懒加载


配合redux,dispatch => reducer更新 => mapstate更新,在根组件进行loading的渲染

7.代码分析

我把页面分为4种情况,分别编写了4个逻辑组件

1.页面无数据,静态页面 使用GetPage组件

2.页面有数据,需要渲染页面的, 使用GetData组件

3.页面是表单页面,使用GetInput组件

4.页面为需要翻页的列表页,使用GetNextPage组件

注:逻辑组件内部会与redux关联,然后会把一些公共组件,
比如一些头部组件,底部组件之类的放在逻辑组件内部,
进行统一化管理,所以所有页面尽量采用这4种逻辑组件
作为统一外部组件,实现统一管理

详细请见本demo地址 src/routers/router.js——render函数

8.总结

1.开发spa应用,利用webpack进行了按需加载,大大提高了页面的性能

2.使用逻辑组件,页面根据对应的需求采用对应的逻辑组件,
大大提高了页面的开发效率,方便进行统一管理

3.采用redux进行页面的状态管理,把每个页面具备的公共状态抽离出来,
如alert组件,loading组件的状态,在逻辑组件统一管理

4.每个页面都有对应的reducer唯一标识,用来记录每个页面的独立状态,
在reducers目录进行配置

5.每个页面都会获取自己页面的状态,通过在reducer配置的唯一标识来区分

6.页面需要改变状态,需要发送action行为,统一定义为setState,用来改变状态

7.为了页面还原到离开之前的状态,在离开页面的时候
会把当前的离开位置,记录在当前页面的状态中,回来的时候还回到离开的位置

8.离开页面会把当前页面的数据存储在状态中,重新回到页面,
会首先加载状态中的数据,后面再通过数据请求,
比较新旧数据的区别进行替换,大大加快了页面的2次打开速度

9.借助webpack可以编译sass,px转rem,es6编译成es5,模块化开发,代码压缩混淆,图片压缩......

10.项目以模块化为基准,每个目录下面都会对应模块的分类,
请注意模块的分类,相同子模块放到一个父模块里面。
图片名称,文件夹名称,文件名称,样式名称,统一采用-命名
如: about-us 采用中划线的命名方式

3. 配置路由对象

本demo使用的是router4,官方文档演示为单行Route,未有统一配置对象。 管理系统基本围绕着content进行业务开发,构建通用配置有助于开发 构建router.config.js

const routers = [ { menuName: '主页', menuIco: 'home', component: 'home/home.js', // 主页 path: '/admin/home' // 主页 }, { menuName: '用户', menuIco: 'user', children: [ { menuName: '用户列表', component: 'user/list.js', // 主页 path: '/admin/user/list' // 主页 } ] }, { menuName: '多级菜单', menuIco: 'setting', children: [ { menuName: '多级菜单2', children: [ { menuName: '菜单', component: 'user/list.js', // 主页 path: '/admin/user/list3' // 主页 } ] } ] }, { menuName: '关于我', menuIco: 'smile-o', component: 'about/about.js', // 主页 path: '/admin/about' // 主页 }]

实现思路,最外层布局为Admin,content被Admin包裹,那么可以利用 this.props.children ,把内容打入content中。(利用bundle组件异步加载后塞入组件进行渲染)

  {this.props.children} // Content组件内部render() { return (  {this.props.children}  )}// 本demo实现,详见src/routers/router.js (  {initRouters.map(el => this.deepItem(el, { ...this.props, ...item}))}  )}/>

4. 配置通用reducer

多人配合开发,一些业务场景的组件需要状提升

import otherReducers from './otherReducers'const App = combineReducers({ rootReducers, ...otherReducers // 其他需要增加的reducers})

5. 登陆验证

利用 withRouter 函数,页面进行路由跳转时触发该函数

const newWithRouter = withRouter

return 

6. 路由拦截

同上,根据路由配置与权限,返回相应的菜单或屏蔽

return 

7 其他配置

// 修改webpack.config.dev.js 和 webpack.config-prod.js 配置文件{ test: /.$/, // 匹配src的都自动加载css-module include: [/src/], exclude: [/theme/], use: [ require.resolve, { loader: require.resolve, options: { importLoaders: 1, modules: true, // 新增对css modules的支持 localIdentName: '[path]___[name]__[local]___[hash:base64:5]' } }, { loader: require.resolve, options: { // Necessary for external CSS imports to work // https://github.com/facebookincubator/create-react-app/issues/2677 ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), autoprefixer({ browsers: [ '>1%', 'last 4 versions', 'Firefox ESR', 'not ie < 9', // React doesn't support IE8 anyway ], flexbox: 'no-2009' }) ] } }, { loader: require.resolve // compiles Less to CSS } ]}, { // 不匹配node_modules,theme的都不能自动加载css-module test: /.$/, include: [/node_modules/,/theme/], use: [ { loader: "style-loader" }, { loader: "css-loader", options: { importLoaders: 1 } }, { loader: require.resolve // compiles Less to CSS } ]},

使用: 在App.js中直接导入

import './assets/theme/App.less'

// 安装react-hot-loader npm install --save-dev react-hot-loader

在webpack.config.js 的 entry 值里加上 react-hot-loader/patch

webpackDevServer.config.js中hot设置为true

步骤四: 在webpack.config.dev.js中在babel-loader中plugins加入react-hot-loader/babel

{ test: /.$/, include: paths.appSrc, loader: require.resolve, options: { // This is a feature of `babel-loader` for webpack . It // enables caching results in ./node_modules/.cache/babel-loader/ directory for // faster rebuilds. cacheDirectory: true, plugins: [ 'react-hot-loader/babel' ] }},

重写index.js,App挂载

import { AppContainer } from 'react-hot-loader'const render = Component => { ReactDOM.render(   , document.getElementByIdif { module.hot.accept => { render;}

直接在package.json中 加入

homepage:'.'

后记:使用react与vue的感悟

react是函数式编程,代码难度、学习曲度、装逼指数,社区生态多样性相比vue更高一点。

vue提供了大量的指令降低了开发难度,详细完善的文档,上手更快。

react提供较少的api,相比vue的指令,业务场景的功能需要自己实现,难度更高一点

vue适合中小型项目,单兵、少量人员配合快速开发

react适合大型项目,多人协作

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。