一. 项目概览

- 上面的今日头条项目,源码在课件资料中寻找



二. 项目介绍
- 爱彼迎项目:
- 爱彼迎团队:
- 开发团队国内200多个人
- 经历过数年版本迭代最终完成的一个产品
- 爱彼迎目前的项目
- 已经不针对国内房东、订房进行维护,依然可以订阅国外的房屋
- 爱彼迎已经退出中国市场,不排除有一天国内的网站不能继续访问
- 项目本身确实非常美观、好看
- 爱彼迎团队:
- 项目核心学习、掌握的知识点:
- 对于第一个
React项目,我们的核心是对前面所学知识进行练习、实战 - 掌握
React开发的流程、模式、项目架构,项目中会有很多组件、工具等封装、抽取、复用思想 - 最重要的是学习
React开发的模式和编程的思想,而不是局限于我上课期间所讲的内容,并且大部分样式和布局内容需要大家课下自行完成 - 在这个项目过程中,我会尽量将之前所学的所有知识都运用起来,但是我们不会为了用某个知识而用某个知识
- 课程中会使用我服务器已经获取到的数据,一是国内的数据更好看,二是担心它数据有一天不再维护,三是我对数据已经进行了大量的整理
- 后续我们还会专门学习
React+TypeScript项目实战的内容,React本身非常的灵活,对js本身要求也较高,但是最重要的还是练习
- 对于第一个
三. 项目规范
- 项目规范:项目中有一些开发规范和代码风格
- 文件夹、文件名称统一小写、多个单词以连接符(
-)连接 JS变量名称采用小驼峰标识,常量全部使用大写字母,组件采用大驼峰CSS采用普通CSS和styled-component结合来编写(全局采用普通CSS、局部采用styled-component)- 整个项目不再使用
class组件,统一使用函数式组件,并且全面拥抱Hooks - 所有的函数式组件,为了避免不必要的渲染,全部使用
memo进行包裹 - 组件内部的状态,使用
useState、useReducer;业务数据全部放在redux中管理 - 函数组件内部基本按照如下顺序编写代码:
- 组件内部
state管理 redux的hooks代码- 其他
hooks相关代码(比如自定义hooks) - 其他逻辑代码(网络请求/事件监听/副作用)
- 返回
JSX代码
- 组件内部
redux代码规范如下:redux目前我们学习了两种模式,一种普通方式,一种RTK,在项目实战中尽量两个都用起来,都需要掌握- 每个模块有自己独立的
reducer或者slice,之后合并在一起 redux中会存在共享的状态、从服务器获取到的数据状态
- 网络请求采用
axios- 对
axios进行二次封装 - 所有的模块请求会放到一个请求文件中单独管理
- 对
- 项目使用
AntDesign、MUI(Material UI)- 爱彼迎本身的设计风格更多偏向于
Material UI,但是课程中也会尽量讲到AntDesign的使用方法 - 项目中某些
AntDesign、MUI中的组件会被拿过来使用 - 但是多部分组件还是自己进行编写、封装、实现
- 爱彼迎本身的设计风格更多偏向于
- 其他规范在项目中根据实际情况决定和编写
- 文件夹、文件名称统一小写、多个单词以连接符(
四. 创建React项目
创建项目的方式:
create-react-app项目配置:
- 配置项目的
icon - 配置项目的标题
- 配置
jsconfig.json- 增强
vscode的智能提示 react项目中不能在该文件中写注释- 会报如下错误
Unexpected token / in JSON at position xxx(编译后的行号)- 意思就是:意外的符号
/在JSON位置xxx号
- 增强
- 配置项目的
通过
craco配置别名和less文件:jsconfig.json中配置的别名,只是为了增强vscode的智能提示- 所以必须通过
webpack来配置alias别名,当然react中默认是隐藏了webpack的配置文件的,我们这里采用的是craco来修改webpack配置文件,当然还有一种方式是通过npm run eject命令来暴露webpack配置文件(不介意使用)
js// craco.config.js const path = require('path') const resolve = pathname => path.resolve(__dirname, pathname) const CracoLessPlugin = require('craco-less'); module.exports = { plugins: [ { plugin: CracoLessPlugin, // 下面这些是针对ant design增加的一些配置,如果没用到ant design,可以不配置下面的选项 // options: { // lessLoaderOptions: { // lessOptions: { // modifyVars: { '@primary-color': '#1DA57A' }, // javascriptEnabled: true, // }, // }, // }, }, ], webpack: { "alias": { "@": resolve('src'), "components": resolve('src/components'), "utils": resolve('src/utils') } } };
五. 项目目录结构划分
对项目进行目录结构的划分:

assets:资源文件夹,像img,css等base-ui:多个项目都可能用到的公共ui组件components:公共组件库hooks:存放自定义hookrouter:路由相关文件services:网络请求相关文件store:状态管理相关文件utils:一些工具文件views:视图文件
六. CSS样式的重置
对默认
CSS样式进行重置:npm安装一个normalize.cssshellnpm i normalize.css手动创建一个
reset.css在
css/index.less文件中引入下面文件,index.js入口文件中引入index.less即可- 引入之后,就会在
webpack的依赖图中,到时候打包的时候,webpack就会帮我对引入的文件进行打包


- 引入之后,就会在
默认是无法打包
less文件的,这里需要安装lessshellnpm i less -D
七. 全家桶 – Router配置
- 安装
redux router


八. 全家桶 – Redux状态管理
Redux状态管理的选择:普通方式:目前项目中依然使用率非常高
@reduxjs/toolkit方式:推荐方式, 未来的趋势安装
Redux Toolkit:Redux ToolKit本质上其实是对编写redux做的一层封装,如果想在react中使用,还是需要安装react-redux包才行redux toolkit内部有依赖redux,所以不需要再安装redux了
jsnpm install @reduxjs/toolkit react-redux



九. 网络请求 - axios


十. 项目细节
首页
header部分采用display布局,左右两项采用flex为1,中间一项就会居中svg相当于通过一大堆标签,路径的方式画出来图案,就不是图片了,可以让网站资源更小一些,加快首页的渲染速度怎么引入
svg呢?方式一:
- 将
svg保存成一张图片,如xxx.svg,之后当成图片去引用,如<img src='xxx.svg'/>、background-image: url('xxx.svg')
- 将
方式二:
使用
svg标签,直接嵌入到对应的html标签中,这样做的好处是:svg的内容会随着html文件的下载,跟着一块下载下来,这样后续就不需要再去请求对应的图片,且svg是一个相对比较小的文件我们可以以组件的形式进行嵌套

但是需要注意,在
jsx中嵌套svg,还需要对style中对应的属性转化为js对象
所以我们可以使用一个工具函数来进行转化
js// input: "display: block; font-size: 80px; background-image: url('xxx.jpg')" // output: {display: 'block', fontSize: '80px', backgroundImage: "url('xxx.jpg')"} function styleStrToObject(styleStr) { const obj = {} const s = styleStr.toLowerCase() .replace( /-(.)/g, function (m, g) { return g.toUpperCase(); } ) .replace( /;\s?$/g, "" ) .split( /:|;/g ) for (var i = 0; i < s.length; i += 2) { obj[s[i].replace(/\s/g,"")] = s[i+1].replace(/^\s+|\s+$/g,"") } return obj } export default styleStrToObject如何修改
svg的color,我们可以修改离它最近父元素的color,因为svg会使用离它最近的父元素的color
在
React中,基于webpack环境,动态引入本地图片方式一:
import引入对应的图片style.js文件中
组件中

方式二:
require函数进行包裹,包裹之后意味着是一个js代码style.js文件中
组件中

早期
react中,需要使用的是require函数返回值的default属性,才能拿到图片这些特性都是
webpack的,而不是react的,而vue-loader有做特殊处理,所以可以在.vue文件中使用~assets来获取jsxrequire("xxx/xxx/xxx.jpg").defaultMaterial UI默认使用的是emotion作为样式引擎,如果想要使用styled-components代替,安装命令如下:jsnpm install @mui/material @mui/styled-engine-sc styled-componentshttps://mui.com/zh/material-ui/getting-started/installation/
然后,我们只需要在
webpack配置alias如下:jsmodule.exports = { //... + resolve: { + alias: { + '@mui/styled-engine': '@mui/styled-engine-sc' + }, + }, };https://mui.com/zh/material-ui/guides/styled-engine/
但是按照上面步骤(来自官网)执行可能会遇到一些问题,导致项目报错,比如组件,然后报错:不能从
undefined中读取xxx测试解决办法:可以采取
emotion的方案,然后注释前面设置的别名配置:'@mui/styled-engine': '@mui/styled-engine-sc'那如果
css in js使用styled-components,然后又安装emotion,会不会造成打包文件较大呢?- 不会,因为打包出来的文件都是我们项目中应用到的,最终转化为普通的
css文件
- 不会,因为打包出来的文件都是我们项目中应用到的,最终转化为普通的
无论是
Vuex/pinia,还是redux,本质都是通过js代码存储在内存中的数据,在网页刷新/重定向的时候,都会丢失

如果
Suspense组件放在Provider组件外部,在某些异步组件未加载完成时,意味着通过Provider给store里的某些东西提供数据的时候,很有可能这个页面js文件还未加载下来,下载下来之后,在该页面修改store中的数据,其他页面是不会监听到该页面发出去的事件的这些是源码内部所作的处理,源码的
subcribe是不会监听异步加载的页面所发出去dispatch的事件,虽然能修改数据解决办法:
- 将
Suspense放在Provider组件内部 - 意味着
Suspense组件本身就属于Provider组件的一部分,对于Provider内部页面所发生的dispatch的事件,就能监听到
- 将
Suspense放置到如下位置时,导致AppHeader组件打印两次问题的解决:
将
Suspense放置到使用useRoutes函数的组件当中进行包裹,因为Suspense只需要对异步组件进行包裹,但是如果提供一个字符串,效果并不好,页面上会出现该字符串闪出然后切换,最好是组件或空字符串(用户不易感知)如果多级路由中,子路由也有使用异步加载,可以给父路由中的
Outlet占位组件也包裹一层Suspense组件(这样当父路由中的子路由发生变化,最外层useRoutes所包裹的Suspense的替代方案将被子路由这一层中的Suspense所替代,也就不会引起父路由那一层级的也进行闪烁)

CRA + craco + postcss 实现移动端适配
https://4k8k.xyz/article/weixin_51491109/125889716
