create-react-app 脚手架快速搭建 react 项目
1、安装 create-react-app
npm install -g create-react-app // Windows
sudo npm install -g create-react-app // Linux
2、检测 create-react-app 是否安装成功
create-react-app -V // 注意:V 是大写的
3、创建 react 项目
npx create-react-app <项目名称>// 注意:名字不能包含大写字母。我的是 my-app。
cd my-app
npm start
项目结构介绍
app.js => app.vue
index.js => main.js
样式和组件是分开的,有专门的css文件
react组件分类
函数组件(相当于vue3组合式api)
组件名不大写就会认为是一个方法
function App(){
// 函数组件的组件名 首字母定要大写
function FnHello(){
return(<div>hello</div>)
}
return (
<FnHello></FnHello>
)
}
class组件
class App extends React.Component {
render() {
return <div>123</div>;
}
}
export default App;
react不一定要写jsx,也可以直接通过react.createElement来创建dom元素,可以在js文件下写jsx,也可以直接把文件名改成jsx,最终页面上现实的是react-element对象
jsx中渲染不同内容
类组件响应式数据的定义
1.响应式数据定义在state属性中
2.修改值需要通过setState,setstate是通过钱合并来修改数据,调用setstate会触发更新,修改state不会触发更新
3.因为setstate是异步的,我们要获取修改后的值要在setstate的第二个参数中获取
特性:
1.多次修改会合并为一次,统一更新
2.setstate返回会触发更新,重复修改同样的值也会更新
3.一定不要在render中设置setstate,PureComponent
方法一般是写成箭头函数,或者通过调用时用bind制定this
修改数组或对象时要先解除引用,通过展开运算符等方式
react表单绑定
原生表单获取输入值,可以监听input或者是change事件,获取e.target.value
要设置表单的值,通常设置value属性,选择框是checked属性
class App extends React.Component {
state = {
inputValue: "",
checkedValue: ["c1"],
};
handleCheckBox = (e) => {
let arr = [...this.state.checkedValue];
if (e.target.checked) {
arr.push(e.target.value);
} else {
arr.splice(arr.indexOf(e.target.value), 1);
}
this.setState({
checkedValue: arr,
});
};
render() {
return (
<div>
{this.state.inputValue}
<input
value={this.state.inputValue}
onInput={(e) => {
this.setState({
inputValue: e.target.value,
});
}}
/>
<br></br>
{this.state.checkedValue}
<input
type="checkbox"
value="c1"
checked={this.state.checkedValue.includes("c1")}
onChange={this.handleCheckBox}
/>
选项一
<input
type="checkbox"
value="c2"
checked={this.state.checkedValue.includes("c2")}
onChange={this.handleCheckBox}
/>
选项二
<input
type="checkbox"
value="c3"
checked={this.state.checkedValue.includes("c3")}
onChange={this.handleCheckBox}
/>
选项三
</div>
);
}
}
props和组件间传值,插槽
一切写在组件上的属性和子节点都属于props,父子传值,插槽都是基于props
props的类型验证和静态值
插槽的本质
在jsx的加持下,可以吧html像普通字符串一样传递,插槽只要直接作为props传入就可以了
class App extends React.Component {
state = {
msg: 123,
};
render() {
return (
<div>
<Son a="123" b={<div>我是b插槽</div>}>
<div>哈哈哈</div>
<span className="spancolor">123</span>
</Son>
</div>
);
}
}
class Son extends React.PureComponent {
state = {};
render() {
console.log(this.props);
return (
<div>
// 类似具名插槽
{this.props.b}
123{this.props.msg}
// 这也是插槽
{this.props.children}
</div>
);
}
}
子传父
兄弟传值
子1 =》 父 =》 子2
类型验证借助proptypes库
npm install proptypes
示例
class App extends React.Component {
state = {
msg: "",
};
changeMsg = (msg) => {
this.setState({
msg: msg,
});
};
render() {
return (
<div>
msg: {this.state.msg}
<br></br>
<Son
a="123"
scopeSlot={(scope) => {
return <strong>{scope}</strong>;
}}
b={<div>我是b插槽</div>}
changeMsg={this.changeMsg}
>
<div>哈哈哈</div>
<span className="spancolor">123</span>
</Son>
</div>
);
}
}
export default App;
class Son extends React.PureComponent {
state = {};
render() {
console.log(this.props);
return (
<div>
{this.props.b}
123{this.props.msg}
{this.props.children}
{this.props.scopeSlot("我是子组件传过来的值")}
<br></br>
子传父
<button
onClick={() => {
this.props.changeMsg("hello father");
}}
>
子传父传值
</button>
</div>
);
}
}
Son.propTypes = {
msg: proptypes.string,
};
Son.defaultProps = {
msg: "i am son msg",
};
样式操作
这样引入样式是全局生效的,文件名要改成 App.module.css 才是只在对应组件内生效
// 全局生效
import "./App.css";
下载classnames库可以实现动态绑定类名
npm i classnames
import classNames from "classnames";
// 相当于app.vue
class App extends React.Component {
state = {
hasson1: false,
};
render() {
return (
<div>
<span
className={classNames({
spancolor: true,
spanstyle: this.state.hasson1,
})}
style={{ fontSize: "30px" }}
>
hello
</span>
<button
onClick={() => {
this.setState({
hasson1: !this.state.hasson1,
});
}}
>
修改classname
</button>
<Son></Son>
</div>
);
}
}
如果要配合模块化,要引入classnames/bind文件
import sonStyle from "./Son.module.css";
// 如果要配合模块化,要引入classnames/bind文件
import classNames from "classnames/bind";
let bindClassName = classNames.bind(sonStyle);
console.log("sss", sonStyle);
class Son extends React.PureComponent {
state = {};
render() {
return (
<div>
<span
className={bindClassName({
son: true,
son1: true,
})}
>
我是儿子
</span>
</div>
);
}
}
生命周期
函数组件和hook
下面例举的是常用的hook,也可以hook自定义
hook只能用于函数组件,用在其他地方会报错
函数组件
1.没有生命周期函数
2.没有this
3.
useState
生命响应式变量
useEffect
模拟生命周期,或者是监听属性
useMemo
类似计算属性,让一个属性不再被重复创建
useCallback
让一个方法不再被重复创建,用于性能优化
useContext
相当于provirde/inject
export let Context1 = React.createContext();
import Son from "./Son";
function App() {
// return 的内容相当于组件的html
return (
<div>
<Context1.Provider value="i am a context value">
<Son fatherMsg="i am father"></Son>
</Context1.Provider>
</div>
);
}
// son.js
import { useContext } from "react";
import { Context1 } from "./App";
function Son() {
let value = useContext(Context1);
return <div>{value}</div>;
}
export default Son;
高阶组件Hoc
单纯的逻辑复用,相当于mixin和自定义指令
使用:使用高阶组件实现PureComponent作用(父组件更新数据,与子组件无关的数据,子组件不更新)
react性能优化
1.父组件数据更新导致子组件更新
vue可以进行依赖收集,做到了最小范围的更新;react要优化是时间切片,先计算一部分更新,然后让渡给渲染进程,在进行下一部分的更新,这样就不会导致页面长时间白屏了
fiber(数据结构):为了能支持时间切片,每一个组件会被转化为一个fiber结构的对象,组成一个个单元,fiber让我们有回去上一次中断计算进度的能力
(1)避免state相同的值产生更新:
类组件可以使用purecomponent;函数组件本身就会判断
(2)props更新导致
类组件可以使用purecomponent;
函数组件React.memo包裹子组件,使用useCallback包裹方法,useMemo包裹对象作为props传递
React-router
react-router 服务端渲染
react-route-dom 浏览器渲染
react-router-native react-native 混合开发使用
其他路由组件:
navigate 路由重定向
outlet 嵌套路由的子路由显示处
获取路由参数
location通常用来获取当前路径
import { useParams, useSearchParams, useLocation } from "react-router-dom";
function Page1() {
let [searchParams, setSearchParams] = useSearchParams();
// console.log(searchParams.get("b"));
let params = useParams();
// console.log(params);
let location = useLocation();
// console.log(location);
return (
<div>
hello i am page1
<button
onClick={() => {
setSearchParams({
b: 666,
a: 888,
});
}}
>
修改query参数的值
</button>
</div>
);
}
export default Page1;
js控制跳转地址
v6- useNavigate创建跳转方法
v5- this.props.history.push()
let nav = useNavigate();
return (
<div>
<button
onClick={() => {
nav("/page2", {
state: {
state1: "123",
},
});
}}
>
跳转到page2
</button>
权限控制
通过navigate,直接不生成
异步路由
let lazyPage2 = lazy(() => {
return import("./Page2");
});
function App() {
return (
<div>
<Link to="/page1">page1</Link>
<Suspense fallback={<h2>加载中</h2>}>
<Routes>
<Route path="/page1/:id" Component={Page1}></Route>
<Route path="/page2" Component={lazyPage2}></Route>
</Routes>
</Suspense>
</div>
);
}
react状态管理
1.创建全局数据管理对象
2.通过某个组件连接到react
redux创建仓库
编写reducer方法
let defaultState = {
mes: "hello",
};
function mesReducer(state = defaultState, action) {
// 具体修改数据的行为
switch (action.type) {
case "changeMes":
state.mes = action.payload;
// 字后一定要return state,并且接解除引用
return { ...state };
default:
return state;
}
}
export default mesReducer;
给state默认值作为初始数据
判断type修改数据
调用createStore创建仓库
import {
legacy_createStore as createStore,
combineReducersbineReducers,
} from "redux";
import mesReducer from "./mesReducer";
import numReducer from "./numReducer";
let reducer = combineReducersbineReducers({
mesReducer,
numReducer,
});
let store = createStore(reducer);
export default store;
react-redux
通过provider组件,把store关联到目标项目
import { Provider } from "react-redux";
import store from "./store";
// 相当于main.js 把项目挂在到指定的dom
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
// react的严格模式 可去掉
// <React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
// </React.StrictMode>
);
利用connet连接到某组件
编写映射关系
import { connect } from "react-redux";
function App(props) {
console.log(props);
return (
<div>
{props.mes}
<button
onClick={() => {
props.changeMes();
console.log(props);
}}
>
修改msg
</button>
</div>
);
}
// connet第一个参数state的映射,你要把那些state映射到该组件的props中
// connet第二个参数方法映射,你要给props加入什么方法
export default connect(
(state) => {
// 一定要return 一个对象
return {
// 分模块就是先取模块,再取值
mes: state.mesReducer.mes,
};
},
(dispatch) => {
return {
changeMes() {
dispatch({
type: "changeMes",
payload: "888",
});
},
};
}
)(App);
现在常用的是@reduxjs/toolkit
创建切片
整合切片,创建仓库
react-redux连接到react
npm install @reduxjs/toolkit
vue和react的区别
1.更新方式不同
vue可以进行依赖收集,做到了最小范围的更新;
react父组件更新会让子组件也更新,所以要优化做时间切片(先计算一部分更新,然后让渡给渲染进程,在进行下一部分的更新,这样就不会导致页面长时间白屏了)
2.全局插件使用方式不同
react中没有vue那样的vue.use方法,
react使用一个插件或者是库,都是引入一个组件,然后把要使用该插件的地方包起来