宠物网站建设规划书,没有做icp备案的网站,哈尔滨网站建设丿薇,网站建设公司主要一、准备工作 本文略长#xff0c;建议耐心读完#xff0c;每一节的内容与上一节的内容存在关联#xff0c;最好跟着案例过一遍#xff0c;加深记忆。 1.1 创建项目
第一步#xff0c;执行下面的命令来创建一个 React 项目。
npx create-react-app react-example
cd rea…一、准备工作 本文略长建议耐心读完每一节的内容与上一节的内容存在关联最好跟着案例过一遍加深记忆。 1.1 创建项目
第一步执行下面的命令来创建一个 React 项目。
npx create-react-app react-example
cd react-example第二步安装依赖运行项目
yarn install 或 npm install
yarn start 或 npm run start1.2 项目结构
如图
1.3 初始化
将 src/index.js 的默认代码删掉保留下面这部分。
import React from react;
import ReactDOM from react-dom/client;
import ./index.css;
const root ReactDOM.createRoot(document.getElementById(root));
const App () {return divHello/div
}
root.render(App /);现在项目看起来就像这样一个简单的 Hello。
二、React 的基本用法 如果你还不熟悉 React 的基础语法可以阅读我前面写的 React 工作日常语法。 1.1 输出 Hello, world
第一步肯定是要先来句 Hello,world!
import React from react;
import ReactDOM from react-dom/client;
import ./index.css;const root ReactDOM.createRoot(document.getElementById(root));
const App () {return divHello, world!/div // 改动点
}
root.render(App /);1.2 组件的使用 组件可以由函数或者类来进行创建像上面的 App 函数就是一个组件里面可以混合写 HTML JS 代码React 会自动帮我们解析这些语法像这种写法就叫就 JSX 。 下面我将以函数的方式来定义组件作为案例类组件在讲生命周期时做演示。
第一步随便定义一个叫 List 的组件内容随便写比如 Hi List
const List () {return divHi List./div
}第二步引入 List目前入口只有 App很明显只能放这里。
const App () {return divHello, world! /List /div
}效果
1.3 组件嵌套组件 像 App 组件嵌套 List 组件我们管这叫父子组件App 是父组件 List 是子组件当 List 套上 Item 组件时那 Item 的父组件就是 List而祖父组件是 App 以此类推。 现在我们来给 List 嵌套一个 Item 组件。
const List () {// 多行时要用 () 包裹return (ulItem/ // 引入/ul)
}
const Item () {return liitem1/li
}效果
别忘了还可以写 JSX 的现在试试用 Array.map 循环多个 Item 组件。
const List () {return (ul{[1, 2, 3, 4].map((num) Item(num) )}/ul)
}
const Item (num) {return li key{num}item{num}/li
}在看效果前思考下上面代码具体会发生哪些变化
Item 函数组件多了个 num 参数我们用它来接受并用 {num} 括号包括起来进行访问Item/ 的调用方式变成 Item(num) 因为我们要将 num 值传递给 Item 函数所以我们又发现了一个特性在不传参的情况下直接声明 Item/ 是会自动触发函数的。循环组件时key 属性得带上且是唯一的。
效果
1.3 组件绑定点击事件
干巴巴的列表没有交互行为怎么能行呢 现在我们就来定义一个事件当点击某个 Item 时提示对应的 num 数值代码如下
const onClickItem (num) {alert(num);
}
const Item (num) {return li key{num} onClick{() onClickItem(num)}item{num}/li
}解释
点击事件用 onClick 驼峰形式来表示其它事件也是类似的比如 onFocus, onMouse 等。onClick{() 函数} 花括号返回一个箭头函数函数里面就是返回我们定义的函数后面点击时会触发。
不用怀疑语法是否有问题连 JSX 语法都有了你还在乎这个
效果
1.5 响应式数据
光弹窗没啥用啊要不点击后直接变更 Item 的数据如何 问题是… 怎么变更直接将 num xxx熟悉 JS 的朋友应该知道当 num 参数传递的是【基本类型】时只是一个副本更改后对原来的 num 是无效的。 就算有效你又如何将视图中的 num 也发生变化呢 看来还是得借助 React 提供的语法了。 唉没办法学吧~ 第一步先将原来的 [1,2,3,4] 以 useState 提供的函数来定义并导出 arr 变量数组如图 效果还是与原来一样这里就不贴图了。 第二步在 arr 后面再声明一个 setArr 函数名字随便定义但一般用 set 开头作为规范好点表示用来变更 arr 数据的。
const List () {const [arr, setArr] useState([1, 2, 3, 4])
// ...省略第三步触发 setArr 函数更改 arr 数组里的数据来达到我们想要的视图变更效果 由于 setArr 是在 List 函数组件里定义的其它函数无法直接访问得通过传参的方式带过去、一直传到 onClickItem 中有点绕将就一下吧相信你不介意的/逃。
这里我们重点关注这一段代码
setArr(state {const arr [...state];arr[1] 1000;return arr;
})当点击后将 arr[1] 改为 1000并将新的 arr 返回出去效果如图 你可能注意到state 就是原先的 arr然后我们用扩展符号将它拷贝一份到新的 arr 中为什么要这样做呢直接 state[1] 1000 再 return 出去不行吗多此一举嘛这不是。
是可以这样做数据也会发生变化了但视图不会发生变化因为 React 明确规定state 是不可变数据你不能直接改变它而是要用一份副本去改变。
为什么 state 要坚持不可变原则呢官方也说了当你要实现一个撤销恢复的功能时就没辙了或者说实现起来更复杂React 这时要是还允许你去那岂不是有点不地道了这不跟吃了上顿不考虑下顿的道理嘛。
小结
useState 可以声明响应式数据。state 数据不可变要用副本代替遵循不可变原则。
1.6 什么是钩子函数 Hook Function
其实我们已经用过钩子函数了上面的 useState 就是一个钩子函数React 内置了许多的钩子比如 useEffect, useRef 等这里就不一一介绍只需明白以 use 为开头的均属于钩子函数即可。
我们也可以自定义钩子问题来了在什么样的场景下需要呢里面写啥呢 这个其实没有唯一答案每个人对理解钩子的程度是不同的导致有千千万万种类型的钩子。
我个人更喜欢将它归为处理脏活的一类函数更通俗点来说是用来处理响应式数据的比如有个奖品业务的功能模块针对这个模块可能有验证奖品配置的逻辑那么我就会给这个奖品加上几个 hook 函数以便后续方便调用。
const usePrize () {const verifyPrizeConfig (state) {// Do something ...}const resetPrizeConfig (state) {// Do something ...}return [verifyPrizeConfig,resetPrizeConfig,]
}
const [ verifyPrizeConfig ] usePrize();1.7 组件的生命周期
每个组件渲染时React 会逐步按顺序触发一些内置函数这些被称为”生命周期函数“我们可以根据不同周期函数来做一些业务处理比如我想在组件渲染前先请求接口得到数据。
这里需要注意函数组件是没有命周期函数的只有类组件才有既然这样我们将 App 这个组件变成类组件即可其它保持不变谁规定类组件里面就不能嵌套函数组件
第一步将 App 函数组件改为类组件如下
// 源 App 函数组件
// const App () {
// return divHello, world! List/ /div
// }
// 新 APP 类组件
class App extends React.Component {constructor(props) {super(props);}render() {return divHello, world! List / /div}
}第二步添加生命周期函数这里用最常见的 componentDidMount 函数表示组件第一次渲染时提前触发。
class App extends React.Component {// 省略componentDidMount() {// Do something...alert(Init)}
}1.8 函数组件模拟生命周期
你可能想这不公平函数组件凭啥没有周期函数别急React 提供的钩子函数 useEffect 就派上用场了该钩子完全可以模拟生命周期的三大核心函数
componentDidMount() {} 组件第一次渲染时就刚刚用到的
componentDidUpdate() {} 组件数据更新时state 更新
componentWillUnMount() {} 组件销毁时使用方式也很简单这里以 List 组件作为案例不能忘记我们的老伙伴~
第一步模拟 componentDidMount如下
import { useState, useEffect } from react;
const List () {const [arr, setArr] useState([1, 2, 3, 4]);// 模拟 componentDidMountuseEffect(() {alert(List init) });return (ul{arr.map((num) Item(num, setArr) )}/ul)
}第二步模拟 componentDidUpdate在 useEffect 的第二个参数监听 state
// ... 省略
const [arr, setArr] useState([1, 2, 3, 4])useEffect(() {alert(List init)}, [arr]) // componentDidUpdate当点击更新数据时这个函数就会再次触发如图
第三步模拟 componentWillUnMount在 useEffect 里面 return 一个函数即可。
const [arr, setArr] useState([1, 2, 3, 4])useEffect(() {alert(List init)return () { // componentWillUnMountalert(List destroyed!);};}, [arr])componentWillUnMount 什么时候触发呢实际上每当 state 更新时组件会重新渲染一次这属于销毁行为因此当点击更新数据后会先触发 componentWillUnMount 再触发 componentDidUpdate 。 效果 三、React-router-dom
1.1 什么是 React-router-dom
一个页面怎么够用呢现在我们想要通过点击 item 进入另一个页面且保持页面不刷新这里便可以用 React 提供的插件 React-router-dom 俗称路由来实现。
1.2 React-router-dom 和 React-router 版本的区别
React-router-dom 是基于 React-router 改造的新版本现在大家常用的版本是 React-router-dom 本案例将用 React-router-dom 来作为演示。
1.3 React-router-dom 的使用
第一步下载 react-router-dom。
yarn add react-router-dom第二步在 src/index.js 中引入。
import {createBrowserRouter,RouterProvider,
} from react-router-dom;第三步将我们之前的 App 组件引入方式重新改造一下
// 旧代码
// root.render(App /);
// 新代码
const router createBrowserRouter([{path: /,element: App/,},
]);root.render(RouterProvider router{router} /);现在效果和原来没区别这里解释下步骤
将原来 root.render 里的 App 替换成 RouterProvider 组件在 createBrowserRoute 里面的 element 引入 App。path 路由当访问 / 根目录时才会渲染 App 组件。
第四步新建 AppDetail 组件并挂载到 /detail 路由上。
const AppDetail () {return h1Detail data/h1
}
const router createBrowserRouter([{path: /,element: App/,},// 挂载到 /detail 路由{path: /detail,element: AppDetail/,},
]);第五步访问 /detail 看看效果 第六步以点击跳转的方式访问 /detail这里用到 router 提供的 Link 标签。
import {createBrowserRouter,RouterProvider,Link,
} from react-router-dom;
const Item (num, setArr) {return (li key{num} onClick{() onClickItem(num, setArr)}Link to/detailitem{num}/Link/li)
}效果
react-router-dom 就这么简单至于其它 API 的使用可以参考文档这里不过多讲解。
四、React-redux
1.1 什么是 React-redux
React-redux 可以用来管理全局状态 state 的工具使得组件之间可以访问同一份 state。
1.2 什么时候用 React-redux
当多个组件重复使用同一份 state 时就可以考虑用 redux 将它提升到全局中以便于维护。
1.3 React-redux 下载配置
第一步下载 react-redux这里官方还提供了一个工具包 reduxjs/toolkit里面包含了 react-redux 所有功能以及内置一些其它功能是官方极力推荐的这两个一起下载。
yarn add react-redux reduxjs/toolkit第二步在 src 下新建 store/index.js 文件负责管理全局 state初始化内容如下
// ./src/store/index.js
import { configureStore, createSlice } from reduxjs/toolkit
const userSlice createSlice({name: User, // name 必填的当前作用域的标识符可以理解为 nameSpace 命名空间否则页面上无法正常展示。initialState: { // 声明 state 的地方},reducers: { // 声明 reducer 函数的地方}
});
// 将 store 导出去。
const store configureStore({reducer: userSlice.reducer
})export default store;
第三步在 src/index.js 中引入 store
import store from ./store/index;
import { Provider } from react-redux
root.render(Provider store{store}RouterProvider router{router} //Provider
);解释
从 store.js 中引入 store 对象。从 redux 中引入 Provider 组件并将原先的 RouterProvider 组件包裹起来。将 store 作为参数传递给 Provider 组件。
目前来讲页面跟原来没有区别但是现在我们多了 redux 的功能何乐而不为呢。
1.4 什么是 Reducer
Reducer 与 Vuex 中的 mutations 差不多同一个意思里面专门定义一些处理 state 的函数reducer 主要接受一个 state 和一个 action 根据这两个参数处理相关逻辑然后返回新的 state 遵循前面所说的“不可变原则”。
1.5 什么是 dispatch(action)
dispatch 是用来调用 reducer 函数的。action 是 dispatch 调用 reducer 函数时要传递的一个描述对象好让 reducer 知道该干什么事。该描述对象总共就俩参数type/payloadtype 是调用 reducer 的函数名payload 是我们要传参的数据给 reducer 接受用的。
1.6 使用
理解 reducer/dispatch/action 三大核心概念之后我们来开始使用
第一步在 initialState 对象中定义全局响应式数据。
// 省略...
initialState: {// 新增user: {name: Jack,desc: Hello,world!}
},第二步新建 User 组件该组件用来访问上面声明的响应式数据并挂载到 App 和 AppDetail 中渲染代码如下
// ./src/index.js
// ...省略
import { Provider, useSelector } from react-redux
// 新增
const User () {// 用 redux 提供的钩子来获取 stateconst user useSelector(state state.user); return (divspan{user.name}/spanspan says: {user.desc}/span/div)
}// ./src/index.js
// ...省略
class App extends React.Component {// ...省略render() {return (divUser / // 新增挂载 UserList / /div)}
}
const AppDetail () {return (h1Detail dataUser / // 新增挂载 User/h1)
}现在的效果Jack says: Hello,world!
第三步声明 reducer 函数来变更 state 数据。
// .src/store/index.js
// ...省略
reducers: { // 声明 reducer 函数的地方// 新增changeUserInfo(state, action) {const { payload } action;switch(payload.state) {case name:return {...state,user: {...state.user,name: 杰克,}}case desc:return {...state,user: {...state.user,desc: 你好,世界!,}}default:return state;}}}changeUserInfo 函数解释
根据 payload.state 即我们准备传参的数据来变更用户名 name 还是描述 desc遵循不破坏 state 原则这里我们用扩展符来合并。
第四步使用 dispatch(action) 来触发 reducer完成变更效果。
// ./src/index.js
import { Provider, useSelector, useDispatch } from react-redux
const User () {const user useSelector(state state.user);const dispatch useDispatch(); // 引入触发 reducer 的钩子return (divspan{user.name}/spanspan says: {user.desc}/span// 以下是新增的button onClick{() dispatch({type: User/changeUserInfo,payload: {state: name}})}更换名字/buttonbutton onClick{() dispatch({type: User/changeUserInfo,payload: {state: desc}})}更换描述/button/div)
}现在来看看总体效果
五、总结
不知不觉我们已经用到了 React-dom React-router React-redux
root.render( // react-domProvider store{store} // react-reduxRouterProvider router{router} / // react-router-dom/Provider
);恭喜你已成功入门 React 全家桶剩下就交给实践的时间来帮助我们熟能生巧。
有问题欢迎指出
完 案例已放在 github 上