docs(frontend-engineering): enhance build pipeline demo and documentation
This commit is contained in:
+188
-14
@@ -6,32 +6,103 @@
|
|||||||
<span class="subtitle">从源代码到产物的完整旅程</span>
|
<span class="subtitle">从源代码到产物的完整旅程</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="intro-text">
|
||||||
|
想象你在开一家<span class="highlight">面包店</span>:面粉要过筛、搅拌、发酵、烘烤,最后才能变成香喷喷的面包。代码也一样,需要经过一道道"加工工序",才能变成浏览器能运行的程序。
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="pipeline">
|
<div class="pipeline">
|
||||||
<div v-for="(stage, i) in stages" :key="stage.id" class="stage">
|
<div
|
||||||
|
v-for="(stage, i) in stages"
|
||||||
|
:key="stage.id"
|
||||||
|
class="stage"
|
||||||
|
:class="{ active: activeStage === stage.id }"
|
||||||
|
@click="activeStage = activeStage === stage.id ? null : stage.id"
|
||||||
|
>
|
||||||
<div class="stage-icon">{{ stage.icon }}</div>
|
<div class="stage-icon">{{ stage.icon }}</div>
|
||||||
<div class="stage-name">{{ stage.name }}</div>
|
<div class="stage-name">{{ stage.name }}</div>
|
||||||
<div class="stage-desc">{{ stage.desc }}</div>
|
<div class="stage-simple">{{ stage.simple }}</div>
|
||||||
<div v-if="i < stages.length - 1" class="arrow">→</div>
|
<div v-if="i < stages.length - 1" class="arrow">→</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Transition name="fade">
|
||||||
|
<div v-if="activeStage" class="stage-detail">
|
||||||
|
<div class="detail-header">
|
||||||
|
<span class="detail-icon">{{ currentStage?.icon }}</span>
|
||||||
|
<span class="detail-title">{{ currentStage?.name }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="detail-content">
|
||||||
|
<p class="detail-desc">{{ currentStage?.detailDesc }}</p>
|
||||||
|
<div class="detail-example">
|
||||||
|
<div class="example-label">🌰 举个例子:</div>
|
||||||
|
<div class="example-content">{{ currentStage?.example }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
|
||||||
|
<div v-if="!activeStage" class="hint-text">
|
||||||
|
👆 点击上方任意阶段,查看详细解释
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="info-box">
|
<div class="info-box">
|
||||||
<span class="icon">💡</span>
|
<span class="icon">💡</span>
|
||||||
<strong>流水线思想:</strong>就像工厂流水线一样,代码经过一道道工序,最终变成可以在浏览器运行的产物。每个阶段各司其职,环环相扣。
|
<strong>核心思想:</strong>就像工厂流水线一样,代码经过一道道工序,最终变成可以在浏览器运行的产物。每个阶段各司其职,环环相扣。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
|
const activeStage = ref(null)
|
||||||
|
|
||||||
const stages = ref([
|
const stages = ref([
|
||||||
{ id: 1, icon: '🔍', name: '代码检查', desc: 'ESLint/TSC' },
|
{
|
||||||
{ id: 2, icon: '⚙️', name: '代码转换', desc: 'Babel/SWC' },
|
id: 1,
|
||||||
{ id: 3, icon: '📦', name: '依赖解析', desc: '构建依赖图' },
|
icon: '🔍',
|
||||||
{ id: 4, icon: '📚', name: '模块打包', desc: 'Webpack/Rollup' },
|
name: '代码检查',
|
||||||
{ id: 5, icon: '✨', name: '代码优化', desc: '压缩/TreeShake' }
|
simple: '找错误',
|
||||||
|
detailDesc: '就像写作文前先检查有没有错别字和语法错误。代码检查工具会自动发现你的代码问题,比如变量名拼写错误、漏写了分号、使用了未定义的变量等。',
|
||||||
|
example: '你写了 const mesage = "hello",检查工具会提醒:"mesage 是不是想写 message?这个变量名看起来有拼写错误。"'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
icon: '⚙️',
|
||||||
|
name: '代码转换',
|
||||||
|
simple: '翻译官',
|
||||||
|
detailDesc: '就像把中文翻译成英文让外国人能看懂。你写的可能是 TypeScript 或新版 JavaScript 语法,但老浏览器"看不懂",需要转换成它们能理解的旧版本。',
|
||||||
|
example: '你写了 const name = user?.name(新版语法),转换后变成 var name = user && user.name ? user.name : undefined(老浏览器能懂的写法)'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
icon: '📦',
|
||||||
|
name: '依赖解析',
|
||||||
|
simple: '理关系',
|
||||||
|
detailDesc: '就像整理食谱,搞清楚做一道菜需要哪些食材。你的代码可能引用了很多其他文件,这个阶段会分析"谁依赖谁",画出一张完整的关系图。',
|
||||||
|
example: 'main.js 引用了 utils.js,utils.js 又引用了 helper.js,解析后会生成一张"依赖地图",告诉打包工具按什么顺序处理这些文件。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
icon: '📚',
|
||||||
|
name: '模块打包',
|
||||||
|
simple: '装箱子',
|
||||||
|
detailDesc: '就像搬家时把零散的东西装进几个大箱子。你的项目可能有上百个文件,浏览器加载太多小文件会很慢,打包就是把它们合并成少数几个文件。',
|
||||||
|
example: '原来有 100 个 .js 文件,打包后变成 2 个文件:app.js(你的代码)和 vendor.js(第三方库)。浏览器只需请求 2 次而不是 100 次。'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
icon: '✨',
|
||||||
|
name: '代码优化',
|
||||||
|
simple: '瘦身',
|
||||||
|
detailDesc: '就像压缩行李箱,把不必要的东西扔掉。删除代码中的空格和注释、去掉没用到代码(Tree Shaking)、压缩变量名,让文件体积更小。',
|
||||||
|
example: '原来 100KB 的代码,优化后变成 30KB。比如把 function getUserName() { return name } 压缩成 function a(){return n}'
|
||||||
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const currentStage = computed(() => {
|
||||||
|
return stages.value.find(s => s.id === activeStage.value)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -47,13 +118,28 @@ const stages = ref([
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.demo-header .icon { font-size: 1.25rem; }
|
.demo-header .icon { font-size: 1.25rem; }
|
||||||
.demo-header .title { font-weight: bold; font-size: 1rem; }
|
.demo-header .title { font-weight: bold; font-size: 1rem; }
|
||||||
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.85rem; margin-left: 0.5rem; }
|
.demo-header .subtitle { color: var(--vp-c-text-2); font-size: 0.85rem; margin-left: 0.5rem; }
|
||||||
|
|
||||||
|
.intro-text {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: var(--vp-c-bg);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-text .highlight {
|
||||||
|
color: var(--vp-c-brand-1);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.pipeline {
|
.pipeline {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -68,8 +154,20 @@ const stages = ref([
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
min-width: 80px;
|
min-width: 85px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage:hover {
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage.active {
|
||||||
|
background: var(--vp-c-brand-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stage-icon {
|
.stage-icon {
|
||||||
@@ -82,6 +180,11 @@ const stages = ref([
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 1.25rem;
|
font-size: 1.25rem;
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage:hover .stage-icon {
|
||||||
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stage-name {
|
.stage-name {
|
||||||
@@ -90,20 +193,91 @@ const stages = ref([
|
|||||||
color: var(--vp-c-text-1);
|
color: var(--vp-c-text-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.stage-desc {
|
.stage-simple {
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
color: var(--vp-c-text-3);
|
color: var(--vp-c-brand-1);
|
||||||
margin-top: 0.2rem;
|
margin-top: 0.2rem;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.arrow {
|
.arrow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -12px;
|
right: -12px;
|
||||||
top: 16px;
|
top: 20px;
|
||||||
color: var(--vp-c-text-3);
|
color: var(--vp-c-text-3);
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hint-text {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--vp-c-text-3);
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stage-detail {
|
||||||
|
background: var(--vp-c-bg);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
border: 1px solid var(--vp-c-divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-desc {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-example {
|
||||||
|
background: var(--vp-c-bg-soft);
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
border-left: 3px solid var(--vp-c-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vp-c-text-2);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example-content {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--vp-c-text-1);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
|
||||||
.info-box {
|
.info-box {
|
||||||
background: var(--vp-c-bg-alt);
|
background: var(--vp-c-bg-alt);
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
|
|||||||
@@ -63,7 +63,19 @@
|
|||||||
|
|
||||||
## 2. 核心概念:转译、打包、构建
|
## 2. 核心概念:转译、打包、构建
|
||||||
|
|
||||||
在深入学习具体工具之前,我们需要先搞清楚几个经常被混淆的核心概念:转译、打包和构建。这三个词听起来很像,但实际上各有不同的含义和职责。为了帮助你更好地理解,我们用一个餐厅的比喻来类比它们之间的关系。
|
::: tip 🤔 这些概念和构建有什么关系?
|
||||||
|
转译、打包就是流水线上的关键工序。
|
||||||
|
|
||||||
|
当你运行 `npm run build` 时,构建工具会依次执行:
|
||||||
|
1. **代码检查** → 发现错误
|
||||||
|
2. **转译** → 把新语法翻译成浏览器能懂的代码
|
||||||
|
3. **打包** → 把分散的文件合并起来
|
||||||
|
4. **优化** → 压缩体积、删除无用代码
|
||||||
|
|
||||||
|
所以,**转译和打包是构建流程的核心环节**。理解它们,你才能知道构建工具到底在做什么,为什么有时候构建很慢,为什么有时候打包后体积很大。
|
||||||
|
:::
|
||||||
|
|
||||||
|
在深入学习具体工具之前,我们需要先搞清楚这几个核心概念。为了帮助你更好地理解,我们用一个餐厅的比喻来类比它们之间的关系。
|
||||||
|
|
||||||
### 2.1 用餐厅比喻理解三个概念
|
### 2.1 用餐厅比喻理解三个概念
|
||||||
|
|
||||||
@@ -71,9 +83,9 @@
|
|||||||
|
|
||||||
| 概念 | 🍽️ 餐厅比喻 | 实际作用 | 具体例子 |
|
| 概念 | 🍽️ 餐厅比喻 | 实际作用 | 具体例子 |
|
||||||
|------|-------------|----------|----------|
|
|------|-------------|----------|----------|
|
||||||
| **转译** | 把中文菜谱翻译成英文,让外国厨师也能看懂 | 把新语法转换成浏览器能理解的旧语法 | ES2022 → ES5、TypeScript → JavaScript |
|
| **转译** | 把中文菜谱翻译成英文,让外国厨师也能看懂 | 把新语法转换成浏览器能理解的旧语法 | 你写 `const name = user?.name`,转译后变成 `var name = user && user.name` |
|
||||||
| **打包** | 把各桌点的菜装成一个个外卖盒,方便配送 | 把分散的模块文件合并成少数几个文件 | 100 个源文件 → 3 个 bundle 文件 |
|
| **打包** | 把各桌点的菜装成一个个外卖盒,方便配送 | 把分散的模块文件合并成少数几个文件 | 你写了 50 个 .js 文件,打包后变成 2 个文件 |
|
||||||
| **构建** | 从接单、做菜、打包到配送的完整流程 | 从源代码到生产代码的完整转换过程 | 编译、检查、打包、优化、输出 |
|
| **构建** | 从接单、做菜、打包到配送的完整流程 | 从源代码到生产代码的完整转换过程 | 执行 `npm run build` 后,src 文件夹变成 dist 文件夹 |
|
||||||
|
|
||||||
### 2.2 转译(Transpile):代码的"翻译官"
|
### 2.2 转译(Transpile):代码的"翻译官"
|
||||||
|
|
||||||
@@ -114,6 +126,21 @@ var result =
|
|||||||
- **SWC** 是用 Rust 语言重写的转译器,速度比 Babel 快 20 倍以上,正在被越来越多的项目采用,包括 Next.js 等知名框架。
|
- **SWC** 是用 Rust 语言重写的转译器,速度比 Babel 快 20 倍以上,正在被越来越多的项目采用,包括 Next.js 等知名框架。
|
||||||
- **esbuild** 是用 Go 语言编写的,同样以速度著称,Vite 在开发模式下就使用它来进行快速转译。
|
- **esbuild** 是用 Go 语言编写的,同样以速度著称,Vite 在开发模式下就使用它来进行快速转译。
|
||||||
|
|
||||||
|
::: details 🔍 我的项目用的是什么转译工具?
|
||||||
|
你不需要刻意选择,通常是由项目脚手架决定的:
|
||||||
|
|
||||||
|
| 项目类型 | 默认转译工具 |
|
||||||
|
|---------|-------------|
|
||||||
|
| Vite 项目 | esbuild(开发模式)+ esbuild/rollup(生产模式) |
|
||||||
|
| Create React App | Babel |
|
||||||
|
| Next.js | SWC(新版本)/ Babel(旧版本) |
|
||||||
|
| Vue CLI | Babel |
|
||||||
|
|
||||||
|
想知道自己项目用的是什么?打开 `package.json`,搜索 `babel`、`@babel/core` 这些关键词。如果找到了,说明用的是 Babel;如果没有,很可能是 esbuild 或 SWC。
|
||||||
|
|
||||||
|
**其实你不需要关心这个**——这些工具对开发者是"透明"的,你只管写代码,它们会在后台默默工作。
|
||||||
|
:::
|
||||||
|
|
||||||
### 2.3 打包(Bundle):模块的"打包员"
|
### 2.3 打包(Bundle):模块的"打包员"
|
||||||
|
|
||||||
打包是指把多个分散的模块文件合并成一个(或几个)文件的过程。在早期的前端开发中,我们习惯把所有代码写在一个 JS 文件里,但随着项目规模增大,这种方式变得难以维护。现代前端采用模块化开发,每个功能一个文件,但浏览器加载大量小文件会带来性能问题,这就需要打包工具来帮忙。
|
打包是指把多个分散的模块文件合并成一个(或几个)文件的过程。在早期的前端开发中,我们习惯把所有代码写在一个 JS 文件里,但随着项目规模增大,这种方式变得难以维护。现代前端采用模块化开发,每个功能一个文件,但浏览器加载大量小文件会带来性能问题,这就需要打包工具来帮忙。
|
||||||
@@ -202,23 +229,79 @@ dist/
|
|||||||
|
|
||||||
## 3. 实战:一个团队的工程化演进之路
|
## 3. 实战:一个团队的工程化演进之路
|
||||||
|
|
||||||
讲了这么多概念,让我们看一个真实的案例:某创业公司是如何从"直接写 HTML"一步步进化到"现代化工程化流程"的。这个案例中的很多问题和解决方案,可能正是你目前面临或将要面临的。
|
::: tip 🤔 什么是"工程化"?
|
||||||
|
说了半天"工程化",它到底是什么意思?
|
||||||
|
|
||||||
|
**简单来说,工程化就是把"手工作坊"变成"现代化工厂"的过程。**
|
||||||
|
|
||||||
|
想象一下:你在家做饭,想吃什么就做什么,很自由。但如果要开一家餐厅,每天服务几百个顾客,就不能再"想做什么做什么"了——你需要标准化的菜谱、规范的操作流程、统一的原材料采购,这样才能保证每道菜的质量稳定、出餐效率高。
|
||||||
|
|
||||||
|
前端开发也一样。一个人写小项目,怎么写都行。但团队协作、项目变大后,就需要:
|
||||||
|
- **统一的代码规范**:大家都按同样的方式写代码
|
||||||
|
- **自动化工具**:让机器帮我们检查错误、转换代码、打包文件
|
||||||
|
- **标准化流程**:从开发到上线有一套清晰的步骤
|
||||||
|
|
||||||
|
**这就是工程化:用工具和规范,让开发更高效、代码更可靠、协作更顺畅。**
|
||||||
|
:::
|
||||||
|
|
||||||
|
讲了这么多概念,让我们看一个真实的案例:某创业公司是如何从"直接写 HTML"一步步进化到"现代化工程化流程"的。通过这个案例,你会更直观地理解工程化到底解决了什么问题。
|
||||||
|
|
||||||
|
::: tip 📖 背景知识:jQuery、Vue、React 是什么?
|
||||||
|
在开始案例之前,先简单介绍一下这些名词:
|
||||||
|
|
||||||
|
- **jQuery**:十多年前最流行的 JavaScript 库,用来简化 DOM 操作(比如"点击按钮后改变文字")。现在已经被 Vue、React 等现代框架取代,但很多老项目还在用。
|
||||||
|
- **Vue / React**:现代前端开发的主流框架。它们让你用"组件"的方式组织代码,数据和视图自动同步,开发效率更高。你现在学的很可能就是其中之一。
|
||||||
|
|
||||||
|
**简单理解**:jQuery 是"手动挡",你要自己操作每一个元素;Vue/React 是"自动挡",你只需要告诉它数据是什么,它会自动更新界面。
|
||||||
|
:::
|
||||||
|
|
||||||
### 3.1 演进的全景图
|
### 3.1 演进的全景图
|
||||||
|
|
||||||
| 阶段 | 时代背景 | 主要特征 | 核心痛点 |
|
::: tip 🤔 什么是脚手架?
|
||||||
|------|----------|----------|----------|
|
脚手架就是帮你"搭好项目骨架"的工具。比如 `npm create vite@latest` 会自动创建一个配置好的项目,里面有目录结构、配置文件、示例代码,你直接开始写业务代码就行。
|
||||||
| **阶段一:原始时代** | 早期小团队 | 直接写 HTML/JS/CSS | 全局变量污染、依赖混乱 |
|
|
||||||
| **阶段二:模块化** | 团队扩张期 | 引入 Webpack + Babel | 构建速度慢、配置复杂 |
|
|
||||||
| **阶段三:现代化** | 追求效率期 | 迁移到 Vite | 需要学习新工具链 |
|
|
||||||
| **阶段四:持续优化** | 成熟稳定期 | 不断改进和优化 | 避免常见的打包陷阱 |
|
|
||||||
|
|
||||||
### 3.2 阶段一:原始时代——痛点初现
|
**没有脚手架的时代**:你要手动创建文件夹、写配置文件、安装依赖...一个项目搭建下来可能要半天。
|
||||||
|
**有脚手架的时代**:一条命令,30 秒搞定。
|
||||||
|
:::
|
||||||
|
|
||||||
在这个阶段,团队只有 3 个前端工程师,做一个管理后台项目。当时的项目结构非常简单:几个 HTML 页面,加上一些 CSS 和 JS 文件,以及一些图片资源。
|
下面这张表展示了工程化演进的四个阶段,你可以看到构建工具、脚手架、框架是如何一步步进化的:
|
||||||
|
|
||||||
|
| 阶段 | 构建工具 | 脚手架 | 框架 | 核心变化 |
|
||||||
|
|------|---------|--------|------|----------|
|
||||||
|
| **阶段一:原始时代** | 无(直接运行) | 无(手动建文件) | jQuery | 没有任何工具,全靠手工 |
|
||||||
|
| **阶段二:模块化** | Webpack + Babel | 简单模板复制 | Vue 2 / React | 开始有构建流程,但配置很麻烦 |
|
||||||
|
| **阶段三:现代化** | Vite | create-vite / create-react-app | Vue 3 / React 18 | 开箱即用,零配置启动 |
|
||||||
|
| **阶段四:持续优化** | Vite + 插件 | 自定义脚手架模板 | 框架 + TypeScript | 团队规范化、模板化 |
|
||||||
|
|
||||||
|
::: tip 📊 从表格中你能看到什么?
|
||||||
|
让我们逐行解读这张表:
|
||||||
|
|
||||||
|
**阶段一 → 阶段二**:从"没有工具"到"有了工具"。这是质的飞跃——你开始用构建工具处理代码,用框架组织项目。但代价是配置复杂,新人上手难。
|
||||||
|
|
||||||
|
**阶段二 → 阶段三**:从"能用"到"好用"。Vite 把原来需要手动配置的东西都自动化了,脚手架一键生成项目,开发体验大幅提升。你现在大概率就处在这个阶段。
|
||||||
|
|
||||||
|
**阶段三 → 阶段四**:从"个人好用"到"团队高效"。当团队变大后,需要统一的技术栈和规范,这时候会自定义脚手架模板,让所有项目保持一致的风格。
|
||||||
|
|
||||||
|
**总结一下**:工程化演进不只是"构建工具变快了",而是**整个开发体验的升级**——从手动搭建项目到脚手架一键生成,从复杂配置到开箱即用,从各自为战到团队规范。
|
||||||
|
:::
|
||||||
|
|
||||||
|
### 3.2 阶段一:原始时代——全靠手工
|
||||||
|
|
||||||
|
为什么叫"原始时代"?因为这个阶段没有任何自动化工具,所有事情都要手动完成——创建文件夹、写代码、管理依赖、调试问题,全部靠人工。
|
||||||
|
|
||||||
|
在这个阶段,团队只有 3 个前端工程师,做一个管理后台项目。项目很小,大家各写各的,看起来没什么问题。但随着项目变大,问题开始暴露出来。
|
||||||
|
|
||||||
|
**开发方式**:
|
||||||
|
- **构建工具**:无,直接写 HTML/JS/CSS,浏览器直接运行
|
||||||
|
- **脚手架**:无,手动创建文件夹和文件
|
||||||
|
- **框架**:jQuery,用选择器操作 DOM
|
||||||
|
|
||||||
|
**这个阶段的特点**:
|
||||||
|
- ✅ **优点**:简单直接,没有学习成本,写完就能跑
|
||||||
|
- ❌ **缺点**:代码一多就乱,团队协作困难,没有代码检查容易出 bug
|
||||||
|
|
||||||
::: details 查看当时的项目结构和代码方式
|
::: details 查看当时的项目结构和代码方式
|
||||||
**项目结构**:
|
**项目结构**(手动创建):
|
||||||
```
|
```
|
||||||
project/
|
project/
|
||||||
├── index.html
|
├── index.html
|
||||||
@@ -266,47 +349,72 @@ var ModuleA = (function () {
|
|||||||
|
|
||||||
这种开发方式在小项目中还能应付,但随着团队扩大到 8 人、项目变得越来越复杂,这些问题开始严重影响开发效率和代码质量,团队迫切需要一种更好的组织方式。
|
这种开发方式在小项目中还能应付,但随着团队扩大到 8 人、项目变得越来越复杂,这些问题开始严重影响开发效率和代码质量,团队迫切需要一种更好的组织方式。
|
||||||
|
|
||||||
### 3.3 阶段二:引入模块化——初见曙光
|
### 3.3 阶段二:模块化时代——开始有工具链
|
||||||
|
|
||||||
转折点出现在团队决定引入现代化工具链。他们选择了 Webpack 作为打包工具,Babel 作为转译器,ESLint 作为代码检查工具,并用 npm/yarn 管理依赖。这次升级带来了质的飞跃。
|
原始时代的问题积累到一定程度,团队终于决定引入现代化工具链。这是一个重要的转折点——从"手工劳动"进入"机械化生产"。
|
||||||
|
|
||||||
::: details 查看引入 Webpack 后的变化
|
但这个阶段也有代价:工具链的学习成本很高,配置文件复杂,新人上手需要时间。
|
||||||
**新的项目结构**(模块化组织):
|
|
||||||
|
**开发方式**:
|
||||||
|
- **构建工具**:Webpack + Babel,需要写配置文件
|
||||||
|
- **脚手架**:复制旧项目模板,手动改配置
|
||||||
|
- **框架**:Vue 2 / React,组件化开发
|
||||||
|
|
||||||
|
**这个阶段的特点**:
|
||||||
|
- ✅ **优点**:模块化开发,代码可维护性大幅提升,有代码检查
|
||||||
|
- ❌ **缺点**:配置复杂,启动慢,脚手架简陋容易出错
|
||||||
|
|
||||||
|
::: details 查看引入工具链后的变化
|
||||||
|
**项目结构**(Webpack + Vue 2 时代):
|
||||||
```
|
```
|
||||||
src/
|
my-project/
|
||||||
├── components/ # 可复用的 UI 组件
|
├── build/ # 构建配置(这个阶段配置很复杂!)
|
||||||
│ ├── Button/
|
│ ├── webpack.base.js
|
||||||
│ │ ├── index.js
|
│ ├── webpack.dev.js
|
||||||
│ │ ├── Button.vue
|
│ └── webpack.prod.js
|
||||||
│ │ └── Button.test.js
|
├── config/ # 环境配置
|
||||||
│ └── Modal/
|
|
||||||
├── utils/ # 工具函数
|
|
||||||
│ ├── index.js
|
│ ├── index.js
|
||||||
│ ├── date.js
|
│ ├── dev.env.js
|
||||||
│ └── http.js
|
│ └── prod.env.js
|
||||||
├── services/ # API 服务层
|
├── src/
|
||||||
│ ├── user.js
|
│ ├── components/ # 组件
|
||||||
│ └── order.js
|
│ ├── views/ # 页面
|
||||||
├── assets/ # 静态资源
|
│ ├── router/ # 路由
|
||||||
│ ├── images/
|
│ ├── store/ # 状态管理
|
||||||
│ └── styles/
|
│ ├── App.vue
|
||||||
├── App.vue # 根组件
|
│ └── main.js
|
||||||
└── main.js # 入口文件
|
├── static/ # 静态资源
|
||||||
|
├── .eslintrc.js # ESLint 配置
|
||||||
|
├── .babelrc # Babel 配置
|
||||||
|
├── package.json
|
||||||
|
└── index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
**现代化代码示例**:
|
**配置文件示例**(这就是为什么说"配置复杂"):
|
||||||
```js
|
```js
|
||||||
// ES6 模块化导入,依赖关系清晰
|
// webpack.base.js - 仅仅是基础配置就有这么多内容
|
||||||
import { debounce } from 'lodash-es'
|
const path = require('path')
|
||||||
import Button from '@/components/Button.vue'
|
const VueLoaderPlugin = require('vue-loader/lib/plugin')
|
||||||
|
|
||||||
export default {
|
module.exports = {
|
||||||
components: { Button },
|
entry: './src/main.js',
|
||||||
methods: {
|
output: {
|
||||||
// 使用 debounce 防抖,代码简洁优雅
|
path: path.resolve(__dirname, '../dist'),
|
||||||
handleInput: debounce(function() {
|
filename: '[name].[contenthash].js'
|
||||||
// 处理输入逻辑
|
},
|
||||||
}, 300)
|
module: {
|
||||||
|
rules: [
|
||||||
|
{ test: /\.vue$/, loader: 'vue-loader' },
|
||||||
|
{ test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
|
||||||
|
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
|
||||||
|
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] },
|
||||||
|
{ test: /\.(png|jpg|gif)$/, loader: 'url-loader', options: { limit: 8192 } }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [new VueLoaderPlugin()],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.vue', '.json'],
|
||||||
|
alias: { '@': path.resolve(__dirname, '../src') }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -316,31 +424,81 @@ export default {
|
|||||||
2. **代码复用**:组件和工具函数可以在不同项目中复用,不用再复制粘贴
|
2. **代码复用**:组件和工具函数可以在不同项目中复用,不用再复制粘贴
|
||||||
3. **代码质量**:ESLint 在保存时自动检查,TypeScript 在编译时发现类型错误
|
3. **代码质量**:ESLint 在保存时自动检查,TypeScript 在编译时发现类型错误
|
||||||
4. **性能优化**:Webpack 的代码分割和懒加载让首屏加载速度大幅提升
|
4. **性能优化**:Webpack 的代码分割和懒加载让首屏加载速度大幅提升
|
||||||
|
|
||||||
|
**新的痛点**:
|
||||||
|
1. **配置复杂**:webpack.config.js 动辄几百行,新人很难上手
|
||||||
|
2. **启动慢**:冷启动 30 秒以上,改代码热更新要等 5 秒
|
||||||
|
3. **脚手架简陋**:复制旧项目模板,经常忘记改配置,导致各种奇怪问题
|
||||||
:::
|
:::
|
||||||
|
|
||||||
然而,随着项目规模继续增长,Webpack 的问题也开始显现:冷启动时间越来越长,大型项目的冷启动可能需要 30 秒以上;修改代码后的热更新也需要等待好几秒;复杂的配置文件动辄几百行,新人很难快速上手。
|
### 3.4 阶段三:现代化时代——开箱即用
|
||||||
|
|
||||||
### 3.4 阶段三:迁移到 Vite——效率飞跃
|
阶段二的痛点(配置复杂、启动慢)困扰了开发者很多年。直到 2021 年,Vite 的出现彻底改变了这一切。
|
||||||
|
|
||||||
|
Vite 的核心理念是"约定优于配置"——它内置了合理的默认配置,你不需要写几百行配置文件,开箱即用。这就像从"自己组装电脑"变成了"买品牌机",省去了大量折腾的时间。
|
||||||
|
|
||||||
2021 年之后,团队开始用 Vite 替代 Webpack,开发体验得到了质的提升。
|
2021 年之后,团队开始用 Vite 替代 Webpack,开发体验得到了质的提升。
|
||||||
|
|
||||||
::: details Vite 与 Webpack 的对比
|
**开发方式**:
|
||||||
| 对比项 | Webpack | Vite | 差异 |
|
- **构建工具**:Vite,零配置启动,秒级热更新
|
||||||
|
- **脚手架**:`npm create vite@latest`,一键生成项目
|
||||||
|
- **框架**:Vue 3 / React 18,更强大的组件系统
|
||||||
|
|
||||||
|
**这个阶段的特点**:
|
||||||
|
- ✅ **优点**:秒级启动,热更新极快,配置简单,新人友好
|
||||||
|
- ❌ **缺点**:生态还在完善中,某些特殊需求可能需要额外配置
|
||||||
|
|
||||||
|
::: details Vite 带来的变化
|
||||||
|
**项目结构**(Vite + Vue 3 时代):
|
||||||
|
```
|
||||||
|
my-project/
|
||||||
|
├── src/
|
||||||
|
│ ├── components/ # 组件
|
||||||
|
│ ├── views/ # 页面
|
||||||
|
│ ├── router/ # 路由
|
||||||
|
│ ├── stores/ # 状态管理(Pinia)
|
||||||
|
│ ├── assets/ # 静态资源
|
||||||
|
│ ├── App.vue
|
||||||
|
│ └── main.js
|
||||||
|
├── public/ # 公共资源
|
||||||
|
├── vite.config.js # 配置文件(简洁!)
|
||||||
|
├── package.json
|
||||||
|
└── index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
**配置文件对比**(Vite 配置有多简洁):
|
||||||
|
```js
|
||||||
|
// vite.config.js - 整个配置文件就这么点
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
resolve: {
|
||||||
|
alias: { '@': '/src' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 对比上面 Webpack 的配置,是不是简洁太多了?
|
||||||
|
```
|
||||||
|
|
||||||
|
| 对比项 | 阶段二(Webpack) | 阶段三(Vite) | 体验提升 |
|
||||||
|--------|---------|------|------|
|
|--------|---------|------|------|
|
||||||
| 冷启动 | 30s+ | <1s | **快 30 倍以上** |
|
| 创建项目 | 复制模板,手动改配置 | `npm create vite@latest` | 30 秒搞定 |
|
||||||
| HMR 更新 | 3-5s | <100ms | **快 30 倍以上** |
|
| 冷启动 | 30s+ | <1s | **快 30 倍** |
|
||||||
| 配置复杂度 | 高(需大量配置) | 低(约定优于配置) | **大幅简化** |
|
| 热更新 | 3-5s | <100ms | **快 30 倍** |
|
||||||
| 学习曲线 | 陡峭 | 平缓 | **更容易上手** |
|
| 配置文件 | 几百行 | 几十行甚至不需要 | **大幅简化** |
|
||||||
|
|
||||||
**实际体验对比**:
|
**实际体验对比**:
|
||||||
```bash
|
```bash
|
||||||
# 以前使用 Webpack
|
# 阶段二:使用 Webpack
|
||||||
npm run dev
|
npm run dev
|
||||||
# 等待 30 秒...喝杯咖啡回来还在编译
|
# 等待 30 秒...喝杯咖啡回来还在编译
|
||||||
# [INFO] Compiled successfully in 30123ms
|
# [INFO] Compiled successfully in 30123ms
|
||||||
# 修改代码 -> 保存 -> 等待 5 秒 -> 终于看到效果
|
# 修改代码 -> 保存 -> 等待 5 秒 -> 终于看到效果
|
||||||
|
|
||||||
# 现在使用 Vite
|
# 阶段三:使用 Vite
|
||||||
|
npm create vite@latest my-project # 一键创建项目
|
||||||
|
cd my-project && npm install
|
||||||
npm run dev
|
npm run dev
|
||||||
# 等待 300 毫秒...还没反应过来就好了
|
# 等待 300 毫秒...还没反应过来就好了
|
||||||
# [INFO] ready in 312ms
|
# [INFO] ready in 312ms
|
||||||
@@ -348,11 +506,81 @@ npm run dev
|
|||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### 3.5 阶段四:持续优化——避坑指南
|
### 3.5 阶段四:持续优化——团队规范化
|
||||||
|
|
||||||
在工程化演进的过程中,团队也踩过不少坑。这些坑很多都与构建和打包有关,了解它们可以帮助你避免重蹈覆辙。
|
当工具链成熟后,团队开始关注更深层次的问题:如何让团队协作更高效?如何避免重复踩坑?如何统一代码风格?
|
||||||
|
|
||||||
::: details 常见踩坑与解决方案
|
这个阶段的核心是"规范化"——不只是工具好用,还要让团队所有人用同样的方式工作。
|
||||||
|
|
||||||
|
**开发方式**:
|
||||||
|
- **构建工具**:Vite + 自定义插件,适配团队特殊需求
|
||||||
|
- **脚手架**:团队内部脚手架模板,统一技术栈和规范
|
||||||
|
- **框架**:Vue 3 / React 18 + TypeScript,类型安全
|
||||||
|
|
||||||
|
**这个阶段的特点**:
|
||||||
|
- ✅ **优点**:团队协作高效,代码风格统一,新人入职有模板可循
|
||||||
|
- ❌ **缺点**:需要投入时间维护脚手架和规范,有一定维护成本
|
||||||
|
|
||||||
|
**这个阶段会做什么?**
|
||||||
|
1. **自定义脚手架模板**:把团队常用的配置、目录结构、公共组件打包成模板,新项目一键生成
|
||||||
|
2. **引入 TypeScript**:让代码有类型检查,减少运行时错误
|
||||||
|
3. **建立代码规范**:ESLint 规则、Git 提交规范、代码审查流程
|
||||||
|
4. **持续集成/持续部署(CI/CD)**:代码提交后自动测试、自动部署
|
||||||
|
|
||||||
|
::: details 团队规范化阶段的项目结构
|
||||||
|
**项目结构**(团队内部模板 + TypeScript):
|
||||||
|
```
|
||||||
|
my-project/
|
||||||
|
├── .husky/ # Git hooks(提交前自动检查)
|
||||||
|
├── src/
|
||||||
|
│ ├── components/ # 组件
|
||||||
|
│ ├── views/ # 页面
|
||||||
|
│ ├── router/ # 路由
|
||||||
|
│ ├── stores/ # 状态管理
|
||||||
|
│ ├── api/ # API 接口
|
||||||
|
│ ├── utils/ # 工具函数
|
||||||
|
│ ├── types/ # TypeScript 类型定义
|
||||||
|
│ ├── assets/ # 静态资源
|
||||||
|
│ ├── App.vue
|
||||||
|
│ └── main.ts # 注意是 .ts 不是 .js
|
||||||
|
├── public/
|
||||||
|
├── .eslintrc.cjs # ESLint 配置(团队统一规则)
|
||||||
|
├── .prettierrc # Prettier 配置(代码格式化)
|
||||||
|
├── tsconfig.json # TypeScript 配置
|
||||||
|
├── vite.config.ts # Vite 配置
|
||||||
|
├── package.json
|
||||||
|
└── README.md # 项目文档
|
||||||
|
```
|
||||||
|
|
||||||
|
**团队规范化的具体体现**:
|
||||||
|
```js
|
||||||
|
// tsconfig.json - TypeScript 配置,类型安全
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"strict": true, // 开启严格模式
|
||||||
|
"noImplicitAny": true, // 禁止隐式 any
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": { "@/*": ["src/*"] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .eslintrc.cjs - 团队统一的代码规范
|
||||||
|
module.exports = {
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/vue3-recommended',
|
||||||
|
'@vue/standard',
|
||||||
|
'@vue/typescript/recommended'
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'no-console': 'warn', // 禁止 console.log
|
||||||
|
'no-debugger': 'error', // 禁止 debugger
|
||||||
|
'vue/multi-word-component-names': 'error' // 组件名必须是多词
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**常见踩坑与解决方案**:
|
||||||
|
|
||||||
**坑一:引入整个库而不是按需引入**
|
**坑一:引入整个库而不是按需引入**
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user