
1. 从“零”开始为什么我们要绕开脚手架最近在面试和带新人的过程中我发现一个挺有意思的现象很多简历上写着“精通Vue3”的朋友当被问到“如果不使用create-vue或Vite模板你如何从零初始化一个Vue3项目”时往往会卡壳。他们的第一反应是“啊现在不都用脚手架吗自己搭多麻烦。”这恰恰点出了当前前端学习的一个普遍误区——对工具的过度依赖导致对底层构建逻辑的认知模糊。脚手架如create-vue、Vite无疑极大地提升了开发效率它像一份精心搭配好的“预制菜”开箱即用。但长期只吃“预制菜”你可能会失去挑选食材、掌握火候、甚至设计菜谱的能力。所以这次我们不依赖任何现成的项目框架或脚手架就用最原始的npm init从一个空文件夹开始一步步搭建一个“过得去”的Vue3前端项目。这个“过得去”的标准是具备现代前端开发的核心要素模块化、热更新、类型检查、代码规范、构建优化结构清晰便于后续维护和扩展。这不仅仅是一个搭建教程更是一次对前端工程化核心链条的深度梳理。你会彻底明白那些被脚手架隐藏起来的配置究竟在为什么服务。2. 项目初始化与最简依赖引入我们的起点是一个绝对空白的目录。打开终端执行以下命令mkdir vue3-from-scratch cd vue3-from-scratch npm init -y这会生成一个最基础的package.json文件。现在我们需要思考一个Vue3应用最核心的依赖是什么答案是vue本身。所以我们首先安装Vue3npm install vuenext注意这里我们特意使用next来确保安装的是Vue3的最新稳定版。安装后你的package.json的dependencies中会出现vue: ^3.x.x。只有Vue库是不够的。Vue3的模板如template中的内容需要被转换成浏览器能理解的渲染函数。在Vue生态中负责这项工作的核心工具是vue/compiler-sfc(Single File Component compiler)。它专门用于编译.vue单文件组件。因此我们需要将其安装为开发依赖npm install vue/compiler-sfc -D至此我们拥有了运行和编译Vue3代码的最基础能力。但如何让这些代码在浏览器中运行起来呢这就需要引入构建工具。3. 构建工具选型为什么是Vite在早期Webpack是绝对的主流但其复杂的配置和缓慢的启动/热更新速度在项目变大后成为痛点。我们选择Vite原因非常直接极致的开发体验基于原生ES模块ESMVite在开发环境下无需打包启动速度极快。热更新HMR也是按需进行几乎瞬间完成。对Vue的一等公民支持Vite由Vue作者尤雨溪创建对Vue单文件组件SFC的支持是原生且最优的。简洁的配置相比WebpackVite的配置更加直观和简洁更符合我们“从零理解”的目标。因此我们安装Vitenpm install vite -D同时为了让Vite能够正确识别和编译.vue文件我们还需要安装官方的Vue插件npm install vitejs/plugin-vue -D现在我们的核心开发依赖已经齐全。让我们创建项目的基础结构。4. 搭建项目骨架与基础配置首先创建必要的目录和入口文件vue3-from-scratch/ ├── public/ # 静态资源目录会被直接复制到构建输出目录 ├── src/ # 源代码目录 │ ├── App.vue # 应用根组件 │ └── main.js # 应用入口文件 ├── index.html # 页面入口HTML ├── package.json └── vite.config.js # Vite配置文件4.1 配置index.html这是应用的页面入口。关键点在于我们需要一个div idapp/div作为Vue应用的挂载点并通过script typemodule引入我们的入口JS文件。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleVue3 From Scratch/title /head body div idapp/div script typemodule src/src/main.js/script /body /html4.2 编写src/main.js这是JavaScript的入口文件。在这里我们导入Vue和根组件并创建Vue应用实例。import { createApp } from vue import App from ./App.vue createApp(App).mount(#app)4.3 编写src/App.vue这是我们的第一个单文件组件采用Vue3的script setup语法这是目前最推荐、最简洁的组合式API写法。template div h1Hello, Vue3 From Scratch!/h1 p这是一个完全不依赖脚手架从零搭建的项目。/p /div /template script setup // 这里可以导入其他组件或逻辑 /script style scoped h1 { color: #42b983; } /style4.4 配置vite.config.js这是Vite的核心配置文件。我们需要引入vitejs/plugin-vue来让Vite处理.vue文件。import { defineConfig } from vite import vue from vitejs/plugin-vue // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()] })至此一个最最基础的Vue3项目骨架就完成了。在package.json的scripts中添加启动命令scripts: { dev: vite, build: vite build, preview: vite preview }运行npm run dev打开浏览器访问http://localhost:5173你应该能看到绿色的“Hello, Vue3 From Scratch!”字样。恭喜你已经成功迈出了第一步5. 工程化深化路由、状态管理与代码规范一个“过得去”的项目不能只是一个Hello World。接下来我们为其注入现代前端应用的灵魂路由和状态管理。5.1 集成Vue Router对于单页面应用SPA路由是必不可少的。我们安装Vue Router 4对应Vue3的版本npm install vue-router4在src目录下创建router/index.js文件来配置路由import { createRouter, createWebHistory } from vue-router // 定义路由组件这里先使用懒加载提升首屏性能 const Home () import(../views/Home.vue) const About () import(../views/About.vue) const routes [ { path: /, component: Home }, { path: /about, component: About } ] const router createRouter({ history: createWebHistory(), routes }) export default router然后在main.js中安装路由import { createApp } from vue import App from ./App.vue import router from ./router createApp(App).use(router).mount(#app)最后修改App.vue加入router-view来显示路由组件并添加导航template div nav router-link to/Home/router-link | router-link to/aboutAbout/router-link /nav router-view / /div /template别忘了创建对应的src/views/Home.vue和src/views/About.vue组件。5.2 集成Pinia进行状态管理对于复杂应用组件间的状态共享是个问题。Vuex 4虽然可用但Pinia是Vue官方推荐的新一代状态管理库API更简洁且完美支持TypeScript。我们选择Pinia。npm install pinia在src目录下创建stores目录并创建一个示例store例如src/stores/counter.jsimport { defineStore } from pinia export const useCounterStore defineStore(counter, { state: () ({ count: 0 }), actions: { increment() { this.count } }, getters: { doubleCount: (state) state.count * 2 } })在main.js中安装Piniaimport { createApp } from vue import { createPinia } from pinia import App from ./App.vue import router from ./router const app createApp(App) app.use(createPinia()) app.use(router) app.mount(#app)现在你就可以在任何组件中通过const counterStore useCounterStore()来使用这个store了。5.3 引入ESLint和Prettier统一代码风格团队协作和代码维护离不开统一的代码规范。ESLint负责检查代码质量问题Prettier负责代码格式化。首先安装相关依赖npm install eslint eslint-plugin-vue typescript-eslint/parser typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier -D注意这里我们一并考虑了未来可能使用TypeScript的情况所以安装了typescript-eslint相关的包。如果确定只用JavaScript可以暂时不装。创建.eslintrc.js配置文件module.exports { root: true, env: { node: true, browser: true, es2021: true }, extends: [ eslint:recommended, plugin:vue/vue3-recommended, // 使用Vue3的推荐规则 plugin:prettier/recommended // 将Prettier规则集成进ESLint ], parserOptions: { ecmaVersion: latest, sourceType: module }, rules: { // 可以在这里覆盖或添加自定义规则 vue/multi-word-component-names: off // 允许单个单词的组件名如Home.vue } }创建.prettierrc配置文件{ semi: false, singleQuote: true, trailingComma: none, printWidth: 100, tabWidth: 2, endOfLine: auto }在package.json中添加lint和format脚本scripts: { dev: vite, build: vite build, preview: vite preview, lint: eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore, format: prettier --write . }现在运行npm run lint可以自动修复大部分代码风格问题npm run format可以用Prettier格式化所有文件。6. 开发体验与生产优化配置6.1 配置路径别名Alias在import模块时../../../这种相对路径非常难以维护。Vite允许我们配置路径别名。修改vite.config.jsimport { defineConfig } from vite import vue from vitejs/plugin-vue import path from path export default defineConfig({ plugins: [vue()], resolve: { alias: { : path.resolve(__dirname, ./src) // 将 指向 src 目录 } } })同时为了让ESLint和TypeScript如果使用能识别这个别名需要在项目根目录创建jsconfig.json对于JS项目或tsconfig.json对于TS项目。jsconfig.json示例{ compilerOptions: { baseUrl: ., paths: { /*: [src/*] } }, exclude: [node_modules, dist] }现在在代码中就可以使用import HelloWorld from /components/HelloWorld.vue这样的清晰路径了。6.2 环境变量管理不同环境开发、测试、生产需要不同的配置如API地址。Vite使用.env文件来管理环境变量。.env所有环境共享.env.development开发环境npm run dev时自动加载.env.production生产环境npm run build时自动加载创建一个.env.development文件VITE_API_BASE_URLhttp://localhost:3000/api重要Vite规定只有以VITE_开头的变量才会被暴露给客户端代码。这是出于安全考虑避免敏感信息泄露。在代码中可以通过import.meta.env.VITE_API_BASE_URL来访问这个变量。6.3 生产构建优化Vite的默认构建配置已经不错但我们还可以进行一些微调。分包策略ManualChunks将node_modules中的依赖打包成单独的chunk避免业务代码变更导致整个vendor缓存失效。资源内联/压缩对极小的资源如图标进行Base64内联减少HTTP请求。一个优化后的vite.config.js生产构建部分示例如下import { defineConfig } from vite import vue from vitejs/plugin-vue import path from path export default defineConfig({ plugins: [vue()], resolve: { alias: { : path.resolve(__dirname, ./src) } }, build: { rollupOptions: { output: { manualChunks(id) { if (id.includes(node_modules)) { // 将vue相关的库拆分成一个chunk if (id.includes(vue)) { return vue-vendor } // 将其他的node_modules依赖拆分成一个chunk return vendor } } } }, // 启用/禁用 CSS 代码拆分 cssCodeSplit: true, // 构建后生成资源的目录 assetsDir: static, // 小于此阈值的资源将被内联为 base64 assetsInlineLimit: 4096 } })7. 样式方案、组件库与测试接入可选但推荐7.1 选择CSS预处理器虽然可以直接写CSS但Sass/Less等预处理器能提供变量、嵌套、混合等强大功能。以Sass为例npm install sass -D安装后就可以在.vue文件的style langscss中直接使用Sass语法了。你还可以在vite.config.js中配置全局的Sass变量文件。7.2 按需引入UI组件库对于一个需要快速开发的项目引入一个成熟的UI组件库如Element Plus、Ant Design Vue、Naive UI是明智的选择。但全量引入会显著增加打包体积。我们必须掌握按需引入。以Element Plus为例首先安装npm install element-plus然后我们需要安装用于自动按需导入的插件npm install -D unplugin-vue-components unplugin-auto-import修改vite.config.jsimport { defineConfig } from vite import vue from vitejs/plugin-vue import AutoImport from unplugin-auto-import/vite import Components from unplugin-vue-components/vite import { ElementPlusResolver } from unplugin-vue-components/resolvers export default defineConfig({ plugins: [ vue(), // 自动导入API如ref, reactive, onMounted等 AutoImport({ resolvers: [ElementPlusResolver()], }), // 自动导入UI组件 Components({ resolvers: [ElementPlusResolver()], }), ], })配置完成后你就可以在模板中直接使用el-button等Element Plus组件而无需在组件内手动import和app.use。插件会在构建时自动处理按需引入极大地提升了开发体验。7.3 集成单元测试VitestVite生态下首推的测试框架是Vitest它拥有和Vite相同的配置、转换器和解析器速度极快。安装Vitest和Vue测试工具npm install vitest vue/test-utils jsdom -D在package.json中添加测试脚本scripts: { test: vitest, coverage: vitest run --coverage }创建vitest.config.js其配置可以与vite.config.js大部分共享import { defineConfig } from vitest/config import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], test: { environment: jsdom, // 模拟浏览器环境 } })然后你就可以在src目录下创建__tests__文件夹来编写你的组件单元测试了。8. 部署上线与持续集成思路项目构建完成后会生成一个dist目录里面是静态文件HTML, JS, CSS, 图片等。部署的本质就是将这些文件放到一个HTTP服务器上。8.1 简单的静态服务器部署你可以使用任何静态文件服务器如Nginx、Apache或者云服务商的对象存储如阿里云OSS、腾讯云COS配合CDN。以Nginx为例一个最简单的配置如下server { listen 80; server_name your-domain.com; root /path/to/your/dist; index index.html; location / { try_files $uri $uri/ /index.html; # 支持Vue Router的history模式 } }关键点是try_files $uri $uri/ /index.html;它确保了在直接访问子路由如/about时Nginx会返回index.html然后由前端的Vue Router来处理路由而不是返回404。8.2 自动化部署与CI/CD对于团队项目手动上传dist文件夹是不可靠的。我们可以结合Git和CI/CD工具如GitHub Actions, GitLab CI, Jenkins实现自动化部署。一个简单的GitHub Actions工作流示例.github/workflows/deploy.ymlname: Deploy to Production on: push: branches: [ main ] jobs: build-and-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - name: Setup Node.js uses: actions/setup-nodev3 with: node-version: 18 - name: Install Dependencies run: npm ci - name: Lint run: npm run lint - name: Build run: npm run build env: VITE_API_BASE_URL: ${{ secrets.PROD_API_URL }} - name: Deploy to Server via SSH uses: appleboy/ssh-actionv0.1.5 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /path/to/your/project git pull origin main npm ci --onlyproduction npm run build # 这里可以加上重启Nginx或PM2的命令这个工作流会在代码推送到main分支时自动触发执行安装依赖、代码检查、构建并通过SSH连接到服务器进行部署。走完这一整套流程你已经不仅仅是从零搭建了一个Vue3项目而是亲手铺设了一条从本地开发、代码规范、测试到自动化部署的完整前端工程化流水线。这个过程可能会遇到各种报错和配置冲突但每一次解决问题的经历都会让你对“前端项目”这四个字有更深的理解。下次再看到脚手架生成的那一堆文件你就能清晰地知道每一行配置的意图这才是真正的“过得去”。