前端实习周记5

322 阅读8分钟

前端实习周记5

打开Drawer不刷新页面

原生antD组件中的Drawer应该是新建了一个z-index为最高值的遮罩层,在body下又生成一个独立于原本react渲染的标签进行渲染。根据需求,原本页面分为查询条件区域和查询结果区域,要求点开每一个单独的查询结果会出现一个Drawer,且这个Drawer要覆盖住查询结果和查询条件的区域,遂将Drawer中的getContainerof属性挂载到最外层包裹着查询条件和查询结果的组件里,但这又出现新的问题:

  • 关闭Drawer后页面重新渲染,重新根据查询条件发送请求,不能像原来一样打开Drawer后仍然保持着搜索后的结果状态

​ 想了很多解决办法,一开始并没有想到是因为Drawer挂载的地方覆盖了查询条件的区域,还想着利用redux存储查询的状态,按下Drawer关闭的按钮时调用这个状态;还想着使用usememo()缓存状态。

​ 最后为了满足需求还是使用障眼法,将Drawer挂载到查询结果的组件内,调整高度和定位将其从表面上覆盖住查询条件的区域,虽然会出现不能响应式调整高度的问题,但考虑到项目本身是固定分辨率的,也算是解决了。

hooks缓存

​ react渲染页面是通过render把虚拟结点渲染到页面,通过对比fiber树,re-render重新渲染页面,为了更好的性能,我们一般提倡尽可能地减少react重新渲染页面的次数,这时候,usememo()和useCallback()就应运而生。

useMemo()
const cachedValue= useMemo(calculateValue,dependencies)

可以在react每次重新渲染的时候缓存计算的结果,其中:

  • calculateValue: 缓存计算值的函数,且要为没有任何参数的纯函数,可以返回任意类型。react只会在首次渲染的时候调用该函数,之后如果dependencies没有发生变化时,react就直接返回相同的数值,避免了重复调用该函数;如果dependencies变化,才再调用该函数。
  • dependencies: 依赖项,为calculateValue中所使用到的响应式变量组成的数组,包括props\state和所有直接在组件中定义的函数和变量。React 使用 Object.is 将每个依赖项与其之前的值进行比较。
Object.is()

Object.is()== 运算符并不等价。== 运算符在测试相等性之前,会对两个操作数进行类型转换(如果它们不是相同的类型),这可能会导致一些非预期的行为,例如 "" == false 的结果是 true,但是 Object.is() 不会对其操作数进行类型转换。

Object.is()等价于 === 运算符。Object.is()=== 之间的唯一区别在于它们处理带符号的 0 和 NaN 值的时候。=== 运算符(和 == 运算符)将数值 -0+0 视为相等,但是会将 NaN 视为彼此不相等。

注意:

和其他hooks一样,只能在组件的顶层和自定义hooks中使用,不能在循环或条件语句中调用。

用法
  1. 缓存组件,避免反复重新加载整个组件

    function Todolist({todos,tab}){
        const visibleTodos=filterTodos(todos,tab)
    }
    

    上面的组件需要利用filterTodos的方法过滤得到有效的todos,但由于利用到了props和state,所以每次更新都会重新运行filterTodos函数,因此,我们可以使用usememo进行改善:

    function Todolist({todos,tab}){
        const visibleTodos=useMemo(()=>filterTodos(todos,tab),[todos,tab])
    }
    
  2. 跳过组件的重新渲染

    在默认情况下,当一个组件重新渲染时,react会递归地重新渲染它的所有子组件。依旧使用上述的例子,假设将visibleTodos传递给todoList的子组件:

    function TodoList({todos,tab}){
    const visibleTodos=filterTodos(todos,tab)
    //依然是建议使用useMemo()
      const visibleTodos=useMemo(()=>filterTodos(todos,tab),[todos,tab])
     return(
     <div>
         <List items={visibleTodos} />
     </div>
     )
    }
    

    注意:对于一个直接依赖于在组件中创建的对象,需要对这个对象本身进行memo包裹

    例如:这里的NotedTodos是在组件中创建的对象,当组件重新渲染时,组件中所有的代码都会再次运行,所以创建NotedTodos的代码也会在每次重新渲染时运行,而每次重新渲染的NotedTodos不一样,所以这里即便使用了useMemo, react也会重新渲染。

    function TodoList({todos,tab,ischecked}){
    const NotedTodos={noted:ischecked}
    
    const visibleTodos=useMemo(()=>{
        return filterTodos(todos,tab,NotedTodos)
    },[todos,tab,NotedTodos])
    }
    

    解决方式,使用memo包裹NotedTodos及使用了其作为依赖项的变量:

    function TodoList({todos,tab,ischecked}){
    const visibleTodos = useMemo(()=>{
        const NotedTodos={noted:ischecked}
        return filterTodos(todos,tab,NotedTodos)
    },[todos,tab,ischecked])
    
    
    
useCallback( )

简单来说,可以看作是useMemo的一个语法糖,适用于缓存函数:

const visibleTodos=useMemo(()=>{
    return ()=>{
        todos.find(item=>item===tabs)
    }
},[todos,tab])
}

//相当于
const visibleTodos=useCallback(()=>{
    todos.find(item=>item===tabs)
} ,[todos,tab])
}

// 在 React 内部的简化实现
function useCallback(fn, dependencies) {
  return useMemo(() => fn, dependencies);
}

  • 二者区别:
    • useMemo 缓存函数调用的结果。在这里,它缓存了调用 computeRequirements(product) 的结果。除非 product 发生改变,否则它将不会发生变化。这让你向下传递 requirements 时而无需不必要地重新渲染 ShippingForm。必要时,React 将会调用传入的函数重新计算结果。(调用)
    • useCallback 缓存函数本身。不像 useMemo,它不会调用你传入的函数。相反,它缓存此函数。从而除非 productIdreferrer 发生改变,handleSubmit 自己将不会发生改变。这让你向下传递 handleSubmit 函数而无需不必要地重新渲染 ShippingForm。直至用户提交表单,你的代码都将不会运行。(创建)
使用场景
  1. usememo返回函数

  2. 优化自定义hooks

纯函数

纯函数是函数式编程的一个重要概念,纯函数的主要特征:

  • 只负责自己的任务,不会更改已存在的对象或变量,即不会改变函数作用域外的变量、不会改变在函数调用前创建对象
  • 输入相同,输出则相同。给定相同的输入,总返回相同的结果。

针对第一点,我们进行局部mutation是可以的,比如我们可以在渲染时更改我们刚刚创建的变量和对象,例如:

function Todo(){
let list=[];
for(let i=1;i<10;i++){
list.push(<Item key={i} />)
}
}

这样的操作是可行的,但如果list变量是在Todo函数之外创建的话,这就违反了第一点的更改已存在的对象的要求。

副作用

副作用区别于纯函数,指的是不得不发生改变的情况,这些改变包括更新屏幕、启动动画、更改数据,在react中大多为事件处理程序如onClick。

事件处理程序

传递给事件处理函数的函数应该直接传递,而不是被调用,如:

//正确 ✔
<button onClick={handleClick}></button>
//错误 ❌
//这会使得handleClick函数在每次渲染时触发,即使不点击也会调用handleClick函数
<button onClick={handleClick()}></button>

同样的陷阱:

//正确 ✔
<button onClick={()=>alert('right')}
//错误 ❌
<button onClick={alert('wrong')}

调整图片拉伸

使用object-fit调整:和的内容适应到高度和宽度确定的框。

使用object-position切换:被替换元素的内容对象在元素框内的对齐方式。

redux原理

  • store : 存放对象的状态的集合。唯一改变的store的方法是创建action,一个描述发生了什么的对象state,将其dispatch给store。
  • state:描述发生了什么的对象state
  • action: 描述行为,为reducer提供判断
  • reducer: 纯函数,根据reducer中的逻辑,返回新的state来更新状态.
    • (state, action) => newState

项目别名报错

报错信息:Failed to resolve import "./App" from "src\main.tsx". Does the file exist?

ts项目中导入文件名错误,排查原因后得出是在vite.config.ts中

 resolve: {
     ...//没有添加tsx
    extensions: ['.js', '.ts', '.json'] // 导入时想要省略的扩展名列表
  },

修改后:

resolve: {
		alias: {
			'&': path.resolve(__dirname, './src') // 路径别名
		},
		extensions: ['.tsx', '.ts', '.js', '.json'] // 导入时想要省略的扩展名列表
	}

引入png透明底文件失效

​ 经检查是由于有样式覆盖该图片,导致其透明底色失效,解决方案:

background-color:unset

去除原有样式即可。

sass基础使用

变量

sass允许用变量$来存储重复使用的css数值,通常在scss文件顶部声明,例如:

$primary-color:#66ccff

body{
    .container{
    background-color:$primary-color
    }

}
Mixins

可以让我们定义一组css规则,并且能够让我们在需要的地方重用这些规则,有些类似react当中我们自己封装的组件,用mixins写好之后,在调用的时候只需要传递相应的参数就可以显示,这有利于减少代码量,使得样式编写组件化,例如:

@mixin theme($theme: DarkGray) {
  background: $theme;
  box-shadow: 0 0 1px rgba($theme, .25);
  color: #fff;
}
//这里定义了一组名为theme的样式,可以传入$theme变量
作为backgroundbox-shadow的颜色

//直接利用@include引用theme,并根据需要修改$theme
.info {
  @include theme;
}
.alert {
  @include theme($theme: DarkRed);
}
.success {
  @include theme($theme: DarkGreen);
}

文件模块化

在scss文件中,可以利用类似import语法如@use引入其他文件的变量,mixins和函数

// _base.scss
$primary-color:#66ccff

body{
    .container{
    background-color:$primary-color
    }

}

//style,scss
@use 'base';

.filter{
    color:base.$primary-color
}
继承

利用%为前缀编写变量,如果有样式@extend该变量,则继承并显示该变量的内容,需要注意的是,只有被@extend的%变量才会显示:

//message-shared被以下的类样式继承了
%message-shared {
  border: 1px solid #ccc;
  padding: 10px;
  color: #333;
}

//equal-heights没有被继承
%equal-heights {
  display: flex;
  flex-wrap: wrap;
}

.message {
  @extend %message-shared;
}

.success {
  @extend %message-shared;
  border-color: green;//继承后也可以额外添加样式
}

.error {
  @extend %message-shared;
  border-color: red;
}

.warning {
  @extend %message-shared;
  border-color: yellow;
}
计算

可以引用sass中的math,在scss文件中进行计算,例如

@use "sass:math"
 
    //将px转换为百分比
.container{
    width: math.div(600px * 960px) * 100%
        
}

我们也可以在一个scss文件中定义变量并计算,并在vite中配置全部scss文件,就可以使用里面的变量啦。