一天入门React学习心得
阅读前必读
本文写的仓促,算是一个学习笔记吧,算是一个入门级的学习文章。如果你刚刚入门,那么可能一些入门的视频可能更加适合你。但如果具备一些知识,比如Vue
,那么视频就不适合了。建议看完本篇文章在脑海中过一遍映像,在去官网深读,和一些深入文章,想来会有非常好的提升。
“一个框架不是看几篇文章,写写Demo就能说自己会,熟悉。需要不断实践,踩坑,学习原理。多想才能掌握。我只是结合
- - QAQVue
进行的React
学习,只是冰山一角罢了。充其量只是API
的搬运工。
初始化项目
新手还是推荐官方的脚手架,通过npx
来进行项目的初始化一个demo项目。
npx create-react-app 项目名称
默认生成的目录情况下,项目的解构就是这样的。
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 20.6.15 14:21 public
d----- 20.6.15 16:41 src
-a---- 20.6.15 14:21 310 .gitignore
-a---- 20.6.15 14:22 746 package.json
-a---- 20.6.15 14:21 2884 README.md
-a---- 20.6.15 14:22 526017 yarn.lock
随后通过npm
或者 yarn
来将项目跑起来。执行shell
后会自动打开一个浏览器,当看到localhost:3000
渲染后。那么就可以开启React大门了
# npm shell 命令
npm run start
# yarn shell 命令
yarn start
React元素渲染-JSX
在Vue
中template
模板得到大量的使用,只要你会写HTML
那么应该难不倒你这个小机灵鬼。而React
则是使用了在函数中直接返回DOM
。看起来非常的神奇,也导致了一些小伙伴在入门的时候会有点摸不着头脑,但是如果有一定基础,在Vue
中写过了Render
函数的话,我想上手起来还是非常容易的。它看起来是下面这个样子的。其本质上就是一个createElement
的过程。所以,将这种模板的渲染方式称之为JSX
。
import React from 'react';
import './App.css';
function App() {
return (
<div className="App"><h1>你好呀h1><p>今天又是充满希望的一天...p>div>
);
}
export default App;
通过React
的JSX模板引擎,最终将其渲染到DOM上
变量绑定
在Vue
的模板中,通过{{}}
两个花括号来声明约束,表示这个声明中的字段是一个js的值,而不是一个文本。在React则是使用了{}
一个花括号来做约定。那么就可以在DOM
中使用js
了。下面是一个Class
组件,将state
的text
值绑定在DOM
的。
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
text: '我是wangly19'
}
}
updateText () {
this.setState({
text: '我是帅气的wangly19'
})
}
render () {
return (
<div className="App"><p>我是一个动态的数据: {this.state.text}p><button onClick={ this.updateText.bind(this) }>更换button>div>
)
}
}
条件渲染
在Vue
中,如果需要动态的渲染一个节点,是通过v-if
指令来实现的,在React中呢,可以使用运算符来渲染元素。通过Vue
来做一个对比吧。
“通过
- - QAQ&&(或)
运算符来达到v-if
的一样效果。
render () {
return (
<div className="App"><p>我是一个动态的数据: {this.state.text}p>
{/* { <p v-if="true">2p> } */}
{true && <p>2p>}div>
)
}
“通过三元运算符可以来达到
- - QAQv-if
和v-else
一样的效果。
render () {
return (
<div className="App"><p>我是一个动态的数据: {this.state.text}p>
{/* { <p v-if="true">2p> } */}
{true ? <p>truep> : <p>falsep>}div>
)
}
列表渲染
通过map
方法来遍历一些基本数据结构的节点。
“通过数据结构自带的map可以遍历出
- - QAQvalue
,index
,key
在return
中返回节点信息。
事件处理
在JavaScript
中,通过onClick
来绑定一个事件。
<button onclick="activeBuff">激活按钮button>
而在jsx中,则是通过onClick
属性,且后面加一个jsx渲染模板的方式。
“需要注意的是,
- - QAQClass组件
下的this
是不存在的,需要用户手动为方法绑定this,在调用的点击事件方法。
this.activeBuff.bind(this) }>激活按钮</button>
组件
众所周知,Vue
和React
都是组件化解决方案,那么在React
中是如何创建一个组件的呢?在React
新版本中,推出了React Hooks
方案。所以现在主流的还是Class
组件和Function
组件。
Class组件
Class组件的创建方式非常的简单,只需要创建一个Class
类并且让它继承React.Component
,在Render
方法中return
出JSX的模板。
同时通过构造函数,处理Props
。并且声明state
状态初始化一个对象,将Props
挂载到super
上。
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
}
}
// render函数
render () {
return (
<div className="App"><p>我是Class组件p>div>
)
}
}
Function组件
随着React Hooks
发布,开发的主流也渐渐的转入了函数组件,不过Class
组件在旧项目中还是会用的。所以都学习一下。对比Class组件,函数组件就非常的简单了。在函数内部返回一个render模板就OK了。如下:
import React from 'react'
function FunctionDemo () {
return (
<div>
我是函数组件div>
)
}
export default FunctionDemo
Class组件State状态
这里着重的开个单章,主要是Class
组件中的State
值更改,因为函数组件最好使用的是hooks
,所以单拎出来主要解一下Class
组件下State
状态。在React
不建议直接修改State
的值,而是使用setState
的方式更新状态。
this.setState({
default: '修改后的文件'
})
同样的,setState
后当前的值并不会直接改变。它是一个异步函数,在异步函数中可以拿到最新的状态。
如果需要进行计算值的累加,推荐使用的是通过函数的返回值形式。
“这里的原理类似
- - QAQVue
的组件data
的函数return
方式。如果使用对象方式的话,在多个setState
使用的话会被后面的任务覆盖,从而直接执行一次
// no
this.setState({ index: this.state.index + 1 });
this.setState({ index: this.state.index + 1 });
// yes
this.setState({ index: this.state.index + 1 });
this.setState({ index: this.state.index + 1 });
this.setState((prevState, props) => {
return {quantity: prevState.quantity + 1};
});
组件通信
组件通信是开发中经常用到的功能,可谓是灵魂之一了。那么React的组件通信是如何完成的呢?
子组件获取父组件的值 Props
通过Props
可以使子组件非常快捷的拿到父组件的传递过去的内容。
- 1.在子组件上添加属性名称和数据
"wangly19">ClassDemo>
- 2.在Class中使用Props
constructor (props) {
super(props)
this.state = {
defaultText: '我是默认的文字'
}
}
- 3.使用
通过this.props.父组件绑定的属性名称
{ this.props.name }</p>
定义默认的Props属性
在Vue
中可以定义Props
的默认值,哪怕用户没有传递,就会显示默认Props
中定义的内容。
ClassDemo.defaultProps = {
name: '我是默认的名称'
// ... 参数列表
}
子组件传递父组件
通过Props
传递一个函数,当子组件需要改变父组件的值时,通过this.props.[函数]
执行回调。
// 父组件
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
childMessage: '2222222222222222'
}
}
onPropChange (newVal) {
this.setState({
childMessage: newVal
})
}
render () {
return (
<div className="App"><p>{ this.state.childMessage }p><ClassDemo onPropChange={ this.onPropChange.bind(this) }>ClassDemo>
{/* <FunctionDemo>FunctionDemo> */}div>
)
}
}
import React from 'react';
class ClassDemo extends React.Component {
constructor (props) {
super(props)
this.state = {
defaultText: '我是默认的文字'
}
}
changeText () {
this.props.onPropChange('111111111111111')
}
render () {
return (
<div className="App"><button onClick={ this.changeText.bind(this) }>更改文本button>div>
)
}
}
export default ClassDemo;
生命周期
看了官方的生命周期介绍,挺简洁的。分别是组件模板选然后,已经准备就绪的时候,可以做组件加载后的一些逻辑操作,镇楼神图。
挂载
constructor
构造函数初始化,最先被执行,初始化State
等等。
getDerivedStateFromProps
这是一个静态方法,需要在前面增加static
的属性
render
渲染函数,返回渲染的内容,当页面产生更新也会触发该方法。
componentDidMount
组件挂载之后,这个时候组件已经挂载完毕了
更新
getDerivedStateFromProps
组件即将被更新,这里参数分别对应前后被修改的内容,通过返回一个布尔值告知是否需要更新视图。
render
当视图更新,那么Render
也会重新更新
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate
在render
之后componentDidUpdate
之前输出,类似于中间件用来做一些捕获操作。
componentDidUpdate
getSnapshotBeforeUpdate
,有三个参数prevProps
,prevState
,snapshot
,表示之前的props
,之前的state
,和snapshot
。snapshot
是getSnapshotBeforeUpdate
返回的值
constructor (props) {
super(props)
this.state = {}
console.log('1.constructor构造函数')
}
componentDidMount () {
console.log('componentDidMount')
Store.subscribe(() => {
this.setState({})
})
}
static getDerivedStateFromProps (nextProps, prevState) {
console.log('getDerivedStateFromProps')
console.log(nextProps, prevState)
return true
}
getSnapshotBeforeUpdate (prevProps, prevState) {
console.log(prevProps, prevState)
return 'top: 200'
}
componentDidUpdate (prevProps, prevState, snapshot) {
console.log(prevProps, prevState, snapshot)
}
componentWillUnmount () {
console.log('componentWillUnmount')
}
changeText () {
Store.dispatch({
type: 'changeName',
value: '我是ClassDemo中修改的名字:wangly'
})
}
render () {
console.log('3.render函数')
return (
<div className="App"><p>{ Store.getState().redux_name }p>
{ this.state.redux_name }<button onClick={ this.changeText.bind(this) }>更改文本button>div>
)
}
卸载
componentWillUnmount
组件卸载,我们可以清除一些定时器,取消网络请求。
组件插槽
插槽对于Vue来说并不是很陌生,在React
中插入的节点会以Props
的方式传递。可以通过pro1ps.children
找到并且渲染出来。
// 父亲组件
this.onPropChange.bind(this)}><h1>插入的元素 h1>ClassDemo>// 子组件<div className="App">
{this.props.children}<button onClick={this.changeText.bind(this)}>更改文本button>div>
Router路由
路由对于SPA应用来讲可谓是重中之重,没有它的话,那么这个页面也就不能成为应用,只能称之为页面。两者可谓天差地别。
安装react-router-dom --save
# shell
npm install react-router-dom --save
创建路由模式
在Vue中都知道路由的mode
有两种,一种是hash
一种是history
模式。分别如下,通过引入不同的包来创建不同的Router
。
// histoty
import { BrowserRouter as Router, Link, Route } from 'react-router-dom';
// hash
import { Link, Route, HashRouter as Router } from 'react-router-dom';
创建一个简单的路由
通过as出来的Router
包裹路由快,通过Link
作为跳转的行为容器。这样一个基本的路由容器就完成。
“需要通过使
- - QAQRoute
进行对组件的声明配置,才能被Link
找到哦。
import React from 'react';
// histoty
import { BrowserRouter as Router, Link, Route } from 'react-router-dom';
// hash
// import { Link, Route, HashRouter as Router } from 'react-router-dom';
function Page1 () {
return (
<h1>我是Page1h1>
)
}
function Page2 () {
return (
<h1>我是Page2h1>
)
}
function Page3 () {
return (
<h1>我是Page3h1>
)
}
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
}
}
render () {
return (
<div className="App"><Router><ul><li><Link to="page1">Page1Link>li><li><Link to="page2">Page2Link>li><li><Link to="page3">Page3Link>li>ul><Route exact path="/page1" component={ Page1 }>Route><Route exact path="/page2" component={ Page2 }>Route><Route exact path="/page3" component={ Page3 }>Route>Router>div>
)
}
}
export default App;
路由传值
路由传递参数一般使用param
和query
参数。通过给to
传递一个对象的方式来进行数据的传递。可以看到,向page1
的路由上添加了一个:id
表示需要传递param
的id
的值,同时声明了search
的文本和state对象
多种方式传递了参数。以便根据不同的场景使用。
;<div className="App"><Router><ul><li><Linkto={{pathname: '/page1/10',search: '?roles=[10, 20]',state: { name: 'wangly19' },
}}
>
Page1Link>li><li><Link to="page2">Page2Link>li><li><Link to="page3">Page3Link>li>ul><Route exact path="/page1/:id" component={Page1}>Route><Route exact path="/page2" component={Page2}>Route><Route exact path="/page3" component={Page3}>Route>Router>div>
手动跳转
当你使用History
路由的时候,某些时候需要主动的跳转道某个路由,这个时候又不能去触发节点行为,所以这个时候就可以通过API的方式,进行跳转。使用方式和Vue
大差不差。
// 跳转页面
this.props.history.push(参数和to的内容像素)
this.props.history.push('page1')
// 重定向页面
this.props.history.replace('page2')
当然还有hash
的go
方法。
Redux状态管理
Redux
是类似于Vuex
的一个全局状态解决方案,它的功能主要是用来存储公有全局状态。来方便管理一些共有配置参数,解决业务体积大,结构复杂的项目提供好的状态管理。
“如果项目不是特别需要,尽量不去使用它。
- - QAQ
安装Redux
# shell
npm install redux --save
创建Store State
看到官方的Demo,是非常容易懂的。下面是官方的代码,一眼就能看出流程。
import { createStore } from 'redux'
/**
* This is a reducer, a pure function with (state, action) => state signature.
* It describes how an action transforms the state into the next state.
*
* The shape of the state is up to you: it can be a primitive, an array, an object,
* or even an Immutable.js data structure. The only important part is that you should
* not mutate the state object, but return a new object if the state changes.
*
* In this example, we use a `switch` statement and strings, but you can use a helper that
* follows a different convention (such as function maps) if it makes sense for your
* project.
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// Create a Redux store holding the state of your app.
// Its API is { subscribe, dispatch, getState }.
let store = createStore(counter)
// You can use subscribe() to update the UI in response to state changes.
// Normally you'd use a view binding library (e.g. React Redux) rather than subscribe() directly.
// However it can also be handy to persist the current state in the localStorage.
store.subscribe(() => console.log(store.getState()))
// The only way to mutate the internal state is to dispatch an action.
// The actions can be serialized, logged or stored and later replayed.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
- 创建Stoge
// 声明默认的State状态值
const modeStore = {
redux_name: '我是Redux中声明的名称:wangly19'
}
// 声明Reducer
const reducer = (state = modeStore, action) => {
return state
}
// createStore
import { createStore } from 'redux';
import reducer from './reducer'
const store = createStore(reducer)
export default store
- 视图渲染
import Store from './index'
{ Store.getState().redux_name }
- 触发更新行为dispatch
this.changeText.bind(this) }>更改文本</button>
// dispatch
changeText () {
Store.dispatch({
type: 'changeName',
value: '我是ClassDemo中修改的名字:wangly'
})
}
前提是,需要对action
的方法做一个声明。类似于Vuex的Action
。
const reducer = (state = modeStore, action) => {
switch (action.type) {
case 'changeName':
const newState = {...state}
console.log(state)
newState.redux_name = action.value
console.log(newState)
console.log(state)
return newState
default:
return state;
}
}
- 监听变化。
“作为行为触发行为后更新视图的凭证。在组件注销时,注意销毁它哦。
- - QAQ
componentDidMount () {
/**
* 回调函数
*/
Store.subscribe(() => {
console.log(Store.getState().redux_name)
this.setState({})
})
}
Hooks
“Hooks我准备写新的文章。
- - QAQ
总结
学习React很多都是以Vue的思路先入个门,不至于一问三不知。也明白了知其一而知其三。如果有基础那么学习起来其实并不是很困难。但是它的文档并不如Vue的全面,且对于某些方面来说。花了一天时间整理一些基本的学习东西
“如果觉得对你有帮助,不妨点个赞哦。
- - QAQ
参考资料
React文档
react生命周期个人理解
react模板
React路由
Redux