Skip to content

一. 项目概览


image-20220923215013917
  • 上面的今日头条项目,源码在课件资料中寻找
image-20220922140642373image-20220922140654193image-20220922140715612

二. 项目介绍


  • 爱彼迎项目:
    • 爱彼迎团队:
      • 开发团队国内200多个人
      • 经历过数年版本迭代最终完成的一个产品
    • 爱彼迎目前的项目
      • 已经不针对国内房东、订房进行维护,依然可以订阅国外的房屋
      • 爱彼迎已经退出中国市场,不排除有一天国内的网站不能继续访问
      • 项目本身确实非常美观、好看
  • 项目核心学习、掌握的知识点:
    • 对于第一个React项目,我们的核心是对前面所学知识进行练习、实战
    • 掌握React开发的流程、模式、项目架构,项目中会有很多组件、工具等封装、抽取、复用思想
    • 最重要的是学习React开发的模式和编程的思想,而不是局限于我上课期间所讲的内容,并且大部分样式和布局内容需要大家课下自行完成
    • 在这个项目过程中,我会尽量将之前所学的所有知识都运用起来,但是我们不会为了用某个知识而用某个知识
    • 课程中会使用我服务器已经获取到的数据,一是国内的数据更好看,二是担心它数据有一天不再维护,三是我对数据已经进行了大量的整理
    • 后续我们还会专门学习React+TypeScript项目实战的内容,React本身非常的灵活,对js本身要求也较高,但是最重要的还是练习

三. 项目规范


  • 项目规范:项目中有一些开发规范和代码风格
    1. 文件夹、文件名称统一小写、多个单词以连接符(-)连接
    2. JS变量名称采用小驼峰标识,常量全部使用大写字母,组件采用大驼峰
    3. CSS采用普通CSSstyled-component结合来编写(全局采用普通CSS、局部采用styled-component
    4. 整个项目不再使用class组件,统一使用函数式组件,并且全面拥抱Hooks
    5. 所有的函数式组件,为了避免不必要的渲染,全部使用memo进行包裹
    6. 组件内部的状态,使用useState、useReducer;业务数据全部放在redux中管理
    7. 函数组件内部基本按照如下顺序编写代码:
      • 组件内部state管理
      • reduxhooks代码
      • 其他hooks相关代码(比如自定义hooks
      • 其他逻辑代码(网络请求/事件监听/副作用)
      • 返回JSX代码
    8. redux代码规范如下:
      • redux目前我们学习了两种模式,一种普通方式,一种RTK,在项目实战中尽量两个都用起来,都需要掌握
      • 每个模块有自己独立的reducer或者slice,之后合并在一起
      • redux中会存在共享的状态、从服务器获取到的数据状态
    9. 网络请求采用axios
      • axios进行二次封装
      • 所有的模块请求会放到一个请求文件中单独管理
    10. 项目使用AntDesign、MUI(Material UI)
      • 爱彼迎本身的设计风格更多偏向于Material UI,但是课程中也会尽量讲到AntDesign的使用方法
      • 项目中某些AntDesign、MUI中的组件会被拿过来使用
      • 但是多部分组件还是自己进行编写、封装、实现
    11. 其他规范在项目中根据实际情况决定和编写

四. 创建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')
            }
          }
        };

五. 项目目录结构划分


  • 对项目进行目录结构的划分:

    image-20220922172717719
    • assets:资源文件夹,像imgcss
    • base-ui:多个项目都可能用到的公共ui组件
    • components:公共组件库
    • hooks:存放自定义hook
    • router:路由相关文件
    • services:网络请求相关文件
    • store:状态管理相关文件
    • utils:一些工具文件
    • views:视图文件

六. CSS样式的重置


  • 对默认CSS样式进行重置:

    • npm安装一个normalize.css

      shell
      npm i normalize.css
    • 手动创建一个reset.css

    • css/index.less文件中引入下面文件,index.js入口文件中引入index.less即可

      • 引入之后,就会在webpack的依赖图中,到时候打包的时候,webpack就会帮我对引入的文件进行打包
      image-20220922174419530image-20220922174154138
  • 默认是无法打包less文件的,这里需要安装less

    shell
    npm i less -D

七. 全家桶 – Router配置


  • 安装redux router
image-20220922183053994

image-20220922183112877 image-20220922183153593

八. 全家桶 – Redux状态管理


  • Redux状态管理的选择:
    • 普通方式:目前项目中依然使用率非常高

    • @reduxjs/toolkit方式:推荐方式, 未来的趋势

    • 安装Redux Toolkit

      • Redux ToolKit 本质上其实是对编写redux做的一层封装,如果想在react中使用,还是需要安装react-redux包才行
      • redux toolkit内部有依赖redux,所以不需要再安装redux
      js
          npm install @reduxjs/toolkit react-redux

      image-20220922214445047 image-20220922214504154

      image-20220922214559403image-20220922214607373

九. 网络请求 - axios


image-20220923210417054 image-20220923210435117 image-20220923210542621

image-20220923210501911

十. 项目细节

  • 首页header部分采用display布局,左右两项采用flex1,中间一项就会居中

  • svg相当于通过一大堆标签,路径的方式画出来图案,就不是图片了,可以让网站资源更小一些,加快首页的渲染速度

  • 怎么引入svg呢?

    1. 方式一:

      • svg保存成一张图片,如xxx.svg,之后当成图片去引用,如<img src='xxx.svg'/>background-image: url('xxx.svg')
    2. 方式二:

      • 使用svg标签,直接嵌入到对应的html标签中,这样做的好处是:svg的内容会随着html文件的下载,跟着一块下载下来,这样后续就不需要再去请求对应的图片,且svg是一个相对比较小的文件

      • 我们可以以组件的形式进行嵌套

        image-20220923225818337
      • 但是需要注意,在jsx中嵌套svg,还需要对style中对应的属性转化为js对象

        image-20220923225945582
      • 所以我们可以使用一个工具函数来进行转化

        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
      • 如何修改svgcolor,我们可以修改离它最近父元素的color,因为svg会使用离它最近的父元素的color

  • React中,基于webpack环境,动态引入本地图片

    • 方式一:import引入对应的图片

      • style.js文件中

        image-20220924164857030
      • 组件中

        image-20220924164956847
    • 方式二:require函数进行包裹,包裹之后意味着是一个js代码

      • style.js文件中

        image-20220924165403861
      • 组件中

        image-20220924165305701
  • 早期react中,需要使用的是require函数返回值的default属性,才能拿到图片

  • 这些特性都是webpack的,而不是react的,而vue-loader有做特殊处理,所以可以在.vue文件中使用~assets来获取

    jsx
    	require("xxx/xxx/xxx.jpg").default
  • Material UI默认使用的是emotion作为样式引擎,如果想要使用styled-components代替,安装命令如下:

    js
    	npm install @mui/material @mui/styled-engine-sc styled-components
    • https://mui.com/zh/material-ui/getting-started/installation/
  • 然后,我们只需要在webpack配置alias如下:

    js
        module.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代码存储在内存中的数据,在网页刷新/重定向的时候,都会丢失

image-20220928222331116
  • 如果Suspense组件放在Provider组件外部,在某些异步组件未加载完成时,意味着通过Providerstore里的某些东西提供数据的时候,很有可能这个页面js文件还未加载下来,下载下来之后,在该页面修改store中的数据,其他页面是不会监听到该页面发出去的事件的

  • 这些是源码内部所作的处理,源码的subcribe是不会监听异步加载的页面所发出去dispatch的事件,虽然能修改数据

  • 解决办法:

    • Suspense放在Provider组件内部
    • 意味着Suspense组件本身就属于Provider组件的一部分,对于Provider内部页面所发生的dispatch的事件,就能监听到
  • Suspense放置到如下位置时,导致AppHeader组件打印两次问题的解决:

    image-20220930003217126
    • Suspense放置到使用useRoutes函数的组件当中进行包裹,因为Suspense只需要对异步组件进行包裹,但是如果提供一个字符串,效果并不好,页面上会出现该字符串闪出然后切换,最好是组件或空字符串(用户不易感知)

    • 如果多级路由中,子路由也有使用异步加载,可以给父路由中的Outlet占位组件也包裹一层Suspense组件(这样当父路由中的子路由发生变化,最外层useRoutes所包裹的Suspense的替代方案将被子路由这一层中的Suspense所替代,也就不会引起父路由那一层级的也进行闪烁)

    image-20220930003341193
  • CRA + craco + postcss 实现移动端适配

  • https://4k8k.xyz/article/weixin_51491109/125889716

Released under the MIT License.