不管是任何框架, React|Vue|angular也好, 设计之初都是希望我们按照 组件|模块
的方式来构建程序, 也就是把一个程序划分成多个组件, 写好后渲染到页面即可
-
优点:
-
利于多人开发, 每个人只需要管理自己的组件
-
组件能够被重复利用
-
减少代码冗余
…
-
-创建组件
组件存放位置为 项目根目录/src/component
1.函数声明式
-
首先在src/component下创建组件 导航栏组件
Header.js
-
编写组件代码
import React from 'react' // 每个组件中都要导入React, 因为需要用到React.reactElement渲染JSX语法 /** * 函数声明式对话框组件 * 1. 函数返回结果是一个新的JSX对象, 也就是当前组件的结构 * 2. props变量存的是一个对象, 包含了调用组件时传递的属性, 不传递为{} */ export default function Dialog(props) { console.log(props) // Object { con: "hhh", ll: 6, what: "666" } let {con='我是默认的内容', what='我也是默认的内容'} = props // 对组件调用传入参数的结构 con = con !== 1 ? con : '啦啦啦' what = what !== 1 ? what : '嘻嘻嘻' return <section> <h2>{con}</h2> <div> {what} </div> </section> }
-
调用组件
2.继承式创建
-
首先在src/component下创建组件 导航栏组件
Header.js
-
编写组件代码
export default class Dialog extends React.Component { constructor(props, context) { super(props, context) // 写法1 super() // 写法2 // super(), 如果就这样写, 不挂载props和context, React.Component会有方法帮你挂载 // 1. super()继承绑定两个属性 1. props属性 2. context上下文 // 2. 就算没有在super里面传入props和context, 在构造函数执行的时候会自己挂载到实例 } render() { // 必须有render函数, 内部会调用render并获得返回的组件 let { type, content, children } = this.props console.log(content) // => 自己写的样式 const myCss = { width: '50%', margin: '0 auto 10px', padding: '0' } // => 传入的类型处理 let typeValue = type || '系统提示' if (typeof type === 'number') { switch (type) { case (0): { typeValue = '系统提示' break } case (1): { typeValue = '系统错误' break } case (2): { typeValue = '系统警告' break } default: { typeValue = '真香' break } } } return <section className="panel panel-default col-lg-4" style={myCss}> <div className="panel-heading"> <h3 className="panel-title">{typeValue}</h3> </div> <div className="panel-body">{content}</div> { children ? <div className="panel-footer"> {React.Children.map(children, item => item)} </div> : null } </section> } }
-
调用组件
3.两种方式对比
- 函数式组件更加快捷方便, 但是实现的功能比较单一
- 继承式组件虽然复杂, 但是可以实现更多的功能, 还能调用生命周期函数操作业务
- 各有优缺点, 根据需求来取舍
-组件属性管理
在继承式组件中, 所有实例的属性都是不可修改的
1.设置默认值
虽然在继承式组件中不能修改props的值, 但是可以设置默认值
// 在组件中加入这行代码, 设置props里面的默认值
static defaultProps = {
lx: '我是默认值'
}
2.设置类型限制
需要用到React全家桶中的prop-types
插件
-
下载
yarn add prop-types
-
使用
import PropTypes from 'prop-types' static propTypes = { // 设置时候, 仅仅是在console上进行warning警告 lx: PropTypes.number, // 必须是number content: PropTypes.string.isRequired, // 不仅是字符串, 而且必须传递值 value: PropTypes.instanceOf('Promise') // 必须是Promise的示例 }
-
使用
-组件状态管理
React中有两个非常重要的概念
- 组件的属性 [只读] : 调用组件时候调用方传递进来的
- 组件的状态 [读写] : 自己在组建中设定和编写的 ( 只有类组件才有状态管理 )
首先做一个案例: 时钟效果
-
函数组件实现
函数组件式
静态组件
, 和执行普通方法一样, 调取一次, 就能获得一个固定的结果, 如果不重新调取组件, 组件内的值就不会变化 -
类组件实现
可以借用钩子函数 ( 周期函数 ), 来对组件的状态进行改变
当组件状态一发生改变, 组件就会基于 DOM-DIFF 算法进行再次DOM差异渲染
1.初始化状态
constructor(props) {
super(props)
// 初始化组件状态(对象类型)=>把需要被修改的状态在这里挂载初始化
this.state = {
time: (new Date()).toLocaleString()
}
}
2.改变组件状态
componentDidMount() {
// 钩子函数(生命周期函数之一): 该组件第一次渲染之后触发
setInterval(() => {
// 修改组件状态, 并通知React重新渲染页面
/**
* 1.修改状态对象中的部分状态, 只把我们传递状态的进行修改
* 2.当状态修改完成后, 会通知React把组件中的部分JSX元素重新渲染
*/
this.setState({ time: (new Date()).toLocaleString() }, () => {
console.log('ok') // 当React把需要重新渲染的JSX渲染之后触发
})
}, 1000)
}
-组件事件处理
首先做一个案例,简单的投票系统
-
代码
class Vote extends React.Component { static defaultProps = { title: '我是标题' } static propTypes = { title: PropTypes.string.isRequired } constructor(props, context) { super(props, context) this.state = { support: 0, oppose: 0, } } render() { let { support, oppose } = this.state, rate = support + oppose === 0 ? 0 : ((support / (support + oppose)) * 100).toFixed(2) return <section className='panel panel-default' style={{ width: '60%', margin: '20px auto' }}> <div className='panel-heading'> <h3 className='panel-title'>{this.props.title}</h3> </div> <div className='panel-body'> 支持人数: {support}<br /><br /> 反对人数: {oppose}<br /><br /> 支持率: {rate}% </div> <div className='panel-footer'> {/* <button className='btn btn-success' onClick={this.support.bind(this)}>支持</button> */} <button className='btn btn-success' onClick={this.support}>支持</button> <button className='btn btn-danger' onClick={this.oppose}>反对</button> </div> </section> } support = (ev) => { this.setState({ support: this.state.support + 1 }) } oppose = (ev) => { this.setState({ oppose: this.state.oppose + 1 }) } }
-
运行效果
1.添加事件
class Example extends React.Component {
constructor(props, context) {
super(props, context)
}
render() {
return <div>
<button onClick={this.alertLogin}>登录</button>
<button onClick={this.alertLogout}>注销</button>
</div>
}
alertLogin = (e) => {
// 会默认传入事件对象e
alert('登录')
}
alertLogout = (e) => {
// 会默认传入事件对象e
alert('注销')
}
}
2.事件回调this指向
在React组件中调用事件回调函数, 事件函数内部的this
与我们想象的不太一样, 是undefined
-
普通函数用
bind
绑定class Example extends React.Component { constructor(props, context) { super(props, context) } render() { return <button onClick={this.alertThis.bind(this)}>按钮</button> } alertThis(e) { // 会默认传入事件对象e console.log(this) } }
-
利用箭头函数特性
ES6在类中定义的箭头函数, 其上下文是当前实例
class Example extends React.Component { constructor(props, context) { super(props, context) } render() { return <button onClick={this.alertThis}>按钮</button> } alertThis = (e) => { // 会默认传入事件对象e console.log(this) } }
-
普通函数+箭头函数
class Example extends React.Component { constructor(props, context) { super(props, context) } render() { return <button onClick={() => this.alertThis()}>按钮</button> } alertThis (e) { // 会默认传入事件对象e console.log(this) } }
3.向事件回调传递参数
-
bind实现
<button onClick={this.alertThis.bind(e, 'v1', 'v2')}>按钮</button> alertThis(value, value2, e) { console.log(e) console.log('value', value) console.log(this) }
-
箭头函数实现
<button onClick={(e) => this.alertThis(e, 'v1', 'v2')}>按钮</button> alertThis(e, value1, value2) { console.log(e) console.log('value1', value1) console.log('value2', value2) console.log(this) }