首先我说说我为何学React,React能够说是三大框架中思想最为先进的一款,在国内Vue虽然很火,可是Vue也开始引入了React的一些思想,好比虚拟DOM,接着Vue3.0还会引入JSX,React很是注重组件化和复用性,学习React能够比之前更熟悉一些架构知识。
工做时间也有3个多月了,该学会写东西总结了,最近利用工做时间学习React,今天又有空写一下总结,怕过了一段时间忘记,这篇文章我是看了 React小书 总结下来的,若是有人跟我同样是React入门的话,建议去React小书学起,从脚手架学起真的是快不少,咱们老师教咱们学东西首先要学会用,再学原理。
这篇文章不适合大神看,若是有大神想指导能够在下面发布评论。
React是个帮你构建页面UI的库,他至关于MVC里面的View,React将咱们界面分红各个独立的小块,每一个小块都是一个组件,而这些组件能够互相嵌套丶结合,而后就造成了页面css
因为React要配合其余工具和库辅助,例如编译须要babel,组织代码须要redux等第三方状态管理工具,写单页面应用须要应用到React-router,这也至关于React的全家桶。html
npm install -g create-react-app
前端
create-react-app my-app
node
npm
切换成cnpm
就能够解决了。
npm config set registry https://registry.npm.taobao.org
//将npm切换成cnpmresource
下面的app里面的extensions
打开后看到有git文件夹,删除,重启。建立完成以后而后cd
到项目根目录而后npm start
就能够启动项目了react
建立完成以后cd
到建立的目录而后能够看到脚手架帮咱们建立的结构git
node_modules | public | scripts | src |
---|---|---|---|
依赖文件 | 静态库 | 项目启动配置 | 开发目录 |
开发时候在src目录上开发就好了,若是要查看项目配置,能够输入npm run eject
把config
目录暴露出来,这个是默认隐藏的,你也能够在node_modules
目录上找到,这个目录是项目的配置。若是运行npm run eject
报错的话,把代码储存到本地就好了git add .
,而后git commit -m ''
,接着从新运行指令,这执行是不可逆的。npm
在public
有个index.html
这是整个项目的页面,以后咱们要把写好的东西渲染到里面去。
src
能够看到一个index.js
,因为写React几乎都是组件,而index
通常用来渲染其余组件redux
首先咱们把index.js
的代码改为这样再看看效果数组
import React, { Component } from 'react' // 写React必需要引入这两样东西
import ReactDOM from 'react-dom' // 组件不须要引入,他只有一个做用,就是渲染DOM
import './index.css' // 能够直接引入css或者是sass,也能够引入图片
<!-- 使用组件继承Component类 -->
class Header extends Component {
render () { // render方法把里面东西jsx返回
return (
<!-- jsx -->
<div>
<h1>React大法好</h1>
</div>
)
}
}
<!-- 渲染出去 -->
ReactDOM.render(
<Header />,
document.getElementById('root')
)
复制代码
JSX 是 JavaScript 语言的一种语法扩展,长得像 HTML,但并非 HTML。浏览器
平时咱们写html
是这样写的
<div class='box' id='content'>
<div class='title'>Hello</div>
<button>Click</button>
</div>
复制代码
若是咱们用JS对象
来表示的话
{
tag: 'div',
attrs: { className: 'box', id: 'content'},
children: [
{
tag: 'div',
arrts: { className: 'title' },
children: ['Hello']
},
{
tag: 'button',
attrs: null,
children: ['Click']
}
]
}
复制代码
没错,其实JSX
也至关于JS对象
,React也会根据这样编译,转化为HTML结构以后就能够拿去构建真正的DOM,这是最后那段渲染DOM所作的事情
ReactDOM.render(
<Header />,
document.getElementById('root')
)
复制代码
这是整个过程流程图,单独把ReactDOM抽出来的缘由是由于咱们能够渲染到Canvas或者转化原生App(ReactNative)
React中一切皆为组件,写组件的时候通常都须要继承React.js的Component
,这个方法必须返回一个JSX
元素,而返回并列JSX
元素是不合法的
<!-- bad -->
render() {
return {
<div>one</div>
<div>two</div>
}
}
复制代码
必需要用一个外层元素包起来,不能有多个外层元素
<!-- good -->
render() {
return {
<div>
<div>one</div>
<div>two</div>
</div>
}
}
复制代码
表达式能够插入变量,还能够计算,还能够条件返回,而且能够写函数,render会把真实的内容返回,特别的灵活
render() {
const word = 'word'
const isGoodWord = true
return(
<div>
<h1>HELLO {word}</h1> // 变量
<h1>{1 + 2}</h1> // 计算
<h1>{(function(){ return 'React' })()}</h1> // 函数
// 条件返回
{
isGoodWord
? <span>好文章</span>
: <span>坏文章</span>
}
</div>
)
}
复制代码
表达式不只能够插入标签内部,还能够插入属性
render() {
const className = 'header'
return(
<div className={ className }></div>
)
}
复制代码
因为class
是JS的关键字,React.js换成了className
,还有一个就是for
换成了htmlfor
自定义的组件都必需要用大写字母开头
class Title extends Component {
render () {
return (
<h1>标题标题标题</h1>
)
}
}
class Header extends Component {
render () {
return (
<div>
<!-- 渲染三次 -->
<Title />
<Title />
<Title />
</div>
)
}
}
复制代码
能够直接在Header
标签里面使用,React.js会在<Title />
,组件的render
方法表示的JSX
内容渲染出来,
它会显示在相应的位置上
在须要监听事件的元素加上属性相似 onClick
onKeyDown
这样的属性便可,事件属性必须使用驼峰命名法。
class Title extends Component {
handleOnClickTitle(hello) {
console.log(hello)
console.log(this)
}
render () {
return (
<!-- 绑定事件时须要绑定this,不绑定则拿不到 -->
<h1 onClick={this.handleOnClickTitle.bind(this, 'hello')}>标题标题标题</h1>
)
}
}
复制代码
在 React.js 不须要手动调用浏览器原生的addEventListener
进行事件监听。React.js 帮咱们封装好了一系列的 on* 的属性,当你须要为某个元素监听某个事件的时候,只须要简单地给它加上 on* 就能够了。并且你不须要考虑不一样浏览器兼容性的问题,React.js 都帮咱们封装好这些细节了。
React.js 将浏览器原生的event
对象封装了一下,对外提供统一的 API 和属性,这样你就不用考虑不一样浏览器的兼容性问题。
若是你不手动绑定this
在函数里面打印的会是undefined
,这是由于 React.js 调用你所传给它的方法的时候,并非经过对象方法的方式调用,而是直接经过函数调用,因此事件监听函数内并不能经过this 获取到实例若是你想在事件函数当中使用当前的实例,你须要手动地将实例方法 bind 到当前实例上再传入给 React.js
state
是组件的状态,咱们能够用它来进行状态切换,如显示或隐藏,或者是改变className
等操做。
class Index extends Component {
constructor(props) {
super(props)
<!-- 状态 -->
this.state = {
isShow: true
}
}
render () {
handleState() {
this.setState({
isShow: !this.state.isShow
})
}
return (
<div className='index'>
{
this.state.isShow === true
? <h1>显示<h1/> : null
}
<button onClick={this.handleState.bind(this)}></button>
</div>
)
}
}
复制代码
setState
方法由父类 Component 所提供。当咱们调用这个函数的时候,React.js 会更新组件的状态 state ,而且从新调用 render 方法,而后再把 render 方法所渲染的最新的内容显示到页面上。
setState
能够接受函数或者对象做为参数,这里要注意一个问题,当你调用setState
时React并不会立刻修改state,而是把这个对象放在一个更新队列,稍后才会从队列当中把新的状态提取出来合并到 state 当中,而后再触发组件更新。
handleState() {
console.log(this.state.isShow) // true
this.setState({
isShow: !this.state.isShow
})
console.log(this.state.isShow) // true
}
复制代码
若是要作到后续操做依赖前一个setState
的这种操做的话,咱们能够把参数换成函数。
handleState() {
console.log(this.state.isShow) // true
this.setState((prevState) => {
return { isShow: !this.state.isShow }
})
console.log(this.state.isShow) // false
}
复制代码
在一个函数里面同步使用setState
它只会执行一个,React.js 内部会把js
事件循环中的消息队列的同一个消息中的setState
都进行合并之后再从新渲染组件,若是你先调用一个setState
而后用定时器调用的话,就会触发两次
说到setState
还要说到一个React的受控组件,好比 input
、textarea
这种输入框,若是你直接写这种标签时候React会报一个错误。
state
控制,只要相似于 <input />
、<textarea />
这样的输入控件被设置了 value 值,那么它们的值永远以被设置的值为准。这时候只要给他绑定一个onChange
就好了。 class InputWithUserName extends Component {
constructor() {
super()
this.state = {
value: ''
}
}
changeValue(event) {
console.log(event.target.value)
this.setState({
value: event.target.value
})
}
render() {
return (
<div>
{this.props.content}
<input type="text" value={this.state.value} onChange={this.changeValue.bind(this)}/>
</div>
)
}
}
复制代码
每一个组件均可以接受一个 props 参数,它是一个对象,包含了全部你对这个组件的配置。
在使用一个组件的时候,能够把参数放在标签的属性当中,全部的属性都会做为 props 对象的键值
class Index extends Component {
render () {
return (
<div className='index'>
<!-- 这里两个花括号只是JSX里面再嵌套一个对象而已 -->
<!-- 还能够传函数 -->
<Main options={{title: 'React', content: 'React大法好'}} onClick={()=> console.log('Click in Index')} />
</div>
)
}
}
class Main extends Component {
<!-- 若是没有东西传过来能够在这里设置默认值 -->
static defaultProps = {
options = {
title: '默认头部',
content: '默认内容'
}
}
<!-- 这里能够省略,由于React会自动生成 -->
constructor(props) {
super(props)
}
handleIndexClick() {
if(this.props.onClick) {
<!-- 执行Index传过来的函数 -->
this.props.onClick()
}
}
render() {
const options = this.props.options
<div className='main'>
<h1>{options.title}</h1>
<p>{options.content}</p>
<button onClick={this.handleIndexClick.bind(this)}></button
</div>
}
}
复制代码
不要试图去改变传过来的props
,React.js 但愿一个组件在输入肯定的 props 的时候,可以输出肯定的 UI 显示形态。若是 props 渲染过程当中能够被修改,那么就会致使这个组件显示形态和行为变得不可预测,这样会可能会给组件使用者带来困惑。
handleIndexClick() {
this.props.options = null; // 报错
if(this.props.onClick) {
<!-- 执行Index传过来的函数 -->
this.props.onClick()
}
}
复制代码
但这并不意味着由props
决定的显示形态不能被修改。组件的使用者能够主动地经过从新渲染的方式把新的props
传入组件当中,这样这个组件中由props
决定的显示形态也会获得相应的改变。
class Index extends Component {
constructor (props) {
super(props)
this.state = {
options: {
title: 'React',
content: 'React大法好'
},
}
}
<!-- 修改传过去的值 -->
handleOptionsChange() {
this.setState({
options: {
title: 'Vue',
content: 'Vue大法好'
},
})
}
render () {
return (
<div className='index'>
<Main options={ this.state.options } onClick={()=> console.log('Click in Index')} />
<!-- 点击修改 -->
<button onClick={this.handleOptionsChange.bind(this)}></button>
</div>
)
}
}
复制代码
因为setState
会致使Index
从新渲染,因此<Main />
也会接受到新的值而且从新渲染,这样就能作到修改<Main />
的显示效果
state
和 props
state
的主要做用是用于组件保存、控制、修改本身的可变状态。state
在组件内部初始化,能够被组件自身修改,而外部不能访问也不能修改。你能够认为 state
是一个局部的、只能被组件自身控制的数据源。state
中状态能够经过 this.setState
方法进行更新,setState
会致使组件的从新渲染。
props
的主要做用是让使用该组件的父组件能够传入参数来配置该组件。它是外部传进来的配置参数,组件内部没法控制也没法修改。除非外部组件主动传入新的 props
,不然组件的 props
永远保持不变。
state
和 props
有着千丝万缕的关系。它们均可以决定组件的行为和显示形态。一个组件的 state
中的数据能够经过 props
传给子组件,一个组件可使用外部传入的 props
来初始化本身的 state
。可是它们的职责其实很是明晰分明:state
是让组件控制本身的状态,props
是让外部对组件本身进行配置。
若是你以为仍是搞不清 state
和 props
的使用场景,那么请记住一个简单的规则:尽可能少地用 state
,尽可能多地用 props
。没有 state
的组件叫 无状态组件(stateless component),设置了 state
的叫作 有状态组件件(stateful component)。由于状态会带来管理的复杂性,咱们尽可能多地写无状态组件,尽可能少地写有状态的组件。这样会下降代码维护的难度,也会在必定程度上加强组件的可复用性。前端应用状态管理是一个复杂的问题。
有状态组件
class HelloWorld extends Component {
constructor() {
super()
this.state = {
isShow: true
}
}
sayHi () {
alert('Hello World')
}
render () {
return (
<div onClick={this.sayHi.bind(this)}>Hello World</div>
)
}
}
复制代码
无状态组件
const HelloWorld = (props) => {
const sayHi = (event) => alert('Hello World')
return (
<div onClick={sayHi}>Hello World</div>
)
}
复制代码
函数式的组件编写方式是一个函数就是一个组件,你能够和之前同样经过<HellWorld />
使用该组件。不一样的是,函数式组件只能接受 props
而没法像跟类组件同样能够在 constructor 里面初始化 state
。你能够理解函数式组件就是一种只能接受 props
和提供render
方法的类组件。
直接使用ES6的map遍历代码比较简洁,也能够手动写循环构建列表的JSX,在React遍历使用map是很是常见的。
const users = [
{ username: 'Jerry', age: 21, gender: 'male' },
{ username: 'Tomy', age: 22, gender: 'male' },
{ username: 'Lily', age: 19, gender: 'female' },
{ username: 'Lucy', age: 20, gender: 'female' }
]
class Index extends Component {
render () {
return (
<div>
{users.map((user, i) => <User user={user} key={i} />)}
</div>
)
}
}
class User extends Component {
render () {
const { user } = this.props
return (
<div>
<div>姓名:{user.username}</div>
<div>年龄:{user.age}</div>
<div>性别:{user.gender}</div>
<hr />
</div>
)
}
}
复制代码
React.js 的是很是高效的,它高效依赖于所谓的 Virtual-DOM 策略。简单来讲,能复用的话 React.js 就会尽可能复用,没有必要的话绝对不碰 DOM。对于列表元素来讲也是这样,可是处理列表元素的复用性会有一个问题:元素可能会在一个列表中改变位置。但其实 React.js 只须要交换一下 DOM 位置就好了,可是它并不知道其实咱们只是改变了元素的位置,因此它会从新渲染后面两个元素(再执行 Virtual-DOM 策略),这样会大大增长 DOM 操做。但若是给每一个元素加上惟一的标识,React.js 就能够知道这两个元素只是交换了位置
render () {
return (
<div>
{users.map((user, i) => <User user={user} key={i} />)}
</div>
)
}
复制代码
这样 React.js 就简单的经过 key 来判断出来,这两个列表元素只是交换了位置,能够尽可能复用元素内部的结构。
对于用表达式套数组罗列到页面上的元素,都要为每一个元素加上 key 属性,这个 key 必须是每一个元素惟一的标识,通常后台会返回。