React
是一个构建用户界面的javascript库 是脸书开源前端应用框架 是三大框架之一
作用:主要用于编写html页面构建web应用 只提供了视图的渲染 相当于mvc中的v视图层
m:模型
v:视图
c:控制器
特点
-
声明式
- 我们只描绘ui页面骨架部分的构成 相当于html 怎么实现的由react去决定
const jsx = <div className="app"> <h1>hello React! 动态变化数据:{count}</h1> </div>
-
*基于组件
- 核心部分 通过各种细小的组件 来组成一整个页面的内容
-
学习一次 多处使用
- 可用之处很多web应用
- 可以开发移动端react-native
- 可以开发VR应用react 360
基本使用步骤
- 安装
npm i react react-dom
// react 包是核心,提供创建元素,组件等功能
// react-dom 包提供DOM相关功能
- index.html 引入文件 (那个页面需要引入哪个页面)
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
<!-- 注:有引入顺序的影响 react.development先引入 -->
- 创建 React元素
const el = React.createElement('h1/创建的html元素', null ,'元素子节点 可以是文字或者可以再嵌套一个节点React.createElement')
// 1.创建的dom元素html名称
// 2.元素的属性 {title:'这是标题属性'/h1的标题属性, id:'h1'/这是h1的id属性}
// 3.元素的子节点 可以嵌套react元素节点
// 创建繁琐 并不经常使用 了解就行 后续有JSX代替他来创建元素
- *渲染 ReactDOM.render 刚刚创建的React元素
<!-- 创建id为root的dom节点 -->
<div id="root"></div>
<srcipt>
ReactDOM.render('el/渲染节点名称',document.getElementById('root'))
// 渲染到页面
// 1.要渲染的元素名称
// 2.通过getElementById选中要挂载的DOM节点
<srcipt>
小结: 在开发不同平台时 所使用的渲染方法不同 但是react是必须要导入的
* 脚手架
作用:快速生成项目结构 提高开发效率
- 创建脚手架:
npx create-react-app 项目名称
// 创建成功 happy hacking
// npx命令 可以在没有全局安装脚手架时 使用脚手架中的一些命令
- 进入到项目根目录 启动脚手架命令:
npm start
yarn start
// 两者都可以启动 但是yarn需要安装 所以npm比较多
- 在index.js中引入React
import React from 'react'
import ReactDOM from 'react-dom'
- 创建元素
React.createElement('h1/创建的节点', {})
// 创建繁琐 并不经常使用 了解 JSX代替他来创建元素
- 渲染
// 渲染到页面
ReactDOM.render('节点名称', getElementById('root'))
// 此处的root 在index.html页面脚手架自动创建了
生成的项目结构
// 脚手架创建成功后显示的目录结构
public/公共资源
index.html 代表的是首页 必需
manifest.json PWA应用的元数据
// PWA 用来指定一些应用的名称、图标等...
src/项目源码 程序员写代码的地方
index.js 项目的入口文件 必需
App.js 项目的根组件
APP.test.js APP组件的测试文件
serviceWorker.js 用来实现PWA 可选
// 项目调节目录结构
src/
assets/ 资源(图片、字体图标)
components/ 公共文件
pages/ 页面
utils/ 工具
App.js/ 根组件(配置路由信息)
index.css/ 全局样式
index.js/ 项目入口文件(通过render渲染根组件 导入组件库)
index.html 是首页 所有页面的展示页 页面的内容依据此处来展示 创建完元素最后都会要渲染挂载到首页id名为 root的标签中<div id="root"> </div>
类似于vue中的路由占位符
JSX
是JavaScript XML 的简写 表示在JavaScript代码中写HTML格式的代码 用于代替react中的创建元素
优点:声明式语法更加直观 与HTML结构相同 提升开发效率
注意点:
- 属性名使用驼峰命名
- 特殊的几个属性名不能命令
class→className for→htmlFor
for在label登录注册输入框中出现 label是在输入框中自动聚焦功能 - 在创建时使用小括号()包裹 防止js中自动插入分号
- jsx外必须只有一个根元素进行包裹
// 报错,因为根元素的位置有两个并列的<h1>标签
const lut= <h1>tiantian</h1><h1>520</h1>
- 在jsx如果双标签中没内容 可写成单标签
在脚手架中可以使用jsx 是因为底层通过@babel/preset-react重新编译了
babel是脚手架内默认自带了配置 不需要手动进行配置
使用步骤
- 导入react
import React from 'react'
import ReactDOM from 'react-dom'
- 创建react元素
// 声明的需要插入的jsx元素
let content = <span>我是jsx元素</span>
// 创建的react的h1元素
let h1 = (<h1>我是通过JSX创建的元素+ {content}</h1>)
- 渲染
// 渲染到页面
// 1.要渲染的元素名称
// 2.要挂载的DOM节点
ReactDOM.render('h1/渲染节点名称',document.getElementById('root'))
插入表达式 { 名称 }
- 只能插入表达式三元表达和简单计算、函数的调用和自身的jsx插入
- 不能插入对象形式
{a: 12}
不能插入语句 if、for语句等 - jsx也是表达式 所以也能放在{}中 为模块化开发做铺垫
* 条件渲染
根据条件是否渲染 JSX 的代码结构
// if
const isload = true
const loadData = () => {
if(isload){
return <div> 条件为true时渲染 </div>
}
return <div> 条件为false时渲染 </div>
}
// 三元表达式
const loadData = () => {
return isload ? (<div> 条件为true时渲染 </div>) : (<div> 条件为false时渲染 </div>)
}
// 逻辑与运算符
const loadData = () => {
return isload && (<div> 条件为true时渲染 </div>)
}
// 通过逻辑中断来实现是否渲染后续代码 要么展示条件为true时候的代码 要么就不展示
* map 列表渲染
在某个位置要渲染一组数据时 使用数组的map() 方法
注:需要给添加key属性 遍历谁给谁添加key属性 避免使用索引号作为key属性
// 一组数组
const user = [
{id: 1, name:'xxx01'},
{id: 2, name:'xxx02'},
{id: 3, name:'xxx03'}
]
const list = (
<ui>
{
user.map(i => <li key = {i.id}> {i.name} </li>)
}
</ui>
)
行内样式
cosnt li = (
<li style={{'color': 'red',"backgroundColor": 'pink'}}></li>
)
// 外面的花括号是插值表达式 里面的花括号是表示对象形式的数据
注:在react中不能直接在标签src路径中写相对路径 需要先导入图片 在标签中填写导入的名称
* 类名
优点:代码结构较为清晰 与逻辑代码分离 写在单独的文件中
- 创建react元素
// css文件的导入
import './css/index.css'
cosnt clasname = (
<h1 class="container">jsx样式</h1>
)
- css 样式文件的设置
.container {
text-align: center
}
react和vue的区别
react是利用js语言本身的能力来编写代码
vue是使用一些框架提供的指令 例如v-model等来编写代码
* react 组件
是react的一等公民 使用react就是在使用组件
页面中的功能都代表是一个组件
特点:
- 可重复使用
- 具有独立性互不影响
- 可以组合使用:通过组和组件来组合成页面
函数组件
使用js的函数或箭头函数创建的组件是函数组件 称为无状态组件
注意点:
- 函数名以大写字母开头
- 必须要有返回值 return
// 1.定义
// 1.1标准函数
function Hello() {
return (
<div>这是第一个函数组件</div>
)
}
// 1.2使用箭头函数定义 更为简洁
const Hello = () => (<div>这是第一个函数组件</div>)
// 2.渲染使用
// 通过将定义的函数名称作为标签来使用
ReactDOM.render(<Hello />,document.getElementById('root'))
类组件
是使用es6的class创建的组件 称为有状态组件
注意点:
- 类名称以大写字母开头
- 必须继承React.Component父类 从而在之后可以使用父类中提供的方法和属性
- 必须要有render渲染方法
- render() 方法内部必须要有返回值 return
// 定义
class Hello extends React.Component {
render() {
return (<div>hello 类组件 </div>)
//在不想要返回值时 return null
}
}
// 渲染使用
// 通过将定义的类名称作为标签来使用
ReactDOM.render(<Hello />,document.getElementById('root'))
组件抽离单独JS文件
组件作为一个独立的个体 放到单独的js文件中 结构清晰 也方便使用
// 1.创建holle.js文件
// 2. 每个单独的js文件都要需要使用react 所以每个文件需要导入react
import React form 'react'
// 3.创建组件 函数组件或者类组件
function Holle() {
return (<div>首次抽离单独组件</div>)
}
// 4.通过es6语法进行暴露导出
export default Hello
// 1.在index.js 中导入holle组件
import Hello from './Hello'
// 2.渲染导入的组件
ReactDOM.render(<hello />, document.getElementById('root'))
事件绑定
React事件绑定语法和DOM事件绑定语法相似
语法:on+事件名称/click = {事件处理程序}
注意点:绑定的事件名称采用驼峰命名法 例:onClick
// 类组件中
// 类需要this来保障是当前的实例对象调用了按钮
class Hello extends React.Component {
// 事件处理程序
Hello() {
console.log('类组件触发了点击事件')
}
render() {
return (
<button onClick={this.Hello}>按钮</button>
// 在类组件中 绑定事件需要this
)
}
}
// 在函数组件中
// 函数绑定事件不需要this是因为直接调用的是函数名称
function Hello() {
// 函数事件处理程序
function handleClick() {
console.log('函数组件触发了点击事件')
}
return (
<button οnclick={handleClick}>按钮</button>
)
}
// 渲染组件
ReactDOM.render(<hello />, document.getElementById('root'))
事件对象
是通过事件处理程序的形参获取到事件对象 存放着一些和事件相关的数据
react中的事件对象叫做:合成事件 对象 并不是原生的事件对象 和原生事件对象不要混用
优点:兼容所有浏览器 不需要担心浏览器兼容问题
// 函数事件处理程序
function handleClick(e){
// e 就是react的事件对象
e.preventDefault()
console.log('react的事件对象',e)
}
// 类组件事件处理对象也是类事件处理程序的形参
<a onClick={handleClick}>事件对象</a>
有无状态state 组件
state状态=数据
无状态组件 函数
使用场景:当页面不会变动 只负责展示让数据展示 一直保持静态的时候 使用无状态函数组件
有状态组件 类
使用场景:当页面需要根据用户的变化产生变动时
比如说当用户触发了界面的某个事件 需要页面上展示的数据随之更新 就需要使用有状态的类组件
state 状态
是组件内部的 私有 数据 只能在组件内部使用 如果需要获取私有数据时可以通过组件的通信来获取
class Hello extends React.Component {
constructor() {
// super 是es6中必须要写的
super()
// state初始化
this.state = {
count: 0
}
}
// 简化写法
state = {
count: 0
}
render() {
return (
<div>初始化的值:{ this.state.count }</div>
// 拿数据this.state.属性名 必须这样才能拿到数据
)
}
}
// 渲染创建的类组件
ReactDOM.render(<Hello />, document.getElementById('root'))
注: 定义state 没有return返回
constructor在什么使用:其实写不写都默认有都会添加 在需要获取this并更改this指向时 需要写在constructor中
setState({ : })
作用:通过setState修改State中的数值 只能通过他来修改State 更新UI界面
数据驱动视图思想: 数据发生变化后 ui视图根据数据变化再发生变化
// 定义类组件
class Hello extends React.Component {
state = {
count: 0
}
render() {
return (
// 注意必须只有一个根元素
<div>
<h1> State: {this.state.count}</h1>
<button onClick={() => {
// 因为箭头函数没有自己的this绑定 此处的this指向组件实例
this.setState({
count: this.state.count + 1
})
}}>+1</button>
</div>
)
}
}
// setState({修改的state属性名:修改的操作})
// 渲染创建的类组件
ReactDOM.render(<Hello />, document.getElementById('root'))
抽离jsx逻辑代码
作用:让分工更加明确 render中只处理ui界面相关代码 将逻辑代码单独抽离到独立的方法中
// 定义类组件
class Hello extends React.Component {
state = {
count: 0
}
// 事件处理程序 存放逻辑代码的方法
addCount(){
// 此处的this为undefined
// 方法中的this是谁调用指向谁
this.setState({
count: this.state.count + 1
})
}
render() {
return (
// 注意必须只有一个根元素
// <!-- 此处会因为this指向的问题找不到addCount方法 -->
<div>
<h1> State: {this.state.count}</h1>
<button onClick={ this.addCount}>+1</button>
</div>
)
}
}
// setState({修改的state属性名:修改的操作})
// 渲染创建的类组件
ReactDOM.render(<Hello />, document.getElementById('root'))
事件绑定的this指向
问题原因:原先事件处理逻辑代码是存放在render方法中 使用的是箭头函数 箭头函数没有自己的this指向 会指向离自己最近方法的实例对象 此时render方法是有当前组件的实例对象的 所以this指向没有问题
抽离出来之后事件处理程序变成了普通函数 普通函数的this是谁调用指向谁 此时的this指向了调用它的点击事件 this找不到组件的实例对象 所以this变成了undefined
以下是三种解决方法 ↓
将调用事件的方法改为箭头函数
点击事件后通过箭头函数调用的事件处理程序 利用箭头函数this指向自己最近方法的实例的特性 指向了render拿到了组件实例对象的this
// 事件处理程序 存放逻辑代码的方法
addCount (){
// 此处的this为undefined
// 方法中的this是谁调用指向谁
this.setState({
count: this.state.count + 1
})
}
render() {
return (
// 注意必须只有一个根元素
<div>
<h1> State: {this.state.count}</h1>
<!-- 此处更改为箭头函数 -->
<button onClick={() => this.addCount}>+1</button>
</div>
)
}
bind
通过调用bind方法将改变后的this指向再次覆盖原先的undefined的this
// 定义类组件
class Hello extends React.Component {
constructor() {
super()
state = {
count: 0
}
// 将事件处理程序的this 更改后重新存入
// 此时需要获取this 所以写在constructor中
this.addCount = this.addCount.bind(this)
}
}
// 事件处理程序addCount 省略...
render() {
return (
// 注意必须只有一个根元素
<div>
<h1> State: {this.state.count}</h1>
<!-- 在constructor中重新存入了this 所以不会出现问题 -->
<button onClick={this.addCount}>+1</button>
</div>
)
}
*** 将调用的事件改为箭头函数**
将事件处理程序的函数改为箭头函数就能解决
注:是实验性语法 需要有babel的存在才可以直接使用 一般脚手架会默认配置babel 是最为便捷的方法
// react在事件处理函数中的this是未定义
// 在类组件的第一层定义箭头函数名= () => {}
// 事件处理程序 存放逻辑代码的方法
addCount = () => {
// 此处的this为undefined
// 方法中的this是谁调用指向谁
this.setState({
count: this.state.count + 1
})
}
// render省略...
render() {
return (
// 注意必须只有一个根元素
<div>
<h1> State: {this.state.count}</h1>
<!-- 在constructor中重新存入了this 所以不会出现问题 -->
<button onClick={this.addCount}>+1</button>
</div>
)
}