Skip to content

Latest commit

 

History

History
1139 lines (906 loc) · 43 KB

README.md

File metadata and controls

1139 lines (906 loc) · 43 KB

7-react-admin-java

7-react-admin-java-pro

(一) 技术栈

ts + react-hooks + react-router-dom@5.2.0 + redux@^4.0.5 + react-redux + axios

husky + lint-staged + commitlint + eslint + stylelint + prettier + EditorConfig

styled-components + echarts + antd

redux-logger + redux-thunk 中间件

通过 create-react-app 脚手架构建

本项目构建过程记录博客

(二) 主要功能

(1) 功能
    - router menu breadcrumb 三合一
    - 自定义hooks ( useFetch, useModal, useViewprot, useIntersectionObserver, useThrottle, useDebounce等)
    - 自定义redux, 懒加载, 权限控制, 回到顶部, echarts封装, react-redux-hooks-api, react-router-hooks-api
    - alias别名, 全局sass, 新手功能引导动画(react-joyride)(reactour), 瀑布流, axios取消请求
    - webpack-bundle-analyzer 包分析插件
(2) css特效
    - stickyFooter粘性页脚, ceiling吸顶效果, shockWave冲击波, 水平垂直居中, 两栏三栏布局
    - rem响应布局, em, @media媒体查询, 一物理像素边框, 盒模型, 三角形, 进度条, margin重叠
    - inline-block间隙, 两行三行省略号, 多列等高布局, 滑动菜单slide-menu
(3) 源码分析
    - redux axios webpack-Compiler
    - 具体在本项目 src/SOURCE-CODE-ANALYSIS 文件夹中

(三) Vue3 组件库 - DivinePlus

基于 vue3 + webpack5 + typescript + vuepress2 构建的组件库

(四) Electron 桌面端工具箱 - 8-Electron-Tools

基于 Electron + Vue3 + TypeScript + Vite + Pinia + ElementPlus 构建的桌面端工具箱

(五) 源码分析

(1) redux 和 react-redux 源码分析 [redux^4.0.5]

(2) 手写 webpack Compiler 源码 [webpack^4.42.0]

(3) axios 源码分析 [axios^0.20.0]

(4) vue 源码分析 [vue^2.6.12]

(5) vuex 源码分析 [v2.6.10]

(6) react 源码分析 [react^17.0.3]

(7) a-hooks 源码分析 [a-hooks^2.10.9]

(8) a-hooks 源码分析 [a-hooks^3.1.9]

(9) koa 源码分析 [koa^2.13.1]

(10) badJs-report 源码分析

(11) element-ui 源码分析 [element-ui^2]

(12) element-plus 源码分析 [element-plus^2.26]

(13) vant [vant^3.4.5]

(14) Diff-virtualDOM

(六) 博客

[react] Hooks

[封装01-设计模式] 设计原则 和 工厂模式(简单抽象方法) 适配器模式 装饰器模式
[封装02-设计模式] 命令模式 元模式 组合模式 代理模式
[封装03-设计模式] Decorator 装饰器模式在前端的应用
[封装04-设计模式] Publish Subscribe 发布订阅模式在前端的应用
[封装05-ElementUI源码01] Row Col Container Header Aside Main Footer
[封装06- Divine-plus] 从0开始封装一个VUE3-UI组件库 初始化
[封装07- Divine-plus] 从0开始封装一个VUE3-UI组件库 Watermark Scrollbar

[React 从零实践01-后台] 代码分割
[React 从零实践02-后台] 权限控制
[React 从零实践03-后台] 自定义hooks
[React 从零实践04-后台] docker-compose 部署react+egg+nginx+mysql
[React 从零实践05-后台] Gitlab-CI使用Docker自动化部署

[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] koa
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend

[源码-vue06] Vue.nextTick 和 vm.$nextTick
[源码-vue07] keep-alive

[源码-react01] ReactDOM.render01
[源码-react02] 手写hook调度-useState实现

[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI
[部署04] [复习] gitlabCI + docker-compose + ssh免密登录 + 最全Dockerfile
[部署05] Kubernetes01
[部署06] Kubernetes02

[数据结构和算法01] 二分查找和排序
[数据结构和算法02] 回文字符串
[数据结构和算法03] 栈 和 队列
[数据结构和算法04] 链表 和 树

[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[深入21] 数据结构和算法 - 二分查找和排序
[深入22] js和v8垃圾回收机制
[深入23] JS设计模式 - 代理,策略,单例
[深入24] Fiber01
[深入25] Typescript
[深入26] Drag

[前端学java01-SpringBoot实战] 环境配置和HelloWorld服务
[前端学java02-SpringBoot实战] mybatis + mysql 实现歌曲增删改查
[前端学java03-SpringBoot实战] lombok,日志,部署
[前端学java04-SpringBoot实战] 静态资源 + 拦截器 + 前后端文件上传
[前端学java05-SpringBoot实战] 常用注解 + redis实现统计功能
[前端学java06-SpringBoot实战] 注入 + Swagger2 3.0 + 单元测试JUnit5
[前端学java07-SpringBoot实战] IOC扫描器 + 事务 + Jackson
[前端学java08-SpringBoot实战总结1-7] 阶段性总结
[前端学java09-SpringBoot实战] 多模块配置 + Mybatis-plus + 单多模块打包部署
[前端学java10-SpringBoot实战] bean赋值转换 + 参数校验 + 全局异常处理
[前端学java11-SpringSecurity] 配置 + 内存 + 数据库 = 三种方式实现RBAC
[前端学java12-SpringSecurity] JWT
[前端学java13-SpringCloud] Eureka + RestTemplate + Zuul + Ribbon
[前端学java14-Mybatis Plus] 分页插件 和 乐观锁插件

复习笔记-01
复习笔记-02
复习笔记-03
复习笔记-04

(七) LeetCode 算法

6-penetrate - 算法,前端,设计模式,英语,面试

(八) 复习资料

(九) 代码规范相关 ( 1-9 )

  • 123456789

安装 husky + lint-staged + commitlint + eslint + stylelint + prettier + EditorConfig

(1) EditorConfig

  • EditorConfig 官网
  • EditorConfig 教程
  • 作用
    • EditorConfig 可以帮助开发者在不同的编辑器和 IDE 之间定义和维护一致的代码风格
  • 重点规则
    • [*] 表示用于所有文件的规则
    • [.md] 表示用于.md 文件的规则
    • indent_style=space 表示当键下 tab 键时使用软选项卡填充缩进,也就是使用 ( 空格 ) 填充
    • indent_style=tab 表示键下 tab 键时使用硬选项卡填充缩进,也就是使用 ( \t ) 填充
    • indent_size=2 表示两个空格
    • trim_trailing_whitespace=true 表示将换行符前面的空格删除掉 trim: 修剪 trailing: 尾部
    • insert_final_newline=true 确保文件保存的时候以换行符结尾,即文件末尾会多一行空行
  • 配置步骤
    • 1.在 vscode 中下载 EditorConfig for VSCode 插件,该插件在保存时会执行.editorconfig 中指定的规则
    • 2.新建 .editorconfig 文件
root=true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

(2) husky + lint-staged

  • husky 官网
  • lint-staged 官网
  • 教程
  • 配置步骤
    • 1.安装 npm install -D husky lint-staged
    • 2.在 package.json 文件中配置 huskylint-staged 配置项
    • 3.如果在 husky => hooks => pre-commit中用到 eslint,就需要配置.eslintrc.js.eslintignore不然会报错
    • 4.在配置中就用到了几乎所有代码规范的配置 husky + lint-staged + commitlint + eslint + styleslnt + prettier
  • package.json 配置如下
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "eslint --config .eslintrc.js --fix"
    ],
    "*.{css,less}": [
      "stylelint --config .stylelintrc.js --fix"
    ],
    "*.{ts,tsx,js,jsx,css,less}": "prettier --write --ignore-unknown"
  }

(3) commitlint

  • commitlint 官网
  • 配置步骤
    • 1.安装 npm install --save-dev @commitlint/config-conventional @commitlint/cli
    • 2.安装 npm install --save-dev husky
    • 3.新建.huskyrc 文件或者在package.json中配置husky选项
    • 4.新建 .commitlintrc.js 注意是 js 文件,不能是 ts,或者.commitlintrc.json 等
    • 5.在.commitlintrc.js中添加扩展@commitlint/config-conventional // conventional: 传统的
在 .commitlintrc.js 中配置如下

module.exports = {
  extends: ['@commitlint/config-conventional']
};

(4) eslint

(1) 安装
- npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

(2) 新建 .eslintrc.js 和 .eslintignore 两个文件

(3) 配置 .eslintrc.js 文件
module.exports = {
  parser: "@typescript-eslint/parser",
  extends: ["react-app", "plugin:@typescript-eslint/recommended"],
  plugins: ["@typescript-eslint", "react"],
  rules: {
    "no-console": 0,
    "@typescript-eslint/no-empty-function": 0,
    "@typescript-eslint/no-explicit-any": 0,
    "no-use-before-define": 0,
    "@typescript-eslint/explicit-module-boundary-types": 0,
    "react-hooks/exhaustive-deps": 0,
  },
};

  • 自动配置步骤
(1) 安装 npm install -D eslint

(2) 在项目根目录中,执行命令 npx eslint --init

(3) 执行2的命令后,会进入交互式界面,可根据上面的 [教程1] 进行配置,这个过程可能安装依赖时会报错,不行就自行安装依赖吧

(4) 经过23后,就会自动生成 .eslintrc.js 文件,并且配置好了

(5) stylelint

  • stylelint 官网
  • 配置步骤
    • 1.npm install --save-dev stylelint stylelint-config-standard
    • 2.npm install -D stylelint-scss stylelint-webpack-plugin
    • 3.新建.stylelintrc.js
    • 4.然后添加到 huskyhooks
  • package.json 配置如下
"husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
    }
  },
  "lint-staged": {
    "*.{ts,tsx,js}": [
      "eslint --config .eslintrc.js --fix"
    ],
    "*.{css,sass,scss}": [
      "stylelint --config .stylelintrc.js --fix",
      "git add"
    ]
  }

(6) prettier

  • prettier 官网
  • prettier 教程
  • 配置步骤
    • 1.npm i -D prettier eslint-config-prettier eslint-plugin-prettier
    • 2.新建 .prettierrc.js 文件
    • 3.同时修改 .eslintrc.js 中的配置
  • .prettierrc.js 配置如下
module.exports = {
  printWidth: 120, //一行的字符数,如果超过会进行换行,默认为80
  tabWidth: 2, //一个tab代表几个空格数,默认为2
  singleQuote: true,
  semi: false,
}
  • .eslintrc.js 配置如下
module.exports = {
  parser: '@typescript-eslint/parser',
  extends: [
    'react-app',
    'plugin:@typescript-eslint/recommended',
    //  "plugin:prettier/recommended" 暂不开启
  ],
  plugins: ['@typescript-eslint', 'react', 'prettier'],
  rules: {
    'no-console': 0,
    '@typescript-eslint/no-empty-function': 0,
    '@typescript-eslint/no-explicit-any': 0,
    'no-use-before-define': 0,
    '@typescript-eslint/explicit-module-boundary-types': 0,
    'react-hooks/exhaustive-deps': 0,
  },
}

(7) git 提交规范

  • 文档
  • feat: 新功能 feature
  • fix: 修复 bug
  • chore:构建过程 或 辅助工具的变动 (chore:日常事务,乏味无趣的工作的意思)
  • refactor:重构(即不是新增功能,也不是修改 bug 的代码变动)
  • perf: 性能优化
  • docs: 文档 documentation
  • test:测试
  • style:格式(不影响代码运行的变动)

(8) js 注释规范

(9) react-hooks 的 eslint 规范插件

(十) 按需加载相关

(1) react-router-dom

  • npm install react-router-dom
  • npm install @types/react-router-dom
  • 集中式路由插件 ( react-router-config )
    • npm install --save react-router-config
    • npm install @types/react-router-config -S
    • 官网
    • 使用教程
    • renderRoutes
  • react-router-dom 中的 hooks
    • useHistory
    • useLocation
    • useParams
      • useParams只有在动态路由对应的组件中可以获取到,在父组件或者其他组件都不能后去到
      • 做全局 面包屑的时候容易采坑
    • useRouteMatch
  • 路由按需加载

(2) antd 按需加载

  • babel-plugin-import 官网
  • 实例教程
  • npm install babel-plugin-import -D
  • 配置 .babelrc 文件
    • 注意需要 eject 后修改 webpack.config.js 中的 babelrc: true 启用 .babelrc 文件
    • 同时需要删除在 package.json 中定义的 babel 选项
.babelrc
-----
{
  "presets": ["react-app"],
  "plugins": [
    [ "import", { "libraryName": "antd", "style": "css"} ]
  ]
}

(3) antd 使用 iconfont 自定义图标

  • 官网
  • scriptUrl 就是 Symbol 在线生成的文件路径

(4) redux 相关

  • npm install redux react-redux -S
  • npm install @types/react-redux -S
  • npm install --save redux-devtools-extension
  • npm install redux-devtools -D

(十一) create-react-app 相关设置

(1) 添加别名 @ 映射 src 在 TS 的项目中

    1. create-react-app 构建的项目,eject 后,找到 config/webpack.config.js => resolve.alias
    1. tsconfig.json 中删除 baseUrlpaths,添加 "extends": "./paths.json"
    1. 在根目录新建 paths.json 文件,写入 baseUrlpaths 配置
  • 教程地址
1. webpack.config.js => resolve => alias
module.export = {
   resolve: {
     alias: {
      "@": path.resolve(__dirname, '../src')
     }
   }
 }
2. 根目录新建 paths.json 写入以下配置
{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "@/*": ["*"]
    }
  }
}
3. 在 tsconfig.json 中做如下修改,添加( extends ), 删除(  baseUrl,paths )
{
  // "baseUrl": "src",
  // "paths": {
  //   "@/*": ["src/*"]
  // },
  "extends": "./paths.json"
}

(1.2) 遇到问题

  • 问题:当在 webpack 配置了别名后,ts 报错找不到模块,同时 vscode 点击时不跳转
  • 回答:
    • paths:因为 webpack 知道了别名路径,但是 ts 并不知道这是设置了别名,所以需要设置 tsconfig.json 文件中的 paths
    • baseUrl: 当设置了 paths 时,就必须设置 baseUrl
  • 如下配置后,ts 不再报错,并且 vscode 中点击也可以跳转
{
  "compilerOptions": {
    "baseUrl": ".", // this must be specified if "paths" is specified.当指定paths的时候,就必须指定baseUrl
    "paths": {
      "jquery": ["node_modules/jquery/dist/jquery"] // this mapping is relative to "baseUrl" 该映射是相对于 baseUrl 的
      "@/*": "src/*"
    }
  }
}

官网说明:https://www.typescriptlang.org/tsconfig#paths

(2) create-react-app 配置全局的 scss ,而不需要每次 @import

  • 安装 sass-resources-loader
  • 修改 config/webpack.config.js 如下
  • 注意:很多教程修改use:getStyleLoaders().concat()这样修改不行
const getStyleLoaders = (cssOptions, preProcessor) => {
  const loaders = [......].filter(Boolean);
  if (preProcessor) {
    loaders.push(......);
  }
  if (preProcessor === 'sass-loader') {
    loaders.push({
      loader: 'sass-resources-loader',
      options: {
          resources: [
            // 这里按照你的文件路径填写../../../ 定位到根目录下, 可以引入多个文件
            path.resolve(__dirname, '../src/style/index.scss'),
          ]
      }
    })
  }
  return loaders;
};

(2-2) create-react-app 配置全局 less

  • 安装 style-resources-loader
  • cnpm i style-resources-loader
  • github 仓库
  // common function to get style loaders
  const getStyleLoaders = (cssOptions, preProcessor) => {
    const loaders = [
      ...
    ].filter(Boolean);
    if (preProcessor) {
      loaders.push(
        {
          loader: require.resolve('resolve-url-loader'),
          options: {
            sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
            root: paths.appSrc,
          },
        },
        {
          loader: require.resolve(preProcessor),
          options: {
            sourceMap: true,
          },
        }
      );
    }
    if (preProcessor === 'less-loader') {
      loaders.push({
        loader: 'style-resources-loader',
        options: {
          patterns: path.resolve(__dirname, '../src/styles/*.less'),
          injector: 'append'
        }
      })
    }
    return loaders;
  };

(3) css-module

  • 需要安装 node-sass
  • npm install node-sass -D
  • xxxx.module.scss

(4) 在 create-react-app 实现代码分割

(5) 使用 TS 中的 enum 枚举类型避免魔法字符串

  • export enum SYSTEMTYPE { ADMIN, BIGSCREEN }
  • import { SYSTEMTYPE } from 'src/global/enum.ts'
  • 注意点 1:
    • 问题:元素隐式具有 "any" 类型,因为类型为 "string" 的表达式不能用于索引类型 "typeof SYSTEMTYPE"。
    • 解决方案:在 tsconfig.json => compilerOptions => "suppressImplicitAnyIndexErrors": true,
  • 注意点 2:
export enum SYSTEMTYPE1 {
  ADMIN = 'ADMIN',
  BIGSCREEN = 'BIGSCREEN',
}

export enum SYSTEMTYPE2 {
  ADMIN,
  BIGSCREEN,
}


SYSTEMTYPE1.ADMIN =========> 'ADMIN'
SYSTEMTYPE2.ADMIN =========> 0

(6) require.context => ts 报错不存在属性

  • npm install @tyeps/webpack-env -D
  • require.context(direactory, useSubdirectorys, regExp, mode)
  • 作用
    • 创建自己的 context
  • 参数
    • directory: 需要搜多的文件夹
    • useSubdirectory: 是否搜索其子目录
    • regExp: 匹配文件的正则表达式
    • mode:模式,比如 'sync'

(7) require.context() 实现自动化 import 功能

  • require.context(directory, useSubdirectories, regExp, mode)
  • 作用
    • 创建自己的 context
  • 参数
    • directory:需要搜索的文件夹
    • useSubdirectories:是否搜索其子目录
    • regExp:匹配文件的正则表达式
    • mode:模式,比如 'sync'
  • 返回值
    • context 导出的 require() 函数
  • require() 函数
    • 参数:request
    • 属性
      • resolve 函数,它返回 request 被解析后得到的 ( 模块 id ),可能在 module.hot.accept 时会用到
      • keys 函数,它返回一个数组,由所有可能被此 context module 处理的请求组成
  • require.context()本项目使用到的案例
    • 详见 requireModules 函数

(1) 获取模块的路径
const moduleContext = require.context('./Knowledge', true, /index.tsx/, 'sync')
moduleContext.keys().forEach(modulePath => {
  console.log('modulePath就是模块的路径')
})


(2) 获取模块的源码
const moduleContext = require.context('./Knowledge', true, /index.tsx/, 'sync')
moduleContext.keys().forEach(modulePath => {
  const moduleSourceCode = moduleContext(modulePath).default
  console.log('moduleSourceCode就是模块的(组件)源码')
})

(8) console.log('123 %c abc', 'font-size: 30px; color: red;')

(9) 环境变量

  • NODE_ENV
    • 运行 cnpm run start 时, NODE_ENV = 'development'
    • 运行 cnpm run test 时,NODE_ENV = 'test'
    • 运行 cnpm run build 时,NODE_ENV = 'production'
    • 获取:process.env.NODE_ENV
    • 注意:你无法手动覆盖 NODE_ENV
  • 在 ( .env ) 中添加开发环境变量
    • 要定义( 永久环境变量 ),请在项目的 ( 根目录 ) 创建名为 ( .env ) 的文件
    • 必须以 REACT_APP_ 开头来创建自定义环境变量,除了 ( NODE_ENV ) 之外的任何其他变量都将被忽略
    • 此功能需要在 react-scripts@0.5.0及更高版本
  • 除了 .env 文件,还可以用哪些命名?
    • .env:默认
    • .env.local:本地覆盖。除 test 之外的所有环境都加载此文件
    • .env.development, .env.test, .env.production:设置特定环境
    • .env.development.local, .env.test.local, .env.production.local:设置特定环境的本地覆盖
    • 注意: ( .env.development.local ) 文件会覆盖掉 ( .env.development ) 中设置的环境便变量,但不会影响 ( NODE_ENV )
    • 注意:.env.development.local文件不被识别,git push 时并不会提交,其实也没有必要使用 local 文件

(十二) 大屏

(1) screenfull

  • 安装:cnpm install screenfull -S
  • 官网

(2) echarts 封装

(3) 大屏线上项目

(4) 大屏滚动效果

(十三) BUG 总结

(1) 登陆页面出现的 bug

  • 问题描述:登陆页面,选择系统时,报错,并且 select 每 change 一次,动画就渲染一次
  • 报错描述:Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
  • 原因分析:
    • Select 的 value 值,直接从 redux 全局 state 中取数据,change 后又把改变的数据传入 store
    • 因为是全局顶层 store,导致基本整个项目根组件都重新渲染了
  • 解决办法:
      1. 可以获取 store 的 state,在传给组件的 state,然后 change Select 时,修改组件自己的 state,只影响该组件,然后点击登陆时,在把数据传递给 redux

(2) [ts]未终止的正则表达式文字

  • 注意如果有组件或者 jsx 返回的 DOM,就必须用 tsx 文件后缀

(3) 找不到模块“xxxx”或其相应的类型声明。

  • 最简单的修复方式:在 import 前面加上 // @ts-ignore

  • 合理的解决办法:

    • 1、 项目根目录下添加 index.d.ts, 并在其中写类型声明

    • 2、 将 index.d.ts 加入到 tsconfig.json 中的配置项 include

      1. 创建一个 types 目录,专门用来管理自己写的声明文件,将 foo 的声明文件放到 types/foo/index.d.ts 中
      1. tsconfig.json 中的 paths 和 baseUrl 字段
    根目录/types/redux/index.d.ts
    declare module 'redux' {
      const bindActionCreators: any
      export { bindActionCreators }
    }
    
    tsconfig.json
    "compilerOptions": {
      "baseUrl": "./",
      "paths": {
        "*": [
          "types/*"
        ]
      }
    },
    
  • issue

  • ts 类型声明文件的正确使用姿势

(4) style-lint

  • :global 报错
  • 解决方法:
    • .stylelintrc.js 中做以下修改
rules: {
    "selector-pseudo-class-no-unknown": [true, {
      ignorePseudoClasses: ["global"],
    }]
  },

(5) 依赖没有锁版本造成的报错

  • 报错:TypeScript error in /@pretty-format/build/index.d.ts(7,13): '=' expected. TS1005
  • 原因:"typescript": "~3.7.2", 版本问题
  • 解决:"typescript": "^3.8.2"
  • 资料:https://www.jianshu.com/p/a69ff39a91c5

(6) antdMenu Sider inlineCollapsed 报错

  • Warning: [antd: Menu] inlineCollapsed not control Menu under Sider. Should set collapsed on Sider instead.
  • mene 的 inlineCollapsed 在有 Sider 父组件时,不能操作 Sider,必须用 Sider 的 collapsed 属性来操作 Silder 展开/收缩

(7) echarts 初始化时超出容器的宽度

  • 问题:当初始化时,div 按照样式设置了 50%的大小,但是 echarts 图标超出了容器大小
  • 解决:
    • 把 init 设置成 async 函数,当实例存在的时候,再去设置 setOption
    • 即确保在 setOption 时,echarts 实例存在并且,如果实例存在,通过 echarts.getInstanceByDom()直接复用,不用重新生成

(8) antd - BackTop 组件不生效

  • 原因:因为 antd 的布局组件 Content 是不能直接设置宽高和 overflow 等属性
  • 解决:
    • Content 组件内部在设置容器 scrollOuterWrap 在包裹住 scrollInnterWrap,由scrollInnterWrap 包裹需要滚动的元素
    • .scrollOuterWrap:需要设置固定的高度,通过 calc 计算得到;和其他 overflow-y 相关的属性
    • antd 的 BackTop 组件的层级,在哪里的层级都可以

(9) 前端部署到服务器后,刷新 404

原因:因为 web 单页应用,其实就一个 index.html 页面,跳转路由只是更新页面的一部分,而不存在真正的路由对应的页面,刷新访问的地址页面是不存在的,所以要重新重定向到 index.html 解决:配置 nginx 的 try_files 具体:try_files $uri $uri/ /index.html 说明:

  • try_files:依次尝试访问对应的资源,第一个访问不到,就访问第二个资源,一次往后
  • $uri:表示 Nginx 地址变量,即当前的 rul 地址
  • $rui/:表示一个目录,nginx 会自动识别是目录还是文件 - 比如:访问http://www.baidu.com/a/b/,则 ( $uri/ ) 为 ( /a/b/ ) 代码:
server {
	listen 80;
	server_name localhost;
	location / {
		root /usr/share/nginx/html;
		index index.html index.htm;
		try_files $uri $uri/  /index.html;
	}
}

表示:当 $uri 和 $uri/ 均不是对应资源时,返回 /index.html 资源

(10) 本地开发环境用 express 的 proxy 做的代理,部署到服务器后需要用 nginx 做代理

  • 遇到问题:部署后前端代码是跑在 nginx 中的,代理要用 nginx 来做,同时因为单页应用需要做 try_files 来解决刷新问题
  • 解决:在前端的请求 path 中加入 /api,在 nginx 中对 /api 做反向代理
server {
	listen 80;
	server_name localhost;
	location / {
		root /usr/share/nginx/html;
		index index.html index.htm;
		try_files $uri $uri/  /index.html;
	}

	location /api {
		proxy_pass  http://49.233.215.163:7001; // 因为egg启动的是7001端口
	}
}

(11) React.lazy(() => import())

  • React.lazy(() => import(/* webpackChunkName: "[request]" */, @/pages/${path}))
  • 上面的 request 就是 path 变量的值
  • 比如:path=aaa => request=aaa => 按需加载的 chunk 包名就包含 aaa.chunk.js
  • ( React.lazy ) 需要配合 ( Suspense ) 组件
  • suspense:是悬念的意思
import React, { Suspense } from 'react'
import { Route, Switch } from 'react-router-dom'
import { lazyLoad } from '@/utils'
// 基础路由
const Login = lazyLoad('@/pages/login')
const NotFound = lazyLoad('@/pages/404')
const Layout = lazyLoad('@/pages/layout')

// renderRoutes 中包含 Switch 组件
const Router = () => {
  return (
    <Suspense fallback={<div>loading...</div>}>
      <Switch>
        <Route path='/login' render={props => { console.log("login路由"); return <Login {...props} />}} />
        <Route path='/404' render={props => { console.log("NotFount路由"); return <NotFound {...props} />}}  />
        <Route component={Layout} />
      </Switch>
    </Suspense>
  )
}

export default Router

(12) 当 eject 后报错

  • 原因:eject 后,如果之前有安装依赖,就都会消失,也不会在 package.json 中有记录
  • 解决:
      1. 删除 node_modules 文件夹
      1. npm run eject 后需要 npm install 安装依赖,因为弹出了很多其他的依赖并没有安装
      1. 需要从新安装 eject 之前你安装的依赖

(13) 记录一个因为不小心而踩的深坑 - ( 关于变量和引用 )

// 根据权限对(menu),和(router注册)进行过滤
function routesFilter(routes, role) {
  return routes.filter((route) => {
    let { meta, subs } = route;
    if (subs) {
      route.subs = routesFilter(subs, role);
      // 坑:这里有个巨坑,这里一定要用route.subs
      // 例如: subs = routesFilter(subs, role);
      // 说明:不能用 subs, 因为subs是新声明的变量,直接替换了整个subs后,不会影响route.subs
    }
    return !meta.needLoginAuth || meta.rolesAuth.includes(role);
  });
}

(14) 关于菜单的 openkeys selectedKeys collapsed

  • 问题记录
  • 问题:当菜单中有展开和选中的项时,点击向左缩窄菜单时,再次向右展开,状态没有保存
  • 解决:可以通过 onOpenChange 中的 openKeys 来做持久化
    • 刚刚点击的 openKeys 存在,就使用,不存在,就使用 localStorage 中的
  • 新问题:
    • 1.这样在缩窄的菜单中 hover 一些之后,展开状态也变了,因为缩窄的菜单 hover 时也会触发 openchange
    • 2.有选中和展开状态时去缩窄菜单,会有选中的菜单突出,影响观感
  // 展开/关闭的回调
  const onOpenChange = (openKeys: any) => {
    console.log(openKeys, 'onOpenChange执行了')
    setOpenKeys(() => openKeys)
    setLocalStorage(CONST.OPENKEYS, openKeys) // 记住展开关闭的组,刷新持久化
  }

  // const onOpenChange = (openKeys: any) => {
  //   console.log(openKeys, 'onOpenChange执行了')
  //   const currentopenKeys = openKeys.length
  //     ? openKeys
  //     : getLocalStorage(CONST.OPENKEYS)
  //   setLocalStorage(CONST.OPENKEYS, currentopenKeys) // 记住展开关闭的组,刷新持久化
  //   setOpenKeys(() => currentopenKeys)
  // }

(15) useDebounce

  • 问题:当 UseDebounce 组件中有其他 state 更新时,useDebounce 是新的函数重新执行了,timer 又会被重新赋值为初始值,造成错乱,不能达到 debounce 效果
  • 如何验证:useDebounce 在 UseDebounce 组件有其他 state 更新时重新执行了:在 useDebounce 中 console.log() 打印即可
  • 如何解决:使用 useRef 固定数据,类似 class 中的实例变量
import { useRef } from "react";

interface IuseDebounce {
  (fn: Ifn, delay?: number, immediate?: boolean): IClosure;
}

interface Ifn {
  (...rest: any[]): any;
}

interface IClosure {
  (e: any, ...rest: any[]): any;
}

/**
 * @desc debounce 防抖函数
 * @param {function} fn 需要执行的函数
 * @param {number} delay 延时执行的时间段
 * @param {boolean} immediate 是否立即执行
 */
export const useDebounce: IuseDebounce = (
  fn: any,
  delay = 1000,
  immediate = false
) => {
  const refTimer = useRef(0); // 相当于class中的实例属性

  return (e, ...rest) => {
    if (immediate && !refTimer.current) {
      fn.call(rest);
      refTimer.current = 1; // 除了第一次进入,后面都不会在进入该函数
      return; // 第一次不往下执行
    }
    if (refTimer.current) {
      window.clearTimeout(refTimer.current);
    }

    refTimer.current = window.setTimeout(() => {
      fn.call(rest);
    }, delay);
  };
};

// -------------------- 变量 timer 版本 --------------------
// 问题:当 UseDebounce 组件中有其他 state 更新时,useDebounce是新的函数重新执行了,timer又会被重新赋值为初始值,造成错乱,不能达到debounce效果
// 如何验证:useDebounce在UseDebounce组件有其他state更新时重新执行了:在useDebounce中 console.log() 打印即可
// 如何解决:使用 useRef 固定数据,类似class中的实例变量
// export const useDebounce: IuseDebounce = (fn: any, delay = 1000, immediate = false) => {
//   let timer = 0;
//   return (e, ...rest) => {
//     if (immediate && !timer) {
//       fn.call(rest);
//       timer = 1;
//       return;
//     }
//     if (timer) {
//       window.clearTimeout(timer);
//     }
//     timer = window.setTimeout(() => {
//       fn.call(rest);
//     }, delay);
//   };
// };

(16) react-markdown

  • 1.安装: npm install --save react-markdown
  • 2.安装: npm install -D raw-loader
  • 2.引入 md 文件作为 source
    • 引入前需要安装的依赖:npm install -D raw-loader,配置 webpack
  • 3.接下来就可以正常使用了
  • 4.教程链接:https://segmentfault.com/a/1190000020294373

(17) webpack-bundle-analyzer 使用报错

  • 原因:端口被占用,如果有别的项目中也使用到了 webpack-bundle-analyzer 时就会产生端口调用
  • 解决: 关闭其他项目的服务
  • 该项目已经在开发环境添加了 webpack-bundle-analyzer 分析

(18) 在 react 中如果通过 create-react-app 创建的项目,之后想加 Typescript 配置

  • 1.同过配置别名时已经做了相关配置了
  • 2.在 1 的基础上在 scr 文件夹中添加 global.d.ts 配置文件

(十四) TS 相关

(1) tsconfig.json

  • 官网介绍
  • 顶级属性
  • 如果项目中存在 tsconfig.json 代表着这个目录是 Typescript 项目的 根目录
  • tsconfig.json文件中指定了编译这个项目的 ( 根文件 ) 和 ( 编译选项 )
    • 根文件
    • 编译选项

(1-1) @types, typeRoots, types

  • @types: 默认所有可见的@types 包会在 编译过程中被包含进来,比如 node_modules/@type/...
  • typeRoots: 如果指定了typeRoots,则只有 typeRoots 下面的包才会包含进来
  • types: 只有被列举的包才会包含进来
{
  "compilerOptions": {
    "typeRoots" : ["./typings"]
  }
}
表示:这个配置文件会包含 ./typings 下面的包,而不包含 ./node_modules/@types 里面的包
{
  "compilerOptions": {
    "types" : ["node", "lodash", "express"]
  }
}
表示:
1. 这个tsconfig.json文件将仅会包含 ./node_modules/@types/node,./node_modules/@types/lodash和./node_modules/@types/express
2. /@types/。 node_modules/@types/*里面的其它包不会被引入进来

(1-2) extends

  • tsconfig.json 文件可以利用 extends 属性从 另一个配置文件里继承配置
  • extends的值是一个字符串,表示继承文件的路径
  • 源文件的配置先被加载,然后被继承文件中的配置重写,如果循环引用就会报错
  • 顶级属性

(1-3) compilerOptions 编译选项

  • allowJs
    • boolean,默认值 false,表示允许编译 js 文件
  • declaration
    • boolean,默认值 false,表示生成相应的 .d.ts 文件
  • jsx
    • 表示在 .tsx 文件中支持 jsx

(2) 如何编写 .d.ts

  • It's not difficult.