-
组件
-
适用ant-design组件
-
起步安装
npm install antd --save 复制代码
-
试用button组件
import React, { Component } from 'react'; import Button from 'antd/lib/button' import 'antd/dist/antd.css' class App extends Component { render() { return ( <div className="App"> <p>hello world</p> <Button type="primary">Button</Button> </div> ); } } export default App; 复制代码
-
配置按需加载:上边组件如果使用就需要全部引用antd.css ,引用别的组件还得import
安装react-app-rewired取代react-script,可以对webpack的配置进行扩展,类似于vue.config.js,
npm install react-app-rewired@2.0.2-next.0 babel-plugin-import --save 复制代码
更改package.json ,使用react-app-rewired
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", "eject": "react-app-rewired eject" }, 复制代码
新增config-overrids.js文件
const {injectBabelPlugin} = require('react-app-rewired'); module.exports = function override(config,env) { config =injectBabelPlugin( ['import',{libraryName: 'antd', libraryDirectory: 'es', style: 'css'}], config ); return config }; 复制代码
-
参考官网使用
-
-
容器组件Vs展示组件
-
基本原则:容器组件负责数据的获取,展示组件负责根据props显示信息
class CommentList extends Component{ constructor(props){ super(props); this.state={ comments:[] } } render() { return <ul> {this.props.comments.map(value => { return <li>{value}</li> })} </ul> } componentDidMount() { // 模拟一下ajax数据获取 setTimeout(()=>{ this.setState({ comments:["哈哈","嘻嘻","嘿嘿"] }) },1000) } } 复制代码
-
进行分离,分离成容器组件和展示组件
class CommentList extends Component{ constructor(props){ super(props); this.state={ comments:[] } } render() { return <CommentDetail comments={this.state.comments}/> } componentDidMount() { // 模拟一下ajax数据获取 setTimeout(()=>{ this.setState({ comments:["哈哈","嘻嘻","嘿嘿"] }) },1000) } } // 将上边的组件进行拆分 显示组件和动作组件也就是获取数据的组件 class CommentDetail extends Component{ render() { return <ul> {this.props.comments.map(value => { return <li>{value}</li> })} </ul> } } 复制代码
-
将上边显示组件 写成function的组件形式
function CommentDetail({comments}) { return <ul> {comments.map(value => { return <li>{value}</li> })} </ul> } 复制代码
-
进行分离的优势:
- 能清楚的看到如何工作和如何展示
- 重用性高
- 更高的可用性
- 更易于测试
-
-
PureComponent
定制了shouldComponentUpdate后的Component(进行浅比较)
-
形式 :直接使用
class Comp extends PureComponent{ ....} 复制代码
-
原理:定制shouldComponentUpdate生命周期,如果props没变化,阻止渲染
shouldComponentUpdate(nextProps, nextState) { // 如果nextProps的内容等于this.porps,nextState的内容等于this.state return false } 复制代码
-
实现 : 如果不写shouldComponentUpdate这个生命周期的对比,则不管title变不变都重新渲染,但是使用了PureComponent或者加上下边生命周期 ,title不变就不会重新渲染,其PureComponent就是通过shouldComponentUpdate定制了一个组件
class TitleDisplay extends Component{ shouldComponentUpdate(nextProps, nextState, nextContext) { // 通过对比 if (nextProps.title ===this.props.title){ return false } } render() { console.log('title重新渲染了'); return <h2> {this.props.title} </h2> } } 复制代码
-
缺点 :只有class 形式的组件才能使用PureComponent这个功能
-
React16 新增一个新功能React.memo 来完美实现React组件,让函数式的组件也有PureComponent的功能
const TitleDisplay =React.memo(props=>{ // 函数式组件的 return <h2>{props.title}</h2> }) 复制代码
-
-
HOC高阶组件:高阶组件一般都是以with开头进行命名
提高组件的复用率,首先想到就是抽离相同的逻辑,在React里就有了Hoc的概念,高阶组件也是一个组件,但是它返回另外一个组件。产生的新的组件可以对属性进行包装也可以重写部分生命周期
-
简单的高阶组件实现:高阶组件增加属性
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; ReactDOM.render( <App age="18" />, //这个age =18的属性后边的{...props}就可以接收到 document.getElementById('root') ); 复制代码
const withKaikeba = (Component)=>{ const newComp = props=>{ // 这个newComp是一个函数式的组件 console.log(props); return <Component {...props} name="开课吧高阶组件测试" /> // 将全部的props展开添加进属性中 比如在index.js中使用了App这个组件那么他的props就会传递进来 }; return newComp }; class App extends Component{ render() { return <div> <h1>XXX</h1> <p>{this.props.name}</p> </div> } } App = withKaikeba(App); //使用这个组件 export default App; 复制代码
-
简单的高阶组件实现:定制组件的生命周期
const withLog = Component=>{ class NewComponent extends Component{ componentWillMount() { console.time('ComponentRender'); console.log(`组件${Component.name}准备完毕了`) } render() { return <Component {...this.props} /> //这也是展开props属性 } componentDidMount() { console.timeEnd('ComponentRender') console.log(`组件${Component.name}渲染完毕了`) } } return NewComponent; }; App = withKaikeba(withLog(App)); // 高阶组价支持链式调用 复制代码
-
-
高阶组件的装饰器写法
上边的链式操作比较繁琐 ,逻辑比较绕,ES7中有一个优秀的语法——装饰器,专门处理这种问题
-
安装babel插件,用于解析Babel 5中的旧装饰器行为
npm install --save-dev babel-plugin-transform-decorators-legacy 复制代码
-
在config-overrides.js进行扩展,才能编译
const {injectBabelPlugin} = require('react-app-rewired'); module.exports = function override(config,env) { config =injectBabelPlugin( // 这是为能够按需加载的扩展 ['import',{libraryName: 'antd', libraryDirectory: 'es', style: 'css'}], config ); config=injectBabelPlugin( // 这是为支持装饰写法的扩展 ['@babel/plugin-proposal-decorators',{legacy:true}], config ); return config }; 复制代码
-
之后直接使用
@withKaikeba @withLog class App extends Component{ render() { return <div> <h1>XXX</h1> <p>{this.props.name}</p> </div> } } // App = withKaikeba(withLog(App)); export default App; 复制代码
-
-
组件间通信
-
父组件向子组件通信
class Child extends Component{ // class 组件 render() { return <div> {this.props.name} </div> } } function Child(props) { // 函数式组件 return <div> {props.name} </div> } class App extends Component{ render() { return <div> <Child name={"向子组件传递值"}/> </div> } } 复制代码
-
子组件向父组件通信 ——利用回调函数
这块有必要说一下 ,在 cb={this.callback.bind(this)} ,由于cb是子组件传递过来的,msg当然应该绑定cb函数本身,不能绑定App组件的this也就是用箭头函数不能获取到传递的值
彻底理解可以参考
function Child(props) { // 子组件 return <div> <div> {props.name} </div> <Button type={"primary"} onClick={()=>props.cb('子组件传递的值')}>点击我传递</Button> </div> } class App extends Component{ //父组件 callback (msg) { console.log(msg); } render() { return <div> <Child name={"向子组件传递值"} cb={this.callback.bind(this)} /> </div> } } 复制代码
-
跨级组件之间通信:使用 context 对象上下文(Context) 提供了一种通过组件树传递数据的方法,无需在每个级别手动传递 props 属性
React.createContext: const {Provider, Consumer} = React.createContext(defaultValue);
Provider :提供者<Provider value={/* some value */}>
Consumer:订阅者 < Consumer> {value => {…}} < /Consumer>
vuejs中可以使用privide&inject模式来进行组件之间的通信,这个模式的来源–context
react中也有两个角色,Provider和Consumer,Provider在外层组件,内部需要数据,就使用Consumer来读取
import {store} from './store' const AppContext = React.createContext(); //新建上下文 ,Provider和Consumer可以拆分出去 const {Provider,Consumer} =AppContext; class Demo extends Component{ render() { // 下边使用Consumer来进行接收 return <Consumer> { // 下边这块其实就是一个函数式的组件 store=>{ return <Button type={"primary"} onClick={()=>store.sayHi()} > {store.name} </Button> } } </Consumer> } } class App extends Component{ render() { // 直接进行嵌套 ,value是固定写法 return <Provider value={store}> <div> <h1>哈喽啊!小年依旧学习React</h1> <Demo/> </div> </Provider> } } 复制代码
-
-
其他多数据的组件通信就可以使用Redux进行
-
实现createContext ? createContext就是一个高阶组件,结合用法去理解
// createContext初始化 简单的实现原理 import React ,{Component} from 'react' function createContext() { let instance = { // 1. 先声明一个对象初始化value value:null }; class Provider extends Component{ constructor(props){ super(props); instance.vlaue = props.value // 2. 赋给instance的value } render() { return this.props.children // 3.这块类似于vuejs的<slot></slot> 扩展 } } class Consumer extends Component{ constructor(props){ super(props); this.state={ value:instance.vlaue } } render() { return this.props.children(this.state.value) // 4.这块就是一个函数,将instance.value 传递进去 } } return {Provider,Consumer} } 复制代码
-
-
React未来
- Fiber:异步渲染 ,底层在实现虚拟Dom中实现了类似CPU调度的功能,
- Suspense :用同步的代码实现异步的操作
- Hooks:开发者可以完全抛弃class ,拥抱函数式编程
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
喜欢就支持一下吧