Web开发 | React 介绍 & 使用总结 (八)

React简介

React 起源于Facebook的内部项目,因为该公司对市场上所有JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。

  • 由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。

React 中文官网

React 核心的概念

虚拟DOM(Virtual Document Object Model)

  • DOM的本质:就是用JS表示的UI元素
  • 虚拟DOM:并不是由浏览器提供的,而是我们程序员手动模拟实现的,类似于浏览器中的DOM,有着本质的区别
// 导入模块
import React from 'react'
import ReactDOM from 'react-dom'

// 创建虚拟Dom元素
var myDiv = React.createElement('div', {
    title: 'this is a title',
    id: 'mydiv'
}, '我是一个 div')
ReactDOM.render(myDiv, document.getElementById('app'))

Diff算法

  • tree diff:新旧DOM树,逐层对比的方式,就叫做 tree diff,每当我们从前到后,把所有层的节点对比完后,必然能够找到那些 需要被更新的元素;
  • component diff:在对比每一层的时候,组件之间的对比,叫做 component diff;当对比组件的时候,如果两个组件的类型相同,则暂时认为这个组件不需要被更新,如果组件的类型不同,则立即将旧组件移除,新建一个组件,替换到被移除的位置;
  • element diff:在组件中,每个元素之间也要进行对比,那么,元素级别的对比,叫做 element diff;
  • key:key这个属性,可以把 页面上的 DOM节点 和 虚拟DOM中的对象,做一层关联关系;
image.png

JSX语法

  1. 如要要使用 JSX 语法,必须先运行 npm i babel-preset-react -D,然后再 .babelrc 中添加 语法配置;
  2. JSX语法的本质:还是以 React.createElement 的形式来实现的,并没有直接把 用户写的 HTML代码,渲染到页面上;
  3. 如果要在 JSX 语法内部,书写 JS 代码了,那么,所有的JS代码,必须写到 {} 内部;
  4. 当 编译引擎,在编译JSX代码的时候,如果遇到了<那么就把它当作 HTML代码去编译,如果遇到了 {} 就把 花括号内部的代码当作 普通JS代码去编译;
  5. 在{}内部,可以写任何符合JS规范的代码;
  6. 在JSX中,如果要为元素添加class属性了,那么,必须写成className,因为 class在ES6中是一个关键字;和class类似,label标签的 for 属性需要替换为 htmlFor.
  7. 在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹;
  8. 如果要写注释了,注释必须放到 {} 内部
// JSX语法创建
var mytitle = 'this is a title'
var name = 'Alex'
var myDiv = 

我是一个标题,title 属性: {mytitle}

我是 P 标签

我的名字是 {name}

ReactDOM.render(myDiv, document.getElementById('app'))

React 项目搭建

  1. 运行 npm i react react-dom -S 安装包

  2. 在项目中导入两个相关的包

    • 在 React 学习中,需要安装 两个包 react react-dom
    • react 这个包,是专门用来创建React组件、组件生命周期等这些东西的;
    • react-dom 里面主要封装了和 DOM 操作相关的包,比如,要把 组件渲染到页面上
    import React from 'react'
    import ReactDOM from 'react-dom' 
    
  3. 使用JS的创建虚拟DOM节点

// 2. 在 react 中,如要要创建 DOM 元素了,只能使用 React 提供的 JS API 来创建,不能【直接】像 Vue 中那样,手写 HTML 元素
// React.createElement() 方法,用于创建 虚拟DOM 对象,它接收 3个及以上的参数
// 参数1: 是个字符串类型的参数,表示要创建的元素类型
// 参数2: 是一个属性对象,表示 创建的这个元素上,有哪些属性
// 参数3: 从第三个参数的位置开始,后面可以放好多的虚拟DOM对象,这写参数,表示当前元素的子节点
var myH1 = React.createElement('h1', null, '这是一个大大的H1')
var myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '这是一个div', myH1)
  1. 使用 ReactDOM 把元素渲染到页面指定的容器中:
// ReactDOM.render('要渲染的虚拟DOM元素', '要渲染到页面上的哪个位置中')
// 注意: ReactDOM.render() 方法的第二个参数,和vue不一样,不接受 "#app" 这样的字符串,而是需要传递一个 原生的 DOM 对象
ReactDOM.render(myDiv, document.getElementById('app'))


React-组件

创建方式一:

函数组件

// 组件创建一
var person = {name: 'alex',age: 20}

function Hello(props) {
    return 

我是一个组件,我通过function 构造器来创建

我的姓名是 {props.name}, 我的年龄: {props.age}

} // 扩散传值 ReactDOM.render(, document.getElementById('app'))

通过props 传值

function Welcome (props) {
    return 
Hello, {props.name}
} ReactDOM.render(, document.getElementById('app'))

创建方式二:

class 组件

var person = { name: 'alex', age: 20 }
class Hello extends React.Component{
    render() {
        return 

我是一个组件,我通过function 构造器来创建

我的姓名是 {this.props.name}, 我的年龄: {this.props.age}

} } var myDiv =
ReactDOM.render(myDiv, document.getElementById('app'))

组合组件

// 组件组合
function Welcome(props) {
    return 
Hello, {props.name}
} function App() { return (
) } ReactDOM.render(, document.getElementById('app'))

两种创建组件方式的对比

  1. 用构造函数创建出来的组件:专业的名字叫做“无状态组件”
  2. 用class关键字创建出来的组件:专业的名字叫做“有状态组件”

用构造函数创建出来的组件,和用class创建出来的组件,这两种不同的组件之间的本质区别就是:有无state属性!!!
有状态组件和无状态组件之间的本质区别就是:有无state属性!

组件生命周期&State

在组件创建、到加载到页面上运行、以及组件被销毁的过程中,总是伴随着各种各样的事件,这些在组件特定时期,触发的事件,统称为 组件的生命周期;

组件生命周期分为三部分:

1.组件创建阶段

组件创建阶段的生命周期函数,有一个显著的特点:创建阶段的生命周期函数,在组件的一辈子中,只执行一次;

  • componentWillMount: 组件将要被挂载,此时还没有开始渲染虚拟DOM
  • render:第一次开始渲染真正的虚拟DOM,当render执行完,内存中就有了完整的虚拟DOM了
  • componentDidMount: 组件完成了挂载,此时,组件已经显示到了页面上,当这个方法执行完,组件就进入都了 运行中 的状态

2.组件运行状态

组件运行阶段:也有一个显著的特点,根据组件的state和props的改变,有选择性的触发0次或多次;

  • componentWillReceiveProps: 组件将要接收新属性,此时,只要这个方法被触发,就证明父组件为当前子组件传递了新的属性值;
  • shouldComponentUpdate: 组件是否需要被更新,此时,组件尚未被更新,但是,state 和 props 肯定是最新的
  • componentWillUpdate: 组件将要被更新,此时,尚未开始更新,内存中的虚拟DOM树还是旧的
  • render: 此时,又要重新根据最新的 state 和 props 重新渲染一棵内存中的 虚拟DOM树,当 render 调用完毕,内存中的旧DOM树,已经被新DOM树替换了!此时页面还是旧的
  • componentDidUpdate: 此时,页面又被重新渲染了,state 和 虚拟DOM 和 页面已经完全保持同步

3.组件销毁阶段

也有一个显著的特点,一辈子只执行一次;

  • componentWillUnmount: 组件将要被卸载,此时组件还可以正常使用;
image.png

defaultProps

在组件创建之前,会先初始化默认的props属性,这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。在组件被创建并加载候,首先调用 constructor 构造器中的 this.state = {},来初始化组件的状态。

React生命周期的回调函数总结成表格如下:


image.png

组件生命周期的执行顺序:

  • Mounting:
  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()
  • Updating:
  • componentWillReceiveProps(nextProps)
  • shouldComponentUpdate(nextProps, nextState)
  • componentWillUpdate(nextProps, nextState)
  • render()
  • componentDidUpdate(prevProps, prevState)
  • Unmounting:
  • componentWillUnmount()

绑定this并传参的三种方式

1.在事件中绑定this并传参:


handleMsg (num, num2) {
        console.log(num, num2);
        this.setState({
            msg: num
        })
 }

2.在构造函数中绑定this并传参:

// 修改构造函数中的代码:
this.handleMsg2 = this.handleMsg2.bind(this, '', '');

// 在构造函数中绑定this并传参
handleMsg2(arg1, arg2) {
    this.setState({
        msg: '在构造函数中绑定this并传参:' + arg1 + arg2
    });
}

3.用箭头函数绑定this并传参

 { this.handleMsg3('', '') }} />
// 用箭头函数绑定this并传参
    handleMsg3(arg1, arg2) {
        this.setState({
            msg: '用箭头函数绑定this并传参:' +    arg1 + arg2
        });
}

绑定文本框与state中的值

在React.js中,默认没有提供双向数据绑定这一功能,默认的,只能把state之上的数据同步到界面的控件上,但是不能默认实现把界面上数据的改变,同步到state之上,需要程序员手动调用相关的事件,来进行逆向的数据传输!

绑定文本框和state的值:

{/*只要将value属性,和state上的状态进行绑定,那么,这个表单元素就变成了受控表单元素,这时候,如果没有调用相关的事件,是无法手动修改表单元素中的值的*/}

// 这是文本框内容改变时候的处理函数
handleTextChange = () => {
    this.setState({
        msg: this.refs.txt.value
    });
}

注意setState的一个问题:

// 保存最新的state状态值,在保存的时候,是异步地进行保存的,所以,如果想要获取最新的,刚刚保存的那个状态,需要通过回掉函数的形式去获取最新state
this.setState({
msg: this.refs.txt.value
// msg: e.target.value
}, function () {
// 获取最新的state状态值
console.log(this.state.msg);
});

context特性

context 可以将父组件的值传递给子组件来进行使用

// getChildContextTypes
// 1. 在 父组件中,定义一个 function,这个function 有个固定的名称,叫做 getChildContext ,内部,必须 返回一个 对象,这个对象,就是要共享给 所有子孙自建的  数据
getChildContext() {
    return {
        color: this.state.color
    }
}

// 2. 使用 属性校验,规定一下传递给子组件的 数据类型, 需要定义 一个 静态的(static) childContextTypes(固定名称,不要改)
static childContextTypes = {
    color: ReactTypes.string // 规定了 传递给子组件的 数据类型
}

// 3. 上来之后,先来个属性校验,去校验一下父组件传递过来的 参数类型
static contextTypes = {
    color: ReactTypes.string // // 这里,如果子组件,想要使用 父组件通过 context 共享的数据,那么在使用之前,一定要先 做一下数据类型校验
}

记住一串单词组合getChildContextTypes
前3个、后3个、后两个
一个方法、两个静态属性

Ant Design

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。

官方地址

路由

1.安装: npm add react-router-dom
2.导入: import { HashRouter, Route, Link } from 'react-router-dom'

  • HashRouter 表示一个路由的根容器,将来,所有的路由相关的东西,都要包裹在 HashRouter 里面,而且,一个网站中,只需要使用一次 HashRouter 就好了;
  • Route 表示一个路由规则, 在 Route 上,有两个比较重要的属性, path component
  • Link 表示一个路由的链接 ,就好比 vue 中的

3.设置HashRouter根路由
4.使用Linke创建路由连接
5.Route创建路由规则

// 路由学习
export default class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        }
    }
    render() {
        // 当 使用 HashRouter 把 App 根组件的元素包裹起来之后,网站就已经启用路由了
        return 
            

这是网站的APP根组件


首页    电影    关于
{/* Route 创建的标签,就是路由规则,其中 path 表示要匹配的路由,component 表示要展示的组件 */} {/* Route 具有两种身份:1. 它是一个路由匹配规则; 2. 它是 一个占位符,表示将来匹配到的组件都放到这个位置, 如果想让路由规则,进行精确匹配,可以为 Route,添加 exact 属性,表示启用精确匹配模式 */}
} }

你可能感兴趣的:(Web开发 | React 介绍 & 使用总结 (八))