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中的对象,做一层关联关系;
JSX语法
- 如要要使用 JSX 语法,必须先运行
npm i babel-preset-react -D
,然后再.babelrc
中添加 语法配置; - JSX语法的本质:还是以 React.createElement 的形式来实现的,并没有直接把 用户写的 HTML代码,渲染到页面上;
- 如果要在 JSX 语法内部,书写 JS 代码了,那么,所有的JS代码,必须写到 {} 内部;
- 当 编译引擎,在编译JSX代码的时候,如果遇到了
<
那么就把它当作 HTML代码去编译,如果遇到了{}
就把 花括号内部的代码当作 普通JS代码去编译; - 在{}内部,可以写任何符合JS规范的代码;
- 在JSX中,如果要为元素添加
class
属性了,那么,必须写成className
,因为class
在ES6中是一个关键字;和class
类似,label标签的for
属性需要替换为htmlFor
. - 在JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹;
- 如果要写注释了,注释必须放到 {} 内部
// JSX语法创建
var mytitle = 'this is a title'
var name = 'Alex'
var myDiv =
我是一个标题,title 属性: {mytitle}
我是 P 标签
我的名字是 {name}
ReactDOM.render(myDiv, document.getElementById('app'))
React 项目搭建
运行 npm i react react-dom -S 安装包
-
在项目中导入两个相关的包
- 在 React 学习中,需要安装 两个包 react react-dom
- react 这个包,是专门用来创建React组件、组件生命周期等这些东西的;
- react-dom 里面主要封装了和 DOM 操作相关的包,比如,要把 组件渲染到页面上
import React from 'react' import ReactDOM from 'react-dom'
使用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)
- 使用 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'))
两种创建组件方式的对比
- 用构造函数创建出来的组件:专业的名字叫做“无状态组件”
- 用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:
组件将要被卸载,此时组件还可以正常使用;
defaultProps
在组件创建之前,会先初始化默认的props属性,这是全局调用一次,严格地来说,这不是组件的生命周期的一部分。在组件被创建并加载候,首先调用 constructor 构造器中的 this.state = {},来初始化组件的状态。
React生命周期的回调函数总结成表格如下:
组件生命周期的执行顺序:
- 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 属性,表示启用精确匹配模式 */}
}
}