
1. 为什么会有在Mac上装低版本Chrome这种需求1.1 高版本Chrome带来的真实困扰先说一个我亲身踩过的坑。去年接手一个老项目前端用的是一套基于NPAPI接口的打印控件客户那边一直跑在Chrome 87上。我本地图省事装了最新版Chrome结果打开页面直接白屏控制台报了一堆Uncaught TypeError排查了半天才发现是新版Chrome彻底移除了某些老接口。这种时候你没法让客户升级控件只能自己把浏览器降下来。类似场景其实挺多的。做自动化测试的朋友经常需要锁定特定大版本因为某些WebDriver和Chrome的版本是强绑定的做前端兼容性验证的要同时开好几个不同版本的Chrome对比渲染效果还有一些企业内部系统证书链或者加密算法只兼容到某个版本之前。这些需求归结起来就一句话我需要一个指定版本的Chrome而且要和系统里现有的Chrome共存不能互相干扰。Mac上这件事比Windows麻烦一点。Windows下你下载一个离线安装包装到不同目录就行。Mac的Chrome默认装在/Applications/Google Chrome.app直接覆盖安装会把原来的版本干掉而且macOS的Gatekeeper对来源不明的应用有额外限制。所以核心思路就变成了把指定版本的Chrome装到一个独立目录用命令行直接启动它完全绕开系统默认的Chrome。1.2 本文适合哪些人看这篇内容主要面向三类人。第一类是开发和测试工程师需要多版本Chrome共存做兼容性验证第二类是运维或者技术支持要帮别人复现某个特定版本下的问题第三类就是单纯好奇、想折腾一下的Mac用户。不管你之前有没有用过命令行只要跟着步骤走都能搞定。我会把每一步为什么这么做讲清楚避免你照抄命令却不知道出了问题时该往哪查。需要提前说明的是本文所有操作都基于官方公开的Chrome历史版本分发渠道不涉及任何非官方修改版。整个方案的核心工具就是**终端Terminal**加几个基础命令不需要装额外的包管理器也不需要动系统配置。2. 整体方案设计与思路拆解2.1 为什么不用Homebrew装Chrome很多人第一反应是brew install google-chrome但这条路走不通。Homebrew的cask里只有最新稳定版的Chrome它不提供历史版本。你就算指定版本号cask也会告诉你找不到对应的artifact。而且Homebrew装完的Chrome还是在/Applications下和手动装的没区别一样会覆盖旧版本。那有人会想我能不能用brew install --cask google-chrome87这种写法不行Chrome从来没有官方维护过带版本号的cask formula。所以Homebrew这条路直接放弃别在这上面浪费时间。我见过有教程教你改cask文件里的版本号和下载地址理论上可行但每次Chrome更新你都得手动改维护成本太高不推荐。2.2 核心思路独立目录加命令行启动真正靠谱的方案分三步走。第一步从官方渠道拿到指定版本的离线安装包注意是.dmg格式的第二步把dmg挂载后把里面的Google Chrome.app整个拷贝到一个自定义目录比如~/ChromeVersions/87.0.4280.88/第三步用命令行直接调用这个目录下的可执行文件启动浏览器。这个方案的好处非常明显。首先它完全不碰系统默认的Chrome你原来的浏览器该咋用还咋用。其次每个版本放在独立目录里想装多少个版本都行互不干扰。最后命令行启动可以带各种参数比如指定用户数据目录、禁用自动更新、开启远程调试端口这些在兼容性测试里非常有用。注意拷贝出来的Chrome.app首次启动时macOS可能会提示无法验证开发者。这是因为应用被移动过签名校验路径变了。解决办法是在系统设置-隐私与安全性里点仍要打开或者用命令行加--no-sandbox参数临时绕过。但后者有安全风险仅建议在测试环境使用。2.3 版本号怎么选才不踩坑Chrome的版本号格式是主版本.次版本.构建号.补丁号比如87.0.4280.88。你选版本的时候重点看主版本号就行因为大的接口变动基本都发生在主版本升级时。比如Chrome 88移除了Flash支持Chrome 92改了SameSite Cookie的默认行为Chrome 109是最后一个支持Windows 7的版本。如果你不确定该选哪个先去查一下你要用的功能是在哪个版本被废弃的然后选它前一个主版本。另外要注意Chrome的离线安装包分稳定版、测试版、开发版和金丝雀版。做兼容性测试一般用稳定版就够了。下载地址的规律是https://www.slimjet.com/chrome/google-chrome-old-version.php这类第三方归档站但更稳妥的方式是直接查Chrome的版本历史页面找到对应版本的官方下载链接。不过官方链接有时候会失效所以建议下载后把dmg文件备份一份免得以后找不到。3. 核心细节解析与实操要点3.1 下载指定版本dmg的完整流程打开浏览器访问Chrome的版本历史查询页面。这个页面会列出所有历史版本号你找到自己想要的那个版本点进去会看到各个平台的下载链接。Mac版要选macOS对应的x64或arm64架构M1及以后的芯片选arm64Intel芯片选x64。选错了也能跑但会通过Rosetta转译性能差一截。下载下来的文件名一般长这样googlechrome87.0.4280.88.dmg。文件大小在150MB到200MB之间如果明显偏小说明下载不完整重新下。下载完成后双击挂载你会看到一个虚拟磁盘里面就一个Google Chrome.app。先别急着拖到Applications里那样就覆盖了。提示如果你找不到官方历史版本页面可以搜Chrome旧版本下载找一些归档站点。但一定要核对文件的SHA256校验值确保没被篡改。校验命令是shasum -a 256 文件名.dmg和官方公布的对比。3.2 创建独立目录并拷贝应用打开终端先建一个专门放多版本Chrome的目录。我习惯放在用户目录下这样不需要sudo权限mkdir -p ~/ChromeVersions/87.0.4280.88然后把挂载的dmg里的应用拷进去。假设dmg挂载在/Volumes/Google Chrome执行cp -R /Volumes/Google Chrome/Google Chrome.app ~/ChromeVersions/87.0.4280.88/拷贝完成后先卸载dmghdiutil detach /Volumes/Google Chrome这时候你去~/ChromeVersions/87.0.4280.88/下面看应该有一个完整的Google Chrome.app。右键点它选显示包内容进入Contents/MacOS/里面有个叫Google Chrome的可执行文件这就是我们后面要调用的。3.3 命令行启动与常用参数启动这个独立版本的Chrome直接用完整路径调用可执行文件~/ChromeVersions/87.0.4280.88/Google\ Chrome.app/Contents/MacOS/Google\ Chrome注意路径里有空格所以要用反斜杠转义或者给整个路径加引号。第一次启动可能会弹安全提示去系统设置里放行就行。实际使用中我强烈建议加上--user-data-dir参数给每个版本指定独立的用户数据目录。这样不同版本的Chrome不会共用书签、Cookie和扩展避免互相污染~/ChromeVersions/87.0.4280.88/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \ --user-data-dir$HOME/ChromeVersions/87.0.4280.88/userdata如果你要做自动化测试再加一个--remote-debugging-port9222这样就能用CDP协议操控这个浏览器了。注意端口别和系统里其他服务冲突9222是常用端口如果被占了就换9223。3.4 防止Chrome自动更新低版本Chrome最怕的就是它自己偷偷更新到最新版。虽然我们拷贝出来的副本不会自动更新但保险起见还是把更新相关的组件禁掉。进入Google Chrome.app/Contents/Frameworks/找到GoogleSoftwareUpdate.framework或者类似的更新框架把它重命名或者删掉。更简单的办法是在启动参数里加--disable-background-networking这个参数会阻止Chrome在后台做各种网络请求包括更新检查。另外如果你发现启动后版本号还是变了检查一下是不是系统里默认的Chrome在捣乱。用--version参数确认当前启动的版本~/ChromeVersions/87.0.4280.88/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version输出应该显示Google Chrome 87.0.4280.88如果显示的是别的版本说明你调用的路径不对。4. 实操过程与核心环节实现4.1 完整操作步骤复盘我把整个流程从头到尾走一遍你照着做就行。假设我要装Chrome 109.0.5414.119这个版本这是最后一个支持某些老系统的稳定版。第一步下载dmg。找到对应版本的下载链接下载到~/Downloads/目录。下载完成后在终端里校验文件完整性cd ~/Downloads shasum -a 256 googlechrome109.0.5414.119.dmg把输出的哈希值和官方公布的对比一致就继续。第二步挂载dmghdiutil attach googlechrome109.0.5414.119.dmg挂载成功后终端会显示挂载点通常是/Volumes/Google Chrome。第三步创建目标目录并拷贝mkdir -p ~/ChromeVersions/109.0.5414.119 cp -R /Volumes/Google Chrome/Google Chrome.app ~/ChromeVersions/109.0.5414.119/第四步卸载dmghdiutil detach /Volumes/Google Chrome第五步创建启动脚本。每次都敲一长串路径太麻烦我习惯在~/ChromeVersions/下建一个launch-chrome.sh脚本#!/bin/bash VERSION109.0.5414.119 APP_PATH$HOME/ChromeVersions/$VERSION/Google Chrome.app/Contents/MacOS/Google Chrome USER_DATA$HOME/ChromeVersions/$VERSION/userdata $APP_PATH --user-data-dir$USER_DATA --disable-background-networking $给脚本加执行权限chmod x ~/ChromeVersions/launch-chrome.sh以后要启动这个版本直接运行~/ChromeVersions/launch-chrome.sh就行。想传额外参数比如打开某个网址直接在后面加~/ChromeVersions/launch-chrome.sh https://example.com。4.2 多版本共存的管理技巧当你装了三个以上版本时手动记路径就有点乱了。我的做法是在~/ChromeVersions/下建一个versions.txt每行记录一个版本号和对应的用途备注87.0.4280.88 - 老打印控件测试 109.0.5414.119 - 兼容性基线 120.0.6099.109 - 当前主力测试版然后写一个通用的启动脚本接受版本号作为参数#!/bin/bash VERSION$1 shift APP_PATH$HOME/ChromeVersions/$VERSION/Google Chrome.app/Contents/MacOS/Google Chrome USER_DATA$HOME/ChromeVersions/$VERSION/userdata if [ ! -f $APP_PATH ]; then echo 版本 $VERSION 未安装 exit 1 fi $APP_PATH --user-data-dir$USER_DATA --disable-background-networking $保存为~/ChromeVersions/run.sh以后启动就变成~/ChromeVersions/run.sh 87.0.4280.88这样管理起来清爽多了。你还可以把这个脚本加到shell的alias里比如在~/.zshrc里加一行alias chrome87~/ChromeVersions/run.sh 87.0.4280.88以后直接敲chrome87就能启动。4.3 用户数据目录的隔离与清理每个版本用独立的--user-data-dir有个副作用就是磁盘占用会越来越大。Chrome的用户数据目录里缓存、历史记录、扩展数据加起来用久了能到几个GB。定期清理是必要的。清理的时候不要直接删整个userdata目录那样书签和密码也没了。正确的做法是进到userdata/Default/下面删掉Cache、Code Cache、GPUCache这几个缓存目录就行。命令如下rm -rf ~/ChromeVersions/87.0.4280.88/userdata/Default/Cache rm -rf ~/ChromeVersions/87.0.4280.88/userdata/Default/Code\ Cache rm -rf ~/ChromeVersions/87.0.4280.88/userdata/Default/GPUCache如果你确定这个版本只是临时测试用不需要保留任何数据那直接删整个版本目录更省事rm -rf ~/ChromeVersions/87.0.4280.88注意删除前确认没有正在运行的该版本Chrome进程否则可能删不干净。用ps aux | grep ChromeVersions查一下有的话先kill掉。5. 常见问题与排查技巧实录5.1 启动报错应用已损坏怎么办这是macOS Gatekeeper的拦截。因为拷贝出来的应用签名路径变了系统认为它可能被篡改。解决办法有两个。一是右键点应用选打开然后在弹窗里点打开这样系统会记住你的选择。二是用命令行移除隔离属性xattr -dr com.apple.quarantine ~/ChromeVersions/87.0.4280.88/Google\ Chrome.app执行完再启动就不会弹提示了。但要注意这个命令会移除所有扩展属性包括一些安全标记所以只对你信任的来源使用。5.2 版本号显示不对或启动的是系统Chrome这种情况通常是因为你调用的路径不对或者系统里有多个Chrome可执行文件。先用which google-chrome查一下系统PATH里有没有别的Chrome。然后用绝对路径启动并在启动后访问chrome://version/页面确认版本号。如果页面显示的版本和你预期的不一样说明启动的不是你拷贝的那个副本。还有一种可能是LaunchServices缓存了旧的应用路径。可以重建缓存/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user这个命令会重置应用的注册信息执行后可能需要重新登录。5.3 扩展程序无法安装或加载低版本Chrome对扩展的签名要求和新版不一样。如果你从Chrome应用商店装扩展可能会提示无法从该网站添加应用、扩展程序和用户脚本。这是因为旧版本的安全策略和新版商店不兼容。解决办法是下载扩展的.crx文件然后拖到chrome://extensions/页面里安装。但要注意Chrome 75之后对.crx的安装限制更严了可能需要开启开发者模式。如果扩展还是装不上检查一下启动参数里有没有--disable-extensions有的话去掉。另外--user-data-dir指定的目录如果权限不对扩展也装不进去。确保目录属主是你自己chown -R $(whoami) ~/ChromeVersions/87.0.4280.88/userdata5.4 常见问题速查表问题现象可能原因解决方法启动提示应用已损坏Gatekeeper拦截右键打开或执行xattr -dr com.apple.quarantine版本号显示不对调用了系统Chrome用绝对路径启动检查chrome://version/扩展无法安装旧版商店不兼容下载crx文件拖入安装开启开发者模式启动后闪退用户数据目录权限问题执行chown -R $(whoami)修复权限自动更新到新版更新组件未禁用加--disable-background-networking参数磁盘占用过大缓存未清理删除Default/Cache等缓存目录5.5 几个我踩过的坑第一个坑是路径里的空格。Google Chrome.app这个名字里有空格在命令行里必须转义或者加引号否则shell会把Google和Chrome.app当成两个参数。我一开始没注意报了一堆command not found排查了半天。第二个坑是dmg挂载后没卸载就直接删文件。macOS会认为文件还在使用中删不干净。一定要先hdiutil detach再操作。第三个坑是多个版本共用同一个--user-data-dir。我图省事让87和109共用一个数据目录结果87的书签被109升级了格式再切回87就打不开了。所以每个版本必须独立数据目录这是铁律。第四个坑是忘了关自动更新。有一次测试到一半Chrome自己更新了版本号变了测试结果全废。后来我在启动脚本里强制加了--disable-background-networking再也没出过这个问题。6. 进阶用法与自动化集成6.1 用脚本批量管理多版本当你需要频繁切换版本时可以写一个更完善的bash脚本支持列出已安装版本、启动指定版本、清理缓存等操作。下面是我自己在用的版本#!/bin/bash BASE_DIR$HOME/ChromeVersions case $1 in list) echo 已安装版本 ls -1 $BASE_DIR | grep -E ^[0-9] ;; run) VERSION$2 shift 2 APP$BASE_DIR/$VERSION/Google Chrome.app/Contents/MacOS/Google Chrome if [ ! -f $APP ]; then echo 版本 $VERSION 未安装 exit 1 fi $APP --user-data-dir$BASE_DIR/$VERSION/userdata \ --disable-background-networking $ ;; clean) VERSION$2 rm -rf $BASE_DIR/$VERSION/userdata/Default/Cache rm -rf $BASE_DIR/$VERSION/userdata/Default/Code Cache echo 版本 $VERSION 缓存已清理 ;; *) echo 用法: $0 {list|run|clean} [版本号] ;; esac保存为~/ChromeVersions/manage.sh加执行权限。以后管理就三条命令./manage.sh list看有哪些版本./manage.sh run 87.0.4280.88启动指定版本./manage.sh clean 87.0.4280.88清理缓存。6.2 结合自动化测试工具使用如果你用Selenium或者Puppeteer做自动化测试可以直接指定这个独立Chrome的可执行文件路径。以Python的Selenium为例from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.chrome.options import Options chrome_path /Users/你的用户名/ChromeVersions/87.0.4280.88/Google Chrome.app/Contents/MacOS/Google Chrome options Options() options.binary_location chrome_path options.add_argument(--user-data-dir/Users/你的用户名/ChromeVersions/87.0.4280.88/userdata) options.add_argument(--disable-background-networking) driver webdriver.Chrome(optionsoptions) driver.get(https://example.com)注意ChromeDriver的版本必须和Chrome主版本号匹配。Chrome 87就要用ChromeDriver 87去ChromeDriver的官方归档页面下载对应版本。版本不匹配会报session not created错误。6.3 把启动命令做成macOS应用快捷方式如果你不想每次都开终端可以用Automator把启动脚本包装成一个应用。打开Automator新建应用程序添加运行Shell脚本动作把启动命令粘进去保存为Chrome 87.app。以后双击这个应用就能启动指定版本的Chrome跟正常应用一样。不过要注意Automator包装的应用启动时工作目录可能不是你预期的位置所以脚本里所有路径都要用绝对路径不能有相对路径。7. 一些个人体会和后续扩展思路这套方案我用了快两年从Chrome 70多一直管到120多中间换过M1的Mac也换过Intel的整体很稳定。最大的感受就是独立目录加独立用户数据目录这个组合是核心只要这两点做到位多版本共存基本不会出问题。剩下的就是些细节比如路径转义、权限修复、缓存清理踩过一次坑就记住了。后续如果你需要更自动化的管理可以考虑用Docker跑Chrome。Linux版的Chrome在容器里跑通过VNC或者远程调试端口访问这样连macOS的Gatekeeper都绕过了。不过那个方案复杂度更高适合团队共享测试环境用个人本地测试还是本文这套轻量方案更顺手。另外如果你只是偶尔需要某个低版本不想长期维护可以用完就删。把dmg文件留好需要的时候重新拷一份出来几分钟的事。没必要为了一个临时需求搞一套复杂的版本管理系统。工具是为人服务的怎么省事怎么来。