前端必考【React与项目优化面试题解】

React

1.React中的事件机制

JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了组件树的根上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件

2.React 高阶组件、Render props、hooks 有什么区别,为什么要不断迭代

  • HOC

    简单来说就是一个函数接受一个组件,这个函数中又定义了一个组件,在这个新定义的组件中可以写一些属性方法等公共代码,然后把这些属性方法传给接受的组件,最后return新定义的组件。

    // hoc的定义
    function withSubscription(WrappedComponent, selectData) {
      return class extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            data: selectData(DataSource, props)
          };
        }
        // 一些通用的逻辑处理
        render() {
          // ... 并使用新数据渲染被包装的组件!
          return <WrappedComponent data={this.state.data} {...this.props} />;
        }
      };
    
    // 使用
    const BlogPostWithSubscription = withSubscription(BlogPost,
      (DataSource, props) => DataSource.getBlogPost(props.id));
    
  • Render props

    父组件向Render props组件传递了一个render函数,这个render函数定义了渲染内容;Render props组件通过this.props.render获取函数然后写入参数。

    // DataProvider组件内部的渲染逻辑如下
    class DataProvider extends React.Components {
         state = {
        name: 'Tom'
      }
    
        render() {
        return (
            <div>
              <p>共享数据组件自己内部的渲染逻辑</p>
              { this.props.render(this.state) }
          </div>
        );
      }
    }
    
    // 调用方式
    <DataProvider render={data => (
      <h1>Hello {data.name}</h1>
    )}/>
    
  • Hooks

    可以在里面用其他的hook,直接return出数据,使用的时候useXxxx就行了,非常方便。

3.对React-Fiber的理解,它解决了什么问题?

  1. 可中断的渲染

    以前react更新过程是同步的,一旦开始就会进行到底,这可能导致主线程被占据,页面卡顿做不了其他的事情。现在react把更新过程拆分成多个小任务,这样渲染的过程就能被其他优先级更高的任务插队,从而更好的响应用户交互。

  2. 优先级管理

    比如用户输入,图表更新,在浏览器原生的任务调度机制下,用户的输入可能会被图表更新所堵塞,但是在Fiber的操作下,用户的输入是优先于图表更新的。 React Fiber 如何在任务调度方面比浏览器提供更细粒度的控制。

  3. 并发渲染

    在传统架构中,React 是单线程的,无法充分利用多核 CPU 的优势。Fiber 使得 React 能够更好地利用现代多核处理器,提高渲染性能。

4.React.PureComponent

在函数组件里面就是React.memo,react会自动浅比较一下传入props,变化了就更新组件。

5.类组件中的生命周期

  • 挂载阶段

    1. constructor

      用于初始化 state 和绑定方法。

      constructor(props) {
       super(props);
       this.state = { count: 0 };
      }
      
    2. static getDerivedStateFromProps

      根据 props 更新 state。是一个静态方法。返回值会合并到state中

      static getDerivedStateFromProps(nextProps, prevState) {
       if (nextProps.someValue !== prevState.someValue) {
         return { someValue: nextProps.someValue };
       }
       return null;
      }
      
    3. render

      返回要渲染的元素。

      render() {
       return <div>{this.state.count}</div>;
      }
      
    4. componentDidMount

      组件挂载后调用,可以进行 DOM 操作或数据请求。

      componentDidMount() {
       // 进行数据请求或订阅操作
      }
      
  • 更新阶段

    1. getDerivedStateFromProps

      在每次组件更新时调用(见上)。

    2. shouldComponentUpdate

      决定组件是否应该重新渲染。返回 truefalse

      shouldComponentUpdate(nextProps, nextState) {
       return nextState.count !== this.state.count;
      }
      
    3. render

      重新渲染

    4. getSnapshotBeforeUpdate

      在更新前获取一些信息(例如,DOM 状态)。

      getSnapshotBeforeUpdate(prevProps, prevState) {
       if (prevProps.someValue !== this.props.someValue) {
         return { scrollPosition: window.scrollY };
       }
       return null;
      }
      
    5. componentDidUpdate

      组件更新后调用,可以进行 DOM 操作或数据请求。

      componentDidUpdate(prevProps, prevState, snapshot) {
       if (snapshot !== null) {
         window.scrollTo(0, snapshot.scrollPosition);
       }
      }
      
  • 卸载阶段

    componentWillUnmount

    componentWillUnmount() {
     // 进行清理工作,如取消订阅
    }
    

6.哪些方法会触发 React 重新渲染?重新渲染会做些什么?

  1. State 变化

    • 当组件的 state 发生变化时,会触发重新渲染。
    • 使用 this.setState 方法更新 state 会导致重新渲染。
  2. Props 变化

    • 当父组件传递给子组件的 props 发生变化时,子组件会重新渲染。
    • 这也是为什么组件应该是纯函数的一个原因,即它们的输出应该完全由输入(props 和 state)决定。
  3. 强制更新

    • 使用 this.forceUpdate 方法可以强制组件重新渲染。通常不推荐使用这个方法,因为它绕过了 React 的优化机制。
  4. Context 变化

    • 当使用 React Context 时,如果 Context 的值发生变化,所有使用该 Context 的组件都会重新渲染。
  5. 父组件变化

    • 父组件的渲染会带着子组件一起重新渲染一遍。

重新渲染就会用diff算法来对新旧VNode进行比对。

7.React如何判断什么时候重新渲染组件?

写了componentShouldUpdate就根据返回值来判断,true就重新渲染,false就放弃这次渲染。

8.无状态组件

就是没有自己的状态,就负责展示,可以传递props给它展示。

9.如何获取DOM元素

  1. 在类组件中使用 ref

    在类组件中,你可以通过创建一个 ref 并将其附加到一个 DOM 元素上来获取对该元素的引用。

    import React, { Component } from 'react';
    
    class MyComponent extends Component {
      constructor(props) {
        super(props);
        // 创建一个 ref
        this.myRef = React.createRef();
      }
    
      componentDidMount() {
        // 访问 DOM 元素
        console.log(this.myRef.current);
      }
    
      render() {
        return <div ref={this.myRef}>Hello, World!</div>;
      }
    }
    
    export default MyComponent;
    

10.React中可以在render访问refs吗?为什么?

不能,要在render之后。

11.对React的插槽(Portals)的理解,如何使用,有哪些使用场景

Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案。

React 提供了 ReactDOM.createPortal 方法来创建一个 Portal。这个方法接收两个参数:

  1. 子组件(或元素)
  2. DOM 节点
import React from 'react';
import ReactDOM from 'react-dom';

class Modal extends React.Component {
  render() {
    return ReactDOM.createPortal(
      <div className="modal">
        {this.props.children}
      </div>,
      document.getElementById('modal-root')
    );
  }
}

// 在你的 HTML 文件中,需要有一个 id 为 'modal-root' 的元素,就是入口文件
// <div id="modal-root"></div>

class App extends React.Component {
  render() {
    return (
      <div className="app">
        <h1>My App</h1>
        <Modal>
          <p>This is a modal!</p>
        </Modal>
      </div>
    );
  }
}

export default App;

应用:弹窗类用的最多。

12.在React中如何避免不必要的render?

  1. shouldComponentUpdate
  2. PureComponent
  3. React.memo

13.对 React context 的理解

用来组件通信的

import React from 'react';

// 创建一个 Context 对象,并设置默认值
const ThemeContext = React.createContext('light');

export default ThemeContext;
 //父组件
 <ThemeContext.Provider value={this.state.theme}>
        <div>
          <button onClick={this.toggleTheme}>Toggle Theme</button>
          <ThemedComponent />
        </div>
</ThemeContext.Provider>
//子组件
  <ThemeContext.Consumer>
    {theme => (
      <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
        <h2>Themed Component</h2>
        <p>The current theme is {theme}</p>
      </div>
    )}
  </ThemeContext.Consumer>

14.React中什么是受控组件和非控组件

指那些表单数据由React组件状态state控制的表单元素,表单元素的值和组件的状态同步。

15.React中refs的作用是什么?有哪些应用场景?

直接获取DOM元素或者React组件实例,但是函数组件没有实例,直接获取的话会报错,所以要用到forwardRef

const A = forwardRef((props,ref)=>{
    //接收两个参数
   	//	props表示传参
    //	转发ref给其他能获取到实例的元素
    return  <input ref={ref} type="text" />
})
import React, { useRef, useEffect, forwardRef } from 'react';

// 定义一个函数组件并使用 React.forwardRef 转发 ref
const FunctionComponent = forwardRef((props, ref) => (
  <input ref={ref} type="text" />
));
//------------------------------------------------------------------
const ParentComponent = () => {
  const inputRef = useRef(null);

  useEffect(() => {
    // 组件挂载后,让输入框自动获得焦点
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return <FunctionComponent ref={inputRef} />;
};

export default ParentComponent;

16.React中除了在构造函数中绑定this,还有别的方式吗?

一起列举出来

  1. 在构造函数中绑定 this

    class MyComponent extends React.Component {
      constructor(props) {
        super(props);
        this.state = { count: 0 };
        this.handleClick = this.handleClick.bind(this);
      }
    
      handleClick() {
        this.setState({ count: this.state.count + 1 });
      }
    
      render() {
        return <button onClick={this.handleClick}>Click me</button>;
      }
    }
    
  2. 使用箭头函数定义类方法(类字段语法)

    class MyComponent extends React.Component {
      state = { count: 0 };
    
      handleClick = () => {
        this.setState({ count: this.state.count + 1 });
      };
    
      render() {
        return <button onClick={this.handleClick}>Click me</button>;
      }
    }
    
  3. 在jsx中使用箭头函数

    class MyComponent extends React.Component {
      state = { count: 0 };
    
      handleClick() {
        this.setState({ count: this.state.count + 1 });
      }
    
      render() {
        return <button onClick={()=>this.handleClick()}>Click me</button>;
      }
    }
    
  4. 在jsx中用bind,this.handleClick.bind(this);

17.React组件的构造函数有什么作用?它是必须的吗?

构造函数的主要作用:初始化状态和绑定事件处理器中的 this

所以如果props不涉及初始化,又不需要处理this的问题的话,那就可以不用构造函数。

class LikeButton extends React.Component {
  constructor() {
    super();
    this.state = {
      liked: false
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState({liked: !this.state.liked});
  }
  render() {
    const text = this.state.liked ? 'liked' : 'haven't liked';
    return (
      <div onClick={this.handleClick}>
        You {text} this. Click to toggle.
      </div>
    );
  }
}
ReactDOM.render(
  <LikeButton />,
  document.getElementById('example')
);

18.setState的用法和原理

推荐使用函数式的,可以获取到正确的状态。

this.setState((prevState, props) => ({
 ...
}),callback);
              
//第二个参数是回调函数,state完成更新后执行

useState()也是的,如果依赖旧状态,也建议写函数式的

import React, { useState } from 'react';

function Counter() {
  // 声明一个叫做 "count" 的状态变量,并赋初始值为 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount((prevCount )=>prevCount +1)}>
        Click me
      </button>
    </div>
  );
}
  1. 状态更新请求: 当 setState 被调用时,React 接收到一个状态更新请求,这个请求可以是一个对象或者一个函数。
  2. 合并状态: React 将新的状态与当前状态合并(浅合并),但不会立即更新 this.state。相反,React 会将状态更新放入一个队列中,稍后处理。
  3. 批量更新: 为了提高性能,React 会批量处理多个 setState 调用。在事件处理、生命周期方法或合成事件中,状态更新不会立即生效,而是会被暂存并在稍后的一个批处理中一起处理。但是如果在一些原生事件中,setState可能是同步的,因为合成事件不会处理。
  4. 重新渲染: 批处理完成后,React 会重新计算组件的状态和属性,并触发组件的重新渲染。React 会调用 render 方法生成新的虚拟 DOM,然后通过与旧的虚拟 DOM 进行对比(diffing),找到需要更新的部分,并进行实际的 DOM 更新。

19.React中defaultProps

在 React 16.3 及更高版本中,推荐使用 defaultProps 静态属性来定义默认属性值:

  • 类组件

    class MyComponent extends React.Component {
      static defaultProps = {
        name: 'Default Name'
      };
    
      render() {
        return <div>{this.props.name}</div>;
      }
    }
    
    // 使用 defaultProps 的效果与 getDefaultProps 相同
    <MyComponent /> // 渲染结果:<div>Default Name</div>
    
  • 函数组件

    直接使用默认入参就行了

20.React中的setState和replaceState的区别是什么?

replaceState已经被废弃了,不了解。

21.React性能优化在哪个生命周期?它优化的原理是什么?

shouldComponentUpdate(nextProps,nextState),可以决定组件是否更新。

22.state和 props 触发更新的生命周期分别有什么区别?

getDerivedStateFromProps仅在 props 变化时调用。

23.React-Router的实现原理是什么?

客户端路由实现的思想:

  • 基于 hash 的路由:通过监听hashchange事件,感知 hash 的变化
    • 改变 hash 可以直接通过 location.hash=xxx
  • 基于 H5 history 路由:
    • 改变 url 可以通过 history.pushState 和 resplaceState 等,会将URL压入堆栈,同时能够应用 history.go() 等 API
    • 监听 url 的变化可以通过自定义事件触发实现

24.React-Router怎么设置重定向?

使用 <Navigate> 组件

25.React-Router如何获取URL的参数?

  1. 路径参数

         // 定义路由
         <Route path="/user/:id" component={UserComponent} />
    
         // 获取参数
         const { id } = useParams();
    
  2. 查询参数

         // 定义路由
         <Route path="/search" component={SearchComponent} />
    
         // 获取查询参数
         const [searchParams] = useSearchParams();
         const query = searchParams.get('query');
    
  3. state参数

    const navigate = useNavigate(); 
    navigate('/user', { state: { id: 123, name: 'John Doe' } }); // 导航并通过 state 传递参数
    
    ---------------------------
        
    const location = useLocation();
    const userState = location.state; // 获取通过 state 传递的参数
    
  4. 其他的钩子

    import React from 'react';
    import { useMatch } from 'react-router-dom';
    
    const About = () => {
      const match = useMatch('/about');
    
      return (
        <div>
          <h1>About Page</h1>
          {match && <p>This is the About page</p>}
        </div>
      );
    };
    
    export default About;
    

26.useLayoutEffect

用法和useEffect一样,普通的useEffect会再浏览器完成绘制之后执行,但是useLayoutEffect会在DOM变更后就执行(浏览器绘制之前) ,这有什么卵用呢?

比如说要写一个tooltip,当上方的空间不够了,那就应该尝试出现在下方,所以要在浏览器完成绘制之前也就是DOM一旦变更就可以去测量上方的空间够不够了,上方空间不够用立马就更改他的位置,让浏览器再次渲染一遍。

27.React 数据持久化有什么实践吗?

redux-persist

28.Redux原理

在React中,组件通过connect连接到 Redux ,如果要访问 Redux,需要dispatch一个包含 type和负载(payload) 的 Action。Action 中的 payload 是可选的,Action 将其转发给 Reducer。

当Reducer收到Action时,通过 switch…case 语法比较 Action 中type。 匹配时,更新对应的内容返回新的 state。

当Redux状态更改时,连接到Redux的组件将接收新的状态作为props。当组件接收到这些props时,它将进入更新阶段并重新渲染 UI。

import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './actions';

function App({ count, increment, decrement }) {
  return (
    <div>
      <button onClick={increment}> + </button>
      <p>{count}</p>
      <button onClick={decrement}> - </button>
    </div>
  )
}

//把store中的数据映射到组件的props中
const mapStateToProps = state => {
  return {
    count: state.count,
  }
}

//把actions映射到组件的props中
const mapDispatchToProps = {
  increment,
  decrement,
}

// 使用 connect 函数连接组件和 Redux store
export default connect(mapStateToProps, mapDispatchToProps)(App)

29.reducer

reducer 函数接收当前状态 state 和一个动作 action,然后根据 action.type 返回新的状态。

函数组件中使用useReducer

import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
}

export default Counter;

30.useTransition

延迟的更新:使用 useTransition 标记的状态更新会被延迟,直到所有高优先级的更新完成。

可中断的更新:过渡更新可以被高优先级的更新中断,以保持应用的响应性。

指示过渡状态:可以获取过渡是否进行中的状态,以便在 UI 中展示加载指示器等。

import React, { useState, useTransition } from 'react';

function App() {
  const [isPending, startTransition] = useTransition();
  const [count, setCount] = useState(0);

  const handleClick = () => {
    startTransition(() => {
      setCount(count + 1);
    });
  };

  return (
    <div>
      <button onClick={handleClick}>Increment</button>
      {isPending ? <p>Loading...</p> : <p>Count: {count}</p>}
    </div>
  );
}

export default App;

31.useDeferredValue

创造一个延迟更新的值,第一次返回旧值,当其他优先级高的任务执行完了再创造一个新值。

32.React.lazy()和Suspense

用于路由懒加载

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Suspense, lazy } from 'react';
const Home = lazy(() => import('./routes/Home'))
const count = lazy(() => import('./routes/count'))
 

export default class App extends Component {
  render() {
    return (
      <div>
          <Router>
            <Suspense fallback={<h1>Loading...</h1>}>
              <Switch>
                <Route exact path="/" component={Home}/>
                <Route path="/count" component={count}/>
                ...
              </Switch>
            </Suspense>
          </Router>
      </div>
    )
  }
}

33.JSX是怎么变成真实的DOM的

JSX 解析和编译:JSX 通过 Babel 编译成 React.createElement 调用。

创建虚拟 DOMReact.createElement 创建虚拟 DOM 对象。

构建虚拟 DOM 树:React 通过虚拟 DOM 对象构建虚拟 DOM 树。

比较和更新虚拟 DOM 树:React 比较新旧虚拟 DOM 树,找出差异。

更新真实 DOM:React 将差异转换为实际的 DOM 操作,更新真实 DOM。

渲染真实 DOM:React 将虚拟 DOM 树中的元素渲染成真实的 DOM 元素,并插入页面。

34.为什么 Hooks 不能在条件判断中使用?

React 依赖 Hook 调用顺序来正确关联状态和副作用

35.如何通过 DOM 元素找到对应的 Fiber 对象

具体来说,React 在内部创建 DOM 元素时,会在 DOM 元素上附加一个 __reactFiber$ 前缀的属性,该属性的值是 fiber 对象。

36.React源码

36-1.JSX转换成ReactElement的过程

调用了createElement方法

/**
* 创建React Element
* type  元素类型
* config  配置属性
* children  子元素
* type  元素类型
*	1.分离props属性和特殊属性
*	2.将子元素挂在到props.children中
*	3.为props属性赋默认值
*	4.创建并返回ReactElement
*/
function createElement(type,config,children){
    ...
}

如何判断一个参数是ReactElement?

看对象的$$typeof属性

object.$$typeof === REACT_ELEMENT_TYPE
36-2.React架构
  • Scheduler(调度层):调度任务的优先级,高优先级的先进入协调器。
  • Reconciler(协调层):构建Fiber数据结构,对比Fiber对象找出差异,记录Fiber对象要进行的DOM操作。
  • Renderer(渲染层):将变化的部分渲染到页面上。

调度层和协调层的工作是在内存当中进行的,渲染层设定的就是不可以打断的,所以不会出现DOM渲染不完整的问题。

36-3. Fiber数据结构
type Fiber= {
    /************DOM实例相关************/
    
    //表示Fiber节点的类型 用于协调和渲染的时候做判断的
	tag:WorkTag,
    
    //React元素的类型 DOM元素就是字符串("div","span") React元素就是组件的构造函数或者类
    type:any,
    
    //与Fiber节点关联的实例对象,对于DOM元素来说是实际的DOM节点,对于类组件来说是组件实例
    stateNode: any,
    
    /************构建Fiber树相关的************/
    
    //父Fiber节点,表示树结构中的父节点
  	return: Fiber | null,

  	//子Fiber节点,表示树结构中的第一个子节点。
  	child: Fiber | null,

  	//兄弟Fiber节点,表示树结构中当前节点的下一个兄弟节点。
  	sibling: Fiber | null,
    
    //有了这些数据,无论出于Fiber树种哪个位置,都能快速的找到父级 子级 兄弟级节点了
    
    //指的是 workInProgress Fiber节点
    alternate: Fiber | null,
    
    /************状态数据相关的************/
    
    //将用于渲染的新属性
    pendingProps: any,

  	//上一次渲染时使用的属性
  	memoizedProps: any,

  	//上一次渲染时使用的状态
 	 memoizedState: any,
    
     /************副作用相关************/
    
    //该Fiber对应的组件所产生的状态更新都会放在这个队列里
    updateQueue:updateQueue<any> | null,
    
    //用来记录当前Fiber要执行的DOM操作,比如说当前对应的DOM节点要做插入 删除等操作
    effectTag:sudeEffectTag,
    
    //指向当前Fiber节点的第一个副作用Fiber节点。
    firstEffect:Fiber | null,
    
    //指向当前Fiber节点的下一个副作用Fiber节点。
    nextEffect:Fiber | null,
    
    //指向当前Fiber节点的最后一个副作用Fiber节点。
    lastEffect:Fiber | null,
    
    //通过设置不同的expirationTime,React可以控制各个更新任务的执行顺序,确保用户交互等高优先级任务能及时响应。
    expirationTime:ExpirationTime,
    
    //表示当前Fiber节点的渲染模式  并发渲染,同步渲染,不使用特定的渲染模式等等
    mode:TypeOfMode,  
}
36-4.双缓存技术

React最多同时存在两颗Fiber树,屏幕中看到的讲座current Fiber树,当发生更新的时候,React会在内存里面重新构建一颗workInProgress Fiber树。当workInProgress Fiber树构建好了直接替换current Fiber树,更新就更流畅了。

36-5.FiberRoot和RootFiber
  • FiberRoot

    是React应用的顶层结构,包含了整个应用的渲染状态和调度信息。它主要负责管理整个应用的渲染和更新过程。

  • RootFiber

    是整个React应用的顶层Fiber节点,是FiberRoot的一个属性。rootFiber表示当前渲染的Fiber树的根节点,是从这个节点开始递归遍历整个Fiber树。

36-6.整个过程
  1. render方法的调用其实是returnlegacyRenderSubtreeIntoContainer的结果。
  2. legacyRenderSubtreeIntoContainer是为container<div id = 'app'>)创建或者获取FiberRoot,然后开始下一步调用updateContainer
  3. 更新过程就是调用了updateContainerupdateContainer会创建任务(初始化渲染或者更新渲染)放在任务队列里面,等待浏览器空闲执行。
  4. 任务执行调用scheduleUpdateOnFiber,这会进入渲染阶段,渲染阶段包括了协调和提交两个子阶段。
  5. 在协调阶段会对比新旧Fiber树的差异,React会建立一个Effect List,用于在提交阶段执行所有的副作用。每个Fiber节点都会有一个effectTag,用于标记要执行的操作,例如更新、插入、删除等等。
  6. 提交阶段又分了三个小阶段,Before Mutation、Mutation和Layout。
  7. Before Mutation会处理DOM更新前要处理的副作用,例如getSnapshotBeforeUpdate
  8. Mutation就处理DOM节点的添加更新等。
  9. Layout会执行所有需要再DOM变更之后处理的副作用例如 componentDidMountcomponentDidUpdate
  10. DOM的更新。
  11. 在所有的更新完成之后,React会执行需要清理的任务,例如 useEffect 中的回调函数。

项目优化

框架中的项目都差不多

  • 编码阶段

    1. 使用路由懒加载、异步组件
    2. 第三方模块按需导入
    3. 图片懒加载
    4. 防抖和节流
    5. 减少响应式数据
    6. 减少组件的嵌套
    7. 虚拟列表
  • 打包阶段

    1. SplitChunksPlugin分包
    2. Tree Shaking树摇
    3. TerserPlugin用于压缩JS文件
    4. css-minimizer-webpack-plugin压缩CSS文件
    5. hash生成唯一的文件名
    6. thread-loader使用多线程加载,提升构建速度。
    7. cache-loader缓存编译的结果
    8. happyPack使用多线程加载,提升构建速度。
    9. webpack-bundle-analyzer分析打包的结果
    10. SpeedMeasurePlugin Webpack 构建过程中的各个步骤所花费的时间。
  • 用户体验

    1. 使用cdn加载第三方模块
    2. 利用缓存

总结

专注收集整理Java面试题,不定期给大伙分享面试中的高频题与大厂难题。
如果你觉得文章对你有用,可以点赞和关注哦!❤
欢迎大家在评论区留下你宝贵的建议📢

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值