面试必备 Vue React Webpack 全掌握

Vue 基本使用

指令、插值

  • 插值、表达式
  • 指令、动态属性
  • v-html 会有 xss 风险,会覆盖子组件

computed 和 watch

  • computed 有缓存,data 不变则不会重新计算
  • watch 如何深度监听
  • watch 监听引用类型,拿不到 oldVal
1
2
3
4
5
6
watch : {
handler (oldVal, val) {
console.log('watch info', oldVal, val)
},
deep: true
}

class style

  • 使用动态属性
  • 使用驼峰式写法

条件渲染

  • v-if v-else 的用法,可以使用变量,也可以使用 === 表达式
  • v-if 和 v-show 的区别
  • v-if 和 v-show 的使用场景

循环列表的渲染

  • 如何遍历对象——也可以使用 v-for
  • key 的重要性,key 不能乱写,如 random 或者 index
  • v-for 和 v-if 是不能一起使用的

事件

  • event 参数,自定义参数($event,原生 event)
  • 事件修饰符,按键修饰符
  • 【观察】事件被绑定到哪里(写在哪,挂载在哪)

image.png

image.png

表单

  • v-model
  • 常见的表单项, textarea,checkbox,radio,select
  • 修饰符 lazy number trim

image.png

vue 组件使用

  • props 和 $emit
  • 组件间通讯-自定义事件
  • 组件生命周期

单个组件

  • 挂载阶段
  • 更新阶段
  • 销毁阶段

created vue 实例化完成
mounted 元素挂载完成

父子组件
image.png
创建从外到内,渲染从内到外
image.png

image.png

Vue 高级特性

  • 自定义 v-model
  • $nextick
  • slot
  • 动态、异步组件
  • keep-alive
  • mixin

v-model

1
<intput :value="text" @input="$emit('change', $event.target.value)" />
1
2
3
4
5
6
7
8
9
10
export default {
model: {
prop: "text",
event: "change",
},
props: {
text: String,
default: "",
},
};

$nextTick

  • Vue 是异步渲染
  • data 改变之后,DOM 不会立刻渲染
  • $nextTick 会在 DOM 渲染之后被触发,以获取最新的 DOM 节点

image.png

slot

  • 基本使用
  • 作用域插槽
  • 具名插槽

动态组件

  • :is=”component-name” 用法 <component :is="NextTick" />
  • 需要根据数据,动态渲染的场景。即组件类型不确定

Vue 如何加载异步组件

  • import() 函数
  • 按需加载,异步加载大组件

keep-alive

  • 缓存组件
  • 频繁切换,不需要重复渲染
  • Vue 常见的性能优化

mixins

  • 多个组件有相同的逻辑,抽离出来
  • mixin 并不是最完美的解决方案,会有一些问题
  • Vue3 提出的 CompositionAPI 旨在解决这些问题

mixin 缺点

  • 变量来源不明确,不利于阅读
  • 多个 mixin 可能会造成命名冲突
  • mixin 和组件可能会出现多对多的关系,复杂度高

Vuex 使用

基本概念

  • state
  • getters
  • action
  • mutation

用于 vue 组件

  • dispatch
  • commit
  • mapState
  • mapGetters
  • mapActions
  • mapMutations

image.png

VueRouter

  • 路由模式,(hash,H5 history)
  • 路由配置,(动态路由,懒加载)

Vue 原理

  • 组件化
  • 响应式
  • vdom 和 diff
  • 模板编译
  • 渲染过程
  • 前端路由

组件化基础

  • “很久以前”就有组件化
  • 数据驱动视图(MVVM,setState)

  • 传统组件,只是静态渲染,更新还是要依赖于操作 DOM
  • 数据驱动视图 - Vue MVVM
  • 数据驱动视图 - React setState

Vue MVVM

image.png

Vue 响应式

  • 核心 API Object.defineProperty
  • proxy 有兼容性的问题,且无法 polyfill
  • 监听对象,监听数组
  • 复杂对象,深度监听
  • 几个缺点

  • Object.defineProperty 深度监听,需要递归到底,一次性计算量大
  • 无法监听新增属性,删除属性
  • 无法监听原生数组,需要做特殊处理

虚拟 DOM 和 diff

  • vdom 是实现 vue 和 React 的重要基石
  • diff 算法是 vdom 中最核心最关键的部分
  • vdom 是一个热门话题,也是面试中最热门的话题

  • DOM 的操作非常耗费性能
  • 以前使用 jQuery,可以自行控制 DOM 操作的时机,手动调整
  • Vue 和 React 是数据驱动视图,如何有效的控制 DOM 操作

  • 有了一定复杂度,想减少计算次数比较难
  • 能不能把计算,更多的转移为 JS 计算,因为 JS 执行速度很快
  • vdom - 用 js 模拟 DOM 结构,计算出最小的变更,操作 DOM

  • vue3 重写了 vdom 的代码,优化了性能
  • 但是 vdom 的基本里边不变,面试考点也不变

snabbdom 使用

  • 用 JS 模拟 DOM 结构(vnode)
  • 新旧 vnode 对比,得出最小的更新范围,最后更新 DOM
  • 数据驱动视图的模式下,有效控制 DOM 的操作

diff 算法

树 diff 的时间复杂度是 O(n^3)——算法不可用的
优化时间复杂度到 O(n)

  • 只比较同一层级,不跨级比较
  • tag 不相同,则直接删掉重建,不再深度比较
  • tag 和 key, 两者都相同,则认为是相同节点,不在深度比较

image.png
image.png
h 函数: 返回一个 vnode

  • patchNode
  • addVnodes removeVNodes
  • updateChildren

  • 细节不重要,updateChildren 的过程也不重要,不要深究
  • vdom 的核心概念很重要,h,vnode,patch、diff、key
  • vdom 存在的价值更加重要:数据驱动视图,控制 DOM 操作

模板编译

  • 模板是 vue 开发中最常用的部分,即与使用相关联的原理
  • 他不是 html,有指令、插值、JS 表达式到底是什么
  • 面试不会直接问,但是会通过“组件渲染和更新过程”考察

  • v-if 三元表达式
  • v-for _l renderList
  • 事件
  • v-model

vue-template-compiler

组件 渲染/更新 过程

  • 响应式:监听 data 属性的 getter 和 setter (包括数组)
  • 模板编译:模板到 render 函数,再到 vnode
  • vdom :patch(ele, vnode) patch(vnode, newVNode)

初次渲染的过程

  • 解析模板为 render 函数,(或者在开发环境中已经完成,vue-loader)
  • 触发响应式,监听 data 属性 getter setter
  • 执行 render 函数,生成 vnode,patch(ele, vnode)

更新过程

  • 修改 data, 触发 setter(此前在 getter 中已被监听)
  • 重新执行 render 函数,生成 newVNode
  • patch(vnode, newVnode)

image.png

异步渲染

  • 回顾$nexttick
  • 汇总 data 的修改,一次性更新视图
  • 减少 DOM 的操作次数,提高性能

前端路由的原理

路由模式

  • hash
  • history

hash 的特点

  • hash 变化会触发网页的跳转,即浏览器的前进后退
  • hash 变化不会刷新页面,SPA 必备特点
  • hash 永远不会提交到 server 端

window.onhashchange = () => {}

H5 history

  • 用 url 规范的路由,但跳转时不刷新页面
  • history.pushState
  • window.onpopstate
  • 需要后端配合

Vue 面试真题

v-show v-if 区别

为何在 v-for 中使用 key

  • 必须用 key,且不能是 index 和 random
  • diff 算法中通过 tag 和 key 来判断,是否是 sameNode
  • 减少渲染次数提升渲染性能

描述 Vue 组件的生命周期

  • 单组件的生命周期图
  • 父子组件生命周期关系

Vue 组件如何通讯

  • 父子组件 props 和 this.$emit
  • 自定义事件,event.$on event.$off event.$emit
  • vuex

描述组件渲染和更新的过程

image.png

双向数据绑定 v-model 的实现原理

  • ipnut 元素的 value = this.name
  • 绑定 input 事件 this.name = $event.target.value
  • data 更新触发 re-render

对 mvvm 的理解

computed 有何特点

  • 缓存,data 不变不会重新计算
  • 提高性能

为何组件 data 必须是一个函数

ajax 请求应该放在那个生命周期中

  • mounted
  • js 是单线程的,ajax 异步获取数据
  • 放在 mounted 之前没有用只会让逻辑更加复杂

如何将组件所有 props 传递给子组件

  • v-bind=”$props”

如何自己实现一个 v-model

多个组件有相同的逻辑,怎么抽离

mixin

何时要使用 异步组件

  • 加载大组件
  • 路由

何时需要使用 keep-alive

  • 缓存组件,不需要重复渲染
  • 性能优化

何时需要使用 beforeDestory

  • 解绑自定义事件 event.$off
  • 清除定时器
  • 解绑自定义的 DOM 事件

什么是作用域插槽

Vuex 中 action 和 mutation 有什么区别

image.png

vue-router 常用的路由模式

vue-router 异步加载

请用 vnode 描述一个 DOM 结构

监听 data 变化的核心 API 是什么

Vue 如何监听数组变化

请描述响应式原理

Diff 算法的时间复杂度

简述 diff 算法的过程

image.png

Vue 为何是异步渲染,$nextTick 何用

Vue 性能优化

  • 合理使用 v-show 和 v-if
  • 合理使用 computed
  • v-for 时加 key ,避免和 v-if 同时使用
  • 自定义事件,以及 DOM 事件要及时销毁
  • 合理使用 异步组件
  • 合理使用 keep-alive
  • data 层级不要太深
  • 使用 vue-loader 在开发环境做模板预编译
  • webpack 层的优化
  • 前端通用的优化,图片懒加载
  • 使用 SSR

Vue3

Vue3 对 Vue2 有什么优势

  • 性能更小
  • 体积更小
  • 更好的 TS 开发的
  • 更好的代码组织
  • 更好的逻辑抽离

Composition API 和 Options API

  • 不建议共用,会引起混乱
  • 小型项目业务逻辑简单,用 Options API
  • 中大型项目、复杂逻辑,用 Composition API

ref

  • 生成值类型的响应式数据
  • 可用于模板和 reactive
  • 通过 .value 修改值

toRef

  • 针对一个响应式对象(reactive 封装)的 prop
  • 创建一个 ref,具有响应式
  • 两者保持引用关系

toRefs

  • 将响应式对象(reactive 封装)转换为普通对象
  • 对象的每一个 prop 都是对应的 ref
  • 两者保持引用关系

toRef 和 toRefs 的最佳使用方式

  • 用 reactive 做对象的响应式,用 ref 做值类型的响应式
  • setup 中返回 toRefs(state) 或者 toRef(state, ‘xxx’)
  • ref 的变量命名都用 xxxRef
  • 合成函数返回响应式对象时,使用 toRefs

深入理解 ref

为什么使用 ref

  • 返回值类型,会丢失响应式
  • 如 setup, computed,合成函数,都有可能返回值类型
  • Vue 如不定义 ref,用户会自造 ref,反而混乱

为什么需要.value

  • ref 是一个对象(不丢失响应式),value 存储值
  • 通过 .value 属性的 get 和 set 实现响应式
  • 用于模板、reactive 时,不需要.value,其他情况下都需要

为什么需要 toRef 和 toRefs

初衷:不丢失响应式的情况下,把数据对象 分解/扩散
前提:针对的是响应式对象(reactive 封装的)非普通对象
注意:不创造响应式,而是延续响应式

vue 升级了哪些重要的功能

  • createApp
  • emit 属性
  • 生命周期
  • 多事件
  • Fragment
  • 移除 .sync
  • 异步组件的写法
  • 移除 filter
  • suspense
  • composition API

Vue2 如何实现响应式

Object.defineProperty 的缺点

  • 深度监听需要一次性递归
  • 无法监听新增属性和删除属性
  • 无法监听数组,需要特殊处理

image.png

Reflect 作用

  • 和 Proxyu 能力一一对应
  • 规范化、标准化、函数式
  • 代替 object 的工具函数

Proxy 实现响应式

  • 深度监听,性能更好
  • 可监听 新增、删除属性
  • 可监听数组变化

image.png

watch 和 watchEffect 区别

  • 两者都可以监听 data 属性变化
  • watch 需要明确监听哪个属性
  • watchEffect 会根据其中的属性,自动监听其变化

image.png
image.png

setup 中如何获取组件的实例

  • 在 setup 和其他 compositionAPI 中没有 this
  • 可以通过 getCurrentInstance 获取当前实例
  • 若使用 options API 可照常使用 this

Vue3 为何比 Vue2 快

  • Proxy 响应式
  • PatchFlag
  • hoistStatic
  • cacheHandler
  • SSR 优化
  • tree-shaking

PatchFlag

  • 编译模板时,动态节点做标记
  • 标记,分为不同类型,如 TEXT,PROPS
  • diff 算法时,可以区分静态节点,以及不同类型的动态节点

image.png

hoistStatic

  • 将静态节点的定义,提升到父作用域,缓存起来
  • 多个相邻的静态节点会被合并起来
  • 典型的那空间换时间的优化策略

cacheHandler

  • 缓存事件

SSR 优化

  • 静态节点直接输出,绕过了 vdom
  • 动态节点,还是需要动态渲染

vite 是什么

  • 一个前端的打包工具,Vue 作者发起的项目
  • 借助 Vue 的影响力,发展较快,和 webpack 竞争
  • 优势: 开发环境下无需打包,启动快

Vite 为何启动快

  • 开发环境使用 ES Module,无需打包——非常快
  • 生产环境下使用 rollup,并不会快很多

es module: type=’module’

Composition API 和 react hooks 对比

  • 前者 setup 只会调用一次,而后者函数会被调用很多次
  • 前者无需调用 useMemo,useCallback 因为 setup 只会调用一次
  • 前者无需顾虑调用的顺序,而后者需要保证 hooks 的顺序一致
  • 前者 reactive 和 ref 比后者 useState,要更加难以理解

Vue3 和 JSX

image.png

jsx 和 template 区别

  • 语法上是有很大的区别的

  • 本质是相同的

  • 具体示例,插值,自定义组件,属性和事件,条件和循环

  • jsx 的本质就是 js 代码,可以使用 js 的任何能力

  • template 只能嵌入简单的 js 表达式,其他需要指令,如 v-if

  • jsx 已经成为 es 规范,template 还是 Vue 自家的规范

Vue3 script setup

React

关于 react 17

  • 没有明显的新特性,使用是和 react 16 是一致的
  • 面试时也不会被重点考察

React 基本使用

事件

  • bind this
  • 关于 event 参数
  • 传递自定义参数

react 中的 event 不是原生的 event ,而是 syntheticEvent(组合事件)

react 事件和 DOM 事件的区别

  • event 是 SyntheticEvent ,模拟出原生事件所有的能力
  • event.nativeEvent 是原生事件对象
  • 多有的时间都被挂载到 document 上
  • 和 DOM 时间不一样,和 Vue 事件也不一样

image.png

React 16 绑定到 document 上
react 17 绑定在 root 组件上
有利于多个 react 版本并存,例如微前端

image.png

表单

  • 受控组件
  • input textarea select 用 value
  • checkbox radio 用 checked

setState

  • 不可变值
  • 可能是异步更新
  • 可能会被合并

setState 是同步还是异步的

  • 正常情况下是异步的
  • setTimeout 中 setState 是同步的
  • 自定义 DOM 事件中也是同步的

setState 可能会被合并

  • 传入对象会被合并
  • 传入函数不会被合并

单组件生命周期

image.png

生命周期

React 高级特性

  • 函数组件
  • 非受控组件
  • Portals
  • context
  • 异步组件
  • 性能优化
  • 高阶组件 HOC
  • Render Props

函数组件

  • 输入 props 输出 jsx
  • 没有实例,没有生命周期,没有 state
  • 不能扩展其他方法

什么是非受控组件

  • ref
  • defaultValue defaultChecked
  • 手动操作 DOM 元素

使用场景

  • 必须手动操作 DOM 元素,setState 实现不了
  • 文件上传,input type=’file’
  • 某些富文本编辑器,需要传入 DOM 元素

受控组件 vs 非受控组件

  • 优先使用受控组件,符合 React 设计原则
  • 必须操作 DOM 时,再使用非受控组件

Portals

  • 组件默认会按照既定的层级嵌套渲染
  • 如何让组件渲染到父组件外

ReactDOM.createPortals

image.png

context

  • 公共信息(语言,主题)如何传递给每个组件
  • 用 props 太繁琐
  • 用 redux 小题大做

react 如何加载异步组件

image.png

性能优化

  • shouldComponentUpdate
  • PureComponent 和 React.memo
  • 不可变值 immutable.js

image.png

React 默认:父组件更新,子组件则默认无条件更新
性能优化对于 React 更加重要!

SCU 一定每次都要用吗——需要的时候才会优化

shouldComponentUpdate 在做深层比较的时候,如果使用 isEqual 会对数组或者对象做深层比较,(一次性递归到底)

  • SCU 默认返回 true,即 React 默认重新渲染所有子组件
  • 必须配合不可变值一起使用
  • 可先不使用 shouldComponentUpdate ,有性能问题时在考虑使用

PureComponent 和 memo

  • PureComponent,scu 中实现了浅比较
  • React.memo. 函数组件中的 PureComponent
  • 浅比较已经适用大部分情况(尽量不要做深度比较)

immutable.js

  • 彻底拥抱“不可变值”
  • 基于共享数据(不是深拷贝),速度好
  • 有一定学习和迁移成本,按需使用

image.png

关于组件公共逻辑的抽离

  • mixin 已经被 React 废弃
  • 高阶组件 HOC
  • render props

高阶组件不是一种功能,而是一种模式

  • 透传所有 props(Vue 中 v-bind $props)
  • 增加 mouse 属性

redux connect 是高阶组件

向组件属性中传入 render 函数

Redux 使用

  • 基本概念
  • 单向数据流
  • react-redux
  • 异步 action
  • 中间件

基本概念

  • store state
  • action
  • reducer

流程

  • dispatch(action)
  • reducer -> newState
  • subscribe 触发通知

react-redux

  • connect
  • mapStateToProps mapDispatchToProps

image.png
image.png

  • redux-thunk
  • redux-promise
  • redux-saga

image.png

react router

  • 路由模式(hash ,H5 history)
  • 路由配置(动态路由,懒加载)
  • 掌握基本使用

React 原理

  • 大厂使用 React 概率更高
  • react 对开发人员要求更高
  • 大厂更偏爱考察原理

  • 函数式编程
  • vdom 和 diff 算法
  • JSX 的本质
  • 合成事件
  • setState batchUpdate
  • 组件渲染过程

函数式编程

  • 一种编程范式,概念比较多
  • 纯函数
  • 不可变值

vdom 和 diff

  • h 函数
  • 返回 vnode 数据结构
  • patch 函数

diff 算法的改造

  • 只比较同一层级,不跨级比较
  • tag 相同,则之间删掉重建,不再深度比较
  • tag 和 key ,两者都相同,则认为是相同节点,不在深度比较

  • Vue2 Vue3 React 三者实现 vdom 细节均不相同
  • 核心概念和实现思路都一样
  • 面试主要考察后者,不用全部掌握细节

JSX 的本质

  • JSX 等同于 Vue 的模板
  • Vue 模板不是 HTML
  • JSX 也不是 JS

  • React.createElement 即 h 函数,返回 vnode
  • 第一个参数,可能是组件,也可能是 html tag
  • 组件名,首字母必须大写(React 规定)

react 合成事件机制

image.png

为什么要合成事件机制

  • 更好的兼容性和跨平台
  • 挂载到 document 上,减少内存消耗,避免频繁解绑
  • 方便事件的统一管理

image.png

React 的 batchUpdate 机制

  • 有的时候是异步的(普通使用),有时同步(setTimeout,DOM 事件)
  • 有时合并(对象形式),有时不合并(函数形式)
  • 后者比较好理解,向 Object.assign ,主要讲解后者

  • setState 主流程
  • batchUpdate 机制
  • transaction (事物)机制

image.png

image.png

  • setState 无所谓异步还是同步
  • 看是否可以命中 batchUpdate 机制
  • 判断 isBatchingUpdates

那些可以命中 batchUpdate 机制

  • 生命周期(和它调用的函数)
  • React 中注册的时间(和它调用的函数)
  • React 可以“管理”的入口

  • setTimeout setInterval 等(和它调用的函数)
  • 自定义的 DOM 事件(和它调用的函数)
  • React “管不到”的入口

简述 transaction 事务机制

image.png
image.png

组件渲染和更新过程

  • JSX 如何渲染为页面
  • setState 之后如何更新页面
  • 面试考察全流程

  • props state
  • render() 生成 vnode
  • patch(elem, vnode)

  • setState(newState) –> dirtyComponents(可能有子组件)
  • render() 生成 newVnode
  • patch(vnode, newVnode)

更新的两个阶段

  • 上述 patch 被拆分成了两个阶段
  • reconciliation 阶段 - 执行 diff 算法,纯 JS 计算
  • commit 阶段 - 将 diff 结果渲染 DOM

如果不拆分成两个阶段,可能会有性能问题

  • js 是单线程,且和 DOM 渲染公用一个线程
  • 当组件足够复杂,组件更新时计算和渲染都压力大
  • 同时再有 DOM 操作需求,(动画,鼠标拖拽),将卡顿

解决方案 fiber

  • 将 reconciliation 阶段进行任务拆分,(commit 无法拆分)
  • DOM 需要渲染时暂停,空闲时恢复
  • window.requestIdleCallback

React 面试真题

组件间如何通讯

JSX 的本质是什么

context 是什么如何应用

shouldComponentUpdate 的用途

redux 单向数据流

setState 场景题

image.png

什么是纯函数

React 组件证明周期(单组件,父子组件)

ajax 应该在那个生命周期上

渲染列表,为何使用 key

函数组件和 class 组件的区别

什么是受控组件

何时使用异步组件

多个组件有公共逻辑,如何抽离

redux 如何进行异步请求

react-router 如何配置懒加载

image.png

PureComponent 有何区别

React 事件和 DOM 事件的区别

React 性能优化

  • 渲染列表时使用 key
  • 自定义事件 DOM 事件及时销毁
  • 合理使用异步组件
  • 减少函数 bind this 的次数
  • 合理使用 shouldComponentUpdate PureComponent 和 memo
  • 合理使用 Immuta.js
  • webpack 层面的优化
  • 前端通用的性能优化,如图片懒加载
  • 使用 SSR

React 和 Vue 的区别

共同点

  • 都支持组件化
  • 都是数据驱动视图
  • 都使用 vdom 操作 DOM

不同点

  • React 使用 jsx 拥抱 js ,vue 使用模板拥抱 html
  • React 函数式编程,Vue 声明式编程
  • React 更多的需要自力更生,Vue 把想要的都给你

Webpack

  • webpack 已经是前端打包构建的不二选择
  • 每日必用,面试必考
  • 成熟的工具,重点在于配置和使用,原理并不高优

webpack 5

  • webpack 5 主要是内部效率的优化
  • 对于 webpack4 ,没有太多使用上的改动
  • 你可以直接使用 webpack5

webpack 基本配置

  • 拆分配置 和 merge
  • 启动本地服务
  • 处理 es6
  • 处理样式
  • 处理图片
  • (模块化)

postcss 兼容性处理

webpack 高级配置

  • 多入口

image.png

  • 抽离 css 文件

image.png

  • 抽离公共代码

image.png

  • 懒加载
  • 处理 jsx 处理 vue

module chunk bundle 的区别

  • module - 各个源码文件,webpack 中一切皆模块
  • chunk - 多模块合成的 如 entry import() splitChunk
  • bundle - 最终输出的文件

image.png

webpack 性能优化

webpack 怎么优化打包构建速度,开发体验和效率
优化产出代码 - 产品性能

优化 babel-loader
image.png

ignorePlugin 避免引用无用模块
image.png
image.png

noParse 避免重复打包
image.png

ignorePlugin vs noParse

  • IgnorePlugin 直接不引入,代码中没有
  • noParse 引入,但不打包

happyPack 多进程打包

  • JS 单线程,开启多进程打包
  • 提高构建速度,特别是多核 CPU

image.png
image.png

parallelUglifyPlugin 多进程压缩 JS

  • webpack 内置 uglify 工具压缩 JS
  • JS 单线程,开启多进程压缩更快
  • 和 happyPack 同理

image.png

关于开启多线程

  • 项目较大,打包较慢,开启多线程能提升速度
  • 项目较小,打包较快,开启多进程会降低速度
  • 按需使用

自动刷新
image.png

热更新

  • 自动刷新:整个网页全部刷新,速度较慢
  • 自动刷新网页的状态会丢失
  • 热更新就是新代码生效,网页不刷新,状态不丢失

image.png
image.png
image.png

dllPlugin 动态链接库插件

  • 前端框架如 vue react 体积大,构建慢
  • 较稳定,不常更新版本
  • 同一个版本只构建一次即可,不用每次都重新构建

  • webpack 已经内置了 DllPlugin 支持
  • DllPlugin - 打包出 dll 文件
  • DllReferencePlugin - 使用 dll 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const path = require("path");
const DllPlugin = require("webpack/lib/DllPlugin");
const { srcPath, distPath } = require("./paths");

module.exports = {
mode: "development",
// JS 执行入口文件
entry: {
// 把 React 相关模块的放到一个单独的动态链接库
react: ["react", "react-dom"],
},
output: {
// 输出的动态链接库的文件名称,[name] 代表当前动态链接库的名称,
// 也就是 entry 中配置的 react 和 polyfill
filename: "[name].dll.js",
// 输出的文件都放到 dist 目录下
path: distPath,
// 存放动态链接库的全局变量名称,例如对应 react 来说就是 _dll_react
// 之所以在前面加上 _dll_ 是为了防止全局变量冲突
library: "_dll_[name]",
},
plugins: [
// 接入 DllPlugin
new DllPlugin({
// 动态链接库的全局变量名称,需要和 output.library 中保持一致
// 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
// 例如 react.manifest.json 中就有 "name": "_dll_react"
name: "_dll_[name]",
// 描述动态链接库的 manifest.json 文件输出时的文件名称
path: path.join(distPath, "[name].manifest.json"),
}),
],
};

image.png

webpack 优化构建速度(可以用于生产环境)

  • 优化 babel-loader include exclude
  • IgnorePlugins
  • noParse
  • happyPack
  • ParallelUglifyPlugin

webpack 优化构建速度(不可用于生产环境)

  • 自动刷新
  • 热更新
  • DllPlugin

webpack 性能优化 - 产出代码

  • 体积更小
  • 合理分包,不重复加载
  • 速度更快,内存使用更小

  • 小图片 base64 编码
  • bundle 加 hash
  • 懒加载
  • 提取公共代码
  • IgnorePlugin
  • 使用 CDN 加速
  • 使用 production
  • Scope Hosting

使用 Production

  • 开启代码压缩
  • vue react 等会删掉调试代码(如开发环境下的 warning)
  • 启用 tree shaking

必须使用 ES6 Module 才能让 tree-shaking 生效
commonjs 就不行

ES Module 和 Commonjs 的区别

  • ES module 静态引入,编译时引入
  • commonjs 动态引入,执行时引入
  • 只有 ES6 Module 才能静态分析,实现 Tree-shaking

Scope Hosting

  • 代码体积会更小
  • 创建函数作用域更少
  • 代码的可读性更好

image.png

babel

  • 环境搭建
  • .babelrc 配置
  • preset 和 plugins

preset 是 plugins 的集合

什么是 babel-polyfill

  • 什么是 polyfill
  • core-js 和 regenerator
  • babel-polyfill 就是上面两者的集合

  • babel-polyfill 在 babel 7.4 之后被弃用
  • 推荐直接使用 core-js 和 regenerator

core-js: polyfill 集合

babel-polyfill 如何按需引入

  • 文件较大
  • 只有一部分功能,无需全部引入
  • 配置按需引入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 3,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}

问题

  • 污染了全局环境
  • 如果做一个 web 系统,则无碍
  • 如果是一个第三方的 lib,会有问题

babel-runtime

Webpack 面试题

前端为什么要进行打包构建

  • 体积更小(tree-shaking、压缩、合并),加载更快
  • 编译高级语言或语法(TS,ES6+,模块化,scss)
  • 兼容性和错误检查(Polyfill,postcss,eslint)

  • 统一高校的开发环境
  • 统一的构建流程和产出标准
  • 继承公司构建规范(提测、上线)

module chunk bundle 的区别

loader 和 plugins 的区别

常见 loader 和 plugins

babel 和 webpack 的区别

如何产出一个 lib

babel-polyfill 和 babel-runtime 的区别

webpack 如何实现懒加载

为何 Proxy 不能被 polyfill

webpack 优化构建速度

webpack 优化产出代码

组件设计

  • 从功能上拆分层次
  • 尽量让组件原子化
  • 容器组件(只管理数据) UI 组件(只显示视图)

React Hooks

  • 可选功能
  • 100% 向后兼容,没有破坏性改动
  • 不会取代 class 组件,尚无计划要移除 class 组件

函数组件的特点

  • 没有组件实例
  • 没有生命周期
  • 没有 state 和 setState,只能接受 props

class 组件的问题

  • 大型组件很难拆分和重构,很难测试(即 class 不易拆分)
  • 相同业务逻辑,分散到各个方法中逻辑混乱
  • 复用逻辑复杂,Mixins,HOC,Render Props

react 组件更易用函数表达

  • react 倡导函数式编程
  • 函数更灵活更易拆分,更易测试
  • 但函数组件太简单,需要增强能力——hooks

state hook

  • 默认组件没有 state
  • 函数组件是一个纯函数,执行完就销毁,无法储存 state
  • 需要 state hook ,即把 state 功能“钩”到纯函数中

Effect

  • didMount didUpdate - useEffect
  • didMount - useEffect(()=>{},[])

image.png

模拟 unmount 的时候注意事项

image.png

其他 hooks

  • useRef
  • useContext
  • usereducer
  • useMemo
  • useCallback

image.png

自定义 hooks

  • 封装通用的功能
  • 开发和使用第三方 hooks
  • 自定义 hook 带来无线的扩展性,解耦代码

Hooks 使用规范

  • 只能用于 react 组件和自定义 hook 中,其他地方不可以
  • 只能用于顶层代码,不能用于循环判断中使用 hooks
  • eslint 插件 eslint-plugin-react-hooks

为何 Hooks 要依赖于调用顺序

  • 函数组件,纯函数,执行完即销毁
  • 所以,无论组件初始化,还是组件更新
  • 都会执行过一次这个函数,获取最新的组件
  • render: 初始化 state 的值 “张三”
  • re-render:读取 state 的值 “张三”

  • render: 添加 effect 函数
  • re-render:替换 effect 函数,内部的函数也会重新定义

class 组件逻辑复用

  • mixin 早已废弃
  • 高阶组件 HOC
  • render props

mixin 问题

  • 变量作用域来源不清楚
  • 属性重名
  • mixin 引入过多会导致顺序冲突

HOC

  • 组件层级嵌套过多,不易渲染,不一调试
  • HOC 会劫持 props,必须严格遵守规范,容易出现疏漏

render props

  • 学习成本较高,不易理解
  • 只能传递纯函数,默认情况下纯函数功能有限

Hooks 组件逻辑复用有什么好处

  • 完全符合 Hooks 原有规则,没有其他要求,易理解记忆
  • 变量作用域很明确
  • 不会产生组件嵌套

React Hooks 注意事项

  • useState 初始化值,只有第一次有效
  • useEffect 不能修改 state
  • useEffect 可能出现死循环