Skip to content

240328面试 线上

作者:guo-zi-xin
更新于:2 个月前
字数统计:4k 字
阅读时长:13 分钟

ES6常用语法

CSS盒模型

盒模型是一个盛放内容的容器, 它由四部分组成 元素的具体内容content、 内边距padding、边框border、外边距margin组成

设置元素的宽高只是设置了内容区域的宽高,盒子真正的宽高应该是 内容宽高 + 内填充 + 边界边框 + 外边距

和模型有两种 标准盒模型IE盒模型, 这两者的区别主要在于宽高的包含范围:

标准盒模型的宽高指的是内容区域content的宽高, 而IE盒模型的宽高指的是内容区content + 内边距padding + 边框border的宽高

Promise和 Async Await用法区别

Promise

Promise 是 JavaScript 中用来处理异步操作的对象,它代表一个异步操作最终的完成或失败,可以获取异步操作的结果或错误。 Promise 通过 then() 方法来处理异步操作的成功和失败情况,可以链式调用多个 then() 方法。 使用 Promise 时,需要手动处理异步操作的状态,即 pending、fulfilled(resolved)和 rejected 状态。

React组件传参

父组件传子组件

通过props参数进行传递
  • 父组件
tsx
import React from 'react'

const Parent = ():React.FC => {
  return <Child param1="1" param2="2"/>
}
  • 子组件
tsx
import React from 'react'

const Child = ({ param1, param2 }):React.FC => {
  return <>父组件传递的参数:{ param1 }, { param2 }</>
}

上述示例中, param1param2就是父组件传递给子组件的参数,子组件运行结果为 父组件传递的参数:1,2

子组件传递给父组件

子组件传递给父组件严格来讲还是父传子, 通过props或者ref来实现传参效果

使用回调函数

在父组件中定义一个回调函数getChildVal, 将这个函数绑定在子组件上通过props传入子组件,当子组件中是时间或者什么实际需要向父组件传值时, 在子组件内就可以调用这个函数并传入值,此时定义在父组件的函数被触发,并可以拿到子组件的传值

  • 父组件
tsx
import React { useState } from 'react'

const Parent = ():React.FC => {
  const [data, setData] = useState('')
  const getChildVal = (value) => {
    console.log('从子组件获取的值', value)
    setData(value)
  }

  return (
    <>
    父组件接收到的值: {data}
    <Child onchange={getChildVal}/>
    </>
  )
}
  • 子组件
tsx
const Child = (props):React.FC =>{
  const [value, setValue] = useState<number>(1)
  const changeVal = () => {
    setValue(value + 1)
    props.onchange(value + 1)
  }

  return (
    <>
    {/* 当子组件需要向父组件传值时, 调用changeVal函数进行传值 */}
      <div onClick={changeVal}>子组件传递给父组件</div>
    </>
  )
}
通过ref

子组件传递给父组件严格来讲还是父传子, 父组件传递给子组件ref,子组件将想要暴露给父组件的值放在上面, 然后父组件就可以使用这个值;

首先需要导入对应模块包useImperativeHandleuseRefforwardRef

tsx
import React, { useRef, useImperativeHandle, useEffect, forwardRef } from 'react'
  • 父组件
tsx
const Parent = ():React.FC => {
  const ref = useRef()
  useEffect(()=> {
    console.log(ref)
  }, [])

  return (
    <Child  ref={ref}/> // 将ref传递给子组件
  )
}
  • 子组件
tsx
// 如果ref是设置在自定义组件上, 则需要用forwardRef处理一下
const Child = forwardRef(({}, ref)=> {
  useImperativeHandle(ref, () =>({
    data: '我是子组件'
  }))
  return <>我是子组件</>
})

上述示例运行后控制台输出

json
{
  "current": {
    "data": "我是子组件"
  }
}

跨组件传值(父组件传孙组件)

在跨组件传值过程中,我们可以使用父传子的方法,一层一层嵌套传递

  • 父组件
tsx
const Parent = ():React.FC => {
  return <Child1 param1="1" param2="2"/>
}
  • 子组件
tsx
//子组件
const Child1 = ({ param1, param2 }) => {
  return <Child2 param1={param1} param2={param2} />
}
  • 孙组件
tsx
const Child2 = ({ param1, param2 }) => {
  return <>父组件传递的参数:{param1},{param2}</>
}

通过层级嵌套传参 我们也可以实现跨组件传递,但如果有多层嵌套时,一层一层之间传递很非常冗余和麻烦,有时候也会搞混参数来源, 所以可以使用context来解决这个问题

再项目目录创建一个context.ts的文件,用于创建我们的context

typescript
import { createContext } from 'react'

const myContext = createContext(null)

export default myContext

然后在组件文件中引入定义的myContext, 并引入react包:

tsx
import React, { useContext } from 'react'
import MyContext from '@/context'
  • 父组件
tsx
const Parent = ():React.FC => {
  //使用provider传值
  return (<>
    <MyContext.Provider value={{ param1: '1', param2: '2' }}>
      <Child1/>
    </MyContext.Provider>
  </>)
}
  • 子组件
tsx
//子组件无需改动
const Child1 = () => {
  return <Child2 />
}
  • 孙组件
tsx
const Child2 = () => {
  //通过useContext获取父组件的值
  const { param1, param2 } = useContext(MyContext)  
  return <>父组件传递的参数:{param1},{param2}</>
}

React 子组件如何传递给父组件参数

父组件通过设置onchange事件传递给子组件, 子组件通过调用props.onchange事件来传递参数

CSS3常用属性

  • border-radius 边框圆角

允许创建元素的圆角边框

css
div {
  border-radius: 10px
}
  • box-shadow 容器阴影

添加元素的阴影效果

css
div {
  box-shadow: 3px 3px 5px #000
}
  • text-shadow 文本阴影

添加文本的阴影效果

css
div {
  text-shadow: 2px 2px 4px #000000
}
  • gradient 渐变

用于创建颜色渐变的背景

css
div {
  background-image: linear-gradient(to right, red, yellow)
}
  • transform 变换

用于对元素进行旋转、缩放、平移或倾斜

css
div {
  transform: rotate(45deg); /**旋转 */
  transform: translate(10px); /**平移 */
  transform: scale(0.5);  /**缩放 */
  transform: skew(30deg) /**倾斜 */
}
  • transition 过渡

用于在不同状态之间平滑地过渡CSS属性的值

css
div {
  transition: width 2s
}
  • animation 动画

用于创建自定义的动画效果

css
@keyframes slide {
 from { left: 0px }
 to { left: 100px }
}

div {
  animation: slide 2s infinite, alternate;
}
  • flex 弹性布局

用于创建灵活的布局结构

css
div {
  display: flex;
}
  • grid 网格布局
css
div {
  display: grid
}
  • media queries 媒体查询

用于根据设备特性(如屏幕尺寸、分辨率等) 应用不同的CSS样式

css
@media screen and (max-width: 768px) {
  /** styles for screens up to 768px wide */
}

常用布局方式

在网页设计和前端开发中,有许多常用的布局方式可以实现不同的页面结构和样式。以下是一些常用的布局方式:

  1. 传统布局

    • 基于浮动(Float)和清除浮动(Clearfix)的布局。
    • 使用定位(Positioning)属性(如 position: relativeposition: absolute)来布局。
  2. Flexbox 布局

    • 使用 Flexbox(弹性盒子布局)可以实现灵活的、自适应的布局。
    • 通过设置容器的 display: flex 属性,可以指定其为 Flex 容器,然后使用各种 Flexbox 属性来控制其子元素的排列方式。
  3. Grid 布局

    • 使用 CSS Grid(网格布局)可以创建复杂的网格结构。
    • 通过设置容器的 display: grid 属性,可以指定其为 Grid 容器,然后使用网格行和列的属性来定义布局。
  4. 响应式布局

    • 使用媒体查询(Media Queries)可以根据不同的设备尺寸和屏幕宽度应用不同的样式。
    • 可以使用相对单位(如百分比、em、rem 等)和 Flexbox/Grid 布局来实现响应式设计,使页面在不同设备上具有良好的显示效果。
  5. 流式布局

    • 使用百分比单位来设置宽度,使得元素可以根据父容器的大小自动调整大小,从而实现流式布局。
  6. 固定布局

    • 使用固定单位(如像素)来设置元素的宽度和高度,使得元素在不同设备上保持固定的大小和位置。
  7. 层叠布局

    • 使用定位属性(如 position: absoluteposition: relative)来实现元素的层叠效果,使得页面元素可以重叠、覆盖和定位在不同的位置上。
  8. 混合布局

    • 可以结合使用以上多种布局方式,根据具体需求来实现复杂的页面结构和样式。

CSS实现垂直居中

在 CSS 中实现垂直居中可以使用多种方法,以下是其中几种常见的方式:

  1. 使用 Flexbox

    css
    .container {
      display: flex;
      align-items: center; /* 垂直居中 */
    }
  2. 使用表格布局

    css
    .container {
      display: table;
    }
    .content {
      display: table-cell;
      vertical-align: middle; /* 垂直居中 */
    }
  3. 使用绝对定位和负边距

    css
    .container {
      position: relative;
    }
    .content {
      position: absolute;
      top: 50%;
      transform: translateY(-50%); /* 垂直居中 */
    }
  4. 使用网格布局

    css
    .container {
      display: grid;
      place-items: center; /* 垂直和水平居中 */
    }
  5. 使用 Flexbox 和伪元素

    css
    .container {
      display: flex;
    }
    .container::before,
    .container::after {
      content: '';
      flex: 1;
    }
    .content {
      align-self: center; /* 垂直居中 */
    }
  6. 使用 line-height 属性(适用于单行文本)

    css
    .container {
      line-height: 200px; /* 父容器高度的一半 */
    }
  7. 使用 calc() 函数

    css
    .container {
      position: relative;
      height: 400px;
    }
    .content {
      position: absolute;
      top: calc(50% - 50px); /* 垂直居中 */
    }

宏任务和微任务的关系

宏任务(Macro Task)和微任务(Micro Task)是 JavaScript 中处理异步操作的两种不同类型。

  • 宏任务(Macro Task):通常代表一些较大的任务,比如 setTimeout、setInterval、I/O 操作等。当执行完一个宏任务后,JavaScript 引擎会检查是否有微任务需要执行,然后再执行微任务队列中的任务。

常见的宏任务包括整体代码(Script)执行、setTimeout、setInterval、I/O 操作、UI 渲染等。

  • 微任务(Micro Task):微任务通常是在当前任务执行结束后立即执行的任务,它们相对于宏任务来说执行顺序更早。

常见的微任务包括 Promise 的 then 方法、MutationObserver、process.nextTick 等。

JavaScript 中执行异步操作时,宏任务和微任务的执行顺序如下:

  1. 执行一个宏任务(例如执行整体代码、定时器等)。
  2. 当宏任务执行完毕后,在宏任务执行栈清空之前,执行所有微任务队列中的任务。
  3. 当前宏任务执行栈清空后,检查是否有渲染任务,如果有则执行渲染。
  4. 执行下一个宏任务(如果有)。

总的来说,微任务优先级比较高,会在下一个宏任务之前执行完毕,而宏任务则相对较慢,会在当前宏任务执行完毕后执行。

闭包

是什么 当一个函数中的内部函数被拿到函数外部调用,又因为在js中内层作用域总是能访问外层作用域的,那么内部函数存在对外部函数中变量的引用,这些变量的集合称之为闭包

使用场景

创建私有变量 (全局变量不易维护) 延长变量的生命周期 实现柯里化(颗粒)

React18新特性

升级

  1. React18 不再支持IE浏览器
  2. 新项目直接使用 npm 或者 yarn 安装最新版依赖即可
  3. 改变根节点的挂在方式: 使用新的API creatRoot, 使用旧的API仍然兼容, 只用使用了createRoot之后才会有React18的新特性
tsx
ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <Provider store={store}>
      <App/>
    </Provider>
  </React.StrictMode>,
)

在上面的示例中,我们使用了ReactDOM.createRoot方法创建了一个根节点,并使用 render方法将组件渲染到根节点中。这样可以让React应用更快地响应用户操作,提高用户体验。

setState异步同步

  1. 异步更新(默认行为)

    在 React 18 中,默认情况下,setState 方法会以异步方式进行更新。这意味着它会将多个状态更新批量处理,并在适当的时机进行合并和应用,以优化性能。这样做可以减少不必要的重渲染,并提高应用程序的响应性。

  2. 同步更新(使用flushSync)

    尽管setState默认以异步方式进行更新,但在某些情况下,您可能需要立即获取更新后的状态。为了实现此目的,React 18 提供了flushSync方法,可以强制执行同步更新。

tsx
import { flushSync } from 'react-dom';

// 同步更新

flushSync(() => {

  this.setState({ count: this.state.count + 1 });

});

新增API

  1. startTransition

    startTransition 是一个新的 React API,旨在帮助开发者优化应用程序的性能和用户体验。这个函数可以告诉 React 在下次重新渲染组件时,应该延迟更新状态。这样,一些较慢的操作(例如异步请求等)就可以在后台执行,不会影响应用程序的交互性能。

  2. useTransition

    useTransitionstartTransition 的 hook 版本。它可以在函数组件中使用,从而让开发者更方便地控制异步操作的状态。

  3. createRoot

    createRoot 是一个新的入口函数,用于创建根React组件。它可以替代原先的ReactDOM.render方法,使得开发者可以将多个根节点渲染到一个页面上。

  4. useDeferredValue

    useDeferredValue 是一个新的 hook,可以将某个状态值的更新延迟一段时间后再执行,从而提高应用程序的性能和用户体验。类似于防抖

    tsx
    const deferredSearchTerm = useDeferredValue(searchTerm, {       
      timeoutMs: 1000
    });
  5. useMutableSource

    useMutableSource 允许开发者访问可变的数据源,并在多个组件之间共享状态。这对于高性能的数据订阅和共享非常有用。

严格模式

React 严格模式(Strict Mode)是一个开发模式,可以帮助开发者发现一些潜在的问题,以提高应用程序的质量。启用严格模式后,React 会执行额外的检查和警告,以帮助开发者发现一些常见问题,并尽早地解决它们。

React 严格模式只在开发环境下工作,不会影响生产环境下的应用程序。

tsx
import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

React 严格模式主要包含以下几个方面的检查和提示:

  • 识别不安全的生命周期方法,提示开发者修改,这些方法可能会导致意外的副作用或错误。

  • 检测意外的副作用,例如:多余的重新渲染、不符合预期的函数调用等。

  • 检测某些过时的 API 使用,提供更好的替代方案。

  • 检测警告信息,使其更加明显和易于发现。

在应用程序启动时禁用严格模式

在一些情况下,移除 React.StrictMode 组件可能不太方便,例如:在大型项目中或已经存在大量的 console.log 调用等代码片段。此时,可以在应用程序启动时禁用严格模式。

在应用程序启动文件中,我们可以使用 React 的 unstable_disableDevMode() 函数来禁用严格模式

tsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

React.unstable_disableDevMode();

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

并发模式

React 并发模式(React Concurrent Mode)是 React 的一项新功能,旨在改善在复杂应用程序中的用户体验和性能。在传统的 React 中,更新组件树时会阻塞用户界面的响应,可能导致卡顿和延迟。

而并发模式通过将任务分解为多个小步骤,让 React 在执行渲染和布局时可以中断和恢复任务,从而提供更平滑和响应式的用户体验。

在 React 并发模式中,引入了两个主要概念:任务调度和优先级。任务调度器负责决定哪些任务执行、何时执行以及中断和恢复任务。优先级允许 React 根据任务的紧迫性来安排任务的执行顺序,确保响应度更高的任务能够优先执行。

利用并发模式,React 可以将渲染过程分解为多个小任务,并根据优先级来动态调整任务执行的顺序。这样,在浏览器空闲时间或网络请求等异步操作期间,React 可以暂停当前任务,执行其它具有更高优先级的任务,以实现更爽快的用户交互体验。

总而言之,React 并发模式通过任务调度和优先级机制,提供了更好的用户体验和性能,使得 React 应用程序能够更加平滑地响应用户操作。

tsx
import React, { 
  useState, 
  useEffect, 
  unstable_ConcurrentMode as ConcurrentMode 
} from 'react';


const App = ():React.FC => {
  const [count, setCount] = useState<number>(0);

  useEffect(() => {
    const timer = setInterval(() => {
      setCount((prevCount) => prevCount + 1);
    }, 1000);

    return () => {
      clearInterval(timer);
    };

  }, []);

  return (
    <ConcurrentMode>
      <div>
        <h1>计数器</h1>
        <p>{count}</p>
      </div>
    </ConcurrentMode>
  );
}

export default App;

人生没有捷径,就像到二仙桥必须要走成华大道。