本文写的仓促,算是一个学习笔记吧,算是一个入门级的学习文章。如果你刚刚入门,那么可能一些入门的视频可能更加适合你。但如果具备一些知识,比如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 复制代码
在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">2</p> } */} {true && <p>2</p>} </div> ) } 复制代码
“通过三元运算符可以来达到
- - QAQv-if
和v-else
一样的效果。
render () { return ( <div className="App"> <p>我是一个动态的数据: {this.state.text}</p> {/* { <p v-if="true">2</p> } */} {true ? <p>true</p> : <p>false</p>} </div> ) } 复制代码
通过map
方法来遍历一些基本数据结构的节点。
“通过数据结构自带的map可以遍历出
- - QAQvalue
,index
,key
在return
中返回节点信息。
在JavaScript
中,通过onClick
来绑定一个事件。
<button onclick="activeBuff">激活按钮</button> 复制代码
而在jsx中,则是通过onClick
属性,且后面加一个jsx渲染模板的方式。
“需要注意的是,
- - QAQClass组件
下的this
是不存在的,需要用户手动为方法绑定this,在调用的点击事件方法。
<button onClick={ this.activeBuff.bind(this) }>激活按钮</button> 复制代码
众所周知,Vue
和React
都是组件化解决方案,那么在React
中是如何创建一个组件的呢?在React
新版本中,推出了React Hooks
方案。所以现在主流的还是Class
组件和Function
组件。
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> ) } } 复制代码
随着React Hooks
发布,开发的主流也渐渐的转入了函数组件,不过Class
组件在旧项目中还是会用的。所以都学习一下。对比Class组件,函数组件就非常的简单了。在函数内部返回一个render模板就OK了。如下:
import React from 'react' function FunctionDemo () { return ( <div> 我是函数组件 </div> ) } export default FunctionDemo 复制代码
这里着重的开个单章,主要是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
可以使子组件非常快捷的拿到父组件的传递过去的内容。
<ClassDemo name="wangly19"></ClassDemo> 复制代码
constructor (props) { super(props) this.state = { defaultText: '我是默认的文字' } } 复制代码
通过this.props.父组件绑定的属性名称
<p>{ this.props.name }</p> 复制代码
在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; 复制代码
看了官方的生命周期介绍,挺简洁的。分别是组件模板选然后,已经准备就绪的时候,可以做组件加载后的一些逻辑操作,镇楼神图。
构造函数初始化,最先被执行,初始化State
等等。
这是一个静态方法,需要在前面增加static
的属性
渲染函数,返回渲染的内容,当页面产生更新也会触发该方法。
组件挂载之后,这个时候组件已经挂载完毕了
组件即将被更新,这里参数分别对应前后被修改的内容,通过返回一个布尔值告知是否需要更新视图。
当视图更新,那么Render
也会重新更新
getSnapshotBeforeUpdate
在render
之后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> ) } 复制代码
组件卸载,我们可以清除一些定时器,取消网络请求。
插槽对于Vue来说并不是很陌生,在React
中插入的节点会以Props
的方式传递。可以通过pro1ps.children
找到并且渲染出来。
// 父亲组件 <ClassDemo onPropChange={this.onPropChange.bind(this)}> <h1>插入的元素 </h1> </ClassDemo> // 子组件 <div className="App"> {this.props.children} <button onClick={this.changeText.bind(this)}>更改文本</button> </div> 复制代码
路由对于SPA应用来讲可谓是重中之重,没有它的话,那么这个页面也就不能成为应用,只能称之为页面。两者可谓天差地别。
# 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>我是Page1</h1> ) } function Page2 () { return ( <h1>我是Page2</h1> ) } function Page3 () { return ( <h1>我是Page3</h1> ) } class App extends React.Component { constructor (props) { super(props) this.state = { } } render () { return ( <div className="App"> <Router> <ul> <li> <Link to="page1">Page1</Link> </li> <li> <Link to="page2">Page2</Link> </li> <li> <Link to="page3">Page3</Link> </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> <Link to={{ pathname: '/page1/10', search: '?roles=[10, 20]', state: { name: 'wangly19' }, }} > Page1 </Link> </li> <li> <Link to="page2">Page2</Link> </li> <li> <Link to="page3">Page3</Link> </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
是类似于Vuex
的一个全局状态解决方案,它的功能主要是用来存储公有全局状态。来方便管理一些共有配置参数,解决业务体积大,结构复杂的项目提供好的状态管理。
“如果项目不是特别需要,尽量不去使用它。
- - QAQ
# shell npm install redux --save 复制代码
看到官方的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 复制代码
// 声明默认的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' <p>{ Store.getState().redux_name }</p> 复制代码
<button onClick={ 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我准备写新的文章。
- - QAQ
学习React很多都是以Vue的思路先入个门,不至于一问三不知。也明白了知其一而知其三。如果有基础那么学习起来其实并不是很困难。但是它的文档并不如Vue的全面,且对于某些方面来说。花了一天时间整理一些基本的学习东西
“如果觉得对你有帮助,不妨点个赞哦。
- - QAQ
参考资料
React文档
react生命周期个人理解
react模板
React路由
Redux