这两天花了点时间学习了下React,本人并不是一名纯前端研发人员,之所以想学习React只是想补全自己的技术栈。之前一直都是使用jquery
+bootstrap
来开发页面,其实大多数情况下这种技术栈也够了。但是现在都流行前后端分离,所以诸如React和Vue这类MVVM前端框架才会大行其道。
这所以写这篇文章,主要还是为了对自己这两天的学习做个总结,免得后面遗忘。同时,也希望能给那些想学习React却比较迷茫的同行们来个抛砖引玉,说下我个人的学习路线。
我觉得React的优点在于组件化以及JSX语法。组件化实现了页面通用元素的封装,提高页面元素的复用性;而JSX语法则便于我们在js中编写html代码。
学习React是有一定的学习成本的,主要是如果想要用它进行项目开发,就需要学习一系列它的技术栈,包括基本知识、运行编译环境和一系列插件。而我也只是学了个冰山一角。
我采用的nodejs+npm+yarn+webpack。
Nodes:javascript运行环境,我们编写的代码需要在nodejs上运行。
npm:在安装完nodejs后,默认就已经按转了npm了。npm是包管理软件,能够快速地基于命令行的方式安装各种组建包,并对组建包进行管理。例如:
npm install -g create-react-app --save-dev
Yarn:yarn和npm功能类似,都是对包进行管理。yarn具有缓存包的功能,下载过一次,下次安装可以直接从本地进行安装。
yarn add react-redux
Webpack:打包工具,用于将javascript进行打包。
要学习并使用react,ES6是必须要学习的。在react开发过程中,会大量地使用到ES6中的各种特性。诸如:
class InputComponent extends React.Component
;+
来组装字符串。还有其他很多特性会用到,所以对ES6的学习非常重要。
React是基于组件化的思想进行开发的js框架,所以我们需要有组件化的思想。学习React主要会用到:
this.props
进行传递。Redux用于对JavaScript的状态进行管理,通过Redux,可以有效衔接页面操作和操作完成后的状态之间的关系,以及基于双向绑定实现状态对监听状态变化的监听器的影响。所有的状态数据存储在同一个Store中。
其中一个词:状态,很重要。我们在使用Redux,在动作产生后(例如页面的一个点击事件),那么传递给状态的不是对事件的处理动作,而是事件完成后的状态。例如点击登陆按钮进行登录,状态不是执行动作的事件,而是动作完成后的状态:用户名密码是什么?验证是否通过?
所以Redux会涉及到四个概念:
Action:该动作不是点击事件的动作,而是点击之前或者点击之后设置状态的动作。例如:
const loginReceived = (checked)=>{
type:'login_received',
checked
}
从上面可以看出,我们传递的不是事件,而是是否验证通过这个状态。
Reduce:用于接受action产生的状态,并对store中的状态进行修改。所以reduce会在创建store时作为参数传进去,一旦有action被dispatch到store中,store就会调用reduce进行处理。Reduce是一个纯函数。并且只能有一个。多个Reduce通过combineReducers
进行组合。
Dispatch:动作分发,将动作分发到store中。
Store:用于存储状态State。
那么既然用于传递的是用户名、密码、是否通过,那么“验证”事件什么时候执行呢?理论上应该是在状态——获取到是否通过——之前:动作—>验证—>状态。
也就是说“验证”这一步是发生在Action发生之后,Reduce之前。这里就涉及到middleware
组件。middleware是一个概念,类似于拦截器,用于拦截每一个dispatch到store中的action,在action发生之前和发生之后进行拦截处理。
例如记录日志,可以使用createLogs
,上面说的中间处理,可以使用redux-thunk
。
环境搭建包括开发环境和编译环境。
我主要是用VS Code进行开发,优点不多说,绝对好用就是了。需要安装一些插件才能发挥更强大的作用。我装的插件如下图所示:
安装nodejs。这个可以直接下载安装包进行安装,安装完成后,也就支持npm了。
安装yarn。windows平台下下载安装包安装,mac可以使用homebrew进行安装。
brew install yarn
切换淘宝镜像:
npm config set registry https://registry.npm.taobao.org
安装cnpm
npm install -g cnpm
安装react开发环境创建组件。
npm install -g create-react-app
以后可以直接使用create-react-app projectname
来创建开发环境,会默认初始化好开发环境需要的基本依赖。
接下来介绍如何搭建一个完整的项目开发环境。例如项目名称是一个天气查询项目,展示当前的天气。那么我们可以在命令行下做如下操作:
cd Documents/projects/
create-react-app weather
等待项目初始化环境创建完成后,可以通过yarn安装其他以来包:
yarn add redux
yarn add react-redux
yarn add redux-logger
yarn add redux-thunk
yarn add cross-fetch
如果需要其他依赖包,也可以这样安装即可。
我们就以编写一个获取天气的简单小项目来上手。
默认我们在src
目录下创建四个子目录:
actions
:用于存放action的js文件。再次记住:action不是事件函数,是传递状态state的修改值的函数。reduces
:用于存放reduce的js文件。containers
:用于存放将state、dispatch方法与有状态组件(也就是用于最后渲染界面的组件)进行连接的函数。components
:用于存放无状态组件和有状态组件的函数。
ReactDOM.render
渲染成页面的组件。可能我们还会创建utils
目录或者其他目录。最后,还有一个最外层的index.js
文件,我们一般在这里创建store,并渲染页面。
编写action,我们需要分析页面获取天气会产生几个事件。页面的功能是:“根据选择的不同地市,获取该地市的天气,将获取到的地市展示在界面上“。根据这句话,我们来分析有哪些action。
onChange
或者onClick
事件。选择的“地市”就是状态值。所以我们会建一个selectedCity(cityCode)
的函数。注意方法名是selected
,而不是select
,用的是过去式,表示被选择的,是一种状态——天气“待获取”。requestWeather(cityCode)
的函数。那ajax在哪儿发起呢?会用到前面说的redux-thunk
来发起。receivedWeather(cityCode,json)
的函数。redux-thunk
来编写,同时会调用requestWeather(cityCode)
和receivedWeather(cityCode,json)
函数,来完成获取天气之前的准备动作(例如弹出转圈等待)和获取到天气之后的动作(例如渲染到页面)。所以我们会创建一个fetchWeather(cityCode)
函数。因为reduce是用于修改state的,并且是一个纯函数。那么首先第一步,也是最重要的就是想想看我们的state长什么样子?也就是什么样的数据结构(json结构)。
{
selectedCity:'xxx', //选择的地市
weatherInfo:{//天气信息,是一个json
},
isFetching:true/false //是否获取中,如果是ajax请求中,则是true,否则是false。
}
想好了state长什么样子,就要考虑编写几个reduce函数呢?我这里有个个人看法,就是看state里有几个顶级的key,就写几个函数。例如上面有selectedCity
、weatherInfo
、isFetching
三个顶级key,那么我们就创建三个同名的函数。为什么要这样呢?一是为了方法的独立性,便于方法重用;二是正好基于combineReduces
函数,在集成这三个reduce函数后,就会生成如上的数据结构。
既然是写三个函数,同时也说了这样可以保证方法的独立性。那么每个函数返回的state也需要相应的只返回对应的独立的结果。例如selectedCity
函数返回的state就只是一个字符串。是不是很独立呢?
编写components,就需要考虑:
select
组件是无状态组件weatherApp
是有状态组件,里面包含select组件。select
组件会用到 selectedCity
的值;weatherApp
会用到 weatherInfo
的值;同时还会根据 isFetching
的值显示“加载中…“。select
组件的onChange事件会触发 selectedCity
函数;weatherApp
的 componentWillReceiveProps()
方法会触发 fetchWeather
函数,用于监听一旦selectedCity
的值发生变化,就获取天气。containers的写法一般是固定的,就三个作用:
mapStateToProps(state)
方法;(当然也可以不写这个函数,那就是将state作为props的一部分传递给组件)mapDispatchToProps(dispatch)
方法;connect
方法,将组件与redux连接起来,并将上面的两个方法作为connect的参数。经过上面的分析,我们就可以编写代码了,分别在对应的目录下创建js文件,并在js文件中创建对应的方法。