React学习笔记 --- jsx核心语法(下)

835 阅读10分钟

一、 在属性中使用变量

基本使用

this.state = {
    imgUrl: 'http://p1.music.126.net/uFtGUm56jgbGlBQOk_PGcw==/109951164832828416.jpg'
}

render() {
    {/*
       JSX主要写在render函数中,随后将界面结构交由React进行渲染
    */}
    const { imgUrl } = this.state

    return (
        <div>
            {/* 可以直接使用大括号语法在属性中使用变量 */}
            <img src={ imgUrl } alt="网络图片" />
        </div>
    )
}

此时在界面上就已经出现了一张图片

dM4nFH.png

但是这个图片可能比较大,所以我们可以使用如下几种方式来修改这张图片的大小

  1. 通过css来进行设置

    • 浏览器要解析CSS代码
    • 浏览器需要缩放图片

    所以这种设置方式不被推荐

  2. 在获取图片URL的时候,传递参数,以便于获取不同尺寸的图片(推荐)

    例如

    //  原始图片地址
    http://p1.music.126.net/uFtGUm56jgbGlBQOk_PGcw==/109951164832828416.jpg
    
    // 在请求的时候,告诉服务器 需要多高,多宽的图片
    // 请求方式1 --- 查询字符串 (网易云)
    http://p1.music.126.net/uFtGUm56jgbGlBQOk_PGcw==/109951164832828416.jpg?param=140x140
    
    // 请求方式2 --- params参数 (简书)
    https://upload.jianshu.io/users/upload_avatars/9838715/bb84ff12-e1b8-4226-93ba-5271229c29c0.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240
    
    • 使用这种方式在浏览器端不需要对图片的大小上进行过多的处理
    • 会增加服务器处理图片的压力

调用函数

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            imgUrl: 'http://p1.music.126.net/uFtGUm56jgbGlBQOk_PGcw==/109951164832828416.jpg'
        }
    }

    formatImg(url, w, h) {
        return `${url}?param=${w}x${h}`
    }

    render() {
        const { imgUrl } = this.state

        return (
            <div>
                {/*
                  可以直接在属性这里调用函数表达式来实现图片地址的格式化操作
                  这一操作类似于Vue中的过滤器 imgUrl | formatImg
                */}
                <img src={ this.formatImg(imgUrl, 140, 140) } />
            </div>
        )
    }
}

一些特殊的名称

{/*
    JSX 将JS 和 html 混合在了一起
    但是html中的一些属性 会和 js中的一些属性重名
    此时html中的相关属性就需要使用JSX为其设置的alias

    例如: for ---> htmlFor  ===> 比如 <label htmlFor="XXX"> xxx </label>
          class ---> className
*/}

{/* 如果没有使用别名,不会报错,但是会有warning */}
<div className="box title">我是一段文本</div>

样式的绑定

  1. 动态绑定class样式
class App extends React.Component{
    constructor() {
        super()

        this.state = {
            active: true
        }
    }
    
    render() {
        const { active } = this.state

        return (
            <div>
              {/*
                1. 动态绑定样式可以使用的是字符串拼接
                2,因为需要先判断三元运算符, 所以使用括号进行包裹
                3. 因为样式和样式之间使用空格进行隔开,所以在title后边需要加一个空格 (★★★)
              */}
              <div className={ 'box title ' + ( active ? 'active' : '' ) }>一段测试文本</div>
            </div>
        )
    }
}
  1. 动态绑定style样式
class App extends React.Component{
    constructor() {
        super()

        this.state = {
            active: true
        }
    }
    render() {
        const { active } = this.state

        return (
            <div>
                {/*
                1. 这里有2个大括号  外层的括号,表明内部需要使用的是jsx语法
                2. 内部的大括号 表明的是 内部需要传递的是一个样式对象
                3,一样特设样式的样式名需要进行转换
                   例如: font-size ---> fontSize
            	   或者 `font-size`: '18px' 注意这里的`font-size`需要使用单引号括起来,表示是字符串
                4. 样式对象中的属性值需要传递的解释字符串类型的数据
             */}
                <div style={ { color: 'red', 'fontSize': '18px' } }>一段测试文本</div>
            </div>
        )
    }
}

二、 React中的事件绑定

基本使用

class App extends React.Component{
    constructor() {
        super()

        this.state = {
        }
    }

    render() {
        return (
            <div>
                {/*
              1. 原生的js事件是全部都是小写的, 但是JSX中的事件其是使用小驼峰的书写方式,其是React对原生的js事件的封装
              2. 在{ } 中传入事件 事件需要加上this,表示的是调用的是当前实例身上的this
            */}
                <button onClick={ this.handleClick }>按钮</button>
            </div>
        )
    }

    handleClick() {
        console.log('我被点击了')
    }
}

事件中的this指向

暴露出来的问题

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            message: 'React事件中的this指向问题'
        }
    }

    render() {
        return (
            <div>
            {/*
              1. React中的事件在调用我们传入的callback的时候
                 调用方式是 this.handClick.call(undefined)
                 所以在React的事件绑定中this指向会转换为undefined
            */}
            <button onClick={ this.handleClick }>按钮</button>
</div>
)
}

handleClick() {
    {/*
          1. 此时点击是会报错的
             因为此时这里的this的值是undefined
        */}
    console.log(this.state.message)
}
}

解决方式1 --- 使用bind方法(隐式调用)

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            message: 'React事件中的this指向问题'
        }
    }

    render() {
        return (
            <div>
                {/*
              bind 操作会返回给我们一个改变this后的新的函数
              所以此时的this被修改回了实例对象
            */}
                <button onClick={ this.handleClick.bind(this) }>按钮</button>
            </div>
        )
    }

    handleClick() {
        console.log(this.state.message)
    }
}

遗留的问题

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            message: 'React事件中的this指向问题'
        }
    }

    render() {
        return (
            <div>
                {/*
                此时有多个元素需要设置点击事件,此时就会有大量的重复的bind操作,代码十分的冗余
              */}
                <button onClick={ this.handleClick.bind(this) }>按钮1</button>
                <button onClick={ this.handleClick.bind(this) }>按钮2</button>
                <button onClick={ this.handleClick.bind(this) }>按钮3</button>
                <button onClick={ this.handleClick.bind(this) }>按钮4</button>
                <button onClick={ this.handleClick.bind(this) }>按钮5</button>
                <button onClick={ this.handleClick.bind(this) }>按钮6</button>
            </div>
        )
    }

    handleClick() {
        {/*
          1. 此时点击是会报错的
             因为此时这里的this的值是undefined
        */}
        console.log(this.state.message)
    }
}

修改如下:

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            message: 'React事件中的this指向问题'
        }

        // 在初始化实例的时候,就把实例中的this修改掉
        // 所以就不需要在重复书写bind部分的代码了
        this.handleClick = this.handleClick.bind(this)
    }

    render() {
        return (
            <div>
                {/*
                此时有多个元素需要设置点击事件,此时就会有大量的重复的bind操作,代码十分的冗余,减低了运行性能
              */}
                <button onClick={ this.handleClick }>按钮1</button>
                <button onClick={ this.handleClick }>按钮2</button>
                <button onClick={ this.handleClick }>按钮3</button>
                <button onClick={ this.handleClick }>按钮4</button>
                <button onClick={ this.handleClick }>按钮5</button>
                <button onClick={ this.handleClick }>按钮6</button>
            </div>
        )
    }

    handleClick() {
        console.log(this.state.message)
    }
}

方式1 虽然是React中的this指向修改为了组件的实例对象

但是如果在点击的时候,需要给handleClick传递不同的参数

此时就无法在constructor中统一修改this指向了

所以这种方式不推荐使用

解决方式2 --- 使用箭头函数

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            message: 'React事件中的this指向问题'
        }
    }

    render() {
        return (
            <div>
                <button onClick={ this.handleClick }>按钮</button>
            </div>
        )
    }

    handleClick = () => {
        {/*
          因为箭头函数内部是没有this的
          所以根据js的作用域链,箭头函数内部的this会向上一级去进行寻找,直到最后寻找到全局作用域

          所以此时箭头函数内部的this就是外层的this,也就是组件的实例对象

          但是 在ES6中如果需要使用箭头函数定义方法,只能通过class fields的方式去进行进行定义
          但是这种定义方式本质上定义的是成员属性,不是方法,所以依旧需要进行修改

        */}
        console.log(this.state.message)
    }
}

方式3 ---事件监听时传入箭头函数

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            message: 'React事件中的this指向问题'
        }
    }

    render() {
        return (
            <div>
                {/*
                在函数调用的外围封装一层箭头函数,
                因为箭头函数内部无this,所以给箭头函数显示绑定this是没有什么作用和意义的
                有因为作用域链,此时方法handleClick的调用的this,就是外层的this
                也就是当前的组件对象
              */}
                <button onClick={ () => this.handleClick }>按钮</button>
            </div>
        )
    }

    handleClick() {
        console.log(this.state.message)
    }
}

使用方式3来进行this的修改,定义的callback是一个函数,不是方式2中的成员变量,又比较便于进行参数的传递,

所以方式3 推荐使用

三、 React事件调用和传递参数

3.1 事件调用

原生的事件调用

<div id="dv">点击我</div>

<script>
    document.getElementById('dv').addEventListener('click', e => console.log(e))
</script>

React中事件的调用

class App extends React.Component{
    constructor() {
        super()
    }

    btnClick(e) {
        console.log(e)
    }

    render() {
        return (
            <div onClick={e => this.btnClick(e)}>
            {/*
              1. react中的事件是对原生的js浏览器事件进行了封装
                 在原生的js事件中,有一个默认参数event,是浏览器中和当前事件相关的所有相关属性的集合对象(俗称事件对象)
                 在React中, 调用事件函数的时候,其也会传递进来一个事件对象,这个事件对象是React对原生的浏览器事件对象的二次封装				   形成的对象

              2. 在使用箭头函数作为React的事件函数绑定的this,因为React调用的是外部的箭头函数,所以其默认的event对象是传递给箭				  头函数的,所以需要自己手动的将其传递给内部实际调用的那个对象

              3. 使用箭头函数的封装实际调用函数的好处是:
                   1. 内部thi指向 ---- React的实例对象
                   2. 函数调用的时候,是自己调用的,不是react帮助我们进行调用的,所以对于参数的传递和使用更好的把控
            */}
                点击我
            </div>
        )
    }
}

原生的事件对象

原生的事件对象

React中的事件对象

React中的事件对象

3,2 参数传递

需求:

​ 有一个列表,点击列表项,在控制台中输出元素名称,索引值,this对象

使用bind来进行调用

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            persons: [
                '张三三',
                '李思思',
                '网无无',
                '刘莉莉'
            ]
        }
    }

    liClick(item, index, e) {
        console.log(item, index, e)
    }

    render() {
        const { persons } = this.state

        return (
            <ul>
                {
                    persons.map((item, index) => {
                        return (
                            <li onClick={this.liClick.bind(this, item, index)}>
                                {/*
                      如果使用bind进行调用的时候,可以在bind后面进行传参
                      但是 其不可以写在构造函数中 如 this.liClick = this.liClick.bind(this, item, index)
                      因为在构造函数中并不存在变量item 和 index

                      在liClick进行调用的时候, 其依旧会传递进一个event对象,但是这个event对象不是第一个参数,而是函数调用时候的最后一个参数
                      liClick(item, index, e) (★★★)
                    */}
                                {item}
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}

使用箭头函数来进行调用

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            persons: [
                '张三三',
                '李思思',
                '网无无',
                '刘莉莉'
            ]
        }
    }

    liClick(item, index, e) {
        console.log(item, index, e)
    }

    render() {
        {/*
            对象解构
         */}
        const { persons } = this.state

        return (
            <ul>
                {
                    persons.map((item, index) => {
                        return (
                            <li onClick={e => this.liClick(item, index, e)}>
                                {/*
                       				 手动传递event参数
                     			 */}
                                {item}
                            </li>
                        )
                    })
                }
            </ul>
        )
    }
}

四、 条件渲染

需求: 用户登录的时候,在页面显示欢迎回来, 如果用户没有登录的时候,显示请先登录

if表达式

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            isLogin: false
        }
    }

    render() {
       {/* 在每次调用render函数的时候,是重新开辟了一个函数执行上下文,在其中开辟了一个新的常量 */}
        const { isLogin } = this.state
		
        let msg = null

        if (isLogin) {
            msg = <h2>欢迎回来</h2>
        } else {
            msg = <h2>请先登录</h2>
        }

        return (
            <div>
                { msg }
            </div>
        )
    }
}

三目运算符

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            isLogin: false
        }
    }

    render() {
        const { isLogin } = this.state


        return (
            <div>
                <h2>{ isLogin ?  '欢迎回来' : '请先登录'} </h2>
            </div>
        )
    }
}

使用逻辑与运算符

需求:如果用户登录后,在界面上显示用户名,

​ 如果用户没有登录, 界面上什么也不显示

<div>
    {/*
               如果isLogin为true,就返回userName的值
               如果isLogin为false, 那么就直接返回false
               但是 React中 false在界面中不存在

               注意: 这种写法,如果isLogin的值是false的时候
                     其页面中存在一个空的<h2></h2>
            */}
    <h2>{ isLogin && userName } </h2>

    {/*
              使用这种方式去书写的时候,如果isLogin为false的时候
              其是不会存在空的h2标签的
            */}
    { isLogin && <h2>userName</h2> }
</div>

空的h2标签

4.1 模拟v-if 和 v-show

vue控制元素的显示和隐藏有2条常用的指令

v-if 通过移除元素和添加元素的方式来控制元素的显示和隐藏 适合于切换频率不高的元素

v-show通过设置元素的display属性的方式来控制元素的显示和隐藏 适合于切换频率比较高的元素

模拟v-if

class App extends React.Component{
    constructor() {
        super()

		this.state = {
          isLogin: false,
          userName: 'Klaus'
        }
    }

    render() {
        const { isLogin, userName } = this.state


        return (
            <div>
                <h2>{ isLogin && userName } </h2>
            </div>
        )
    }
}

模拟v-show

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            isLogin: false,
            userName: 'Klaus'
        }
    }

    render() {
        const { isLogin, userName } = this.state


        return (
            <div>
                <h2 style={{'display' : isLogin ? 'block': 'none'}}>{ userName } </h2>
            </div>
        )
    }
}

五、列表渲染

5.1 简单的列表渲染

class App extends React.Component{
    constructor() {
        super()

        this.state = {
            nums: [15, 20, 43, 54, 76, 123]
        }
    }

    render() {
        const { nums } = this.state


        return (
            <ul>
                {
                    nums.map(item => <li>{ item }</li>)
                             }
            </ul>
        )
    }
}

5.2 显示大于50的数据项

<ul>
    {
        nums
            .filter(item => item > 50)
            .map(item => <li>{ item }</li>)
                 }
</ul>

5.3 显示前4条数据项

<ul>
    {
        nums
            .slice(0, 4)
            .map(item => <li>{ item }</li>)
                 }
</ul>

补充

  1. 基本上大多数的html标签都是有title属性的,表示的是鼠标悬浮上去时候的提示信息

    <div title="这是div"></div>
    
    <img src="这是图片的地址" alt="这是图片加载失败时候的替换文本"  title="这是鼠标悬浮上去时候的提示文本" />
    
  2. 数值的map, filter方法都有2个参数,分别为callback,和 函数调用时候的this指向,每一个callback都有3个参数,分别为item,index,arr

   arr.map((item, index, arr) => return item, this指向)
   
   arr.filter((item, index, arr) => return boolean值, this指向)
  1. 数组slice方法的作用是截取数组中的某一段内容并进行返回
`arr.slice(start, end) ==> return 截取之后形成的新数组`

Tips: 1. 注意截取范围是[start, end)
	  2. `start`必须为大于0的正整数
      3. `end`可以是负整数,表示的是从后往前数
      	 例如 `-1 ==> -1 + arr.length ==> 即为数组胡总的最后一个元素`
      4. 如果`slice`方法只写了一个参数,那么第2个参数的默认值就是`arr.length`
      	 也就是数组的元素是从起始的位置开始,到数组的最后一个元素(`arr.length - 1`,所以`包含最后一个元素`)
         例如 [1, 2, 3, 4, 5].slice(2) ==> [3, 4, 5]

上一篇 React学习笔记 --- jsx核心语法(上) 下一篇 React 学习笔记 --- JSX的本质