以下是一个最简单的 demo,将一个最简单的组件渲染到页面上。
import React from 'react'
import { render } from 'react-dom'
// 定义组件
class Hello extends React.Component {
render() {
// return 里面写jsx语法
return <p>hello world</p>
}
}
// 渲染组件到页面
render(<Hello />, document.getElementById('root'))
**深入一下,这里`import React from 'react'`引用的是什么?**
这里的`'react'`对应的就是`./package.json`文件中`dependencies`中的`'react'`,即在
该目录下用`npm install`安装的 react 。npm 安装的 react 的物理文件是存放在
`./node_modules/react`中的,因此引用的东西肯定就在这个文件夹里面。
打开`./node_modules/react/package.json`找到`"main": "react.js",`,这里的`main`即
指定了入口文件,即`./node_modules/react/react.js`这个文件。那么,问题的答案自然
就出来了。
jsx 中不能一次性返回零散的多个节点,如果有多个请包涵在一个节点中。例如,
return (
<div>
<p>段落1</p>
<p>段落2</p>
<p>段落3</p>
</div>
)
```
再例如:
```jsx
// { } 中返回的两个 <p> 也要用 <div> 包裹
return (
<div>
<p>段落1</p>
{true ? (
<p>true</p>
) : (
<div>
<p>false 1</p>
<p>false 2</p>
</div>
)}
</div>
)
```
### 注释
jsx 中用`{/* */}`的注释形式
```jsx
return (
// jsx 外面的注释
<div>
{/* jsx 里面的注释 */}
<p>hello world</p>
</div>
)
对应 html 的两种形式,jsx 的样式可以这样写: css 样式:`<p
className="class1">hello world</p>`,注意这里是`className`,而 html 中是`class`
内联样式:`<p style={{display: 'block', fontSize: '20px'}}>hello world</p>`,注
意这里的`{{...}}`,还有`fontSize`的驼峰式写法
拿 click 事件为例,要在标签上绑定 click 事件,可以这样写
class Hello extends React.Component {
render() {
return <p onClick={this.clickHandler.bind(this)}>hello world</p>
}
clickHandler(e) {
// e 即js中的事件对象,例如 e.preventDefault()
// 函数执行时 this 即组件本身,因为上面的 .bind(this)
console.log(Date.now())
}
}
注意,`onClick`是驼峰式写法,以及`.bind(this)`的作用
在 jsx 中使用循环,一般会用到`Array.prototype.map`(来自 ES5 标准)
class Hello extends React.Component {
render() {
const arr = ['a', 'b', 'c']
return (
<div>
{arr.map((item, index) => {
return <p key={index}>this is {item}</p>
})}
</div>
)
}
}
注意,`arr.map`是包裹在`{}`中的,`key={index}`有助于 React 的渲染优化,jsx 中
的`{}`可放一个可执行的 js 程序或者变量
jsx 中使用判断一般会用到三元表达式(表达式也是放在`{}`中的),例如:
return (
<div>
<p>段落1</p>
{
true
? <p>true</p>
: <p>false</p>
</div>
}
</div>
)
也可以这样使用:
<p style={{display: true ? 'block' ? 'none'}}>hello world</p>
代码分离:
之前的 demo 代码都是在一个文件中,实际开发中不可能是这样子的,因此这里就先把组件
的代码给拆分开。我们将使用 es6 的模块管理规范。
### page 层
创建`./app/containers/Hello/index.jsx`文件,将之前创建组件代码复制进去
```jsx
import React from 'react'
class Hello extends React.Component {
render() {
return <p>hello world</p>
}
}
export default Hello
```
然后`./app/index.jsx`中代码就可以这样写。
```jsx
import Hello from './containers/Hello'
render(<Hello />, document.getElementById('root'))
```
注意,代码`import Hello from './containers/Hello';`这里可以写
成`./containers/Hello/index.jsx`也可以写成`./containers/Hello/index`
### subpage 层
如果`Hello`组件再稍微复杂一点,那么把代码都放一块也会变得复杂,接下来我们再拆分
。
创建`./app/containers/Hello/subpage`目录,然后在其下创建三个文件`Carousel.jsx`
`Recommend.jsx` `List.jsx`,分别写入相应的代码(看代码文件即可),然
后`./app/containers/Hello/index.js`中即可这样写
```jsx
import Carousel from './subpage/Carousel'
import Recommend from './subpage/Recommend'
import List from './subpage/List'
class Hello extends React.Component {
render() {
return (
<div>
<p>hello world</p>
<hr />
<Carousel />
<Recommend />
<List />
</div>
)
}
}
```
注意,这里`import`时`.jsx`后缀省略了。
数据传递 & 数据变化
### props
接着刚才 Header 的话题往下说,每个页面都会使用 Header ,但是 Header 上显示的标题
每个页面肯定是不一样的。我们需要这样解决:页面中引用 Header 时,这样写 `<Header
title="Hello 页面 "/>`,即给 Header 组件设置一个 title 属性。而在 Header 组件中
可以这样取到
render() {
return (
<p>{this.props.title}</p>
)
}
在 React 中,父组件给子组件传递数据时,就是以上方式,通过给子组件设置 props 的方
式,子组件取得 props 中的值即可完成数据传递。被传递数据的格式可以是任何 js 可识
别的数据结构,上面 demo 是一个字符串。**React 中,props 一般只作为父组件给子组件
传递数据用,不要试图去修改自己的 props ,除非你想自找麻烦**
上面提到了 props 不能被自身修改,如果组件内部自身的属性发生变化,该怎么办?——
React 为我们提供给了 `state`,先看一个 demo:
class Hello extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
// 显示当前时间
now: Date.now()
}
}
render() {
return (
<div>
<p>hello world {this.state.now}</p>
</div>
)
}
}
还有一点非常重要,**React 会实时监听每个组件的 props 和 state 的值,一旦有变化,
会立刻更新组件,将结果重新渲染到页面上**,下面 demo 演示了`state`的变化
,`props`也是一样的
class Hello extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
// 显示当前时间
now: Date.now()
}
}
render() {
return (
<div>
<p onClick={this.clickHandler.bind(this)}>
hello world {this.state.now}
</p>
</div>
)
}
clickHandler() {
// 设置 state 的值的时候,一定要用 this.setState ,不能直接赋值修改
this.setState({
now: Date.now()
})
}
}
智能组件 & 木偶组件
这是用 React 做系统设计时的两个非常重要的概念。虽然在 React 中,所有的单位都叫做
“ 组件 ”,但是通过以上例子,我们还是将它们分别放在
了`./app/containers`和`./app/components`两个文件夹中。为何要分开呢?
* **智能组件** 在日常开发中,我们也简称**“ 页面 ”**。为何说它 “ 智能 ”,因为它只
会做一些很聪明的事儿,脏活累活都不干。它只对数据负责,只需要获取了数据、定义好
数据操作的相关函数,然后将这些数据、函数直接传递给具体实现的组件即可。
* **木偶组件** 这里 “ 木偶 ” 一词用的特别形象,它总是被人拿线牵着。它从智能组件
(或页面)那里接受到数据、函数,然后就开始做一些展示工作,它的工作就是把拿到的
数据展示给用户,函数操作开放给用户。至于数据内容是什么,函数操作是什么,它不关
心。
以上两个如果不是理解的很深刻,待把课程学完再回头看一下这两句话,相信会理解的。