React小技巧汇总

image.png

引言

使用 React.js 一段时间了,把使用过程遇到的小坑和小技巧记录下来,但愿可以帮助到其余人。此文章是长篇大论你只有耐得住寂寞,禁得住诱惑才会有所成长。javascript

image.png

1、工具篇

一、显示 html

<div dangerouslySetInnerHTML={{ __html: LANG.auth_register_tips1 }}/>复制代码

二、经常使用组件

  • axios(http请求模块,可用于前端任何场景,很强大)
  • echarts-for-react(可视化图表,别人基于react对echarts的封装,足够用了)
  • recharts(另外一个基于react封装的图表)
  • nprogress(顶部加载条,蛮好用)
  • react-draft-wysiwyg(别人基于react的富文本封装,若是找到其余更好的能够替换)
  • react-draggable(拖拽模块,找了个简单版的)
  • screenfull(全屏插件)
  • photoswipe(图片弹层查看插件,不依赖jQuery,仍是蛮好用)
  • animate.css(css动画库)
  • redux Web 应用是一个状态机,视图与状态是一一对应的.全部的状态,保存在一个对象里面
  • redux-logger 日志
  • Reselect 记忆组件
  • redux-thunk 为了解决异步action的问题
  • redux-saga 为了解决异步action的问题
  • react-router-redux 保持路由与应用状态(state)同步
  • react-router-dom

三、react-devtools 调试工具

工具地址:github.com/facebook/re…css

全局安装:html

yarn global add react-devtools复制代码

配置:在package.json中配置上去:前端

"scripts": {
"devtools": "react-devtools"
}复制代码

而后就能够启动了:yarn run devtoolsvue

image.png

2、组件通信篇

须要组件之进行通讯的几种状况

  • 父组件向子组件通讯
  • 子组件向父组件通讯
  • 跨级组件通讯
  • 没有嵌套关系组件之间的通讯
  1. redux 架构
  2. 父组件向子组件 —— props
  3. 子组件向父组件 —— props.funciton 接收参数
  4. 利用事件机制

1. 父组件向子组件通讯

React数据流动是单向的,父组件向子组件通讯也是最多见的;父组件经过props向子组件传递须要的信息java

2. 子组件向父组件通讯

  • 利用回调函数
  • 利用自定义事件机制

子组件改变父组件的state

// 通常改变state值的一种方式
const { data } = this.state;
this.setState({ data: {...data, key: 1 } });
// 另一种能够经过callback的方式改变state的值
this.setState(({ data }) => ({ data: {...data, key: 1 } }));
// 还能够
this.setState((state, props) => {
return { counter: state.counter + props.step };
});复制代码

3. 跨级组件通讯

  • 层层组件传递props
例如A组件和B组件之间要进行通讯,先找到A和B公共的父组件,A先向C组件通讯,C组件经过props和B组件通讯,此时C组件起的就是中间件的做用
  • 使用context
context是一个全局变量,像是一个大容器,在任何地方均可以访问到,咱们能够把要通讯的信息放在context上,而后在其余组件中能够随意取到;可是React官方不建议使用大量context,尽管他能够减小逐层传递,可是当组件结构复杂的时候,咱们并不知道context是从哪里传过来的;并且context是一个全局变量,全局变量正是致使应用走向混乱的罪魁祸首.

使用context

下面例子中的组件关系: ListItem是List的子组件,List是app的子组件react

ListItem.jswebpack

import React, { Component } from 'react';
import PropTypes from 'prop-types';
class ListItem extends Component {
    // 子组件声明本身要使用context
    static contextTypes = {
        color: PropTypes.string,
    }
    static propTypes = {
        value: PropTypes.string,
    }
    render() {
        const { value } = this.props;
        return (
            <li style={{ background: this.context.color }}>
                <span>{value}</span>
            </li>
        );
    }
}
export default ListItem;复制代码

List.jsios

import ListItem from './ListItem';
class List extends Component {
    // 父组件声明本身支持context
    static childContextTypes = {
        color: PropTypes.string,
    }
    static propTypes = {
        list: PropTypes.array,
    }
    // 提供一个函数,用来返回相应的context对象
    getChildContext() {
        return {
            color: 'red',
        };
    }
    render() {
        const { list } = this.props;
        return (
            <div>
                <ul>
                    {
                        list.map((entry, index) =>
                            <ListItem key={`list-${index}`} value={entry.text} />,
                       )
                    }
                </ul>
            </div>
        );
    }
}
export default List;复制代码

App.jsgit

import React, { Component } from 'react';
import List from './components/List';
const list = [
    {
        text: '题目一',
    },
    {
        text: '题目二',
    },
];
export default class App extends Component {
    render() {
        return (
            <div>
                <List
                    list={list}
                />
            </div>
        );
    }
}复制代码

4. 没有嵌套关系的组件通讯

  • 使用自定义事件机制
在componentDidMount事件中,若是组件挂载完成,再订阅事件;在组件卸载的时候,在componentWillUnmount事件中取消事件的订阅;以经常使用的发布/订阅模式举例,借用Node.js Events模块的浏览器版实现

使用自定义事件的方式

下面例子中的组件关系: List1和List2没有任何嵌套关系,App是他们的父组件;

实现这样一个功能: 点击List2中的一个按钮,改变List1中的信息显示

首先须要项目中安装events 包:

npm install events --save复制代码

在src下新建一个util目录里面建一个events.js

import { EventEmitter } from 'events';
export default new EventEmitter();复制代码

list1.js

import React, { Component } from 'react';
import emitter from '../util/events';
class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            message: 'List1',
        };
    }
    componentDidMount() {
        // 组件装载完成之后声明一个自定义事件
        this.eventEmitter = emitter.addListener('changeMessage', (message) => {
            this.setState({
                message,
            });
        });
    }
    componentWillUnmount() {
        emitter.removeListener(this.eventEmitter);
    }
    render() {
        return (
            <div>
                {this.state.message}
            </div>
        );
    }
}
export default List;复制代码

List2.js

import React, { Component } from 'react';
import emitter from '../util/events';
class List2 extends Component {
    handleClick = (message) => {
        emitter.emit('changeMessage', message);
    };
    render() {
        return (
            <div>
                <button onClick={this.handleClick.bind(this, 'List2')}>点击我改变List1组件中显示信息</button>
            </div>
        );
    }
}复制代码

APP.js

import React, { Component } from 'react';
import List1 from './components/List1';
import List2 from './components/List2';
export default class App extends Component {
    render() {
        return (
            <div>
                <List1 />
                <List2 />
            </div>
        );
    }
}复制代码

自定义事件是典型的发布订阅模式,经过向事件对象上添加监听器和触发事件来实现组件之间的通讯

组件间通讯之onRef方法

组件间通讯除了props外还有onRef方法,不过React官方文档建议不要过分依赖ref。本文使用onRef语境为在表单录入时提取公共组件,在提交时分别获取表单信息。

下面demo中点击父组件按钮能够获取子组件所有信息,包括状态和方法,能够看下demo中控制台打印。

// 父组件
class Parent extends React.Component {
  testRef=(ref)=>{
    this.child = ref
    console.log(ref) // -> 获取整个Child元素
  }
  handleClick=()=>{
    alert(this.child.state.info) // -> 经过this.child能够拿到child全部状态和方法
  }
  render() {
    return <div>
      <Child onRef={this.testRef} />
      <button onClick={this.handleClick}>父组件按钮</button>
    </div>
  }
}
// 子组件
class Child extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      info:'快点击子组件按钮哈哈哈'
    }
  }
  componentDidMount(){
    this.props.onRef(this)
    console.log(this) // ->将child传递给this.props.onRef()方法
  }
  handleChildClick=()=>{
    this.setState({info:'经过父组件按钮获取到子组件信息啦啦啦'})
  } 
  render(){
    return <button onClick={this.handleChildClick}>子组件按钮</button>
  }
}复制代码
原理:

当在子组件中调用onRef函数时,正在调用从父组件传递的函数。this.props.onRef(this)这里的参数指向子组件自己,父组件接收该引用做为第一个参数:onRef = {ref =>(this.child = ref)}而后它使用this.child保存引用。以后,能够在父组件内访问整个子组件实例,而且能够调用子组件函数。

3、路由篇

使用React构建的单页面应用,要想实现页面间的跳转,首先想到的就是使用路由。在React中,经常使用的有两个包能够实现这个需求,那就是react-router和react-router-dom。本文主要针对react-router-dom进行说明。

image.png

一、React Router中有三类组件:

  • router 组件(BrowserRouter,HashRouter)
  • route matching 组件(Route,Switch)
  • navigation 组件(Link)

基于React Router的web应用,根组件应该是一个router组件(BrowserRouterHashRouter)。 项目中,react-router-dom提供了和两种路由。两种路由都会建立一个history对象。若是咱们的应用有服务器响应web的请求,咱们一般使用<BrowserRouter>组件; 若是使用静态文件服务器,则咱们应该使用<HashRouter>组件

二、HashRouter和BrowserRouter

其实就是路由的hash和history两种模式(要是不了解这两种模式之间的区别那就须要去恶补下啦)

而且这两个组件是路由的容器,必须在最外层

// hash模式
ReactDom.render(
  <HashRouter>
        <Route path="/" component={Home}/>
    </HashRouter>
)
// history模式
ReactDom.render(
  <BrowserRouter>
        <Route path="/" component={Home}/>
    </BrowserRouter>
)复制代码

下面说说HashRouter和BrowserRouter上的参数

  • basename 路由的基础连接,用来部署到非根目录下,好比你须要将项目部署到 www.xxxx.com/web 下,则设置basename="/web"
  • getUserConfirmation 用来拦截Prompt组件,而且决定是否跳转
  • forceRefresh 用来设置是否强制浏览器总体刷新,默认值为false
  • keLength 用来设置location.key的长度,默认是6,能够自定义

三、Route

Route是路由的一个原材料,它是控制路径对应显示的组件

Route的参数

  • path 跳转的路径
  • component 对应路径显示的组件
  • render 能够本身写render函数返回具体的dom,而不须要去设置component
  • location 传递route对象,和当前的route对象对比,若是匹配则跳转
  • exact 匹配规则,true的时候则精确匹配。
path url 是否开启 匹配结果
/a /a/b false yes
/a /a/b true no
  • sensitive 是否区分path的大小写
path url 是否开启 匹配结果
/a /a true yes
/a /A true yes
  • strict 是否匹配后面的/
path url 是否开启 匹配结果
/a /a/ true yes
/a /a/c true yes
/a /a true no

四、Router

低级路由,适用于任何路由组件,主要和redux深度集成,使用必须配合history对象

使用Router路由的目的是和状态管理库如redux中的history同步对接

<Router history={history}>
    ...
</Router>
复制代码复制代码

五、Link和NavLink

二者都是跳转路由,NavLink的参数更多些

Link的api

  • to 有两种写法,表示跳转到哪一个路由
  • 字符串写法
<Link to="/a"/>
复制代码复制代码
  • 对象写法
<Link to={{
  pathname: '/courses',
  search: '?sort=name',
  hash: '#the-hash',
  state: { fromDashboard: true }
}}/>
复制代码复制代码
  • replace 就是将push改为replace
  • innerRef 访问Link标签的dom

NavLink的api

  • Link的全部api
  • activeClassName 路由激活的时候设置的类名
  • activeStyle 路由激活设置的样式
  • exact 参考Route,符合这个条件才会激活active类
  • strict 参考Route,符合这个条件才会激活active类
  • isActive 接收一个回调函数,active状态变化的时候回触发,返回false则中断跳转
const oddEvent = (match, location) => {
  console.log(match,location)
  if (!match) {
    return false
  }
  console.log(match.id)
  return true
}
<NavLink isActive={oddEvent} to="/a/123">组件一</NavLink>
复制代码复制代码
  • location 接收一个location对象,当url知足这个对象的条件才会跳转
<NavLink to="/a/123" location={{ key:"mb5wu3", pathname:"/a/123" }}/>
复制代码复制代码

六、Redirect

Redirect重定向很简单,咱们直接看代码便可

// 基本的重定向
<Redirect to="/somewhere/else" />
// 对象形式
<Redirect
  to={{
    pathname: "/login",
    search: "?utm=your+face",
    state: { referrer: currentLocation }
  }}
/>
// 采用push生成新的记录
<Redirect push to="/somewhere/else" />
// 配合Switch组件使用,form表示重定向以前的路径,若是匹配则重定向,不匹配则不重定向
<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>
复制代码复制代码

七、Switch

路由切换,只会匹配第一个路由,能够想象成tab栏

Switch内部只能包含Route、Redirect、Router

<Switch>
  <Route exact path="/" component={Home}/>
  <Route path="/about" component={About}/>
  <Route path="/:user" component={User}/>
  <Route component={NoMatch}/>
</Switch>
复制代码复制代码

八、withRouter

当一个非路由组件也想访问到当前路由的match,location,history对象,那么withRouter将是一个很是好的选择,能够理解为将一个组件包裹成路由组件

import { withRouter } from 'react-router-dom'
const MyComponent = (props) => {
    const { match, location, history } = this.props
     return (
        <div>{props.location.pathname}</div>
    )
}
const FirstTest = withRouter(MyComponent);
复制代码复制代码

九、history对象

用过vue的都知道,vue-router有组件形式的导航,也有编程式导航,那react-router怎么使用api来控制前进后退和刷新呢?这就须要咱们来讲明下history对象的做用了

其实在每一个路由组件中咱们可使用this.props.history获取到history对象,也可使用withRouter包裹组件获取,

在history中封装了push,replace,go等方法,具体内容以下

History {
    length: number;
    action: Action;
    location: Location;
    push(path: Path, state?: LocationState): void; // 调用push前进到一个地址,能够接受一个state对象,就是自定义的路由数据
    push(location: LocationDescriptorObject): void; // 接受一个location的描述对象
    replace(path: Path, state?: LocationState): void; // 用页面替换当前的路径,不可再goBack
    replace(location: LocationDescriptorObject): void; // 同上
    go(n: number): void; // 往前走多少也页面
    goBack(): void; // 返回一个页面
    goForward(): void; // 前进一个页面
    block(prompt?: boolean | string | TransitionPromptHook): UnregisterCallback;
    listen(listener: LocationListener): UnregisterCallback;
    createHref(location: LocationDescriptorObject): Href;
}
复制代码复制代码

这样咱们想使用api来操做前进后退就能够调用history中的方法啦

其次也可经过暗转history库来实现,具体案例以下

import { BrowserRouter } from 'react-router-dom';
const history = require('history').createBrowserHistory();
/**
 * forceRefresh: bool
 * 做用:当浏览器不支持 HTML5 的 history API 时强制刷新页面。
 */
const supportsHistory = 'pushState' in window.history;
 <BrowserRouter
        history={history}
        basename="/"
        forceRefresh={!supportsHistory}
      >
        {/* 路由入口 */}
       ......
</BrowserRouter>复制代码

十、路由鉴权

4、性能篇

写了一段时间的react以后,渐渐的喜欢上了使用react来写应用。

咱们知道,Facebook在推出react时打出的旗号之一就是高性能。

今天咱们还一块儿来聊一聊react的性能优化,思考还能经过哪些手段来提高React的性能,使咱们的react更快,性能更好。

一、react组件的性能优化(渲染角度优化)

一、react性能查看工具

再讲性能优化以前,咱们须要先来了解一下如何查看react加载组件时所耗费的时间的工具,在react 16版本以前咱们可使用React Perf来查看。

react16版本以前,咱们可使用react-addons-perf工具来查看,而在最新的16版本,咱们只须要在url后加上?react_pref。

首先来了解一下react-addons-perf

react-addons-perf这是 React 官方推出的一个性能工具包,能够打印出组件渲染的时间、次数、浪费时间等。

简单说几个api,具体用法可参考官网

  • Perf.start() 开始记录
  • Perf.stop() 结束记录
  • Perf.printInclusive() 查看全部设计到的组件render
  • Perf.printWasted() 查看不须要的浪费组件render

你们能够在chorme中先安装React Perf扩展,而后在入口文件或者reduxstore.js中加入相应的代码便可:

image.png

再来了解一下,在最新的React16版本中,在url后加上?react_pref,就能够在chrome浏览器的performance,咱们能够查看User Timeing来查看组件的加载时间。点击record开始记录,注意记录时长不要超过20s,不然可能致使chrome挂起。

image.png

使用此工具的具体操做你们能够看下图:

image.png

二、单个react组件性能优化

一、render里面尽可能减小新建变量和bind函数,传递参数是尽可能减小传递参数的数量。

首先咱们先思考一个问题,好比我要实现一个点击按钮使相应的num增长1,咱们有哪一些方法。

你们应该都能想到,无非就是三种,以下图:

image.png


第一种是在构造函数中绑定this,第二种是在render()函数里面绑定this,第三种就是使用箭头函数,都能实现上述方法;

可是哪种方法的性能最好,是咱们要考虑的问题。应该你们都知道答案:第一种的性能最好

由于第一种,构造函数每一次渲染的时候只会执行一遍;

而第二种方法,在每次render()的时候都会从新执行一遍函数;

第三种方法的话,每一次render()的时候,都会生成一个新的箭头函数,即便两个箭头函数的内容是同样的。

第三种方法咱们能够举一个例子,由于react判断是否须要进行render浅层比较,简单来讲就是经过===来判断的,若是state或者prop的类型是字符串或者数字,只要值相同,那么浅层比较就会认为其相同;

可是若是前者的类型是复杂的对象的时候,咱们知道对象是引用类型,浅层比较只会认为这两个prop是否是同一个引用,若是不是,哪怕这两个对象中的内容彻底同样,也会被认为是两个不一样的prop

举个例子:

当咱们给组件Foo给名为styleprop赋值;

<Foo style={{ color:"red" }}复制代码

使用这种方法,每一次渲染都会被认为是一个style这个prop发生了变化,由于每一次都会产生一个对象给style

那么咱们应该如何改进,若是想要让react渲染的时候认为先后对象类型prop相同,则必需要保证prop指向同一个javascript对象,以下:

const fooStyle = { color: "red" }; //取保这个初始化只执行一次,不要放在render中,能够放在构造函数中

<Foo style={fooStyle} />复制代码

二、定制shouldComponentUpdate函数

shouldComponentUpdate是决定react组件何时可以不从新渲染的函数,可是这个函数默认的实现方式就是简单的返回一个true。也就是说,默认每次更新的时候都要调用所用的生命周期函数,包括render函数,从新渲染。

咱们来看一下下面的一个例子

image.png

咱们写两个组件,AppDemo组件,并写两个方法,一个改变App中的num的值,一个是改变title,咱们在Demo的render中打印render函数。咱们能够看到如下的效果:

image.png

咱们能够清晰的看到虽然demo组件里的title值没有改变,可是仍是render了。

为了解决这个问题,咱们能够对demo组件进行以下的修改:

image.png

只有当demo的title值发生改变的时候,咱们才去render,咱们能够看一下效果:

image.png

以上只是一个特别简单的一个对于shouldComponentUpdate的定制。

在最新的react中,react给咱们提供了React.PureComponent,官方也在早期提供了名为react-addons-pure-render-mixin插件来从新实现shouldComponentUpdate生命周期方法。

image.png

经过上述的方法的效果也是和咱们定制shouldComponentUpdate的效果是一致的。

可是咱们要注意的是,这里的PureRender是浅比较的,由于深比较的场景是至关昂贵的。因此咱们要注意咱们在1.1中说到的一些注意点:不要直接为props设置对象或者数组不要将方法直接绑定在元素上,由于其实函数也是对象

三、多个react组件性能优化,key的优化

react组件在装载过程当中,react经过在render方法在内存中产生一个树形结构,树上的节点表明一个react组件或者原生的Dom元素,这个树形结构就是咱们所谓的Vitural Dom,react根据这个来渲染产生浏览器的Dom树。

react在更新阶段对比原有的Vitural Dom和新生成的Vitural Dom,找出不一样之处,在根据不一样来渲染Dom树。

react为了追求高性能,采用了时间复杂度为O(N)来比较两个属性结构的区别,由于要确切比较两个树形结构,须要经过O(N^3),这会下降性能。

简单来讲,react利用key来识别组件,它是一种身份标识标识,就像咱们的身份证用来辨识一我的同样。

列表的 key

  • key 不会传给组件,若是须要使用 key 的值,另外命名为其余属性传进去
  • 当一个列表顺序会重排时,不适合使用数组的索引做为 key

*注意:另外有个方式:推荐使用shortid生成惟一key的数组,和数据数组一块儿使用,省去提交数据时再重组数组。

案例:

import React from 'react';
import shortid from 'shortid';

class Demo extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
      data: ['a', 'b', 'c']
    }
    this.dataKeys = this.state.data.map(v => shortid.generate());
  }
  
    deleteOne = index => { // 删除操做
        const { data } = this.state;
        this.setState({ data: data.filter((v, i) => i !== index) });
        this.dataKyes.splice(index, 1);
    }
    
    render() {
      return (
          <ul>
               {
                   data.map((v, i) => 
                    <li 
                        onClick={i => this.deleteOne(i)}  
                        key={this.dataKeys[i]}
                    >
                        {v}
                    </li>
                    )
               } 
            </ul>
      )
  }
}
// 稍微抽取,能够封装一个通用的组件复制代码

另外须要指明的是:

key不是用来提高react的性能的,不过用好key对性能是有帮组的。

不能使用random来使用key

key相同,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新。

key值不一样,则react先销毁该组件(有状态组件的componentWillUnmount会执行),而后从新建立该组件(有状态组件的constructorcomponentWillUnmount都会执行)

咱们举几个状况,你们就会立刻理解:

  • 节点类型不一样
// A组件

<div>
  <Todos />
</div>

// B组件
<span>
  <Todos />
</span>复制代码

咱们想把A组件更新成B组件,react在作比较的时候,发现最外面的根结点不同,直接就废掉了以前的<div>节点,包括里面的子节点,这是一个巨大的浪费,可是为了不O(N^3)的时间复杂度,只能采用这种方式
因此在开发过程当中,咱们应该尽可能避免上面的状况,不要将包裹节点的类型随意改变。

  • 两个节点类型同样

这里包括两种状况,一种是节点是Dom类型,还有一种react组件。

对于dom类型,咱们举个例子:

// A组件
<div style={{color: 'red',fontSize:15}} className="welcome">
  Hello World!!!
</div>

// B组件
<div style={{color: 'green',fontSize:15}} className="react">
  Good Bye!!!
</div>复制代码

上述A和B组件的区别是文字、classNamestyle中的color发生改变,由于Dom元素没变,React只会修改他变化的部分。

针对react组件类型,渲染无非就是在走一遍组件实例的更新过程,最主要的就是定制shouldComponentUpdate,咱们上面也有讲到,就不细讲了。

  • 多个子组件状况

咱们看两个例子就能明白

例子一:

// A
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
  <TodoItem text="Third" complete={false} />
</ul>复制代码

从A变到B,若是shouldComponentUpdate处理得当,咱们只须要更新装载third的那一次就行。

咱们来看看下一个例子:

// A
<ul>
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem text="Zero" complete={false} />
  <TodoItem text="First" complete={false} />
  <TodoItem text="Second" complete={false} />
</ul>复制代码

这里由于react是采用O(n)的时间复杂度,因此会依次将text为First的改成Zero,text为Second改成First,在最后再加上一个组件,text为Second。现存的两个的text的属性都被改变了,因此会依次渲染。

若是咱们这里有1000个实例,那么就会发生1000次更新。

这里咱们就要用到Key

简单来讲,其实这一个Key就是react组件的身份证号。

咱们将上一个例子改为以下,就能够避免上面的问题了,react就可以知道其实B里面的第二个和第三个组件其实就是A中的第一个和第二个实例。

// A
<ul>
  <TodoItem key={1} text="First" complete={false} />
  <TodoItem key={2} text="Second" complete={false} />
</ul>

// B
<ul>
  <TodoItem key={0} text="Zero" complete={false} />
  <TodoItem key={1} text="First" complete={false} />
  <TodoItem key={2} text="Second" complete={false} />
</ul>复制代码

不过如今,react也会提醒咱们不要忘记使用key,若是没有加,在浏览器中会报错。

image.png关于key的使用咱们要注意的是,这个key值要稳定不变的,就如同身份证号之于咱们是稳定不变的同样。

一个常见的错误就是,拿数组的的下标值去当作key,这个是很危险的,代码以下,咱们必定要避免。

<ul>
  {
        todos.map((item, index) => {
            <TodoItem
              key={index}
              text={item.text}
              completed={item.completed}
        })
  }
</ul>复制代码

二、redux性能优化:reselect(数据获取时优化)

mapStateToProps也被叫作selector,在store发生变化的时候就会被调用,而无论是否是selector关心的数据发生改变它都会被调用,因此若是selector计算量很是大,每次更新都从新计算可能会带来性能问题。

Reselect能帮你省去这些不必的从新计算。

Reselect 提供 createSelector 函数来建立可记忆的 selector。

createSelector 接收一个 input-selectors 数组和一个转换函数做为参数。

若是 state tree 的改变会引发 input-selector 值变化,那么 selector 会调用转换函数,传入 input-selectors 做为参数,并返回结果。

若是 input-selectors 的值和前一次的同样,它将会直接返回前一次计算的数据,而不会再调用一次转换函数。

这样就能够避免没必要要的计算,为性能带来提高。

例子:

import {} from 'reselect';
export const getItems = (state) => state.cart.get('items');
export const getItemsWithTotals = createSelector(
[ getItems ],
(item) => {
 return items.map(i =>{
  return i.set('total', i.get('price', 0) * i.get('quantity');
 });
}
)复制代码

建立一个记忆性的selector.这个意思是getItemWithTotals在第一次函数运行的时候将会进行运算.

若是同一个函数再次被调用,可是输入值(getItems的值)没有变化,函数将会返回一个缓存(cached)的计算结果.

若是items被修改了(例如:item添加,数量的变化,任何事情操做了getItems的结果),函数将会再次执行.

在前面的优化过程当中,咱们都是优化渲染来提升性能的,既然reactredux都是经过数据驱动的的方式驱动渲染过程,那么处理优化渲染过程,获取数据的过程也是须要考虑的一个优化点。

//下面是redux中简单的一个筛选功能
const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case 'SHOW_ALL':
      return todos
    case 'SHOW_COMPLETED':
      return todos.filter(t => t.completed)
    case 'SHOW_ACTIVE':
      return todos.filter(t => !t.completed)
  }
}

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}复制代码

mapStateToProps函数做为redux store中获取数据的重要一环,当咱们根据filtertodos来显示相应的待办事项的时候,咱们都要遍历todos字段上的数组。

当数组比较大的时候,则会下降性能。

这个时候,reselect就应运而生了,它的动做原理:只要相关的状态没有改变,那么就直接使用上一次的缓存结果。

具体的用法我就不在这里过多介绍了,已经有不少的牛人写了相关的文章,我也不重复写了,你们若是想深刻了解的话,能够参考reselect的giuhubRedux的中间件-Reselect

三、分隔代码

一、动态加载

ES6标准引入了import以方便咱们静态加载模块。形式如:

import xxx from xxx.js复制代码

尽管import对于咱们加载模块颇有帮助,可是静态加载模块的方式必定程度上限制了咱们来实现异步模块加载。不过,目前动态加载模块的import()语法已处于提案阶段,而且webpack已将他引入并使用。import()提供了基于Promise的API,所以,import()的返回值是一个完成状态或拒绝状态的Promise对象。形式如:

import(/* webpackChunkName: 'module'*/ "module")
    .then(() => {
        //todo
    })
    .catch(_ => console.log('It is an error'))复制代码

webpack在编译时,识别到动态加载的import语法,则webpack会为当前动态加载的模块建立一个单独的bundle。若是你使用的是官方的Create-react-app脚手架或React的服务端渲染框架Next.js,那么能够直接使用动态import语法。若是你的脚手架是你本身配置的webpack,那么你须要按照官方指南来设置,请移步[1]。

二、动态加载React组件

当前最为流行的一种方法是使用 React-loadable [2]库提供的懒加载React组件。它利用import()语法,使用Promise语法加载React组件。同时,React-loadable支持React的服务端渲染。 一般,咱们以以下方式实现组件:

import LazyComponet from 'LazyComponent';
export default function DemoComponent() {
    return (
        <div>
            <p>demo component</p>
            <AComponent />
        </div>
    )
}复制代码

在上面的例子中,假设 LazyComponetDemoComponent 渲染时咱们并不展现。可是由于咱们使用import语法将 LazyComponet 导入,因此在编译时会将 LazyComponet 的代码与 DemoComponent 的代码打包到同一个bundle里面。 可是,这并非咱们想要的。因此咱们能够经过使用 React-loadable 来懒加载 LazyComponet ,同时将 LazyComponet 的代码单独打包到一个bundle里面。咱们能够看一下官网提供的例子:

import Loadable from 'react-loadable';
import Loading from './my-loading-component';
const LoadableComponent = Loadable({
  loader: () => import('./my-component'),
  loading: Loading,
});
export default class App extends React.Component {
  render() {
    return <LoadableComponent/>;
  }
}复制代码

从例子中咱们能够看到,react-loadable使用动态import()方法,并将导入的组件分配给loader属性。同时,react-loadable提供了一个loading属性,以设置在加载组件时将展现的组件。

三、lazy和suspense的使用

React.lazy and Suspense is not yet available for server-side rendering. If you want to do code-splitting in a server rendered app, we recommend Loadable Components. It has a nice guide for bundle splitting with server-side rendering.


在使用以前,咱们须要特别注意的一点是,React官方明确支持,React.lazy和Suspense并不支持服务端渲染。所以,使用服务端渲染的同窗,请绕行至 react-loadableloadable-components [3]。

因为我是对原有项目进行的升级,所以,本文如下内容主要针对于老项目升级React最新版所作的工做。

一、代码升级React最新版

若是你的代码是Reactv16,那么能够直接升级到最新版本,固然React16.6已经提供了lazy和suspense方法。若是是v16以前,则按照官方操做迁移。

二、肯定原有代码的懒加载组件

首先,按照需求,将非首屏加载的组件肯定为懒加载组件,个人项目共肯定五个组件能够进行懒加载。修改方式很简单,原有引入组件的方法为:

import LazyComponent from "../components/LazyComponent ";复制代码

修改成:

const LazyComponent = React.lazy(() =>
  import(/* webpackChunkName: 'lazyComponent'*/ "../components/LazyComponent")
);复制代码

如代码所示:将静态引用组件的代码替换为调用React.lazy(),在lazy()传入一个匿名函数做为参数,在函数中动态引入 lazyComponent 组件。这样在咱们渲染这个组件前,浏览器将不会下载 lazyComponent.bundle.js 文件和它的依赖。 其中,import内的webpackChunkName为咱们定义的bundle文件名。

若是React要渲染组件时,组件依赖的代码还没下载好,会出现什么状况? <React.Suspense/>的出现帮咱们解决问题。在代码未下载好前,它将会渲染fallback属性传入的值。所以咱们的原有代码为:

return (
        <div>
            <MainComponet />
            <LazyComponent />
        </div>
    )复制代码

修改成:

return (
        <div>
            <MainComponet />
            <React.Suspense fallback={<div>正在加载中</div>}>
                <LazyComponent />
            </React.Suspense>
        </div>
    )复制代码

fallback中能够修改成任意的spinner,本次不作过多优化。假如你不使用React.suspense,则React会给出你错误提示,所以记得React.lazy和React.Suspense搭配使用。 此时咱们能够从网络请求中看到,动态加载的lazyComponet组件被单独打包到一个bundle里面,然而,在首屏加载的时候,该bundle已经加载到咱们的页面中了,这也许并非咱们想要的,咱们想要的是当咱们须要的时候再加载。接下来咱们就控制一下,当咱们须要的时候,再加载该文件。

三、经过变量控制加载

本来我选择的五个懒加载组件均属于弹层性质的组件,所以必然会设置一个state来控制该组件的显示与隐藏,所以咱们将代码改成:

return (
        <div>
            <MainComponet />
            {this.state.showLazyComponent && (
                <React.Suspense fallback={<div>正在加载中</div>}>
                    <LazyComponent />
                </React.Suspense>
            )}
        </div>
    )复制代码

由此,在首屏加载时,并未加载咱们的懒加载组件 LazyComponent 所对应的bundle包。等到咱们点击须要该组件显示时,页面才去加载该js。这便达到了咱们代码分离并懒加载的目的。那么咱们这么操做,到底主bundle包的体积减小了吗?接下来咱们打包文件看一下。

四、打包文件

优化前打包出来的文件:

image.png

优化后打包出来的文件:

image.png

app.js 文件变小,随之增长 lazyComponent.js 。当懒加载组件多时,咱们即可必定程度上减小首屏加载文件的大小,提升首屏的渲染速度。本实验仅仅抽取一个组件做为示例,若是懒加载的数量较多,足以明显减少app.js的体积。

总结——>验证优化的有效性

五、[验证优化的时效性] 利用Puppeteer和Performance API作对比

为了验证前面我所作的优化的有效性,我作了一组对比实验。实验内容为使用puppeteer分别访问优化前和优化后的页面1000次,使用Performance API分别统计五项数据在这1000次访问时的平均值。 实验结果以下图所示,其中:

  • A为request请求平均耗时
  • B为解析dom树耗时平均耗时
  • C为请求完毕至DOM加载平均耗时
  • D为请求开始到domContentLoadedEvent结束平均耗时
  • E为请求开始至load平均耗时

image.png


折线图没法准确展现数据,所以,附表格数据以下(均为平均耗时):

类别 优化后 优化前

A(request请求)

7.01 7.04

B(解析dom树平均)

30.28 32.59

C(请求完毕至DOM加载)

552.86 582.0

D(请求开始到domContentLoadedEvent结束)

569.13 589.0

E(请求开始至load结束)

1055.59 1126.94

从数据中咱们能够看出,优化先后请求时间并无什么影响,可是整体load的时间明显缩短并立刻进入1000ms大关,可见优化后对于首屏的加载速度仍是有明显提高。

注:因puppeteer运行1000次的过程当中,会出现网络波动的状况,致使有些请求的数据偏大,所以平均值并不能彻底体现正常的请求时间。但1000次的平均值足以进行优化先后的请求时间对比。

六、[验证优化的时效性] 利用Chorme Performance 参数作对比

由于Performance API提供的参数有限,所以我从Chrome浏览器的performance summary中拿到了单次页面请求时的参数。由于是单次数据,所以咱们不进行详细的对比。在此列出,只为说明优化先后浏览器渲染时间上哪些部分有提高。 优化前: 优化后:

image.png image.png

  • 蓝色:加载(Loading)时间下降
  • 黄色:脚本运算(Scripting)时间下降
  • 紫色:渲染(Rendering)时间下降
  • 绿色:绘制(Painting)时间持平
  • 灰色:其余(Other)时间下降
  • 闲置:浏览器空闲时间下降

另外,我从Network中发现,优化后由于页面解析的相对以前较快,所以主接口的请求时间也相应的提早了一些。

四、总结

从多项数据代表, React.lazyReact.Suspense 的使用必定程度上加快了首屏的渲染速度,使得咱们的页面加载更快。 另外,当咱们想添加一个新功能而引入一个新依赖时,咱们每每会评估该依赖的大小以及引入该依赖会对原有bundle形成多大影响。假如该功能不多被用到,那么咱们能够痛快地使用 React.lazyReact.Suspense 来按需加载该功能,而无需牺牲用户体验了。

四、使用React.memo()来优化函数组件的性能

eact16.6加入的另一个专门用来优化 函数组件(Functional Component)性能的方法: React.memo

一、无用的渲染

组件是构成React视图的一个基本单元。有些组件会有本身本地的状态(state), 当它们的值因为用户的操做而发生改变时,组件就会从新渲染。在一个React应用中,一个组件可能会被频繁地进行渲染。这些渲染虽然有一小部分是必须的,不过大多数都是无用的,它们的存在会大大下降咱们应用的性能。
看下面这个例子:

import React from 'react';

class TestC extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    
    componentWillUpdate(nextProps, nextState) {
        console.log('componentWillUpdate')
    }
    
    componentDidUpdate(prevProps, prevState) {
        console.log('componentDidUpdate')
        
    }
    
    render() {
        return (
            <div >
            {this.state.count}
            <button onClick={()=>this.setState({count: 1})}>Click Me</button>
            </div>
        );
    }
}
export default TestC;

复制代码

TestC组件有一个本地状态count,它的初始值是0(state = {count: 0})。当咱们点击Click Me按钮时,count的值被设置为1。这时候屏幕的数字将会由0变成1。当咱们再次点击该按钮时,count的值仍是1, 这时候TestC组件不该该被从新渲染,但是现实是这样的吗?

为了测试count重复设置相同的值组件会不会被从新渲染, 我为TestC组件添加了两个生命周期函数: componentWillUpdate和componentDidUpdate。componentWillUpdate方法在组件将要被从新渲染时被调用,而componentDidUpdate方法会在组件成功重渲染后被调用。

在浏览器中运行咱们的代码,而后屡次点击Click Me按钮,你能够看到如下输出:


咱们能够看到'componentWillUpdate'和'componentWillUpdate'在每次咱们点击完按钮后,都会在控制台输出来。因此即便count被设置相同的值,TestC组件仍是会被从新渲染,这些就是所谓的无用渲染。

二、函数组件

上面咱们探讨了如何使用PureComponentshouldComponentUpdate的方法优化类组件的性能。虽然类组件是React应用的主要组成部分,不过函数组件(Functional Component)一样能够被做为React组件使用。

function TestC(props) {
    return (
        <div>
            I am a functional component
        </div>
    )
}
复制代码

对于函数组件,它们没有诸如state的东西去保存它们本地的状态(虽然在React Hooks中函数组件可使用useState去使用状态), 因此咱们不能像在类组件中使用shouldComponentUpdate等生命函数去控制函数组件的重渲染。固然,咱们也不能使用extends React.PureComponent了,由于它压根就不是一个类。

要探讨解决方案,让咱们先验证一下函数组件是否是也有和类组件同样的无用渲染的问题。

首先咱们先将ES6的TestC类转换为一个函数组件:

import React from 'react';

const TestC = (props) => {
    console.log(`Rendering TestC :` props)
    return ( 
        <div>
            {props.count}
        </div>
    )
}
export default TestC;
// App.js
<TestC count={5} />

复制代码

当上面的代码初次加载时,控制台的输出是:


一样,咱们能够打开Chrome的调试工具,点击React标签而后选中TestC组件:


咱们能够看到这个组件的参数值是5,让咱们将这个值改成45, 这时候浏览器输出


因为count的值改变了,因此该组件也被从新渲染了,控制台输出Object{count: 45},让咱们重复设置count的值为45, 而后再看一下控制台的输出结果:

由输出结果能够看出,即便count的值保持不变,仍是45, 该组件仍是被重渲染了。

既然函数组件也有无用渲染的问题,咱们如何对其进行优化呢?

三、解决方案: 使用React.memo()

React.memo(...)是React v16.6引进来的新属性。它的做用和React.PureComponent相似,是用来控制函数组件的从新渲染的。React.memo(...) 其实就是函数组件的React.PureComponent

如何使用React.memo(...)?

React.memo使用起来很是简单,假设你有如下的函数组件:

const Funcomponent = ()=> {
    return (
        <div>
            Hiya!! I am a Funtional component
        </div>
    )
}
复制代码

咱们只需将上面的Funcomponent做为参数传入React.memo中:

const Funcomponent = ()=> {
    return (
        <div>
            Hiya!! I am a Funtional component
        </div>
    )
}
const MemodFuncComponent = React.memo(FunComponent)
复制代码

React.memo会返回一个纯化(purified)的组件MemoFuncComponent,这个组件将会在JSX标记中渲染出来。当组件的参数props和状态state发生改变时,React将会检查前一个状态和参数是否和下一个状态和参数是否相同,若是相同,组件将不会被渲染,若是不一样,组件将会被从新渲染。

如今让咱们在TestC组件上使用React.memo进行优化:

let TestC = (props) => {
    console.log('Rendering TestC :', props)
    return ( 
        <div>
        { props.count }
        </>
    )
}
TestC = React.memo(TestC);
复制代码

打开浏览器从新加载咱们的应用。而后打开Chrome调试工具,点击React标签,而后选中<Memo(TestC)>组件。

接着编辑一下props的值,将count改成89,咱们将会看到咱们的应用被从新渲染了:


而后重复设置count的值为89:


这里没有从新渲染!

这就是React.memo(...)这个函数牛X的地方!

在咱们以前那个没用到React.memo(...)的例子中,count的重复设置会使组件进行从新渲染。但是咱们用了React.memo后,该组件在传入的值不变的前提下是不会被从新渲染的。

高阶组件(HOC)

高阶函数,能够传入函数做为参数的函数,如 map,sort,reduce。高阶组件包装了另外一个组件的组件。

  1. 属性代理 (Props Proxy)
  2. 反向继承 (Inheritance Inversion)

Immutable.js

由于数据是不可变的,能够避免引用传值的问题,但也麻烦

无状态组件

使用无状态组件,只从父组件接收 props,能够提升组件的渲染性能

const HelloWorld = (props) => <div>{props.name}</div>ReactDOM.render(<HelloWorld name="HelloWorld" />,App)复制代码

componentWillReceiveProps 中取 props 的值

注意应该取 nextProps,而不是 this.props

bind 绑定函数

利用 bind 绑定函数,是默认有 event 这个参数的,只是这个参数在给定参数以后

handleClockClick (id, e) {console.log(id,e)}<button onClick={this.handleClockClick.bind(this, 2)}>Clock</button>复制代码

ES6 类中,函数 this 不默认指向 对象

获取元素

  • this.getDomNode 已经在低版本被移除了,如今设置 ref=xxx,而后使用 this.refs.xxx 访问 DOM 元素
  • ref 能够赋值两种类型,一种是字符串,一种是函数, 字符串只能用在类组件,DOM 元素使用函数,纯函数组件不能使用 ref。旧版本 DOM 元素虽然可使用 ref,可是 React 已不推荐。

ref="test" // this.refs.test 访问ref={test => this.test = test} // this.test 访问复制代码

组合 VS 继承

React 推荐使用组合,而不是继承,组合在 UI 来的更加直观,代码看起来也比较容易,更符合咱们的认知,也符合 React component-base 的特性。

当只写属性名时,默认值为 true

<MyComponent isStock/>// isStock 默认为 true复制代码

createPortal

  • 将子节点挂载到父组件之外组件的方法,在 render 方法中使用,不能挂载到父组件,由于此时父组件都没有渲染好,没法获取到 DOM
  • 行为上跟其余子节点没有区别,由于 React DOM 树上依然在这个组件上,包括事件冒泡等东西
import { createPortal } from 'react-dom'createPortal(this.props.children, document.getElementById('portal-root'))复制代码

错误边界

componentDidCatch 错误边界,为组件定义一个父组件,父组件捕获错误,并提供回退的 UI

  • 用法
componentDidCatch(error, info) {this.setState({ hasError: true });console.log(error, info)}复制代码
  • 没法捕获的错误
    事件处理
    异步代码 (例如 setTimeout 或 requestAnimationFrame 回调函数)
    服务端渲染
    错误边界自身抛出来的错误 (而不是其子组件)

高级组件就是函数

  • 不该该在高阶组件修改原组件的属性
  • 利用函数包裹组件,返回一个新的组件
  • 为组件切换不一样的数据源
  1. Showcase 组件,利用 getData (Showcase, data)函数包裹,获取不一样数据
  • 不要在 render 中使用高阶组件
    由于每一次挂载组件,都会从新获取一个高阶组件的实例
  • hoistNonReactStatic
    将原始组件的静态方法拷贝到包裹组件中

容器组件

  • 处理数据订阅和状态管理
  • 高阶组件是参数化的容器组件

rander prop

标题相同,利用高阶组件把标题渲染到不一样的组件

React 中使用 Web component

这个时候有一点要注意的是,对于 Web component 应该使用 class,而不是 className

lable 的 for

for 是 JS 的保留字,因此使用 htmlFor 替代 for

style属性

浏览器后缀除了ms之外,都应该以大写字母开头。这就是为何WebkitTransition有一个大写字母W。

const divStyle = {WebkitTransition: 'all', // note the capital 'W' heremsTransition: 'all' // 'ms' is the only lowercase vendor prefix};复制代码

在 IE11 如下使用 React16

React16 依赖集合类型 Map 和 Set,在未提供原生支持的浏览器,须要使用一个 polyfill,例如 core-js 和 babel-polyfill

使用 core-js 支持

import 'core-js/es6/map';import 'core-js/es6/set';import React from 'react';import ReactDOM from 'react-dom';ReactDOM.render(<h1>Hello, world!</h1>,document.getElementById('root'));复制代码

componentDidMount 请求服务器数据

在 componentDidMount 请求服务器数据并利用 setState 时应注意,在组件卸载 componentWillUnmount 应该把去求去掉

使用es6新特性传递组件props

const {data, type} = this.state;
// 通常方法
<Demo data={data} type={type}/>
// es6方法
<Demo {...{data, type}}/>复制代码

三、 利用es6 rest 参数(形式为...变量名)传递可变数量的props

// 定义子组件
const Demo = ({ prop1, prop2, ...restProps }) => (
<div>{ restProps.text}</div>
)
// 父组件使用Demo
<Demo prop1={xxx} prop2={xxx} text={xxx}/>复制代码

四、setState的其余用法

// 通常改变state值的一种方式
const { data } = this.state;
this.setState({ data: {...data, key: 1 } });
// 另一种能够经过callback的方式改变state的值
this.setState(({ data }) => ({ data: {...data, key: 1 } }));
// 还能够
this.setState((state, props) => {
return { counter: state.counter + props.step };
});复制代码

五、React 性能优化

// React 性能优化有不少种方式,
// 那常见的一种就是在生命周期函数shouldComponentUpdate里面判断
// 某些值或属性来控制组件是否从新再次渲染。
// 判断通常的字符串,数字或者基础的对象,数组都仍是比较好处理
// 那嵌套的对象或者数组就比较麻烦了,对于这种
// 推荐使用lodash(或者其余的相似库)的isEqual对嵌套数组或对象进行判断
shouldComponentUpdate(nextProps, nextState) {
if (_.isEqual(nextState.columns, this.state.columns)) return false;
return true;
}复制代码

React 进阶提升 - 技巧篇(28 个视频)连接

介绍 React 的一些进阶知识点,一些开发上的实践技巧,一些工具库等。

视频更新地址:www.qiuzhi99.com/

React 进阶提升 - 技巧篇

react 技巧 #1 如何用 netlify 云服务部署 react 应用 65「07:14」

react 技巧 #2 把 react 应用部署到 GitHub Pages 18「05:34」

react 技巧 #3 react-router 教程 part 1 51「10:29」

react 技巧 #4 react-router 教程 part 2 11「07:39」

React 进阶提升 #5 无状态组件的最佳写法 44「Pro」「04:52」

React 进阶提升 #6 Fragment 14「Pro」「02:36」

React 进阶提升 #7 context(上下文) 9「03:58」

React 进阶提升 #8 高阶组件 14「Pro」「02:51」

React 进阶提升 #9 强大酷炫好玩的 web IDE 工具(鼠标点击生成代码,缩减 N 倍开发时间) 12「Pro」「08:20」

React 进阶提升 #10 用高阶组件来重构代码 11「05:58」

React 进阶提升 #11 我最爱的 React 库 - 功能强大的可插入组件 (简化代码) 1「Pro」「04:30」

React 进阶提升 #12 返回多个组件的正确方式 5「Pro」「03:07」

React 进阶提升 #13 netlifyctl 一键部署前端应用 2「06:49」

React 进阶提升 #14 defaultProps 和 类型检查 PropTypes part 1 4「06:37」

React 进阶提升 #15 类型检查 PropTypes part 2「Pro」「09:57」

React 进阶提升 #16 用 Render Props 代替 HOC(高阶组件) 5「Pro」「」

React 进阶提升 #17 错误边界和生命周期函数 componentDidCatch 9「Pro」「11:45」

React 进阶提升 #18 升级到 16.3「02:37」

React 进阶提升 #19 探索 bind (this) 的写法 9「03:50」

React 进阶提升 #20 React 16.3 全新的 Context API 1「06:50」

React 进阶提升 #21 React 16.3 全新的 Context API - 实践 3「Pro」「09:19」

React 进阶提升 #22 从 Redux 迁移到 React 16.3 的 Context API 之实践 1「Pro」「11:37」

React 进阶提升 #23 对象,数组,可变数据 9「Pro」「06:10」

React 进阶提升 #24 React.Children API 和 props.children 讲解 4「Pro」「06:06」

React 进阶提升 #25 如何使用 styled-components 5「Pro」「04:56」

React 进阶提升 #26 如何使用 styled-components(实践篇)「Pro」「07:29」

React 进阶提升 #27 你应该使用 redux-form(介绍) 12「Pro」「06:40」

React 进阶提升 #28 你应该使用 redux-form(实践篇) 7「Pro」「10:34」

学习资料

资料

doc.react-china.org/ 翻译后的官方文档,学技术必定要多看几遍文档

React小书 强烈推荐,由浅入深,按部就班

reactpatterns.com/ 因为react自己 API 比较简单,贴近原生。经过组件变化产生一系列模式

github.com/CompuIves/c… react在线编辑器,方便的分享你的react项目

image.png

devhints.io/react

image.png

js.coach 找js包的网站

image.png

视频

基础的免费,高级的收费 egghead.io

其余资料

React项目国际化(antd)多语言开发

使用 husky、commitlint 和 lint-staged 来构建你的前端工做流(vue、react、dva)

欢迎你们转发交流分享

微信交流群

微信号

image.png

微信交流群image.png

钉钉交流群

image.png

相关文章
相关标签/搜索