美文网首页Web前端之路React.js
React最小系统的搭建

React最小系统的搭建

作者: MASON_S | 来源:发表于2019-08-10 12:56 被阅读1次

    React最小系统的搭建

    与Angular、Vue.js和微信小程序等开发一样,React也是一门数据驱动的语言(相对而言的dom驱动代表是jquery),其中Angular、Vue和React又称是新兴框架的三巨头。总的来说,React和Angular、Vue等的模式类似,一要学会其中一种,就可以快速入手其它任意一门。

    1、搭建开发环境

    开发环境基于nodejs,没有安装npm的需要先去了解前面的教程。

    我们先全局安装create-react-app(react官网推荐的一个脚手架)

    image

    安装完create-react-app后就可以搭建项目了,我们先找一个合适的位置,然后进入cmd中

    image image

    比如我们要创建一个叫demo的项目,输入create-react-app demo,回车后等待一段时间,项目会自动创建好

    image image

    等待下载(大概132M),需要一定的时间,看网速了

    image image

    下载完成后cmd中进入到项目目录

    image

    启动项目

    image image image

    默认情况下,运行成功后会自动在浏览器中打开(http://localhost:3000/),样式是默认的

    如果发现端口被占用,可以先把其他3000端口停用,也可以把本项目的端口修改。修改本项目端口需要进入到(node_modules\react-scripts\scripts\start.js),然后搜索3000,修改掉就行(一般在第51行)

    image

    2、分析项目

    接着我们来分析这个项目是如何构成的

    image

    》node_modules是npm模块,刚刚下载的132M模块大部分都在里面,其次我们人为安装的也会知道下载到里面(后面介绍安装路由模块和状态管理器)

    》Public是公共资源,里面主要有首页、图标等(其中首页在开发模式下回自动导入相关的js)

    》Src是和源码相关的,我们一般把开发的代码全部放在这里(将来会打包到build文件夹下)

    》gitignore,看文件名就知道了,git的相关忽略配置,git会按照里面的配置自动忽略监听某些文件(默认就行,不用改,需要注意的是,该文件要在git初始化前创建,创建好修改不起作用,需要清除git缓存

    》Package-lock.json自动生成的文件

    》Package.json项目配置文件(重要),我们项目的信息在这里有配置,例如版本和依赖等

    》Readme.md, readme

    》Build执行打包后生成的静态文件,可用于部署发布

    image

    接下来我们先看public/index.html

    image

    发现是一个极其简单的静态页面,没有导入任何的css和js,不过有点奇怪的是部分路径使用了%PUBLIC_URL%,查看注释发现编译的时候回自动把它替换成public文件夹的路径。同时,在开发环境下,会生成一个缓存文件,缓存文件是index.html的副本,但是会自动导入相关的js和css

    再接着我们来看src/index.js,它是项目的项目的入口文件,开发是从这个文件正式开始的。

    image

    Index.js入口文件中导入了react的核心文件,同时也导入了样式和一个叫App的组件,还有一个registerServiceWorker文件。为了排除没必要的影响,我们可以不导入registerServiceWorker,并把最后一行删除,其实这个文件是在开发环境下利用缓存加快加载速度的一个服务,删掉没有影响。

    这样下来,整个index.js的核心代码就是

    image

    意思是以index.html下id为root的节点为作用域,实例化react,接着把root的内容全部替换成App组件

    既然说到index.js入口文件里面已经说到App组件,那就不得不说App组件了。App组件在src文件夹下,代码如下

    image

    通过代码可以看到App.js导入了react的核心代码和组件模块,然后定义了一个class继承Component组件(也就是定义了一个组件),最后把组件导出去供调用方使用。当然导出还可以改成下面的模式,是一样的:

    image

    每一个组件都有很多钩子(类似Vue下vue组件的钩子),有组件初始化钩子、组件开始构建时的钩子、组件构建完成时的钩子等等,其次最最核心渲染钩子,也就是上面看到的render函数。Render函数最终需要返回一个jsx对象,里面包含html和相关绑定的变量,写法和angular的模板、vue组件以及handlebars等极其相似,这里就没必要继续展开了(有些细节需要注意的是在es6下class是关键字,所以html里面的class要改成className)。

    导出组件后,其他调用方就可以使用,比如index.js下是这样使用的:

    image

    如果代码开发完成了,脱离开发环境,代码是无法运行的,所以我们需要把代码打包,在cmd中输入npm run build即可,打包完成会项目目录下生成静态文件,上传到服务器部署即可

    image

    3.项目组件划分

    当然这样是远远不够的,离一个可用的系统还差一段距离,接下来我们添加一个简单的功能,同时当页面开始多的时候我们也需要用上路由。先来说说要开发的项目是怎么样的

    我们要开发的系统有首页、文章列表和文章详情,而且页面的顶部和底部要固定,每个页面要有一致的效果显示,所以我们可以分成下列的组件:

    》Header组件(顶部)

    》Home组件(首页)

    》Acticlelist组件(文章列表)

    》Acticledetail组件(文章详情)

    》Footer组件(底部)

    然后分别创建acticle、common和default文件夹,active文件夹放和文章相关的组件,common放通用的资源(如公共方法、路由配置和状态管理等),default放默认的组件(如home、header和footer)。

    image image image image

    紧接着在根组件App中导入刚刚创建的全部组件,然后把里面的jsx代码替换成于它们相关的,代码如下:

    image image

    3、加载react路由

    组件初步开发好了,但不能切换,我们要做的是一个单页应用,为了加快开发进度,决定采用路由(react-router-dom)。

    首先还是去下载模块(同时保持到项目配置文件中)

    image

    下载完成后发现package.json发生了变化

    image

    接着我们去到根组件App里面导入路由模块,然后再进行相关配置(配置可问度娘)

    image

    相关页面

    image image

    4、React组件钩子(钩子函数、也可以叫生命周期)

    和angular、vue、小程序等一样,react组件有众多的钩子,配合钩子可以开发出复杂的应用。下面列举一些常用的钩子,不了解什么是钩子函数的可以看我之前的文章

    钩子 ****执行时间 ****作用 ****Vue下对应的钩子

    constructor() 组件初始化时(又称构造函数) 初始化组件数据 Data()

    componentWillMount() 即将渲染前组件 加载前的预处理 beforeCreate()、created()

    Render() 正在渲染时 返回jsx对象(html) 类似template

    componentDidMount() 渲染完成后 渲染后执行 Mounted()

    自定义钩子 手动调用 处理具体业务逻辑 Methods.自定义方法

    下面以根组件为例,测试各个钩子

    image image image

    接着分析一下代码

    image

    在构造函数中,因为组件App要继承React的组件模块,所以在构造函数中要调用超类(父类)的构造函数,也就是圈出来的 super() (如果涉及组件间通讯,构造函数还可以传入props参数,后面介绍)。在一个组件中,数据存在状态state中,每当状态state发送改变,UI也会发生相应的变化(双向数据绑定)。但是和angular和vue不一样,react直接改变 this.state 不会触发UI重新渲染,必须调用 this.setState(obj) 才会重新渲染数据,这和微信小程序下的setData一模一样,开发时稍微调整下思路即可。

    5、Redux状态管理

    在任何一个大型用于下,状态管理都是必不可少,状态管理器可以规范我们的数据,但是在开始接受状态管理前先介绍一下react下的组件间通讯

    在上一个演示的基础上,我们修改根组件App如下

    image

    简单改动html部分(App.js)

    接着我们需要在Header组件中接收刚刚的参数并把它渲染出来

    image

    修改继承并获取参数(Header.js)

    在Header组件的构造函数中,必须传递进props参数,否则无法接收到父组件传递进来的参数,获取参数时使用 this.props.log 即可获得到,此时页面如下

    image image

    题外话:如果存在需要子组件需要调用父组件方法的需求,写法也是差不多,父组件传递时传递方法名即可,但是有一点需要注意的,默认情况下,被调用的方法(假设是App下的debug方法)的上下文是props,取不到父组件的数据,如果需要去到父组件的数据,需要在父组件的构造函数中绑定上下文,如

    image

    现在可以正式介绍状态管理了,但是需要注意状态管理不是必需的,不推荐滥用,只有在需要全局共享数据等情况下才建议使用,在不合适场景下使用反而会导致代码难以维护(比如可以用组件间通讯解决问题)。

    我们使用redux状态管理器(在react下叫react-redux),我们先安装redux和react-redux

    image

    然后我们在common文件夹下创建一个reducers.js文件,它的作用是定义redux的全局状态,代码如下

    image

    代码中我们导入了redux模块,并且自定义了一个全局状态onlione(用来设置在线状态)模板,将来调用SET_ONLINE即可设置它的值(默认为false),调用获取快照可获取到其当前状态。

    然后我们去根组件App中定义仓库存放状态,当然该导入还是得先导入,代码如下:

    image

    状态仓库定义好了,我们要去使用它,使用前得先设置仓库的使用范围,我们用Provider标签设置其范围,然后绑定上仓库store,代码如下:

    image

    仓库是绑定上去了,但是怎么获取仓库内的状态呢,我们叫获取快照,也就是获取当前的状态值,方法为 store.getState() ,我们测试一下

    image image

    问题又来了,怎么设置状态值呢,这就和我们以前开发的不太一样了,我们需要调用dispatch方法,如下

    image

    store.dispatch({type:"SET_ONLINE", payload: true});

    状态的确改变了,使用状态里面的数据时直接绑定到state上即可,如

    image image

    但是问题又来了,发现状态是更新了,但是UI没有发生变化,我们现在需要监听仓库里面状态的变化,然后实时setState,实现如下:

    image

    问题又来了,在子组件中如何获取到 online 或修改它呢(情况是希望在header组件中完成登录,然后其他组件能够获取到登录状态和用户信息)?获取和修改就需要用到我们前面说的组件间通讯了,我们把online参数传递进Header组件中,然后Header组件通过this.props.dispatch更新状态,此时App.js:

    image

    主要修改UI和屏蔽了更新状态的代码

    然后Header组件改动如下:

    image image

    很不幸,运行时发现报错, this.props 中并不存在 dispatch 属性

    为什么会报错呢,因为我们还没把组件和redux仓库连接起来,我们要对Header组件再做一个小改动,连接一下仓库即可:

    image image

    完美没毛病

    那么问题又来了,刚刚的操作其实是父子组件间的通讯,是在父组件(根组件)中监听store的改动,然后动态绑定到子组件的props上,在非父子组件中又如何获取到store里面的值呢,这个时候我们就需要订阅store了,也就是在组件连接上redux的时候,给它绑定上订阅事件,当store发送改变时,组件重新渲染。比如说我们要在路由Home中获取到online这个状态,我们需要先在Home组件中连接Redux,然后订阅store中online的更新,下面是具体实现

    image

    然后Home组件中通过this.props.online即可拿到store中的值,同样,每当store的online发送变化时,Home组件会重新渲染online(这个过程就叫订阅),更多方法可以查看链接:http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html

    PS:this.props一般是外部传递过来的,不可修改,而this.state是本组件的状态,可修改,要注意两者区别

    现在总结一下react下redux的使用:

    1.下载redux和react-redux

    2.创建一个状态模板

    3.根组件定义状态仓库

    4.根组件添加仓库的作用域并绑定上去()

    5.根组件通过store.subscribe监听状态变化,然后用store.getState获取状态快照,最后绑定到自己的state上

    6.子组件需要更新全局状态,需要把组件和redux连接起来(connect,不需要更新可不连接),然后通过 this.props.dispatch 更新全局状态,需要获取状态从父组件获取即可

    7.如果组价需要监听(订阅)store的更新,根组件使用store.subscribe,而其他组件可以使用订阅的方式来实现。

    6、React交互事件

    一个健壮的系统怎么可以没有交互呢,react下从很多交互事件,如click、change等,我们先在Header组件中添加一个组件,然后实现表单数据绑定,这时候开发就和angular和Vue不一样了,而于微信小程序更像,因为在表单中无法实现双向数据绑定,需要自己去监听表单change,代码如下:

    image

    开发过小程序就会发现这两者有异曲同工之妙(header.js)

    image

    接着我们给它添加一个搜索按钮,并绑定点击事件(当然也需要绑定点击事件的上下文)

    image

    要注意的是jsx里面的html不是真实的dom,如果需要获取dom元素,需要给标签添加refs属性,然后通过this.refs[...]即可获取到元素的dom元素

    下面我们可以把上面的代码稍微修改一下

    image image

    7、渲染服务端数据

    除了事件交互,更重要的还有和服务器的交互,可以react并没有提供官方的ajax交互,我们可以使用jquery的ajax,也可以采用其他库,当然还可以选择h5下的fetch,下面是fetch的一个简要代码

    image

    一般来说,不用fetch,而是采用axios插件,下面简单介绍一下axios的使用和封装http拦截器(vue、angular和小程序中也封装http拦截器,会方便很多)

    import axios from 'axios';

    import JavascriptCommon from './javascript.common';

    import { Toast } from 'antd-mobile';

    //基础设置

    axios.defaults.timeout = 1000 * 60 * 2;

    axios.defaults.baseURL = "你的http前缀";

    axios.defaults.transformRequest = [

    function (data) {
    
        let ret = ""
    
        for (let it in data) {
    
            ret += encodeURIComponent(it) +
    
                "=" +
    
                encodeURIComponent(data[it]) +
    
                "&"
    
        }
    
        return ret
    
    }
    

    ];

    //微信授权

    axios.authorization = (appid, url, state) => {

    let _url = window.location.hash.toLocaleLowerCase();
    
    let flag = _url.indexOf("#/authorization/") === -1
    
        && _url.indexOf("#/recruit") === -1
    
        && _url.indexOf("#/valuation") === -1;
    
    if (flag) {
    
        window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" +
    
            appid + "&redirect_uri=" +
    
            url + "&response_type=code&scope=snsapi_userinfo&state=" +
    
            state + "#wechat_redirect";
    
    }
    

    };

    // http请求拦截器

    axios.interceptors.request.use(function (config) {

    config.headers = config.headers || {};
    
    //模拟登录需要设置sessionId,具体值参考登录后返回sessionid
    
    //JavascriptCommon.SetUserSessionId("**********");
    
    let sessionId = JavascriptCommon.GetUserSessionId();
    
    if (sessionId) {
    
        if (!config.headers["SESSIONID"]) { config.headers["SESSIONID"] = sessionId; }
    
    }
    
    JavascriptCommon.AjaxLoading(true);
    
    return config;
    

    }, function (err) {

    return Promise.reject(err);
    

    });

    // http响应拦截器

    axios.interceptors.response.use(function (res) {

    JavascriptCommon.AjaxLoading(false);
    
    try {
    
        if (typeof res.data === "object") {
    
            if (res.data.status) {
    
                if (res.data.status === -200) {
    
                    axios.authorization("appid", "编码后的回调地址", "weixin_h5");
    
                    return Promise.reject(res);
    
                } else if (res.data.status === -201) {
    
                    Toast.info(res.data.msg, 1.2);
    
                    window.location.hash = "/supplement";
    
                    return Promise.reject(res);
    
                }
    
            }
    
        }
    
    } catch (err) {
    
        console.log("请求异常", err);
    
    }
    
    return res;
    

    }, function (err) {

    JavascriptCommon.AjaxLoading(false);
    
    return Promise.reject(err);
    

    });

    export default axios;//最后导出模块

    使用方式如下:

    import axios from './common/axiosConfig'; //导入封装的模块

    //发起请求

    axios.get("/interest/category").then((res) => {

      store.dispatch({ type: "SET_CATEGORY", payload: res.data });
    

    });

    8、打包部署上线

    image

    相关文章

      网友评论

        本文标题:React最小系统的搭建

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