标签类型
JSX语法中,标签类型有2种,DOM类型的标签和React组件类型的标签。DOM类型的标签首字母必须小写,React组件类型的标签首字母必须大写,React正式通过首字母的大小写判断渲染的是一个DOM类型的标签还是React类型的标签
1 2 3 4 5 6 7 8 9 10
| const element = <h1> hello </h1>
const element = <HelloWorld/>
const element = ( <div> <HelloWorld /> </div> )
|
组件跟元素
React元素就是一个普通的js对象,这个对象通过DOM节点或React组件描述界面是什么样的,jsx语法就是用来创建React元素的
1 2
| const element = <h1>Hello, world</h1>;
|
React组件是个class或函数,接受一些属性作为输入,返回一个React元素,React组件是由若干React元素组建而成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Button extends React.Component { render() { return (<button>OK</button>) } }
const button = <Button />
class Page extends React.Component { render() { return ( <div> {button} </div> ) } } // 上面的写法等价于下面 class Page extends React.Component { render() { return ( <div> <Button /> </div> ) } }
|
状态提升 子组件如何传值给父组件
其实还是父组件改变自己的状态,只不过是父组件改变状态的方法传给子组件,子组件调用父组件的方法 ,改变父组件的状态,有点像Vue里的$emit('handleChange', value)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| export default class TemperatureInput extends React.Component { handleChange(e) { this.props.handleChange(e.target.value) } render() { const temp = this.props.temp return ( <fieldset> <input value={temp} onChange={e => this.handleChange(e)} /> </fieldset> ) } } // parent.js 通过props方式向子组件传入handleChange属性 export class Calcuator extends React.Component { constructor(props) { super(props) this.state = { temp: '', scale: 'c' } } handleCelsiusChange(temp) { this.setState({scale: 'c', temp}); } handleFahrenheitChange(temp) { this.setState({scale: 'f', temp}); } render() { const temp = this.state.temp const scale = this.state.scale const celsius = scale === 'f' ? Util.tryConvert(temp, Util.toCelsius) : temp; const fahrenheit = scale === 'c' ? Util.tryConvert(temp, Util.toFahrenheit) : temp; return ( <div> <TemperatureInput scale='c' temp={celsius} handleChange={(e) => this.handleCelsiusChange(e)}/> <TemperatureInput scale='f' temp={fahrenheit} handleChange={(e) => this.handleFahrenheitChange(e)}/> </div> ) } }
|
组合和继承
跟Vue里的slot有点像,占坑!!
组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| export function SplitPane(props) { return ( <div className='SplitPane'> <div className='SplitPane-left'> {props.left} </div> <div className='SplitPane-right'> {props.right} </div> </div> ) } export function ParentPanel() { return ( <SplitPane left={ <Contacts name='contact'/> } right={ <Chat name='chart'/> } /> ) } function Contacts(props) { return <div className="Contacts" >左边{props.name}</div>; } function Chat(props) { return <div className="Chat" >中间位置{props.name}</div>; }
|
yarn build之后查看打包之后的文件
执行yarn build之后,create react app会给一下提示
1 2 3 4
| yarn global add serve serve -s build
"server": "serve -s build"
|
想要浏览打包后的静态文件,即执行yarn server即可
code split
基于路由的代码分割,有一个前提得是
React.lazy接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 defalut export 的 React 组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React, { Suspense, lazy } from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </Suspense> </Router> );
|
全局状态 Context
国内下载github里的资源太难了
码云还是不错的react-devtools,这篇文章写得还行react-devtools安装以及使用中的问题
HOC高阶组件
实际上装饰器模式的应用!
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式,
具体而言,高阶组件是参数为组件,返回值为新组件的函数。
HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import React from 'react' export class MyComponent extends React.Component { render() { return ( <> <div>{this.props.data}</div> </> ) } } function WithPerData(WrappedComponent, key) { class WithSubscription extends React.Component { constructor(props) { super(props) this.state = { data: '' } } componentDidMount() { let data = localStorage.getItem(key) this.setState({data}) } render() { return ( <div style={{backgroundColor: 'red'}}> <WrappedComponent data={this.state.data} {...this.props}/> </div> ) } } WithSubscription.displayName = 'WithSubscription(Component)' return WithSubscription } export const MyComponentWithPerData = WithPerData(MyComponent, 'data1') export const MyComponentWithPerData2 = WithPerData(MyComponent, 'data2')
|
约定:最大化可组合性
1 2
| const ConnectedComment = connect(commentSelector, commentActions)(CommentList);
|
把它分开看
1 2 3 4
| const enhance = connect(commentListSelector, commentListActions);
const ConnectedComment = enhance(CommentList);
|
换句话说,connect 是一个返回高阶组件的高阶函数!这种形式可能看起来令人困惑或不必要,但它有一个有用的属性。 像 connect 函数返回的单参数 HOC 具有签名 Component => Component。 输出类型与输入类型相同的函数很容易组合在一起。
1 2 3 4 5 6 7 8 9 10
| const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent))
const enhance = compose( withRouter, connect(commentSelector) ) const EnhancedComponent = enhance(WrappedComponent)
|