@wy
2018-10-23T12:44:55.000000Z
字数 6291
阅读 525
react
React.js 是一个帮助你构建页面 UI 的库。主要解决了数据和UI同步的问题。
React.js 将帮助我们将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。
一个组件的显示形态和行为有可能是由某些数据决定的。而数据是可能发生改变的,这时候组件的显示形态就会发生相应的改变。
React.js 不是一个框架,它只是一个库。它只提供 UI (view)层面的解决方案。在实际的项目当中,它并不能解决我们所有的问题,需要结合其它的库,例如 Redux、React-router 等来协助提供完整的解决方法。
使用create-react-app脚手架工具搭建项目,全局安装:
npm i -g create-react-app
之后就可以使用create-react-app命令
创建项目目录
create-react-app
创建之后,cd < project-dir >进入项目目录,启动项目:
npm start
启动后,自动打开浏览器。
const element = < h1>Hello, world!</h1>;
这种有趣的标签语法既不是字符串也不是HTML。这称作是JSX ,他是 JavaScrip 的一种扩展语法。
JSX语法用来描述 UI 信息,也就是描述组件长什么样的。
可以用 花括号 把任意的 JavaScript 表达式 嵌入到 JSX 中。
类似:1+1;函数之类的。
let data = 'hello react';
const element1 = < h1>{data}</h1>;
const element2 = < h1>{1+1}</h1>;
const element3 = < h1>{[1,2,3].map(item => item * 2)}</h1>;
let attr = 'hello react';
const element1 = < h1 hello = {attr}>{attr}</h1>;
你可以将字符串常量作为属性值传递。下面这两个 JSX 表达式是等价的:
<div message="hello world"></div>
<div message={'hello world'}></div>
如果你没有给属性传值,它默认为 true。因此下面两个 JSX 是等价的:
<div autocomplete ></div>
<div autocomplete={true} ></div>
注意:写的jsx也是js,不能出现关键字:
直接在可以使用原生的for循环或者map来渲染结构。
let html = [];
let data = [1,2,3];
for(let i = 0; i < data.length; i++ ){
html.push(<span key={i}>{data[i]}</span>)
}
渲染页面中:
<div>{html}</div>
jsx中表达式可以是一个数组,内部会循环插入到指定的位置,如果是html结构会直接解析。
一般这样循环数组数据,把结构存放在数组后渲染在页面中,可以使用声明式的方式调用map函数代替:
let data = [1,2,3];
< div>
{
data.map((item,index) => < span key={index}>{item}< /span>)
}
< /div>
注意:
Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。
根据条件选择性的渲染结构:
function getGreeting(user) {
if (user) {
return <h1>Hello, {user}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
首先我们在一个 HTML 页面中添加一个 id="root" 的 < div>
<div id="root"></div>
在此 div 中的所有内容都将由 React DOM 来管理,所以我们将其称之为 “根” DOM 节点。
我们用React 开发应用时一般只会定义一个根节点。但如果你是在一个已有的项目当中引入 React 的话,你可能会需要在不同的部分单独定义 React 根节点。
ReactDOM.render(jsx,root)
在构建界面开发领域,组件应该是一种独立的、可复用的交互元素的封装,专注于构建每一个单独的部件。组件是由模板结构、数据、样式、业务逻辑组成的。例如在HTML标签中的input、select这样的元素,都属于是组件,由浏览器内置提供。当然这些组件远远不能满足我们的开发需求,需要做开发者自己封装组件。
组件化开发是什么?
页面上的每个 独立的 可视/可交互区域视为一个组件;
每个组件相对独立,页面只不过是组件的容器,组件自由组合形成功能完整的界面;
当不需要某个组件,或者想要替换组件时,可以整个目录删除/替换。
每个组件对应一个工程目录,组件所需的各种资源都在这个目录下就近维护;
参考:https://github.com/fouber/blog/issues/10
组件化开发的好处:
如何设计一个组件?
在React中,核心就是组件。组件有输入、自己的状态和输出。
- 输入在React中叫做prop
- 自己的状态在React中叫做state
- 输入在React中是render函数返回值。
定义一个组件最简单的方式是使用JavaScript函数:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
使用:
<Welcome name="hello,react"></Welcome>
该函数是一个有效的React组件,它接收一个单一的“props”对象并返回了一个React元素。我们之所以称这种类型的组件为函数定义组件,是因为从字面上来看,它就是一个JavaScript函数。
你也可以使用 ES6 class 来定义一个组件:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
使用:
<Welcome name="hello,react"></Welcome>
注意:
组件名字未来使用自定义标签的形式渲染的,要被当做是组件形式,而不是自定义的元素标签,所以组件名称必须以大写字母开头。
组件是相互独立、可复用的单元,一个组件可能在不同地方被用到。
组件封装后,需要定制组件的数据,需要传入Props。实际上组件看起来就像函数或者是一个类,Props实际是就是传入的参数。
当React遇到的元素是用户自定义的组件,它会将JSX属性作为单个对象传递给该组件,这个对象称之为“props”。
把参数放在标签的属性当中,所有的属性都会作为 props 对象的键值:
<Welcome name="hello,react"></Welcome>
注意:
无论是使用函数或是类来声明一个组件,它决不能修改它自己的props
类似于上面的这种函数称为“纯函数”,它没有改变它自己的输入值,当传入的值相同时,总是会返回相同的结果。
简单来说,一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数。这么说肯定比较抽象,我们把它掰开来看:
- 函数的返回结果只依赖于它的参数。
- 函数执行过程里面没有副作用。
static defaultProps = {
title: '默认的标题'
}
使用模块prop-types,npm地址:https://www.npmjs.com/package/prop-types
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
具体类型设置参考:https://www.npmjs.com/package/prop-types
React 元素的事件处理和 DOM元素的很相似。但是有一点语法上的不同:
没有经过特殊处理的话,这些 on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上。
这些事件属性名都必须要用驼峰命名法。
React中写法:
<button onClick={activateLasers}>
Activate Lasers
</button>
当你使用 ES6 class 语法来定义一个组件的时候,事件处理器会成为类的一个方法
class Toggle extends React.Component {
constructor(props) {
super(props);
// 把this绑定指向当前的组件的实例
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this)
}
render() {
return (
<button onClick={this.handleClick}>
测试this
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
如果使用 bind 让你很烦,这里有两种方式可以解决。如果你正在使用实验性的属性初始化器语法,你可以使用属性初始化器来正确的绑定回调函数:
class LoggingButton extends React.Component {
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
以上这种语法暂时在原生的ES6不支持,需要借助babel的插件来做,https://babeljs.io/docs/plugins/transform-class-properties/
如果你没有使用属性初始化器语法,你可以在回调函数中使用 箭头函数:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
这个语法有个问题就是每次 LoggingButton 渲染的时候都会创建一个不同的回调函数。
使用bind
<button onClick={this.handleClick.bind(this,参数)}>
Click me
</button>
使用箭头函数
<button onClick={(e) => this.handleClick(e,参数)}>
Click me
</button>
合成事件可以参考这里:https://doc.react-china.org/docs/events.html
一个组件的显示形态是可以由它数据状态和配置参数决定的。一个组件可以拥有自己的状态。
constructor(props){
super(props);
this.state = {
value: 1
}
跟新状态,必须使用setState:
this.setState({
value:2
})
取值
{this.state.value}
当我们调用setState函数时会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上。
当你调用 setState 的时候,React.js 并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state 当中,然后再触发组件更新。这一点要好好注意。可以体会一下下面的代码:
this.setState({ count: 0 }) // => this.state.count 还是 undefined
this.setState({ count: this.state.count + 1}) // => undefined + 1 = NaN
this.setState({ count: this.state.count + 2}) // => NaN + 2 = NaN
如果需要同步运算,可给setState传入函数:
this.setState((prevState) => {
return { count: 0 }
})
this.setState((prevState) => {
return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
})
this.setState((prevState) => {
return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
})
// 最后的结果是 this.state.count 为 3
这样就可以达到上述的利用上一次 setState 结果进行运算的效果。
上面我们进行了三次 setState,但是实际上组件只会重新渲染一次,而不是三次;这是因为在 React.js 内部会把 JavaScript 事件循环中的消息队列的同一个消息中的 setState 都进行合并以后再重新渲染组件。
在使用 React.js 的时候,并不需要担心多次进行 setState 会带来性能问题。
数据流指的是数据的流向,在React中,数据是自定向下单向流动的,即父组件到子组件。这个原则让组件之间的关系变得简单并且可以预测。
在顶层初始化了props,会遍历整颗组件树,渲染相关的子组件。props本身是不可变的。
state只关心组件内部的自己的状态,这些状态只在组件内部改变。