news 2026/10/18 12:40:14

基于Vue CLI创建Vue2工程的完整指南与踩坑实录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Vue CLI创建Vue2工程的完整指南与踩坑实录

先说个很常见的场景。手头已经有一个跑了好几年的 Vue2 老工程,或者团队明确说了新项目继续用 Vue2,那你迟早会被“基于脚手架创建 Vue2 工程”这件事卡住。网上教程刷到不少,可跟着敲完vue create,下一秒就是 Node 版本不兼容、依赖装不上、路由版本对不上、组件库装成了 Vue3 专用版,折腾一晚上还在原地打转。这篇文章把我实际创建 Vue2 工程的完整过程写出来,包括选哪套脚手架、每一步在做什么、创建完以后哪些配置文件必须动、后续要补哪些依赖,以及我反复踩过的一些坑。适合正在摸脚手架的新手,也适合有 Vue3 经验但需要回头维护 Vue2 工程的同学。

1. 为什么到了现在还要新建 Vue2 工程

1.1 老项目的技术现实决定新模块的选型

我参与维护过一个内部后台系统,代码从一开始就是基于某套 Vue2 模板仓库搭建的,业务组件、权限指令、报表模块全都长在 Vue2 的生态上。业务方后来要在里面加一个新模块,最稳妥的方式不是开一个全新的 Vue3 工程,而是在同一个仓库、同一套技术栈下面继续扩展页面。这么做的理由很直接:登录态、路由守卫、权限指令、布局组件都能直接复用,新写的代码也能被老同事照常 review。

很多团队面临的问题不是“哪个框架好”,而是“存量代码怎么办”。既然没人愿意花两个月把老系统重写一遍,那新模块继续用 Vue2 就是最务实的答案。这种情况下,基于脚手架快速生成一个结构清晰的 Vue2 工程,再逐步迁移或扩展功能,比从零手写 webpack 配置要高效得多。

1.2 动手之前,先想清楚这三件事

第一,团队里还有没有人熟悉 Vue2 的写法。如果团队全是 Vue3 出身,写 Options API 和this.$router这类语法时会很不习惯,得提前约定写法规范。第二,新工程是独立部署还是要嵌入老应用。独立部署可以放开手脚配置路由模式,嵌入老应用则要尽量对齐原有依赖版本。第三,是否存在历史包袱。如果是从零起步、没有兼容压力,当然可以认真评估 Vue3;但如果要复用的组件库、工具函数和参考代码都锁定在 Vue2 上,硬上新框架只会加大成本。

有一个很容易被忽略的点:新建 Vue2 工程通常是“为了维护存量系统”而不是“逆着潮流选型”。想清楚这个前提,后面所有技术决策都会顺畅很多。

1.3 官方演进放缓,不代表工程不能继续生长

不少新手担心 Vue2 是不是已经彻底不能用了。准确地说,官方不再围绕 Vue2 做新功能演进,维护动作也按既定计划执行,但存量项目的数量仍然非常大,相关文档、组件库、历史问答都还在。这并不等于你该无脑选 Vue2,而是要在面对一个已经存在的 Vue2 体系时,能判断出“新建一个 Vue2 工程”依然是一个合理选项。

工程能不能继续生长,取决于团队愿不愿意持续维护。只要代码规范、依赖版本、构建链路都是清楚的,Vue2 项目的迭代体验不会因为框架版本变老而突然崩塌。

2. 脚手架与构建工具怎么选,不能只看热度

2.1 三条主要路线:Vue CLI、Vite 插件方案、手动拼 webpack

网上讨论 Vue2 工程创建,主要会提到三条路线。第一条是官方 Vue CLI,无论 4.x 还是 5.x,都能通过交互式命令生成 Vue2 或 Vue3 工程,内置 webpack、devServer、ESLint、多环境等能力,属于最主流的选择。第二条是 Vite 配合支持 Vue2 的社区插件,开发服务器启动速度和热更新确实快,但插件的成熟度、兼容范围都不如 webpack 生态。第三条是手动搭 webpack 5 加 vue-loader,适合对构建产物有特殊要求、或者想彻底掌控配置的极少数场景。

对普通业务工程来说,手动拼配置的维护成本很高。你要同时处理 babel 转译、样式预处理器、静态资源处理、开发服务器代理、多环境变量,稍有不慎就漏掉一块。除非团队里有专门负责工程化的同学,否则真不建议从零写。

2.2 我为什么最后留在 Vue CLI

我自己的选择比较朴素:团队大多数人已经接触过 Vue CLI 生成的工程结构,出问题后在社区里能搜到大量现成答案。Vue CLI 5 内部用的是 webpack 5,相比老版本的构建性能有明显提升,同时保留了良好的配置覆盖能力,需要改 devServer 代理、处理资源路径时,可以借助vue.config.js完成操作。

创建工程这件事,稳定性永远比新鲜感重要。Vite 的方案我也试过,小项目里确实很爽,启动速度快到有点不真实,可一旦遇到老的 CJS 依赖、需要特殊 babel 插件或者想复用某个基于 webpack 的定制 loader,排查成本一下就上来了。所以最终给团队的推荐还是 Vue CLI,跑通闭环最快,也好交接。

2.3 工具选型对比与适用场景

对比项Vue CLIVite 插件方案手动 webpack
上手成本低中高
Vue2 编译链路成熟度高中中
开发服务器速度中等快取决于配置
社区资料和插件丰富度高中低低
适合场景常规业务工程、团队协作个人原型、项目对构建速度要求极高特殊构建需求、深度定制

如果非要选 Vite,一定要先确认你要用的第三方库都兼容这套编译链路。实测下来,部分老组件库、样式处理方案和动态导入方式都需要额外适配。不要只因为“快”就换工具,开发体验的收益如果抵不上排查兼容性浪费的时间,那就是亏的。

3. 从零跑通一个 Vue2 工程的完整过程

3.1 环境准备:Node 和 npm 先过一遍

动手前先打开终端确认环境:

node -v npm -v

Vue CLI 5 对 Node 版本有要求,建议使用 14、16 或 18 这样比较稳的 LTS 版本。我实测在 Node 18 下运行 Vue CLI 5 很稳定,Node 20 大部分场景也没问题,但早期 Node 22 版本可能会遇到部分 webpack loader 的兼容问题。因此,老实的做法是安装 Node 18 LTS,不要盲目追求最新版本。

如果 npm 下载依赖很慢,可以先把镜像源换到国内公共源:

npm config set registry https://registry.npmmirror.com

这里要留意一点:如果团队有内网私有源,建议直接使用内网源,公共镜像和私有源的同步速度、包版本可能不一样,会造成同一份锁文件在两个人手里安装出不同结果。

3.2 安装脚手架并确认版本

全局安装 Vue CLI:

npm install -g @vue/cli@5 vue --version

我习惯固定到 5.x 大版本,避免未来 CLI 行为变化影响团队一致性。安装完成后,vue --version能正常输出版本号,就说明脚手架已经就绪。这里有个小提醒:如果之前装过老版本,最好先卸载再装:

npm uninstall -g @vue/cli

否则可能残留旧命令,导致生成工程时出现预期外的模板行为。

3.3 用 vue create 完成交互式创建

创建工程:

vue create my-vue2-project

交互界面里常见的选择如下:

  1. 选择Manually select features,不要直接回车选 Default,因为默认预设通常是 Vue 3。
  2. 在功能列表里勾选:Babel、Router、Vuex、CSS Pre-processors、Linter / Formatter。用方向键移动,空格键勾选。
  3. 选择 Vue 版本时,明确选2.x。
  4. CSS 预处理器建议选Sass/SCSS (with dart-sass),尽量避免老旧的 node-sass。
  5. Linter / Formatter 建议选ESLint + Prettier。
  6. 触发检查时机选Lint on save。
  7. 配置文件位置选In dedicated config files,生成的配置集中在独立文件里,后续好改。

确认选项后,CLI 会自动安装依赖。整个过程会根据网络速度从几十秒到几分钟不等,耐心等它跑完即可。

3.4 打开工程,理清目录和入口

依赖装完后:

cd my-vue2-project npm run serve

浏览器访问http://localhost:8080,看到默认欢迎页就说明工程已经能跑了。生成的核心目录结构大致如下:

src/ main.js App.vue router/index.js store/index.js views/ public/ index.html vue.config.js babel.config.js package.json

main.js是整个应用入口,它在里面创建 Vue 实例,挂载路由和状态管理。App.vue是根组件,router/index.js和store/index.js分别负责路由和全局状态。public/index.html是最终承载构建产物的 HTML 模板。建议把这个目录结构烂熟于心,因为后面所有工具链配置都是在这些文件之间打转。

4. 建完工程后,先把这些配置文件理顺

4.1 package.json 里的依赖搭配逻辑

刚生成的package.json只能保证工程能跑,真正决定它稳不稳定的,是依赖版本之间的匹配关系。写 Vue2 工程时最重要的规则是:如果用的是 Vue 2.6 及以下版本,vue和vue-template-compiler版本必须严格一致,比如"vue": "^2.6.14","vue-template-compiler": "^2.6.14"。很多莫名其妙的编译报错都是这两个包版本不匹配导致的。

如果使用的是 Vue 2.7,情况会有所变化,模板编译器的处理方式不再需要单独遵循老规则。团队里我建议统一约定:新工程尽量使用 Vue 2.7 的最新补丁版本,同时写业务代码时仍以 Options API 为主,降低学习成本和排查负担。

另外,core-js承担了大部分 polyfill 职责,@vue/cli-service是构建核心,不要随意调整它们的版本,除非你明确知道改动的影响范围。

4.2 vue.config.js 一份最常用的底稿

Vue CLI 项目里,很多工程化配置都集中在vue.config.js。下面这份是我常用的底稿:

const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, publicPath: './', outputDir: 'dist', assetsDir: 'static', devServer: { port: 8080, proxy: { '/api': { target: 'http://your-api-host', changeOrigin: true, pathRewrite: { '^/api': '' } } } } })

publicPath很关键,如果工程要部署在服务器子目录,直接把publicPath从'/'改成实际子路径,比如'/admin/';如果是纯静态文件部署且路由使用 hash 模式,写成'./'也能省事。outputDir和assetsDir控制构建输出的目录名和静态资源目录。devServer.proxy是开发环境代理,前端页面里写/api/xxx,本地开发时就会被转发到后端服务,后面我会专门讲这个配置的坑。

4.3 目标浏览器、Babel 与 polyfill

再看.browserslistrc文件,它决定了 Babel 转译和样式兼容的作用范围。默认常见配置是:

> 1% last 2 versions not dead

含义是兼容全球使用率超过 1% 的浏览器、最近两个版本的浏览器、还在维护的浏览器。如果项目只给内部团队用 Chrome,可以把它精简成:

last 2 Chrome versions

这样构建产物体积会明显变小,因为不再需要为大量老旧浏览器准备 polyfill。babel.config.js里通常就是@vue/cli-plugin-babel/preset,它会结合.browserslistrc自动决定转译目标。很多新手只关注业务代码,却忽略了这份配置,结果就是构建产物大、兼容范围完全没按需求设计。

4.4 环境变量与多环境构建

工程根目录下可以新建.env.development和.env.production两个文件。文件里定义的变量,只有以VUE_APP_开头的会暴露给前端代码:

VUE_APP_API_BASE_URL=/api VUE_APP_TITLE=后台管理系统

业务代码里通过process.env.VUE_APP_API_BASE_URL读取。执行npm run serve时加载.env.development,执行npm run build时加载.env.production。如果后续要增加测试环境,可以添加.env.test,再用vue-cli-service build --mode test触发对应模式。

这个机制的力量在于:不同环境使用不同接口域名,命令行一句就能切换,不用每次改代码。

5. 新工程从“能跑”到“能开发”的落地清单

5.1 先把路由和状态管理的版本钉死

Vue2 对应的路由库是vue-router@3,状态管理库是vuex@3。很多人图方便直接执行npm install vue-router vuex,结果装成了 Vue3 专用版本,一运行各种报错。创建工程后接着执行:

npm install vue-router@3 vuex@3 --save

package.json里应该是:

"vue-router": "^3.6.5", "vuex": "^3.6.2"

路由写法也要区分。Vue2 里创建路由对象用的是new VueRouter({ mode: 'history', routes }),而不是 Vue3 的createRouter。如果从老项目复制代码,这个差异是最常见的问题来源。

5.2 UI 组件库选择前先看 peerDependencies

Vue2 生态里常见的组件库是 Element UI,而 Element Plus 是给 Vue3 用的,二者千万别装混。实际上不光是组件库,任何第三方库都要先确认它声明的peerDependencies里是不是vue: ^2.x。打开node_modules/库名/package.json,一眼就能看到支持范围。

我在一个项目里见过同事直接安装了 Element Plus,结果导入组件时控制台大量警告,排查了半天才发现是版本错位。建议在安装任何依赖之前,先到 npm 页面看一眼版本下的说明,或者看依赖声明里的 peerDependencies,这种检查习惯能避免大量浪费时间的错误。

5.3 axios 封装与基础请求层

业务开发前,先把网络请求层搭好。axios本身不挑 Vue 版本,但封装逻辑要提前规划。我习惯在src/api目录下统一维护请求实例:

import axios from 'axios' const request = axios.create({ baseURL: process.env.VUE_APP_API_BASE_URL, timeout: 10000 }) request.interceptors.request.use((config) => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) request.interceptors.response.use( (response) => response.data, (error) => { // 统一处理 401、接口异常等 return Promise.reject(error) } ) export default request

这样做的好处是,以后所有页面请求都走同一个实例,统一处理鉴权、错误提示、请求超时,不用在每个组件里重复写。等工程规模变大以后,你会庆幸一开始就留好了这层抽象。

5.4 代码规范与提交前检查

脚手架已经生成了 ESLint 配置,接下来可以补充 Prettier 和提交前检查。安装 lint-staged 和 husky:

npm install -D lint-staged husky

然后在package.json里增加:

"lint-staged": { "*.{js,vue}": ["eslint --fix", "prettier --write"] }

husky 的初始化方式在不同版本里差别很大,建议按你安装的主版本官方说明执行。只要设置了提交前检查,团队成员在提交代码时就会自动做格式修正,避免“本地缩进风格各不相同”的混乱场面。注意不要为了减少干扰而关闭 lint,不然代码规范形同虚设。

6. 我踩过的最多的坑

6.1 Node 版本过高或过低,依赖直接罢工

我在新电脑上第一次跑老 Vue2 工程,遇到的是node-sass编译失败。原因很简单,Node 版本太高,老的原生模块编译不过去。解决办法是优先使用dart-sass替代node-sass。Vue CLI 交互式创建时选 Sass 预处理器,记得选带dart-sass的那一项,不要接受默认的 node-sass。

另一个问题是 Node 版本过低时,@vue/cli-service可能直接提示不支持。遇到这类问题,最省心的方式不是改代码,而是让全团队统一到 Node 18 LTS,在项目 README 里写清楚版本要求。很多所谓“环境问题”根本不是代码问题,而是版本矩阵不一致。

6.2 混用 npm 与 yarn 带来的依赖漂移

团队里如果有人用 npm 更新过依赖、有人坚持用 yarn,package-lock.json和yarn.lock会各自生长,最终 node_modules 结构完全不一致,出现“我本地没问题、你那边装不上”的经典矛盾。我在内部项目里吃过一次亏后,强制整个仓库统一使用 npm,并且只提交package-lock.json,相关人员一律删除node_modules后重新安装。

新的成员加入时,也要求先确认包管理器配置再执行安装命令。工程根目录可以放一个.npmrc,把 registry 固定下来,同时设置engine-strict=true,让包管理器在 Node 版本不符合要求时直接给出明显错误,而不是一路硬装到最后才崩溃。

6.3 代理配置写了却不起作用的几类原因

开发时要接入后端接口,最容易见到的情况是改了devServer.proxy但请求仍然不能转发。常见原因有三类:改了配置没有重启 devServer;target写了完整路径而不是域名根地址;pathRewrite规则写错导致接口路径拼错。

再看一次配置:

devServer: { proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true, pathRewrite: { '^/api': '' } } } }

假设页面请求/api/user,上面配置会把它转发到http://localhost:3000/user,其中pathRewrite的'^/api': ''负责去掉前缀,让后端不需要额外处理/api路径。changeOrigin必须为true,否则部分后端会校验 Host 字段并直接拒绝请求。改完配置后,记得重启 devServer,它是不会自动感知vue.config.js变化的。

6.4 history 路由部署子目录后的空白页

使用 Vue Router 的 history 模式时,如果工程部署在子目录,比如http://example.com/admin/,访问首页大概率是空白页。这是因为静态资源路径没有对齐。处理方法是把vue.config.js里的publicPath设置为'/admin/',同时给路由对象配置base:

new VueRouter({ mode: 'history', base: '/admin/', routes })

如果后端没有把未知路径回退到index.html,刷新二三级页面还会出现 404。这种情况要么让部署服务器配置 fallback 规则,要么在路由方案上改成 hash 模式图省心。我的建议是:开发前先问清楚最终部署方式,再决定路由模式,能少走很多弯路。

6.5 Vue 2.7 带来的组合式 API 兼容问题

如果新工程直接用 Vue 2.7,理论上可以写组合式 API,甚至用<script setup>。实际情况是,很多第三方组件、混入工具和参考代码还停留在 2.6 时代的 API 习惯,直接照搬容易踩兼容问题。我更推荐的团队约定是:使用 Vue 2.7 作为基础版本,但业务代码继续沿用 Options API,新语法只在小范围试验,不当作团队主流写法。

这种选择并不是保守,而是为了降低协作成本。Vue2 工程的最大价值是稳定承接既有业务,而不是充当新语法的试验场。等整个仓库真的有条件迁移到 Vue3 时,再统一升级反而比在 Vue2 里混着两种写法更容易收拾。

最后再补一条很实用的小技巧:在 README 里写清楚四个约定——Node 版本、包管理器、Vue 版本、UI 组件库版本。团队协作时这四行字能解决大量“为什么你那边能跑”的问题。无论你是基于脚手架创建 Vue2 工程来维护老系统,还是想补一下历史技术栈,先把能跑通的闭环打通,再把配置逐个理顺,后续的开发体验就会顺畅得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 7:31:26

从微记录到经验资产:Hindsight个人复盘系统全解析

1. 项目概述&#xff1a;从"后见之明"到"先见之明"的复盘革命"Hindsight"这个词&#xff0c;字面意思是"后见之明"&#xff0c;说白了就是事后聪明。但如果你只把它理解成"马后炮"&#xff0c;那就太可惜了。我最初接触这个…

作者头像 李华
网站建设 2026/10/18 16:00:06

WorkBuddy Linux版安装实测:五种包格式对比与避坑指南

前阵子我把 WorkBuddy 装到 Linux 上跑了一遍&#xff0c;从下载安装包到日常使用&#xff0c;踩了不少坑&#xff0c;也摸出了一些门道。先说结论&#xff1a;这东西在 Linux 上的体验比我想象中成熟&#xff0c;但前提是你要选对安装包格式、知道哪些组件在 Linux 下需要额外…

作者头像 李华
网站建设 2026/10/17 20:58:01

LVGL Spinbox 数值微调控件实战:从示例代码到源码实现全解析

嵌入式物联网智能硬件嵌入式GUI硬件开发 【免费下载链接】HoloCubic 带网络功能的伪全息透明显示桌面站 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ho/HoloCubic 点击查看 免费下载 导读&#xff1a;本文以 HoloCubic 开源仓库中 LVGL 官方示例文档 index.rst 及其…

作者头像 李华
网站建设 2026/10/18 22:14:17

强化学习真机部署:跨越仿真与现实的认知鸿沟

1. 项目概述&#xff1a;当强化学习走出仿真器&#xff0c;机器人第一次“睁眼”时在想什么&#xff1f;“强化学习真机部署&#xff1a;机器人在仿真里&#xff0c;究竟学会了什么&#xff1f;”——这句话不是设问&#xff0c;是我在去年带一个四足机器人项目时&#xff0c;连…

作者头像 李华
网站建设 2026/10/18 2:12:15

高速扫描振镜驱动原理图设计五大硬约束与实测验证

简介&#xff1a;本资源是一份面向电机驱动工程师、光学扫描系统开发者及嵌入式硬件设计学习者的高速扫描振镜驱动电路原理图&#xff0c;聚焦于激光打标、精密投影等场景下的高动态响应控制需求。原理图完整呈现了基于PID闭环的位置伺服架构&#xff0c;涵盖差分指令输入、编码…

作者头像 李华