面试问1:请问你平时都是如何 优化项目 or 打包 的?
优化策略:安装visualizer看图,针对大区块进行着重优化,配置vite.config,引入CDN。
亮点:针对性分析优化 + 什么手段 + 解决了什么。
最终结果
安装相关Npm依赖
分析优化前占比图(预览图)
单条拆分优化
1、排除打包 *.json 文件 + 动态加载
2、echarts、element-plus、jsoneditor 不做外联确保稳定。(可自主选择优化)
3、通过 CDN 方式挂载:markdonwit、xlsx
推荐使用国内镜像:
- BootCDN: BootCDN
- 字节跳动: 字节跳动静态资源公共库
- 引用 +esm 适配 Esm模式
运行 depcheck 查看无用安装依赖
对未使用的依赖进行删除,需要确保不会对项目造成破坏,如 tailwindcss 被检测无使用,实际在使用中,故不能删除。删除后本地确保没问题,再提交!!!
- Unused:未使用依赖
- Missing:丢失依赖
打包输出配置,采用 esbuild
- 生产模式移除 输出/断点
- sourcemap:定位错误(自行选择)
- treeShake、cache:清理无用代码|缓存(默认true)
- output:命名代码分割时创建共享块
- external:排除不需要的打包文件/文件夹
全局引入改为局部/按需
Element-plus引入及图标
路由懒加载及搭配单个Chunk
- 通过 import 方式载入页面地址
- 根据打包配置中的 output 输出为 单个Chunk,实现按需载入
面试问2:下拉列数据量 很大很卡 怎么办?有什么优化策略吗?
优化策略:节流 + 自定义插件
亮点:自定义组件封装 + 自定义插件
1、实现防抖节流
特性 | 防抖(Debounce) | 节流(Throttle) |
---|---|---|
触发频率 | 最后一次操作后延迟执行 | 固定时间间隔内只执行一次 |
适用场景 | 搜索输入、窗口大小调整、表单验证等 | 滚动加载、按钮点击、鼠标移动等 |
2、创建自定义指令
关键点: binding.arg 是用于区别 同页面中存在多个下拉。
3、全局挂载(如没有多复用,则页面单独使用)
import permissionDirective from './utils/permission.directive' // 引用
app.directive('el-select-loadmore', loadMores) // (指令名,指令)
4、El-Plus组件使用自定义指令
面试问3:平时都用什么组件? 有封装过什么吗?
1、Echarts 图表进行封装,增加自适应窗口并统一管理调用
封装策略:复用性高 传值
亮点:二次封装、减少代码冗余、统一管理、ResizeObserver、自适应窗口大小
<template>
<div ref="chartRef" :style="{ width: width, height: height }"></div>
</template>
<script setup>
import echarts from 'echarts'
const props = defineProps(['options','width','height']
const chartRef = ref(null)
let myChart = null
// 1、初始化图表
const initChart = () => {
if (chartRef.value) {
if (myChart) { myChart.dispose() }
myChart.setOption(options)
}
}
// 2、ResizeObserver 监听容器大小变化
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
if (myChart && !myChart.isDisposed()) { myChart.resize() }
}
})
// 3、装载
onMounted(() => {
nextTick(() => {
initChart()
if (chartRef.value) { resizeObserver.observe(chartRef.value) }
})
})
// 4、卸载
onUnmounted(() => {
if (chartRef.value) { resizeObserver.unobserve(chartRef.value) }
if (myChart && !myChart.isDisposed()) { myChart.dispose() }
})
// 5、监听 options 的变化
watch(
() => props.options,
(newOptions) => {
nextTick(() => {
if (myChart && !myChart.isDisposed()) { myChart.setOption(newOptions) }
else { initChart()}
})
}, { deep: true }
)
</script>
2、对 ElementPlus 组件进行封装,通过传入组件方式统一管理
封装策略:复用性高 进阶Api 传值
亮点:二次封装、父子传递、统一管理、shallowRef
<template>
<el-drawer v-model="drawer" :with-header="false" :destroy-on-close="true">
<component :is="currentComponent" v-bind="dynamicAttrs" />
</el-drawer>
</template>
<script lang="ts" setup>
const props = defineProps({ toToData: Object, currentComponent: Object })
const drawer = ref(false)
// 动态组件引用
const currentComponent = shallowRef(props.currentComponent)
watch(
() => props.currentComponent,
(newValue) => { currentComponent.value = newValue },
{ immediate: true }
)
// 动态绑定的属性
const dynamicAttrs = computed(() => {
return props.toToData ? { toToData: props.toToData } : {}
})
// 展开/关闭
const exchangeStatus = () => { drawer.value = !drawer.value }
defineExpose({ exchangeStatus })
provide('exchangeStatus', { exchangeStatus })
</script>
// 使用方式
<DrawerComp
size="35%"
ref="draw"
:current-component="activityManageOpContent"
:toToData="parentData"
></DrawerComp>
面试问4:了解 vue3的生态吗?是如何选择的?
- 构建采用: v3 + vite (速度快,Tree机制)
- 路由状态: vue-router@4 + Pinia(轻量、TS 友好,替代 Vuex)
- 编码类型:强类型 TypeScript(方便定位错误类型)
- Ui 组件库: Element Plus
- CSS: Tailwind (统一全局样式 响应式布局) CSS scss(嵌套好管理)
- 图表/可视化: Echart
针对以上的生态提出问题:
-
vue3 为什么比 vue2 快?
-
vue3 可以用 vuex吗? vue2 可以用 pinia 吗?
-
平时都是怎么做响应式布局的?
-
平时都是怎么管理项目/代码的?
-
封装过echart吗?
后续编写记录。后续会开单独面试题。
- 多页面引用则单独抽离,传值配置
- 减少多层嵌套,提早return
- 命名规范,驼峰命名 + 常量大写
- 组件抽离及语法糖
- 这里不一一介绍了,看官方文件,背背理解理解,如果记不住就抄吧。组合式 API:setup() | Vue.js