遇到这样一个问题,初始化时用户登陆后需要获取到用户信息,但是发现获取用户信息这个接口触发了2次,这是不应该的,于是我查阅了一下资料,把自己的笔记记录下来。 还有就是使用mobx遇到的控制台警告问题,也一并记录一下。
一.React18,useEffect被调用了两次
-  import { useState, useEffect } from "react"; const Counter = () => { const [count, setCount] = useState(5); useEffect(() => { console.log("rendered"); console.log(count); }, [count]); console.log("rendered"); return ( <div> <h1> Counter </h1> <div> {count} </div> <button onClick={() => setCount(count + 1)}> click to increase </button> </div> ); }; export default Counter;
可以用这段话来解释:
当你在
strict mode in development时,这是一个来自React18本身的问题。基本上,即使在React18中卸载之后,核心团队仍在试图改变组件的状态。useEffect两次被调用与此功能有关。目前正在讨论如何解决这个问题。要了解更多信息,请观看YouTube视频。现在,您可以使用useRef使用布尔值ref来避免它,在您的例子中是这样的:
import  { useState, useEffect, useRef } from "react";
const Counter = () => {
  const firstRenderRef = useRef(true);
  const [count, setCount] = useState(5);
  useEffect(() => {
    if(firstRenderRef.current){
      firstRenderRef.current = false;
      return;
    }
    console.log("rendered");
    console.log(count);
  }, [count]);
console.log("rendered");
  return (
    <div>
      <h1> Counter </h1>
      <div> {count} </div>
      <button onClick={() => setCount( count + 1 )}> click to increase </button>
    </div>
  );
};
export default Counter;其实还有一种做法就是不适用框架默认的严格模式,这样初始化数据的时候也只会走一次接口,我们看下react官网是怎么说的:
React 提供了 “严格模式”,在严格模式下开发时,它将会调用每个组件函数两次。通过重复调用组件函数,严格模式有助于找到违反这些规则的组件。
...
严格模式在生产环境下不生效,因此它不会降低应用程序的速度。如需引入严格模式,你可以用
<React.StrictMode>包裹根组件。一些框架会默认这样做。
2.[MobX] Since strict-mode is enabled, changing (observed) observable values without using an action..
报错源码展示:
import { makeAutoObservable,  } from 'mobx'
import { getUserInfo } from '../api/login'
class UserStore {
    userInfo = {}
    constructor() {
        makeAutoObservable(this)
    }
    getUserInfo = async ({ account }) => {
        const res = await getUserInfo({ account })
        if (res.code !== 'SUCCESS') return
        console.log('用户个人信息res', res);
        this.userInfo = res.data
        // console.log('用户信息', this.userInfo);
    }
}
export default UserStore改进之后:
import { makeAutoObservable, runInAction } from 'mobx'
import { getUserInfo } from '../api/login'
class UserStore {
    userInfo = {}
    constructor() {
        makeAutoObservable(this)
    }
    getUserInfo = async ({ account }) => {
        const res = await getUserInfo({ account })
        if (res.code !== 'SUCCESS') return
        console.log('用户个人信息res', res);
        runInAction(() => {
            this.userInfo = res.data
        })
        // console.log('用户信息', this.userInfo);
    }
}
export default UserStore控制台报警告的原因其实是mobx中只能在acrion中重新赋值,异步导致赋值操作被加载到队列中,在action外面了,
 runInAction 函数将赋值操作包裹在action内部.
3.关闭scss文件自动生成的css文件和min.css文件
每次创建.scss文件后,添加我们自己的样式,总是会多出两个不必要的css文件,看着挺讨人厌的,现在我们将其关闭一下。
(1)找到设置打开它

(2)在搜索框里搜索easysass,并点击在setting.json中编辑

(3)添加以下代码
"easysass.excludeRegex": "false",
"easysass.formats": [
    {
      "format": "expanded",
      "extension": ""
    },
    {
      "format": "compressed",
      "extension": ""
    }
  ],
这样就不会再生成css文件了。
4.执行完npm run eject项目报错
react报错jsx: Using `babel-preset-react-app` requires that you specify `NODE_ENV` or `BABEL_ENV` envi
查了一下,报错的原因是:eslint插件启动没有注入开发环境ENV,导致babel-preset-react-app无法解析
删除package.json中的eslintConfig配置

但是其实这样不能完美解决问题,推荐的做法是执行 npm run eject命令之前需要先提交一次我们的代码,因为执行这个操作时不可逆的,因为这个吃了点亏。
5.项目中建立全局样式
assets/styles/global.scss
建好全局样式文件后,引入到index.js中:
import './assets/styles/global.scss'//全局样式验证一下全局样式有没有生效:
  <label className='test'>label</label>

可以看到样式效果有了,也就是全局样式生效了,然后就可以自定义自己的全局样式文件了。
6.配置别名路径(用@代替src)
安装修改CRA配置的包:yarn add -D @craco/craco;
项目根目录下创建文件 craco.config.js (一定记得要和src同级,不要建错地方了)
craco.config.js中代码如下
const path = require("path")
module.exports = {
  webpack:{
    alias:{
      "@":path.resolve(__dirname,"src")
    }
  }
}package.json中修改部分:
//将 start/build/test 三个命令改为craco方式
 "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },然后重启项目,发现@配置src是生效了,但是@后面没有任何提示,一点不好用,还得自己手写,这显然不是我们想要的效果,再来配置一下。
@别名路径提示
在根目录下创建jsconfig.json配置文件(一定记得和src处于同级位置)
jsconfig.json添加如下代码:
{
    "compilerOptions":{
        "baseUrl":"./",
        "paths":{
            "@/*":["src/*"]
        }
    }
}再重启项目,发现@后有了智能提示,有了我们想要的效果。
7.封装react高阶组件(实现路由权鉴)
其实也就是要实现vue的路由前置守卫的功能,但是react并没有给提供像vue的beforeEach这样的方法,所以需要自己封装高阶组件,实现登陆有token,跳转主页,没有token(未登录),不能访问(拦截在登陆页面),再就是有token但是想回到登录页,也要实现拦截,核心就是这样的思路。
新建一个AuthComponent.js文件,建议放在公共组件里面
import { getToken } from '@/utils'
import { Navigate } from 'react-router-dom'
const AuthComponent = ({ children }) => {
    const isToken = getToken()
    if (isToken) {
            return <>{children}</>
    }
     else {
        return <Navigate to='/login' replace></Navigate>
    }
}
export {
    AuthComponent
}然后引入到项目得根文件App.js中,哪个页面需要登陆才能访问,就包裹哪个组件,以主页home为例:
    <Route path='/' element={
              <AuthComponent>
                <Home />
              </AuthComponent>
            }>
            </Route>这样就实现了路由鉴权,也就是登陆这一模块的控制。
8.vscode自动格式化插件配置说明(Prettier - Code formatter)
安装Prettier插件,会解决保存后代码自动对齐的问题,之前配置的有点问题,导致每次保存代码后,还得手动格式化代码,非常麻烦,这下一并解决一下。

安装完之后然后添加以下配置代码:
 // prettier相关设置
  // 使能每一种语言默认格式化规则
  "[html]": {
    "editor.defaultFormatter": "vscode.html-language-features",
  },
  "[javascript]": {
    "editor.defaultFormatter": "vscode.typescript-language-features",
  },
 
  "editor.formatOnSave": true, // 保存格式化
  // html不换行
  "js-beautify-html": {
    // 换行
    // "wrap_attributes": "force-aligned"
    // 不换行 
    "wrap_attributes": "auto",
    // "wrap_attributes": "aligned-multiple",
  },
  "prettier": {
    "printWidth": 300, // 代码宽度  js超过 300换行
    "bracketSameLine": true,
    "trailingComma": "none", //禁止随时添加逗号,这个很重要。找了好久
    "singleQuote": true, // 单引号不自动转换双引号
    "semi": false, // 不添加分号
    "proseWrap": "preserve", // 代码超出是否要换行 preserve保留
    "arrowParens": "avoid", // 箭头函数一个参数不加括号
  },
  "files.associations": {
    "*.html": "html"
  },
  "workbench.iconTheme": "vscode-icons",
  "backgroundCover.autoStatus": true,
 
  // 格式化vue文件
  "files.autoSave": "onFocusChange",
  "editor.fontSize": 14,  // 设置字体
  "editor.tabSize": 2,    // 设置tab位2个空格,设置后在页面可查看.
  "editor.tabCompletion": "on",  // 用来在出现推荐值时,按下Tab键是否自动填入最佳推荐值
  "editor.codeActionsOnSave": {
      "source.fixAll.eslint": true,
      "source.organizeImports": true    // 这个属性能够在保存时,自动调整 import 语句相关顺序,能够让你的 import 语句按照字母顺序进行排列
  },
  "editor.lineNumbers": "on",  // 设置代码行号
  "editor.formatOnSave": true,
  "terminal.integrated.shell.osx": "zsh",
  "workbench.iconTheme": "vscode-icons",
  "explorer.confirmDelete": false, 
  // #让vue中的js按"prettier"格式进行格式化
  "vetur.format.defaultFormatter.html": "js-beautify-html",
  "vetur.format.defaultFormatter.js":"prettier-eslint",
  "vetur.format.defaultFormatterOptions": {
      "js-beautify-html": {
          // #vue组件中html代码格式化样式
          "wrap_attributes": "force-aligned", //也可以设置为“auto”,效果会不一样
          "wrap_line_length": 200,
          "end_with_newline": false,
          "semi": false,            "singleQuote": true
        },
        "prettier": {
            "semi": false,
            "singleQuote": true,
            "editor.tabSize": 2
        },
        "prettyhtml": {
            "printWidth": 160,
            "singleQuote": false,
            "wrapAttributes": false,
            "sortAttributes": false
        }
    },
  // 设置编译器默认使用vetur方式格式化代码
  "[vue]": {
    "editor.defaultFormatter": "octref.vetur"
  },



















