前言
在三大前端框架中,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>
因为害怕自己并非明珠而不敢刻苦琢磨,
又因为有几分相信自己是明珠,
而不能与瓦砾碌碌为伍。
《山月记》
——中岛敦
评论
还没有任何评论,你来说两句吧!