react 官方脚手架 create-react-app 的使用
-
全局安装脚手架
$ npm install create-react-app -g
-
创建项目
$ create-react-app 项目名称 # 项目名不能是大写字母 # OR $ npx create-react-app 项目名称 # 先检测有没有安装脚手架,没有就先本地安装,再创建项目
react 介绍
react 起源于 Facebook 的内部项目,后发现这套框架好用,在 2013年5月开源了。react 并不是完整的 MVC/MVVM 架构,它专注于提供清晰、简洁的 View 层解决方案。它也是对虚拟 DOM 进行操作,提高性能。
jsx 语法
- JSX 将 HTML 语法直接加入到 JavaScript 代码中,再通过翻译器转换成纯 JavaScript 后由浏览器执行。在实际开发中,JSX 在产品打包阶段都已经编译成纯 JavaScript,不会带来任何副作用,反而让代码更加直观并易于维护。编译过程由 Babel 的 JSX 编译器实现的。
<React.StrictMode> 严格模式作用:
- 识别不安全的生命周期
- 关于使用过时的字符串 ref API 的警告
- 检测意外的副作用
- 检测过时的 context API
组件
-
组件的书写规范
- 组件首字母必须大写,否则会被认为是原生 dom 节点
- 组件最外层需要有一个根元素,不能有兄弟节点
- return 后面加上小括号可以回车
- 组件可以嵌套
- 组件的函数式写法和 class 类写法
- 样式
- class 换成 className
- 行内样式,注意 小驼峰写法
// 引入 react import React from 'react' // 引入外部样式 import './index.css' // 函数式写法 定义组件 function App () { return ( <div> <p style={{background: "pink"}}>行内样式</p> <p className="box">class 外部样式</p> </div> ) } // class 类写法 定义组件 class App extends React.Component { render () { return ( <div> <p className="box">这是使用 class 定义的组件</p> <div style={ {color: 'red'} }>我是 div 元素</div> <Child></Child> </div> ) } } // 定义子组件 class Child extends React.Component { render () { return ( <div> <p className="box">我是 child 组件</p> </div> ) } } // 导出组件 export default App
- 事件绑定(需要注意 this 指向问题)
import React from 'react' export default class App extends React.Component { render () { return ( <div> <label>用户名:<input ref="username" type="text"/></label> <br /> <button onClick={ () => console.log(this.refs.username.value) }>提交1</button> <button onClick={ this.handleClick2.bind(this) }>提交2</button> <button onClick={ this.handleClick3 }>提交3</button> </div> ) } handleClick2 () { console.log(this.refs.username.value) } handleClick3 = () => { console.log(this.refs.username.value) } /* 改变 this 指向问题 call(this, 1, 2) 改变 this 指向,并自动调用函数 apply(this, [1, 2]) 改变 this 指向,并自动调用函数 bind(this, 1, 2) 改变 this 指向,不自动调用函数 */ } // 这里使用的ref官网已不再推荐,具体用法如下
- ref 新用法
import React from 'react' export default class App extends React.Component { username= React.createRef(); // this.username.current 获取的是 input 的真实 dom render () { return ( <div> <label>用户名:<input ref="{this.username}" type="text"/></label> <br /> <button onClick={ () => console.log(this.username.current.value) }>提交</button> </div> ) } }
- 事件传参方式
<button onClick={ () => this.deleteRow(id) }>Delete Row</button> <button onClick={ this.deleteRow.bind(this, id) }>Delete Row</button>
- state 状态
import React, { Component } from 'react' export default class App extends Component { // es6 语法 constructor () { super() // super 必须写 this.state = { msg: '信息' } } // es7 语法,等价于上面的 es6 写法 state = { msg: '信息' } render() { return ( <div> <p>{ this.state.msg }</p> <button onClick={this.clickHandle}>修改 msg 状态</button> </div> ) } clickHandle = () => { /* setState() 并不保证是同步执行的, 当 setState() 在同步代码中执行时,它将是异步执行,第二个参数是个回调函数, 并且会进行 batchUpdate 批处理 当 setState() 在异步代码中执行时,它将是同步执行 */ this.setState({ // state 状态不能直接修改,只能通过 setState() 来修改 msg: '修改了' }, callback) } }
注意:将 html 字符串,解析成 html 标签
<div dangerouslySetInnerHTML={{ __html: this.state.html }} />
条件渲染
-
在 react 中使用与运算符(&&)、三目运算符进行条件渲染
import React, { Component } from 'react' export default class App extends Component { state = { isShow: true, list: [] } render() { return ( <div> { this.state.list.length > 0 && <div>条件为真时渲染</div> } { this.state.isShow ? <div>条件为真时渲染</div> : <div>条件为假时渲染</div> } </div> ) } }
-
阻止组件渲染
import React, { Component } from 'react' class Child extends Component { render () { // 传递来的数组长度为 0,就阻止组件渲染,直接返回 null if (!this.props.list.length) return null return <div>child 组件</div> } } export default class App extends Component { state = { list: [] } render() { return ( <div> <Child list={ this.state.list }/> </div> ) } }
列表渲染
-
使用 map() 方法渲染列表
import React, { Component } from 'react' export default class App extends Component { state = { list: [111, 222, 333] } render() { return ( <ul> { this.state.list.map((item, index) => <li key={index}>{item}</li>) } </ul> ) } }
父与子组件通信
-
在子组件上通过 key = value 写属性传递数据,子组件内通过 this.props 获取属性
-
注意在传递参数时,如果写成 isShow = “true” ,是一个字符串;如果写成 isShow = {true},是一个布尔值
-
prop 的类型验证和默认值
import React, { Component } from 'react' // 引入 prop-types 模块 import myPropTypes from 'prop-types' class NavBar extends Component { render() { return ( <div style={{ background: 'pink' }}> <p>这是 子组件</p> <div>{this.props.title}</div> { this.props.isShow ? <button>按钮</button> : null } </div> ) } // prop 类型验证 static propTypes = { title: myPropTypes.string, isShow: myPropTypes.bool } // prop 默认值 static defaultProps = { title: "标题", isShow: true } } export default class App extends Component { render() { return ( <div> <p>这是父组件</p> <NavBar title="导航栏" isShow={false}></NavBar> <NavBar></NavBar> </div> ) } }
-
插槽
import React, { Component } from 'react' class Child extends Component { render () { return ( <div> <p>child</p> {/* 整体插入 */} {this.props.children} {/* 单个插入 this.props.children 是个数组*/} {this.props.children[0]} </div> ) } } export default class App extends Component { render() { return ( <div> <p>app</p> <Child> <div>插槽内容1</div> <div>插槽内容2</div> </Child> </div> ) } }
子与父组件通信
-
给子组件传递一个回调函数,在子组件中触发这个回调函数,并将数据传递给父组件
import React, { Component } from 'react' class Child extends Component { state = { msg: 'child 的数据' } render () { return ( <div> <p>child</p> {/* 触发父组件传递来的回调函数,并将数据传递给父组件 */} <button onClick={ () => this.props.myEvent(this.state.msg) }> 向父组件传递数据 </button> </div> ) } } export default class App extends Component { render () { return ( <div> <p>father</p> <Child myEvent={ this.getChildData } /> </div> ) } getChildData = (data) => { // 用来获取子组件数据的回调函数 console.log(data) } }
ref 标记组件通信
-
通过 ref 给组件做标记。适合给子组件做标记
import React, { Component } from 'react' class Child extends Component { state = { msg: 'child 数据' } render () { return ( <div> <p>child----msg: { this.state.msg }</p> </div> ) } setMsg = (data) => { this.setState({ msg: data }) } } export default class App extends Component { render () { return ( <div> <p>father</p> <button onClick={ () => this.refs.child.setMsg('修改 msg 数据') }> click me </button> <Child ref="child" /> </div> ) } }
非父子关系组件通信
-
状态提升,使用父与子、子与父组件的通信方式,关系复杂时,容易掉头发,不推荐
-
利用发布订阅模式,实现事件总线机制
import React, { Component } from 'react' // 实现事件总线 const bus = { list: {}, $on: function (eventName, callback) { this.list[eventName] = callback }, $emit: function (eventName, payload) { payload ? this.list[eventName](payload) : this.list[eventName]() } } class Son extends Component { state = { msg: 'son 数据' } render () { return ( <div> <p>son</p> {/* 发布数据 */} <button onClick={ () => bus.$emit('getSonData', this.state.msg) }> publish </button> </div> ) } } export default class App extends Component { render() { return ( <div> <p>app</p> <Son /> </div> ) } componentDidMount () { // 订阅数据 bus.$on('getSonData', (data) => { console.log(data) }) } }
-
Context 状态树通信(跨级通信)
import React, { Component } from 'react' const MyContext = React.createContext() class Son extends Component { render() { return ( // 消费者,里面是个回调函数,context 就是生产者 value 传过来的数据 // 通过 context 改变根组件状态 <MyContext.Consumer> { context => ( <div> <p>son 组件</p> <button onClick={() => context.setMsg("修改了")}>修改 App 状态</button> </div> ) } </MyContext.Consumer> ) } } class Child extends Component { render() { return ( <div> <p>child 组件</p> <Son /> </div> ) } } export default class App extends Component { state = { msg: '数据' } render() { const obj = { msg: this.state.msg, setMsg: (data) => { // 提供一个修改状态的方法,供消费者调用 this.setState({ msg: data }) } } return ( // 生产者,value 是固定的,值为要通信的数据 <MyContext.Provider value={obj}> <div> <p>app---{this.state.msg}</p> <Child /> </div> </MyContext.Provider> ) } }