React教程

教程

一、demo

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>Hello React!</title>
    <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
    <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
    <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>

<div id="example"></div>
<script type="text/babel">
    ReactDOM.render(
        <h1>Hello, world!</h1>,
        document.getElementById('example')
    );
</script>

</body>
</html>

二、安装

官方CDN,生成环境不要用

<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>

需要3个库:

  • react.min.js React的核心库
  • react-dom.min.js react的dom相关库
  • babel.min.js 对不支持es6的浏览器的兼容

使用npm构建环境

教程

三、元素渲染

<div id="example"></div>
<script type="text/babel">
    function tick() {
        const element = (
            <div>
                <h2>now is {new Date().toLocaleTimeString()}</h2>
            </div>
        )
        ReactDOM.render(element, document.getElementById('example'))
    }

    setInterval(tick, 1000)

</script>
  • 定义一个element,它是React的元素对象
  • 通过 ReactDOM.render,把元素渲染到dom里面
  • 渲染后,修改元素不会同步到dom,如果要修改dom,需要重新渲染,也就是调用render函数
  • element只能是const,也就是只读类型

每次修改dom,就要重新获取元素,所以把元素的获取封装起来,就比较方便了。可以通过元素函数,或者元素类来封装。名字必须大写开头。

元素函数

function Clock(props) {
     return (
         <div>
             <h2>now is {props.date.toLocaleTimeString()}</h2>
         </div>
     );
 }

 function tick() {

     ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('example'));

 }

 setInterval(tick, 1000);
  • 定义了获取元素的函数Clock
  • 通过<Clock date={new Date()}/>,这个代码会执行Clock函数,然后把入参存在函数的props属性

元素类

除了可以封装为函数外,也可以封装为类。

class Clock extends React.Component {
    render() {
        return (
            <div>
                <h2>now is {this.props.date.toLocaleTimeString()}</h2>
            </div>
        );
    }
}

function tick() {

    ReactDOM.render(<Clock date={new Date()}/>, document.getElementById('example'));

}
  • 定义Clock类,继承React.Component
  • 定义render返回元素对象
  • 通过this.props访问props属性
  • 调用方法和函数一样,都是<Clock date={new Date()}/>

四、JSX

React使用JSX来代替js
JSX是很像XML的js语法拓展
JSX优点:

  1. 执行更快
  2. 类型安全,会检查编译错误
  3. 使用模板更加快捷简单

JSX是用来在js语法中,声明React的元素的(也就是html代码),但是又不像字符串。


var arr = [
    <h1>h111</h1>,
    <h2>h222</h2>,
    3
]
var style = {
    fontSize: 100,
    color   : 'blue'
}
const element1 = <h1>
    <p style={style} font='1px' data-myattribute="kevinlu">{1 + 1},{1 == 1 ? 2 : 3}</p>
    element1
    {/*注释...*/}
    <div>{arr}</div>
</h1>

function tick() {
    ReactDOM.render(element1, document.getElementById('example'));

}
  • 这样就声明了一个元素element1 ,但是又不需要用引号包裹。声明时可以换行。最外层只能是一个标签
    • 例如这样是不行的:const ele = <p>1</p><p>2</p>因为最外层有2个标签
    • 这样可以:const ele = <div><p>1</p><p>2</p></div>,通过外层加一个div标签。
  • 如果要定义自定义属性,可以:<p font='15pt' data-myattribute = "kevinlu"></p>data-前缀
  • 可以在元素中使用js表达式,用{}包裹
  • 不支持if else,但可以用三元运算符
  • 支持内联样式,把样式定义在一个字典里面,然后再元素里引用字典
  • 支持数组,数据里面的元素可以是元素,元素会被逐一展示

五、组件

一个元素可以以组件形式被另一个元素引用,而且可以传参数。

function Con(props) {
    return (
        <div>
            <h2>I am Con name={props.name}</h2>
        </div>
    );
}


const ele = <div><Con name="kevinlu"></Con><h3>content</h3></div>


ReactDOM.render(ele, document.getElementById('example'));
  • Con是一个组件,通过props.name获取入参。组件也可以用类的形式来定义
  • ele 是一个元素,引用了Con组件,然后传输了入参name="kevinlu"
  • 也可以创建复合组件,就是多个组件组合为一个新组件
  • 组件必须大写开头,区别于html默认标签都是小写开头

六、组件状态

为组件赋予状态属性,通过修改状态来动态更新DOM

class Clock extends React.Component {
    constructor(props) { //构建函数
        super(props);
        this.state = {date: new Date()}
    }

    componentDidMount() {//当DOM被创建(挂载),执行该函数
        this.timerId = setInterval(() => this.tick(), 1000) //() => this.tick()相当于定义了一个箭头函数,()表示无入参
    }

    componentWillUnmount() { // 当DOM被删除(卸载),执行改函数
        clearInterval(this.timerId)
    }

    tick() {//通过setState来更新state,如果直接更新,例如this.state.date=new Date(),不会同步到DOM
        this.setState({date: new Date()})
    }

    render() {
        return <div>
            <h1>Time</h1>
            <h2>Now is {this.state.date.toLocaleTimeString()}</h2>
        </div>
    }
}

ReactDOM.render(
    <Clock></Clock>,
    document.getElementById('example')
)
  • 执行ReactDOM.render 渲染Clock元素到
    • 生成一个Clock实例
    • 执行构建函数constructor
    • 执行render生成元素的html代码
    • 渲染元素的html到DOM
    • 执行componentDidMount钩子,里面设置了一个定时器,定时器ID存储在实例的变量this.timerId中
  • 定时器定时执行() => this.tick()
    • 这是一个箭头函数,直接传this.tick是不行的,因为会丢失this参数。
    • 箭头函数执行this.tick也就是Clock实例的tick函数,
    • tock函数通过setState更新Clock实例的state里面的date属性,更新为当前时间
    • setState函数会做:
      • 更新变量
      • 重新这些render生成html
      • 把html更新到DOM
      • 所以如果直接更新变量,例如this.state.date=new Date(),是不会同步到DOM的
  • 当DOM被删除componentWillUnmount函数会被执行,然后删除定时器

七、props

类型检查,元素组件支持入参,props支持类型检查,检查入参是否符合要求,如果不符合,或早console显示warning信息。

<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
function Clock(props) {
    return <div>{props.name}</div>
}

Clock.propTypes = {
    name: PropTypes.string
}
ReactDOM.render(
    <Clock name={123}></Clock>,
    document.getElementById('example')
)
  • Clock要求的name入参类型是string
  • 要加入babel文件的引入
  • 传入了number,所以警告:
react.development.js:372 Warning: Failed prop type: Invalid prop `name` of type `number` supplied to `Clock`, expected `string`.
    in Clock

支持的类型:

	// 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
	optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,
 
    // 可以被渲染的对象 numbers, strings, elements 或 array
    optionalNode: React.PropTypes.node,
 
    //  React 元素
    optionalElement: React.PropTypes.element,

八、事件

元素里面的事件,需要调用函数来触发。

class Toggle extends React.Component{
    constructor(props){
        super(props);
        this.state={isToggleOn:true}
        this.handleClick=this.handleClick.bind(this) //需要绑定this
    }
    handleClick(){
        this.setState({isToggleOn:!this.state.isToggleOn})
    }
    render(){
        return <button onClick={this.handleClick}>
            {this.state.isToggleOn?'ON':'OFF'}
        </button>
    }

}
ReactDOM.render(
    <Toggle></Toggle>,
    document.getElementById('example')
)
  • 这里写了一个开关功能,点击变ON,再次点击变OFF,以此循环。
  • 设置buttom的 onClick={this.handleClick},表示当buttom被点击,会调用handleClick函数
  • 这里需要事先为handleClick函数绑定this,否则在handleClick函数里面访问this会返回undefined,而不是Toggle的实例

不需要绑定this的方法

方法1:

class ToggleTest extends React.Component {
    handleClick = () => {
        console.log('this is ', this)
    }

    render() {
        return <button onClick={this.handleClick}>xxx</button>
    }
}
ReactDOM.render(<ToggleTest ></ToggleTest >,document.getElementById('example'))
  • 修改定义handleClick 的方法,加上() =>,这个叫:属性初始化器语法

方法2:

class ToggleTest extends React.Component {
    handleClick ()  {
        console.log('this is ', this)
    }

    render() {
        return <button onClick={(e)=>this.handleClick(e)}>xxx</button>
    }
}
ReactDOM.render(<ToggleTest ></ToggleTest >,document.getElementById('example'))
  • onclick的时候,填入箭头函数。这里相当于重新定义了一个函数。如果向更低阶的元素组件传递函数,会重复构建函数,导致性能问题。

传递参数

class ToggleParam extends React.Component {
    handleClick(name, e) { // e是React的事件实例,需要放在最后面
        alert(name)
    }
    handleClick1(name) { // 也可以不要e参数
    }

    render() {
        return <button onClick={this.handleClick.bind(this, 'kevinlu')}>xxx</button>
    }
}
ReactDOM.render(<ToggleParam></ToggleParam>,document.getElementById('example'))
  • e是React的事件实例,需要放在所有参数最后面,也可以不设置e参数。
  • 这里通过bind的方法,绑定this和name参数,bind会返回一个新函数,当按钮被点击,React会调用该函数
  • 可以通过e.preventDefault();来阻止默认的操作。例如点击链接会跳转到新页面,这个命令可以阻止跳转操作。

九、条件渲染

1. if条件判断

function If(props) {
    if (props.isLogin) {
        return <h1>is Login</h1>
    } else {
        return <h1>is not Login</h1>
    }
}

    class Login extends React.Component {
        constructor(props) {
            super(props);
            this.state = {isLogin: false}
        }

        login() {
            this.setState({isLogin: true})
        }

        logout() {
            this.setState({isLogin: false})
        }

        render() {
            if (this.state.isLogin)
                return <div>now is login <button onClick={this.logout.bind(this)}>logout</button></div>
            else
                return <div>now is logout <button onClick={this.login.bind(this)}>login</button></div>
        }

    }
ReactDOM.render(<If isLogin={false}></If>, document.getElementById('example'))
  • If和Login两个元素组件,都是根据属性,来判断render的返回html

2.&&运算符

function At(props) {
    return <div>kkkkk {props.number > 0 && <h2>number is {props.number} </h2>}</div>
}
  • 如果props.number,显示元素<h2>number is {props.number} </h2>

3. 三目运算符

和IF类似,不过它可以在元素里面通过{}的方式定义:{props.number ?"large 0":'least 0'}

4. 阻止渲染

直接让元素组件返回null,就不会渲染这个元素。

function NoneTest(props) {
    if (props.number > 0)
        return <div>number is {props.number}</div>
    else
        return null
}

十、数组

var l = [1, 2, 3, 4, 5]
const ele = <ul>
    {l.map(
        (number) => <li key={number}>{number}</li>
    )}
</ul>
ReactDOM.render(ele, document.getElementById('example'))
  • 通过JS的map方法,遍历l数组,对每个元素执行(number) => <li>{number}</li>函数,返回的是元素数组
  • 对于元素列表,React会遍历数组,逐个显示,所以url标签里面会有5个li标签
  • 数组构建的元素,需要包含一个唯一的key,不然会报错。key一般是数组元素的id,如果没有id可以用下标,即(number,index) => <li key={index}>{number}</li>

十一、组件API

  • setState(object nextState[, function callback]) 设置状态
    *callback可选,会在修改状态后,渲染DOM后被调用
    • 没有设置的key,不会被替换
  • replaceState(object nextState[, function callback]) 替换状态
    • 和setState类似,区别是没有设置的key,会被设置为undefined
  • setProps(object nextProps[, function callback]) 设置props
  • replaceProps(object nextProps[, function callback])
  • forceUpdate([function callback]) 通知组件重绘DOM
  • DOMElement findDOMNode() 获取组件对应的DOM元素,如果没有,返回null
  • bool isMounted() 判断组件挂载状态

十二、组件生命周期

生命周期有:

  • Mounting 已挂载,也就是已插入到真实的DOM
  • Updating 更新中,重新渲染
  • Unmounting 已移出真实的DOM

方法:

  • componentWillMount 组件将要挂载前调用
  • componentDidMount 组件挂载完成调用
  • componentWillReceiveProps 组件接收一个新的prop时被调用,第一次渲染不会调用。主要用户组件的更新
  • shouldComponentUpdate 返回bool值,在组件接收到新的props或者state时被调用,第一次渲染不调用
  • componentWillUpdate 在组件接收到新的props或者state但还没有render时被调用
  • componentDidUpdate 更新完成后调用,第一次渲染不调用
  • componentWillUnmount 组件被移除前调用

十三、AJAX

一般在componentDidMount 中通过AJAX获取数据,然后通过setState更新组件。

class AjaxTest extends React.Component{
    constructor(props) {
        super(props);
        this.state={content:"test"}
    }

    componentDidMount() {
        this.req = $.get('/demo/react_demo/data', function (res) {
            this.setState({content: res})
        }.bind(this))
    }
    componentWillUnmount(){
        this.req.abort()
    }
    render(){
        return <div>{this.state.content}</div>
    }
}
  • 这里用了jq的ajax方法,导入:`
  • get的回调函数需要bind this 不然回调函数里面访问this,不会执行组件实例
  • componentWillUnmount当组件被卸载,关闭http请求
    `

十四、表单

class Form extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name: "unknow"}
    }

    onAttrChange(key, e) {
        var state = {}
        state[key] = e.target.value
        this.setState(state, () => console.info('new name', this.state.name))
    }

    render() {
        return <div>名字:<input value={this.state.name} onChange={this.onAttrChange.bind(this, 'name')}></input></div>
    }
}
  • 表单的值修改后,需要通过onChange事件,更新新的值到组件的变量

如果是嵌套组件,需要把父组件的this传给子组件

function Input(props) {
    return <input value={props.this.state.name} onChange={props.this.onAttrChange.bind(props.this, 'name')}></input>
}

class Form extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name: "unknow"}
    }

    onAttrChange(key, e) {
        var state = {}
        state[key] = e.target.value
        this.setState(state, () => console.info('new name', this.state.name))
    }

    render() {
        return <div>名字:<Input this={this}></Input></div>
    }
}

Select

class Select extends React.Component {
    constructor(props) {
        super(props);
        this.state = {value: "unknow"}
    }

    onAttrChange(key, e) {
        var state = {}
        state[key] = e.target.value
        this.setState(state, () => console.info('new value', this.state.value))
    }

    render() {
        return <div><select value={this.state.value} onChange={this.onAttrChange.bind(this, 'value')}>
            <option value="gg">Google</option>
            <option value="fb">Facebook</option>
        </select></div>
    }
}

十五、Ref

我们可以在元素里面的某个标签,设置一个id,然后通过ref,获取到这个标签。

class RefTest extends React.Component {
    constructor(props) {
        super(props);
    }

    onClick() {
        var name = this.refs.myRef //获取到input实例
        console.info(name.value)//获取input的值
    }

    render() {
        return <div>
            name: <input ref="myRef"></input>
            <button onClick={this.onClick.bind(this)}>BTN</button>
        </div>
    }
}
  • 这里我们为input 加了一个ref属性
  • 在onClick里,通过this.refs.myRef可以获取到这个input的实例,然后获取它的value值
posted @ 2021-09-27 17:43  Xjng  阅读(164)  评论(0编辑  收藏  举报