react闲谈——webpack2、react-router4稳定版架构

随着react技术栈插件的不断升级,我也不想拖后腿,所以把webpack2和react-router4融入了项目中:https://github.com/hyy1115/re...

前端编程宗旨:工程化、流程化、简单化为目的,拒绝装逼无用的代码风格!

欢迎来到react的世界,下面你将会学习到react技术栈是如何使用的

1、认识本项目的结构

我采用的是react、redux、webpack2、react-router4的基本架构,属于经典主流类型。

文件夹介绍

doc:项目文档  

mock:静态数据

public:静态图片

src:前端主目录

test:基本测试代码

根目录下面的 server.js和webpack.config.js需要多关注一下。

2、修改前端服务器端口号

在server.js中

var port = 3011; //修改成你需要的端口

3、开启代理服务器

我们有时候会遇到跨域访问数据的问题,这时候你可以选择开启 server.js 中的代理服务器。

//现在你只需要执行这一行代码,当你访问需要跨域的api资源时,就可以成功访问到了。
// app.use('/api/*',proxy({
//     target: 'http://www.baidu.com',//     changeOrigin: true
// }))

4、webpack.config.js的作用

webpack的配置文件是一个object,你只需要记住webpack的配置都是通过设置entry、output等参数的值,还有更加丰富的功能去官网看看。

5、src目录的介绍

这个目录的重要性不言而喻,你今后的绝大部分react代码都会在该目录结构下组织,当前采用的是redux架构,所以你会看到整体目录结构由redux的几个主要部分构成。

action:redux的控制中心,任何一个状态state的更新操作都需要dispatch一个action去修改。这一步在一些人看来是鸡肋的操作,他们会觉得观察者模式比你这个统一大脑指挥中心方便多了。

components:管理比较细的一些组件。比如Header、Form、Banner等。

containers:管理路由级别的组件,这些大组件往往是一整个页面,内部嵌套了很多小组件,也可能嵌套其他路由级组件进来。

reducers:这里是redux的数据管理中心,reducer是纯函数,你不能在reducer中修改当前的state,只能返回一个新的state,如果你直接修改state,将不能重新渲染。

utils:一些工具js的管理。

app.css:基本的css配置,你也可以把reset.css或者其他初始化样式的css写入app.css中。

appContainer.js:我们叫他根组件,在SPA应用中,通常只有一个根组件。

bundle.js:react-router4中使用的懒加载代码,目前我已经注释掉,有需求的可以自己尝试使用。

index.js:webpack中entry使用的入口js文件,包括store的管理,根组件的渲染都在该文件中。

6、实现一个首页的流程步骤解析

有了这样一个搭建好的框架,你如果是个初学者,是不是很想快点用它做出自己的网站,下面我就将首页的实现过程大概梳理一下,之后你可以尝试写一个属于自己的页面。

第一步:想要用react写一个网页,你的第一个想法便是去container目录新建一个文件夹,比如Home,表示当前首页的路由组件homeContainer.js,文件名我通常开头不大写,但是在class HomeContainer中就需要开头大写了。 此外,还需要在Home文件夹下面新建files和styles,分别用来存放当前组件的图片等资源和样式。

第二步:你可以开始写homeContainer组件的代码了,通常一个基本的路由组件是下面这种结构。

import React,{ Component } from 'react'; //react必须导入
import { bindActionCreators } from 'redux'; //bindActionCreators用来绑定你的action到该组件上
import { connect } from 'react-redux'; //connect()是个函数,顾名思义是把react和redux连接起来。
import PropTypes from 'prop-types'; //PropTypes原来是在React中使用,现在被官方拆分出来单独管理。

import Header from 'components/Home/header'; //导入子组件,子组件写在components文件夹。

require(`./styles/home.less`); //导入首页样式

//请注意@connect()必须写在组件的上面,而且紧挨着组件,不要拆散他们俩。
@connect(
    state => state
)
export default class HomeContainer extends React.Component {
    render() {
        return (
            <div>
                <Header title="" imgUrl="" linkTo="" bgColor="" match=""/>
            </div>
        )
    }
}

第三步:去components文件夹下面,新建一个Home文件夹,于containers下面的Home文件夹一一对应,这样做的好处是父子组件既能分开管理,也能快速找到。 接着在Home下面新建header.js文件。子组件可以是函数,也可以是react类型的组件。我在这里定义的是一个react子组件。

import React,{ Component } from 'react'; //无论是函数组件还是react组件,都需要导入React。
import { Link } from 'react-router-dom'; //Link相当于a标签。
import PropTypes from 'prop-types'; 
//header子组件只作为数据渲染,数据从父组件传递到子组件使用props。在jsx中绑定数据使用大括号。请注意标签中的class需要改成classname,而style里面写成object的形式。
如果你不喜欢字符串之间的参数用加号拼接,那么可以使用`${value}`的方式。
export class Header extends React.Component {
    render() {
        const {title,imgUrl,linkTo,bgColor,match} = this.props
        return (
            <header className='header' style={bgColor}>
                {title}
                <Link to={`${match.url + linkTo}`} className="a_link" >
                    <img src={imgUrl} className="a_img" />
                </Link>
            </header>
        )
    }
}

第四步:我们现在还没有用到action和reducer,别着急,看到nav.js组件没?Nav组件是一个li列表,列表的文案数据是从HomeContainer父组件传递过来的,这些菜单列表数据我用静态json文件的方式 写在了mock文件夹下面,正常情况下,它可能在你的后端服务器,也就是说你需要请求ajax返回导航数据。

可以思考一下,在什么时候、什么地方去请求后端的导航数据呢?

我是在homeContainer组件的componentWillMount(即将渲染前,也就是还没有开始渲染)使用ajax请求接口数据。有人可能会在componentDidMount(渲染完成后)再去服务器拿数据,当然,你喜欢这样,没人拦着。

是不是直接在componentWillMount写ajax代码就行了呢?别忘了你已经使用了redux,这时候你就需要在action中新建一个js,然后定于一个action用来发送保存从后端接收到的导航数据。

//这个就是传说中的action,他只是返回一个对象,可以是多个参数。
const receiveNav = (response) => ({
    type: 'RECEIVE_NAV',navMain: response.data
})

通常你的ajax代码写在当前action的下面,两者写在一起管理非常方便,要是写到其他文件夹下面,到时候你的项目一大,找个ajax都找不到,特别头疼。

这是一个异步函数,你可以选择使用Promise或者其他异步方法,我在这里使用的是async/await,dispatch是redux中非常重要的一个方法。

export const getNav = () => async (dispatch,getState) => {
    try {
        let response = await getData(`/api/book/navigation`) //ajax请求采用axios插件
        await dispatch(receiveNav(response)) //靠着这个神奇的dispatch(),可以直接调用action对应的函数,去更新store里面的数据。
    } catch (error) {
        console.log('error: ',error)
    }
}

第五步:现在数据有了,我们知道redux是单向数据流,那么数据会从action流向哪里呢?答案就是reducer。在reducers文件夹下面,新建一个nav.js文件, reducer作为数据流向store最后一层屏障,每次都会拷贝一个新的state,所以通常是这种写法。

// 初始化状态
let initNavList = {
    navMain: []
}

export function nav(state = initNavList,action) {
    switch (action.type) {
        case 'RECEIVE_NAV':
            return {
                ...state,//三个点是展开符
                navMain: action.navMain
            }

        default:
            return {...state};
    }
}

我们看看第一个case里面return出来的是什么东西

{
    navMain: [data]
}

原来navMain是一个空数组,当有数据返回的时候,整个initNavList就会拷贝一个新的对象出来,注意这个initNavList仅仅是整颗store状态树的一部分节点。
第六步:你是不是有点好奇,为什么action和reducer不需要import或者require文件,数据就会那么听话的流过来呢?

答案就在reducers文件夹下面的index.js文件中。你如果在reducer文件夹下面新建了js文件,需要在该文件夹下的index.js中注册你的reducer。

//关键的2个插件

import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux'

第七步:reducer写好了,那么数据下一步就要流向container组件了。来吧,宝贝。 在路由组件(或者叫做父组件)中,关联state和action是其中非常重要的一环,不然你是无法读取state和action的。

import * as navActions from 'actions/nav'; //导入nav文件下面的所有函数,不管是action函数ajax方法

@connect(
    state => state,dispatch => bindActionCreators({...navActions},dispatch)
)
接着你就可以在组件中通过props的方式去调用action里面的方法了。

componentWillMount() {
        const { navMain } = this.props.nav //读取reducer中的nav。

        //如果state中的navMain为空,那么就执行getNav()函数去请求后端导航数据。
        if (navMain.length === 0) {
            this.props.getNav();
        }
    }

第八步:父子组件和数据都写好了,想要运行在网页上测试一下?你还有最后一步没有完成呢。

最关键的:路由。

在appContainer中,管理主要的路由组件,具体看代码。

import HomeContainer from './containers/Home/homeContainer';

<Route location={location} exact path="/" component={HomeContainer} />

你也可以结合我在文章开头分享的github项目来学习。

提问环节:

小白:我V2、V3都还没搞懂,你分享V4有什么用?

我:前端就是这样,每天都在出新的插件,紧跟步伐,才能更好的应用于项目之中。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom