一、工作中解决的困难问题 / 项目亮点

核心思路:采用「问题场景→分析过程→解决方案→优化迭代→价值体现」的结构化叙述,结合具体案例说明。

 

示例:
在某电商项目中,发现首屏加载时间长达 8s(通过 Lighthouse 检测),用户流失率超 30%。

 

  1. 问题定位:
    • 排查发现:第三方 SDK(如统计、客服)阻塞渲染,首屏包含 12 张未优化的大图,JS 打包体积达 2.3MB;
    • 工具验证:通过 Chrome Performance 面板确认第三方脚本为长任务,Network 面板显示图片未启用压缩和懒加载。
  2. 解决方案:
    • 第三方 SDK 异步加载:将非核心 SDK(如客服)改为dynamic import,延迟到首屏渲染后加载;
    • 图片优化:采用 webp 格式 +srcset响应式加载,首屏仅加载可视区图片(懒加载);
    • 代码分割:路由懒加载(() => import('./Page.vue')),将主包体积降至 800KB。
  3. 结果:首屏加载时间降至 2.5s,用户流失率下降至 12%,核心转化指标提升 18%。

 

总结:解决问题的关键是「数据驱动定位瓶颈」+「分层优化(资源加载 / 代码体积 / 执行效率)」,过程需记录每步尝试与结果(如笔记记录各优化措施的具体收益),便于复盘和复用。

二、浏览器事件循环(Event Loop)

1. 为什么需要事件循环?

 

 

2. 宏任务(Macro Task)与微任务(Micro Task)

 

 

为什么需要微任务?
单一任务队列无法区分优先级,微任务用于处理「高优先级异步操作」(如 Promise 回调需在 UI 渲染前执行),避免被低优先级宏任务(如定时器)阻塞。

 

3. 浏览器事件循环流程

 

  1. 执行当前宏任务(如script整体代码);
  2. 执行所有微任务队列中的任务(按顺序清空);
  3. 执行 UI 渲染(若有);
  4. 取下一个宏任务,重复 1-3。

 

4. Node 与浏览器事件循环的差异

 

 

5. 代码执行顺序示例

 

javascript
 
运行
 
 
 
 
console.log('1');
setTimeout(() => console.log('2'), 0);
new Promise(resolve => {
  console.log('3');
  resolve();
}).then(() => console.log('4'));
console.log('5');

// 输出:1 → 3 → 5 → 4 → 2
// 解析:
// 1. 执行宏任务(script):打印1→3→5;
// 2. 清空微任务(Promise.then):打印4;
// 3. 执行下一个宏任务(setTimeout):打印2。
 

三、事件冒泡与捕获机制

1. 基本概念

 

 

2. window.addEventListener 的默认阶段
默认监听冒泡阶段(useCapture: false),若需监听捕获阶段,需设置useCapture: true:

 

javascript
 
运行
 
 
 
 
// 监听捕获阶段
window.addEventListener('click', () => console.log('捕获'), true);
// 监听冒泡阶段
window.addEventListener('click', () => console.log('冒泡'), false);
 

 

3. 应用场景与事件委托

 

 

4. 封禁用户点击拦截方案
利用事件捕获在最顶层拦截所有点击(比冒泡更早触发):

 

javascript
 
运行
 
 
 
 
// 若用户被封禁,拦截所有点击
if (user.banned) {
  window.addEventListener('click', (e) => {
    e.stopPropagation(); // 阻止事件传播
    e.preventDefault(); // 阻止默认行为
    alert('您已被封禁,无法操作');
  }, true); // 捕获阶段拦截(比任何子元素的监听更早)
}
 

四、防抖与节流

1. 基本概念

 

 

2. 适用场景

 

 

3. 节流实现(时间戳 + 定时器版)

 

javascript
 
运行
 
 
 
 
// 节流函数:保证每隔wait时间最多执行一次func
const throttle = (func, wait) => {
  let lastTime = 0; // 上次执行时间
  let timer = null;

  return function (...args) {
    const now = Date.now();
    const remaining = wait - (now - lastTime); // 剩余时间

    // 若已过等待时间,直接执行
    if (remaining <= 0) {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
      lastTime = now;
      func.apply(this, args);
    } 
    // 否则设置定时器,确保最后一次触发能执行
    else if (!timer) {
      timer = setTimeout(() => {
        timer = null;
        lastTime = Date.now();
        func.apply(this, args);
      }, remaining);
    }
  };
};

// 说明:此实现为「首节流+尾节流」(首次触发立即执行,最后一次触发延迟执行)
 

五、Promise 深入

1. 缓存 Promise 结果(装饰器写法)

 

javascript
 
运行
 
 
 
 
// 装饰器:缓存Promise的成功结果(key为参数的JSON字符串)
const cachePromise = (fn) => {
  const cache = new Map();
  return async (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key); // 返回缓存结果
    }
    const result = await fn(...args);
    cache.set(key, result); // 缓存结果
    return result;
  };
};

// 使用:缓存接口请求结果
const fetchData = cachePromise(async (id) => {
  const res = await fetch(`/api/data?id=${id}`);
  return res.json();
});
 

 

2. 实现 Promise.all

 

javascript
 
运行
 
 
 
 
Promise.myAll = (iterable) => {
  return new Promise((resolve, reject) => {
    const results = [];
    let completed = 0;
    const len = iterable.length;

    if (len === 0) return resolve(results); // 空数组直接resolve

    iterable.forEach((item, index) => {
      // 处理非Promise值(用Promise.resolve包装)
      Promise.resolve(item)
        .then((res) => {
          results[index] = res; // 保持顺序
          completed++;
          if (completed === len) {
            resolve(results); // 所有完成,返回结果
          }
        })
        .catch(reject); // 任一失败,立即reject
    });
  });
};
 

 

3. Promise 并发控制

 

javascript
 
运行
 
 
 
 
// 限制同时执行的Promise数量
const promiseLimit = (tasks, limit) => {
  return new Promise((resolve) => {
    const results = [];
    let index = 0;
    let running = 0;

    const run = () => {
      if (index >= tasks.length && running === 0) {
        return resolve(results); // 所有任务完成
      }
      while (index < tasks.length && running < limit) {
        const task = tasks[index];
        const i = index; // 保存当前索引
        running++;
        index++;
        Promise.resolve(task())
          .then((res) => {
            results[i] = res;
          })
          .catch((err) => {
            results[i] = err;
          })
          .finally(() => {
            running--;
            run(); // 继续执行下一个任务
          });
      }
    };

    run();
  });
};
 

六、算法:接雨水

问题:计算柱状图能接住的雨水总量(每个柱子宽度为 1)。

 

解法:双指针法(时间 O (n),空间 O (1))

 

 

javascript
 
运行
 
 
 
 
const trap = (height) => {
  let left = 0, right = height.length - 1;
  let leftMax = 0, rightMax = 0;
  let res = 0;

  while (left < right) {
    if (height[left] < height[right]) {
      // 左侧柱子更矮,雨水高度由左侧最高柱决定
      if (height[left] >= leftMax) {
        leftMax = height[left]; // 更新左侧最高
      } else {
        res += leftMax - height[left]; // 累加雨水
      }
      left++;
    } else {
      // 右侧柱子更矮,雨水高度由右侧最高柱决定
      if (height[right] >= rightMax) {
        rightMax = height[right]; // 更新右侧最高
      } else {
        res += rightMax - height[right]; // 累加雨水
      }
      right--;
    }
  }

  return res;
};

// 示例:
console.log(trap([0,1,0,2,1,0,1,3,2,1,2,1])); // 6
console.log(trap([4,2,0,3,2,5])); // 9
 

七、软实力提升建议

  1. 输入输出结合:学习后通过博客、笔记或讲解给他人检验理解(如用费曼学习法);
  2. 问题驱动学习:遇到问题先独立排查(如利用调试工具),再查资料,最后总结解决方案;
  3. 工程化思维:不仅关注功能实现,还需考虑性能、可维护性、兼容性(如写组件时设计合理的 API);
  4. 沟通协作:与产品、后端明确需求边界,用技术方案对齐预期(如提前评估实现成本和风险)。

 

前端开发的核心竞争力不仅是技术深度,更是「用技术解决实际问题」的能力。