[ 一起学React系列 -- 1 ] 信笔说JSX

本篇算是[一起学React系列]真正意义上的第一篇文章。笔者突然想开这个系列文章已经是酝酿了很久,不是因为自己对React有多深的理解,相反笔者对React仍停留在使用的层面,说到深入理解的话谈不上,科普的话算勉强;因为笔者是React的忠实粉丝,有计划得深入学习React也是自己在前端技术领域进一步发展的第一站。所以本系列文章更多是笔者一边学习一边分享学习心得的文章,算不上散播知识散播爱也达不到网上大佬文章的高度。只想和在座的每一位学习React的同仁一起学习,一起进步。其次本系列文章并不会过多得谈及高级概念或者专业词汇(因为笔者也懒得去理解它们,很累),所以本系列文章将大篇幅使用浅显易懂的词句分享笔者的学习心得。

JSX初识

JSX是个好东西啊!!!
作为React的核心之一, JSX语法深得React Developer的喜爱。首次接触它的人可能会被这个JSX词给吓到,认为这会是一个很难学习的东西然而事实并不是残酷的反而是宁人欣慰的, FaceBook选择JSX作为React的首选开发语法契合React本身组件化的原则, 原因是JSX是将 XMLJavaScript 融合而成的一种新的结构性语法, 可以用写XML的方式写JavaScript, 使得用JSX写组件更为便捷、结构更加清晰;举个例子:

如果我们想写如下的结构的组件

let comp = (
    <ul class="list">
       <li>one</li>
       <li>two</li>
    </ul>
)

JSX的写法与上面一致,只是需要将class改成className(因为class是html的保留字,JSX中需要使用className代替)

而JavaScript的写法比较繁琐:

let li1 = React.createElement('li', null, 'one');
let li2 = React.createElement('li', null, 'two');
let comp = React.createElement('ul', { className: 'list' }, li1, li2);

可以看出,使用JSX写组件方便的不是一点半点。点个赞!!

据了解JSX并不是Facebook首创,只是被Facebook发扬光大;

其次开发React并不只能用JSX, TypeScript可以了解下。

其次,因为JSX写出来的代码并不能直接在目前任何一款浏览器上运行,因此在运行前需要预先使用工具把它翻译成浏览器所能识别的ES5代码,目前主流的是使用 WebPack + Babel 进行编译、打包。使用create-react-app搭建的项目中可以看到相关的WebPack配置文件(需要先运行 npm run eject 将文件释放出来,配置文件在 config 目录中), 打开配置文件可以看到这么一段

// Process JS with Babel.
  {
    test: /\.(js|jsx|mjs)$/,
    include: paths.appSrc,
    loader: require.resolve('babel-loader'),
    options: {
      
      // This is a feature of `babel-loader` for webpack (not Babel itself).
      // It enables caching results in ./node_modules/.cache/babel-loader/
      // directory for faster rebuilds.
      cacheDirectory: true,
    },
  },

可以看出Webpack处理 js | jsx | mjs类型文件的时候用babel-loader进行翻译,最终生成浏览器可直接运行的ES5代码。

JSX写法介绍

写JSX代码,笔者最大的体会就是:不是在HTML中写JavaScript就是在JavaScript中写HTML。虽然很狗血,但是真的是这样。下面一个一个看:

写组件

React中最简单的组件可以简单到什么程度?答案是就好像写一个HTML中的DOM节点一样

<h1>Hello World</h1>

这个在React体系中就算一个最简单的组件,是不是和写HTML很像?因为编译器在遇到<>会将该对象当做组件解析,遇到{}会当做JavaScript解析。所以这也是为什么可以想在HTML中写js一样,举个例子:

function title(){
    const name = 'World';
    return (
        <h1>Hello {name}</h1>
    )
}

那么此时这个方法返回的组件就是

<h1>Hello World</h1>

是不是很简单呢?

当然React也支持编写复杂组件,方法有两种:
第一种是:

import React from 'react';

const Demo = React.createClass({
  render() {
    return (
      <div></div>
    );
  }
});
export default Demo;

另一种是使用ES6中class的写法,推荐该写法

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div></div>
        );
    }
}
export default Demo;

写JavaScript代码

上面有提到过在组件中的{}里写JavaScript, 这里笔者主要提及两个地方:
第一个:
因为JSX本质上也是写js,因此可以像常规使用变量一样在{}中使用变量(只要能访问到),比如我们写如下一个组件:

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        const age = 26;
        return (
            <div>明年我的年龄是:{age + 1} 岁</div>
        );
    }
}

export default Demo;

此时的展示内容是:

《[ 一起学React系列 -- 1 ] 信笔说JSX》

可以看出,我们不仅可以在{}访问某变量,还可以做逻辑处理。

第二个:
可以在组件的{}中访问该组件的this对象。本文不对该对象进行介绍,后续会有相关博客。不过笔者想说这个this很重要, 有了它可以做很多事。写例子吧, 我们将刚刚例子中的age变量放在this中试一下:

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
        this.age = 26
    }

    render() {
        return (
            <div>明年我的年龄是:{this.age + 1} 岁</div>
        );
    }
}

export default Demo;

效果一致:

《[ 一起学React系列 -- 1 ] 信笔说JSX》

逻辑判断

书写React组件的时候无法使用if...else相关判断, 只能使用三元运算。举个例子,我们实际开发中可以使用某个变量进行判断而渲染出不同的内容,比如:

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
        this.age = 18
    }

    render() {
        return (
            <div>
                {(this.age >= 18) ? <p>成年了</p> : <p>未成年</p>}
            </div>
        );
    }
}

export default Demo;

当年龄大于等于18岁的时候,就渲染出:
《[ 一起学React系列 -- 1 ] 信笔说JSX》

如果小于18岁的话,就渲染出:
《[ 一起学React系列 -- 1 ] 信笔说JSX》

注:这种写法在实际开发中经常用到,多多注意

样式处理

在React中添加样式和常规HTML+CSS开发是一样的,可以使用行内样式也可以使用class(React中是className)添加样式。这里主要介绍行内样式的处理,因为使用className处理样式又涉及到另外的技术了,有兴趣的朋友可了解下css module
JSX语法写样式和平常我们写HTML行内样式大同小异,到底异在哪儿?我们平时写HTML样式是这样写的:

<div style="background:red"></div>

就是给DOM元素添加style属性, 然后用分号分开的样式组成的字符串进行赋值,其实JSX的写法类似,只是赋值的不是字符串而是一个对象:

<div style={{"background":"red"}}></div>

修改下刚才的例子

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
        this.age = 17
    }

    render() {
        return (
            <div>
                {(this.age >= 18) ? <div style={{'background': 'red'}}>成年了</div> : <div style={{'background': 'red'}}>未成年</div>}
            </div>
        );
    }
}

export default Demo;

结果显示

《[ 一起学React系列 -- 1 ] 信笔说JSX》

可能会有疑问为什么是对象?虽然是React的规定,但是笔者认为JSX本质也是JavaScript, 编译器去编译JSX代码时候读取一个对象远比分析一段字符串要来得快。哈哈。。。一方之言,看看就好。当然,如果样式对象有多个该肿么办?很简单,使用ES6的解构,举个例子:

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
        this.age = 17
    }

    render() {
        const style1 = {'background': 'red'};
        const style2 = {'color': 'yellow'};
        return (
            <div>
                {(this.age >= 18) ? <div style={{...style1, ...style2}}>成年了</div> : <div style={{...style1, ...style2}}>未成年</div>}
            </div>
        );
    }
}

export default Demo;

这样样式正常显示

《[ 一起学React系列 -- 1 ] 信笔说JSX》

批量渲染

最后来说一下批量渲染。什么意思呢?假如我们要从一个数据里取出数据并且渲染成一条一条的菜单项或者列表项,该如何做?前面说过,可以在组件的{}中写逻辑处理。最直接的方法如下:

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
        this.list = ['one', 'two', 'three']
    }

    render() {
        return (
            <ul>
                {
                    this.list.map(function (item) {
                        return (
                            <li>{item}</li>
                        )
                    })
                }
            </ul>
        );
    }
}

export default Demo;

《[ 一起学React系列 -- 1 ] 信笔说JSX》

这样写可以实现需求,但是有个问题是组件代码结构复杂且冗余、维护代价高,尤其是当需要渲染的项目有别的复杂的需求的话,那么此处的复杂度就无法预计了,因此最有效最可靠的办法就是将生成项目的逻辑单独写在一个方法里并把项目放入数组中返回,可以这样写:

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
        this.list = ['one', 'two', 'three']
    }

    createList = (listData) => {
        return listData.map(function (item) {
            return (
                <li key={item}>{item}</li>
            )
        });
    };

    render() {
        const lists = this.createList(this.list);
        return (
            <ul>{lists}</ul>
        );
    }
}

export default Demo;

这样不管需要渲染的项有多复杂,只需要关注那个方法即可。不过有两点需要注意:

1 组件中自定义的方法建议必须使用ES6的箭头函数,因为它能保证this的正确指向。

2 在组件同一个节点渲染多个相同结构项目的时候请记得加上
key,并且值需要是对于
它们(这几个项目)而言都是独一无二的。有利于优化。

事件绑定

最后一个重头戏了。前端开发中给DOM绑定事件的动作到处都是。虽说React对开发者和用户屏蔽了DOM但也提供了绑定事件的接口,幸运的是它和普通DOM事件绑定大同小异,异在哪儿?请参考样式绑定。。。哈哈
平时我们给DOM绑定事件很简单

<div onClick='function'></div>

这样就实现了事件的绑定。React给组件的某个节点绑定事件也是如此:

import React, {Component} from 'react';

class Demo extends Component {
    constructor(props) {
        super(props);
    }

    handler = () => {
        alert('Click!')
    };

    render() {
        return (
            <div onClick={this.handler}>
                点我
            </div>
        );
    }
}

export default Demo;

演示结果

《[ 一起学React系列 -- 1 ] 信笔说JSX》

之所以onClick后面是{}是因为需要从组件的this对象中取出该方法对象进行赋值。同时,我们在组件中定义的方法都在this对象中

好了,差不多JSX的介绍就结束了, 日常开发常用的东西都在这里了(仅限笔者日常开发哇!)。其实如果要把JSX讲解清楚,这种篇幅的文章根本不够用。所以需要的是我们在实际开发中不断得摸索和研究,实践方能出真知。另外如果大家需要实际操作的话, 可以将笔者已经搭建好的项目download下来,代码可自行修改,通过npm start运行即可。

    原文作者:风吹过的夏夜
    原文地址: https://segmentfault.com/a/1190000016184699
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞