aiyoudiao aiyoudiao
  • JavaScript
  • Vue
  • React
  • 低代码
  • 线性系统
  • 暂未分类
  • LeetCode
  • 算法
  • 数据结构
  • 设计模式
  • Other
  • PMP
  • Office
  • 面试
  • Bash
  • 流年往事
  • 经验片段
  • 读书杂感
  • 归档
  • 分类
  • 标签
  • 简介
  • 收藏
  • 有趣
  • 文档

码二

扫微信二维码,认识一下码二吧😉。
  • JavaScript
  • Vue
  • React
  • 低代码
  • 线性系统
  • 暂未分类
  • LeetCode
  • 算法
  • 数据结构
  • 设计模式
  • Other
  • PMP
  • Office
  • 面试
  • Bash
  • 流年往事
  • 经验片段
  • 读书杂感
  • 归档
  • 分类
  • 标签
  • 简介
  • 收藏
  • 有趣
  • 文档
  • JavaScript

  • vue

  • react

    • 16_4

      • React16_4整理-开篇
      • React16_4整理-TodoList
      • React16_4整理-上阶
      • React16_4整理-Redux开篇
      • React16_4整理-Redux上阶
      • React16_4整理-实战
        • 前言
        • 项目目录搭建
        • Styled-Components
        • Styled-Components 的使用
        • 制作字体图标
        • 使用 combineReducers 完成对数据的拆分管理
        • actionCreators 与 constants 的拆分
        • 使用 Immutable.js 来管理 store 中的数据
        • 使用 redux-immutable 统一数据的格式
        • 使用 ajax 获取数据
        • 什么是路由,如何在 React 中使用路由功能
        • 向 styled-components 组件中传值
        • immutable 中的 fromJS 与 List 方法
        • 不必拘于形式
        • 页面性能优化及路由跳转
          • 页面性能优化
          • 路由跳转
        • 页面路由参数的传递
        • 获取 styled.Components 中的 dom
        • 路由跳转 Redirect
        • 代码优化
        • 异步组件以及 witchRouter 路由方法的使用
        • 总结
          • 常用的hooks就以下这些:
          • class和hook的对比
          • react性能优化
        • 基础的项目上线流程大概这样
    • react心得
    • react设计的哲学
    • react上手知识点(上篇)
    • react上手知识点(下篇)
    • antd
  • 低代码

  • 读书会

  • 线性代数

  • docker

  • auto

  • 杂记

  • 笔记
  • react
  • 16_4
aiyoudiao
2022-04-13

React16_4整理-实战

# 前言

前面做了那么多准备,开发一个门户应用。在开发过程中,使用更多技术点。比如Styled-Components、Immutable.js、redux-immutable、页面性能优化、react-router等等等。
总结的时候会把常用的hooks以及react性能优化、基础的项目上线流程简单说一下。

# 项目目录搭建

安装 Create-React-App:npm install -g create-react-app
运行 create-react-app 命令 新建一个 react 项目
安装第三方模块儿 Styled-Components :yarn add styled-components

# Styled-Components

在react中 你在一个地方引入css 就会在全局使用
使用第三方模块儿Styled-Components 对组件中的样式进行管理,使得每一个组件的样式只对自己生效
最终会返回这个样式的组件给你,你可以直接使用这个自定义样式的组件。

# Styled-Components 的使用

将css文件改为js文件,并且修改用引入的css为js。

在js文件这样写:

/* injectGlobal 表示注入全局样式  */
import {injectGlobal} from 'styled-components'

injectGlobal`
    body {
        margin: 0;
        padding: 0;
        font-family: sans-serif;
        background: green;
    }
`

使用 reset.css

去官网下载 reset.css :https://meyerweb.com/eric/tools/css/reset/index.html。reset.css 能够清空html 标签在所有浏览器的默认样式了,也就是默认样式归0。

给某一个组件单独设置样式:先写样式文件,并且导出样式对应的控件,最后你使用那个控件包裹你的内容即可。

import styled from 'styled-components';

export const HeaderWarpper = styled.div`
    height: 58px;
    background: #f0f0f0;
    border-bottom: 1px solid #fff;
`;
import React, {Component} from 'react';
import { HeaderWarpper } from './style.js'
class Header extends Component {
    render (){
        return (
                <HeaderWarpper>
                    Header
                </HeaderWarpper>
            )
    }
}

export default Header;

模板标签以及模板字符串

function tag (str) {
    console.log(str);
}

tag`123456`//最后会输出["123456"] 使用这种方式调用会将传递进去的参数包装成一个数组

# 制作字体图标

iconfont.cn 可以用来制作自己需要的字体图标。

# 使用 combineReducers 完成对数据的拆分管理

一般情况下,一个文件的代码超过 300 行,就说你的设计肯定是有问题的。
reducer.js 中如果存放过多的数据,就需要拆分了,比如将不同组件需要使用的 reducer 放到不同组件下的 store 文件夹下,然后使用 combineReducers 整合一下,给不同的 reducer 加一个别名,使用的时候通过 state.别名.value 来用。

/* 这个是根目录 store目录下的reducer ,用来整合其它的reducer */
import { combineReducers } from 'redux';
import headerReducer from '../common/header/store/reducer';

// export default combineReducers({
//     header:headerReducer
// })
const reducer = combineReducers({
    header: headerReducer
})
export default reducer;
/*  之前使用是直接state.focused,定义了别名后 可以这么用了*/
const mapStateToPorps = (state) => {
    return {
        focused: state.header.focused
    }
}

按需引入时可以给引入的对象起一个别名,使用as关键字

    import {reducer as headerReducer} from '../common/header/store';
    const reducer = combineReducers({
        header: headerReducer
    })
    export default reducer;

# actionCreators 与 constants 的拆分

如果你引入的文件里是按需导出的,但是你又向一下子全部导入,你可以使用通配符加 as 来进行全部导入,不需要加花括号,加花括号其实是通过解构的方式进行赋值。

    import * as actionCreators from './store/actionCreators';

    //使用的时候就可以通过 actionCreators.成员的方式了。

如果你想一下子把某一个文件夹下的 js 文件全部导出,你可以在该文件夹下新建一个 index.js 文件,然后在这个文件中引入当前文件夹下所有的 js,之后以整体导出的方式导出去。
最后你在别的地方可以直接导入这个文件夹,你可以直接导入也可以按需(解构)的方式导入。

import reducer from './reducer';
import * as actionCreators from './actionCreators';
import * as constants from './constants';
/* 整体导出 */
export {reducer, actionCreators, constants}
/* 以解构的方式按需导入 */
import {actionCreators} from './store/';

# 使用 Immutable.js 来管理 store 中的数据

immutabel.js 是 facebook 花三年时间开发的一个项目,也是一个第三方的模块儿。
使用 immutabel.js 可以生成一个 immutabel的对象,immutabel表示不可改变
安装immutabel:yarn add immutable

immutable 库,将一个 js 对象转换为一个immutable对象,它的底层还是采用虚拟dom加diff算法,这样改值得时候性能特别好

/*fromJS可以将一个js对象转换为一个immutable对象*/
import {fromJS} from 'immutable';

/* 这时候 defaultState是一个immutable对象*/
const defaultState = fromJS({
    focused: false
});
export default (state = defaultState, action) => {
    // 当state对象为一个普通的对象时可以这么做,
    // 但是state对象已经是一个immutable对象了,所以不能返回一个普通对象
    // if (action.type === constants.SERCH_FOCUS){
    //     return {
    //         focused: action.focused
    //     }
    // }

    if (action.type === constants.SERCH_FOCUS){
        /*只能够调用set方法了*/
        /* immutable对象的set方法,会结合之前immutable对象的值
            和设置的值,返回一个全新的immutable对象,也就是减少了你克隆state的那一步。
        */
        return state.set('focused', action.focused);
    }
}

// 这里面传递的是一个immutable对象而不是一个简单的{},所以不能够直接.的方式取值了,要使用get的方式取值
const mapStateToPorps = (state) => {
    return {
        // focused:state.header.focused
        focused: state.header.get('focused')
    }
}

# 使用 redux-immutable 统一数据的格式

安装redux-immutable:yarn add redux-immutable

使用 redux-immutable 来整合所有的reducer

/* 之前整合所有的 reducer 是 引入redux中的 combineReducers*/
import { combineReducers } from 'redux';
import {reducer as headerReducer} from '../common/header/store';

/* 现在改用 redux-immutable中的combineReducers*/
import { combineReducers } from 'redux-immutable';
/* 这里面的state不是一个immutable对象,而 header是一个immutable对象*/
const mapStateToPorps= (state) => {
        return {
            // focused:state.header.focused
            focused: state.header.get('focused')
        }
}

/*
    现在改用 redux-immutable中的combineReducers之后 state也是一个immutable对象了 ,这样对数据的操作就统一了
*/
const mapStateToPorps = (state) => {
        return {
            // focused:state.header.focused
            // focused:state.header.get('focused')
            focused:state.get('header').get('focused')
            // 你也可以使用getIn这个方法,与上面等价
            focused:state.getIn(['header','focused'])
        }
}

# 使用 ajax 获取数据

你可以将数据放到 public目录下的api文件夹,以json的格式存放。因为底层是一个node服务器,它会以public文件夹为网站根目录。
这样一来你往网站根目录中存放数据,请求是网站根目录下的资源,就可以获取对应的数据。

与后端开发的时候,先自己模拟数据,但是在这之前要和后端约定好,约定好返回的数据格式,例如{sucess:true,data:[]},这样的。
这样你才能够在模拟数据的时候写的 ajax 与真正上线的时候请求后端的接口统一。

immutable 会将state中的数组成员转换为immutable类型的数组,所以当你set的时候,如果是给一个数组成员赋值,那么对应的值也得是immutable类型的数组,不可以将普通数组赋值给immutable类型的数组成员。

immutable对象转换为一个普通的 js 对象:list.toJS();
在使用 immutable 对象的 set 方法时,每次只能够改变一个值,如果你想同时改变两个值,可以这样做。

state.merge(
        {
            list:action.data,
            totalPage:action:totalPage
        }
    );

//上面的写法要比下面的好一些,性能更好
state.set("list",action.data).set("totalPage",action:totalPage)

# 什么是路由,如何在 React 中使用路由功能

安装 react路由第三方模块儿:react-router-dom,安装命令:yarn add react-router-dom
引入路由: Provider 里面最好嵌套一个 div,因为里面只准有一个节点,BrowserRouter也是一样。

import { BrowserRouter,Route } from 'react-router-dom';

简单使用路由

import React, { Component } from 'react';
import Header from './components/header'
import store from './store'
import { Provider } from 'react-redux';
import { BrowserRouter, Route } from 'react-router-dom';

class App extends Component {
    render(){
        return (
            <Provider store = {store}>
            <div>
                <Header/>
                    {/*定义路由作用区域*/}
                <BrowserRouter>
                    <div>
                        {/* exact 表示当你匹配的path完完全全与route中的path相等时才可以匹配成功 */}
                        <Route path = '/' exact render = {() => {return <div>home</div>}}></Route>
                        {/*定义路由*/}
                        <Route path = '/detail' exact render = {() => {return <div>detail</div>}}></Route>
                    </div>
                </BrowserRouter>
            </div>
            </Provider>
        )
    }
}

export default App;

通过路由返回 组件

import React, { Component } from 'react';
import Header from './components/header'
import store from './store'
import { Provider } from 'react-redux';
import { BrowserRouter, Route } from 'react-router-dom';
import Home from './pages/home';
import Detail from './pages/detail';

class App extends Component {
    render(){
        return (
            <Provider store = {store}>
            <div>
                <Header/>
                <BrowserRouter>
                    <div>
                        {/* exact 表示当你匹配的path完完全全与route中的path相等时才可以匹配成功 */}
                        <Route path = '/' exact component = {Home}></Route>
                        <Route path = '/detail' exact component = {Detail}></Route>
                    </div>
                </BrowserRouter>
            </div>
            </Provider>
        )
    }
}
export default App;

# 向 styled-components 组件中传值

styled-components中页面向style.js中定义的样式组件传值,页面直接给对应的组件添加属性,style.js的组件里,可以通过${(props)=>(props.imgUrl)}的方式获取到页面传递过来的值。

# immutable 中的 fromJS 与 List 方法

immutable中除了fromJS可以将一个数组转换为一个immutable对象外,还有一个List方法也能够将数组转换为一个immutable对象,但是这个list方法只能够把数组的外层变成immutable对象,不能递归将数组内的对象也变成immutable对象。

# 不必拘于形式

UI 组件中允许存在少量的逻辑,并不是什么 JS 都不能放在里面。

# 页面性能优化及路由跳转

# 页面性能优化

之前使用 shouldComponentUpdate 来检查是否与当前页面状态有关来决定是否重新渲染当前组件,return true表示重绘否则就不重绘。

react中有一个PureComponent,它与Component的区别是它内部自动实现了shouldComponentUpdate这个生命周期内检查功能,所以你只需要把所有的Component替换成PureComponent就可以减少每一个组件内都写shouldComponentUpdate来检查了。

之所以可以使用PureComponent来大大的提升性能,因为框架中的数据格式都是统一的,都是immutable对象,就是因为这样所以使用PureComponent才一点问题都没有。
如果数据格式不统一那么你会遇到坑。所以使用immutable很重要。

如果你不使用 immutable,那么你就是用Component然后自己写shouldComponentUpdate吧。

# 路由跳转

单页应用就是只会加载一次 html,整个页面都是路由js文件来控制的。重新加载 html 是比较耗性能的。

如果你想使用 a 标签来进行页面跳转,请不要那样做,最好使用 react-route-dom中的Link 来替换a标签的跳转功能,它会跳转到真正的路由中去,而不会先去重新加载 html 再跳转到真正的路由中去。

import {Link} from 'react-route-dom';

/* 不要使用a标签来进行页面跳转 */
<a href='/detail'>
    ...
</a>
/* 将上面的写法 改成这样的 */
<Link key = {index} to = '/detail'>
    ...
</Link>

# 页面路由参数的传递

动态路由

import {Link} from 'react-route-dom';

/* 跳转到详情页的时候 直接传递id过去 */
<Link key = {index} to = {'/detail/'+"5"}>
        ...
</Link>
/* 这种写法要改成下面这种写法了 */
<Route path = '/detail' exact component = {Detail} ></Route>
/* 传递参数就需要 设置一个占位 :id  不然匹配不到,因为这个是完全匹配 */
<Route path = '/detail/:id' exact component = {Detail} ></Route>
/* 页面中可以通过 match.params.id 来进行获取 */
console.log(this.props.match.params.id);//5

queryString的方式传递数据

import {Link} from 'react-route-dom';

/* 跳转到详情页的时候 直接传递?id=xx过去 */
<Link key = {index} to = {'/detail?id='+"5"}>
        ...
</Link>
/* 还是这种写法,依然可以进行匹配 */
<Route path = '/detail' exact component = {Detail} ></Route>
/* 页面中可以通过 location.search 来进行获取,但是需要你自己来解析一下 */
console.log(this.props.location.search);//?id=5

# 获取 styled.Components 中的 dom

styled.Components中的dom对原生dom进行了包裹,所以不再使用ref 而是使用 innerRef。

/* 可以获取 styled.Components组件中包裹的input标签内的真实dom*/
<Input placeholder = "账号" innerRef = {(input) => {this.account = input}} />

/* 打印真实的账号文本框中的值 */
console.log(this.account.value);

# 路由跳转 Redirect

在组件中 直接进行路由跳转

import {Redirect} from 'react-router-dom';

render(){
    return <Redirect to = "/" />
}

# 代码优化

合适的行间距,代码尾部加分号,switch中代码过多,就写一个方法,把代码放到方法中,然后再 switch 的 case 中调用方法即可。

# 异步组件以及 witchRouter 路由方法的使用

页面中所有的 js 代码都在 bundle.js中,这会造成这个js文件异常的大,所以需要使用第三方的异步组件。
安装第三方异步组件 react-loadable:yarn add react-loadable
使用react-loadable

/* 该组件目录下新建一个 loadble.js */

import Loadable from 'react-loadable';
import React from 'react';

const LoadableComponent = Loadable({
    /* 要异步加载的 组件 ./ 表示当前目录下的 ./index.js 文件 */
    loader: () => import('./'),
    loading () { /* 异步加载时的 动画页面 */
        return (<div>正在加载...</div>)
    },
});

/* 将这个组件返回回去 */
export default class App extends React.Component {
    render() {
        return <LoadableComponent/>;
    }
}

/* 你也可以返回一个无状态的组件回去 */
export default () => <LoadableComponent />
/* 原本是这样的 */
import Detail from './pages/detail'

<Route path = '/detail/:id' exact component = {Detail} />
/*现在改为 在App.js 文件中将引入的组件 改为引入这个异步组件 */
/*loadable 将原本引入的组件包装成了异步的组件 */

import Detail from './pages/detail/loadble.js'

<Route path='/detail/:id' exact component = {Detail} />

使用了react-loadable之后就不能够直接获取路由传递过来的参数了,这时候就需要使用react-router-dom 中的 withRouter方法。

    import {withRouter} from 'react-router-dom';

    /* 原来是这样的 */
    export connect(mapStateToPoprs, mapDispatchToPorps)(Detail);

    /* 改为这样 让生成的容器组件 有能力获取传递过来的参数 */
    export connect(mapStateToPoprs,mapDispatchToPorps)(withRouter(Detail));

使用react-loadable 会当对应的组件代码与bundle.js分离,这样就能够大大减少bundle.js中的代码了。

# 总结

本片文章是基于16.4的,最新的版本主要以16.8为标准吧,废除了一些生命周期函数,但主要的componentDidMount、shouldComponentUpdate、componentDidUpdate、componentWillUnmount都没变化。 加入react hooks,hooks的使用降低了理解成本,也让react组件更像是函数。它没有什么魔法,就是数组,还是两个数组,一个存所有state,一个存所有的state的setter。

# 常用的hooks就以下这些:

useState

用两个数组实现的,一个存所有的state,另一个存state的setter。
所以如果是在循环和判断的逻辑下用useState,会导致state和setter错乱,最好在函数顶部使用。

useEffect

治病的过程中产生了额外的影响。
useEffect 就是在你初始化之后,再次render之前,产生的额外影响。比如 数组的slice 和 splice函数。

useEffect 函数就是hooks中副作用函数,第一个参数就是callback函数,第二个参数是一个数组,就是你要监听的变量。如果你放一个空数组,它会起到一个componentDidMount的作用,没有监听任何变量,那么就不存在任何变化,那么就只会执行一次。
如果你想销毁state,可以在useEffect函数return的第一个函数。

useLayoutEffect

useEffect是在浏览器渲染后执行,useLayoutEffect是在浏览器渲染前执行。
官方不建议你是用它,但是官方还是出了这个hooks。
useLayoutEffect 主要在useEffect函数结果不满意的时候才被用到,一般是当处理dom改变带的副作用,这个hook执行时,浏览器并未对dom进行渲染,相对于useEffect执行要早。

useEffect 和 useLayoutEffect

useEffect 和 useLayoutEffect在mount和update阶段执行方法一样,传参不一样。
useEffect异步执行,而useLayoutEffect同步执行。
Effect 用 HookPassive 标记useEffect,用HookLayout标记useLayoutEffect。

useMemo

老的是memo,新的hooks中是useMemo。传递⼀个函数和依赖项,当依赖项发⽣变 化,执⾏函数,该函数需要返回⼀个值

类似于vue中的computed 计算属性,但是写法不同。

useCallback

返回一个函数,当监听的数据发生变化,才会执行回调函数。

useRef

相当于一个id的作用,id能够找真实dom,这个可以用来找虚拟dom,可以拿到fiber对象。

useReducer

redux、mbox是完整独立的状态管理方案。未必一定是为react打造的,它们只是和react进行了结合。react本身关注的是渲染UI和响应各种事件,为了提高效率,所以官方才出了这个hook。

聚合参数,以达到开发效率的提升以及代码的简洁。
useReducer在re-render时候不会改变存储位置,state作为props传给子component不会产生diff的效率问题(useMemo优化)

useReducer也许是替代useState,由于数据并未能共享,所以 并不能替代redux、mbox。

useContext

useContext能够产生一个Provider,能够使得你的Provider标签下面所有的组件共享一组数据。类似于一个局部的window哟。

自定义Hook

自定义的hook其实并不是hook,只是一个自定义的函数,只是它用了use标识开头,看起来像是符合了Hook的规则。
useXXX 和vue3的composition api很像。

# class和hook的对比

官方不考虑废除class。class只是一种编程方式,在16.0之后依然是使用fiber和新的diff算法,效率上并不比class差,提升效率使用的是fiber。

hooks延申的写法太多,也许页面中一堆useState,可能显得比较乱,有时很难搞清楚这些state之间的关联,当然你可以尝试根据相应的业务逻辑做一下聚合。容易发生内存泄露。
class语法虽然难以理解,都是代码逻辑比较清晰,它面向对象嘛。

hook往往作为那种单节点组件,比如几十个表单域。最后使用class来写一个整个表单的数据流的入口。

useEffect是浅监听。

# react性能优化

  1. 避免跨层级的移动
  2. sholudComponentUpdate
  3. Memo/useMemo
  4. useReducer

# 基础的项目上线流程大概这样

后端和前端定了一些接口之后,前端同学去写前端的代码,后端去写后端接口。

以后端是php为例

后端的开发目录一般在htdocs下,接口在 htdocs 的 api 目录下,这里写各种各样的 php 代码去调用数据库。
前端可以将模拟的api文件夹下的数据删除掉了,因为后端同学已经把接口写好了。
前端同学 使用npm run build命令,打包所有的文件到一个build目录下,这时候前端的任务已经完成了。
后端的同学 就会把build目录的文件全部粘贴到 htdocs 目录下,这时候前端的代码就已经放到后端的项目中了,这个时候就完成了项目的上线。
当前端的项目放到了后端的项目里面,这时候就应该去访问后端的项目了。
后端开启服务器,你去访问,成功访问,因为你之前弄的 api 目录和后端的 api 目录一致,所以就可以直接运行了。

#react#redux
上次更新时间: 10年18月2023日 01时57分53秒
React16_4整理-Redux上阶
react心得

← React16_4整理-Redux上阶 react心得 →

最近更新
01
01.数据结构导论一览.md
10-16
02
30.2023年06月04日.md
06-04
03
08.与测量相关.md
05-06
更多文章>
Theme by Vdoing | Copyright © 2017-2023 aiyoudiao 码二 备案号: 鄂ICP备2022002654号-1