React 学习笔记
- Hello World
- JSX (JavaScript XML) 语法规则
- JavaScript 语法
- 函数组件、类组件 & 属性 props
-
- 生命周期函数(不全) & 状态 state
- 事件处理
- refs
- 受控组件、非受控组件 & 高阶函数、函数的柯里化
- 生命周期
- 列表 & Key
- NPM node 包管理器
- React 脚手架 create-react-app
- 网络请求库及代理
- 网络代理
- 1. Ajax (基于XHR XMLHttpRequest)
- 2. Axios
- 3. fetch (window 的新 api,为取代ajax,promise风格)
- pubsub-js 消息发布订阅库
- React 路由
- 什么是路由?
- 路由分类
- react-router-dom
- **路由基本使用**
- **路由组件和一般组件**
- **多级路径刷新页面,样式丢失问题**
- **路由的严格匹配与模糊匹配**
- 嵌套路由(多级路由)使用
- JavaScript CDN 库
- 参考
Hello World
<!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>Hello, React</title>
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
const element = <h1>Hello, World!</h1>;
const container = document.getElementById('root');
ReactDOM.createRoot(container).render(element);
</script>
</body>
</html>
JSX (JavaScript XML) 语法规则
1. 定义虚拟D0M时,不要写引号。
2. 标签中混入JS表达式时要用}。
3. 样式的类名指定不要用class,要用className。
4. 内联样式,要用style={key:value}的形式去写。
5. 只有一个根标签
6. 标签必须闭合
7. 标签首字母
1. 若小写字母开头,则将改标签转为htl中同名元素,若htm1中无该标签对应的同名元素,则报错。
2. 若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
JavaScript 语法
https://es6.ruanyifeng.com/#docs/destructuring
-
console.log(2, "@");
-
debugger;
-
JSON
JSON.stringify() 方法将一个 JavaScript 对象或值转换为 JSON 字符串
JSON.parse () 方法用来解析 JSON 字符串,构造由字符串描述的 JavaScript 值或对象。
JSON.stringify({name:"zhangsan",age:18}) // '{"name":"zhangsan","age":18}'
JSON.parse('{"name":"zhangsan","age":18}') // {name: 'zhangsan', age: 18}
-
类中方法默认开启局部严格模式,所以类中自定义方法中的 this
是 undefined
,而不是类的实例对象!
<script>
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
say() {
console.log(this)
}
}
const p1 = new Person("zhangsan", 13)
p1.say()
const say = p1.say
say()
console.log("----------------")
function fun1() {
console.log(this)
}
function fun2() {
'use strict'
console.log(this)
}
fun1()
fun2()
</script>
-
对象属性的访问方式
- 点属性访问器
对象.属性
:object.property
- 方括号属性访问
对象["属性"]
:object['property']
- 对象解构
const / let { 属性同名的变量名[:别名] } = 对象
:const { property } = object
const app = "appName";
console.log(app);
let obj = {app};
console.log(obj);
let obj2 = {app:app};
console.log(obj2);
let obj3 = {[app]: app};
console.log(obj3);
const { appName } = obj3;
console.log(appName);
const {name: b, age: nianling=5, tall=180, weight} = { name: "zhangsan" }
console.log(b)
console.log(nianling)
console.log(tall)
console.log(weight)
函数组件、类组件 & 属性 props
React 对象三大属性1:props
<body>
<!-- <div id="root"></div> -->
<div id="funId"></div>
<div id="classId"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function FunctionWelcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class ClassWelcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
ReactDOM.createRoot(document.getElementById("funId")).render(<FunctionWelcome name="韩束"/>);
ReactDOM.createRoot(document.getElementById("classId")).render(<ClassWelcome name="克拉斯"/>);
</script>
</body>
组合组件
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
</script>
</body>
生命周期函数(不全) & 状态 state
React 对象三大属性2:state
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({ date: new Date() });
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(<Clock />, document.getElementById('root'));
</script>
</body>
-
组件生命周期函数
componentDidMount
组件已被渲染到 DOM 中后调用componentWillUnmount
组件将卸载时调用
-
state 使用注意事项
-
只能在构造函数或类属性上初始化 state
-
只能使用 this.setState(函数或对象)
更新状态数据,直接更新 state 状态数据无效!
-
state 的更新可能是异步的。出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
this.setState({
counter: this.state.counter + this.props.increment,
});
}
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
this.setState(function (state, props) {
return {
counter: state.counter + props.increment
};
});
-
state 的更新会被合并
当你调用 setState(对象)
的时候,React 会把你提供的对象合并到当前的 state。
-
除了拥有并设置 state 的组件之外,其他组件都无法访问该 state。不过,组件可以选择把它的 state 作为 props 向下传递到它的子组件中。
事件处理
https://zh-hans.reactjs.org/docs/handling-events.html
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。如
onChange={this.saveUsername}
,其含义为:大括号中同 js语法,将大括号中结果(此处结果为一个函数)赋值给onChange,React 负责调用onChange。 - 在 React 中不能通过返回 false 的方式阻止默认行为,必须显式的使用 preventDefault 。
<body>
<a href="#" onclick="console.log(123);">原生js事件,注意浏览器地址栏URL变化</a><br />
<a href="#" onclick="console.log(123);return false;">原生js通过返回false阻止默认事件</a><br />
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function Fun() {
function handleClick(event) {
event.preventDefault();
console.log(event);
}
return (
<div>
<a href="https://www.baidu.com" onClick={(e) => { handleClick }}>React 通过显示调用阻止默认事件函数阻止事件</a><br />
<a href="https://www.baidu.com" onClick={(e) => { event.preventDefault(); console.log(e.target, e) }}>React 通过显示调用阻止默认事件函数阻止事件, e.target是发生事件的DOM元素</a>
</div>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(<Fun />)
</script>
</body>
refs
React 对象三大属性3:DOM 节点引用 refs
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
input4Ref = React.createRef()
input3 = (c) => {
console.log("函数形式", c);
}
render() {
return (
<div>
<input ref="input1" defaultValue="用法1(过时).这是字符串形式ref,效率低" type="text" style={{ width: "50%", color: "#f00" }} name="username" id="uid" /><br />
<input ref={(c) => { this.input2 = c; console.log(c); }} defaultValue="用法2.内联函数形式ref,react会自动调用ref函数,更新过程中会被执行两次,第一次传null,第二次传dom对象" type="text" /><br />
<input ref={this.input3} defaultValue="用法3.回调函数形式的ref,react会自动调用ref函数,更新过程中会被执行两次,第一次传null,第二次传dom对象" type="text" name="username" id="uid" /><br />
<input ref={this.input4Ref} defaultValue="用法4.类绑定的回调,指定回调函数由react创建ref" type="text" name="username" id="uid" /><br />
<input ref={(c) => { this.btn = c }} onClick={(e) => { console.log("不要滥用ref,譬如此按钮的ref可以省略", e.target === this.btn); console.log(1, this.refs.input1); console.log(2, this.input2); console.log(3, this.input3); console.log(4, this.input4Ref.current); this.input4Ref.current.value = "React.createRef"; }} type="button" value="click" />
</div>
);
}
}
ReactDOM.createRoot(document.getElementById("root")).render(<App />)
</script>
</body>
受控组件、非受控组件 & 高阶函数、函数的柯里化
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
class FormApp extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
const { username, password } = this;
alert(`用户名:${username.value},密码:${password.value}`);
}
state = {
username: "",
password: ""
}
saveUsername = (e) => {
this.setState({ username: e.target.value });
}
savePassword = (e) => {
this.setState({ password: e.target.value });
}
handleSubmit2 = (event) => {
event.preventDefault();
alert(`用户名:${this.state.username},密码:${this.state.password}`);
}
saveData = (dataType) => {
return (event) => {
this.setState({ [dataType]: event.target.value });
}
}
saveData2 = (event, dataType) => {
return (event) => {
this.setState({ [dataType]: event.target.value });
}
}
render() {
return (
<div>
<h1>1. 非受控组件,大量使用ref,性能相对低(不太推荐)</h1>
<form method="post" action="http://www.baidu.com" onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username" /><br />
密码:<input ref={c => this.password = c} type="password" name="password" /><br />
{}
<button>登陆</button>
</form><br />
<h1>2. 受控组件(推荐)</h1>
<form method="post" action="http://www.baidu.com" onSubmit={this.handleSubmit2}>
用户名:<input onChange={this.saveUsername} type="text" name="username" /><br />
密码:<input onChange={this.savePassword} type="password" name="password" /><br />
<button>登陆</button>
</form>
<h1>3. 高阶函数(更推荐)</h1>
<form method="post" action="http://www.baidu.com" onSubmit={this.handleSubmit2}>
用户名:<input onChange={this.saveData("username")} type="text" name="username" /><br />
密码:<input onChange={this.saveData("password")} type="password" name="password" /><br />
<button>登陆</button>
</form>
<h1>4. 不使用高阶函数同样实现效果(最推荐)</h1>
<form method="post" action="http://www.baidu.com" onSubmit={this.handleSubmit2}>
用户名:<input onChange={(event)=>{this.setState({"username": event.target.value})}} type="text" name="username" /><br />
密码:<input onChange={(event)=>{this.setState({"password": event.target.value})}} type="password" name="password" /><br />
<button>登陆</button>
</form>
</div>
);
}
}
ReactDOM.createRoot(document.getElementById("root")).render(<FormApp />);
</script>
</body>
生命周期
https://zh-hans.reactjs.org/docs/react-component.html#the-component-lifecycle
生命周期图
注: 图中的 shouldCompnentUpdate
方法返回值为 false
时,即走图中 ×
不再渲染 。加粗样式
列表 & Key
列表的 key 确保唯一不变性
确保key不适用索引,因为含有input元素时倒序增加、增删操作都会产生bug,不仅仅是性能低问题。
如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 元素上,而不是放在 ListItem 组件中的 <li> 元素上。
key 值在兄弟节点之间必须唯一
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
function ListItem(props) {
return <li>{props.value}</li>;
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem key={number.toString()} value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
const numbers = [2, 3, 5, 33, 22, 11];
ReactDOM.createRoot(document.getElementById("root")).render(<NumberList numbers={numbers} />);
</script>
</body>
NPM node 包管理器
npm(“Node 包管理器”)是 JavaScript 运行时 Node.js 的默认程序包管理器。
npm 由两个主要部分组成:
用于发布和下载程序包的 CLI(命令行界面)工具
托管 JavaScript 程序包的 在线存储库
菜鸟教程 nodejs
React 脚手架 create-react-app
-
全局安装
npm i[nstall] -g create-react-app
可另外再安装 yarn:npm i -g yarn
-
切换到想创建工程的目录,创建项目
create-react-app hello-react
-
进入工程目录
cd hello-react
-
启动工程
npm start
或 yarn start
网络请求库及代理
网络代理
因 Ajax 同源策略影响,需配置代理,通过代理间接获取非同源资源。
配置代理方式:
-
package.json
中
"proxy": "http://localhost:8080"
则所有向原本3000端口的请求,3000端口没有的资源都会转发到8080端口访问。
-
通过 http-proxy-middleware
模块代理实现
新建 src/setupProxy.js
文件,内容如下:
const { proxy } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
proxy("/api1", {
target: "http://localhost:8080",
changeOrigin: true,
pathRewrite: { "^/api1": "" },
}),
proxy("/api2", {
target: "http://localhost:8081",
changeOrigin: true,
pathRewrite: { "^/api2": "" },
})
);
};
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/api1", {
target: "http://localhost:8080",
changeOrigin: true,
pathRewrite: { "^/api1": "" },
}),
createProxyMiddleware("/api2", {
target: "http://localhost:8081",
changeOrigin: true,
pathRewrite: { "^/api2": "" },
}),
createProxyMiddleware("/apiGithub", {
target: "https://api.github.com",
changeOrigin: true,
pathRewrite: { "^/apiGithub": "" },
})
);
};
1. Ajax (基于XHR XMLHttpRequest)
https://react.docschina.org/docs/faq-ajax.html
2. Axios
Axios 是一个基于 promise 的网络请求库,可以用于浏览器和 node.js
npm install axios
import React, { Component } from "react";
import axios from "axios";
class Header extends Component {
search = () => {
const keyword = this.keywordElement.value;
console.log(keyword);
this.props.updateState({ isFirst: false, isLoading: true, errorMsg: "" });
axios
.get(`http://localhost:3000/apiGithub/search/users?q=${keyword}`)
.then((response) => {
console.log(response.data);
this.props.updateState({ users: response.data.items });
})
.catch((error) => {
console.log(error);
this.props.updateState({ errorMsg: error.message });
})
.then(() => {
this.props.updateState({ isLoading: false });
});
};
render() {
return (
<div>
<div>Search Github Users</div>
<br />
<input
ref={(c) => (this.keywordElement = c)}
type="text"
name="keyword"
id="keyword"
/>
<button onClick={this.search}>搜索</button>
</div>
);
}
}
export default Header
3. fetch (window 的新 api,为取代ajax,promise风格)
fetch 详解见 MDN
或网友前端阿彬的博文 fetch与XHR(ajax,axios)的区别与优势
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
pubsub-js 消息发布订阅库
github地址
npmjs地址
类似 android 中 EventBus
,基本使用:
import PubSub from 'pubsub-js'
const PubSub = require('pubsub-js');
var mySubscriber = (msg, data)=> {
console.log( msg, data );
};
var token = PubSub.subscribe('MY TOPIC', mySubscriber);
PubSub.publish('MY TOPIC', 'hello world!');
PubSub.publishSync('MY TOPIC', 'hello world!');
PubSub.unsubscribe(token);
PubSub.unsubscribe(mySubscriber);
React 路由
什么是路由?
路由就是一个路径作为key,function或component作为value的映射关系。
路由分类
- 前端路由
- 浏览器路由,value是component,用于展示页面内容。
- 注册路由:
- 工作过程:当浏览器的path变为/test时,当前路由组件就变成Test组件。
- 后端路由
- value是function,用来处理客户端请求。
- 注册路由:router.get(path, function(req,res))
- 工作过程:当node接收到请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据。
react-router-dom
reactrouter 官网 最新版本 V6.4
react-router 中文文档(印记中文)
路由基本使用
- 明确好界面导航区和展示区。
- 导航区的a标签改为Link标签
<Link to='/xxx'>Demo</Link>
。 - 展示区写Route标签进行路径的匹配
<Route path='/xxxx' component={Demo}/>
。 <App/>
的最外层包裹一个<BrowerRouter>
或 <HashRouter>
。Switch
最多只匹配一个路由路径,展示对应路径的的组件。Redirect
一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect
指定的路由。
import {BrowserRouter} from 'react-router-dom' 如`locathost:3000/home`
或
import {HashRouter} from 'react-router-dom' 路径后面有个#,如`locathost:3000/#/home`,警号后面的不会发送给后台,为前台资源
import {Link, NavLink, Route} from 'react-router-dom'
{}
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home" children="Home"/>
{}
<Switch>
<Route path="/about" componen={About}/>
<Route path="/home" component={Home}/>
<Route path="/home" component={Test}/>
<Redirect to="/about"/>
</Switch>
路由组件和一般组件
- 写法不同
- 存放位置不同
- 一般组件:components
- 路由组件:pages
- 接收到的props不同
- 一般组件:写组件标签时传递了什么就能收到什么
- 路由组件:接收到 3 个固定的属性
history:
go: f go(n)
goBack: f goBack()
goForward: f goForward()
push: f push(path, state)
replace: f replace(path, state)
location:
pathname:"/about"
search:""
state:undefined
match:
params:{}
path:"/about"
url:"/about"
多级路径刷新页面,样式丢失问题
- 原因:资源引入使用了相对路径,并使用路由的 BrowerRouter 标签。刷新后,多级 url 路径错误。
- 解决方案:
- 不使用相对路径,要使用绝对路径(相对 public 文件夹),如
/test/a.css
(常用) - 使用
%PUBLIC_URL%
作为前缀,如%PUBLIC_URL%/test/a.css
(常用) - 更换
BrowerRouter
为HashRouter
路由的严格匹配与模糊匹配
- 默认使用的是模糊匹配(即输入的路径必须包含要匹配路径,且前面的顺序要一致)
- 开启严格匹配写法:
<Route exact={true} path="/about" component={About}/>
- **严格匹配不要随便开启,需要再开。**有些时候开启会导致无法继续匹配二级路由问题。
嵌套路由(多级路由)使用
JavaScript CDN 库
- staticfile
- bootcdn
- cdnjs
- jsdelivr
- 75CDN
- 字节跳动静态资源公共库
- cdnjs.loli.net
参考
- MDN
- React 官方中文教程
- ES6 入门教程
- 印记中文(深入挖掘国外前端新领域,为国内 Web 前端开发人员提供优质文档)
- Babel 官网
- 尚硅谷 React 视频教程
- 什么是 npm —— 写给初学者的编程教程
- 浏览器的同源策略机制以及跨域请求
- 跨域不求人,自己动手解决react跨域问题
- Bootstrap
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)