自学内容网 自学内容网

浅学React和JSX

往期推荐

一文搞懂大数据流式计算引擎Flink【万字详解,史上最全】-CSDN博客

数仓架构:离线数仓、实时数仓Lambda和Kappa、湖仓一体数据湖-CSDN博客

一文入门大数据准流式计算引擎Spark【万字详解,全网最新】_大数据 spark-CSDN博客

浅谈维度建模、数据分析模型,何为数据仓库,与数据库的区别_数据建模金博尔-CSDN博客

目录

1. 真实DOM和虚拟DOM

2. JSX语法规则

3. 虚拟DOM中使用内联样式

4. render渲染虚拟DOM

5. 函数式组件和类式组件 

6. State状态

6.1 类的基本知识

6.2 state复杂写法

6.3 state简单写法 

7.Props

7.1 基本使用

7.2 类型限制

8. Ref

8.1 字符串形式的ref 

8.2 回调函数的ref

8.3 createRef()

9. Ajax和Axios

9.1 前置说明

9.2 Ajax请求库

9.3 Axios请求 

9.4 Server.js代理

10. 路由


用antd做个人博客卡到前端了,迫不得已来学react,也是干上全栈了-- --学自尚硅谷张天禹react

React就是js框架,可以理解为对js做了封装,那么封装后的肯定用起来更方便。

相关JS库

  1. react.js:React核心库。
  2. react-dom.js:提供操作DOM的react扩展库。
  3. babel.min.js:解析JSX语法代码转为JS代码的库。 
    浏览器不能直接解析JSX代码, 需要babel转译为纯JS的代码才能运行。只要用了JSX,都要加上type="text/babel", 声明需要babel来处理。

JS库示例 :

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1_使用jsx创建虚拟DOM</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>

<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>

<script type="text/babel" > /* 此处一定要写babel */
//1.创建虚拟DOM
const VDOM = (  /* 此处一定不要写引号,因为不是字符串 */
<h1 id="title">
<span>Hello,React</span>
</h1>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('test'))
</script>
</body>
</html>

浏览器控制台可能会报如下错误:

 找不到favicon.ico的资源,那么左上角的标签页就不显示图标。解决方法是在项目中根目录下放一个同名图标即可。

1. 真实DOM和虚拟DOM

React提供了一些API来创建虚拟DOM对象

虚拟dom定义在<script type="text/babel">    </script>中!!!
 

//创建虚拟dom
1.用React创建,语法:React.createElement('标签名',{标签属性},'标签内容')
const VDOM = React.createElement('Good',{id:'title'},'Hello JSX')

2.用JSX语法创建,可以看到这样创建dom更简单
const VDOM = <Good id="title">Hello JSX</Good>


//创建真实dom,不常用
const DOM = document.createElement()
  • 我们编码时基本只需要操作react的虚拟DOM相关数据, react会转换为真实DOM变化而更新界。
  • 虚拟DOM对象最终都会被React转换为真实的DOM。
  • 虚拟dom本质是一个Object(控制台输出VDOM instanceof Object的结果为true)。
  • 虚拟dom内部元素少,真实dom内部元素多,因为虚拟dom是react内用,无需真实dom那么多属性。

注意!创建的虚拟dom只能有一个根标签,并且内部的标签必须闭合(有 /> 结束),如:

//正确的创建虚拟DOM,根标签只有一个
const VDOM = (
<div>
<h2>青秋</h2>
</div>
)

//错误的创建虚拟DOM,根标签有俩div
const VDOM = (
<div>
<h2>青秋</h2>
</div>
    <div>
<h2>青秋</h2>
</div>
)

//必须有/>结束
const VDOM = (
<div>
<h2>青秋</h2>
    //<input type="text" > 错误,没有闭合
    <input type="text"/> 或 <input type="text"/> </input>
</div>
    
)

2. JSX语法规则

全称是JavaScript XML,是react定义的一种类似于XML的JS扩展语法,可以把js和html写在一起,类似JSP。

JS + XML本质是React.createElement(componentprops, ...children)方法的语法糖

  • 作用: 用来简化创建虚拟DOM
  • 写法:var VDOM<h1>Hello JSX</h1>
  • 注意:这样创建的<h1>不是字符串, 也不是HTML/XML标签,它最终产生的就是一个JS对象
  • 标签名和标签属性任意,可以是HTML标签属性或其它
  • 语法规则:
  • 遇到 < 开头的代码, 以标签的语法解析: 与html同名标签则转换为html同名元素, 其它标签需要特别解析,如:
    //自定义的h1会被替换成html中同名的标签<h1>
    const vdom1=<h1>hello</h1>
    
    //自定义的Good在html中不存在同名标签,浏览器控制台会报错
    const vdom2=<good>hello</good>
    

    也就是说,定义的虚拟dom中,开头小写的标签会去html中寻找同名的元素并替换,找不到就会报上面的错误。如果是开头大写的标签,那么就是自定义的组件,如果写成Good,那么浏览器就会去渲染Good组件,因此Good组件需要提前定义,否则会undefined。
     
  • 遇到以 { 开头的代码,以JS语法解析,标签中的JS表达式必须用{ }包含,比如要在标签中引用自定义的变量,就要用{ }把自定义变量包裹起来,如:
    const myId="青秋"
    const myData="青秋博客"
    
    //1.创建虚拟DOM
    const VDOM = (
    <div>
    <h2 id={myId}>
    <span>{myData}</span>
    </h2>
    </div>
    )

3. 虚拟DOM中使用内联样式

在虚拟dom中使用内联样式(直接在标签中写style),需要用{{ }}包裹,其中外层的{ }代表标签里要写js表达式,内层的{ }代表要写的是一个对象。

另外{{ }}的style属性名是小驼峰的形式,比如font-size写成fontSize。真实dom的类名是class,虚拟dom的类名是className。如:

//真实dom内联样式
<div class="dom">
    <h2 style="color: black;font-size: large;">青秋博客</h2>
</div>

//虚拟DOM内联样式
const VDOM = (
<div className="vdom">
<h2 style={{color:'white',fontSize:'29px'}}>青秋博客</h2>
</div>
)

4. render渲染虚拟DOM

语法:  ReactDOM.render(<MyComponent/>,document.getElementById('test'))
作用: 将定义的虚拟DOM元素渲染到页面中的真实DOM中显示。
参数说明:
MyComponent是创建的虚拟dom对象;document.getElementById('test')是根据id获取的真实dom容器,是用来用来包含虚拟dom的。

5. 函数式组件和类式组件 

简单组件

组件名必须首字母大写!!小写则会去html中寻找同名元素

<script type="text/babel">
//1.创建函数式组件
function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>

复杂组件

<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component {
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/* 
执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>

6. State状态

6.1 类的基本知识

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>1_类的基本知识</title>
</head>
<body>
<script type="text/javascript" >
/*
总结:
1.类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
2.如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的。
3.类中所定义的方法,都放在了类的原型对象上,供实例去使用。
*/
//创建一个Person类
class Person {
//构造器方法
constructor(name,age){
//构造器中的this是谁?—— 类的实例对象
this.name = name
this.age = age
}
//一般方法
speak(){
//speak方法放在了哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},我年龄是${this.age}`);
}
}

//创建一个Student类,继承于Person类
class Student extends Person {
constructor(name,age,grade){
super(name,age)
this.grade = grade
this.school = '门头沟大学'
}
//重写从父类继承过来的方法
speak(){
console.log(`我叫${this.name},我年龄是${this.age},我读的是${this.grade}年级`);
this.study()
}
study(){
//study方法放在了哪里?——类的原型对象上,供实例使用
//通过Student实例调用study时,study中的this就是Student实例
console.log('我很努力的学习');
}
}

class Car {
constructor(name,price){
this.name = name
this.price = price
// this.wheel = 4
}
//类中可以直接写赋值语句,如下代码的含义是:给Car的实例对象添加一个属性,名为a,值为1
a = 1
wheel = 4
static demo = 100
}
const c1 = new Car('奔驰c63',199)
console.log(c1);
console.log(Car.demo);
</script>
</body>
</html>
  • 状态即数据,状态变化会驱动视图变化,可以简单理解为存储数据的对象。
  • 复杂组件即类定义组件有this,那么就有state,可以理解为一个对象的属性,可以通过constructor()传递参数,而简单组件的this是undefined就没有state一说。
  • render方法中的this就是组件实例对象
  • 组件自定义的方法中this为undefined,如何解决?
    1. 强制绑定this,通过函数对象的bind()   2.箭头函数

注意!!状态必须通过setState进行更新,不能直接更改。
正确的:this.setState({isHot:!isHot})
错误的:this.state.isHot = !isHot

6.2 state复杂写法



<script type="text/babel">
//1.创建组件
class Weather extends React.Component{
//构造器调用几次? ———— 1次
constructor(props){
console.log('constructor');
super(props)
//初始化状态
this.state = {isHot:false,wind:'微风'}
//changeWeather是自定义的,要解决changeWeather中this指向问题
                //调用bind方法会生成一个新函数,然后把新函数绑定到this实例对象上并命名为change,那么this实例对象有了名为change的方法
this.change = this.changeWeather.bind(this)
}

//changeWeather调用几次? ———— 点几次调几次
changeWeather(){
//changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
//由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
//类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
console.log('changeWeather');
//获取原来的isHot值
const isHot = this.state.isHot
//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({isHot:!isHot})
console.log(this);

//严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
//this.state.isHot = !isHot //这是错误的写法
}

//render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
render(){
console.log('render');
//读取状态
const {isHot,wind} = this.state

                //render函数中调用该类的另一个函数changeWeather,需要用this调用,否则会找不到要调用的函数!!
return <h1 onClick={this.change}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

</script>

6.3 state简单写法 



<script type="text/babel">
//1.创建组件
class Weather extends React.Component{
//初始化状态
state = {isHot:false,wind:'微风'}

//自定义方法————要用赋值语句的形式+箭头函数
changeWeather = ()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
render(){
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
}
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))
</script>

7.Props

props用于父组件向子组件传递数据,props只读无法修改()

7.1 基本使用

<script type="text/babel">
//创建组件
class Person extends React.Component{
render(){
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li
</ul>
)
}
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry" age={19}  sex="男"/>,document.getElementById('test1'))

const p = {name:'老刘',age:18,sex:'女'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
</script>

7.2 类型限制

<script type="text/javascript" src="../js/prop-types.js"></script>

<script type="text/babel">
//创建组件
class Person extends React.Component{
render(){
const {name,age,sex} = this.props
//props是只读的
//this.props.name = 'jack' //此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//对标签属性进行类型、必要性的限制
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
speak:PropTypes.func,//限制speak为函数
}
//指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
//渲染组件到页面
function speak(){console.log('我说话了');}
ReactDOM.render(<Person name={100} speak={speak}/>,document.getElementById('test1'))


const p = {name:'老刘',age:18,sex:'女'}
ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))

</script>

8. 订阅-发布PubSub

订阅发布用于兄弟组件传递数据,在之前,一个父亲有两个儿子,两个儿子要通信,要借助父亲来传达消息,而现在使用发布订阅,不需要借助父亲,两个儿子可以直接通信。

如List和Search两个兄弟组件

import React, { Component } from 'react'
import PubSub from 'pubsub-js'


export default class List extends Component {

state = { //初始化状态
users:[], //users初始值为数组
isFirst:true, //是否为第一次打开页面
isLoading:false,//标识是否处于加载中
err:'',//存储请求相关的错误信息
} 

componentDidMount(){
this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{
this.setState(stateObj)
})
}

componentWillUnmount(){
PubSub.unsubscribe(this.token)
}

render() {
const {users,isFirst,isLoading,err} = this.state
return (
<div className="row">
{
isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
isLoading ? <h2>Loading......</h2> :
err ? <h2 style={{color:'red'}}>{err}</h2> :
users.map((userObj)=>{
return (
<div key={userObj.id} className="card">
<a rel="noreferrer" href={userObj.html_url} target="_blank">
<img alt="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
</a>
<p className="card-text">{userObj.login}</p>
</div>
)
})
}
</div>
)
}
}







export default class Search extends Component {

search = ()=>{
//获取用户的输入(连续解构赋值+重命名)
const {keyWordElement:{value:keyWord}} = this
//发送请求前通知List更新状态
PubSub.publish('atguigu',{isFirst:false,isLoading:true})
//发送网络请求
axios.get(`/api1/search/users?q=${keyWord}`).then(
response => {
//请求成功后通知List更新状态
PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
},
error => {
//请求失败后通知App更新状态
PubSub.publish('atguigu',{isLoading:false,err:error.message})
}
)
}

render() {
return (
<section className="jumbotron">
<h3 className="jumbotron-heading">搜索github用户</h3>
<div>
<input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;
<button onClick={this.search}>搜索</button>
</div>
</section>
)
}
}

9. Ref

为自定义的标签打标识

9.1 字符串形式的ref 

string形式的 ref 存在效率问题,不推荐使用

<script type="text/babel">
//创建组件
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this.refs
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
render(){
return(
<div>
                        //ref相当于属性id,即标识了一个名为input1的input标签
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
</script>

9.2 回调函数的ref

<script type="text/babel">
//创建组件
class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this
alert(input2.value)
}
render(){
return(
<div>
                        //input的ref为c,把c赋给this实例对象,即把c标识的这个input标签赋给this对象,同时把input标签命名为myinput
<input ref={c => this.myinput = c } type="text" placeholder="点击按钮提示数据"/>&nbsp;
<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
<input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>&nbsp;
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
</script>

9.3 createRef()

<script type="text/babel">
//创建组件
class Demo extends React.Component{
/* 
React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
 */
myRef = React.createRef()
myRef2 = React.createRef()
//展示左侧输入框的数据
showData = ()=>{
alert(this.myRef.current.value);
}
//展示右侧输入框的数据
showData2 = ()=>{
alert(this.myRef2.current.value);
}
render(){
return(
<div>
<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
</script>

10. Ajax和Axios

10.1 前置说明

  1. React本身只关注于界面, 并不包含发送ajax请求的代码
  2. 前端应用需要通过ajax请求与后台进行交互(json数据)
  3. react应用中需要集成第三方ajax库(或自己封装)

10.2 Ajax请求库

  1. jQuery: 比较重, 如果需要另外引入不建议使用
  2. axios: 轻量级, 建议使用
    1. 封装XmlHttpRequest对象的ajax(XHR)
    2. promise风格
    3. 可以用在浏览器端和node服务器端

10.3 Axios请求 

1)GET请求
axios.get('/user?ID=12345')
  .then(function (response) {
    console.log(response.data);
  })
  .catch(function (error) {
    console.log(error);
  });


axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

2)POST请求
axios.post('/user', {
  firstName: 'Fred',
  lastName: 'Flintstone'
  })
 .then(function (response) {
    console.log(response);
  })
.catch(function (error) {
    console.log(error);
  });


10.4 Server.js代理

  • 除了熟知的nginx代理,还可以用js实现代理。
  • 代理是为了解决跨域请求,在前后端交互中,前端发给后端的请求被后端接收到,但是后端返回给前端的数据却无法被前端接收。
export default class App extends React.Component {

getStudentData = ()=>{
axios.get('http://localhost:3000/api1/students').then(
response => {console.log('成功了',response.data);},
error => {console.log('失败了',error);}
)
}

getCarData = ()=>{
axios.get('http://localhost:3000/api2/cars').then(
response => {console.log('成功了',response.data);},
error => {console.log('失败了',error);}
)
}

render() {
return (
<div>
<button onClick={this.getStudentData}>点我获取学生数据</button>
<button onClick={this.getCarData}>点我获取汽车数据</button>
</div>
)
}
}


================================代理===============================

//引入了 http-proxy-middleware 库,它提供了一种简单的方法来创建代理中间件。
const proxy = require('http-proxy-middleware')

module.exports = function(app){
app.use(
proxy('/api1',{ //遇见/api1前缀的请求,就会触发该代理配置
target:'http://localhost:5000', //请求转发给谁
changeOrigin:true,//控制服务器收到的请求头中Host的值。Host请求标识请求来源
pathRewrite:{'^/api1':''} //重写请求路径(必须)
}),
proxy('/api2',{
target:'http://localhost:5001',
changeOrigin:true,
pathRewrite:{'^/api2':''}
}),
)
}

11. 路由

哈希路由和浏览器路由

BrowserRouter与HashRouter的区别
            1.底层原理不一样:
                        BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
                        HashRouter使用的是URL的哈希值。
            2.path表现形式不一样
                        BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
                        HashRouter的路径包含#,例如:localhost:3000/#/demo/test
            3.刷新后对路由state参数的影响
                        (1).BrowserRouter没有任何影响,因为state保存在history对象中。
                        (2).HashRouter刷新后会导致路由state参数的丢失!!!
            4.备注:HashRouter可以用于解决一些路径错误相关的问题。

编程式路由导航

不需要用户触发,可以自动跳转链接

借助this.prosp.history对象上的API对操作路由跳转、前进、后退
             -this.prosp.history.push()
             -this.prosp.history.replace()
             -this.prosp.history.goBack()
             -this.prosp.history.goForward()
             -this.prosp.history.go() 

向路由组件传参 


原文地址:https://blog.csdn.net/qq_73181349/article/details/142739157

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!