前言
在三大前端框架中,Google开发的Angular.js出现最早但应用较少,国人开发的VUE在国内最火,今天的主角则是Facebook开发的React。
作为国内外最流行的前端框架,React最早被Facebook用于开发Instagram,在2013年5月开源。
React优点是,设计思想独特,性能出众,代码逻辑简单。一切以JS来实现,不使用模板,操作的对象是虚拟DOM。
虚拟DOM
虚拟DOM实际是React将DOM抽象成的JS对象,并可通过API来操作DOM。
也就是说,将DOM代码写到JS(script标签)中,而React负责将DOM代码进行渲染,并解析生成一个模拟DOM嵌套关系的虚拟DOM树对象。
再通过diff算法,在修改时逐层次的进行节点的比较,优先修改渲染虚拟DOM,对页面上真正发生变化的部分进行实际DOM操作,减少真实DOM的操作,实现页面元素的高效更新。
安装
直接通过npm安装。
npm i react npm i react-dom npm i babel-standalone
基础使用
首先需要引入react的js库
<!-- react核心文件 --> <script src="react.js"></script> <!-- 渲染页面中的DOM --> <script src="react-dom.js"></script> <!-- 将 ES6 代码转为 ES5 代码,这样就能在目前不支持 ES6 浏览器上执行 React 代码,并且Babel 内嵌了对 JSX 的支持,可以将JSX语法转换成JavaScript --> <script src="babel.js"></script>
然后需要创建DOM根节点,根节点下的内容被React所管理,下面是一个简单的hello world示例。
<div id="app">test</div> <script type="text/babel"> let myDom = <h1>hello react</h1> ReactDOM.render(myDom, document.getElementById("app")) </script>
JSX语法
上面代码中给myDom赋值的语法就是JSX,全称为JavaScript XML,是JavaScript的扩展语法。
优点是执行效率更高、类型安装,编译过程中就能及时发现错误、编写模板更加简单和快速。
需要注意的是以下几点:
1.必须严格按照W3C规范书写
例如`<input>`等自闭合标签必须写结束标签否则会报错。
let myDom = <input type="button" value=""></input>
2.JSX的注释方式与html和JS都不同
let myDom = <div> {/* 注释 */} <h1>myDom</h1> </div>
3.不支持多行标签
如果有多行必须有父标签包裹,并且最好用括号包裹起来。
let myDom = (<div> <input type="button" value=""></input> <input type="button" value=""></input> </div>)
4.赋变量、计算、调用函数等时同样需要用大括号包裹
let text = "hello react" let count = 100 let user = { name: "zhangsan", age: 18 } function aboutUser(userObj){ return "name is " + userObj.name + ",age is " + userObj.age } let myDom1 = <div>{text}</div> let myDom2 = <div>{count+1}</div> let myDom3 = <h1>{aboutUser(user)}</h1>
5.使用 className来替代class
由于class是JavaScript的关键字,因此在JSX中需要使用className来设置类名。
let myDom = <div className="box"></div>
6.设置样式
let myDom = <h1 style={{color:"red"}}>testStyle</h1>
React元素与组件
React元素是构成React应用的最小部件,例如前面hello world实例中的<h1>hello react</h1>
,与浏览器中的DOM元素不同,React元素是创建开销极小的对象,当React元素变化后,ReactDOM会自动去更新真实DOM。
React元素是不可变对象,一旦被创建就不能再更改它的子元素或者属性,更新它的唯一方式是创建一个全新的元素,并将其传入ReactDOM.render()。React元素代表了某个特定时刻的DOM元素状态。
而React组件(component)概念上类似于JavaScript函数,将React元素组成一个有特定功能、独立可复用的部件,能接受任意的入参(props),有自己的状态(state)和生命周期,并返回用于描述页面UI的JSX代码片段。
当应用的UI都是使用组件完成时,就是一个组件化的应用,能够实现页面局部功能的代码集合,简化页面复杂程度,提高运行效率,降低维护难度。
函数组件
function Welcome(props){ return <h1>Hello,{props.name}</h1> } var obj = {name:"zhangsan"}; let myDom = Welcome(obj); // 或者写成以下形式 let myDom = <Welcome {...obj} />; // React会将JSX所接收的属性(attributes)以及子组件(children)转换成单个对象(props)传递给组件 let myDom = <Welcome name="zhangsan" />;
class组件
class Welcome extends React.Component { render() { return <h1>hello,{this.props.name}</h1>; } } let myDom = <Welcome name="zhangsan" />
设置默认入参
可以通过defaultProps设置组件的默认参数,当传入参数缺失时自动调用默认参数。
function Welcome(props){ return <h1>Hello,{props.name}</h1> } Welcome.defaultProps = {name:"zhangsan"}; //默认参数不适用于Welcome()形式 let myDom = <Welcome />;
注意
1.组件名称必须以大写字母开头,小写字母开头的标签会被视为原生DOM标签。
2.传入的props参数为只读,不能进行修改,如果需要React组件随用户操作、网络响应或其他变化而动态更改输出内容,那么可以通过下面的状态(state)实现。
React组件的状态与生命周期
以下是一个每秒钟刷新一次的时钟组件,通过计时器每秒钟获取一次时间并重新渲染组件。
class Clock extends React.Component { render(){ return ( <div> <h1>Hello,react!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ) } } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById("app") ) } setInterval(tick,1000)
这样显然是很蠢的实现方式,理想的情况下应该能够让时钟组件自我更新,下面代码就是利用state(状态)实现的时钟组件。
state与props类似,但state是私有的,并且完全受控于当前组件。
class Clock extends React.Component { // 构造函数,初始化state constructor(props) { super(props); this.state = {date:new Date()}; } // 挂载(mount)函数,当组件第一次被渲染到DOM(render())之后执行,这里用来设置计时器。 componentDidMount() { this.timerID = setInterval( ()=> this.tick(),1000 ); } // 卸载(unmount)函数,当DOM中组件被删除后执行,用于垃圾回收等。 componentWillUnmount() { clearInterval(this.timerID) } tick(){ // 更新state this.setState({ date: new Date() }) } render(){ return ( <div> <h1>Hello,react!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ) } } ReactDOM.render(<Clock />,document.getElementById("app"))
其中,componentDidMount()(挂载函数)和componentWillUnmount()(卸载函数)就被称为生命周期函数。
注意
1.可以给state赋值的地方只有构造函数中。
2.state不能直接修改,应该使用setState()方法。
这样React就会知道state已经改变了,自动调用render()方法渲染更新后的组件。
3.state的更新可能是异步的。
原因是出于性能考虑,React可能会把多个setState()合并成一个调用。
//错误用法 this.setState({ counter: this.state.counter + this.props.increment }) //正确用法 this.setState((state,props) => { counter: state.counter + props.increment })
4.数据是向下流动的。
state是私有的,不能直接被父组件或子组件访问,但可以将state作为props向下传递到子组件中。
<FormattedDate date={this.state.date} />
事件处理
创建事件
事件命名采用小驼峰式,并且需要传入函数作为事件处理函数。
// 传统HTML <button onclick="handleClick()">testEvent</button> //React let myDom = <button onClick={handleClick}>testEvent</button>
阻止默认行为
传统HTML中可以通过返回false的方式阻止默认行为,而React中必须显式使用preventDefault。
//传统HTML <a href="#" onclick="console.log('This link was clicked but refuse');return false">click me</a> //React function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('This link was clicked but refuse'); } return ( <a href="#" onClick={handleClick}>click me</a> ) }
class组件的事件处理函数
下面的代码是一个可以让用户切换开关状态的按钮。
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // 为了在回调中使用 `this`,这个绑定是必不可少的,否则回调中的this会是undefined this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(state => ({ isToggleOn: !state.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } }
如果觉得bind麻烦,可以使用class fields正确的绑定回调函数。
class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; } handleClick = () => { this.setState(state => ({ isToggleOn: !state.isToggleOn })); } render() { return ( <button onClick={this.handleClick}> {this.state.isToggleOn ? 'ON' : 'OFF'} </button> ); } }
向事件处理函数中传递参数
//箭头函数方式,e为React的事件对象 let myDom = <button onClick={(e) => {handleClick(msg,e)}}>testEvent</button> //Function.prototype.bind方式 let myDom = <button onClick={this.handleClick.bind(this,msg)}>testEvent</button>
因为害怕自己并非明珠而不敢刻苦琢磨,
又因为有几分相信自己是明珠,
而不能与瓦砾碌碌为伍。
《山月记》
——中岛敦
评论
666562 763423cleaning supplies should have earth friendly organic ingredients so that they do not harm the environment 779540
183687 375068Thank you for your really excellent info and feedback from you. san jose used car 451774
654549 872892I like this web site because so considerably helpful material on here : D. 301889
220653 421097Thanks for the post, was an intriguing read. Curious as to how you came about that solution 394670
452888 193075a lot of thanks for telling!. Truth is generally the very best vindication against slander. by Abraham Lincoln.. 187346