React基础
一、基本使用
JSX基本使用
state就是vue里的data:
constructor(props) { super(props) this.state = { name: '双越', imgUrl: 'https://img1.mukewang.com/5a9jpg', flag: true } }
// // 获取变量 插值 const pElem = <p>{this.state.name}</p> return pElem
React在{ }里用JS表达式 但在vue都是使用{{ }}来包裹
// // 表达式 const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p> return exprElem
写动态属性统统用{ }包裹就行了 vue里面用的是v-bind:
非动态属性和vue一样 正常写就行了
// // 子元素 const imgElem = <div> <p>我的头像</p> <img src="xxxx.png"/> <img src={this.state.imgUrl}/> </div> return imgElem
React class要写成className vue还是可以写class
// // class const classElem = <p className="title">设置 css class</p> return classElem
设置style使用{ } 在vue中则是使用:style="styleData"
// // style /const styleData = { fontSize: '30px', color: 'blue' } const styleElem = <p style={styleData}>设置 style</p> // // 内联写法,注意 {{ 和 }} 和vue的{{ }}可不一样! const styleElem = <p style={{ fontSize:'30px',color:'blue' }}>设置style</p> return styleElem
原生html就比较麻烦:
// 原生 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
加载组件是通过JSX语法 <组件名/>的形式 当然 也同样需要引入啦!这点和vue一样!不过vue组件后还需要component里面注册一下 react比较方便 直接用就行了!
// // 加载组件 const componentElem = <div> <p>JSX 中加载一个组件</p> <hr/> <List/> </div> return componentElem导出部分也不同 React的导出只有一句话export default JSXBaseDemo 而Vue导出一堆东西
JSX条件判断
react的条件判断非常简单粗暴 vue用的是v-if v-else-if v-else 在react直接if-else
// // 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>总的来说 React的语法非常接近原生JS的语法
JSX列表渲染
搞定map 和 key就够用了
map最终返回的是一个新的数组 react里面的key和vue的类似
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> 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
*事件
重点终于来了!
首先要记住:在React中绑定事件 事件都写为onXxxxx的形式 在vue中都是@click
1.bind this
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) }在react中,this默认是undefined
有两种解决方法:
法一:在调用方法时bind this 使函数指向当前对象 为了提升性能最好把bind this写在constructor里更好
// // this - 使用 bind return <p onClick={this.clickHandler1}> {this.state.name} </p>
clickHandler1() { // console.log('this....', this) // this 默认是 undefined this.setState({ name: 'lisi' }) }
法二:用静态方法写 this就始终指向当前实例 (也就是箭头函数呢!!)
// // this - 使用静态方法 return <p onClick={this.clickHandler2}> {this.state.name} </p>
// 静态方法,this 指向当前实例 clickHandler2 = () => { this.setState({ name: 'lisi' }) }
2.event
// // event return <a href="https://imooc.com/" onClick={this.clickHandler3}> click me </a>
// 获取 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 事件也不一样 }自我总结:
在事件处理程序不传入参数的时候 默认参数就是event 这点是共通的
但在react中的event是被封装过的SyntheticEvent(组合事件)这个组合事件模拟出来了DOM事件的所有能力 而vue中则是原生event
比如点击事件 react的点击事件原型是SyntheticEvent vue中的原型是MouseEvent
我们可以通过nativeEvent才能获取原生事件 可这个原生事件的触发target是当前元素
绑定target是document
这点也和vue不同,vue中的触发事件和绑定事件相同 evnet被挂载到当前元素 和DOM一样
而react中的原生事件都被挂载到document上 挂栽在当前元素上的是假event 是SyntheticEvent
3.传递参数
如果给事件传递了参数,事件处理程序会自动在最后追加一个参数event
// 传递参数 - 用 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>
// 传递参数 clickHandler4(id, title, event) { console.log(id, title) console.log('event', event) // 最后追加一个参数,即可接收 event }
表单
表单中的最大重点是受控组件 我们在vue中都用v-model 可react没有这种功能 只能自己实现
受控组件就相当于咱们自己实现的v-model
受控指的就是 我们的input的值受state控制了
/ // 受控组件(非受控组件,后面再讲) return <div> <p>{this.state.name}</p> <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for */} <input id="inputName" value={this.state.name} onChange={this.onInputChange}/> </div>
onInputChange = (e) => { this.setState({ name: e.target.value }) }
剩下的textarea select checkbox 就用完整代码了
import React from 'react' class FormDemo extends React.Component { constructor(props) { super(props) this.state = { name: '双越', info: '个人信息', city: 'beijing', 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 Copyright © 2018 imooc.com All Rights Reserved | 京ICP备 12003892号-11 简体中文× 拖拽到此处 图片将完成下载
二、父子组件通讯
又使用todolist来演示
父组件:
lass 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}/> //向input传入方法 <List list={this.state.list}/> //向List传入this.state.list值 </div> } onSubmitTitle = (title) => { this.setState({ list: this.state.list.concat({ id: `id-${Date.now()}`, title }) }) } } export default TodoListDemoinput:
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 = () => { //从props调用父组件传过来的方法 const { submitTitle } = this.props.submitTitle submitTitle(this.state.title) // 'abc' this.setState({ title: '' }) } }
list:
class List extends React.Component { constructor(props) { super(props) } render() { //调用父组件传来的值 const { list } = this.props.list return <ul>{list.map((item, index) => { return <li key={item.id}> <span>{item.title}</span> </li> })}</ul> } }
三、**setState**
不可变值
第一,state 要在构造函数中定义
第二、不要直接修改state 它使用的是不可变值 只能使用setState进行修改
第三、如果是数组 不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值
可以看出来 这里使用的函数都是返回一个新数组 而不改变原数组
相当于做了一个副本
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.obj 进行属性设置,这样违反不可变值
this.setState({ obj1: Object.assign({}, this.state.obj1, {a: 100}), obj2: {...this.state.obj2, a: 100} }) // //可能是异步更新
直接用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中是同步的
// // setTimeout 中 setState 是同步的 / setTimeout(() => { this.setState({ count: this.state.count + 1 }) console.log('count in setTimeout', this.state.count) }, 0)自己定义的 DOM 事件,setState 是同步的
bodyClickHandler = () => { this.setState({ count: this.state.count + 1 }) console.log('count in body event', this.state.count) } // componentDidMount() { // document.body.addEventListener('click', this.bodyClickHandler) // } // componentWillUnmount() { // // 及时销毁自定义 DOM 事件 // document.body.removeEventListener('click', this.bodyClickHandler) // // clearTimeout // }
可能会合并
因为异步更新 只在传入对象的情况下才合并
// // 传入对象,会被合并(类似 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 } }) }
四、组件生命周期
可以和vue的结合着看
父子组件生命周期和vue一样
五、React高级特性(一)
函数组件
非受控组件
Portals
context
异步组件
性能优化
高阶组件HOC
Render Props
函数组件
相当于把一个简单的class组件写成函数形式
- 纯函数 输入props 输出JSX
- 没有实例 没有生命周期 没有state
- 不能扩展其他方法
非受控组件
之前讲过受控组件,简单回顾一下:
/ // 受控组件(非受控组件,后面再讲) return <div> <p>{this.state.name}</p> <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for */} <input id="inputName" value={this.state.name} onChange={this.onInputChange}/> </div>非受控组件
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 */} //没有定义onChange事件 所以state不会改变 <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> // 上传文件 拿到文件名 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说白了 input值怎么变化 跟组件无关
我们只是把state的值拿过来做一个default而已
想要拿到里面的值 就需要通过ref来获取DOM节点
必须手动操作DOM元素,setState实现不了 比如文件上传<input type="file>
要注意react里使用ref的方法和VUE不同:
首先要创建ref:
this.nameInputRef = React.createRef() // 创建 ref然后设置在标签上:
<input defaultValue={this.state.name} ref={this.nameInputRef}/>最后通过 ref 获取 DOM 节点:
const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点
Portals
传送门
组件默认会按照既定层次嵌套渲染 但某些情况,需要让组件渲染到父组件以外
这时候就需要Portals
import React from 'react' import ReactDOM from 'react-dom' import './style.css' class App extends React.Component { constructor(props) { super(props) this.state = { } } render() { // // 正常渲染 其实应该把这个放在body最外层 但是正常渲染不行 return <div className="modal">//在modal中position:fixed {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
说白了 就是逃离父组件 应用场景为:
- 父组件设置overflow:hidden
- 父组件z-index值太小
- fixed需要放在第一层
Context
公共信息(语言 主题)用props传递太繁琐 用redux小题大做 用context就刚刚好
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主要是以下步骤
首先,通过React.createContext 创建一个Context
// 创建 Context 填入默认值(任何一个 js 变量) const ThemeContext = React.createContext('light')在最外层组件通过在<ThemeContext.Provider>标签里给context赋值:
return <ThemeContext.Provider value={this.state.theme}>在class类组件里:
使用ThemedButton.contextType = ThemeContext 设置子组件的contextType为父组件的context
也可以通过 静态属性static contextType = ThemeContext 的方法来设置
来读取父组件的context
// 底层组件 - 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。在函数组件里 通过<ThemeContext.Consumer>标签包裹 在标签里就可以使用context的内容了
// 底层组件 - 函数是组件 function ThemeLink (props) { // const theme = this.context // 会报错。函数式组件没有实例,即没有 this // 函数式组件可以使用 Consumer return <ThemeContext.Consumer> { value => <p>link's theme is {value}</p> } </ThemeContext.Consumer> }
异步组件
和vue的异步组件差不多 vue是通过import( )
React使用 React.lazy 和 React.Suspense
React.lazy(() => import('./ContextDemo'))
<React.Suspense fallback={<div>Loading...</div>}> 表示 当加载中调用的函数
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高级特性(二)
**性能优化
性能优化是大重点!!!!!
SUC
React 默认:
- 父组件更新时,子组件无条件更新 因此SCU默认返回ture
- 但SCU不一定每次都用 需要的时候才优化
- 必须配合“不可变值”一起使用 如果不适用不可变值,如果对list数组进行push,push后list就发生了改变,当在setState时,前后两个list值一样!SCU判断前后值时判断相等 返回false 就不会重新渲染了
- (所以不可变值很重要 一定要保证原本的值不变!)
- 但由于react没法约束开发者在setState中都遵循不可变值的原则,所以默认返回true,每次改变都渲染
// // 为了演示 SCU ,故意写的错误用法 this.state.list.push({ id: `id-${Date.now()}`, title }) this.setState({ list: this.state.list })
可使用_.isEqual做深度比较 但并不建议
// 增加 shouldComponentUpdate shouldComponentUpdate(nextProps, nextState) { // _.isEqual 做对象或者数组的深度比较(一次性递归到底)耗费性能 if (_.isEqual(nextProps.list, this.props.list)) { // 相等,则不重复渲染 return false } return true // 不相等,则渲染 }
PureComponent和memo
PureComponent可以在SCU中实现浅比较 但同样要配合不可变值
使用非常简单 就是在初始化组件的时候继承React.PureComponent
class List extends React.PureComponent
memo就是函数组件中的PureComponent
immutable.js
彻底拥抱“不可变值”
基于共享数据(不是深拷贝) 速度更好
但有一定学习成本
高阶组件
高阶组件就是用来做公共逻辑抽离的
在vue中我们只用mixin 但React不用mixin 而是用高阶组件HOC 和Render Props
import React from 'react' // 高阶组件 const withMouse = (Component) => { //创建一个组件 class withMou***ponent 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(VUE中用$props) 2. 增加 mouse 属性 */} //这里的component就是传入的子组件 !!!!// <Component {...this.props} mouse={this.state}/> </div> ) } } return withMou***ponent }
//使用公共逻辑的函数子组件 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) // 做一下封装 返回高阶函数
—————————————————————————————————————
补充知识点:
高阶函数
高阶函数,是一种特别的函数,接受的参数为函数,返回值也是函数。成立条件,二者兼一即可 可以看出来,高阶函数和闭包紧密关联!闭包就是一个内部函数
个人理解是,高阶函数中包含了闭包
常见的高阶函数有:
a).定时器:setTimeout()/setInterval()
b).Promise:Promise(()=>{}) then(value=>{},reason=>{})
c).数组遍历相关的方法: forEach()/ filter()/ map()/ find()/ findindex()
d).fn.bind() 本身是个函数,bind方法返回一个新的函数方法
e).Form.create()() create函数能够包装组件,生成另外一个组件的新功能函数
f).getFieldDecorator()()
——————————————————————————————————_
b).Promise:Promise(()=>{}) then(value=>{},reason=>{})
c).数组遍历相关的方法: forEach()/ filter()/ map()/ find()/ findindex()
d).fn.bind() 本身是个函数,bind方法返回一个新的函数方法
e).Form.create()() create函数能够包装组件,生成另外一个组件的新功能函数
f).getFieldDecorator()()
——————————————————————————————————_
在高阶函数这个概念基础上,再来品味一下高阶组件
高阶组件:就是接受一个组件作为参数并返回一个新组件的函数 本质上它还是一个函数!
也就是把子组件 当作一个参数 传入高阶组件这个函数里 在函数里对子组件这个那个 返回这个那个后的子组件
Render Props
和高阶组件类似
高阶组件是 用函数去包裹子组件
而Render Props 是子组件去包裹render函数组件
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> ) } } //一个类型校验 规定必须接受一个render属性 Mouse.propTypes = { render: PropTypes.func.isRequired // 必须接收一个 render 属性,而且是函数 }
//这个APP组件 没有被包裹 所以可以直接接收外面传过来的参数// const App = (props) => ( <div style={{ height: '500px' }}> <p>{props.a}</p> //用APP组件来包裹父组件 给父组件Mouse传入render这个函数组件作为prop// <Mouse render={ /* render 是一个函数组件 */ //在render里规定了APP组件如何渲染 显示什么内容// ({ x, y }) => <h1>The mouse position is ({x}, {y})</h1> }/> </div> ) /** * 即,定义了 Mouse 组件,只有获取 x y 的能力。 * 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。 */ export default AppHOC:模式简单 但会增减组件层级
Render props:代码简洁
React-router
和vue差不多!
动态路由:
跳转路由
懒加载