赞
踩
React
的基本知识点,如下所示:JSX
基本使用,变量、表达式、class style
、子元素和组件等等,代码如下所示:import React from 'react' import './style.css' import List from '../List' class JSXBaseDemo extends React.Component { constructor(props) { super(props) this.state = { name: '张三', imgUrl: 'https://img1.mukewang.com/5a9fc8070001a82402060220-140-140.jpg', flag: true } } render() { // // 获取变量 插值 // const pElem = <p>{this.state.name}</p> // return pElem // // 表达式 // const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p> // return exprElem // // 子元素 // const imgElem = <div> // <p>我的头像</p> // <img src="xxxx.png"/> // <img src={this.state.imgUrl}/> // </div> // return imgElem // // class // const classElem = <p className="title">设置 css class</p> // return classElem // // style // const styleData = { fontSize: '30px', color: 'blue' } // const styleElem = <p style={styleData}>设置 style</p> // // 内联写法,注意 {{ 和 }} // // const styleElem = <p style={{ fontSize: '30px', color: 'blue' }}>设置 style</p> // return styleElem // 原生 html const rawHtml = '<span>富文本内容<i>斜体</i><b>加粗</b></span>' const rawHtmlData = { __html: rawHtml // 注意,必须是这种格式 } const rawHtmlElem = <div> <p dangerouslySetInnerHTML={rawHtmlData}></p> <p>{rawHtml}</p> </div> return rawHtmlElem // // 加载组件 // const componentElem = <div> // <p>JSX 中加载一个组件</p> // <hr/> // <List/> // </div> // return componentElem } } export default JSXBaseDemo
if else
、三元表达式、逻辑运算符 && ||
,代码如下所示:import React from 'react' import './style.css' class ConditionDemo extends React.Component { constructor(props) { super(props) this.state = { theme: 'black' } } render() { const blackBtn = <button className="btn-black">black btn</button> const whiteBtn = <button className="btn-white">white btn</button> // // if else // if (this.state.theme === 'black') { // return blackBtn // } else { // return whiteBtn // } // // 三元运算符 // return <div> // { this.state.theme === 'black' ? blackBtn : whiteBtn } // </div> // && return <div> { this.state.theme === 'black' && blackBtn } </div> } } export default ConditionDemo
map
和 key
,代码如下所示:import React from 'react' class ListDemo extends React.Component { constructor(props) { super(props) this.state = { list: [ { id: 'id-1', title: '标题1' }, { id: 'id-2', title: '标题2' }, { id: 'id-3', title: '标题3' } ] } } render() { return <ul> { /* vue v-for */ this.state.list.map( (item, index) => { // 这里的 key 和 Vue 的 key 类似,必填,不能是 index 或 random return <li key={item.id}> index {index}; id {item.id}; title {item.title} </li> } ) } </ul> } } export default ListDemo
bind this
,关于 event
参数,传递自定义参数,代码如下所示:import React from 'react' class EventDemo extends React.Component { constructor(props) { super(props) this.state = { name: 'zhangsan', list: [ { id: 'id-1', title: '标题1' }, { id: 'id-2', title: '标题2' }, { id: 'id-3', title: '标题3' } ] } // 修改方法的 this 指向 this.clickHandler1 = this.clickHandler1.bind(this) } render() { // // this - 使用 bind // return <p onClick={this.clickHandler1}> // {this.state.name} // </p> // // this - 使用静态方法 // return <p onClick={this.clickHandler2}> // clickHandler2 {this.state.name} // </p> // // event // return <a href="https://imooc.com/" onClick={this.clickHandler3}> // click me // </a> // 传递参数 - 用 bind(this, a, b) return <ul>{this.state.list.map((item, index) => { return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}> index {index}; title {item.title} </li> })}</ul> } clickHandler1() { // console.log('this....', this) // this 默认是 undefined this.setState({ name: 'lisi' }) } // 静态方法,this 指向当前实例 clickHandler2 = () => { this.setState({ name: 'lisi' }) } // 获取 event clickHandler3 = (event) => { event.preventDefault() // 阻止默认行为 event.stopPropagation() // 阻止冒泡 console.log('target', event.target) // 指向当前元素,即当前元素触发 console.log('current target', event.currentTarget) // 指向当前元素,假象!!! // 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是 SyntheticEvent 组合事件 console.log('event', event) // 不是原生的 Event ,原生的 MouseEvent console.log('event.__proto__.constructor', event.__proto__.constructor) // 原生 event 如下。其 __proto__.constructor 是 MouseEvent console.log('nativeEvent', event.nativeEvent) console.log('nativeEvent target', event.nativeEvent.target) // 指向当前元素,即当前元素触发 console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!! // 1. event 是 SyntheticEvent ,模拟出来 DOM 事件所有能力 // 2. event.nativeEvent 是原生事件对象 // 3. 所有的事件,都被挂载到 document 上 // 4. 和 DOM 事件不一样,和 Vue 事件也不一样 } // 传递参数 clickHandler4(id, title, event) { console.log(id, title) console.log('event', event) // 最后追加一个参数,即可接收 event } } export default EventDemo
input、textarea、 select
用 value
,checkbox、 radio
用 checked
,代码如下所示:import React from 'react' class FormDemo extends React.Component { constructor(props) { super(props) this.state = { name: '张三', info: '个人信息', city: 'shanghai', flag: true, gender: 'male' } } render() { // // 受控组件(非受控组件,后面再讲) // return <div> // <p>{this.state.name}</p> // <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for */} // <input id="inputName" value={this.state.name} onChange={this.onInputChange}/> // </div> // textarea - 使用 value return <div> <textarea value={this.state.info} onChange={this.onTextareaChange}/> <p>{this.state.info}</p> </div> // // select - 使用 value // return <div> // <select value={this.state.city} onChange={this.onSelectChange}> // <option value="beijing">北京</option> // <option value="shanghai">上海</option> // <option value="shenzhen">深圳</option> // </select> // <p>{this.state.city}</p> // </div> // // checkbox // return <div> // <input type="checkbox" checked={this.state.flag} onChange={this.onCheckboxChange}/> // <p>{this.state.flag.toString()}</p> // </div> // // radio // return <div> // male <input type="radio" name="gender" value="male" checked={this.state.gender === 'male'} onChange={this.onRadioChange}/> // female <input type="radio" name="gender" value="female" checked={this.state.gender === 'female'} onChange={this.onRadioChange}/> // <p>{this.state.gender}</p> // </div> // 非受控组件 } onInputChange = (e) => { this.setState({ name: e.target.value }) } onTextareaChange = (e) => { this.setState({ info: e.target.value }) } onSelectChange = (e) => { this.setState({ city: e.target.value }) } onCheckboxChange = () => { this.setState({ flag: !this.state.flag }) } onRadioChange = (e) => { this.setState({ gender: e.target.value }) } } export default FormDemo
props
传递数据,props
传递函数,props
类型检查,代码如下所示:import React from 'react' import PropTypes from 'prop-types' class Input extends React.Component { constructor(props) { super(props) this.state = { title: '' } } render() { return <div> <input value={this.state.title} onChange={this.onTitleChange}/> <button onClick={this.onSubmit}>提交</button> </div> } onTitleChange = (e) => { this.setState({ title: e.target.value }) } onSubmit = () => { const { submitTitle } = this.props submitTitle(this.state.title) // 'abc' this.setState({ title: '' }) } } // props 类型检查 Input.propTypes = { submitTitle: PropTypes.func.isRequired } class List extends React.Component { constructor(props) { super(props) } render() { const { list } = this.props return <ul>{list.map((item, index) => { return <li key={item.id}> <span>{item.title}</span> </li> })}</ul> } } // props 类型检查 List.propTypes = { list: PropTypes.arrayOf(PropTypes.object).isRequired } class Footer extends React.Component { constructor(props) { super(props) } render() { return <p> {this.props.text} {this.props.length} </p> } componentDidUpdate() { console.log('footer did update') } shouldComponentUpdate(nextProps, nextState) { if (nextProps.text !== this.props.text || nextProps.length !== this.props.length) { return true // 可以渲染 } return false // 不重复渲染 } // React 默认:父组件有更新,子组件则无条件也更新!!! // 性能优化对于 React 更加重要! // SCU 一定要每次都用吗?—— 需要的时候才优化 } class TodoListDemo extends React.Component { constructor(props) { super(props) // 状态(数据)提升 this.state = { list: [ { id: 'id-1', title: '标题1' }, { id: 'id-2', title: '标题2' }, { id: 'id-3', title: '标题3' } ], footerInfo: '底部文字' } } render() { return <div> <Input submitTitle={this.onSubmitTitle}/> <List list={this.state.list}/> <Footer text={this.state.footerInfo} length={this.state.list.length}/> </div> } onSubmitTitle = (title) => { this.setState({ list: this.state.list.concat({ id: `id-${Date.now()}`, title }) }) } } export default TodoListDemo
setState
是不可变值,可能是异步更新,可能会被合并,代码如下所示:import React from 'react' // 函数组件(后面会讲),默认没有 state class StateDemo extends React.Component { constructor(props) { super(props) // 第一,state 要在构造函数中定义 this.state = { count: 0 } } render() { return <div> <p>{this.state.count}</p> <button onClick={this.increase}>累加</button> </div> } increase = () => { // // 第二,不要直接修改 state ,使用不可变值 ---------------------------- // // this.state.count++ // 错误 // this.setState({ // count: this.state.count + 1 // SCU // }) // 操作数组、对象的的常用形式 // 第三,setState 可能是异步更新(有可能是同步更新) ---------------------------- // this.setState({ // count: this.state.count + 1 // }, () => { // // 联想 Vue $nextTick - DOM // console.log('count by callback', this.state.count) // 回调函数中可以拿到最新的 state // }) // console.log('count', this.state.count) // 异步的,拿不到最新值 // // setTimeout 中 setState 是同步的 // setTimeout(() => { // this.setState({ // count: this.state.count + 1 // }) // console.log('count in setTimeout', this.state.count) // }, 0) // 自己定义的 DOM 事件,setState 是同步的。再 componentDidMount 中 // 第四,state 异步更新的话,更新前会被合并 ---------------------------- // // 传入对象,会被合并(类似 Object.assign )。执行结果只一次 +1 // this.setState({ // count: this.state.count + 1 // }) // this.setState({ // count: this.state.count + 1 // }) // this.setState({ // count: this.state.count + 1 // }) // 传入函数,不会被合并。执行结果是 +3 this.setState((prevState, props) => { return { count: prevState.count + 1 } }) this.setState((prevState, props) => { return { count: prevState.count + 1 } }) this.setState((prevState, props) => { return { count: prevState.count + 1 } }) } // bodyClickHandler = () => { // this.setState({ // count: this.state.count + 1 // }) // console.log('count in body event', this.state.count) // } // componentDidMount() { // // 自己定义的 DOM 事件,setState 是同步的 // document.body.addEventListener('click', this.bodyClickHandler) // } // componentWillUnmount() { // // 及时销毁自定义 DOM 事件 // document.body.removeEventListener('click', this.bodyClickHandler) // // clearTimeout // } } export default StateDemo // // 不可变值(函数式编程,纯函数) - 数组 // const list5Copy = this.state.list5.slice() // list5Copy.splice(2, 0, 'a') // 中间插入/删除 // this.setState({ // list1: this.state.list1.concat(100), // 追加 // list2: [...this.state.list2, 100], // 追加 // list3: this.state.list3.slice(0, 3), // 截取 // list4: this.state.list4.filter(item => item > 100), // 筛选 // list5: list5Copy // 其他操作 // }) // // 注意,不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值 // // 不可变值 - 对象 // this.setState({ // obj1: Object.assign({}, this.state.obj1, {a: 100}), // obj2: {...this.state.obj2, a: 100} // }) // // 注意,不能直接对 this.state.obj 进行属性设置,这样违反不可变值
setState
有的是同步的,有的是异步的,代码如下所示:import React from 'react' class ListDemo extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } render() { return <p>{this.state.count}</p> } componentDidMount() { // count 初始值为 0 this.setState({ count: this.state.count + 1 }) console.log('1', this.state.count) // 0 this.setState({ count: this.state.count + 1 }) console.log('2', this.state.count) // 0 setTimeout(() => { this.setState({ count: this.state.count + 1 }) console.log('3', this.state.count) // 2 }) setTimeout(() => { this.setState({ count: this.state.count + 1 }) console.log('4', this.state.count) // 3 }) } } export default ListDemo
React
的组件生命周期,单组件生命周期,父子组件生命周期,和 Vue
的一样。
对于 React
的高级特性,分为函数组件、非受控组件、Portals
、context
、异步组件、性能优化、高阶组件 HOC
和 Render Props
,如下所示:
props
,输出 JSX
。没有实例,没有生命周期,没有 state
,不能扩展其它办法ref
、defaultValue
和 defaultChecked
,手动操作 DOM
元素。对于非受控组件的使用场景,必须手动操作 DOM
元素,setState
实现不了。文件上传 <input type=file>
, 某些富文本编辑器,需要传入 DOM
元素。对于受控组件和非受控组件的比较,优先使用受控组件,符合 React
的设计原则。必须操作 DOM
时,再使用非受控组件,代码如下所示:import React from 'react' class App extends React.Component { constructor(props) { super(props) this.state = { name: '张三', flag: true, } this.nameInputRef = React.createRef() // 创建 ref this.fileInputRef = React.createRef() } render() { // // input defaultValue // return <div> // {/* 使用 defaultValue 而不是 value ,使用 ref */} // <input defaultValue={this.state.name} ref={this.nameInputRef}/> // {/* state 并不会随着改变 */} // <span>state.name: {this.state.name}</span> // <br/> // <button onClick={this.alertName}>alert name</button> // </div> // // checkbox defaultChecked // return <div> // <input // type="checkbox" // defaultChecked={this.state.flag} // /> // </div> // file return <div> <input type="file" ref={this.fileInputRef}/> <button onClick={this.alertFile}>alert file</button> </div> } alertName = () => { const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点 alert(elem.value) // 不是 this.state.name } alertFile = () => { const elem = this.fileInputRef.current // 通过 ref 获取 DOM 节点 alert(elem.files[0].name) } } export default App
React Portals
,组件默认会按照既定层次嵌套渲染,让组件渲染到父组件之外。对于 Portals
的使用场景,overflow: hidden
,父组件 z-index
值太小,flexd
需要放在 body
第一层级,代码如下所示:import React from 'react' import ReactDOM from 'react-dom' import './style.css' class App extends React.Component { constructor(props) { super(props) this.state = { } } render() { // // 正常渲染 // return <div className="modal"> // {this.props.children} {/* vue slot */} // </div> // 使用 Portals 渲染到 body 上。 // fixed 元素要放在 body 上,有更好的浏览器兼容性。 return ReactDOM.createPortal( <div className="modal">{this.props.children}</div>, document.body // DOM 节点 ) } } export default App
context
,上下文环境,公共信息传递给每个组件,用 props
太繁琐,用 redux
小题大做,代码如下所示:import React from 'react' // 创建 Context 填入默认值(任何一个 js 变量) const ThemeContext = React.createContext('light') // 底层组件 - 函数是组件 function ThemeLink (props) { // const theme = this.context // 会报错。函数式组件没有实例,即没有 this // 函数式组件可以使用 Consumer return <ThemeContext.Consumer> { value => <p>link's theme is {value}</p> } </ThemeContext.Consumer> } // 底层组件 - class 组件 class ThemedButton extends React.Component { // 指定 contextType 读取当前的 theme context。 // static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext render() { const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。 return <div> <p>button's theme is {theme}</p> </div> } } ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。 // 中间的组件再也不必指明往下传递 theme 了。 function Toolbar(props) { return ( <div> <ThemedButton /> <ThemeLink /> </div> ) } class App extends React.Component { constructor(props) { super(props) this.state = { theme: 'light' } } render() { return <ThemeContext.Provider value={this.state.theme}> <Toolbar /> <hr/> <button onClick={this.changeTheme}>change theme</button> </ThemeContext.Provider> } changeTheme = () => { this.setState({ theme: this.state.theme === 'light' ? 'dark' : 'light' }) } } export default App
import()、React.lazy、React.Suspense
,代码如下所示:import React from 'react' const ContextDemo = React.lazy(() => import('./ContextDemo')) class App extends React.Component { constructor(props) { super(props) } render() { return <div> <p>引入一个动态组件</p> <hr /> <React.Suspense fallback={<div>Loading...</div>}> <ContextDemo/> </React.Suspense> </div> // 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速) // 2. 看 network 的 js 加载 } } export default App
React
的性能优化,shouldComponentUpdate
,简称 SCU
,PureComponent
和 React.memo
,不可变值 immutable.js
,如下所示:SCU
,SCU
默认返回 true
,即 React
默认重新渲染所有子组件,必须配合 不可变值 一起使用。可先不用 SCU
,有性能问题时再考虑使用,代码如下所示:import React from 'react' import PropTypes from 'prop-types' import _ from 'lodash' class Input extends React.Component { constructor(props) { super(props) this.state = { title: '' } } render() { return <div> <input value={this.state.title} onChange={this.onTitleChange}/> <button onClick={this.onSubmit}>提交</button> </div> } onTitleChange = (e) => { this.setState({ title: e.target.value }) } onSubmit = () => { const { submitTitle } = this.props submitTitle(this.state.title) this.setState({ title: '' }) } } // props 类型检查 Input.propTypes = { submitTitle: PropTypes.func.isRequired } class List extends React.Component { constructor(props) { super(props) } render() { const { list } = this.props return <ul>{list.map((item, index) => { return <li key={item.id}> <span>{item.title}</span> </li> })}</ul> } // 增加 shouldComponentUpdate shouldComponentUpdate(nextProps, nextState) { // _.isEqual 做对象或者数组的深度比较(一次性递归到底) if (_.isEqual(nextProps.list, this.props.list)) { // 相等,则不重复渲染 return false } return true // 不相等,则渲染 } } // props 类型检查 List.propTypes = { list: PropTypes.arrayOf(PropTypes.object).isRequired } class TodoListDemo extends React.Component { constructor(props) { super(props) this.state = { list: [ { id: 'id-1', title: '标题1' }, { id: 'id-2', title: '标题2' }, { id: 'id-3', title: '标题3' } ] } } render() { return <div> <Input submitTitle={this.onSubmitTitle}/> <List list={this.state.list}/> </div> } onSubmitTitle = (title) => { // 正确的用法 this.setState({ list: this.state.list.concat({ id: `id-${Date.now()}`, title }) }) // // 为了演示 SCU ,故意写的错误用法 // this.state.list.push({ // id: `id-${Date.now()}`, // title // }) // this.setState({ // list: this.state.list // }) } } export default TodoListDemo
PureComponent
和 memo
,PureComponent
,SCU
中实现了浅比较。memo
,函数组件中的 PureComponent
,浅比较已使用大部分情况,尽量不要做深度比较
immutable.js
,彻底拥抱不可变质,基于共享数据,不是深拷贝,速度好,按需使用
mixin
,已被 React
弃用,高阶组件 HOC,Render Props
。HOC
是模式简单,但会增加组件层级,Render Props
是代码简洁,学习成本较高,按需使用,代码如下所示:import React from 'react' // 高阶组件 const withMouse = (Component) => { class withMouseComponent extends React.Component { constructor(props) { super(props) this.state = { x: 0, y: 0 } } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }) } render() { return ( <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}> {/* 1. 透传所有 props 2. 增加 mouse 属性 */} <Component {...this.props} mouse={this.state}/> </div> ) } } return withMouseComponent } const App = (props) => { const a = props.a const { x, y } = props.mouse // 接收 mouse 属性 return ( <div style={{ height: '500px' }}> <h1>The mouse position is ({x}, {y})</h1> <p>{a}</p> </div> ) } export default withMouse(App) // 返回高阶函数
import React from 'react' import PropTypes from 'prop-types' class Mouse extends React.Component { constructor(props) { super(props) this.state = { x: 0, y: 0 } } handleMouseMove = (event) => { this.setState({ x: event.clientX, y: event.clientY }) } render() { return ( <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}> {/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */} {this.props.render(this.state)} </div> ) } } Mouse.propTypes = { render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数 } const App = (props) => ( <div style={{ height: '500px' }}> <p>{props.a}</p> <Mouse render={ /* render 是一个函数组件 */ ({ x, y }) => <h1>The mouse position is ({x}, {y})</h1> }/> </div> ) /** * 即,定义了 Mouse 组件,只有获取 x y 的能力。 * 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。 */ export default App
Redux
的使用,分为基本概念、单项数据流、react-redux
、异步 action
和中间件,如下所示:redux
的单向数据流,store、state、action 和 reducer
。应用中所有的 state
都以一个对象树的形式储存在一个单一的 store
中。 惟一改变 state
的办法是触发 action
,一个描述发生什么的对象。 为了描述 action
如何改变 state
树,你需要编写 reducers
。这是一个 reducer
,形式为(state, action) => state
的纯函数,描述了 action
如何把 state
转变成下一个 state
。state
的形式取决于你,可以是基本类型、数组、对象、甚至是 Immutable.js
生成的数据结构。惟一的要点是当 state
变化时需要返回全新的对象,而不是修改传入的参数。改变内部 state
惟一方法是 dispatch
一个 action
。action
可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行。对于单向数据流,dispatch(action)、reducer -> newState、subscribe
触发通知react-redux,<Provider> content、connect、mapStateToProps 和 mapDispatchToProps
action
,有 redux-thunk、redux-promise 和 redux-saga
redux
中间件,在单向数据流的时候,button
进行 callback
指向 dispatch
,dispatch
通过 action
指向 reducer
, reducer
通过 state
指向 view
。在有中间件以后,button
通过 callback
指向 new dispatch
。在 new dispatch
中,mid1 -> mid2 -> action -> dispatch
。最后 new dispatch
通过 action
指向 reducer
。redux
的使用,代码如下所示:action
文件夹
let nextTodoId = 0 // 创建一个 todo export const addTodo = text => { return { type: 'ADD_TODO', id: nextTodoId++, text } } // 设置完成状态 export const setVisibilityFilter = filter => { return { type: 'SET_VISIBILITY_FILTER', filter } } // 切换 todo 完成状态 export const toggleTodo = id => { return { type: 'TOGGLE_TODO', id } }
components
文件夹
App.js
import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'
const App = () => (
<div>
<AddTodo />
<VisibleTodoList />
<Footer />
</div>
)
export default App
Footer.js
import React from 'react' import FilterLink from '../containers/FilterLink' const Footer = () => ( <p> Show: {' '} <FilterLink filter="SHOW_ALL"> All </FilterLink> {', '} <FilterLink filter="SHOW_ACTIVE"> Active </FilterLink> {', '} <FilterLink filter="SHOW_COMPLETED"> Completed </FilterLink> </p> ) export default Footer ```
Link.js
import React from 'react' import PropTypes from 'prop-types' const Link = ({ active, children, onClick }) => { if (active) { return <span>{children}</span> } return ( <a href="" onClick={e => { e.preventDefault() onClick() // 设置过滤状态 }} > {children} </a> ) } Link.propTypes = { active: PropTypes.bool.isRequired, children: PropTypes.node.isRequired, onClick: PropTypes.func.isRequired } export default Link
Todo.js
import React from 'react' import PropTypes from 'prop-types' // 函数组件 { onClick, completed, text } = props const Todo = ({ onClick, completed, text }) => ( <li onClick={onClick} style={ { textDecoration: completed ? 'line-through' : 'none' }} > {text} </li> ) Todo.propTypes = { onClick: PropTypes.func.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired } export default Todo
TodoList.js
import React from 'react' import PropTypes from 'prop-types' import Todo from './Todo' // 函数组件 { todos, onTodoClick } = props const TodoList = ({ todos, onTodoClick }) => ( <ul> {todos.map(todo => ( <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} /> // 点击切换完成状态 ))} </ul> ) TodoList.propTypes = { todos: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number.isRequired, completed: PropTypes.bool.isRequired, text: PropTypes.string.isRequired }).isRequired ).isRequired, onTodoClick: PropTypes.func.isRequired } export default TodoList
containers
文件夹
import React from 'react' import { connect } from 'react-redux' import { addTodo } from '../actions' // 函数组件,接收 props 参数 let AddTodo = ({ dispatch }) => { // dispatch 即 props.dispatch let input return ( <div> <form onSubmit={e => { e.preventDefault() if (!input.value.trim()) { return } // 创建一个 todo dispatch(addTodo(input.value)) input.value = '' }} > <input ref={node => { input = node }} /> <button type="submit"> Add Todo </button> </form> </div> ) } // connect 高阶组件 ,将 dispatch 作为 props 注入到 AddTodo 组件中 AddTodo = connect()(AddTodo) export default AddTodo
import { connect } from 'react-redux' import { setVisibilityFilter } from '../actions' import Link from '../components/Link' const mapStateToProps = (state, ownProps) => { // ownProps 即父组件传来的 `filter="SHOW_ALL"` return { active: ownProps.filter === state.visibilityFilter } } const mapDispatchToProps = (dispatch, ownProps) => { // ownProps 即父组件传来的 `filter="SHOW_ALL"` return { onClick: () => { // 设置过滤状态 dispatch(setVisibilityFilter(ownProps.filter)) } } } const FilterLink = connect( mapStateToProps, mapDispatchToProps )(Link) export default FilterLink
import { connect } from 'react-redux' import { toggleTodo } from '../actions' import TodoList from '../components/TodoList' // 不同类型的 todo 列表 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 => { // state 即 vuex 的总状态,在 reducer/index.js 中定义 return { // 根据完成状态,筛选数据 todos: getVisibleTodos(state.todos, state.visibilityFilter) } } const mapDispatchToProps = dispatch => { return { // 切换完成状态 onTodoClick: id => { dispatch(toggleTodo(id)) } } } // connect 高阶组件,将 state 和 dispatch 注入到组件 props 中 const VisibleTodoList = connect( mapStateToProps, mapDispatchToProps )(TodoList) export default VisibleTodoList
reducers
文件夹
import { combineReducers } from 'redux'
import todos from './todos'
import visibilityFilter from './visibilityFilter'
const todoApp = combineReducers({
todos,
visibilityFilter
})
export default todoApp
const todos = (state = [], action) => { switch (action.type) { // 创建一个 todo case 'ADD_TODO': // 注意,返回不可变数据 return [ ...state, { id: action.id, text: action.text, // title completed: false } ] // 切换一个 todo 的完成状态 case 'TOGGLE_TODO': // 注意,返回不可变数据 return state.map(todo => (todo.id === action.id) ? {...todo, completed: !todo.completed} // 切换完成状态 : todo ) default: return state } } export default todos
const visibilityFilter = (state = 'SHOW_ALL', action) => {
switch (action.type) {
// 设置显示类型(所有、完成、未完成)
case 'SET_VISIBILITY_FILTER':
return action.filter
// 默认是 SHOW_ALL
default:
return state
}
}
export default visibilityFilter
index.js
import React from 'react'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'
let store = createStore(todoApp)
export default function () {
return <Provider store={store}>
<App />
</Provider>
}
React-router
,路由模式 hash
和 H5 history
,路由配置 动态路由和懒加载。hash
模式是默认的,如 http://abc.com/#/user/10。H5 history
模式,如 http://abc.com/user/20
,后者需要 server
端支持,因此无特殊需求可选择前者。对于 React
的原理,分为函数式编程、vdom
和 diff
、JSX
本质、合成事件、setState
和 batchUpdate
、组件渲染过程。
对于函数式编程,一种编程范式,纯函数,不可变值。不可变值数组,不能直接对 this.state.list
进行 push pop splice
等,这样违反不可变值。不可变值对象,不能直接对 this.state.obj
进行属性设置,这样违反不可变值。state
要在构造函数中定义
;不要直接修改 state
,使用不可变值;setState
可能是异步更新(有可能是同步更新);state
异步更新的话,更新前会被合并。
对于 vdom
和 diff
,h
函数、vdom
数据结构、patch
函数。只比较同一层级,不跨级比较。tag
不相同,则直接删掉重建,不再深度比较。tag
和 key
,两者都相同,则认为是相同节点,不再深度比较。
对于 JSX
的本质,JSX
等同于 Vue
模版,Vue
模版不是 html
,JSX
也不是 JS
。如第一个参数是 List
组件,找到 List
组件 jsx
结构,继续拆分。
对于合成事件,所有事件挂载到 document
上,event
不是原生的,是 SyntheticEvent
合成事件对象。它和 Vue
事件不同,和 DOM
事件也不同。需要合成事件机制的原因,如下所示:
document
,减少内存消耗,避免频繁解绑setState
和 batchUpdate
,如下所示:setTimeout
、DOM
事件的时候Object.assign
,前者相对于更重要setState
主流程、batchUpdate
机制、transition
事务机制setState
主流程,this.setState(newState)
到 newState
存入 pending
队列,然后判断是否处于 batch update
。如果是的话,保存组件于 dirtyComponents
中。如果不是的话,遍历所有的 dirtyComponents
,调用 updateComponent
,更新 pending state or props
setState
是同步的还是异步的,其实 setState
无所谓异步还是同步,看是否能够命中 batchUpdate
机制,判断 isBatchingUpdates
batchUpdate
机制,如下所示:
React
中注册的事件和它调用的函数React
可以管理的入口batchUpdate
机制,如下所示:
setTimeout、setInterval
等和它调用的函数DOM
事件和它调用的函数React
管不到的入口transition
事务机制,开始处于 batchUpdate
,isBatchUpdates = true
,其它任何操作,结束,isBatchUpdates = false
JSX
如何渲染为页面,setState
之后如何更新页面,以及对于全流程的理解,如下所示:JSX
即 createElement
函数,执行生成 vnode
,patch(elem, vnode)
和 patch(vnode, newVnode)
props state,render()
生成 vnode
,patch(elem, vnode)
setState(newState) -> dirtyComponents
,可能有子组件。render()
生成 newVnode
,patch(vnode, newVnode)
patch
更新的两个阶段,reconciliation
阶段执行 diff
算法,纯 JS
计算。在 commit
阶段,将 diff
结果渲染 DOM
JS
是单线程的,且和 DOM
渲染共用一个线程。当组件足够复杂的时候,组件更新时计算和渲染都压力大,同时再有 DOM
操作需求,如动画、鼠标拖拽等,将卡顿fiber
,将 reconciliation
阶段进行任务拆分,但是 commit
无法拆分。DOM
需要渲染时暂停,空闲时修复,window.requestIdleCallback
props
Redux
和 Context
JSX
的本质是什么,答案如下所示:createElement
vnode
Context
是什么,如何应用,答案如下所示:redux
shouldComponentUpdate
的用途,答案如下所示:arr1 = arr.slice()
React
的组件生命周期,答案如下所示:SCU
React
发起 ajax
应该在哪个生命周期,答案如下所示:componentDidMount
这个生命周期函数key
,答案如下所示:key
,且不能是 index
和 random
diff
算法中通过 tag
和 key
来判断,是否是 sameNode
class
组件的区别,答案如下所示:props
,输出 JSX
state
state
控制onChange
,更新 state
Render Props
mixin
已被 React
废弃redux
如何进行异步请求,答案如下所示:action
redux-thunk
PureComponent
有何区别,答案如下所示:shouldComponentUpdate
React
事件和 DOM
事件的区别,答案如下所示:document
上event
不是原生的,是 SyntheticEvent
合成事件对象dispatchEvent
React
的性能优化,答案如下所示:key
DOM
事件及时销毁bind this
的次数SCU、PureComponent 和 memo
Immutable.js
webpack
层面的优化SSR
React
和 Vue
的区别,答案如下所示:vdom
操作 DOM
React
使用 JSX
拥抱 JS
,Vue
使用模版拥抱 html
React
函数式编程,Vue
声明式编程React
更多的需要是自力更生,Vue
是把想要的都给你Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。