Skip to content

JavaScript 核心概念

导航目录

静态作用域和动态作用域

JavaScript 采用词法作用域(静态作用域),函数作用域在定义时就确定。而动态作用域中,函数作用域在调用时才确定。

对比说明

特性词法作用域(静态)动态作用域
作用域确定时机函数定义时函数调用时
JavaScript 支持✓ 支持✗ 不支持
典型语言JavaScript、PythonBash、Emacs Lisp

执行上下文与变量对象

当执行可执行代码时,会创建对应的执行上下文,包含三个核心属性:

  1. 变量对象(VO)
  2. 作用域链
  3. this

函数上下文

函数上下文中使用活动对象(AO) 表示变量对象。AO 在进入函数上下文时创建,通过arguments属性初始化。

AO 变化过程

  1. 进入执行上下文:添加形参、函数声明、变量声明
  2. 代码执行阶段:修改变量值

示例代码

js
function foo(a) {
  var b = 2;
  function c() {}
  var d = function() {};
  b = 3;
}
foo(1);

// 进入执行上下文时AO:
AO = {
  arguments: {0: 1, length: 1},
  a: 1,
  b: undefined,
  c: reference to function c(){},
  d: undefined
}

// 代码执行后AO:
AO = {
  arguments: {0: 1, length: 1},
  a: 1,
  b: 3,
  c: reference to function c(){},
  d: reference to FunctionExpression "d"
}

作用域链

查找变量时从当前上下文的变量对象开始,沿父级执行上下文逐级查找。作用域链是由多个执行上下文的变量对象构成的链表。

示例代码

js
function foo() {
  function bar() {
    // bar作用域链:[bar.AO, foo.AO, global.VO]
  }
}

词法作用域和原型链

词法作用域

词法作用域:解决的是 "变量从哪里来?" 的问题,即变量的查找规则。

JavaScript 引擎通过作用域链来查找变量。作用域链是由当前执行上下文和所有外层执行上下文的变量对象组成。查找变量时,会从当前作用域开始,逐级向上(向外)查找,直到全局作用域。如果找不到,就会抛出 ReferenceError

原型链

原型链:解决的是 "属性或方法从哪里来?" 的问题,即对象属性的查找规则。

原型链是 JavaScript 实现继承的主要机制。每个对象都有一个内部属性 [[Prototype]](可通过 __proto__Object.getPrototypeOf() 访问),它指向另一个对象(它的原型)。当试图访问一个对象的属性时,如果该对象自身没有这个属性,就会在其原型对象上查找,依此类推,直到找到属性或到达链的末端(null)。

对比说明

特性词法作用域原型链
查找目标变量对象属性/方法
查找方向从内向外从对象到原型
查找失败抛出 ReferenceError返回 undefined
实现机制作用域链原型链

函数参数传递

ECMAScript 中所有函数参数都是按值传递

传递方式

  • 基本类型:传递值的副本
  • 引用类型:传递引用的副本(共享传递)

示例代码

js
// 基本类型传递
var num = 1;
function changeNum(n) {
  n = 2; // 修改副本,不影响原值
}
changeNum(num);
console.log(num); // 1

// 引用类型传递
var obj = { value: 1 };
function foo(o) {
  o = 2; // 修改引用副本,不影响原对象
}
foo(obj);
console.log(obj.value); // 1(原对象未改变)

// 引用类型修改属性
var obj2 = { value: 1 };
function modifyObj(o) {
  o.value = 2; // 通过引用修改对象属性
}
modifyObj(obj2);
console.log(obj2.value); // 2(原对象属性被修改)

手写核心方法

实现 call

js
Function.prototype.call2 = function (context, ...args) {
  // 处理 null/undefined 的情况
  if (context == null) {
    context = globalThis;
  } else {
    // 处理基本类型,需要转换为对象
    context = Object(context);
  }

  // 使用 Symbol 避免属性名冲突
  const fn = Symbol("fn");
  context[fn] = this;

  const result = context[fn](...args);

  delete context[fn];
  return result;
};

实现 bind

js
Function.prototype.myBind = function (context, ...bindArgs) {
  const self = this;

  // 定义绑定函数
  function boundFn(...callArgs) {
    // 判断是否通过 new 调用
    const isNewCall = this instanceof boundFn;

    // new 调用时 this 指向新创建的对象,否则指向绑定的 context
    const finalContext = isNewCall ? this : context;

    // 合并绑定的参数和调用时的参数
    const args = [...bindArgs, ...callArgs];

    return self.apply(finalContext, args);
  }

  // 保持原型链,使绑定函数可以 instanceof 原函数
  if (this.prototype) {
    boundFn.prototype = Object.create(this.prototype);
  }

  return boundFn;
};

实现 new

js
function myNew(Fn, ...args) {
  // 1. 创建新对象,原型指向 Fn.prototype
  const obj = Object.create(Fn.prototype);

  // 2. 执行构造函数,绑定 this
  const result = Fn.apply(obj, args);

  // 3. 返回:如果构造函数返回对象则返回该对象,否则返回新创建的对象
  return result !== null &&
    (typeof result === "object" || typeof result === "function")
    ? result
    : obj;
}

AJAX vs Fetch

特性AJAX (XMLHttpRequest)Fetch
设计回调函数Promise
API 设计集中式模块化(Request/Response/Headers)
数据流不支持分块读取支持 Stream 分块读取
CORS需手动处理默认不发送 cookies
错误处理需检查 status需检查 response.ok
浏览器支持所有浏览器现代浏览器(需 polyfill)

示例代码

js
// AJAX 示例
const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/data");
xhr.onload = function () {
  if (xhr.status === 200) {
    console.log(xhr.responseText);
  }
};
xhr.send();

// Fetch 示例
fetch("/api/data")
  .then((response) => {
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    return response.json();
  })
  .then((data) => console.log(data))
  .catch((error) => console.error("Error:", error));

let 底层实现

let 的底层实现涉及词法环境和作用域链的管理:

  1. 编译阶段:扫描函数体,为 let 变量生成初始词法环境
  2. 执行上下文:进入块级作用域创建新词法环境
  3. 绑定变量值:运行时在词法环境中搜索变量
  4. 块级作用域:创建子遮蔽环境实现作用域隔离

示例代码

js
// let 的暂时性死区示例
{
  // TDZ 开始
  console.log(x); // ReferenceError: Cannot access 'x' before initialization

  let x = 10;
  // TDZ 结束

  console.log(x); // 10
}

// 块级作用域示例
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

垃圾回收机制

JavaScript 使用自动垃圾回收机制来管理内存,主要有以下几种算法:

引用计数

  • 优点:立即回收垃圾
  • 缺点:循环引用问题,计数器占用空间

标记清除

  1. 标记阶段:从根对象开始,标记所有可达对象
  2. 清除阶段:清除未被标记的对象
  • 优点:实现简单,能处理循环引用
  • 缺点:内存碎片

分代回收

  • 新生代:新创建对象,回收频率高
  • 老生代:长期存活对象,回收频率低

示例代码

js
// 循环引用问题(引用计数无法处理)
function createCycle() {
  const objA = {};
  const objB = {};
  objA.ref = objB;
  objB.ref = objA;

  return { objA, objB };
}

// 标记清除可以处理循环引用
const cycle = createCycle();
cycle = null; // 两个对象都会被回收

函数式编程核心

函数式编程是一种编程范式,强调使用纯函数、不可变性和函数组合。

1. 纯函数

纯函数:相同输入 => 相同输出,无副作用

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

2. 不可变性

不可变性:数据创建后不可修改

js
const newArr = [...arr, newItem];
const newObj = { ...oldObj, newProp: value };

3. 高阶函数

高阶函数:函数作为参数或返回值

js
function multiplier(factor) {
  return (num) => num * factor;
}

const double = multiplier(2);
console.log(double(5)); // 10

4. 函数组合

函数组合:将多个函数组合成一个函数

js
const compose =
  (...fns) =>
  (x) =>
    fns.reduceRight((acc, fn) => fn(acc), x);

const process = compose(
  (x) => x * 2,
  (x) => x + 1,
  (x) => x - 3
);

console.log(process(5)); // (5 - 3) * 2 + 1 = 5

发布订阅 vs 观察者模式

特性发布订阅观察者
耦合度完全解耦相互耦合
通信方式消息通道直接调用
扩展性高(多发布/订阅者)
中间件需要事件中心不需要

示例代码

js
// 发布订阅模式
class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach((callback) => callback(data));
    }
  }
}

// 观察者模式
class Subject {
  constructor() {
    this.observers = [];
  }

  attach(observer) {
    this.observers.push(observer);
  }

  notify(data) {
    this.observers.forEach((observer) => observer.update(data));
  }
}

class Observer {
  update(data) {
    console.log("Received:", data);
  }
}

精度丢失问题

JavaScript 使用 IEEE 754 双精度浮点数表示数值,这会导致一些精度问题。

问题示例

js
0.1 + 0.2 !== 0.3; // true
0.1 + 0.2; // 0.30000000000000004
9007199254740991 + 1; // 精度丢失

原因分析

浮点数二进制表示存在近似值,某些十进制小数无法精确表示为二进制浮点数。

解决方案

js
// 方法1:使用 toFixed
function add(a, b) {
  return parseFloat((a + b).toFixed(10));
}

// 方法2:转换为整数计算
function add2(a, b) {
  const multiplier = Math.pow(10, 10);
  return (a * multiplier + b * multiplier) / multiplier;
}

// 方法3:使用第三方库(如 decimal.js)

WeakMap vs Map

特性WeakMapMap
Key 类型仅对象任意值
引用类型弱引用强引用
垃圾回收自动回收不回收
可枚举
size 属性
遍历方法forEach、keys、values、entries

示例代码

js
// Map 示例
const map = new Map();
const key1 = { name: "key1" };
const key2 = "key2";

map.set(key1, "value1");
map.set(key2, "value2");

console.log(map.size); // 2
console.log(map.get(key1)); // 'value1'

// WeakMap 示例
const weakMap = new WeakMap();
const obj = { id: 1 };

weakMap.set(obj, "metadata");
console.log(weakMap.get(obj)); // 'metadata'

// 当 obj 被垃圾回收时,WeakMap 中的条目也会被自动移除
obj = null;

小程序双线程架构

小程序采用双线程架构来提高性能和用户体验。

架构说明

  • 渲染层:WebView 渲染界面
  • 逻辑层:JsCore 线程运行 JS 脚本

优势

避免 JS 执行阻塞渲染,提升用户体验。

通信机制

两个线程之间通过消息队列进行通信,数据传输需要序列化。

示例代码

js
// 逻辑层
Page({
  data: {
    message: "Hello",
  },

  onLoad() {
    // 修改数据,触发渲染层更新
    this.setData({
      message: "Hello World",
    });
  },
});

// 渲染层
<view>{{ message }}</view>;

重绘 vs 回流

特性重绘回流
触发条件外观变化布局变化
性能影响较小较大
优化建议避免频繁样式修改批量 DOM 操作

示例代码

js
// 触发重绘
element.style.color = "red";
element.style.backgroundColor = "blue";

// 触发回流
element.style.width = "100px";
element.style.height = "100px";

// 优化:批量操作,减少回流
const element = document.getElementById("my-element");
element.style.cssText = "width: 100px; height: 100px; color: red;";

// 优化:使用文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
  const div = document.createElement("div");
  fragment.appendChild(div);
}
document.body.appendChild(fragment);

setTimeout vs requestAnimationFrame

特性setTimeoutrequestAnimationFrame
执行时机指定时间后下一帧开始前
主线程阻塞不阻塞
后台运行继续执行暂停
适用场景通用定时动画优化

示例代码

js
// setTimeout 示例
setTimeout(() => {
  console.log("Delayed execution");
}, 1000);

// requestAnimationFrame 示例
function animate() {
  // 更新动画状态
  updateAnimation();

  // 请求下一帧
  requestAnimationFrame(animate);
}

// 开始动画
requestAnimationFrame(animate);

// 动画优化示例
let animationId;
function startAnimation() {
  function loop() {
    update();
    draw();
    animationId = requestAnimationFrame(loop);
  }
  animationId = requestAnimationFrame(loop);
}

function stopAnimation() {
  cancelAnimationFrame(animationId);
}

script 加载策略

特性正常加载asyncdefer
HTML 解析暂停并行并行
执行顺序顺序执行下载完立即执行HTML 解析完后顺序执行
使用建议-独立脚本依赖 DOM 的脚本

示例代码

html
<!-- 正常加载:阻塞 HTML 解析 -->
<script src="script1.js"></script>

<!-- async:并行下载,下载完立即执行 -->
<script async src="script2.js"></script>

<!-- defer:并行下载,HTML 解析完后按顺序执行 -->
<script defer src="script3.js"></script>

<!-- 最佳实践:关键脚本使用 defer,非关键脚本使用 async -->
<script defer src="main.js"></script>
<script async src="analytics.js"></script>

加载策略选择

  • 正常加载:脚本执行顺序很重要,且脚本依赖 DOM
  • async:独立脚本,不依赖其他脚本和 DOM
  • defer:脚本执行顺序重要,但可以并行下载

内存泄漏场景

1. 意意全局变量

js
function leak() {
  temp = "leak"; // 意外创建全局变量
}

// 解决方案
function noLeak() {
  let temp = "no leak"; // 使用 let/const
}

2. 未清除定时器

js
// 问题代码
setInterval(() => {
  console.log("tick");
}, 1000);

// 解决方案
const timer = setInterval(() => {
  console.log("tick");
}, 1000);

// 清除定时器
clearInterval(timer);

3. DOM 引用未释放

js
// 问题代码
const element = document.getElementById("my-element");
// 未在不需要时解除引用

// 解决方案
function cleanup() {
  const element = document.getElementById("my-element");
  // 使用元素
  element.innerHTML = "content";

  // 清除引用
  element = null;
}

4. 闭包滥用

js
// 问题代码
function outer() {
  const bigData = new Array(1000000);
  return function inner() {
    console.log(bigData.length);
  };
}

// 解决方案
function outer() {
  const bigData = new Array(1000000);
  const result = bigData.length;
  return function inner() {
    console.log(result); // 只保留需要的数据
  };
}

Vite 为何启动快

Vite 的快速启动主要得益于以下几个设计:

核心优势

  • 开发环境:直接使用 ES6 Module,无需打包
  • 按需编译:仅编译当前请求的模块
  • Esbuild 预构建:极速的依赖预构建

详细说明

  1. ES6 Module 原生支持

    • 开发环境直接使用浏览器的 ES Module 支持
    • 避免了传统打包工具的完整打包过程
  2. 按需编译

    • 只编译浏览器请求的文件
    • 大大减少了启动时的编译时间
  3. Esbuild 预构建

    • 使用 Go 语言编写的 Esbuild 进行依赖预构建
    • 比 Webpack 快 10-100 倍

示例对比

js
// 传统打包工具(如 Webpack)
// 需要打包整个项目,启动慢
npm run dev // 可能需要 10-30 秒

// Vite
// 直接使用 ES Module,启动快
npm run dev // 通常 1-3 秒

Vue vs React 对比

特性VueReact
模板系统基于 HTML 的模板JSX
更新粒度组件级从根节点调度
响应式Proxy 拦截setState 触发
渲染方式递归可中断循环
学习曲线较平缓较陡峭
生态系统官方主导社区主导

示例代码

vue
<!-- Vue 示例 -->
<template>
  <div>
    <h1>{{ message }}</h1>
    <button @click="updateMessage">Update</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "Hello Vue",
    };
  },
  methods: {
    updateMessage() {
      this.message = "Updated!";
    },
  },
};
</script>
jsx
// React 示例
import React, { useState } from 'react';

function App() {
  const [message, setMessage] = useState('Hello React');

  const updateMessage = () => {
    setMessage('Updated!');
  };

  return (
    <div>
      <h1>{message}</h1>
      <button onClick={updateMessage}>Update</button>
    </div>
  );
}
```

<a id="nav-21"></a>
## 前端打包必要性
前端打包是现代前端开发的重要环节,主要解决以下问题:

### 1. 性能优化
- **Tree-Shaking**:移除未使用的代码
- **代码压缩**:减小文件体积
- **代码合并**:减少 HTTP 请求
- **按需加载**:提升首屏加载速度

### 2. 语法转换
- **TypeScript**:转换为 JavaScript
- **ES6+**:转换为 ES5 以支持旧浏览器
- **SCSS/Less**:转换为 CSS

### 3. 工程能力
- **Lint**:代码质量检查
- **测试**:自动化测试
- **CI/CD**:持续集成和部署

### 示例代码
```js
// 源代码(ES6+)
import { add } from './utils.js';

const result = add(1, 2);
console.log(result);

// 打包后(ES5)
'use strict';

var _utils = require('./utils.js');

var result = (0, _utils.add)(1, 2);
console.log(result);
```

<a id="nav-22"></a>
## 开发环境标识
开发过程中通常会使用不同的环境标识来区分不同的部署阶段。

| 环境 | 全称 | 用途 |
| --- | --- | --- |
| **DEV** | Development | 开发 |
| **SIT** | System Integration Test | 系统整合测试 |
| **UAT** | User Acceptance Test | 用户验收测试 |
| **PROD** | Production | 生产环境 |

### 示例代码
```js
// 环境配置
const ENV = process.env.NODE_ENV || 'development';

const config = {
  development: {
    apiUrl: 'http://localhost:3000',
    debug: true
  },
  sit: {
    apiUrl: 'https://sit.example.com',
    debug: true
  },
  uat: {
    apiUrl: 'https://uat.example.com',
    debug: false
  },
  production: {
    apiUrl: 'https://api.example.com',
    debug: false
  }
};

const currentConfig = config[ENV];
console.log('API URL:', currentConfig.apiUrl);
```

<a id="nav-23"></a>
## 循环方法对比
JavaScript 中有多种循环方法,各有不同的特点和适用场景。

| 方法 | 可中断 | 遍历内容 | 适用对象 |
| --- | --- | --- | --- |
| `forEach` | ❌ | 值 | Array |
| `for...in` | ✅ | 键 | Object/Array |
| `for...of` | ✅ | 值 | Array/Map/Set |

### 示例代码
```js
// forEach - 不可中断
const arr = [1, 2, 3, 4, 5];
arr.forEach((item, index) => {
  console.log(item, index);
  // 无法中断循环
});

// for...in - 遍历键
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {
  console.log(key, obj[key]);
  if (key === 'b') break; // 可以中断
}

// for...of - 遍历值
const set = new Set([1, 2, 3]);
for (const value of set) {
  console.log(value);
  if (value === 2) break; // 可以中断
}

// 传统 for 循环
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]);
  if (arr[i] === 3) break; // 可以中断
}
```

<a id="nav-24"></a>
## 数据类型细节
JavaScript 中有两个表示"空"的值,它们有不同的含义和用途。

### undefined
`undefined`:变量声明但未初始化

```js
let a;
console.log(a); // undefined

function foo(b) {
  console.log(b); // undefined
}
foo();

const obj = {};
console.log(obj.property); // undefined
```

### null
`null`:空对象指针,表示"无值"或"空值"

```js
let a = null;
console.log(a); // null

function getNull() {
  return null;
}
console.log(getNull()); // null
```

### 区别
| 特性 | undefined | null |
| --- | --- | --- |
| **类型** | undefined | object |
| **含义** | 未定义 | 空值 |
| **转换** | NaN | 0 |
| **typeof** | "undefined" | "object" |

### 最佳实践
```js
// 检查 undefined
if (typeof variable === 'undefined') {
  // 变量未定义
}

// 检查 null
if (variable === null) {
  // 变量为 null
}

// 检查 null 或 undefined
if (variable == null) {
  // 变量为 null 或 undefined
}
```

<a id="nav-25"></a>
## 递减操作符
JavaScript 提供了前缀和后缀递减操作符,它们的执行顺序不同。

### 操作符说明
- `--a`:前缀递减,先递减后计算
- `a--`:后缀递减,先计算后递减

### 示例代码
```js
let a = 5;
let b = a-- + 2; // b=7, a=4(先计算后递减)
console.log(a, b); // 4, 7

let c = 5;
let d = --c + 2; // c=4, d=6(先递减后计算)
console.log(c, d); // 4, 6

// 更复杂的示例
let x = 10;
let y = x-- + --x;
console.log(x, y); // 8, 18

// 解释:
// x-- 使用当前值 10,然后 x 变为 9
// --x 先递减 x 从 9 到 8,然后使用 8
// y = 10 + 8 = 18
// 最终 x = 8
```

### 注意事项
- 递减操作符会修改原变量的值
 - 在复杂表达式中使用时要注意执行顺序
 - 建议在简单场景中使用,避免在复杂表达式中使用
```

## 检查对象属性是否存在的 4 种方式
### 1. in 运算符:`key in obj`
**特点**:检测自身+原型链上的key,存在返回true(含继承属性)。

```js
const obj = { a: 1 };
console.log('a' in obj); // true
console.log('toString' in obj); // true(继承自原型)
```

### 2. `obj.hasOwnProperty(key)`
**特点**:仅检测对象自身的key(排除原型链),最常用且精准。

```js
const obj = { a: 1 };
console.log(obj.hasOwnProperty('a')); // true
console.log(obj.hasOwnProperty('toString')); // false(原型链上的属性)
```

### 3. `obj[key] !== undefined`
**特点**:简单直接,但无法区分key存在但值为undefined的情况。

```js
const obj = { a: undefined };
console.log(obj['a'] !== undefined); // false(虽然属性存在,但值为undefined)
console.log(obj['b'] !== undefined); // false(属性不存在)
```

### 4. `Reflect.has(obj, key)`
**特点**:ES6新增,行为与in一致(检测自身+原型链),更符合函数式编程风格,可安全处理特殊对象(如冻结对象)。

```js
const obj = { a: 1 };
console.log(Reflect.has(obj, 'a')); // true
console.log(Reflect.has(obj, 'toString')); // true

const frozen = Object.freeze({ a: 1 });
console.log(Reflect.has(frozen, 'a')); // true(可以安全处理冻结对象)
```

### 对比总结
| 方法 | 检测范围 | 能否区分undefined | 函数式 | 推荐度 |
| --- | --- | --- | --- | --- |
| `in` | 自身+原型链 ||| ⭐⭐⭐ |
| `hasOwnProperty` | 仅自身 ||| ⭐⭐⭐⭐⭐ |
| `obj[key] !== undefined` | 自身 ||| ⭐⭐ |
| `Reflect.has` | 自身+原型链 ||| ⭐⭐⭐⭐ |

## 什么是模块化?
模块化是一种**将复杂系统拆分为独立、可复用的小单元(模块)** 的开发范式。每个模块聚焦单一功能,通过清晰定义的接口与其他模块通信,内部实现细节则被封装隐藏。

### 核心价值
1. **解耦性**:将代码按功能拆分(如请求库、工具函数、UI组件),避免牵一发而动全身
2. **复用性**:比如封装的防抖函数、表单组件,可在多个页面甚至多个项目中复用,减少重复开发
3. **可维护性**:模块职责单一,问题定位更精准(如网络错误只需检查API模块)

### 示例代码
```js
// math.js - 数学工具模块
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;

// utils.js - 工具函数模块
export const debounce = (fn, delay) => {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
};

// main.js - 主模块
import { add, multiply } from './math.js';
import { debounce } from './utils.js';

console.log(add(1, 2)); // 3
console.log(multiply(3, 4)); // 12

const debouncedFn = debounce(() => {
  console.log('Debounced!');
}, 300);
```

### 模块化规范
- **CommonJS**:Node.js 使用,`require`/`module.exports`
- **ES Modules**:现代标准,`import`/`export`
- **AMD**:异步模块定义,RequireJS 使用

## 什么是工程化?
"工程化是**用系统化、规范化、工具化的方式管理前端全生命周期**,核心是解决**多人协作、大型项目、高效交付**的问题,把零散的开发流程变成可落地的标准流程

### 前端工程化的核心落地环节
1. **开发规范**:ESLint/Prettier保障代码风格统一,Git提交规范记录变更语义
2. **构建优化**:利用Webpack/Vite实现代码压缩、Tree-Shaking、按需加载
3. **自动化流程**CI/CD流水线自动运行测试、部署发布,减少人工干预
4. **架构设计**:建设私有组件库、搭建BFF层、落地微前端方案等

### 模块化 vs 工程化
- **模块化**:解决的是「**代码层面的混乱**」,如代码组织、依赖管理、复用性
- **工程化**:解决的是「**项目层面的混乱**」,如开发、构建、部署全流程的不规范

### 示例代码
```js
// .eslintrc.js - ESLint 配置
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:prettier/recommended'
  ],
  rules: {
    'no-console': 'warn',
    'no-unused-vars': 'error',
    'prefer-const': 'error'
  }
};

// .prettierrc - Prettier 配置
module.exports = {
  semi: true,
  singleQuote: true,
  tabWidth: 2,
  trailingComma: 'es5',
  printWidth: 80
};

// package.json - 脚本配置
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "test": "jest",
    "lint": "eslint src --ext .js,.jsx,.ts,.tsx",
    "format": "prettier --write src/**/*.{js,jsx,ts,tsx,css,md}",
    "type-check": "tsc --noEmit"
  }
}
```

### 工程化工具链
- **代码规范**:ESLint、Prettier、Stylelint
- **构建工具**:Webpack、Vite、Rollup、Parcel
- **测试框架**:Jest、Mocha、Cypress、Playwright
- **CI/CD**:GitHub Actions、GitLab CI、Jenkins
- **包管理**:npm、yarn、pnpm