目录

环境准备

npm

Babel

核心概念与类组件使用

React特点

虚拟DOM和React18新渲染写法

jsx

结构限制

注释

条件渲染

列表渲染

基本通信

props

构造器获取props

多属性的传递

给属性添加默认值与类型

state

Refs

组件三大属性总结

props

state

refs

组件跨层级通信

action

reducer

store

相关API

事件

事件中this的处理

事件传参处理

非受控组件

受控组件

高阶函数

函数的珂里化

新旧生命周期总结

key

Diff算法

React脚手架

 react脚手架项目结构

样式模块化

组件的组合使用-TodoList

脚手架配置代理

React路由

SPA

Hooks

面试常问

Angular,Vue,React

参考链接


【慕课网imooc】【Vue3+React18 + TS4考勤系统】

环境准备

npm

( Node Package Manager ,即:node包管理器 )

是nodeJS的一个程序包管理和分发的管理工具,npm完全用 JavaScript 写成,

它可以让全世界与Web前端相关开发者共享代码,非常方便的使用各种插件、库和框架,

React用于构建用户界面的JS库。是一个将数据渲染为HTML视图的开源JS库。

eg:vue    react   Bootstrap   jQuery  Angular

优化开发流程的前端开发自动化构建工具: Grunt 、 Gulp 、 Webpack 、 Babel

补充:

npm包管理工具的安装及配置使用_雯倾浅忆的博客-CSDN博客_npm包管理器

主要:(修改环境变量后,要重新打开cmd窗口才能生效,否则会报 不是内部命令)

Nodejs安装教程_彭佼的博客-CSDN博客_nodejs安装

官网下载 

Babel插件

JavaScript 编译器

将es6+语法转换为浏览器兼容的语法,比如将箭头函数转换为普通函数

将jsx转换成浏览器认的js

Babel是什么?Babel到底可以用来干嘛___一文带你从零开始认识Babel_zihanzy.com的博客-CSDN博客_babel是什么

创建项目

npm install create-react-app 

create-react-app  xxx

cd xxx

npm start

核心概念与类组件使用

React特点

虚拟DOM和React18新渲染写法

Vue和React框架都会自动控制DOM的更新,而直接操作真实DOM是非常耗性能的,所以才有了虚拟DOM的概念

下面是一个多次触发DOM更新的例子和只触发一次DOM的一个对比。 时间相差1000倍,

<ul></ul>
<script>
	// 多次触发DOM操作,非常耗时:1000ms左右
    console.time(1)
    let ul = document.querySelector('ul');

    for(let i=0;i<1000;i++){
        ul.innerHTML += `<li>${i}</li>`;
    }
    console.timeEnd(1)
  </script>
<ul></ul>
<script>
	// 只触发一次DOM操作,节约时间:1ms左右
   	console.time(1)
    let ul = document.querySelector('ul');
    let str = '';
    for(let i=0;i<1000;i++){
      str += `<li>${i}</li>`;
    }
    ul.innerHTML = str;
    console.timeEnd(1)
</script>

在React18中,操作虚拟DOM,再一次性的更新真实DOM。需要使用两个文件来初始化框架:

官网下载 

  • react.development.js 或 react模块 -> 生成虚拟DOM

  • react-dom.development.js 或 react-dom/client模块 -> Diff算法 + 处理真实DOM

  • <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
        
      <script src="../react.development.js"></script>
      <script src="../react-dom.development.js"></script>
    </head>
    <body>
      <div id="app"></div>
      <script>
        // React对象 -> react.development.js(产生虚拟DOM)
        // ReactDOM对象 -> react-dom.development.js(渲染成真实DOM)
        //原生DOM获取
        let app = document.querySelector('#app');
        // root根对象,渲染react的DOM,React18
        let root = ReactDOM.createRoot(app); 
        // React.createElement() -> 创建虚拟DOM 
        //createElement(标签,属性,内容)
        let element = React.createElement('h2', {title: 'hi'}, 'hello world');
        root.render(element);
      </script>
    </body>
    </html>

jsx

JSX =JavaScript + XML。是一个 JavaScript 的语法扩展。

let element = React.createElement('h2', {title: 'hi'}, [
    'hello world',
    React.createElement('span', null, '!!!!!!')
]);

JSX写起来就方便很多了,在内部会转换成React.createElement(),然后再转换成对应的虚拟DOM,但是JSX语法浏览器不认识,所以需要利用babel插件进行转义处理。

...
<script src="../babel.min.js"></script>
...
<script type="text/babel">
    let app = document.querySelector('#app');
    let root = ReactDOM.createRoot(app);
    let element = (
      <h2 title="hi">
        hello world
        <span>!!!!!!</span>
      </h2>
    ); 
    root.render(element);
</script>
...

结构限制

JSX =JavaScript + XML,那么就有很多结构限制

  • 标签要小写
  • 单标签要闭合
  • class属性与for属性
  • 多单词属性需驼峰,data-*不需要
  • 唯一根节点

解决:<React.Fragment>,在脚手架下可以直接省略成<>

let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app); 
let element = (
    <React.Fragment>
        <h2 title="hi" className="box">
            hello world
            <span>!!!!!!</span>
            <label htmlFor="elemInput">用户名:</label>
            <input id="elemInput" type="text" tabIndex="3" data-userid="123" />
        </h2>
        <p>ppppppp</p>
    </React.Fragment>
); 
root.render(element);
  • { } 模板语法:{js表达式}

  • 添加注释

  • 属性渲染变量

  • 事件渲染函数

  • style渲染对象

  • { } 渲染 JSX

let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app); 
let myClass = 'box';
let handleClick = () => {
    console.log(123);
}
let myStyle = {
    color: 'red'
};
let element = (
    <div>
        { /* <p>{ 1 + 1 }</p> */ }
        <p className={myClass}>{ 'hello' }</p>
        <p onClick={handleClick}>{ 'hello'.repeat(3) }</p>
        <p style={myStyle}>{ true ? 123 : 456 }</p>
        <p>{ <span>span111</span> }</p>
        <p><span>span222</span></p>
    </div>
); 
root.render(element);

这里可以看到react中的模板语法采用的是单大括号,这一点跟Vue不太一样,Vue采用的是双大括号。

注释

html  <!-- 注释 -->

js  //注释   ctrl+/   /*注释*/  ctrl+shitf+/

jsx  {/*注释*/}  

JSX语法详解_RockyHills的博客-CSDN博客_jsx语法

条件渲染

在React中是没有指令这个概念的,所以条件渲染和列表渲染都需要通过命令式编程来实现(也就是JS本身的能力)。

既然没有相关的指令,那么就只能通过原生JS来实现条件渲染了,具体方案可采用:

  • 条件语句

  • 条件运算符

  • 逻辑运算符

// 方案一,条件语句
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app); 
let isShow = false;
let element;
if(isShow){
    element = (
        <div>
            hello world
        </div>
    );
}
else{
    element = (
        <div>
            hi react
        </div>
    );
}
root.render(element);
// 方案二,条件运算符
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app); 
let isShow = true;
let element = (
    <div>
        {
            isShow ? 'hello world' : 'hi react'
        } 
    </div>
);
root.render(element);
// 方案三,逻辑运算符
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app); 
// JSX中不会渲染的值:false null undefined ''
let isShow = 0;
let element = (
    <div>
        {
            isShow !== 0 && 'hello world'
        } 
    </div>
);
root.render(element);

列表渲染

列表渲染也是需要通过原生JS来实现,具体方案:

  • 循环语句

  • map()方法

这里还需要注意一点,就是循环结构的时候还是需要给每一项添加唯一的key属性,这一点跟Vue非常相似。

// 方案一,循环语句
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app); 
let data = [
    { id: 1, text: 'aaa' },
    { id: 2, text: 'bbb' },
    { id: 3, text: 'ccc' }
];
let ret = [];
for(let i=0;i<data.length;i++){
    ret.push(<li key={data[i].id}>{data[i].text}</li>);
}
// ['a', 'b', 'c']  ->  'a,b,c' 
// { ['a', 'b', 'c'] } ->  'abc' 
let element = (
    <ul>
        { ret }
    </ul>
);
root.render(element);
// 方案二,map()方法
let app = document.querySelector('#app');
let root = ReactDOM.createRoot(app); 
let data = [
    { id: 1, text: 'aaa' },
    { id: 2, text: 'bbb' },
    { id: 3, text: 'ccc' }
];
let element = (
    <ul>
        {
            data.map(v=><li key={v.id}>{v.text}</li>)
        }
    </ul>
);
root.render(element);

基本通信

JSX是包含两部分的:

  • React元素

  • React组件

// React元素
const element = <div />
// React组件
const element = <Welcome name="Sara" />

定义一个组件,就是标签名首字母要大写,在React18中有两种定义组件的写法:

  • 函数组件

  • 类组件

props

组件对外公开一个简单的属性(Props),只读

通过this.props获取属性对象,主要用来传递数据,单向数据流,props不能直接修改

<body>
    <div id = "div">

    </div>

</body>
<script type="text/babel">
// 函数组件
function Welcome(props){
    return (
        <div>hello world, {props.msg}</div>
    );  
}
let element = (
    <Welcome msg="hi react" />
);

// 类组件
    class Person extends React.Component{
        render(){
            return (
                <ul>
                    //接受数据并显示
                    <li>{this.props.name}</li>
                    <li>{this.props.age}</li>
                    <li>{this.props.sex}</li>
                </ul>
            )
        }
    }
   //等同
   const p = {name:"张三",age:"18",sex:"女"}
   ReactDOM.render(<Person {...p}/>,document.getElementById("div"));
    //传递数据
    ReactDOM.render(<Person name="tom" age = "41" sex="男"/>,document.getElementById("div"));
</script>

在上面组件中的msg就是组件通信的数据,可以实现父子传递数值的操作。还可以传递函数给组件内部来实现子父通信操作。代码如下:

// 子组件
class Head extends React.Component {
    render(){
        this.props.getData('子组件的问候~~~')
        return (
            <div>Head Component</div>
        );
    }
}
// 父组件
class Welcome extends React.Component {
    getData = (data) => {
        console.log(data)
    }
    render(){
        return (
            <div>
                hello world, {this.props.msg}
                <br />
                <Head getData={this.getData} />
            </div>
        );
    }
}

2、Reactjs中的属性(this.props)_yubo_725的博客-CSDN博客_react this.props传递对象时

构造器获取props

props是我们React父子组件之间通信的对象,那么这个对象在构造器constructor中是获取不到的。

constructor(){
        super();
        console.log( this.props.msg )   // undefined
    }

可以通过给super(props)传递props参数是可以做到的、

class Foo {
    constructor(props){
        this.props = props;
    }
}
class Bar extends Foo {
    constructor(props){
        super(props);
        console.log(this.props);
    }
    render(){
        console.log(this.props);
        return '';
    }
}
let props = {
    msg: 'hello world'
};
let b = new Bar(props);
b.props = props;
b.render();

多属性的传递

当有非常多的属性要传递的时候,那么会比较麻烦,所以可通过扩展运算形式进行简写。

class Welcome extends React.Component {
    render(){
        //解构
        let { msg, username, age } = this.props;
        console.log( isChecked );
        return (
            <div>hello world, {msg}, {username}, {age}</div>
        );
    }
}
let info = {
    msg: 'hi react',
    username: 'xiaoming',
    age: 20
};
let element = (
    <Welcome {...info} />
);

... 展开运算符还能在复制时,合并属性

 const p1 = {name:"张三",age:"18",sex:"女"}
 const p2 = {...p1,name : "111",hua:"ss"};
 p1.name = "sss";
 console.log(p2)  //{name: "111", age: "18", sex: "女",hua:"ss"}

给属性添加默认值与类型

import PropTypes from 'prop-types'
class Welcome extends React.Component {
    static defaultProps = {
        age: 0
    }
    static propTypes = {
        age: PropTypes.number
    }
    ...
}

这里的类型需要引入第三方模块才可以生效。

当父子通信的时候,如果只写属性,不写值的话,那么对应的值就是布尔值true。

state

组件的私有属性,值是对象(可以包含多个key-value的组合)

通过state设置响应式视图,他是组件内私有的,受控于当前组件。通过state的变化,就可以影响到视图的变化。

class Welcome extends React.Component {
    state = {
        msg: 'hello',
        count: 0
    }
    render(){
        return (
            <div>
                {this.state.msg}, {this.state.count}
            </div>
        );
    }
}
let element = (
    <Welcome />
);

这样就可以在页面中渲染msgcount这两个字段了,那么怎么才能让state修改后视图跟着发生变化呢,首先不能像Vue那样直接对数据进行修改,在React中是不行的。

React类组件中式通过一个具体的方法setState()进行state数据的更新,从而触发render()方法的重渲染操作。

class Welcome extends React.Component {
    state = {
        msg: 'hello',
        count: 0
    }
    handleClick = () => {   
        //this.state.msg = 'hi'  //永远不要这样去操作
        this.setState({
            msg: 'hi'
        });
    }
    render(){
        console.log('render');
        return (
            <div>
                <button onClick={this.handleClick}>点击</button>
                {this.state.msg}, {this.state.count}
            </div>
        );
    }
}
let element = (
    <Welcome />
);

state改变视图的原理就是内部会重新调用render()方法,俗称re-render操作。

这里还有注意一点,setState()并不会影响其他state值,内部会完成合并的处理。

React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等

大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。

在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;

当 React 在调用事件处理函数之前就会先调用函数 batchedUpdates,

batchedUpdates会把 isBatchingUpdates 修改为 true,

如果是同步更新,每一个setState对调用一个render,

并且如果多次调用setState会以最后调用的setState为准

Refs

字符串形式的ref(16.8版本以上已不推荐)

解构赋值

const {inputL}=this.refs

连续解构赋值

const {value}=this.target

<=>

const {target:{value}}=this

//将value重命名为key

const {target:{value:key}}=this

 只有字符串形式时Refs才有值

 回调函数形式的ref

 

  

API创建Ref

组件三大属性总结

props

单向数据流值,父子组件间的唯一通信,不可改

1.每个组件对象都会有props(properties的简写)属性

2.组件标签的所有属性都保存在props中

3.内部读取某个属性值:this.props.propertyName

state

React 把组件看成是一个状态机(State Machines),

只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

  • props中的数据都是外界传递过来的;
  • state中的数据都是组件私有的;(通过Ajax获取回来的数据,一般都是私有数据)

refs

当需要获取某一个真实的DOM元素来交互,比如文本框的聚焦、触发强制动画等

当需要操作的元素和获取的元素是同一个时,无需ref

  • 给DOM元素添加ref属性
  • 给类组件添加ref属性

组件跨层级通信

前面我们学习了父子组件之间的通信,有时候我们需要多层组件之间的嵌套,那么如果从最外层一层一层的把数据传递到最内层的话势必会非常的麻烦。

所以context的作用就是解决这个问题,可以把数据直接从最外层传递给最内层的组件。

let MyContext = React.createContext();
class Welcome extends React.Component {
    state = {
        msg: 'welcome组件的数据'
    }
    render(){
        return (
            <div>
                Hello Welcome
                <MyContext.Provider value={this.state.msg}>
                    <Head />
                </MyContext.Provider>
            </div>
        );
    }
}
class Head extends React.Component {
    render(){
        return (
            <div>
                Hello Head
                <Title />
            </div>
        );
    }
}
class Title extends React.Component {
    static contextType = MyContext
    componentDidMount = () => {
        console.log( this.context );
    }
    render(){
        return (
            <div>
                Hello Title <MyContext.Consumer>{ value => value }</MyContext.Consumer>
            </div>
        );
    }
}
let element = (
    <Welcome />
);

这里传递的语法,是通过<MyContext.Provider>组件携带value属性进行向下传递的,那么接收的语法是通过<MyContext.Consumer>组件。

也可以定义一个静态方法static contextType = MyContext,这样就可以在逻辑中通过this.context来拿到同样的值。

父传子:父组件的state作为子组件的props,当父组件的state改变,子组件的props也跟着改变

祖孙,需要一层层传递,组件层级嵌套到比较深,可以使用上下文getChildContext来传递信息,这样在不需要将函数一层层往下传,任何一层的子级都可以通过this.context直接访问

 

子传父:父组件的函数作为子组件的props,子组件调用父组件的函数来改变state

兄弟:订阅和发布机制PubSub

发布

import Pubsub from 'pubsub-js'
export const pubsubID = 'd806a360-21aa-406e-9e5f-7f375087514f'  // pubsub token
Pubsub.publish(StationStatisticsID, data)
// 或
PubSub.publishSync(StationStatisticsID, data)
PubSub.publish("订阅的消息名称",传递的数据)

订阅


import Pubsub from 'pubsub-js'
import {pubsubID} from 'xxx'

componentDidMount() {
	/**
	* 订阅
	*/
	pubsub = Pubsub.subscribe(pubsubID, (msg, data) => {
		console.log(msg) // 这里将会输出对应设置的 pubsubID
		console.log(data) // 这里将会输出对应设置的参数
	})
}
componentWillUnmount() {
	/**
	* 取消指定的订阅
	*/
	Pubsub.unsubscribe(pubsub)
	/**
	* 取消全部订阅
	*/
	PubSub.clearAllSubscriptions()
}
PubSub.subscribe("订阅的消息名称",回调函数,第一个参数是消息名称,可以使用_来占位,第二个是传递的数据
        })

顶层组件通信:

只是单纯的进行视图的渲染,这时候用回调,context就行,没必要用redux,用了反而影响开发速度。

redux相当于在顶层组件之上又加了一个组件

redux是一个专门用于做状态管理JS(不是react插件库)

action

  1. 动作的对象
  2. 包含2个属性
    1. type:标识属性, 值为字符串, 唯一, 必要属性
    2. data:数据属性, 值类型任意, 可选属性
  3. 例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }


 

reducer

  1. 用于初始化状态、加工状态。
  2. 加工时,根据旧的stateaction 产生新的state纯函数

store

  1. stateactionreducer联系在一起的对象
  2. getState(): 得到state
    1. dispatch(action): 分发action, 触发reducer调用, 产生新的state
    2. subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

相关API

  1. Provider:让所有组件都可以得到state数据

  1. connect:用于包装 UI 组件生成容器组件

mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性

mapDispatchToProps:将分发action的函数转换为UI组件的标签属性

组件之间的信息还可以通过全局事件来传递。不同页面可以通过参数传递数据,下个页面可以用location.param来获取。

事件

 event.target得到发生事件的Dom元素对象

<input onChange={this.saveName} type = "text" name ="username"/>

saveName = (event) =>{
            this.setState({name:event.target.value});
        }

首先React中的事件都是采用事件委托的形式,所有的事件都挂载到组件容器上,其次event对象是合成处理过的。一般情况下这些都是内部完成的,我们在使用的时候并不会有什么影响,作为了解即可。

事件中this的处理

在事件中最重要的就是处理this指向问题了,这里我们推荐采用面向对象中的public class fields语法。

 class Welcome extends React.Component {
    handleClick = (ev) => {  //推荐 public class fields语法,箭头函数不会创建自己的                                      this,它只会从自己的作用域链的上一层继承 this。
        console.log(this);   //对象
        console.log(ev);
    }
    handleClick(){           //不推荐 要注意修正指向
        console.log(this);   //按钮 
    }
    render(){
        return (
            <div>
                <button onClick={this.handleClick}>点击</button>
                hello world
            </div>
        );
    }
}
let element = (
    <Welcome />
);

事件传参处理

推荐采用函数的高阶方式,具体代码如下:

class Welcome extends React.Component {
    handleClick = (num) => {   // 高阶函数
        return (ev) => {
            console.log(num);
        }
    }
    render(){
        return (
            <div>
                <button onClick={this.handleClick(123)}>点击</button>
                hello world
            </div>
        );
    }
}
let element = (
    <Welcome />
);

非受控组件

现用现取,官方建议尽量少用ref,用多了有一定的效率影响

handleSubmit = (event) => {
            event.preventDefault() //阻止表单提交
            const {username, password} = this//拿到的是form下的username, password结点
            alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
        }

render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>登录</button>

受控组件

将输入维护到state,等需要时再从state取出来

    class Login extends React.Component {

//state最好要初始化状态
        state = {
            username: '', //用户名
            password: '' //密码
        }

//保存用户名到状态中
        saveUsername = (event) => {
this.setState({username: event.target.value})
        }

//保存密码到状态中
        savePassword = (event) => {
this.setState({password: event.target.value})
        }

//表单提交的回调
        handleSubmit = (event) => {
          event.preventDefault() //阻止表单提交
          const {username, password} = this.state//获得的是state下的username,password值
          alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
        }

render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername} type="text" name="username"/>
密码:<input onChange={this.savePassword} type="password" name="password"/>
<button>登录</button>
</form>
            )
        }
    }

//渲染组件
    ReactDOM.render(<Login/>, document.getElementById('test'))

高阶函数

参数  或者  返回 为函数

函数的珂里化

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

  function sum(a){
                     return(b)=>{
                        return (c)=>{
                           return a+b+c
                        }
                     }
                  }

//保存表单数据到状态中
        saveFormData = (dataType, event) => {
this.setState({[dataType]: event.target.value})
        }

用户名:<input onChange={event => this.saveFormData('username', event)} type="text" name="username"/>


//珂里化	
		//返回一个函数
        saveType = (type) =>{
            return (event) => {
                this.setState({[type]:event.target.value});
            }
        }

        //因为事件中必须是一个函数,所以返回的也是一个函数,这样就符合规范了
        render() {
            return (
                <form>
      				<input onChange = {this.saveType('name')} type = "text"/>
                    <button>登录</button>
                </form>
            )
        }
    }

新旧生命周期总结

 React组件中包含一系列钩子函数{生命周期回调函数}

key

//index标识
person:[
    {id:1,name:"张三",age:18},
    {id:2,name:"李四",age:19}
]
//map遍历
this.state.person.map((preson,index)=>{
  return  <li key = {index}>{preson.name}</li>
})


//name标识
<button onClick={this.addObject}>点击增加对象</button>
addObject = () =>{
    let {person} = this.state;
    const p = {id:(person.length+1),name:"王五",age:20};
    this.setState({person:[p,...person]});
}

虚拟dom是没有input.value的值的,只有在真实虚拟dom用户输入了才显示,所以diff时候就算是一样的input

Diff算法

react生成的新虚拟DOM和旧虚拟DOM的比较规则:

  • 如果旧的虚拟DOM中找到了与新虚拟DOM相同的key:

    • 如果内容没有变化,就直接只用之前旧的真实DOM
    • 如果内容发生了变化,就生成新的真实DOM
  • 如果旧的虚拟DOM中没有找到与新虚拟DOM相同的key:

    • 根据数据创建新的真实的DOM,随后渲染到页面上

React脚手架

Webpack的基本使用_sut_uestc的博客-CSDN博客

 eject暴露webpack配置

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <!--%PUBLIC_URL%表示public文件夹的路径-->
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <!--用于开启理想视口,用于移动端页面的适配-->
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!--用于配置浏览器地址栏的颜色(仅支持安卓手机浏览器)-->
    <meta name="theme-color" content="#000000" />
    <!--描述网页信息的-->
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <!--用于指定网页添加到手机主屏幕后的图标(仅仅支持ios)-->
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
 
    <!--应用加壳时候的配置文件 -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
  
    <title>React App</title>
  </head>
  <body>
    <!-- 浏览器不支持JS的运行的时候展现 -->
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

 react脚手架项目结构

public ---- 静态资源文件夹

favicon.icon ------ 网站页签图标

index.html -------- 主页面

logo192.png ------- logo图

logo512.png ------- logo图

manifest.json ----- 应用加壳的配置文件

robots.txt -------- 爬虫协议文件

src ---- 源码文件夹

App.css -------- App组件的样式

App.js --------- App组件(首字母大写的是组件)

App.test.js ---- 用于给App做测试

index.css ------ 样式

index.js ------- 入口文件

logo.svg ------- logo图

reportWebVitals.js

--- 页面性能分析文件(需要web-vitals库的支持)

setupTests.js

---- 组件单元测试的文件(需要jest-dom库的支持)

 webpack-dev-server帮助本地内置服务器开启,所以不能关闭cmd

React.Component

等价于 将React中的Component取出来

const {Component}=React

//module.js

//定义了一个React对象
const React={a:1,b:2}

//暴露
export class Component{

}
//追加一个Component属性
React.Component=Component

//暴露了才能引入,默认暴露只能一个
export default  React


//App.js
import React,{Component} from './module'

由于普通的Js和组件都是js,所一最好组件使用jsx去展示组件(组件首字母大写)

样式模块化

两个组件中样式名称有可能会冲突,这样会根据引入App这个组件的先后顺序,后面的会覆盖前面的,

为了避免这样的样式冲突,我们采用下面的形式:

1.将css文件名修改: hello.css --- >hello.module.css

2.引入并使用的时候改变方式:

import React,{Component}from 'react'
import hello from './hello.module.css'  //引入的时候给一个名称
//变量hello自动转换为对象
export default class Hello extends Component{
    render() {
        return (
            <h1 className={hello.title}>Hello</h1>   //通过大括号进行调用
        )
    }
}

组件的组合使用-TodoList

将html复制到jsx时

注意 改缩进+标签闭合+

 

 ../返回到上级目录

回调函数:被当做参数使用的函数

js 彻底理解回调函数_dkvirus的博客-CSDN博客_js回调函数

指定事件回调时,传参时调用得返回一个函数,所以要写成高阶函数

onXxx类事件,得对应函数

脚手架配置代理

React本身只关注与页面,并不包含发送ajax请求的代码,所以一般都是集成第三方的一些库,或者自己进行封装。

推荐使用axios。

在使用的过程中很有可能会出现跨域的问题,这样就应该配置代理。

所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port), 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域 

方法一

在package.json中追加如下配置

"proxy":"请求的地址"      "proxy":"http://localhost:5000"  

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

方法二

1.创建代理配置文件

在src下创建配置文件:src/setupProxy.js
  1. 2.编写setupProxy.js配置具体代理规则:

    const proxy = require('http-proxy-middleware')
    
    module.exports = function(app) {
      app.use(
        proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
          target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
          changeOrigin: true, //控制服务器接收到的请求头中host字段的值
          /*
          	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
          	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
          	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
          */
          pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
        }),
        proxy('/api2', { 
          target: 'http://localhost:5001',
          changeOrigin: true,
          pathRewrite: {'^/api2': ''}
        })
      )
    }

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

React路由

SPA

单页Web应用(single page web application,SPA)。

整个应用只有一个完整的页面。

点击页面中的链接不会刷新页面,只会做页面的局部更新。

数据都需要通过ajax请求获取,并在前端异步展现

React 路由详解(超详细详解)_江北阳小皮~的博客-CSDN博客_react路由

react-router-dom v6 使用_别样红。的博客-CSDN博客_react router v6

Redirect V6 版本已经废弃,需要导入Hook钩子Navigate ,
<Routes>
{/* exact =true 开启路由精准匹配={true}可以省略 尽量不开启严格匹配*/}
<Route exact path="/about" element={<About/>}/>
<Route exact={true} path="/home" element = {<Home/>} />
<Route path="*" element={<Navigate to ="/about" />}/>

</Routes>

Hooks

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

Hook 是一个特殊的函数,它可以让你“钩入” React 的特性。例如,useState 是允许你在 React 函数组件中添加 state 的 Hook。

let { useState } = React;
let Welcome = (props) => {
    //count的初始值0,设置count的函数
    const [count, setCount] = useState(0);
    const handleClick = () => {
        setCount(count + 1)       
    }
    return (
        <div>
            <button onClick={handleClick}>点击</button>
            <div>hello world, { count }</div>
        </div>
    );
}

面试常问

Angular,Vue,React

Angular

框架比较成熟完整,过于庞大,上手难;

指令以ng-xxx开头

Vue

它是一个轻量级框架,其核心库只关注视图层,简单小巧、易学易上手;

指令以v-xxx开头; 个人维护项目; 适合于移动端开发; 

使用DOM模板。

中心思想:一切都是组件,组件实例之间可以嵌套;

React

依赖虚拟DOM;

采用特殊的JSX语法;

中心思想:一切都是组件,组件实例之间可以嵌套;

参考链接

尚硅谷React技术全家桶全套完整版(零基础入门到精通/男神天禹老师亲授)_哔哩哔哩_bilibili

GitHub - xzlaptt/React: React学习

Logo

前往低代码交流专区

更多推荐