React+webpack高频面试题

这篇文章主要包含有 React 的函数组件、类组件、react Hooks、webpack


函数组件和类组件

        1.React 组件之间如何通讯?

                 父给子传参:Props 

                子给父传参:Callback Functions - 回调函数

                Context、redux

        2.JSX本质是什么?

               jsx是语法糖,需要被编译成js才能运行。
               jsx 看似是html 结构,实质是js结构的语法糖,在代码编译阶段被编译成js结构。所以jsx的本质可描述为看似html结构的js结构。

        3.Context 是什么,如何应用?

                是一种跨层级的组件数据传递,提供了一种在组件之间共享此类值的方式

//创建新文件定义Context
import React from 'react';
const UserContext = React.createContext('');
export default UserContext;

//需要传递的数据
import React from 'react';
import UserContext from './UserContext';

const App: React.FC = () => {
  const user = 'John Doe'; // 这是要共享的数据

  return (
    <UserContext.Provider value={user}>
      {/* 其他组件 */}
    </UserContext.Provider>
  );
};

export default App;

//在任何需要访问共享数据的子组件中,使用Consumer来接收数据
//Consumer是一个函数组件,它接受当前Context的值作为参数,并返回一个React节点
import React from 'react';
import UserContext from './UserContext';

const WelcomeMessage: React.FC = () => {
  return (
    <UserContext.Consumer>
      {(user) => (
        <h2>Welcome, {user}!</h2>
      )}
    </UserContext.Consumer>
  );
};

export default WelcomeMessage;

        4.说一下 shouldcomponentUodate 的用途

        shouldcomponentUodate主要是用于优化的作用,他会接受两个参数,一个是nextProps和nextState,分别代表这即将更新的下一个属性和状态,这个方法需要返回一个布尔值来决定要不要重新渲染,起到了避免重复渲染的作用                      

        5.说一下redux 单向数据流的机制

        先通过事件dispatch派发action事件给store,store接受action并且传递给reducer进行处理action类型和payload值对状态的更新,Store最后通知所有订阅者 改变的数据重新在页面渲染。

Redux的单一Store和纯函数Reducer的设计使得状态管理更加容易理解和维护

        6.React 头组件的 setstate 是同步操作还是异步操作?

         自React18更新后,setState被默认为异步,特殊情况也是同步,在setTimeout、promise,axios或者setState在原生事件中是同步的,即通过dom绑定事件的方式实现。这都是根据相应的环境来定的

        7.什么是纯函数

         纯函数就是传入的值是什么样输出就是什么样的,不会对任何状态进行改变 也没有副作用

        8.介绍React 组件生命周期

        主要分为3个模块:挂载、更新、卸载

组件生命周期的执行次数是什么样子的

        只执行一次: constructor、componentWillMount、componentDidMount

        执行多次:render 、子组件的componentWillReceiveProps、componentWillUpdate、componentDidUpdate

        有条件的执行:componentWillUnmount(页面离开,组件销毁时)

        不执行的:根组件(ReactDOM.render在DOM上的组件)的componentWillReceiveProps(因为压根没有父组件给传递props)

react生命周期

初始化阶段

① constructor 执行

在 mount 阶段,实例化 React 组件,组件中 constructor 就是在这里执行的。 在实例化组件之后,会调用 mountClassInstance 组件初始化。

② getDerivedStateFromProps 执行

在初始化阶段,getDerivedStateFromProps 是第二个执行的生命周期,值得注意的是这是个静态方法,传入 props ,state 。 返回值将和之前的 state 合并,作为新的 state ,传递给组件实例使用。

③ componentWillMount 执行

如果存在 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 就不会执行生命周期componentWillMount。

④ render 函数执行

⑤componentDidMount执行

执行顺序:constructor -> getDerivedStateFromProps / componentWillMount -> render -> componentDidMount

更新阶段

①执行生命周期 componentWillReceiveProps

首先判断 getDerivedStateFromProps 生命周期是否存在,如果不存在就执行componentWillReceiveProps生命周期。传入该生命周期两个参数,分别是 newProps 和 nextContext 。

②执行生命周期 getDerivedStateFromProps

getDerivedStateFromProps, 返回的值用于合并state,生成新的state。

③执行生命周期 shouldComponentUpdate

shouldComponentUpdate传入新的 props ,新的 state ,和新的 context ,返回值决定是否继续执行 render 函数,调和子节点。这里应该注意一个问题,getDerivedStateFromProps 的返回值可以作为新的 state ,传递给 shouldComponentUpdate 。

④执行生命周期 componentWillUpdate

⑤执行 render 函数

得到最新的 React element 元素。然后继续调和子节点。

⑥执行 getSnapshotBeforeUpdate

生命周期的返回值,将作为第三个参数传递给 componentDidUpdate

⑦执行 componentDidUpdate

此时 DOM 已经修改完成。可以操作修改之后的 DOM 。

更新阶段对应的生命周期的执行顺序: componentWillReceiveProps( props 改变) / getDerivedStateFromProp -> shouldComponentUpdate -> componentWillUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate

销毁阶段

①执行生命周期 componentWillUnmount

在一次调和更新中,如果发现元素被移除,就会调用 componentWillUnmount 生命周期,接下来统一卸载组件以及 DOM 元素。

        9.React发起ajax应该在哪个生命周期

        componentDidMount

        因为在componentWillMount这个生命周期中他的调用次数是不确定的 可能会多次频繁的去调用不是一个很好的选择 但是在componentDidMount中可以避免在组件未挂载报错的问题

        10.渲染列表,为何使用 key?

        将新旧两棵虚拟 DOM 树进行 diff 对比,计算出 patch 补丁,打到真实 DOM 树上

只做同层级的节点对比,不跨层级比较

        11.函数组件和class 组件的区别

        语法上的区别: 函数式组件是一个纯函数,它是需要接受props参数并且返回一个React元素就可以了。类组件是需要继承React.Component的,而且class组件需要创建render并且返回React元素,语法上来讲更复杂。

        调用方式 函数式组件可以直接调用,返回一个新的React元素;类组件在调用时是需要创建一个实例的,然后通过调用实例里的render方法来返回一个React元素。

        状态管理 函数式组件没有状态管理,类组件有状态管理。

        使用场景 类组件没有具体的要求。函数式组件一般是用在大型项目中来分割大组件(函数式组件不用创建实例,所有更高效),一般情况下能用函数式组件就不用类组件,提升效率。

        12.什么是受控组件、什么是非受控组件

        受控组件其实就是我们对某个组件状态的掌控,他的值是否只能由用户设置,而不能通过代码控制。通过onChange事件来监听输入内容的改变并使用setState更新

        非受控组件:input标签它实际也是一个DOM元素,我们可以用获取DOM元素信息的方式来获取表单元素的值呢?也就是使用 ref 来从 DOM 节点中获取表单数据

        13.何时使用异步组件

        异步组件通常用于提高应用程序的性能和用户体验。

  1. 加快页面加载速度:异步加载可以避免一次性加载过多的 JS 和 CSS 文件,减少页面加载时间。

  2. 减小 JS 文件体积:将组件异步加载,使得每个页面只加载必要的 JS,在不同页面仅保留不同组件的 JS 文件,从而减小整个应用的 JS 文件体积。

  3. 提高用户体验:延迟加载组件可以使用户感觉页面更加流畅和响应快速。

        14.多个组件有公共逻辑,如何抽商?

             高阶组件(Higher-Order Components):高阶组件是一个函数,接收一个组件作为参数,并返回一个新的组件。通过高阶组件,可以将一些常用的逻辑封装起来,并将其应用于多个组件。这样,多个组件就可以共享相同的逻辑代码。

             Render Props 模式:Render Props 是一种在组件之间通过 prop 共享代码的技术。通过将一个函数作为组件的 prop,并在组件内部调用这个函数,实现将共享逻辑传递给子组件。子组件可以通过调用该函数来获取共享逻辑的结果。

             自定义 Hooks:自定义 Hooks 是一种函数,用于封装可重用的逻辑。通过自定义 Hook,可以将一些常用的逻辑代码抽离出来,并在多个组件中使用。自定义 Hook 可以返回任意值,包括状态、事件处理函数等。

        15.Redux 如何进行异步请求

通常使用中间件来进行,比如redux-thunk和redux-saga

Redux Thunk 是一个中间件,允许在 Redux action 中返回函数而不仅仅是纯对象。这使得我们能够在 action 中进行异步操作,并在操作完成后分发一个新的 action

Redux Saga 是另一个常用的 Redux 中间件,它使用生成器函数(Generators)的方式来处理异步操作。它提供了一种声明式的方法来管理和处理副作用

        16.React-router 如何配置懒加载?

         路由懒加载 lazy

        注意⚠️

1、通常情况下,path 和 component 是一一对应的关系。

2、Switch 可以提高路由匹配效率(单一匹配),匹配到之后不会继续向下查找匹配。

3、Switch可以解决router的唯一渲染,能够保证一个路由只渲染一个组件路径。

4、如果不使用Switch,当一个path匹配到多个component时,会将所有匹配到的component都渲染出来。

        17.什么是 PureComponent?        

    PureComponent也就是纯组件

        起到了一个优化的作用

        当组件更新时,如果组件的props和state都没有发生改变,render方法就不会触发,也省去了虚拟dom的生成和diff过程,具体就是react自动帮我们做了一层浅比较

        18.React 事件和DOM 事件有什么区别

        React 事件和 DOM 事件在语法和处理方式上有一些区别,React 的事件系统是对底层 DOM 事件的封装和扩展,提供了更方便、更一致的事件处理方式,并且能够更好地与 React 组件的生命周期集成。

事件名称的命名方式不同:

  • DOM 事件使用小写字母形式的事件名称,例如 clickchange

  • React 事件使用驼峰命名法的事件名称,例如 onClickonChange

事件处理函数的传递方式不同:

  • DOM 事件处理函数直接作为属性传递给 DOM 元素,通常以字符串形式表示,例如 <button onClick="handleClick()">Click me</button>

  • React 事件处理函数是通过 JSX 语法直接绑定到组件的属性上,例如 <button onClick={handleClick}>Click me</button>

事件对象的访问方式不同:

  • DOM 事件处理函数可以通过参数或全局变量 event 来访问事件对象,例如 function handleClick(event) { console.log(event.target); }

  • React 事件处理函数通过自动绑定的方式,在事件处理函数中直接访问事件对象,无需显式传参,例如 function handleClick() { console.log(event.target); }

事件冒泡和默认行为的处理方式不同

  • DOM 事件会按照捕获和冒泡的规则在 DOM 树上进行传播,可以使用 event.stopPropagation() 来停止事件冒泡,使用 event.preventDefault() 来阻止默认行为。

  • React 事件是基于合成事件系统,它模拟了冒泡过程,但并不是真实的 DOM 事件,因此不能使用 DOM 的方法来阻止冒泡或阻止默认行为。要阻止事件冒泡,可以在事件处理函数中使用 event.stopPropagation();要阻止默认行为,可以通过在事件处理函数中调用 event.preventDefault() 返回 false 来实现。

        19.React 性能优化步式有哪些

  1. 组件按需加载

    组件按需加载优化又可以分为:懒加载、懒渲染、虚拟列表 三类。

  2. 批量更新

  3. 缓存优化

    在 React 组件中常用 useMemo 缓存上次计算的结果。当 useMemo 的依赖未发生改变时,就不会触发重新计算。

  4. 跳过不必要的组件更新

  5. PureComponent、React.memo

    PureComponent 和 React.memo 就是应对这种场景的,PureComponent 是对类组件的 Props 和 State 进行浅比较,React.memo 是对函数组件的 Props 进行浅比较

    b. shouldComponentUpdate

    c. useMemo、useCallback 实现稳定的 Props 值

    d. 发布者订阅者跳过中间组件 Render 过程

    e. 状态下放,缩小状态影响范围

        20.说一下React和vue的区别

  1. 架构设计:React是一个JavaScript库,专注于构建用户界面的视图层。它提供了一个组件化的开发模式,通过组件的嵌套和组合来构建应用程序。另一方面,Vue是一个渐进式框架,旨在构建整个应用程序。Vue提供了更多的功能,包括状态管理、路由和构建工具等。

  2. 学习曲线:React的学习曲线相对较陡,因为它需要开发者了解JavaScript语言和一些其他概念,如JSX和虚拟DOM。而Vue的学习曲线相对较平缓,它使用了类似HTML的模板语法,并且易于上手和理解。

  3. 灵活性:React提供了更高的灵活性,允许开发者自由选择使用的工具和库。它没有强制性的项目结构和约束。Vue则更加约束性,提供了一整套的工具和建议的项目结构,适合快速开发和入门。

  4. 生态系统:React拥有庞大且活跃的生态系统,有许多优秀的第三方库和组件可用。它被广泛采用,并得到了许多大型公司的支持和贡献。Vue的生态系统也在迅速发展中,虽然规模相对较小,但也有很多质量高的库可供选择。

  5. 性能:React使用虚拟DOM来优化性能。虚拟DOM使React可以更有效地更新和渲染页面的变化部分,从而提高应用程序的性能。Vue也使用虚拟DOM,并采用了一些其他优化策略,使其在性能方面表现出色。

  6. 社区支持:React拥有庞大而活跃的社区,开发者可以很容易地找到相关的教程、文章和解决方案。Vue的社区虽然规模较小,但也非常友好和积极,提供了大量的学习资源和帮助。

React Hooks

        列举十个常用的React 内置 Hooks

  1. useState: 用于在函数组件中创建和管理状态
  2. useEffect: 在函数组件渲染完成后执行副作用操作,如数据获取、订阅事件等。
  3. useContext: 在函数组件中访问React Context的值。
  4. useReducer: 类似于Redux中的reducer,用于复杂的状态管理。
  5. useCallback: 缓存一个回调函数,优化性能,避免不必要的重新渲染。
  6. useMemo: 缓存计算结果,避免重复计算,优化性能。
  7. useRef: 创建一个可变的引用,用于存储任意可变值。
  8. useLayoutEffect: 与useEffect类似,但会在DOM更新完成后同步执行副作用操作。
  9. useImperativeHandle: 自定义将ref暴露给父组件的实例方法。
  10. useDebugValue: 在React开发者工具中显示自定义标签的自定义hook。

        为什么会有React Hooks,他解决了哪些问题?

  1. 在类组件中,随着应用规模增大,状态逻辑变得复杂,可能会导致组件变得庞大、难以维护和理解。Hooks通过将状态逻辑分离为可复用的函数(称为自定义Hook)来解决这个问题,使得组件结构更清晰、易于管理。

  2. 在类组件中,为了共享状态逻辑,通常需要使用高阶组件(HOC)或渲染属性(Render Props)等方式。而Hooks提供了useState、useEffect等内置Hook,可以直接在函数组件内部使用,避免了类组件中繁琐的包装和嵌套,简化了代码结构。

  3. 在类组件中,为了处理副作用(例如数据获取、订阅事件等),需要在生命周期方法(如componentDidMount、componentDidUpdate)中编写相关逻辑。而Hooks提供了useEffect等内置Hook,可以在函数组件内部直接声明和管理副作用,避免了生命周期方法的使用,使得副作用逻辑更加集中和易于管理。

  4. 在类组件中,this关键字的使用可能会引起一些困惑。在函数组件中使用Hooks则无需关注this的指向,避免了绑定事件处理程序或在构造函数中绑定方法的问题,简化了代码编写。

  5. Hooks能够更好地优化性能。通过使用useCallback和useMemo等内置Hook,可以缓存回调函数和计算结果,避免不必要的重新渲染,提升应用程序的性能。  

        React Hooks 如何模拟组件的生命周期

        useEffect可以进行异步操作并且能够有一个依赖项进行一个监听

        如何自定义Hook ?

        函数名开头用use、内部可以调用其他Hooks

        说一下React Hooks性能优化

//使用useMemo缓存计算结果:当某个计算结果的成本较高,且只在依赖项变化时需要重新计算时,可以使用useMemo将计算结果缓存起来,避免不必要的重复计算。例如:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

//使用useCallback缓存回调函数:当某个回调函数作为props传递给子组件时,可以使用useCallback缓存该回调函数,避免在每次渲染时创建新的回调函数实例。这对于性能敏感的场景尤其有用。例如:
const memoizedCallback = useCallback(() => {
  // 回调函数
}, [dependencies]);

//使用React.memo进行组件的浅比较:React.memo是一个高阶组件,用于对函数组件进行浅比较,以确定是否重新渲染组件。通过使用React.memo,可以避免在没有变化的情况下重新渲染组件。例如:
const MyComponent = React.memo(({ propA, propB }) => {
  // 组件渲染逻辑
});

//使用useCallback和useEffect实现定时器和订阅的正确清理:当使用定时器或订阅时,需要在组件卸载时正确清理它们,以免引起内存泄漏。可以使用useCallback创建一个清理函数,并在useEffect中返回该函数来清理定时器或订阅。例如:
useEffect(() => {
  const timer = setInterval(() => {
    // 定时器逻辑
  }, 1000);

  return () => {
    clearInterval(timer); // 清理定时器
  };
}, []);

//使用React.lazy和Suspense实现组件的懒加载:当某个组件在初始渲染时并不需要立即加载,可以使用React.lazy和Suspense来实现组件的懒加载,提高应用程序的初始加载性能。例如:
const MyComponent = React.lazy(() => import('./MyComponent'));

function App() {
  return (
    <div>
      <Suspense fallback={<Spinner />}>
        <MyComponent />
      </Suspense>
    </div>
  );
}

        使用React Hooks遇到哪些坑?

  1. 忘记在组件顶层使用Hooks:在使用Hooks时,必须确保它们只在函数组件的顶层使用,不能在循环、条件语句或嵌套函数中使用。否则会导致Hook的调用顺序错误或状态无法正确保存。

  2. 没有正确处理依赖项数组:useEffect和useCallback等Hooks接收一个依赖项数组作为第二个参数,用于声明该Hook依赖的变量。如果不正确地处理该依赖项数组,可能导致副作用执行不正确或无法正确触发。

  3. 在条件语句中使用Hooks:在条件语句中使用Hooks时要格外小心。由于React要求Hook的调用顺序是固定的,因此在每次渲染时,条件语句中每个分支都必须调用相同数量和类型的Hooks,以保持调用顺序的一致性。

  4. 错误地使用useState:useState返回一个状态值和更新函数的数组,但不能将更新函数当做状态值本身使用。如果错误地将更新函数当做状态值传递给其他Hooks,可能会引发一些意想不到的问题。

  5. 忘记使用React.memo进行优化:React.memo是一个高阶组件,用于对函数组件进行浅比较,以确定是否重新渲染组件。如果忘记使用React.memo对组件进行优化,可能会导致不必要的重新渲染。

  6. 误用闭包中的引用:在循环中创建Hooks时,需要特别小心闭包中的变量引用。由于闭包会捕获变量最后一次的值,可能会导致状态更新不正确或无法触发。

  7. 错误使用useEffect的清理函数:在使用useEffect时,如果返回一个清理函数,必须确保该清理函数能够正确地清理订阅、定时器等。否则可能会导致内存泄漏或其他副作用问题。

        Hooks 相比 HOC 和 Render Prop 有哪些优点?

总的来说,React Hooks的优点在于简化了组件结构、提供了更方便的逻辑复用方式、增强了代码可读性和维护性,减少了组件的嵌套层级,并且具备更轻量的API。这使得开发者能够更加高效和灵活地开发React组件

  1. 简化组件结构:使用HOC或Render Prop时,需要在组件层级中增加额外的包裹组件或通过props传递函数。而Hooks能够在函数组件内部直接使用,避免了组件层级的增加,使组件结构更加简洁清晰。

  2. 逻辑复用更方便:HOC和Render Prop的方式在逻辑复用时需要显式地将逻辑代码包装成函数或组件,在每个使用该逻辑的地方进行调用或传递props。而Hooks可以在同一个函数组件内部定义和复用逻辑代码,使逻辑代码更容易组织和重用。

  3. 更好的代码可读性和维护性:Hooks让组件的逻辑更加集中和模块化,使代码的可读性和维护性更高。通过将相关的逻辑代码放在同一个自定义Hook中,可以更清晰地了解组件的功能和行为。

  4. 减少嵌套层级:使用HOC或Render Prop时,可能会造成多层嵌套的组件结构,增加了组件之间的耦合性和复杂度。而Hooks可以将逻辑代码直接放在函数组件中,减少了组件的嵌套层级,使代码更加扁平化。

  5. 更轻量的API:使用HOC或Render Prop时,需要编写额外的高阶组件或组件包裹逻辑,增加了代码量和维护成本。而Hooks是React的一部分,使用了原生的React API,提供了一些常用的Hooks(如useState、useEffect等),减少了开发者编写额外代码的工作量。

redux react-redux redux-thunk redux-saga 区别

redux

redux和组件进行对接的时候是直接在组件中进行创建

redux获取state是直接通过store.getState()。

redux是使用dispatch直接触发,来操作store的数据。

原理:首先react通过获取store中的数据并直接在页面渲染,一旦数据发生改变,react就会传递一个action动作,store将这个动作携带给reducer,reducer接收到指令后,通过一系列行为改变数据,再将更新好的数据传递给store,react就会获取store中的数据

react-redux

是运用Provider将组件和store对接,使在Provider里的所有组件都能共享store里的数据,还要使用connect将组件和react连接。

react-redux获取state是通过mapStateToProps函数,只要state数据变化就能获取最新数据

react-redux是使用mapDispathToProps函数然后在调用dispatch进行触发

原理:React-redux是一个redux的官方绑定react库,也是有三要素store,reducer,action 但是获取store中的数据与事件方法不一样,首先使用Provider标签将组件包裹起来,使store与组件对接,并且向store分发actions以更新数据,在组件中通过connect函数将组件与react对接,其中有两个参数一个是mapStatetoprops负责接收store中的数据,另一个是mapDispatchtoProps负责接收传递过来的的actions

redux-thunk  redux-saga都是异步管理中间件

总的来讲Redux Saga适用于对事件操作有细粒度需求的场景,同时它也提供了更好的可测试性,与可维护性,比较适合对异步处理要求高的大型项目,而小而简单的项目完全可以使用redux-thunk就足以满足自身需求了。毕竟react-thunk对于一个项目本身而言,毫无侵入,使用极其简单,只需引入这个中间件就行了。而react-saga则要求较高,难度较大

Webpack面试真题

        前端为何要进行打包和构建?

  1. 代码方面
    1. 体积更小,加载更快(tree-shaking,压缩合并)
    2. 编译高级语言和语法(ts,es6,模块化)
    3. 兼容性和错误提示(polyfill,postcss,eslint)
  2. 研发流程
    1. 统一、高效的开发环境
    2. 统一的构建流程和产出标准
    3. 集成公司构建规范(提测,上线)

        module chunk bundle 的区别

1、Module

Module(模块)是Webpack中最基本的概念,它代表着一个单独的文件(或一组相关的文件),它可以是JavaScript、CSS、图片、JSON等任何类型的文件。在Webpack中,每个Module都会被转换成一个或多个Chunk。

2、Chunk

Chunk(代码块)是Webpack打包过程中的中间产物,它代表着一组被合并在一起的Modules。通常情况下,Chunk是由多个Module组成的,Webpack会根据一定的规则将这些Module打包成一个Chunk。Chunk可以被进一步处理和优化,最终被合并成Bundle。

3、Bundle

Bundle(捆绑包)是Webpack最终输出的文件,它是由一组已经经过加载和编译的Chunk组成的。通常情况下,一个应用程序会生成一个Bundle,它包含了所有的JavaScript、CSS、图片等资源,可以被直接加载到浏览器中运行。

综上所述,Module是Webpack中最小的单元,它们组成了Chunk,而Chunk则最终被合并成Bundle。Bundle是最终生成的文件,可以被直接加载到浏览器中运行。

用一句话说明三者之间的关系:

module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字:

我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle。

        loader 和 plugin的区别

  • loader是一个转换器,将a文件进行编译输出b文件,这里是操作文件。单纯的文件转换。
  • plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行任务
  • Loader(加载器)
    Loader用于对模块的源代码进行转换。loader 可以使你在加载模块时预处理文件 。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。
    
    因为 webpack只能处理 JavaScript,如果要处理其他类型的文件,就需要使用 loader 进行转换,loader 本身就是一个函数,接受源文件为参数,返回转换的结果。
    
    Plugin(插件)
    Plugin 是用来扩展 Webpack 功能的。使用 plugin 丰富的自定义功能扩展以及生命周期事件,可以控制打包流程的每个环节。
    通过plugin,webpack可以实 loader 所不能完成的复杂功能。作用于整个构建周期,实现对 webpack 的自定义功能扩展。

        常见的loader和plugin有哪些

Loader:

eslint-loader    代码规范检查。eslint-config-airbnb eslint-config-alloy eslint-config-ivweb
babel-loader    postcss-loader    style-loader    css-loader    less-loader    file-loader    
url-loader    和 file-loader 类似。可以将小图片等资源以 base64 方式引用
px2rem-loader   

Plugin:

clean-webpack-plugin    
html-webpack-plugin    html 压缩、自动创建 html5 文件、自动引入编译后的 script 等
html-webpack-externals-plugin    将指定的资源设置成 CDN 等形式的外部引用,减少 bundle 大小
SplitChunksPlugin    公共脚本分离成公共包,并进行优化。webpack 自带
copy-webpack-plugin    将符合条件的文件复制到目标位置
friendly-errors-webpack-plugin    webpack 构建日志美化插件

        Babel 和 webpack的区别

webpack是一个打包工具,打包js、css、图片、html等,它可以分析整个项目的文件结构,确认文件之间的依赖,将文件合成、压缩、加入hash等,生成最终项目文件。

babel是一个JS编译器,用来将ES6/ES7等语法转换为ES5语法(浏览器不认识的语法编译成浏览器认识的语法),从而能够使代码在大部分浏览器中运行

        Webpack如何产出一个lib库?

1.配置Webpack:创建一个Webpack的配置文件,比如webpack.config.js。在配置文件中设置入口文件(entry)和输出文件(output)。入口文件是你的库的主要文件,而输出文件是最终生成的库文件。

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'my-lib.js',
    library: 'MyLib',
    libraryTarget: 'umd',
    globalObject: 'this',
  },
};
在上述配置中,entry指定了库的入口文件路径,output定义了输出文件的路径和名称,library设置了库的名称,libraryTarget指定了库的导出方式为通用模块定义(UMD),globalObject指定了在不同环境中使用库时的全局变量。

2.编写库的代码:在入口文件(比如index.js)中编写你的库的代码。这可以是你的库的主要逻辑、功能函数等。确保在代码中将需要暴露给外界的部分以合适的方式导出。
// index.js

export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

3.构建库:运行Webpack构建命令,根据配置文件进行库的构建。
    $ webpack --config webpack.config.js
运行以上命令后,Webpack将根据配置文件对入口文件进行打包和构建,生成最终的库文件。

4.使用库:最终生成的库文件可以在其他项目中使用了。你可以将其作为一种依赖方式通过npm或yarn进行安装,然后在你的项目中引入并使用。
javascript
import { add, subtract } from 'my-lib';

console.log(add(2, 3)); // 输出:5
console.log(subtract(5, 3)); // 输出:2

通过上述步骤,你就可以使用Webpack来产出一个可用的库。配置Webpack、编写库的代码、构建库和使用库是这个过程的关键步骤,确保按照正确的顺序和方法进行操作即可。

        说一下Babel-polyfill 和babel-runtime 的区别

先说两种方式的原理:

babel-polyfill,它不会将代码编译成低版本的ECMAScript,他的原理是当运行环境中并没有实现的一些方法,babel-polyfill中会给做兼容

babel-runtime,将es6编译成es5去运行,前端可以使用es6的语法来写,最终浏览器上运行的是es5

优缺点:

babel-polyfill:通过向全局对象和内置对象的prototype上添加方法来实现,比如运行环境中不支持Array-prototype.find,引入polyfill,前端就可以放心的在代码里用es6的语法来写;但是这样会造成全局空间污染。比如像Array-prototype.find就不存在了,还会引起版本之前的冲突。不过即便是引入babel-polyfill,也不能全用,代码量比较大。

babel-runtime:不会污染全局对象和内置的对象原型。比如当前运行环境不支持promise,可以通过引入babel-runtime/core-js/promise来获取promise,或者通过babel-plugin-transform-runtime自动重写你的promise。但是它不会模拟内置对象原型上的方法,比如Array-prototype.find,就没法支持了,如果运行环境不支持es6,代码里又使用了find方法,就会出错,因为es5并没有这个方法

        Webpack 如何实现懒加载?

其实直接import(’’)或者require.ensure就可以达到分割代码懒加载的需求,但是出于代码的美观健壮啊之类的原因,可以使用已经提供的懒加载组件,react-loadable或React.lazy()等等。

1、React.lazy配合React.Suspense。
React.lazy使用import来懒加载组件,import在webpack中最终babel会解析成调用require.Ensure的方法,动态插入script来请求js文件。

const Foo = React.lazy(() => import('../componets/Foo));
React.lazy不能单独使用,需要配合React.suspense,suspence是用来包裹异步组件,添加loading效果等。<React.Suspense fallback={<div>loading...</div>}><Foo/>
</React.Suspense>

2、react-loadable插件
使用方式:https://www.npmjs.com/package/react-loadable

import Loadable from 'react-loadable';
import Loading from './my-loading-component';const LoadableComponent = Loadable({loader: () => import('./my-component'),loading: Loading,
});export default class App extends React.Component {render() {return <LoadableComponent/>;}
}

3、关于图片的懒加载。
浏览器新引入了一个可以查看元素是否在视图窗口内的API:IntersectionObserver。
事件回调的参数是IntersectionObserverEntry 的集合,代表关于是否在可见视口的一系列值
其中,entry.isIntersecting 代表目标元素可见

const observer = new IntersectionObserver((changes) => {// changes: 目标元素集合changes.forEach((change) => {// intersectionRatioif (change.isIntersecting) {const img = change.targetimg.src = img.dataset.srcobserver.unobserve(img)}})
})observer.observe(img)

        为何 Proxy 不能被 Polyfill?

Proxy(代理)是ES6新增的一个特性,用于创建一个代理对象,可以拦截并自定义对目标对象的操作。而Polyfill(填充)是一种技术手段,用于在旧版本的JavaScript环境中实现新的JavaScript特性。

Proxy无法被Polyfill实现的主要原因是,Polyfill通过在全局对象上添加新的原生对象或方法来实现新特性的功能。而Proxy是一种底层机制,需要直接与JavaScript引擎交互才能实现。

        Webpack 如何优化构建速度?

1.提升开发体验:

使用SourceMap让开发或线上时代码报错能有更加准确的错误提示。

2.提升webpack提升打包构建速度

使用HotModuleReplacement让开发时只重新编译打包更新变化了的代码,不变的代码使用缓存,从而使更新速度更快。

使用OneOf让资源文件一旦被某个loader处理了,就不会遍历了,打包速度更快。

使用Include/Exclude排除或只检测某些文件,处理的文件更少,速度更快。

使用cache对eslint和babel处理结果进行缓存,第二次打包速度更快。

使用thread多进程处理eslint和babel任务,速度更快。

3.减少代码体积

使用Tree shaking 剔除了没有使用的多余代码,让代码体积更小。

使用@babel/plugin-transform-runtime 插件对babel进行处理,让辅助代码从中引入,而不是每个文件都生成辅助代码,从而体积更小。

使用Image Minimizer 对项目中图片进行压缩,体积更小,请求速度更快。(如果项目中图片是在线链接,那就不需要了,本地项目静态图片才需要进行压缩)

4.优化代码性能

使用code split 对代码进行分割成多个js文件,从而使单个文件体积更小,并行加载js更快,并通过import动态导入语法进行按需加载,东二达到使用时加载该资源,不用时不加载资源。

使用Preload/prefetch对代码进行提前加载,等未来需要使用时就能直接使用,从而用户体验更好,但是存在兼容性的问题,看情况使用。

使用NetWork Cache能对输出资源文件进行命名,将来做好缓存,用户体验更好。

使用core-js对js进行兼容性处理,让代码可以在低版本浏览器运行。

使用PWA能让代码离线也可以访问,提升用户体验,但是兼容性较差,看情况使用。

        Webpack 如何优化产出代码?

对生产环境的产出代码合理分包、不重复加载,让其体积更小,这样就能速度更快、内存使用更少,才能使得用户体验更好。

1)小图片base64编码;
2)bundle加hash;
3)懒加载;
4)提取公共代码;
5)使用CDN加速;
6)IgnorePlugin;
7)使用Production模式默认开启tree-shaking;
8)Scope Hosting;

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值