前端高频知识点汇总:从手写实现到工程化实践(面试&开发双视角)

前端高频知识点汇总:从手写实现到工程化实践(面试&开发双视角)

前言

本文汇总了前端开发/面试中高频的6个核心知识点,基于实际开发场景和面试问题展开,涵盖 Promise.all手写实现、懒加载(原生+Vue3)、Axios二次封装、HTTP状态码解析、数组扁平化去重排序、Vue3父子组件通信。每个知识点均包含「原理拆解、代码实现、易错点、优化方案」,既适合新手夯实基础,也适合面试者梳理核心考点,助力快速掌握前端核心技能。

一、手写Promise.all:从错误分析到正确实现

1.1 常见错误实现及原因

很多初学者手写Promise.all时易犯以下错误,导致代码无法运行或偏离原生规则:

javascript 复制代码
// 错误示例
function myPromiseAll(){
  const new Promise((resolve,reject)=>{ // 语法错:const不能修饰new实例
    const results=[]
    const res=Array.from(arguments) // 设计错:依赖arguments,违背原生可迭代对象参数规则
    res.forEach((res.index)=>{ // 语法错:forEach回调参数格式错误
      Promise.resolve(res).then(value=>{ // 逻辑错:处理整个数组而非单个元素
        results.push(value)
        const len=0; len++ // 语法错:const变量不可修改
        if(len===res.length) resolve(results)
      }.catch(err=>reject(err)) // 语法错:then回调未闭合
    })
  })
}

核心错误总结

  • 语法错误:const new Promiseconst重赋值、then/catch括号未闭合、forEach参数格式错;
  • 逻辑错误:依赖arguments(原生接收可迭代对象)、处理整个数组而非单个元素;
  • 设计偏差:未返回Promise、缺少可迭代对象校验、空数组处理。

1.2 正确实现(对齐原生规则)

javascript 复制代码
function myPromiseAll(iterable) {
  return new Promise((resolve, reject) => {
    try {
      // 1. 校验可迭代对象(对齐原生)
      if (typeof iterable[Symbol.iterator] !== 'function') {
        throw new TypeError(`${typeof iterable} ${iterable} is not iterable`);
      }
      const items = Array.from(iterable);
      const total = items.length;
      const results = [];
      let completed = 0;

      // 2. 空数组处理
      if (total === 0) {
        resolve(results);
        return;
      }

      // 3. 遍历处理单个元素
      items.forEach((item, index) => {
        Promise.resolve(item)
          .then(value => {
            results[index] = value; // 按索引存值,保证顺序
            completed++;
            if (completed === total) resolve(results);
          })
          .catch(err => reject(err)); // 一错即错
      });
    } catch (err) {
      reject(err);
    }
  });
}

核心要点

  • 接收「可迭代对象」(如数组),对齐原生Promise.all调用规则;
  • 按索引存储结果,保证输出顺序与输入一致;
  • 支持同步/异步元素,Promise.resolve统一处理;
  • 一错即错:任意元素reject,立即触发整体reject。

二、懒加载实现:从原生到Vue3工程化

懒加载核心是「延迟加载非可视区域资源」,减少首屏请求,提升性能,分「原生实现」和「Vue3插件实现」两种场景。

2.1 原生懒加载(兼容&现代两种方案)

方案1:传统实现(scroll + getBoundingClientRect)

兼容IE,需手动节流优化:

html 复制代码
<!-- HTML -->
<img class="lazy-img" data-src="real-img-1.jpg" alt="懒加载图片">

<script>
  // 节流函数
  function throttle(fn, wait = 100) {
    let timer = null;
    return (...args) => {
      if (!timer) {
        timer = setTimeout(() => {
          fn.apply(this, args);
          timer = null;
        }, wait);
      }
    };
  }

  // 判断元素是否进入视口
  function isInViewport(el) {
    const rect = el.getBoundingClientRect();
    const viewHeight = window.innerHeight || document.documentElement.clientHeight;
    return rect.top <= viewHeight + 100; // 提前100px加载
  }

  // 核心逻辑
  function handleLazyLoad() {
    const imgs = document.querySelectorAll('.lazy-img:not([data-loaded])');
    imgs.forEach(img => {
      if (isInViewport(img)) {
        img.src = img.dataset.src;
        img.dataset.loaded = 'true';
      }
    });
  }

  // 初始化+监听
  handleLazyLoad();
  window.addEventListener('scroll', throttle(handleLazyLoad));
  window.addEventListener('resize', throttle(handleLazyLoad));
</script>
方案2:现代实现(IntersectionObserver)

ES6+新增API,异步监听,性能更优:

html 复制代码
<img class="lazy-img" data-src="real-img-1.jpg" alt="懒加载图片">

<script>
  if ('IntersectionObserver' in window) {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.dataset.src;
          observer.unobserve(img); // 停止监听,释放内存
        }
      });
    }, { rootMargin: '100px 0' }); // 提前100px加载

    document.querySelectorAll('.lazy-img').forEach(img => observer.observe(img));
  } else {
    // 降级到传统方案
    handleLazyLoad();
    window.addEventListener('scroll', throttle(handleLazyLoad));
  }
</script>

2.2 Vue3+VueUse实现懒加载插件

基于useIntersectionObserver封装全局指令,适配Vue3工程化项目:

javascript 复制代码
// src/plugins/lazyPlugin.js
import { useIntersectionObserver } from '@vueuse/core';
import { ElMessage } from 'element-plus';
import 'element-plus/es/components/message/style/css';

export const lazyPlugin = {
  install(app) {
    // 注册全局自定义指令 v-img-lazy
    app.directive('img-lazy', {
      mounted(el, binding) {
        // 监听元素视口状态
        const { stop } = useIntersectionObserver(
          el, // 要监听的DOM元素
          ([{ isIntersecting }]) => { // 视口状态变化回调
            if (isIntersecting) {
              el.src = binding.value; // 赋值真实图片地址
              // 加载失败兜底
              el.onerror = () => {
                el.src = 'error-img.jpg';
                ElMessage.warning('图片加载失败');
              };
              stop(); // 停止监听
            }
          },
          { rootMargin: '100px 0' } // 提前加载
        );
      }
    });
  }
};

// 入口文件 main.js
import { createApp } from 'vue';
import App from './App.vue';
import { lazyPlugin } from './plugins/lazyPlugin';

const app = createApp(App);
app.use(lazyPlugin); // 安装插件
app.mount('#app');

使用方式

vue 复制代码
<template>
  <img v-img-lazy="item.imgUrl" alt="商品图片" class="lazy-img">
</template>

三、Axios二次封装:前端请求层工程化

Axios封装核心是「统一配置、身份认证、全局错误处理」,适配Vue3+Pinia+Element Plus技术栈:

javascript 复制代码
// src/utils/http.js
import axios from 'axios';
import { ElMessage } from 'element-plus';
import 'element-plus/es/components/message/style/css';
import { useUserStore } from '@/stores/userStore';
import router from '@/router';

// 1. 创建独立Axios实例(隔离全局配置)
const httpInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL, // 环境变量区分环境
  timeout: 30000
});

// 2. 请求拦截器:自动携带Token
httpInstance.interceptors.request.use(
  config => {
    const userStore = useUserStore();
    if (userStore.userInfo.token) {
      // Bearer Token格式(OAuth2.0标准)
      config.headers.Authorization = `Bearer ${userStore.userInfo.token}`;
    }
    return config;
  },
  e => Promise.reject(e)
);

// 3. 响应拦截器:统一处理结果+错误
httpInstance.interceptors.response.use(
  res => res.data, // 简化返回数据(只给业务层data)
  e => {
    // 区分网络错误和服务器响应错误
    const msg = e.response ? e.response.data.message : '网络异常,请检查网络';
    ElMessage.warning(msg);

    // 401未授权:Token失效/未登录
    if (e.response?.status === 401) {
      const userStore = useUserStore();
      userStore.clearUserInfo(); // 清空用户状态
      router.push('/login'); // 跳登录页
    }

    return Promise.reject(e); // 保留错误链,供业务层catch
  }
);

export default httpInstance;

核心优势

  • 实例隔离:避免污染全局Axios配置,支持多域名请求;
  • 认证自动化:登录后自动携带Token,401自动清状态跳登录;
  • 错误全局化:统一错误提示,减少业务层重复代码;
  • 环境适配:通过环境变量区分开发/测试/生产环境的baseURL。

四、HTTP状态码300/400/500:核心含义与业务处理

HTTP状态码按首位数字分类,核心区别是「责任方不同」,结合Axios封装讲解业务处理逻辑:

4.1 3xx系列:重定向(资源位置变更)

状态码 含义 常见场景 处理逻辑
301 永久重定向 域名更换、接口路径永久迁移 Axios自动跟随,无需手动处理
302 临时重定向 登录成功跳首页、临时维护跳转 自动跟随,特殊场景可拦截处理
304 资源未修改(缓存命中) 静态资源(图片/CSS/JS)缓存 Axios自动复用缓存,减少请求

4.2 4xx系列:客户端错误(请求有问题)

状态码 含义 常见场景 处理逻辑
400 请求语法/参数错误 必传参数缺失、JSON格式错误 全局提示后端错误信息
401 未授权(Token无效) Token过期、未登录访问权限接口 清用户状态+跳登录页
403 禁止访问(权限不足) 普通用户访问管理员接口 提示"无权限操作"
404 资源/接口不存在 接口路径写错、资源ID无效 提示"接口不存在"
408 请求超时 网络卡顿、请求体过大 提示"请求超时,请重试"

4.3 5xx系列:服务端错误(服务器异常)

状态码 含义 常见场景 处理逻辑
500 服务器内部错误 后端代码Bug、数据库连接失败 提示"服务器错误,请稍后重试"
502 网关错误 Nginx配置错误、后端服务宕机 提示"网关错误,联系管理员"
503 服务不可用 服务器过载、维护中 提示"服务维护中,请稍后重试"
504 网关超时 后端接口响应过慢、数据库超时 提示"网关超时,请重试"

核心原则

  • 4xx:客户端问题(改请求参数/登录/权限);
  • 5xx:服务端问题(客户端只能提示重试,后端排查日志);
  • 3xx:自动处理为主,特殊场景手动拦截。

五、数组扁平化去重排序:从API到手动实现

5.1 简洁实现(ES6+ API)

利用flat(Infinity)+Set+sort实现,适合现代环境:

javascript 复制代码
function flattenAndSort(arr) {
  // 1. 深度扁平化(Infinity表示无限层级)
  const flattened = arr.flat(Infinity); 
  // 2. 去重(Set不允许重复基础类型)
  const unique = [...new Set(flattened)]; 
  // 3. 数字升序排序(避免默认字符串排序)
  return unique.sort((a, b) => a - b); 
}

// 示例:输入[3, [2, [1, 4], 2], [5, 3]] → 输出[1,2,3,4,5]

5.2 手动实现(兼容低版本环境)

手动实现深度扁平化(递归+reduce),替代flat(Infinity)

javascript 复制代码
// 手动深度扁平化
function flatDeep(arr) {
  return arr.reduce((acc, val) => {
    // 递归处理子数组,非数组直接拼接
    return Array.isArray(val) ? acc.concat(flatDeep(val)) : acc.concat(val);
  }, []);
}

// 去重(兼容无Set环境)
function uniqueArr(arr) {
  return arr.filter((item, index) => arr.indexOf(item) === index);
}

// 完整函数
function flattenAndSortOpt(arr) {
  const flattened = flatDeep(arr);
  const unique = uniqueArr(flattened);
  return unique.sort((a, b) => a - b);
}

5.3 易错点说明

  • flat(Infinity)兼容性:IE不支持,需手动实现;
  • Set去重限制:仅支持基础类型(引用类型去重无效);
  • sort()默认坑:默认按字符串Unicode排序,数字排序必须传比较函数(a,b)=>a-b

六、Vue3父子组件通信:核心规则与实践

Vue3父子组件通信遵循「单向数据流」,核心方式是「Props下传,事件上抛」,附完整示例:

6.1 子组件(Child.vue):接收Props+触发事件

vue 复制代码
<template>
  <div class="child">
    <p>父组件传的商品名:{{ productName }}</p>
    <p>当前数量:{{ count }}</p>
    <button @click="handleAdd">数量+1</button>
    <!-- 插槽:父组件传内容 -->
    <slot></slot>
  </div>
</template>

<script setup lang="ts">
// 1. 定义Props(类型校验+默认值)
const props = defineProps({
  productName: { type: String, required: true },
  initCount: { type: Number, default: 0 }
});

// 2. 子组件内部状态
const count = ref(props.initCount);

// 3. 声明自定义事件
const emit = defineEmits(['count-change']);

// 4. 触发事件:给父组件传值
const handleAdd = () => {
  count.value++;
  emit('count-change', count.value); // 事件名+参数
};
</script>

6.2 父组件(Parent.vue):传Props+监听事件

vue 复制代码
<template>
  <div class="parent">
    <p>子组件传回的数量:{{ totalCount }}</p>
    <!-- 使用子组件:传Props+监听事件 -->
    <Child 
      :product-name="productName" 
      :init-count="0"
      @count-change="handleCountChange"
    >
      <!-- 插槽内容 -->
      <p>父组件传给子组件的内容</p>
    </Child>
  </div>
</template>

<script setup lang="ts">
import Child from '@/components/Child.vue';
import { ref } from 'vue';

const productName = ref('Vue实战教程');
const totalCount = ref(0);

// 处理子组件事件
const handleCountChange = (newCount: number) => {
  totalCount.value = newCount;
};
</script>

6.3 核心规则与扩展

  • 单向数据流 :子组件不能直接修改Props,需通过emit通知父组件修改;
  • 命名规范 :Props/事件名用camelCase定义,模板中用kebab-case(如product-name);
  • 多层通信 :爷孙组件用provide/inject,避免Props透传;
  • 访问子组件 :用ref获取子组件实例(慎用,破坏单向数据流)。

七、面试技巧:高频问题回答思路

  1. 手写类问题(Promise.all/扁平化)
    • 先讲核心原理→写代码→分析易错点→优化方案;
  2. 工程化问题(Axios/懒加载插件)
    • 讲封装思路→核心功能(拦截器/指令)→业务处理→优化点;
  3. Vue组件通信
    • 总述核心方式(Props+emit)→代码示例→单向数据流→扩展场景(插槽/provide);
  4. 状态码问题
    • 分类总述(3xx/4xx/5xx责任方)→结合业务处理→工程化优化。

八、总结

本文覆盖的6个知识点是前端「基础+工程化」的核心,也是面试高频考点:

  • 基础能力:Promise.all手写、数组扁平化(考验逻辑思维);
  • 性能优化:懒加载(原生+Vue3,提升页面加载速度);
  • 工程化:Axios封装、Vue组件通信(企业级项目必备);
  • 问题排查:HTTP状态码(前后端联调核心)。

建议结合代码实操,重点掌握「原理+易错点+优化方案」,既能应对面试,也能提升实际开发效率。


参考资料

  1. Vue官方文档:https://vuejs.org/
  2. Axios官方文档:https://axios-http.com/
  3. VueUse文档:https://vueuse.org/
  4. MDN HTTP状态码:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
相关推荐
freexyn1 小时前
Matlab自学笔记六十九:多项式求值、求根、积分和求导
开发语言·笔记·matlab
郝学胜-神的一滴1 小时前
Linux中的alarm函数详解:定时器信号处理指南
linux·服务器·开发语言·c++·程序人生
Q_Q5110082851 小时前
python+django/flask+vue基于web的产品管理系统
前端·spring boot·python·django·flask·node.js
iナナ1 小时前
Java自定义协议的发布订阅式消息队列(一)
java·开发语言·spring·消息队列·生成消费者模型
chilavert3181 小时前
技术演进中的开发沉思-229 Ajax:Firefox 与 Firebug
javascript·okhttp
方也_arkling1 小时前
【JS】日期对象及时间戳的使用(制作距离指定日期的倒计时)
开发语言·javascript·ecmascript
Zfox_1 小时前
【Go】反射
开发语言·后端·golang
xinxinhenmeihao1 小时前
手机socks5代理如何使用?电脑怎么配置http代理?
网络协议·http·智能手机
无奈何杨1 小时前
业务如何对接风控决策,实时/异步,结果同步
前端·后端