react注意事项
-
因 为style = {} 里面传入的是对象, 所以正确的写法是 style={{fontSize: ‘12px’ …}}
-
在标签里面写上 事件名称 on 后面的字母是大写 , 在事件后面跟上 {} 里面传入一个 箭头函数, 里面写上逻辑业务
-
map 数组的遍历 里面必须有return
给点击的项添加红色
// 第一种方式
style={{color: index === current ? 'red' : ''}}
// 第二种方式 在上面 style 里面上写 .red {...} 样式
className={index===curIndex?'red':''}
显示隐藏
// 通过状态动态添加类名
const List = props => {
return (
<ul className={flag ? 'show' : 'hide'}>
{props.arr.map(item => <li key={item}> {item} </li>)}
</ul>
)
}
// 模拟vue的v-if
function App () {
return (
<div>
<h1 onClick={() => {flag=!flag; created()}}>商品列表</h1>
{flag && <List arr={app} />} // 模拟v-if
</div>
)
}
遍历对象
Object.keys(obj).map(item => {
return <p key={item}> {item}: {obj[item]} </p>
})
bind call 的区别
相同点: 都是改变this的指向
不同点: bind方法,他是直接改变这个函数的this
指向并且返回一个新的函数,之后再次调用这个函数的时候this
都是指向bind
绑定的第一个参数
call会在调用之后立即执行
class Counter extends React.Component {
constructor () {
super()
this.state = {
count: 1
}
this.handlerClick = this.handlerClick.bind(this) // 提前绑定好this的执行
}
handlerClick () { // 写成这个普通函数的形式 内部访问的this是undefined
this.setState({
count: ++this.state.count
}, ()=> {
console.log(this.state.count) // 拿到最新的数据
})
// console.log(this.state.count) // 上面的操作是异步的
}
render () {
return (
<div>
<p>counter ===> {this.state.count} </p>
<button onClick={this.handlerClick}>加加</button>
</div>
)
}
}
注意:上面的写法有一个问题,连续执行多次其实内部进行了对象的合并操作
// 解决?可以采用setState的第二种写法解决此类问题
this.setState((prevState)=>{ // prevState可以拿到最新this.state对象
return {
key:新的value值
}
},[,callback])
构造函数为什么方法必须写在原型上?
function Person(name, age){
this.name = name
this.age = age
this.say = function () {
console.log(this.name)
}
}
let p = new Person('张三', 19)
let p2 = new Person('李四', 20)
p.say() // 张三
p2.say() // 李四
console.log(p.say ===p2.say ) // false 代表每创建一个实例都会开辟一个say的函数空间
ES6继承
class Person {
constructor (name, age) {
this.name = name
this.age = age
}
say () {
return this.name + ',' + this.age
}
}
class Student extends Person {
constructor (name, age, school) {
super(name, age)
this.school = school
}
say () {
return super.say() + ',' + this.school
}
}
let stu = new Student('张三', 20, '谷歌')
console.log(stu.say())
受控组件
由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value, 这使得 React 的 state 成为唯一数据源。由于 handlechange 在每次按键时都会执行并更新 React state,因此显示的值将随着用户输入而更新
class App extends React.Component {
constructor () {
super()
this.state = {
value: '111'
}
}
handlerChange = (e) => {
this.setState({
value: e.target.value
})
}
render(){
return (
<div>
<input value={this.state.value} type="text" onChange={this.handlerChange} />
<p> {this.state.value} </p>
</div>
)
}
}
非受控组件
event.preventDafault() 阻止默认行为, 在表单域里阻止提交, 是针对于 form 标签, 并不是input[submit]
class App extends React.Component{
handlerSubmit = (e) => {
console.log(this.input)
e.preventDefault()
}
render () {
return (
<form onSubmit={this.handlerSubmit}>
<input ref={el=>this.input=el} type="text" />
<input type="submit" value='提交' />
</form>
)
}
}
模糊查询的内容高亮, 通过这个方法dangerouslySetInnerHTML() 将字符串解析成标签
blurFind = () => {
let newValue = prompt('请输入要查询的字段')
// let newDes = this.state.list.filter(item => item.indexOf(newValue) !== -1)
let newDes = this.state.list.filter(item => item.includes(newValue))
newDes.forEach((item, i, arr) => {
arr[i] = item.replace(new RegExp(newValue, 'g'), '<span style="color: red">'+newValue+'</span>')
})
this.setState({
des: newDes,
flag: false
})
}
<ul>
{
des.map((item, index) => {
return
<li key={index}>
<div dangerouslySetInnerHTML={{__html: item}}></div>
</li>
})
}
</ul>
react 脚手架安装
npm i create-react-app -g
create-react-app --version (3.4.1)
create-react-app 项目名称
cd 项目名称
yarn start 启动项目
脚手架引入图片的两种方式
// 第一种
import Logo from "图片路径"
<img src={Logo}/>
// 第二种
<img src={require("图片路径")}/>
如果只是简单的给input 绑定内容, 那么就使用官方提供的defaultValue
<form>
<label htmlFor='hobby'>用户民: </label>
<input id='username' type='text' defaultValue='hello' />
<input id='hobby' type='checkbox' defaultChecked />
</form>
子父通信 , todos组件里面有一个Txt 输入框 组件 还有一个LIst组件 是用来渲染输入框里面的数据的
constructor () {
super()
this.state = {
list: []
}
}
// 定义一个修改自身状态的方法, value是子组件调用的时候传入的参数
add = value => {
this.setState({
list: [value, ...this.state.list]
})
}
render() {
return (
<div>
<Txt add={this.add} /> // 把修改自身状态的方法传递给子组件
<List list={this.state.list} /> // 将自身的状态传递给子组件渲染
</div>
)
}
自定义端口号实时监听: json-server --watch 文件名 --port 端口号
上面的方式是将 两个组件公用的状态放在一个父组件中, 这样方便两个组件之间的数据共享, 这样的方式叫做状态提升, 如果不使用状态提升, 那么下面就是一个很好的例子
// 列表组件List, handlerAdd 定义一个可以修改自身状态的方法, 等待父组件去使用
handlerAdd = (str) => {
this.setState({
list: [str, ...this.state.list]
})
}
render() {
let {list} = this.state
return (
<ul>
{
list.map((item, index) => {
return <li key={index}> {item} </li>
})
}
</ul>
)
}
// todos 组件, 两个组件的父组件, 父组件通过ref标记 List组件, 拿到List组件的修改自身的方法, 自己再定义一个触发子组件handlerAdd的方法, 传入 Txt组件去触发
handlerAdd = (str) => {
this.list.handlerAdd(str)
}
render() {
return (
<div>
<Txt add={this.handlerAdd} />
<List ref={el=>this.list=el} />
</div>
)
}
// Txt组件通过键盘的回车键触发父组件的传入的方法, 将文本输入框的内容当做参数传入给List组件渲染
handler = (e) => {
let {add} = this.props
if(e.keyCode === 13){
add(e.target.value)
e.target.value = ''
}
}
react 代理的两种方式
// 通过yarn eject 弹射出config文件, 要不然后续如果继续安装其他模块的话,yarn内部就会进行检测看看有没有文件被改动过,重新变成原来的样子 (如果eject命令不成功, 将.git文件删掉)
proxy: {
"/api":{
target: 'http://47.96.0.211:9000',
changeOrigin: true,
pathRewrite: {
"^/api": ''
}
}
}
// 再者可以通过 http-proxy-middleware中间件实现代理, 安装, 然后再src根目录下新建setupProxy文件, 里面的配置:
const {createProxyMiddleware} = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
createProxyMiddleware('/api', {
target: 'http://47.96.0.211:9000',
changeOrigin: true,
pathRewrite: {
"^/api": ''
}
})
)
}
静态方法
class Person{
static say(){ //静态方法 可能在实例化之前被调用
return "hello"
}
}
// 只能通过这个类使用say() 方法, 实例调用这个方法会报错, 因为static 这个前缀就是静态方法的标识
coonsole.log(Person.say())
react 挂载阶段生命周期钩子函数
// 依次执行
constructor()
static getDerivedStateFromProps(props,state)
render()
conponentDidMount() // 该函数发起http请求
注意: 当组件的状态或者外部传入进来的属性发生改变的时候,组件的render函数会重新执行
派生状态累加的应用
// 父组件 App
constructor () {
super()
this.state = {
nums: [1, 2, 3],
sum: 0
}
}
// 在真实dom挂载完毕之后将 sum状态修改为数据的累加值, 相当于初始化sum的总和
componentDidMount () {
this.setState({
// reduce()数组的高阶函数, 可以轻松实现累加操作
sum: this.state.nums.reduce((a, b) => a+b)
})
}
// 定义一个修改自身 sum状态的方法, 当子组件点击 +1 那么视图的sum总和将会重新渲染, 保持于子组件的步调一致, 因为子组件是通过 +1的方法让视图对应的组件 +1的, 然会传递给子组件
getTotal = p => {
this.setState({
sum: this.state.sum+p
})
}
render() {
let {nums, sum} = this.state
return (
<div>
{
// 通过数据的遍历渲染成三个子组件, 并将状态里的值和修改sum的方法传递子组件
nums.map((item, index) => {
return <Counter getTotal={this.getTotal} key={index} n={item} />
})
}
<p>{sum}</p>
</div>
)
}
// 子组件 counter
constructor (props) {
super(props)
this.state = {
n: props.n // 根据父组件派生一个状态, 因为直接操作父组件的状态是不允许的
}
}
handler (p) {
// 这里判断是否小于0 如果小于0 那么就不会再-1了
if(this.state.n > 0 || p > 0){
this.setState({
n: this.state.n+p // 修改自身的状态, 对应的视图会被重新渲染
}, ()=>{
// 再修改完之后, 再调用父组件传递过来的方法, 并将具体的操作当做参数传递给父组件
this.props.getTotal(p)
})
}
}
render() {
let {n} = this.state
return (
<div>
<button onClick={this.handler.bind(this, -1)}>-</button>
{n}
<button onClick={this.handler.bind(this, +1)}>+</button>
</div>
)
}
static getDerivedStateFromProp(props, state)钩子函数 (获取派生状态来自父组件的属性)
初始化的时候会执行, 当外部传入的props改变, this.setState()改变自身的状态 也会被执行
该函数内部必须要有return, 接收两个参数, props代表外部传入的最新值, state代表自身的状态
constructor (props) {
super(props)
this.state = {
a: props.num, // 派生状态
prevProps: props // 这里可以纪录一下外部传入的num值, props里面有
}
}
static getDerivedStateFromProps (props, state) {
const prevProps = state.prevProps
// 这里判断上面纪录的值是否跟外部传递的值相等, 如果相等那么就让子组件的状态跟随外部传递的转递变化, 再者不相等, 是因为 props.num拿到的是最新的值, 而prevProps.num纪录的值是之前的值, 那么自身管理的a状态就是自身操作的数值
const controlledValue = prevProps.num !== props.num ? props.num : state.a
return {
a: controlledValue,
prevProps: props // 更新纪录的值
}
}
销毁组件触发的钩子函数 componentWillUnmount()
组件卸载的时候, 可以将componentDidMount绑定的定时器及时清除
import ReactDOM from 'react-dom'
<button onClick={()=>{ReactDOM.unmountComponentAtNode(document.getElementById('root'))}}>销毁组件</button>
react性能优化 shouldComponentUpdate()
// 父组件定义一个切换选中框的一个方法
handlerChange = (id) => {
this.state.list.forEach(item=>{
if(item.id === id){
item.flag = !item.flag
}
})
this.setState({
list: this.state.list
})
}
// 将切换的方法传递给子组件
render() {
let {list} = this.state
return (
<ul>
{
list.map(item => {
// 这里的flag状态需要单独传递
return <Two key={item.id} flag={item.flag} item={item} handlerChange={this.handlerChange} />
})
}
</ul>
)
}
// 子组件 Two
change (id) {
this.props.handlerChange(id)
}
//询问组件是否进行更新操作,如果不写默认返回true,就代表组件要更新了,就会走render
//如果说这个钩子函数返回false,就代表不让组件更新,就不会走render
shouldComponentUpdate(nextProps, nextState) {
// nextProps: 拿到最新的状态
// 这里需要拿到最新的状态和之前的状态比较, 如果不是一样的, 那么就执行render函数, 这样render函数就只会被触发一次
// 但是flag状态不可以在这里通过对象的方式取出来, 因为在这里拿出来的对象是跟nextProps.item是一个引用地址,会拿到最新的状态,这样就永远返回的式false, 代表render不会执行
if(this.props.flag !== nextProps.item.flag){
return true
}
return false
}
render() {
// 这里每次切换选中框render都会执行三次, 所以性能损耗太大
console.log('two-render')
let {item} = this.props
return (
<li>
{item.text}
<input onChange={this.change.bind(this, item.id)} type='checkbox' checked={item.flag} />
</li>
)
}
再者, 也可以将使用 PureComponent 纯组件的方式, 原理使用了上面的方式
import React, { Component,PureComponent } from 'react'
export default class index extends Component {
...
}
为什么要优化?
因为每次调用 this.setState() 方法修改状态, 那么想要视图更新, 那么必定要走render(), 即使这个状态已经修改成你想要的状态了, 当你再点击也会走render函数, 所以会损耗一定的性能
constructor () {
super()
this.state = {
num: 10
}
}
// 2.当使用这个方法之后, 视图修改成为1000后, 再点击按钮. render是不会执行的, 当然使用纯组件也是可以的
shouldComponentUpdate(nextProps, nextState){
return nextState.num !== this.state.num
}
render() {
// 1.即使视图已经是1000了, 当你再点击按钮, 还是会执行render
console.log('two-render')
let {item} = this.props
return (
<li>
{this.state.num}
<button onClick={()=>{this.setState({num: 1000})}}>改变num</button>
</li>
)
}
如果是值比较的话,外部传入的属性与内部的属性不一样的话,才会进行render更新操作
如果是引用地址比较的话,例如数组,之前的属性数组的地址与更改后的新的数组的地址一致,那么就不会进行render的更新操作
export default class index extends PureComponent {
addArr = () => {
// 使用push()方法因为还是之前的地址, 所以render不会执行, 视图就不会更新
// this.state.app.push(3)
this.setState({
arr: [...this.state.arr, 3] // 这样因为每次一个新的引用地址
})
}
render() {
return (
<li>
{this.state.arr}
<button onClick={this.addArr}>改变arr</button>
</li>
)
}
}
总结:
所谓的性能提升就是说白了,就是在某些场景下可以优化react的render的渲染次数。
1.在普通组件里面可以使用shouldComponentUpdate钩子函数提升react性能。 (nextProps,nextState)
在内部可以判断组件外部接受的最新属性与之前的属性是否一致,从而约束render刷新的时机。
只要结果返回true,render就会立马执行渲染更新,返回false就代表render不会执行。
2.可以使用PureComponent来优化性能。内部机制是通过浅比较去实现的。
3.对于无状态组件的话,使用 React.memo(组件) 来进行性能提升
forceUpdate() 强制更新
vue里面类似的方法 this. f o r c e U p d a t e ( ) o r t h i s . forceUpdate() or this. forceUpdate()orthis.set()
constructor () {
super()
this.state = {
a: 10
}
this.b = 50
}
componentDidMount () {
// this.state.a = 20 // 不推荐使用
this.setState({
a: 20
})
// 这里修改this上面绑定的属性, 如果想要视图也修改, 那么就可以使用强制更新
this.b = 1000
// 强制更新
this.forceUpdate()
}
componentDidUpdate() 数据改变引发真实dom结构挂载完毕才会触发这个函数
如果出现轮播图实例化划不动的问题, 可能是在真实dom结构还没加载完毕已经实例化轮播图了, 这样componentDidUpdate()可以解决这个问题
componentDidUpdate(){
// 因为数据改变这个函数可能会被执行多次, 所以防止实例化多次, 加一个判断
if(!this.swiper){
this.swiper = new Swiper ('.swiper-container', {
loop: true, // 循环模式选项
// 如果需要分页器
pagination: {
el: '.swiper-pagination',
},
})
}
}
还可以通过将swiper 封装成一个组件, 在父组件请求数据, 将数据传递给swiper组件, 判断数据的长度是否存在来加载swiper组件
// 这里是将需要生成多份的swiper-slide封装出去
<div>
{
this.state.list.length > 0 ? <Swiper list={this.state.list} /> : ''
}
</div>
PropTypes限定传入的数据类型
// 向子组件传递两种数据, 一个是字符串, 一个是数组
<One num={'111'} userArr={this.state.userArr} />
// One组件
import PropTypes from 'prop-types'
// 配置默认项num的值, 外部不给就用自己配置的, 外部传了就覆盖默认的
static defaultProps = {
num: 100
}
static propTypes = {
//限定外部传入的num属性可以是number/string/boolean类型的, 而且是必传项 isRequired
num: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
// 规定外部必须传递一个userArr的数组, 结构是一个对象
// 对象包括里面包含两个字段分别是id: number.isRequired, sex:string.isRequired
userArr: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
sex: PropTypes.string.isRequired
})).isRequired
}
Context
import React, {Component, createContext} from 'react';
// Context 提供了一个无需为每层组件手动添加 props, 就能在组件树中传递数据的方法
let counterContext = createContext()
// Consumer 消费者, Provider 提供者
const {Consumer, Provider} = counterContext
// 定义一个提供者的组件, 在App组件里面包裹着其他组件, 这样里面的组件都可以使用提供者的数据了
class CounterProvider extends Component {
constructor () {
super()
//所有的CounterProvider的后代组件都可以共享的状态
this.state = {
count: 100
}
}
incrementCount = () =>{
this.setState({
count: this.state.count+1
})
}
decrementCount = () =>{
this.setState({
count: this.state.count-1
})
}
render () {
return (
//Provider需要写一个value属性,value就是给Consumer提供的数据或者方法
<Provider value={{
count: this.state.count,
incrementCount: this.incrementCount,
decrementCount: this.decrementCount
}}>
// 这里是为了能够显示 <CounterProvider />包裹的内容, 有点像vue的槽口
{this.props.children}
</Provider>
)
}
}
class Counter extends Component {
//指定contextType 读取当前的counterContext
//React就会向上去查找最近的Provider ,然后使用它的值
//固定写法, 写上在this里面有对应的数据了
static contextType = counterContext
render () {
// console.log(this)
return (
<div>
{this.context.count} // 100
</div>
)
}
}
// 消费者组件(函数式组件)
function CounterBtn (props) {
return (
<Consumer>
{
//Consumer里面必须是一个函数,函数的参数就可以获取到Provider转递的值
// 可以解构出CounterProvider组件提供的两个方法
({incrementCount, decrementCount})=>{
const handler = props.type === 'increment' ? incrementCount : decrementCount
return <button onClick={handler}> {props.children} </button>
}
}
</Consumer>
)
}
// App组件
class App extends Component {
render () {
return (
<CounterProvider> // 提供者组件
// 为了能够区分两个不一样的加减功能,传递了一个字符串, 好判断提供者里面的两个方法
<CounterBtn type="increment">+</CounterBtn>
<Counter />
<CounterBtn type="decrement">-</CounterBtn>
</CounterProvider>
)
}
}
redux
// index.js store文件入口
import {createStore} from 'redux'
import reducer from './reducer'
//可以通过createStore()创建一个store,参数需要接受reducer
const store = createStore(reducer)
export default store
// state.js // 初始化状态, 提供给action
export default {
todos: [
{id: 1, title: '今天周一', isFinished: false},
{id: 2, title: '检查作业', isFinished: false}
]
}
// actionCreators.js // 创建一个任务由store派发给action
import store from './index'
export default {
addNewTodo (title) {
let action = {
type: 'addNewTodo',
title
}
store.dispatch(action)
},
...
}
// reducer.js // 执行action 任务, 修改状态,状态一旦修改, 视图也将改变
import state from './state'
const reducer = (prevState=state,action) => {
let new_state = {...prevState}
switch(action.type){
case 'addNewTodo':
//为了让之前的数据引用地址跟修改之后返回的数据地址不一样, 如果返回的是一样的, 那么store会认为你没有修改过数据, 那么视图也不会变化
new_state.todos = new_state.todos.slice()
new_state.todos.unshift({id:handler.getId(new_state.todos), title: action.title, isFinished: false})
break;
...
}
return new_state
}
export default reducer
const handler = {
getId(todos){
todos = todos.slice()
if(todos.length === 0) return 1;
return todos.sort((a, b) => {
return b.id - a.id
})[0].id+1
},
}
actionType来去规定action的类型为常量的文件, 来约束actionCreators和action 之间的标志信息的容错率
export const ADD_NEW_TODO = 'ADD_NEW_TODO'
...
reducer的拆分
// 需要在store根目录文件夹下新建reducer
import {combineReducers} from 'redux'
import todolist from './todolist/reducer' // 分支
...
// 通过combineReducers来合并所有的 reducer 分支
const reducer = combineReducers({
todolist // 合并
})
export default reducer
// 但是后续使用的组件路径和调用方法就不一样了
// 因为改了文件, 所以store.getState()只能拿到合并分支的对象, {todolist: {todos: ...}}
console.log(store.getState())
this.state = {
// todos: store.getState().todos
todos: store.getState().todolist.todos // 获取到数据
}
react-redux
Prodiver 提供者 属性上通过store将数据派给容器组件
connect 用于连接容器组件与UI组件
connect(mapStateToProps,mapDispatchToProps)(UI组件)
容器组件内部帮你做了 store.subscribe() 的方法订阅状态变化 ==> 容器组件监听状态改变了 ==> 通过属性的方式传给UI组件
把store.getState()
的状态转化为展示组件的props
使用
// index.js
import store from './store'
import {Provider} from 'react-redux'
//这边通过store属性传下去,那么所有的Provider的后代组件都可以通过connect连接,然后获取redux的状态了
<Provider store={store}>
<App />
</Provider>
// 组件
import actionCreators from '../../store/todolist/actionCreators'
import {connect} from 'react-redux'
// 转化为展示组件props的属性和方法
// mapstate这里拿到的是合并后分支的对象
export default connect(mapstate => mapstate.todolist, actionCreators)(TodoContent)
redux-thunk中间件
通常情况下,action只是一个对象,不能包含异步操作,这导致了很多创建action的逻辑只能写在组件中,代码量较多也不便于复用,同时对该部分代码测试的时候也比较困难,组件的业务逻辑也不清晰,使用中间件了之后,可以通过actionCreator异步编写action,这样代码就会拆分到actionCreator中,可维护性大大提高,可以方便于测试、复用,同时actionCreator还集成了异步操作中不同的action派发机制,减少编码过程中的代码量
redux-thunk原理:
可以看出来redux-thunk最重要的思想,就是可以接受一个返回函数的action creator。如果这个action creator 返回的是一个函数,就执行它,如果不是,就按照原来的next(action)执行。
正因为这个action creator可以返回一个函数,那么就可以在这个函数中执行一些异步的操作。换言之,redux的中间件都是对store.dispatch()的增强
// store/index.js 在redux 里面解构出applyMiddleware
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
const store = createStore(reducer, applyMiddleware(thunk))
// actionCreators.js
compute(){
return dispatch => { // 将dispatch派发任务的方法当做参数传递出来
setTimeout(() =>{ // 函数内部可以实现异步操作
let action = {
type: COMPUTE
}
dispatch(action) // 派发任务
}, 1000)
}
}
高阶组件
高级函数: 一个函数内部返回一个新的函数, 内部采用了闭包的写法
var add = x => {
return y => {
return x+y
}
}
var add = x => y => {x+y} // 简写
高阶组件:(HOC) Higher-Order-Component
高阶组件本质上就是一个函数,内部可以接收一个组件,然后返回新的组件。
例如: React.memo(UI组件) connect()(UI组件)
// About组件
import React, { Component } from 'react'
import WithCopy from './withCopy'
class About extends Component {
render() {
return (
<div>
about {this.props.data}
</div>
)
}
}
export default WithCopy(About)
// WithCopy高阶组件
import React, {Component} from 'react'
const WithCopy = Comp => {
// 内部返回一个组件, 相当于页面渲染的是这个组件, 只有把About组件当做参数在这里被渲染
return class WithCopyRight extends Component {
render () {
return (
<div>
//App.js向About传递的数据被WithCopy高阶组件劫持了,必须向下传递About才能拿到数据
<Comp {...this.props} />
©b站大佬
</div>
)
}
}
}
export default WithCopy
// App.js
import About from './components/about'
<About data='哈哈哈' />
后续如果还有一些高阶组件使用, 会在调用()的时候出现层层嵌套的现象, 那么可以使用修饰符
对于CRA的定制
采用装饰器的写法实现调用高阶组件
@WithCopy
class About extends Component {
render() {
return (
<div>
about
</div>
)
}
}
export default About
很不幸,目前的cra是不支持这种写法,我们需要单独进行定制,让其支持这种写法
- yarn add react-app-rewired
- 对脚手架轻微的调整
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test --env=jsdom",
+ "test": "react-app-rewired test --env=jsdom",
"eject": "react-scripts eject"
}
- yarn add customize-cra
- 在src同级目录下新建文件 config-overrides.js, 里面配置如下
const {
addDecoratorsLegacy,
override
} = require("customize-cra");
module.exports = override(
addDecoratorsLegacy() //就代表让当前的cra支持decorators了!
)
其实就会发现 react面向社区化的。它把一些常用到的包都放在社区,用到的话单独去查找去进行相应的配置。
vue的话面向官方化,很多东西内部都帮助我们去实现了。 vue.config.js文件标准
React-Router
最新的路由的版本是5.1.2的版本。里面的话提供了一些包
所在在做web端开发的时候只需要安装react-router-dom就可以了,因为内部已经包含了最核心的内容了
// index.js as 就是起别名
import {HashRouter as Router} from 'react-router-dom'
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root')
);
// render函数内部
<Switch>
// routeProps将component指明的路由组件属性和方法传递给Home组件, 因为Home组件只是一个普通的类组件
<Route path="/home" render={(routeProps)=> {
return this.state.isShow ? <Home data="666" {...routeProps} /> : ''
}} />
<Route path="/article" component={Article} exact />
<Route path="/article/:id" component={ArticleDetail} />
<Route path="/404" component={NotFound} />
<Redirect from='/' to='/home' exact />
<Redirect to='404' />
</Switch>
// Article.js
import {NavLink as Link} from 'react-router-dom'
<div>
<p>Article文章列表</p>
<span><Link to='/article/1?title=文章一'>文章一</Link></span>
<span><Link to={{
pathname: '/article/2',
state: {title: '文章二'}
}}>文章二</Link>
</span>
</div>
// ArticleDeatil.js
// this.props.match.id 获取动态id
// this.props.location.search 获取到query参数
// this.props.location.state 获取到隐式参数
console.log(this.props)
Route里面的path代表路径,component代表映射的路由组件
Redirect 重定向 from 从哪里来 to 指明目标到哪里去
Link 声明式跳转, 默认会被渲染成a标签
NavLink 在被点击的哪一项会被添加一个active类名
exact 声明路径为完全匹配
Switch包裹的路由只会匹配一个
如果 没有加上exact 那么路径里面只要带有 / 就会重定向到home页
如果 没有加上exact 那么详情页的渲染就会被阻止掉
render={()=> { 组件 }} 的好处式, 可以根据条件渲染, 还可以给组件传递参数
后续BackHome 返回home的组件要实现路由跳转, 就要用 {withRouter} 映射成路由组件, 因为只有被component指明的组件this.props才会有路由相关的属性和方法
import {withRouter} from 'react-router-dom'
class BackHome extends Component {
backHome = ()=>{
// this.props.history.push('/home')
// Home因为在App组件通过参数 routeProps 把路由相关的属性和方法传递给Home组件了,所以Home组件也可以this.props.location.state拿到数据
this.props.history.push({
pathname: '/home',
state: {
id: '哈哈哈'
}
})
}
render() {
console.log(this.props)
return (
<div>
<button onClick={this.backHome}>返回首页</button>
</div>
)
}
}
export default withRouter(BackHome)
上面所述, 就是这几天react的小白纪录…
后续还有react实战项目搭配Antd组件库纪录…
谢谢大家的支持
kita…