React学习(一)

93 阅读8分钟

内容

参考

  1. React元素渲染
  2. React JSX
  3. React 组件
  4. React State(状态)

1,元素渲染

元素是构成React应用的最小单位,用于描述屏幕输出的内容。注意:与浏览器的DOM元素不同,React中的元素事实上是普通的对象,React DOM 可以确保 浏览器DOM 的数据内容与React元素保持一致。

const element = <h1>Hello, world!</h1>;
1.1,将元素渲染到DOM中

首先需要在HTML页面中添加一个id="example" 的 <div> ,在此div中的所有的内容都将由React DOM 来管理,所以将其称作 “根”DOM节点

<div id="example"></div>

说明:用React开发应用时,一般只会定义一个根节点,但如果是在一个已有的项目中引入React的话,可能会需要在不同的部分单独定义React根节点。

要将React元素渲染到DOM节点中,需要将元素都传递给ReactDOM.render() 的方法来将其渲染到页面上。 示例:

<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">
const element =<h1>Hello, world!</h1>;
ReactDOM.render(
    element,
    document.getElementById('example')
);
</script>

</body>
</html>

解释:在示例中引用了三个库:

  • react.development.min.js:React的核心库
  • react-dom.development.min.js :提供与DOM相关的功能
  • babel.min.js:Babel可以将ES6代码转为ES5代码,这样就能在不支持ES6的浏览器上执行React代码.除此之外,Bable还内嵌了对JSX的支持.将Babel和babel-sublime 包一同使用,可以让源码的语法渲染上升到一个全新的水平
1.2,更新元素渲染

React元素都是不可变的,当元素被创建后,将无法改变其内容或属性。目前更新界面的唯一方式是创建一个新的元素,然后将其传入ReactDOM.render()方法中,如下示例:

<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">
function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>现在是 {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('example')
  );
}
 
setInterval(tick, 1000);
</script>

</body>
</html>

代码解释:该示例通过 setInterval() 方法,每秒钟调用一次 ReactDOM.render()。可以将要展示的部分封装起来, 以下用一个函数表示该示例:

function Clock(props) { 
    return ( 
      <div> 
          <h1>Hello, world!</h1>
          <h2>现在是 {props.date.toLocaleTimeString()}.</h2> 
      </div> 
       ); 
} 
function tick() { 
    ReactDOM.render( 
    <Clock date={new Date()} />, 
    document.getElementById('example') ); 
} 
setInterval(tick, 1000);

除了函数外,还可以创建一个React.Component 的 ES6 类,该类封装了要展示的元素,需要注意的是在 render() 方法中,需要使用 this.props 替换 props。

React只会更新必要的部分:React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分

2,JSX

React使用JSX来代替常规的JavaScript。JSX是一个看起来很像XML的JavaScript语法扩展。也可以不使用JSX,但使用JSX有以下优点:

  • JSX执行更快,因为它在编译为JavaScript代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用JSX编写模板更加简单快速。

示例:下面的标签语法既不是字符串也不是HTML,它称为JSX,一种JavaScript的语法扩展,推荐使用JSX来描述用户界面。JSX是在JavaScript内部实现的

const element = <h1>Hello, world!</h1>;

说明:元素是构成React应用的最小单位,JSX就是用来声明React当中的元素。与浏览器的DOM元素不同,React DOM 可以确保 浏览器 DOM 的数据内容与 React 元素保持一致。要将 React 元素渲染到根 DOM 节点中,需要通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上。

说明:ReactDOM.render 是 React 的最基本方法用于将模板转为 HTML 语言,并插入指定的 DOM 节点。ReactDOM.render(template,targetDOM) 方法接收两个参数:

  • template:创建的模板,多个 dom 元素外层需使用一个标签进行包裹,如 <div>。
  • targetDOM:可以理解为插入该模板的目标位置。

注意:由于 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议作为 XML 属性名。作为替代,React DOM 使用 className 和 htmlFor 来做对应的属性。如:给 input 添加 className 并更改样式

<input type="text" className="userName" value={value}/> 
.userName{background: yellow} // 在CSS样式中定义

使用JSX示例:

ReactDOM.render(
      	<div>
      	<h1>菜鸟教程</h1>
      	<h2>欢迎学习 React</h2>
        <p data-myattribute = "somevalue">这是一个很不错的 JavaScript 库!</p>
        </div>
      	,
      	document.getElementById('example')
);

代码解释:JSX类似一个HTML,如果需要嵌套多个HTML标签,需要使用一个div元素包裹。该示例中p元素添加了自定义属性data-myattribute,添加自定义属性需要使用data-前缀

JavaScript表达式

可以在JSX中使用JavaScript表达式,表达式写在花括号{}中。如:

ReactDOM.render(
      	<div>
      	  <h1>{1+1}</h1>
        </div>
      	,
      	document.getElementById('example')
);

在JSX中不能使用if else语句,但可以使用三元运算表达式来替代。如:

ReactDOM.render(
      	<div>
      	  <h1>{i == 1 ? 'True!' : 'False'}</h1>
        </div>
      	,
      	document.getElementById('example')
);
样式

React推荐使用内嵌样式,可以使用camelCase 语法来设置内联样式. React 会在指定元素数字后自动添加 px 。如:

<div id="example"></div>
<script type="text/babel">
  var myStyle = {
     fontSize: 100,
     color: '#FF0000'
  };
  ReactDOM.render(
    <h1 style = {myStyle}>菜鸟教程</h1>,
    document.getElementById('example')
  );
</script>
数组

JSX允许在模板中插入数组,数组会自动展开所有的成员。如:

<div id="example"></div>
<script type="text/babel">
  var arr = [
    <h1>菜鸟教程</h1>,
    <h2>学的不仅是技术,更是梦想!</h2>,
  ];
  ReactDOM.render(
    <div>{arr}</div>,
    document.getElementById('example')
  );
</script>

3,组件

React中有两种组件:

  • 函数组件,传递参数只需要props 对象即可,不需要使用this,如:
function HelloMessage(props) {
	return <h1>Hello World!</h1>;
}

const element = <HelloMessage />;

ReactDOM.render(
	element,
	document.getElementById('example')
);
  • 类组件,需要继承React.Component(可能还需要写构造函数),传递参数时需要使用this.props 对象,如:
function HelloMessage(props) extends React.Component{
	return <h1>Hello {this.props.name}!</h1>;
}

const element = <HelloMessage name="Runoob"/>;

ReactDOM.render(
	element,
	document.getElementById('example')
);

说明:const element = 为用户自定义组件(可以单标签也可双标签)。不能在组件名内使用style样式(不会生效)。

注意:原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。

复合组件

可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离。如:

function Name(props) {
	return <h1>网站名称:{props.name}</h1>;
}
function Url(props) {
	return <h1>网站地址:{props.url}</h1>;
}
function Nickname(props) {
	return <h1>网站小名:{props.nickname}</h1>;
}
function App() {
	return (
	<div>
		<Name name="菜鸟教程" />
		<Url url="http://www.runoob.com" />
		<Nickname nickname="Runoob" />
	</div>
	);
}

ReactDOM.render(
	 <App />,
	document.getElementById('example')
);

4,State(状态)

React把组件看作一个状态机(State Machines)。通过与用户进行交互,实现不同状态,然后渲染UI,使用户界面和数据保持一致。在React中,只需要更新组件的state,然后根据新的state重新渲染界面(不需要操作DOM)。如:

<div id="example"></div>
<script type="text/babel">
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('example')
);
</script>

代码解释:创建一个组件类,在render()方法中使用this.state来修改当前的时间,并添加一个构造器来初始化this.state(需要调用父类的构造函数)。

将生命周期方法添加到类中

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

  • 挂载(Mount):每当组件第一次加载到DOM中时,我们想要生成定时器,这在React中称之为挂载。
  • 卸载(UnMount):每当组件生成的这个DOM被移除时,我们也想要清除定时器,这在React中被称为卸载。

可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:

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

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('example')
);

代码解释:componentDidMount() 与 componentWillUnmount() 方法被称作生命周期钩子(hook)。在组件输出到 DOM 后会执行 componentDidMount() 钩子,我们就可以在这个钩子上设置一个定时器。this.timerID 为定时器的 ID,我们可以在 componentWillUnmount() 钩子中卸载定时器。

代码执行顺序:

  1. 当 <Clock /> 被传递给 ReactDOM.render() 时,React 调用 Clock 组件的构造函数。 由于 Clock 需要显示当前时间,所以使用包含当前时间的对象来初始化 this.state 。 稍后会更新此状态。
  2. React 然后调用 Clock 组件的 render() 方法。这是 React 了解屏幕上应该显示什么内容,然后 React 更新 DOM 以匹配 Clock 的渲染输出。
  3. 当 Clock 的输出插入到 DOM 中时,React 调用 componentDidMount() 生命周期钩子。 在该示例中,Clock 组件要求浏览器设置一个定时器,每秒钟调用一次 tick()
  4. 浏览器每秒钟调用 tick() 方法。 在该示例中,Clock 组件通过使用包含当前时间的对象调用 setState() 来调度UI更新。 通过调用 setState() ,React 知道状态已经改变,并再次调用 render() 方法来确定屏幕上应当显示什么。 这一次,render() 方法中的 this.state.date 将不同,所以渲染输出将包含更新的时间,并相应地更新 DOM。
  5. 一旦 Clock 组件被从 DOM 中移除,React 会调用 componentWillUnmount() 这个钩子函数,定时器也就会被清除。
数据自顶向下流动

父组件或子组件都不能知道某个组件是否有状态。并且它们不应该关心某一个组件是哪种(函数或类),这就是为什么状态通常被称为局部或封装。除了拥有并设置它的组件外,其他组件不可访问。示例:FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock 状态、还是来自 Clock 的属性、亦或手工输入:

function FormattedDate(props) {
  return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}

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

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <FormattedDate date={this.state.date} />
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('example')
);

这通常被称为自顶向下或单向数据流。任何状态始终由某些特定组件所拥有,并且从该状态导出的任何数据或UI只能影响树中下方的组件。可以将组件树看作属性的瀑布,每个组件的状态就是一个额外的水源,它连接在任意点,但也能留下来。