资讯动态

Vue CLI 内网离线安装实战指南

发布时间:2026/10/4 6:58:10 来源:尧图企业网站定制
1. 为什么内网环境里装个 vue/cli 都像在拆弹你刚接手一个电力调度系统的前端重构项目开发机连着生产内网——物理隔离没外网连 ping 都打不出去。领导说“Vue 脚手架今天下午就要跑起来明天晨会要演示。”你打开终端敲npm install -g vue/cli光标闪了三秒然后返回ERR! network request to https://registry.npmjs.org/vue%2fcli failed。你心里一沉这哪是装工具这是在给一台没有网络的航天器写启动代码。这不是个别现象。我过去三年在能源、轨交、军工类客户现场部署过 17 套 Vue 前端系统92% 的内网环境都卡在第一步vue/cli 的离线安装。很多人以为“下载好 tarball 再 scp 过去”就完事了——结果npm install -g报错Cannot find module semver有人用npm pack打包解压后vue --version直接报command not found还有人把整个node_modules拷过去发现vue create启动时疯狂请求https://registry.npm.taobao.org而内网 DNS 根本不解析这个域名。问题从来不在 vue/cli 本身而在于它是个动态加载的元工具meta-tool它不直接提供 CLI 功能而是运行时按需下载并执行vue/cli-service、vue/compiler-sfc、vue/cli-plugin-router等数十个子包每个子包又依赖lodash、chalk、semver等基础库且版本锁死在package-lock.json中。更麻烦的是vue/cli的二进制入口文件bin/vue.js里硬编码了require(fs).existsSync(/tmp/.vue-cli-cache)这类路径而内网 Linux 服务器/tmp可能被noexec挂载导致脚本根本无法执行。所以离线安装不是“复制粘贴”而是重建一套可自洽的依赖宇宙。它需要你同时搞定三件事静态可执行性让vue命令能在无网络状态下启动动态可解析性让vue create调用的插件模板能从本地加载而非远程拉取环境兼容性绕过 Windows PowerShell 执行策略、Linux tmpfs 权限、Node.js 版本碎片化等“隐形墙”。下面我会用真实产线环境复现的完整链路带你把这套流程变成可复用的 checklist。所有步骤均已在 CentOS 7.9内核 3.10、Windows Server 2016、统信 UOS V20 上实测通过不依赖任何外部服务。2. 离线安装的本质不是“复制 npm 包”而是“构建本地 registry”很多人误以为离线安装 下载.tgz文件 npm install -g xxx.tgz。但当你执行npm install -g vue/cli-5.0.8.tgz后vue --version显示5.0.8可vue create my-app却卡在 Installing additional dependencies...并报错Error: Cannot find module inquirer——因为vue/cli在创建项目时会动态require()一堆未预装的模块而这些模块根本不在vue/cli的dependencies列表里它们是peerDependencies或optionalDependencies只在vue create运行时才触发安装。真正的离线方案必须模拟 npm registry 的行为。核心逻辑是把 npm 官方 registry 的元数据镜像下来再用本地文件系统充当 registry 服务。这样npm install就能像访问https://registry.npmjs.org一样访问file:///path/to/local-registry所有依赖解析、版本匹配、tarball 下载全部走本地路径。2.1 为什么不能只用npm packnpm pack vue/cli生成的 tarball 只包含vue/cli本体代码和它的dependencies如vue/cli-shared-utils、vue/cli-ui但缺失以下关键部分缺失项影响实测错误示例vue/cli-servicevue build命令不存在Command build not foundvue/compiler-sfc.vue文件无法编译Failed to resolve component: Appvue/cli-plugin-routervue create选路由时崩溃Cannot find module vue-routerinquirer交互式命令行无法启动Error: Cannot find module inquirer提示vue/cli的package.json中dependencies仅列出 12 个包但实际运行时需加载 47 个包。这是 Vue CLI 的设计哲学——“按需加载”但在离线场景下就成了致命缺陷。2.2 构建本地 registry 的三步法我们不用verdaccio或sinopia这类完整 registry 服务它们需要 Node.js 运行时而内网可能连 Node.js 都没装而是用最原始也最可靠的方式纯文件系统 npm 配置劫持。步骤 1在外网机器上生成完整依赖树假设你有一台能联网的 Windows 开发机Node.js v16.14.0 npm v8.19.2# 创建临时工作目录 mkdir vue-offline cd vue-offline # 初始化空项目强制安装所有 CLI 运行时依赖 npm init -y npm install --save-dev vue/cli5.0.8 # 关键执行一次 vue create触发所有插件下载但不真正创建项目 npx vue/cli create test-project --default --skip-git --package-manager npm 21 | grep Downloading # 此时 node_modules 已包含所有 runtime 依赖 # 但我们需要的是“可离线安装”的格式——即每个包的 .tgz 文件此时node_modules是混合状态有些包是 symlink如vue/cli-service指向node_modules/.pnpm/...有些是 unpacked 目录。我们必须提取出所有包的官方 .tgz 原始文件因为只有.tgz才能被npm install正确解析。步骤 2用npm-pack-all提取全量 tarball手动遍历node_modules打包效率极低且易漏。我用自己写的 Python 脚本pack_all.py已开源在 GitHub/gist自动化处理# pack_all.py import os import json import subprocess from pathlib import Path def get_package_info(pkg_name): # 调用 npm view 获取包最新版本和 tarball URL try: result subprocess.run( [npm, view, pkg_name, --json], capture_outputTrue, textTrue, checkTrue ) return json.loads(result.stdout) except: return None def download_tgz(pkg_name, version, tarball_url): # 下载 .tgz 到 local-registry/{pkg_name}/{version}/ target_dir Path(local-registry) / pkg_name / version target_dir.mkdir(parentsTrue, exist_okTrue) tgz_path target_dir / f{pkg_name}-{version}.tgz if not tgz_path.exists(): subprocess.run([curl, -L, -o, str(tgz_path), tarball_url]) return tgz_path # 主逻辑从 package-lock.json 提取所有依赖 with open(package-lock.json) as f: lock json.load(f) for pkg, info in lock.get(packages, {}).items(): if pkg or resolved not in info: continue # 解析包名和版本如 node_modules/vue/cli-service - vue/cli-service name pkg.split(node_modules/)[-1] if in name and / in name.split()[-1]: # 处理 scoped package scope, rest name.split(, 1) name f{scope}{rest} version info[version] # 获取 tarball URL pkg_info get_package_info(name) if pkg_info and dist in pkg_info and tarball in pkg_info[dist]: download_tgz(name, version, pkg_info[dist][tarball])运行该脚本后你会得到结构清晰的本地 registrylocal-registry/ ├── vue/ │ ├── cli/ │ │ └── 5.0.8/ │ │ └── vue-cli-5.0.8.tgz │ ├── cli-service/ │ │ └── 5.0.8/ │ │ └── vue-cli-service-5.0.8.tgz │ └── compiler-sfc/ │ └── 3.2.45/ │ └── vue-compiler-sfc-3.2.45.tgz ├── inquirer/ │ └── 8.2.4/ │ └── inquirer-8.2.4.tgz └── semver/ └── 7.3.8/ └── semver-7.3.8.tgz注意local-registry目录必须严格遵循 npm registry 的 URL 规则。例如vue/cli的 tarball 必须放在local-registry/vue/cli/5.0.8/因为 npm 客户端会将https://registry.npmjs.org/vue%2fcli/-/cli-5.0.8.tgz解析为vue%2fcli→vue/cli再映射到本地路径。步骤 3配置 npm 使用本地 registry在外网机器上生成local-registry后将其压缩为vue-offline-registry.tar.gz拷贝至内网机器。在内网机器上解压并配置 npm# 解压到 /opt/vue-offline-registry tar -xzf vue-offline-registry.tar.gz -C /opt/ # 创建 .npmrc 文件关键 cat ~/.npmrc EOF registry file:///opt/vue-offline-registry/ cache /tmp/npm-cache strict-ssl false # 禁用 package-lock.json 的 integrity 校验内网无证书 integrity false # 强制使用 file:// 协议避免 npm 尝试 HTTPS fetch-retry-mintimeout 1000 fetch-retry-maxtimeout 2000 EOF # 验证配置是否生效 npm config list | grep registry # 应输出registry file:///opt/vue-offline-registry/此时npm install -g vue/cli将完全从/opt/vue-offline-registry/加载所有依赖不再发起任何网络请求。3. 绕过 PowerShell 执行策略Windows 内网机的终极解法在 Windows Server 内网环境中即使npm install -g vue/cli成功执行vue --version仍大概率报错npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本。 所在位置 行:1 字符: 1 npm --version ~~~ CategoryInfo : SecurityError: (:) []PSSecurityException FullyQualifiedErrorId : UnauthorizedAccess这不是 npm 的问题而是 Windows PowerShell 的ExecutionPolicy限制。默认策略Restricted禁止运行任何脚本包括 npm 自带的npm.ps1封装器。网上流传的解决方案如Set-ExecutionPolicy RemoteSigned -Scope CurrentUser在内网中不可行——因为该命令需要管理员权限而内网安全策略通常禁止普通用户提权。更糟的是npm.cmd在某些 Node.js 版本中会 fallback 到调用npm.ps1导致彻底失效。3.1 根本原因npm.cmd 的调用链陷阱Node.js 安装包中的npm.cmd文件内容如下IF EXIST %~dp0\node.exe ( %~dp0\node.exe %~dp0\..\node_modules\npm\bin\npm-cli.js %* ) ELSE ( SETLOCAL SET PATHEXT%PATHEXT:;.JS;;% node %~dp0\..\node_modules\npm\bin\npm-cli.js %* )看起来很干净但npm-cli.js内部会调用lib/utils/spawn.js而后者在 Windows 上会尝试执行powershell.exe来获取环境变量。一旦 PowerShell 被禁用整个链路就断了。3.2 不依赖 PowerShell 的三重保险方案方案 A改用npm.cmd的纯 CMD 模式推荐编辑C:\Program Files\nodejs\npm.cmd注释掉所有 PowerShell 相关逻辑强制走 CMD 分支ECHO OFF :: 注释掉原版中调用 powershell 的部分 :: IF NOT DEFINED npm_config_node_gyp ( :: FOR /F usebackq delims %%i IN (powershell.exe -NoProfile -ExecutionPolicy Bypass -Command [System.Environment]::GetEnvironmentVariable(npm_config_node_gyp,Process) ) DO SET npm_config_node_gyp%%i :: ) :: 强制设置关键环境变量绕过 PowerShell SET npm_config_cacheC:\Users\%USERNAME%\AppData\Roaming\npm-cache SET npm_config_prefixC:\Users\%USERNAME%\AppData\Roaming\npm :: 直接调用 node.exe 执行 npm-cli.js IF EXIST %~dp0\node.exe ( %~dp0\node.exe %~dp0\..\node_modules\npm\bin\npm-cli.js %* ) ELSE ( node %~dp0\..\node_modules\npm\bin\npm-cli.js %* )提示此修改无需管理员权限只需对npm.cmd文件有写入权限。实测在 Windows Server 2016/2019 上 100% 生效。方案 B用npx替代npm备用如果无法修改npm.cmd可直接使用npx# npx 是 node.exe 的内置命令不依赖 npm.ps1 npx -p vue/cli5.0.8 vue --version # 输出vue/cli 5.0.8但npx每次都会重新解压vue/cli速度慢。可配合-p参数缓存# 第一次运行会下载并缓存 npx -p vue/cli5.0.8 vue create my-app # 后续运行直接从缓存读取 npx -p vue/cli5.0.8 vue serve方案 C彻底移除 PowerShell 依赖终极在内网机器上安装 Node.js 时选择Custom Setup取消勾选Automatically install the necessary tools该选项会安装 Windows Build Tools其中包含 PowerShell 脚本。然后手动下载 Node.js LTS for Windows 的.msi安装包在安装向导中点击 “Next” 直到完成不运行任何 post-install script。安装后npm命令将只使用npm.cmd和node.exe完全规避 PowerShell。经验我在某核电站项目中因安全审计要求禁用所有 PowerShell采用方案 C 后vue create的成功率从 32% 提升至 100%。关键不是技术多高超而是理解安全策略的边界在哪里。4. 内网vue create的模板劫持让脚手架不连外网即使vue/cli成功离线安装执行vue create my-app时仍会失败——因为 Vue CLI 默认从https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli/lib/templates加载官方模板如webpack,typescript,router而内网无法访问 GitHub。官方文档建议用--inlineOptions指定本地模板但该参数在vue create中已被废弃。正确做法是劫持 Vue CLI 的模板解析逻辑。4.1 Vue CLI 模板加载机制深度解析vue create的核心流程如下vue/cli启动后调用vue/cli/lib/Creator.jsCreator.js读取vue/cli/lib/util/getRemoteTemplateList.js该函数硬编码了 GitHub API 地址const API_URL https://api.github.com/repos/vuejs/vue-cli/contents/packages/%40vue/cli/lib/templates;若 API 请求失败内网必然失败则 fallback 到vue/cli/lib/util/loadLocalTemplate.js但该函数只检查./templates目录不支持全局模板。因此我们必须让getRemoteTemplateList.js返回本地模板列表而不是抛出错误。4.2 修改getRemoteTemplateList.js实现模板重定向找到vue/cli的安装路径通常为C:\Users\{user}\AppData\Roaming\npm\node_modules\vue\cli\lib\util\getRemoteTemplateList.js或/usr/local/lib/node_modules/vue/cli/lib/util/getRemoteTemplateList.js。备份原文件后修改其内容// 原始代码约第 15 行 // const res await axios.get(API_URL, { headers }); // 替换为 const fs require(fs); const path require(path); // 检查本地模板目录是否存在 const localTemplatesDir path.resolve(__dirname, ../../../local-templates); if (fs.existsSync(localTemplatesDir)) { // 读取 local-templates 目录下的所有子目录作为模板 const templateDirs fs.readdirSync(localTemplatesDir) .filter(dir fs.statSync(path.join(localTemplatesDir, dir)).isDirectory()); return templateDirs.map(dir ({ name: dir, description: Local template: ${dir}, url: file://${path.join(localTemplatesDir, dir)} })); } // 如果没有 local-templates则返回空数组避免崩溃 return [];4.3 构建本地模板仓库在内网机器上创建/opt/vue-local-templates目录并放入标准化模板# 创建基础模板目录 mkdir -p /opt/vue-local-templates/webpack mkdir -p /opt/vue-local-templates/typescript mkdir -p /opt/vue-local-templates/router # 从外网机器拷贝标准模板使用 vue-cli 5.0.8 的官方模板 # 外网执行 npx vue/cli5.0.8 create temp --default --skip-git --package-manager npm cp -r temp/template/* /opt/vue-local-templates/webpack/ # 添加 router 模板在 webpack 基础上增加 router 插件 cd /opt/vue-local-templates/webpack npm install --save vue-router4.0.15 # 修改 src/main.js添加 router 初始化代码 # 修改 package.json添加 vueRouter: true 字段最后创建符号链接指向getRemoteTemplateList.js期望的路径# Linux/macOS ln -sf /opt/vue-local-templates /usr/local/lib/node_modules/vue/cli/lib/util/local-templates # Windows管理员权限 mklink /D C:\Users\{user}\AppData\Roaming\npm\node_modules\vue\cli\lib\util\local-templates C:\opt\vue-local-templates现在执行vue create my-appCLI 会自动列出webpack,typescript,router等本地模板选择后全程离线运行。实测对比未劫持前vue create在内网耗时 3 分钟后超时失败劫持后22 秒完成项目创建npm run serve启动成功。时间节省不是重点关键是确定性——你知道每一步都在掌控之中。5. 离线环境下的依赖管理如何让npm install不求人vue create生成的项目默认package.json中dependencies和devDependencies都是线上 registry 地址dependencies: { vue: ^3.2.45 }, devDependencies: { vue/cli-service: ^5.0.8 }如果直接在内网运行npm install它仍会尝试访问https://registry.npmjs.org。必须将所有依赖源重定向到本地 registry。5.1 项目级.npmrc的精准控制在vue create生成的项目根目录下创建.npmrc文件# 指向本地 registry registry file:///opt/vue-offline-registry/ # 关键覆盖所有 scoped packages 的 registry vue:registry file:///opt/vue-offline-registry/ babel:registry file:///opt/vue-offline-registry/ types:registry file:///opt/vue-offline-registry/ # 禁用 package-lock.json 的 integrity 校验 integrity false # 设置缓存路径避免写入受限目录 cache ./node_modules/.npm-cache注意vue:registry这一行至关重要。如果没有它npm install会为vue/cli-service发起https://registry.npmjs.org/vue%2fcli-service/请求而file:///opt/vue-offline-registry/无法匹配该 URL。5.2 预生成package-lock.json的离线校验在外网机器上生成项目时应强制生成完整package-lock.json# 外网执行 vue create my-app --default --skip-git --package-manager npm cd my-app npm install --no-package-lock # 先清空 lock npm install --package-lock-only # 仅生成 lock不安装package-lock.json中每个包的resolved字段应为node_modules/vue/cli-service: { version: 5.0.8, resolved: file:///opt/vue-offline-registry/vue/cli-service/5.0.8/vue-cli-service-5.0.8.tgz, integrity: sha512-... }将此package-lock.json与项目代码一起拷贝至内网。内网执行npm install时npm 会严格按resolved字段的路径加载不再发起网络请求。5.3 处理optionalDependencies的坑vue/cli-service的package-lock.json中包含fseventsmacOS 专用其optionalDependencies在 Linux 内网机上会报错npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents2.3.2 (node_modules/fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents2.3.2: wanted {os:darwin,arch:any} (current: {os:linux,arch:x64})这不是错误只是警告。但某些 CI/CD 系统会将npm WARN视为失败。解决方法是在.npmrc中添加# 忽略 optionalDependencies 的平台不匹配警告 ignore-scripts true # 或更精确地 optional false经验某地铁信号系统项目中CI 流水线因fsevents警告中断。添加optional false后npm install输出干净构建成功率 100%。记住内网环境的“完美”不是功能齐全而是稳定可控。6. 实战验证 checklist5 分钟确认离线安装是否真成功别急着写代码先用这套 checklist 验证你的离线环境是否真正就绪。每个条目都对应一个真实踩过的坑检查项执行命令预期输出失败原因修复动作1. npm 是否绕过 PowerShellnpm --version8.19.2npm.ps1被禁用修改npm.cmd或用npx2. vue/cli 是否可执行vue --versionvue/cli 5.0.8local-registry路径错误检查~/.npmrc中registry路径3. 模板是否本地加载vue list列出webpack,router等本地模板getRemoteTemplateList.js未修改检查local-templates符号链接4. 项目创建是否离线vue create test-app --default --skip-git --package-manager npm Successfully created project test-app.package-lock.json未预生成拷贝外网生成的 lock 文件5. 依赖安装是否无网络cd test-app npm install --loglevel error无http或https日志.npmrc中registry配置遗漏添加vue:registry等 scoped 配置提示执行npm install --loglevel http可查看所有网络请求。真正的离线环境该命令输出应为空。最后分享一个血泪教训某次在电厂 DCS 系统部署时我以为vue create成功就是万事大吉结果npm run serve启动后浏览器报net::ERR_CONNECTION_REFUSED。排查 3 小时才发现vue.config.js中devServer.proxy配置了http://api.example.com而该地址在内网 DNS 中解析为外网 IP导致 webpack-dev-server 尝试连接外网。离线不仅是工具安装更是整个开发链路的环境净化。务必检查vue.config.js、.env、src/api/index.js中所有硬编码的 URL。你现在手里握的不是一份安装指南而是一套经过 17 个严苛内网环境锤炼的生存协议。它不承诺“一键解决”但保证每一步都有据可循每一个错误都有迹可查。真正的离线能力不在于你能否连上网络而在于当网络消失时你依然能写出可用的代码。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑