各位同学们大家好,今天是4月9号周日,今天我们继续来做“倒计时”这个前端组件。之前我们是使用原生js来实现的,其实更多的只是实现了功能。
这一次我们使用ReactJs来实现它。react本身就不做过多的介绍了,相信真心关注前端的小伙伴们不可能对它一无所知。只提一下它的重点吧,一虚拟dom;二是全组件化。
而我们在日常使用react的过程中,更多的是跟组件化这三个字打交道。一般来讲,组件就是指被封装好的,且有一定功能的ui零件。
而react的思考方式,就是把页面上的每一个部分都按组件来看待。简单来讲,就是每个div,在react中都可以被看做一个组件,然后把这些react编写的组件,像div嵌套那样,进行大组件套小组件的这种层层包装的形式,组装成整个ui页面。
新的开发方式会肯定有它的好处,就是模块之间的彻底分离。比之前的所谓mvc更加彻底。
另外老话重提,react组件三特征:可组合,可重用,可维护。
来看ui图,先一分析一下结构。其实结构很简单,就是div里有一个ul,ul里有三个li容器横向排列,每个li里有从上到下的label span 而已。
在我看来,如果前端新人对于react的组件概念不太好理解,不太容易分得清每个组件要包含哪些东西?
那么可以直接用div容器的概念来理解react的组件。因为它们不管它们在开发、生产环境是什么样的形式,落实到页面dom中,全都是dom节点了。所以开始的时候,可以反着来理解一下。
就说这个倒计时应用吧,在开发它的时候,你可以按着先页面,后js的顺序。也就是先用react来把页面结构生成出来,然后在再相应的页面组件中添加各种js程序。
//==============
首先把页面的结构先搭出来,新建一个目录,。。。新建个html文件,js,css目录,
js目录里放这三个文件:
react.js 、react-dom.js 和 Browser.js
然后在html中引用。
其中,react.js 是 React 的核心库,
react-dom.js 是提供与 DOM 相关的功能,
Browser.js 的作用是将 JSX 语法转为 JavaScript 语法
最后写一个 <script> 标签,注意它的 type 属性为 text/babel 。
<script type="text/babel">
这是因为 React 独有的 JSX 语法,跟 JavaScript 不兼容。
凡是使用 JSX 的地方,都要加上 type="text/babel" 。
现在我们就可以开始写react了
回忆一下我们切静态页面的时候会怎么做?肯定是先搞个大的父容器出来,然后再在父容器中添加相应的各个子容器。
首先肯定得有个“根”节点做为最外层的父容器呀,那么,
<body>
<div id="timeWrap"></div>
</body>
这个timewrap的div,它就是最外层的父容器。而我们刚才已经分析过,div里面就是ul,ul里面就是li,,,,
那么,用react生成的第一个组件类,就是ul,使用 React.createClass 生成第一个组件类:
//要记得react里的组件类,第一个字母要大写
var TimeWrapUl = React.createClass({
render:function(){
return <ul>...</ul>
}
});
这里用到了 render 方法,该方法会返回一个React组件树,用来接受该组件树的变量名称必须首字母大写。并且该组件树只能有一个根节点,最终这棵组件树会被ReactDOM.render渲染成HTML标签。
这时的ul,它并不是一个真正的DOM节点,而是一个虚拟的DOM节点,这些节点就是一些标记之类的记号,只是React知道该如何处理它们。
ReactDOM.render(
<TimeWrapUl />,
document.getElementById('timeWrap')
);
按切页面的套路,接下来就该li了,
用 React.createClass 再来生成一个组件类,Day:
var Day = React.createClass({
render:function(){
return:<li></li>
}
});
它里面要有li。
写好的Day这个组件类,怎么放呢?其实很简单,就是跟切页面的思路一样,就是放在ul里面就好,这样:
var TimeWrapUl = React.createClass({
render: function() {
return <ul>
<Day />
</ul>;
}
});
接下来,把时,分,秒的都如此写出来,好了,到此时,页面结构搭完,就类似于静态页面切完了,该往里写js了。
//===================
按照一般的js开发思路,在写js之前 应该先搞一些初始值,然后开始加载 dom,
再搞一些function方法来修改操作这些值
当dom都加载好了,开始绑定事件,
然后再把这些值传递到“更新dom”的方法中。
//===================
而react它的特点之一,就是把组件看成一个状态机,有一个初始状态。
然后当“情况或条件”发生改变的时候,导致状态变化,然后重新渲染ui了。
而不能用以往操作dom的思路,不能想操作哪些,就去用id控制哪里
//===================
react中
初始化的方法: getInitialState (只会在组件初始化的时候调用一次)就是用来初始化状态的,这就相当于一般js开发之中的init()方法。
而传统前端开发中的“当dom加载完成”,在react中对应的,
// componentDidMount
就可以理解为,只会在组件渲染结束后调用一次
有点类似于window.onload
那么,一些要在dom加载之后才做的事情,应该放在它里面
我们可以通过this.setState()来修改状态。状态的每次修改都会出发render函数。
好啦,初始化,加载dom,修改状态都找到对应的方法了。
那么就这样,
在ul这个组件中添加相应的初始化方法 getInitialState
和
加载dom之后执行的方法 componentDidMount
//===================
getInitialState,用于定义初始状态,就是一个对象,
它可以通过this.state 属性读取
//===================
初始化完了,该加载dom了,用 componentDidMount 方法
在它里面写 this.setState() 来修改状态,触发 return 修改
为啥没变?
你得往Day这个组件里传数据啊,
我们需要给组件添加一个属性
组件的属性可以接受任意值,字符串、对象、函数等等都可以,
也就是说,基本可以随便命名,当然你不能乱写,差不多就行
写一个属性dayVal,
<Day dayVal = {this.state.day} />
为啥还没变?
//===================
因为Day这个组件类里,还没有写接收参数呢。
它也得有 初始化方法,getInitialState
然后还得有个接收参数的方法,它得用来显示日期呀,
因为状态改变了,它做为被加载的组件,得接受新的参数啊
需要使用,componentWillReceiveProps( object nextProps ) 方法
然后 this.setState() 修改状态,在Day中触发 render 修改,
在修改中用 this.state来获取属性
//===========
我们按着day的样式,把时,分,秒都这样写好。这样就是整个组件在初始化的时候给一个值,然后当dom加载完成之后给了另一个值,然后触发了render方法。
接下来我们就要使用setInterval方法,让这个过程不断的重复。这样计时器就Ok了。
把之前的倒计时js拿过来,就这样放这,间隔1000毫秒。
然后这个 this.setState 是不是应该放在setInterval里呀?这样才可以每隔一秒修改一次时分秒的值。
报错!!
说是this.setState is not a function,为啥?
因为在setInterval里,this的值变了。console.log看一下this,它的值变成了window,因为setInverval是window的方法
这有二种解决方法,
第一种,
我们需要一个绑定方法,bind(),许多同学可能以为它是react的。
但事实上,它是es5的方法,
参看,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Function.prototype.bind()
bind()方法会创建一个新函数。当这个新函数被调用时,bind()的第一个参数将作为它运行时的 this,
bind() 函数会创建一个新函数(称为绑定函数),新函数与被调函数(绑定函数的目标函数)具有相同的函数体(在 ECMAScript 5 规范中内置的call属性)。
其实很简单,就是把componentDidMount所对应的匿名函数的this,传到了 setInterval里面去,
其实这种情况我更喜欢这样做,
var _self...
//===========
到这一步,这个倒计时的react版本,基本就算是做完了吧。