1. 先别急着敲命令:Vue2项目到底该怎么搭
说实话,2025年再写一篇"vue2项目搭建"的教程,多少有点怀旧的味道。Vue2官方在2023年12月31日就正式EOL了,按道理新项目应该直接上Vue3 + Vite + TypeScript。但现实是,打开大多数公司的仓库看看,Vue2项目不仅没死,反而还活得好好的——后台管理系统、报表平台、老电商商城,一堆业务还跑在这套技术栈上。我自己手头就有三个Vue2项目在维护,其中一个还是从Vue CLI 2时代一路升上来的,前段时间还帮朋友看了他在HBuilderX里起的Vue2实战项目,甚至连一些开源商城系统(比如TPSHOP)的管理后台都是Vue2 + Element UI这套组合。
所以这篇文章不是劝你"快跑,别用Vue2",而是实打实地讲清楚:如果你因为公司技术栈、存量代码、团队熟悉度等原因,必须搭一个Vue2项目,应该怎么搭才不至于一开始就给自己埋一堆坑。包括你怎么选构建工具、装哪些依赖能少走弯路、vue.config.js里哪些配置是必须的,以及我在实际维护中踩过、也帮别人排查过的高频问题。
这篇文章适合谁?如果你是刚接手老项目的新人,或者团队想从零起一个Vue2基础框架,又或者面试前想快速把Vue2项目搭建流程在脑子里过一遍,这篇内容都适用。我自己从Vue CLI 2用到4、又从Vue CLI切过Vite、还在HBuilderX里创建过Vue2项目,几套路数都试过,接下来全摊开讲。
1.1 Vue2 EOL之后,什么场景仍然需要Vue2
先说结论:Vue2 EOL不代表代码就不能跑了。EOL的意思是官方不再提供安全补丁、不修bug、不更新文档,但你线上项目只要不动依赖,原来能跑的就还能跑。真正会出问题的是未来某天你在老项目里新装某个npm包,发现它的新版暗地里依赖了Vue3的API,或者新安装的第三方组件库和Vue2不兼容,这时候EOL的痛才体现出来。
需要继续用Vue2的场景,我总结下来大概有三种。第一种是最普遍的"存量系统维护":公司后台管理系统、企业内部工具,几百个页面跑在Vue2 + Element UI上,重构成本高到团队根本排不出精力去做技术债偿还,只能继续在旧库上加新页面。第二种是"团队技术栈惯性":新起的内部项目,为了跟老代码保持统一、方便人员复用,技术选型依然锁定Vue2。第三种是"依赖绑架":某个核心的第三方库(比如复杂的富文本编辑器、工作流设计器)只支持Vue2,硬上Vue3就得换库甚至重写业务逻辑。
这种时候,Vue2项目搭建就不再是新不新潮的问题,而是怎么搭得稳的问题。搭一个不合理的Vue2项目,后面每次新增页面、升级依赖、排查构建报错都会加倍痛苦。所以下面这几个章节,本质上都在回答同一个问题:怎么把一个Vue2项目从"能跑"变成"好维护"。
1.2 技术选型:Vue CLI还是Vite,怎么选不后悔
2025年搭Vue2项目,首先面临的选择就是:用Vue CLI还是Vite。
Vue CLI是官方在Vue2时代主推的脚手架,基于Webpack,生态成熟、配置透明、资料多,用npm i -g @vue/cli安装后直接vue create就能干活。Vite则原本是Vue3的官方标配,但社区也有脚架方案能支持Vue2,比如vite-plugin-vue2这个插件。两套方案我都实际用过,谈一下我的真实感受。
如果你的项目要长期维护、团队里的人都熟悉Webpack体系,直接选Vue CLI 5.x,别折腾。原因很简单:Vue CLI生成的工程是"能跑通就老死不相往来"的类型,webpack配置虽老但所有教程、所有历史经验都针对它,遇到问题好搜、好找人问。Vite方案最大的优势是开发启动快,我那台机器上用Vite跑Vue2项目,冷启动基本一两秒,Vue CLI开发模式冷启动大概要5到8秒,比较大的项目差距更明显。但Vite + Vue2的插件方案没有官方兜底,博客项目可以玩,公司产品敢不敢上你自己掂量。
另外一个常见入口是HBuilderX,做uniapp的人应该很熟。HBuilderX内置了Vue2模板,菜单里"文件-新建-项目"选"Vue项目"就能起一个Vue2工程,但它默认的工程结构和Vue CLI生成的差别比较大,更适合跟uni-app生态绑定的场景。如果项目不走uniapp,我建议还是回到Vue CLI这条主流路线上来,后面所有内容也以Vue CLI 5.x + Vue 2.7.16为主线讲。
2. 搭建前的环境准备与基础工程生成
我自己见过太多人项目搭到一半发现是环境版本问题,折腾一晚上最后发现是Node装错了。所以环境准备这部分别跳过,照着来能省事。
2.1 Node版本、包管理器这些不得不说的细节
Vue CLI 5.x官方要求Node版本是^12.13.0 || ^14.15.0 || >=16.15.0,也就是说Node 12.13以上、14.15以上、或者16.15以上版本,都能跑。Node 18实测也能跑,Node 20我也试过能跑,但要注意的是,如果项目里用了Node 17以上版本,而webpack版本又是4.x(Vue CLI 4时代的项目),构建时会报Error: error:0308010C:digital envelope routines::unsupported,这是OpenSSL 3和老webpack不兼容导致的,解决办法是设置NODE_OPTIONS=--openssl-legacy-provider,但那是治标不治本,建议这类老项目直接用Node 16。
包管理器方面,npm当然是随Node自带的,但我个人在老项目里更推荐yarn 1.x。Vue CLI 4时代我用yarn比较多,yarn的依赖安装速度快于npm,lock文件的确定性也更好。pnpm我不太建议在Vue2老项目里用,因为pnpm的node_modules是符号链接结构,有些老依赖(尤其是一些不规范的C库、未按标准发布的小包)会因为在node_modules里找不到扁平结构而报错。你可以用,但要做好给某些包打补丁的心理准备。我的建议是:老项目就老老实实npm或者yarn。
Node版本管理工具,macOS/Linux上用nvm,Windows上推荐nvm-windows。核心思路是同一台机器可以随时切换Node版本,避免项目的Node版本不一致导致构建炸掉。
2.2 用Vue CLI创建标准Vue2工程(实操命令)
安装Vue CLI的过程我就不多废话了,一行命令:
npm install -g @vue/cli # 检查版本,注意必须是4.x或5.x vue --version创建项目有两种方式。一种是命令行交互式创建:
vue create my-vue2-project执行后选择Manually select features,然后勾选Babel、Router、Vuex、CSS Pre-processors和Linter。这里注意:项目名不要用中文,不要带大写字母,不然发布到私有npm仓库或者CI流程里容易出幺蛾子。选版本的时候,Vue CLI会问"Choose a version of Vue.js",记得选2.x而不是3.x。
选完后一路回车,脚手架会帮你初始化git仓库并自动生成基础工程。默认生成的目录结构大致是:
my-vue2-project/ ├── node_modules/ ├── public/ │ ├── favicon.ico │ └── index.html ├── src/ │ ├── assets/ │ ├── components/ │ ├── router/ │ ├── store/ │ ├── views/ │ ├── App.vue │ └── main.js ├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── babel.config.js ├── jsconfig.json └── package.json如果你是离线环境或者不想交互式选择,也可以直接用preset命令:
vue create -p vue2 my-vue2-project-p参数直接指定preset,跳过交互。vue2这个preset生成的工程不含Router和Vuex,适合只想要最纯净骨架的场景。我在几个快速原型项目里用过这个方式,一条命令就起来了,非常省事。
2.3 用Vite搭Vue2的备选路线
如果你确实想体验Vite的开发速度,也想保留Vue2的API,那可以走这条路。先用npm创建好一个Vite空项目,然后安装vite-plugin-vue2:
npm create vite@latest my-vue2-vite -- --template vanilla cd my-vue2-vite npm install vite@^4.0.0 vue@^2.7.0 vite-plugin-vue2@^2.0.0然后在vite.config.js里配置插件:
import { defineConfig } from 'vite' import { createVuePlugin } from 'vite-plugin-vue2' export default defineConfig({ plugins: [createVuePlugin()], })入口文件保持和Vue CLI工程一致,main.js里new Vue即可。注意Vue 2.7是Vue2最后的版本,也是兼容Vue3时代部分特性(比如defineComponent、部分组合式API)的过渡版本,但别指望它能完整拥抱Vue3生态。Vite方案我自己的使用感受是:小项目跑得很爽,热更新几乎秒级,但一旦项目开始引入大量老生态里的组件库、复杂依赖,插件体系的坑就会浮出来。所以结论还是那句话:公司项目求稳,用Vue CLI。
3. 目录结构与核心依赖设计
脚手架生成的是空房子,你真正要住进去还需要自己装修。这个章节讲项目内部的结构和依赖怎么排布。
3.1 一个能打的老项目目录应该怎么规划
Vue CLI默认的src目录只有components、router、store、views、assets,这对一个真实的业务项目来说远远不够。我维护过的几个Vue2项目里,最常用的目录规划是这样:
src/ ├── api/ # 接口请求模块,按业务域拆分 │ ├── user.js │ ├── order.js │ └── request.js # axios统一封装 ├── assets/ # 静态资源:图片、字体 ├── components/ # 公共组件 │ ├── common/ # 纯展示型组件 │ └── business/ # 业务粒度的复合组件 ├── constants/ # 常量枚举 ├── directives/ # 自定义指令 ├── filters/ # 全局过滤器 ├── layout/ # 后台管理系统布局框架 ├── mixins/ # 混入 ├── plugins/ # 第三方插件封装 ├── router/ │ ├── index.js │ └── modules/ # 按模块拆分的路由表 ├── store/ │ ├── index.js │ └── modules/ # vuex模块化 ├── styles/ # 全局样式变量、mixin ├── utils/ # 工具函数 ├── views/ # 页面 ├── App.vue └── main.js这个结构不是拍脑袋来的,核心思路是"按业务域内聚"。api按业务模块拆,router按页面域拆,store按领域模型拆,这样每个人负责自己的模块,冲突率低。我最开始搭项目就吃过亏:把所有的接口请求全塞进一个api.js里,三千行一个文件,后来所有人改这个文件都提心吊胆,最后花了一天重构。所以接口请求按模块拆这件事,从一开始就要做。
3.2 核心依赖清单与版本对照
Vue2项目装依赖是最容易踩坑的地方,特别是老依赖和最新的包混用。我给一个我在多个项目里验证过的版本组合,你们可以直接抄作业:
| 依赖 | 推荐版本 | 说明 |
|---|---|---|
| vue | 2.7.16 | 2.x最后一版,安全基本盘 |
| vue-router | 3.6.5 | Vue2配套只有3.x,别装4.x |
| vuex | 3.6.2 | Vue2配套只有3.x,别装4.x |
| axios | 1.x | 网络请求,老项目有些还在用0.x,建议逐步升到1.x |
| element-ui | 2.15.14 | Vue2生态最全的UI库,已停更但稳定 |
| sass | 1.69.x | 需要配合sass-loader 10.x使用 |
| sass-loader | 10.x | Vue CLI 5下sass-loader 10最稳 |
| core-js | 3.x | babel polyfill依赖 |
这里特别强调:vue-router一定不能装4.x,vuex也不能装4.x,这两个4.x系列是给Vue3用的,装了你就会看到控制台里一堆"Vue Router 4 requires Vue 3"之类的报错。这类问题在刚开始搭项目时极易发生,因为npm install vue-router默认装的就是最新版。我接手过一个项目,package.json里写的是vue-router: "^4.0.0",整个项目根本启动不起来,改回3.x后一切正常。
3.3 环境变量与接口代理配置
项目搭好之后第一件事就是配接口环境。Vue CLI工程支持.env文件系列,放在项目根目录:.env.development(开发环境)、.env.production(生产环境)、.env.test(测试环境)。文件里定义的变量要用VUE_APP_前缀,才能通过process.env.VUE_APP_XXX读到。举个例子:
# .env.development VUE_APP_BASE_API=/api VUE_APP_TITLE=本地开发环境然后vue.config.js里配devServer代理,把/api转给后端服务:
module.exports = { devServer: { host: '0.0.0.0', port: 8088, proxy: { '/api': { target: 'http://192.168.1.100:8080', changeOrigin: true, pathRewrite: { '^/api': '' } } } } }这里pathRewrite的作用是把请求路径里的/api去掉后转发,后端接口通常不带/api前缀。我遇到过同事配代理时忘了配changeOrigin,导致请求带的host头不对,部分后端校验了host直接返回403。changeOrigin: true会让代理服务器把请求头里的host改成target的域名,绝大多数场景都需要开。
还有一个高频细节:如果你本地起项目之后发现修改代码、页面卡顿,大概率是开了hot-reload但项目大了webpack吃不消,可以在vue.config.js里调整babel转译范围,或者用cache-loader提速。具体怎么调后面章节细讲。
4. 关键配置项解析与实战
4.1 vue.config.js里值得关注的配置
vue.config.js虽然是可选的,但任何一个用到生产环境的Vue2项目基本都离不开它。我列几个最常用的,每个都带上用途和注意点。
publicPath是最容易被忽略的配置。默认值是/,意味着构建出来的资源URL都是根路径开头,比如/js/app.js。如果项目部署在服务器子目录下,比如https://example.com/admin/,你必须设置成:
module.exports = { publicPath: process.env.NODE_ENV === 'production' ? '/admin/' : '/', }否则打开页面后js和css全部404。这类问题在部署那一步才暴露,排查起来很尴尬,我建议项目搭好当天就把publicPath想清楚。
outputDir是构建输出目录,默认dist,按需改就行。assetsDir是静态资源子目录,默认static。lintOnSave建议开发环境设false,避免每次保存都卡在eslint检查上,如需强制检查在CI里配即可。productionSourceMap默认是true,生产环境建议设false,既能减少构建产物体积,也避免源码暴露。
还有一个花小钱办大事的配置:configureWebpack和chainWebpack。比如你想给项目加一个全局的自定义webpack插件,或者调整分包策略,就在这两个地方写。很多老项目构建后app.js巨大,可以通过splitChunks把公共库单独抽出来:
module.exports = { configureWebpack: config => { config.optimization = { ...config.optimization, splitChunks: { chunks: 'all', cacheGroups: { vendor: { name: 'chunk-vendor', test: /[\\/]node_modules[\\/]/, priority: 10, chunks: 'initial' } } } } } }这个优化一般项目不用急着做,但如果你构建产物出现十几MB的js文件、加载白屏几秒,回头来找这段配置就对了。
4.2 路由配好了吗?——单页面应用的骨架细节
路由模块的搭建是Vue2项目的重头戏。基础的路由配置大家都会,我更想聊几个容易出问题的细节。
第一个是路由懒加载。Vue2项目的路由用懒加载几乎是标配:
const Home = () => import('@/views/Home.vue') const routes = [ { path: '/', name: 'Home', component: Home } ]懒加载的好处是首次加载只下载当前页面需要的js chunk,但要注意懒加载路由和Nginx配置的配合。如果你用history模式(路由地址是/contact而不是/#/contact),Nginx需要配置try_files $uri $uri/ /index.html;,否则用户刷新一个非根路径的页面就是404。我第一次上线history模式的项目时就被这个坑过:线上用户反馈刷新白屏,查了半天发现是Nginx没配try_files。
第二个是路由守卫的时序。Vue2项目里常见的做法是在router.js里注册全局前置守卫,用来处理登录态校验、动态路由、title切换。有一个经验:不要在beforeEach里做会频繁变动的操作,比如在守卫里调用获取用户信息的接口并等待返回,会导致每次路由切换都额外产生一次请求,页面跳变感很明显。我的做法是:登录信息在登录后一次性拉取存到Vuex里,守卫只从Vuex读,不主动发请求。
第三个是路由模块化。我前面提到建议把路由按模块拆到router/modules下,比如user.js、order.js、dashboard.js,再在index.js里用数组展开。这样多人协作时每个人改自己的模块,不会在同一个大路由表上撞车。
4.3 全局样式、scoped样式与深度选择器
Vue2项目的样式体系,我一向建议"三层结构":第一层是全局基础样式,包括reset、CSS变量、通用mixin,放在styles目录;第二层是组件内部的scoped样式,通过<style scoped>包裹;第三层是覆盖第三方组件库的特殊样式,通常写在需要覆盖组件的页面或组件的<style>非scoped部分,或者用深度选择器。
深度选择器这块,是Vue2项目里问得最多、坑也最多的点。先说原理:scoped样式会通过PostCSS给每个元素加上data属性,然后选择器加上[data-v-xxxx]属性选择器来限定作用域。但第三方组件内部元素的data属性和你当前组件不一样,所以你在scoped样式里直接写.el-dialog .el-dialog__body { padding: 0 }对子组件内部元素是不生效的。这时候要用深度选择器来"穿透"。
Vue2里深度选择器有几种写法,很多人会混淆:
/deep/:在Vue2的sass工程里最常见,是sass-loader时代的推荐写法::v-deep:原Vue2官方文档的推荐写法,配合某些版本的dart-sass或postcss处理时也可能出现编译问题>>>:原生CSS的写法,但不能用于sass等预处理器
在Vue CLI 5 + vue2.7 + sass-loader 10的组合下,我用得最稳的是::v-deep .class-name这种写法,注意后面有空格。如果你遇到::v-deep不起作用,先检查两件事:一是你用的sass是dart-sass还是node-sass,dart-sass对::v-deep的支持和编译位置有讲究;二是检查有没有把选择器写成::v-deep> .el-button这种中间不留空格的写法,在某些编译链路上会被解析成子代选择器的一部分,样式直接失效。如果怎么调都不行,最简单的兜底方案:给该组件外层包一个自定义class,然后在该组件的<style>(非scoped)里写样式。虽然全局样式污染的风险增加了,但在覆盖第三方UI库时这是官方都认可的做法。
4.4 babel和浏览器兼容配置
Vue CLI生成的项目自带babel.config.js,内容是presets: ['@vue/cli-plugin-babel/preset'],默认会按.browserslistrc里定义的浏览器范围来做语法转换和polyfill。.browserslistrc默认长这样:
> 1% last 2 versions not dead如果你项目面向政府或国企系统,用户还在用IE11或者老的内核浏览器,你要把配置改成:
> 0.5% last 2 versions not dead IE >= 11改了之后构建时会自动加大转译和polyfill量,项目加载体积会变大,但换来的是老浏览器能跑,这是权衡,没有完美的方案。我见过一个极端案例:客户办公电脑是IE11 + 360兼容模式,一开始没配IE范围,结果登录页白屏,console报了一堆"对象不支持includes"的错,后来加上IE配置、并在main.js里引入core-js的对应polyfill之后才解决。这类问题属于"平时用不上、用上就致命",搭项目时就把浏览器范围定下来是最好的。
5. 常见问题与排查技巧实录
这个章节全部来自我实际排查过的案例,每条都是"网上搜了要试半天"的那种经验。
5.1 ::v-deep不起作用的完整排查思路
关于::v-deep不起作用,除了上一节提到的sass版本和写法问题,我再补充一个更隐蔽的原因:构建时的PostCSS配置。Vue CLI 5内部使用了PostCSS 8,如果你在项目里额外装了postcss.config.js并且引用了postcss-import或者postcss-nested,可能会导致::v-deep在编译顺序上被提前处理掉,生成的选择器没有了深度语义。排查方法是把postcss.config.js临时删掉重新构建,如果样式正常了,说明就是postcss配置的问题。
另一个容易被忽略的点是::v-deep必须紧跟一个选择器作为组合器的右侧。比如::v-deep .el-button没问题,::v-deep .el-dialog__wrapper .el-dialog也没问题,但如果你写成:
::v-deep { .el-button { color: red; } }这种嵌套写法在部分编译器里可以工作,在部分编译器里不行。最稳妥的方式永远是::v-deep直接跟一个类名或用空格分开再写类名,别让它在行首独立成块。
5.2 Node Sass编译报错与版本升级
老项目最经典的一句报错:
Module build failed (from ./node_modules/sass-loader/lib/loader.js): Error: Node Sass does not yet support your current environment: ...原因很简单:node-sass是C++编译的原生模块,你的Node版本变了它就得重新编译,编译失败就报上面这个错。解决办法有两种。一种是直接重新编译,运行npm rebuild node-sass或者删掉node_modules重新install。另一种是彻底切换到sass(dart-sass),这是我更推荐的长期路线,因为node-sass已经停止维护,而sass包是纯JS实现加原生编译器的双模式方案,兼容性更好。
切换时要注意sass-loader的版本匹配:Vue CLI 5 + sass-loader 10是大家用得最多的组合,sass 1.69之前的版本和sass-loader 10直接配合就OK。如果你升了sass-loader 13,sass版本建议是1.70+,部分情况下还需要配置额外的编译参数。我自己的项目现在锁的是sass: "1.69.x"+sass-loader: "10.x",这是我在多个Vue2项目里验证过的稳定组合。还有一个小坑:dart-sass对四则运算的除法,原来node-sass里写padding: $width / 2是可以的,dart-sass里除法被改成了math.div($width, 2),这会让你在老scss文件里看到成片的编译错误。遇到这个别慌,批量改除法写法即可。
5.3 前端能不能拿MAC地址?答一次就记住
热搜词里有一条是"vue2前端怎么获取当前机器的mac地址",这个问题我在不少群里被问过。直接回答:纯浏览器前端拿不到MAC地址,不管你是Vue2还是Vue3,都不可能。MAC地址是网卡层的物理地址,浏览器运行在应用层,出于隐私和安全考虑,浏览器根本不提供访问网卡信息的API。
以前只有一种例外:IE浏览器里用ActiveX控件(比如WMI Scripting)可以拿到MAC地址,但那依赖本机启用ActiveX和特定的安全设置,而且现在还有多少系统坚持用IE?基本没有了。所以如果你真的需要识别客户端机器,我给你三条现实可行的路线:
- 服务器端获取:如果项目部署在企业内网,可以让后端在网关或者接入层读取ARP表,用客户端的IP去关联MAC地址,这是内网管理系统的通行做法。
- 浏览器指纹替代:前端用canvas指纹、WebRTC的icecandidate里的IP、UA组合,生成一个设备唯一ID存到localStorage,用来做设备标识。这不等同于MAC地址,但能解决大部分"识别同一设备"的需求。
- 如果业务场景需要一个稳妥的硬件标识,走客户端方案,比如Electron的Node层可以用系统API拿MAC地址,然后通过接口上报给前端展示。
再补充一句,热搜里出现这个关键词,大概率是有人在做设备绑定、试用授权之类的需求。这种需求我最推荐的方式是"后端下发deviceId + 前端上报指纹"的方案,既不用强依赖硬件信息,又能在跨浏览器场景下保持相对稳定的设备标识。
5.4 其他高频问题速查表
我把自己这些年在Vue2项目搭建和维护里遇到的高频问题整理成一张速查表,遇到直接查:
| 问题现象 | 常见原因 | 快速处理 |
|---|---|---|
| 启动后端口占用 | devServer默认8080 | 在vue.config.js的devServer.port改端口,或用vue-cli-service serve --port 8090覆盖 |
| 构建报"openssl legacy provider" | Node 17+搭配webpack4 | 用Node 16,或设置NODE_OPTIONS=--openssl-legacy-provider |
| 路由模式刷新404 | history模式缺少Nginx回退 | Nginx配置try_files $uri $uri/ /index.html |
| Element图标不显示 | 字体文件未正确加载 | 检查构建产物中字体文件是否丢失,配置url-loader的limit |
| scoped样式无法覆盖子组件 | 属性选择器不命中内部元素 | 用::v-deep或非scoped样式覆盖 |
| 报"exports is not defined" | babel配置/浏览器兼容问题 | 检查browserslist范围,必要时引入core-js polyfill |
| 热更新失效 | webpack cache或文件监听问题 | 删除node_modules/.cache,重启dev server |
| 首次打开页面白屏、刷新也白屏 | publicPath错误 | 检查publicPath是否匹配部署子路径 |
| Sass除法报错 | dart-sass语法变化 | 写成math.div()或提前换算成具体数值 |
这些问题的共同特点:都不是业务代码错,而是工程性问题,排查起来最耗费时间。所以我一直觉得,搭Vue2项目时把工程配置一次弄对,远比后面一遍遍救火重要。
5.5 老项目常见依赖的坑
最后再分享几个"不是必须但见到就躲不开"的依赖坑。第一个是vue-office,这个库用来在线预览docx、xlsx、pdf,在Vue2生态里比较有名,一些管理系统需要预览附件时经常被引入。它兼容Vue2,安装时注意版本要选兼容2.x的那条线,用npm i @vue-office/docx之类的方式装就行,装完顺手看一眼lock文件确认vue没有被连带升级。第二个是jsPDF、xlsx这类老牌的表格导出/PDF生成库,它们在Vue2项目里运行没问题,但如果你同时升级到Node 18+,部分版本可能触发旧的依赖链警告,建议锁版本。第三个是vue-pdf、vue-cropper这些单页面组件库,在Vue2项目里都很流行,但它们都比较久没更新了,如果遇到新浏览器兼容问题,优先去GitHub看看是否有fork的维护版本,别硬扛。
依赖管理这块,我个人的习惯是:项目搭好后立刻把package.json里的依赖全部锁到精确版本(不带^和~),然后提交一次lock文件。这样能最大程度避免团队成员在不同时间install拿到不同版本的依赖,减少"我这边好的,你那边就报错"的扯皮。
6. 用Vue2搭项目,最后再说几句经验
写到这里,其实把Vue2项目搭建的完整闭环都已经讲完了:环境、脚手架、目录、依赖、配置、常见问题。最后分享几条我自己在这些年维护Vue2项目里沉淀下来的体会吧。
第一条体会是:不要在搭项目的时候追求一次性搞定所有高级功能。我见过有人搭个Vue2项目,上来就配了十几个webpack插件、搞了一堆动态主题、加了一堆抽象封装,最后项目跑起来了,但团队成员进来一看,谁都不敢改。正确做法是先把基础骨架搭稳,路由、状态、请求、样式这一层控制好,高级功能等真正需要的时候再慢慢加。
第二条体会是:一定要保留一份"可重建"的工程说明。Vue2项目最大的风险不是代码,而是"人走了,环境没了"。我在一个公司接手的项目,因为老同事离职,node_modules被清过、Node版本也换过,结果部署流程彻底断了,最后花了两个晚上看构建日志才恢复。如果你现在正在搭Vue2项目,顺手在README里写清楚Node版本、包管理器、构建命令、部署时Nginx需要的配置,这对后面接手的人来说是最大的善意。
第三条体会是:该升级的还是要升级。Vue2 EOL虽然让人伤感,但不代表你要把所有依赖永远钉在旧版本上。我去年把两个老项目的Vue小版本从2.6升到2.7,element-ui也升到了2.15.14,构建链路的报错明显减少,开发体验也好了一截。不用害怕升级,只要你有lock文件、有完整的回归用例,升一个patch版本的风险其实很低。
最后我再给坚持读到这的人一个建议:如果你是用Vue2做新项目,并且公司未来两三年真的没有升级Vue3的打算,那至少要把工程做得"可迁移"——比如组件尽量用Options API的清晰写法、不要在页面里直接依赖this.$route泛滥、页面逻辑和请求逻辑分离。这样就算某天团队决定上Vue3,你的代码迁移成本也会低很多。
Vue2项目搭建,说起来就是一堆命令和配置,但真正能让它稳定跑下去的,永远是那些"提前想到的坑"和"动手试过的路"。我也还在每天打开终端,对着那些跑了好几年的Vue2服务敲下npm run serve,然后默默期待今天不要有新的依赖报错。希望这篇内容能让你少走一点弯路,也希望你搭出来的Vue2项目,能安安静静地服务好业务,直到它完成使命的那一天。