React基本使用

588 阅读3分钟

为什么选择React?

  • 组件化的开发构思,项目便于维护
  • 只需关注业务逻辑,高效快速更新DOM
  • 海量的周边生态,友好的开发环境

hello React

 <div id="root"></div>
    <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>
        const title = React.createElement('h1', null, 'hello React')
        ReactDOM.render(title, document.getElementById('root'));
    </script>

image.png

使用JSX语法

JSX相当于是语法糖,需要bebel进行编译,JSX语法规则如下:

1.定义虚拟dom时,不要用引号

2.标签中混入js表达式时用{}

3.样式的类名用className

4.内联样式,常用style={{}}

5.只有一个根标签

6.标签必须闭合

7.标签首字母 若是小写,则转换味html中同名元素;若是大写,则当组件处理

  <div id="root"></div>
    <!-- 虚拟dom本质是Object类型的对象 -->
    <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>
    <script type="text/babel">
        const title = 'hello JSX语法';
        const vDOM =(<h1 style={{color: 'red' }}>{title}</h1>)      
        ReactDOM.render(vDOM,document.getElementById('root'));
    </script>

image.png

解决React使用JSX语法在Vscode自动保存后报错问题:

下载此插件

image.png

卸载此插件

image.png

JSX语法练习

    <div id="root"></div>
    <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>
    <script type="text/babel">
        const data = ['vue','react','angular'];
         ReactDOM.render(
        <div>
            <h1 style={{color: 'red'}}>前端三大主流框架</h1>
            <ul>
                {data.map((item,index)=>{
                    return <li key={index}>{item}</li>
                })}
            </ul>
        </div>,document.getElementById('root'));
    </script>

image.png

组件基础

React中定义组件的方式有:函数组件与 class 组件

<div id="root1"></div>
    <div id="root2"></div>
    <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>
    <script type="text/babel">
        //1.创建函数式组件
        function MyComponent(){
            return <h1>组件基础--函数式组件【适用于简单组件--无状态】</h1>
        };
        //渲染到页面
        ReactDOM.render(<MyComponent/>,document.getElementById('root1'));
  
        //2.创建类组件
        class MyComp extends React.Component {
            //render是在MyComp的原型对象,供实例使用
            render(){
                console.log(this)  //组件实例对象
                return (
                    <h1>组件基础--类组件【适用于复杂组件--有状态】</h1>
                )
            }
        }
        ReactDOM.render(<MyComp/>,document.getElementById('root2'));
    </script>

image.png

执行了ReactDOM.render(<MyComp/>,document.getElementById('root2'))之后发生了什么?

1.React解析组件标签,找到了MyComp组件;

2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用原型上的render方法;

3.将render返回的虚拟dom转为真实dom,随后渲染到页面。

组件实例属性state

    <div id="root"></div>
    <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>
    <script type="text/babel">
      //注意:类中的方法局部开启了严格模式 若不是组件实例调用则不会指向实例 指向undefined
       class Weather extends React.Component{
           constructor(props){
               console.log('constructor')  // 调用1次
               super(props)
               this.state = {isHot:false} 
            // this.changeWeather = this.changeWeather.bind(this)  改变类中方法this的指向 生成一个新的函数
               this.demo = this.changeWeather.bind(this)
           }
           render(){
               console.log('render')  //调用1+n次  n为更新状态的次数
               // console.log(this) 组件实例对象
               return (
                   <div>
                       <h1>组件核心之一state</h1>
                       <h2 onClick={this.demo}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h2>
                    </div>
               )
           }
           changeWeather(){
               //调用n次
               const isHot = this.state.isHot
               //借助内置api更改state
               this.setState({
                   isHot:!isHot
               })
           }
       }
       ReactDOM.render(<Weather/>,document.getElementById('root'));

简写如下:

//同上效果
<script type="text/babel">
         class Weather extends React.Component {
             state = {isHot:true}   //这样写也是实例上的属性
             render(){
                 const {isHot} = this.state
                return (
                   <div>
                       <h1>组件核心之一state</h1>
                       <h2 onClick={this.changeWeather}>今天天气{isHot ? '炎热' : '凉爽'}</h2>
                    </div>
               )
             }
             //箭头函数中this指向声明时上下文
             changeWeather = ()=>{
                 const {isHot} = this.state
                 this.setState({isHot:!isHot})
             }
         }
         ReactDOM.render(<Weather/>,document.getElementById('root'));
</script>

组件实例属性props

当react元素作为自定义组件,将jsx所接受的属性转换成单个对象传递给组件,这个对象被称为“props”(就是父组件传递给子组件的对象)

<div id="root"></div>
    <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>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.7.2/prop-types.min.js"></script>
    <script type="text/babel">
        class Person extends React.Component {
            render(){
                const {name,age} = this.props    //注意:props是只读的
                return (
                    <ul>
                        <li>name:{name}</li>
                        <li>age:{age}</li>
                    </ul>
                )
            }
        }
        //类型验证
        Person.propTypes = {
            name: PropTypes.string.isRequired,
            age:PropTypes.number,
        }
        //默认值
        Person.defaultProps = {
            age:18
        }
         ReactDOM.render(<Person name='jiangyao'/>,document.getElementById('root'))
        // const batch = {name:'jiangyao',age:21}
        // ReactDOM.render(<Person {...batch}/>,document.getElementById('root'));
    </script>

也可以这样写:

<script type="text/babel">
         class Person extends React.Component {
            //构造器中是否接收props 是否传递给super 取决于是否希望在构造器中通过this访问props
            constructor(props){
                super(props)
                console.log('constructor',this.props)
            }
            //静态属性
            static propTypes = {
                 name: PropTypes.string.isRequired,
                 age: PropTypes.number,
            }

            static defaultProps = {
                 age:18
            }
            render(){
                const {name,age} = this.props
                return (
                    <ul>
                        <li>name:{name}</li>
                        <li>age:{age}</li>
                    </ul>
                )
            }
        }
         ReactDOM.render(<Person name='jiangyao'/>,document.getElementById('root'))
    </script>

函数式组件虽然不可以用实例的属性state和refs,但可以用props,如下:

<script type="text/babel">
       function Person(props){
           //函数式组件也可以用props
           const {name,age} = props
            return (
                <ul>
                    <li>name:{name}</li>
                    <li>age:{age}</li>
                 </ul>                                             
             )
        }
        Person.propTypes = {
            name: PropTypes.string.isRequired,
            age: PropTypes.number,
        }
        Person.defaultProps = {
            age:18
        }
        ReactDOM.render(<Person name='jiangyao'/>,document.getElementById('root'));
</script>

组件实例属性refs

定义ref的三种形式:

1.字符串形式的ref可能会在未来的版本被废弃,效率不高

2.使用回调函数(内联函数)形式的ref,如下(第二个input示例),使用回调函数(内联函数)形式的ref,在更新过程中它会被执行两次

3.函数直接绑定在类上(第三个input示例)

 <script type="text/babel">
        class Demo extends React.Component{
            render(){
                return (
                    <div>
                        <input ref="input" type="text" placeholder="点击按钮显示数据"/>
                        <button onClick={this.showData}>点我</button>
                        <input onBlur={this.showData2} ref={currentNode=>this.input2=currentNode} type="text" placeholder="失去焦点显示数据"/>
                        <input type="text" ref={this.showData3}/>
                    </div>
                )
            }
            showData = ()=>{
                console.log(this)  //组件实例有refs
                console.log(this.refs.input)   //得到真实dom
                const {input} = this.refs
                alert(input.value)
            }
            showData2 = ()=>{
                const {input2} = this  //注意区别,直接把input2挂在组件实例上
                alert(input2.value)
            }
            showData3 = (currentNode)=>{
                this.input3 = currentNode
                console.log(currentNode)
            }
        }
        ReactDOM.render(<Demo/>,document.getElementById('root'));
    </script>

React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点

<script type="text/babel">
         class Demo extends React.Component{
             myRef = React.createRef()
             showData = ()=>{
                 console.log(this.myRef)   
                 alert(this.myRef.current.value)
             }
             render(){
                 return (
                    <div>
                        <input ref={this.myRef} type="text" placeholder="点击按钮显示数据"/>
                        <button onClick={this.showData}>点我</button>
                    </div>
                 )
             }
         }
         ReactDOM.render(<Demo/>,document.getElementById('root'))
    </script>

React中事件处理

1.通过onXxxx属性指定事件处理函数

  • React使用的是自定义(合成)事件,而不是使用原生DOM的事件---为了更好的兼容
  • React中的事件是通过事件委托方式处理的(委托给最外层根元素) 2.通过event.target得到发生事件的DOM元素对象
<script type="text/babel">
        //当发生事件的节点和操作的节点是同一个时可以用event获取值,不要过度使用ref
        class Demo extends React.Component {
            render(){
                return (
                        <input onBlur={this.showVal} type="text"/>
                )
            }
            showVal = (event)=>{
                alert(event.target.value)
            }
        }
        ReactDOM.render(<Demo/>,document.getElementById('root'));
</script>

非受控组件

<script type="text/babel">
        //非受控组件---现用现取
         class Login extends React.Component {
             render(){
                 return (
                    <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
                        用户名:<input ref={current=>this.username=current} type="text" name="username"/>
                        密码:<input ref={current=>this.password=current} type="password" name="password"/>
                        <button>登录</button>
                    </form>
                 )
             }
             handleSubmit = (event)=>{
                event.preventDefault()  //阻止表单默认提交
                const {username,password} = this
                alert(`用户名:${username.value} 密码:${password.value}`)
             }
         }
         ReactDOM.render(<Login/>,document.getElementById('root'));
    </script>

受控组件

类似vue的里面的双向绑定指令v-model

    <script type="text/babel">
         class Login extends React.Component {
            //初始化状态
            state = {
                 username:'',
                 password:''
            }
            render(){
                const {username,password} = this.state
                 return (
                    <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
                        用户名:<input onChange={this.handleName} type="text" name="username" value={username}/>
                        密码:<input onChange={this.handlePass} type="password" name="password" value={password}/>
                        <button>登录</button>
                    </form>
                 )
            }
            handleSubmit = (event)=>{
                event.preventDefault()  //阻止表单默认提交
                const {username,password} = this.state
                alert(`用户名:${username} 密码:${password}`)
            }
            handleName = (event)=>{
                 this.setState({username:event.target.value})
            }
            handlePass = (event)=>{
                this.setState({password:event.target.value})
            }
         }
         ReactDOM.render(<Login/>,document.getElementById('root'));
    </script>

引入生命周期

在具有许多组件的应用程序中,当组件被销毁时释放所占用的资源是非常重要的。

当 A 组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器。这在 React 中被称为“挂载(mount)”。

同时,当 DOM 中 A 组件被删除的时候,应该清除计时器。这在 React 中被称为“卸载(unmount)”。

我们可以为 class 组件声明一些特殊的方法,当组件挂载或卸载时就会去执行这些方法,这些方法叫做“生命周期方法”。

<script type="text/babel">
       //创建组件
        class Test extends React.Component {
            state = {
                opacity:0.5
            }
            //卸载组件
            death = ()=>{
                ReactDOM.unmountComponentAtNode(document.getElementById('root'))
            }
            //生命周期--组件挂载完毕后调用
            componentDidMount(){
                this.timer = setInterval(()=>{
                    let {opacity} = this.state
                    opacity -= 0.1
                    if(opacity <= 0) opacity = 1
                    this.setState({opacity})
                },200)
            }
            //生命周期--组件将要卸载
            componentWillUnmount(){
                clearInterval(this.timer)
            }
            render(){
                //在这里写定时器会无限递归 改变状态便会重新调用render
                // setInterval(()=>{
                //     let {opacity} = this.state
                //     opacity -= 0.1
                //     if(opacity <= 0) opacity = 1
                //     this.setState({opacity})
                // })
                return(
                    <div>
                        <h1 style={{opacity:this.state.opacity}}>react生命周期学习</h1>
                        <button onClick={this.death}>点击</button>
                    </div>
                )
            }
        }
        ReactDOM.render(<Test/>,document.getElementById('root'));
    </script>

旧版生命周期

image.png 应用示例:

    <div id="root"></div>
    <div id="test"></div>
    <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>
    <script type="text/babel">
        //创建组件
        class LifeCycle extends React.Component{
            //挂载时--构造器
            constructor(props){
                console.log('constructor')
                super(props)
                this.state = {count:0}
            }
            add = ()=>{
                const {count} = this.state
                this.setState({count:count+1})
            }
            death = ()=>{
                ReactDOM.unmountComponentAtNode(document.getElementById('root'))
            }
            force = ()=>{
                //强制更新
                this.forceUpdate()
            }
            //挂载时--组件将要挂载的钩子
            componentWillMount(){
                console.log('componentWillMount')
            }
            //挂载时--组件挂载完毕的钩子
            componentDidMount(){
                console.log('componentDidMount')
            }
            //控制组件更新的钩子
            shouldComponentUpdate(){
                console.log('shouldComponentUpdate')
                return true
            }
            //组件将要更新的钩子
            componentWillUpdate(){
                console.log('componentWillUpdate')
            }
            //组件更新完毕的钩子
            componentDidUpdate(){
                console.log('componentDidUpdate')
            }
             //组件将要卸载的钩子
            componentWillUnmount(){
                console.log(' componentWillUnmount')
            }
            //render挂载钩子
            render(){
                console.log('render')
                const {count} = this.state
                return (
                    <div>
                        <h2>当前数字为:{count}</h2>
                        <button onClick={this.add}>点我+1</button>
                        <button onClick={this.death}>点击卸载组件</button>
                        <button onClick={this.force}>不更改数据,强制更新</button>
                    </div>
                )
            }
            
        }
        class Father extends React.Component{
            constructor(props){
                super(props)
                this.state = {name:'jack'}
            }
            test = ()=>{
                const {name} = this.state
                this.setState({name:'sandwich'})
            }
            render(){
                const {name} = this.state
                return (
                    <div>
                        <div>Father</div>
                        <button onClick={this.test}>点击改变数据</button>
                        <Son name={name}/>
                    </div>
                )
            }
        }
        class Son extends React.Component{
            //组件将要接收新的props的钩子
            componentWillReceiveProps(props){
                console.log(props)
                console.log('componentWillReceiveProps')
            }
            render(){
                return (
                    <div>Son:{this.props.name}</div>
                )
            }
        }
        ReactDOM.render(<Father/>,document.getElementById('test'))
        ReactDOM.render(<LifeCycle/>,document.getElementById('root'));
    </script>

新版生命周期

image.png

新增钩子:getDerivedStateFromProps() getSnapshotBeforeUpdate()

即将废弃的钩子:componentWillMount() componentWillReceiveProps() componentWillUpdate()

参考:react.docschina.org/docs/react-…