React 技术分享
1. react 介绍 和 想法
- A declarative, efficient, and flexible JavaScript library for building user interfaces
- react是一个专注于UI的库
- 状态机 输入 => 组件 => 输出
- 组件化 react组件映射 为原生android组件 原生ios 组件 react vr
2. jsx 语法
JSX就是Javascript和XML结合的一种格式,要使用需要在babelrc里配置, Babel 转译器会把 JSX 转换成一个名为 React.createElement() 的方法调用
// 最外层必须有一个元素包裹 <div>hello world!</div> // 属性都要驼峰 <div className="haha" ></div> // 样式 <div style={{ marginTop: '10px'}}></div> // 变量 <div> {user.name }</div> // 事件 <button onClick={}> click me </button> // 遍历 <ul> { list.map( ({name, value}) => <li key={name} value={value}>{name}</li> ) } </ul> // 展开对象 const props = {name: "tom" } <div {...props}></div>
3 虚拟DOM 和 diff 算法
a. 虚拟DOM(Virtual DOM)机制:对于每一个组件,React会在内存中构建一个相对应的DOM树,基于React开发时所有的DOM构造都是通过虚拟DOM进行,每当组件的状态发生变化时,React都会重新构建整个DOM数据,然后将当前的整个DOM树和上一次的DOM树进行对比,得出DOM结构变化的部分(Patchs),然后将这些Patchs 再更新到真实DOM中
b. 两个不同类型的元素会产生不同的树(根元素不同结构树一定不同)
c. 开发者可以通过key属性指定不同树中没有发生改变的子元素
d. diff算法的核心就是同级比较
4. state 和 props
- react 数据是单向流动的 数据自顶向下流动 ,
- props是传递来的参数,从父级组件向子组件传递的数据, 是只读的
- state是自己的内部状态
- 在react更新生命周期setState会导致死循环
// 默认参数 Main.defaultProps = { list: [ ] } // setState 是异步的 this.setState( (prevState,props) => ({ text: prev.text + "-ha" }))
5. 生命周期
Note: 父子组件的挂载顺序,更新顺序
6 函数式编程
- 函数式编程,函数作为一等公民,而不是类和对象
- 纯函数 一个函数的返回结果只依赖于它的参数,并且在执行过程里面没有副作用,我们就把这个函数叫做纯函数
- 高阶函数 函数的参数可以是函数,返回值也可以是函数 , 如数组的
forEach map filter some reduce ,setTimeout setInterval等
- 柯里化是指这样一个函数(假设叫做createCurry),他接收函数A作为参数,运行后能够返回一个新的函数。并且这个新的函数能够处理函数A的剩余参数
6 无状态组件
顾名思义,它没有state,接收props,它是一种只负责展示的纯组件 使用函数式的方式声明,会使得代码的可读性更好,并能大大减少代码量,多写无状态组件
function HelloComponent(props) { return <div>Hello {props.name}</div> } ReactDOM.render(<HelloComponent name="marlon" />, mountNode)
7 使用 fragment 碎片
Jsx 要求返回的元素,必须有东西包裹,使用碎片可以减少一层DOM
<React.Fragment> <td>Hello</td> <td>World</td> </React.Fragment> // 可以简写为 <> <td>Hello</td> <td>World</td> </>
8 使用context
Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性,React.createContext 创建一个context,一个context包含provider(数据提供者), comsumer(数据消费者), Provider 有一个value参数,用来覆盖创建时的默认值
// 创建一个 theme Context, 默认 theme 的值为 light const ThemeContext = React.createContext ('light'); function ThemedButton(props) { // ThemedButton 组件从 context 接收 theme return ( <ThemeContext.Consumer> {theme => <Button {...props} theme={theme} />} </ThemeContext.Consumer> ); } // 中间组件 function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } class App extends React.Component { render() { return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } }
9. mobx
推荐一个react模块的目录结构
/states 状态 /(widgets|components)各类组件、弹窗等 index.js index.less service 建议放在config里,全局统一 Computed用于生成计算数据,autorun类似用来监听某一数据改变而作出反映 import {action, observable ,autorun} from 'mobx'; import StateAdd from "./add" class State { constructor(){ this.$add = new StateAdd(this); } // 显示 @observable show = false; // query @observable query = { trade: null, type: 1 } /** * @name 获取列表 */ @action getList = () => { } } const instance = new State(); autorun(() => { if(instance.show){ console.log("打开弹窗了") } }) export default instance
10 高阶组件
主要有两种实现方式属性proxy、继承实现
通常使用装饰器模式调用,常见的有mobx的 @observer, react-router的withRouter等
------ 属性proxy 实现如下 import React from "react" export default class extends React.Component { render = () => <div> <div> <Hoc1_text name={'Hoc1_text'}/> </div> <div> <Hoc2_text /> </div> </div> } class Text1 extends React.Component { render = () => <div> this is simple text <br/> {this.props.name} </div> } /** * @name 操作props */ function Hoc1(Component){ return class extends React.Component { render = () => <div className='hoc1'> <Component {...Object.assign({} , this.props, {gen : '111'})}/> </div> } } const Hoc1_text = Hoc1(Text1); class Text2 extends React.Component { render = () => <div> <input type="text" value={this.props.value} onChange={this.props.change}/> </div> } /** *@name 抽离state */ function Hoc2(Component){ return class extends React.Component { state = { value: '11' } change = e => { this.setState({ value: e.target.value }) } render = () => <div> <Component value={this.state.value} change={this.change}/> </div> } } const Hoc2_text = Hoc2(Text2); function Hoc3(Component){ return class extends React.Component { getRef = _ref => { this.refs = _ref; } render = () => <div> <Component ref={this.getRef}/> </div> } } ------ 继承组件 实现如下 ------- class Text extends React.Component { render(){ return ( <div> this is text </div> ) } } // 生命周期可以覆盖掉 const hoc2 = Component => { return class NewHoc extends Component { static displayName = `NewHoc-${Component.displayName || Component.name}` render(){ const element= super.render(); console.log(element) const style = { color: element.type === 'div' ? 'red' : 'green' } const newProps = {...this.props, style} return React.cloneElement(element, newProps, element.props.children) } } } const NewCom = hoc2(Text)
11 注意事项
- React组件名字大写
- 不能直接修改state props
- 更新函数里不能做状态操作(死循环)
- 多用无状态组件
- 多用pureComponent React.PureComponent 通过prop和state的浅对比来实现 shouldComponentUpate()。
- 代码清晰,一目了然,注释写好
- 过度封装不如不封装
- 使用 ImmutableJS 处理数据提升性能