React
React 是一个用于构建用户界面的 JavaScript 库。
特点
- 声明式:以声明式编写 UI,可以让你的代码更加可靠,且方便调试。
- 组件化:构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI
- 一次学习,跨平台编写:无论你现在使用什么技术栈,在无需重写现有代码的前提下,通过引入 React 来开发新功能。
react VS vue
相同点:
- 良好的生态系统,轻量级框架
- 虚拟DOM
- 数据驱动视图,组件化思想
react优点:丰富的JavaScript库:来自世界各地的贡献者正在努力添加更多功能;虚拟DOM:基于文档对象模型,允许浏览器友好地以HTML,XHTML或XML格式排列文档;灵活性和响应性:它提供最大的灵活性和响应能力;可扩展性:由于其灵活的结构和可扩展性,React已被证明对大型应用程序更好;Web或移动平台: React提供React Native平台,可通过相同的React组件模型为iOS和Android开发本机呈现的应用程序
vue优点:易于使用:包含基于html的标准模板,可以更轻松的使用现有用用程序;跟顺畅的集成;更好的性能,更小的尺寸:占用更小的空间,比其他框架更好的性能;适应性:提供无障碍的迁移,简单有效的结构和可重用的模板
react脚手架
安装:
npx create-react-app my-app
注意
第一行的 npx
不是拼写错误 —— 它是 npm 5.2+ 附带的 package 运行工具。
my-app是项目名 可自定义
启动项目:
在项目根目录执行命令: npm start
目录结构介绍
Readme.md | 项目说明文件,先不用管它,等托管github或实际工作使用以markdown语法编写就好 |
package.json | webpack配置和项目包管理文件,里边有些命令脚本和依赖 |
lock文件 | package-lock.json或者yarn.lock都是锁定安装版本号的,保证你托管GitHub后大家下载安装的依赖是一致的 |
gitignore | git配置忽略文件,不需要上传的可以写里边,比如node_modules文件夹 |
node_module | 项目依赖包 |
public | 开放出去的公共资源,如果你想读取本地json,请放这个目录下 |
src | 放源码的位置,也是对我们开发者而言最核心的东西 |
src文件夹详解
index.js:项目的入口文件,根组件挂载根元素
输出
App.js
import React from 'react';
function App() {
return (
<div >
hello world
</div>
);
}
export default App;
类组件和函数组件的编写
函数组件
function Header(){
return <h1>我是头部</h1>;
}
function App() {
return (
<Header/>
);
}
组件不管是定义还是使用都是大写,react中,区分组件和HTML标签的方式就是大小写。大写的以组件方式解析,小写的以HTML标签解析。
类组件
输出hello world
import React, {Component} from 'react'
class App extends Component{
render(){
return (
<div>
hello world
</div>
)
}
}
export default App;
JSX使用
JSX是来描述页面的结构,我们一般在编写业务逻辑渲染页面的时候,需要涉及到传递值,调用函数,判断条件,循环等,这一些在JSX中都能得到支持
优势:
1.允许使用熟悉的语法来定义 HTML 元素树;
2.提供更加语义化且移动的标签;
3.程序结构更容易被直观化;
4.抽象了 React Element 的创建过程;
5.可以随时掌控 HTML 标签以及生成这些标签的代码;
6.是原生的 JavaScript。
嵌入JSX表达式
语法:{JavaScritp表达式}
<h1>{1+2}</h1>
列表渲染
- 如果需要渲染一组数据,我们应该使用数组的 map () 方法
- 注意:渲染列表的时候需要添加key属性,key属性的值要保证唯一
- 原则:map()遍历谁,就给谁添加key属性
- 注意:尽量避免使用索引号作为key
let arr = [{
id:1,
name:'孙悟空'
},{
id:2,
name:'沙悟净'
},{
id:3,
name:'唐僧'
},{
id:4,
name:'猪八戒'
}]
let ul = (<ul>
{arr.map(item => <li key={item.id}>{item.name}</li>)}
</ul>)
ReactDOM.render(ul,document.getElementById('root'))
注意
- 推荐使用className的方式给JSX添加样式
- 组件名必须大写
- 事件必须修正this指针,且绑定事件名时要使用小驼峰的写法onclick必须写成onClick
- 如果写行内样式用双花括号
{{}}
<p style={{color:"red"}}>hello</p>
- jsx语法中最外层必须有一个包裹元素,这样不行
- 如果不想加一个额外元素,可以用Fragment官方提供的占位符
JSX使用三元运算符
<h1>{false?"hello":"world"}</h1>
ref的作用
Context(解决层级出现多的情况如爷爷传递给孙子)
调用 React.createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件
const {Provider,Consumer} = React.createContext()
使用Provider组件作为组件的父节点
那一层想要接受父节点传来的数据用Consumer进行包裹,在里面回调函数中的参数就是传递过来的值
生命周期阶段(常用的钩子)
创建时(挂在阶段)
- 执行时机:组件创建时(页面加载时)
- 执行顺序
-
触发时机
-
钩子函数 触发时机 作用 constructor 创建组件时,最先执行 1.初始化state 2.为事件处理程序绑定this render 每次页面渲染时都会触发 渲染UI(注意不能调用setState()) componentDidMount 组件挂载(完成dom渲染)后 1.发网络请求 2 .DOM操作
更新时
- 执行时机:setState() forceUpdate() ,组件接收到新的props
- 说明以上三者任意一种变化,组件就会重新渲染
- 执行顺序
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次组件渲染都会触发 | 渲染UI |
componentDidUpdate | 组件更新(完成dom渲染)后 | 1.发送网络请求2.DOM操作 注意:如果要setState()必须放在一个if条件中 |
卸载时
- 执行时机:组件从页面中消失
- 作用:用来做清理操作
钩子函数 | 触发时机 | 作用 |
---|---|---|
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作(比如:清理定时器等) |
路由的基本使用
安装:
npm install --save react-router-dom
导入路由的三个核心组件: Router / Route / Link
import {BrowserRouter as Router, Route, Link} from 'react-router-dom'
使用Router 组件包裹整个应用
使用Link组件作为导航菜单(路由入口)
常用组件说明
-
Router组件:包裹整个应用,一个React应用只需要使用一次
-
两种常用的Router: HashRouter和BrowserRouter
-
HashRouter: 使用URL的哈希值实现 (localhost:3000/#/first)
-
推荐 BrowserRouter:使用H5的history API实现(localhost3000/first)
-
Link组件:用于指定导航链接(a标签)
-
最终Link会编译成a标签,而to属性会被编译成 a标签的href属性
-
Route组件:指定路由展示组件相关信息
-
path属性:路由规则,这里需要跟Link组件里面to属性的值一致
-
component属性:展示的组件
-
Route写在哪,渲染出来的组件就在哪
编程式导航
- 场景:点击登陆按钮,登陆成功后,通过代码跳转到后台首页,如何实现?
- 编程式导航:通过JS代码来实现页面跳转
- history是React路由提供的,用于获取浏览器历史记录的相关信息
- push(path):跳转到某个页面,参数path表示要跳转的路径
- go(n):前进或后退功能,参数n表示前进或后退页面数量
const Second = (props) => {
function gotoPage () {
props.history.push('/first')
}
return (<div>
<p>页面二</p>
<button onClick={gotoPage.bind(this)}>First</button>
</div>
)
}
精准匹配
-
给Route组件添加exact属性,让其变为精准匹配模式
-
精确匹配:只有当path和pathname完全匹配时才会展示改路由
组件性能优化
避免不必要的重新渲染
- 组件更新机制:父组件更新会引起子组件也被更新
- 问题:子组件没有任何变化值也会重新渲染
- 解决方法:使用钩子函数 shouldComponentUpdate(nextProps, nextState)
- 这个函数中nextProps和nextState是最新的状态以及属性
- 作用:这个函数有返回值,如果返回true,代表需要重新渲染,如果返回false,代表不需要重新渲染
- 触发时机:更新阶段的钩子函数,组件重新渲染执行((shouldComponentUpdate => render)
纯组件
作用以及使用
- 纯组件: PureComponent 与 React.Component 功能相似
- 区别: PureComponent 内部自动实现了 shouldComponentUpdate钩子,不需要手动比较
- 原理:纯组件内部通过分别比对前两次 props 和state的值。来决定知否重新渲染组件
组件复用
使用步骤
- 创建Mouse组件,在组建中提供复用的逻辑代码
- 将要复用的状态作为 props.render(state)方法的参数,暴露到组件外部
- 使用props.render() 的返回值作为要渲染的内容
class Mouse extends React.Component {
// 鼠标位置状态
state = {
x: 0,
y: 0
}
// 监听鼠标移动事件
componentDidMount () {
window.addEventListener('mousemove', this.handleMouseMove)
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
render () {
// 向外界提供当前子组件里面的数据
return this.props.render(this.state)
}
}
class App extends React.Component {
render () {
return (
<div>
App
<Mouse render={mouse => {
return <p>X{mouse.x}</p>
}} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
Children方法代替
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class Mouse extends Component {
// 鼠标位置状态
state = {
x: 0,
y: 0
}
// 监听鼠标移动事件
componentDidMount () {
window.addEventListener('mousemove', this.handleMouseMove)
}
handleMouseMove = e => {
this.setState({
x: e.clientX,
y: e.clientY
})
}
render () {
// 向外界提供当前子组件里面的数据
return this.props.children(this.state)
}
}
class App extends Component {
render () {
return (
<div>
<h1>鼠标移动</h1>
<Mouse >
{({ x, y }) => <p>鼠标位置:{y}</p>}
</Mouse>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
总结:
- render-props 是通过为组件添加 render 属性的方式传递一个函数给子组件,供子组件调用
- children,可以实现 render-props 的功能,区别是将函数通过子组件的内容传递过来,但是 chlldren 更加强大,可以传递任意的数据给子组件