前阵子帮一个做渠道运营的朋友改表,她手里有两份Excel:一份是渠道名单总表,一份是按月度拆分给各区域的跟进表。两份表里都有“当前状态”和“最新联系人”这两列,两边都会改。她说每次月底对账,都需要人工把两份表同一个字段逐行对一遍,改漏了就吵架。我当时的回复很直接:写公式引用解决不了你这个场景,因为你要的不是“一个改了一个跟着改”,而是“两边都能改,任何一边改了另一边都同步”。前者是文档结构问题,后者才是真正的Excel双向数据同步。
这个需求在办公场景里非常普遍。同一份客户资料、同一个项目状态、同一组价格参数,散落在多个工作表或者多个工作簿里,不同部门的人各维护一部分,结果就是永远有人在对数。很多人的第一反应是用等号引用,比如在A1里写=B2,但试过之后就会发现它只解决了一半问题——只能从源单元格往目标单元格单向流动,反方向一改,联动直接断掉。本文要讲的就是怎么用VBA事件机制实现真正意义上“哪边都能改、两边都保持一致”的跨单元格数据联动,同时把部署过程中容易踩的坑一并讲透。适合所有需要在多表之间维护同一份数据的表格管理员和普通办公人员,不需要太深的技术基础,代码我会逐行解释。
1. 先看清等号引用的边界,再决定要不要上VBA
1.1 等号引用的“单向管道”本质
在动手写代码之前,必须先搞清楚一个最底层的事实:等号引用到底在Excel内部做了什么。
假如你在A1单元格里输入=B2,Excel并不是简单地把B2的值“复制”到A1,而是在A1里建立了一个公式,这个公式持续从B2取数并显示出来。你可以把这条公式理解成一根单向管道:数据只能从B2流向A1,A1本身不存储数据,它只是B2的一个“显示器”。
这带来两个直接后果。
第一个后果:你修改B2,A1会立刻更新,这个方向是通的。第二个后果:你直接修改A1,Excel根本不知道要把新值写回哪里。它只有一个选择——把A1里的公式清掉,换成你手动输入的值。从这一刻起,这根管道就断了,A1和B2再无任何关系。更崩溃的是,如果你对A1输入新值的时候不小心按了回车,Excel弹出来的提示是“不能更改部分数组”或者“不能更改公式”,你会觉得这个软件简直在跟你作对。
我见过太多人踩这个坑了。有人把客户跟进表里的手机号用公式引到总表里,第二天总表的人直接把旧号改成新号,跟进表那边还挂着旧号,月底对账时两边数据对不上,互相怀疑对方的表有问题。问题根本不在人,而在方案——等号引用天生就不是为双向需求设计的。
1.2 双向需求从哪来:同一字段在不同工作表中的身份冲突
那什么样的业务场景会产生双向同步的需求?根据我的观察,主要有三类。
第一类,同一个人或同一个项目出现在多个工作表,而同一个字段在两张表中都有编辑权限。比如客户总表和区域跟进表都有“最新联系人”,销售专员改区域表里的联系人,管理员改总表里的联系人,两边都希望最终结果一致。
第二类,不同部门维护同一张明细的不同列,但需要共享某个主键字段。比如运营部维护“活动状态”,财务部维护“预算额”,活动状态变更需要同步到财务的预算表里,财务改预算时也可能顺手调整状态。
第三类,月度模板里有一张源表、一张对外展示表,展示表对外开放填写后,源表也要跟着更新。这种场景在报表管理里尤其常见。
这些场景下,公式引用的三个问题就完全暴露了:不能两端同时修改、循环引用会直接报警(A引用B,B又引用A)、目标单元格本身被公式锁死导致无法承载新值。
所以在做技术选型之前,不妨先画一条线:
| 方案 | 同步方向 | 两端能否同时改 | 是否需要代码 | 最适合的场景 |
|---|---|---|---|---|
| 等号公式引用 | 单向 | 不能 | 否 | 主从表结构、只读汇总 |
| VBA事件同步 | 双向 | 能 | 是 | 多表维护同一字段、状态联动 |
| 数据验证+公式 | 半双向 | 部分能 | 否 | 固定选项的状态同步 |
| Power Query | 单向(源→查询) | 不能反写 | 否 | 多明细表汇总统计 |
如果业务需求已经在第二条赛道,那不用犹豫,直接进入我们的话题:用VBA的Change事件来实现双向数据同步。
2. 双向同步的核心机制:用Change事件当数据钩子
2.1 Excel事件的工作逻辑
VBA能实现双向联动,靠的并不是复杂的计算逻辑,而是Excel提供的一个事件机制——Worksheet_Change。
这个名字起得很直白:当工作表中任意单元格的数值被修改时,Excel都会自动调用一次这个事件。你可以把它想成一个安装在单元格上的智能门铃,任何人碰了单元格,门铃就会响,你要做的就是听到响声后出去看一眼,确认是谁、做了什么、需不需要把消息转发到别的单元格。
在实际代码里,Worksheet_Change会收到一个参数Target,这个Target就是“刚刚被修改的那块区域”。我们所有的判断逻辑,都以Target为基础。
具体流程是这样的:用户改了一个单元格 → Excel自动触发Change事件 → 代码检查Target是不是自己关注的区域 → 如果是,把Target的新值写入配对的另一个单元格 → 完成一次同步。
这里有一个关键点:事件代码写在什么位置,决定了它的作用范围。如果写在某个工作表(比如Sheet1)的代码模块里,那只有这个工作表的修改会触发事件;如果写在ThisWorkbook里,并且使用Workbook_SheetChange,那工作簿里任意工作表的修改都会触发事件。跨工作表同步必须用后者,这一点后面还会展开。
2.2 防死循环的唯一开关:EnableEvents
如果你第一次接触这个事件,大概率会写出这样的代码:用户改B2,事件里把B2的新值写到D4;同时D4的值变化又触发了Change事件;事件里发现D4变化,又把D4的值写回B2;B2又触发事件……循环往复,轻则Excel假死,重则直接卡死崩溃。
这个问题的根源在于:事件被嵌套触发了。解决它的唯一办法,是Excel提供给我们的一个总开关——Application.EnableEvents。
这个属性控制着“是否允许Excel触发后续事件”。把它设为False,就相当于暂时关闭了门铃。我们在事件代码里写入对端单元格之前,先把门铃关掉,写入完成后再把门铃打开,这样对端单元格的变化就不会再次触发同一段代码,循环自然就断了。
Application.EnableEvents = False ' 这里执行写入目标单元格的操作 Application.EnableEvents = True这两行代码,是整个双向同步方案里最核心、最不能省略的部分,没有之一。后面所有代码里你都会看到它,请把这句当成肌肉记忆。
2.3 事件机制与撤销机制的天然矛盾
这里必须提前打一个预防针:一旦通过VBA修改了单元格内容,Excel的撤销栈就会清空。也就是说,用户完成操作后按Ctrl+Z,通常只能撤销最近一次手动操作,而无法撤销脚本写入的那一步。
很多人反馈“用了VBA之后Excel撤销失灵”,其实并不是失灵,而是VBA对工作簿的修改本来就不被Excel登记为可撤销的用户操作。这是平台的既定限制,不是代码可以绕过的。
所以在设计同步方案时,我一般会做两个准备:要么在接受这个限制的前提下,让同步范围尽量小,只在真正需要联动的几个单元格之间工作;要么在事件里顺手把旧值记到一个全局变量里,然后提供一个“撤销同步”的按钮,调用宏把旧值写回去。后面踩坑部分我会给一个简化版的自定义撤销方案。
3. 基础版落地:两个单元格相互同步的完整代码
3.1 落地前置三件事
写代码之前,先把环境准备好,否则代码写好了也跑不起来。
第一件事,是把工作簿另存为启用宏的格式。.xlsx格式不允许存储VBA代码,必须保存为.xlsm。这一步很多人会忘,导致关掉文件再打开,代码全没了。
第二件事,是让“开发工具”选项卡显示出来。右键Excel功能区任意位置 → 自定义功能区 → 勾选“开发工具”,或者直接通过“文件 → 选项”进入设置。没有这个选项卡,你连VBA编辑器都找不到。
第三件事,是宏安全设置。默认情况下Excel可能禁止运行宏,尤其是从网上下载的工作簿,打开时还会出现一个安全警告条。点击警告条里的“启用内容”就好。如果是分发给他人的文件,需要在对方的“信任中心”里把“启用所有宏”或者“禁用宏并通知”设置好。很多公司里同事反馈“宏被禁用”“加载项被禁用”,多半都能在这一环节找到根源。
3.2 完整代码与逐段说明
假设最简单的场景:Sheet1工作表中,B2和D4两个单元格的内容要保持双向一致,改其中一个,另一个自动更新。
请在Sheet1的代码区(不是模块)里粘贴这段代码:
Private Sub Worksheet_Change(ByVal Target As Range) Dim watchRange As Range Set watchRange = Range("B2:D4") ' 如果刚刚修改的位置不在关注区域内,直接退出 If Intersect(Target, watchRange) Is Nothing Then Exit Sub ' 关闭事件,防止写入对端单元格时再次触发本过程 Application.EnableEvents = False On Error GoTo CleanFail ' 只处理单个单元格被修改的情况 If Target.Cells.Count = 1 Then If Target.Address = "$B$2" Then Range("D4").Value = Target.Value ElseIf Target.Address = "$D$4" Then Range("B2").Value = Target.Value End If End If Application.EnableEvents = True Exit Sub CleanFail: Application.EnableEvents = True End Sub我逐段说下思路。
watchRange是“关注区域”。我故意把它的范围设为B2:D4而不是只设两个单元格,这样你可以顺便测试一下:改中间区域的任意一个非目标单元格,代码因为地址不匹配,什么都不会发生。这里的核心思路是先用Intersect做粗筛,再用Target.Address做精确判断,两层过滤,避免无关操作触发无意义的同步。
Target.Cells.Count = 1这一步非常实用,它拦住了“一次性粘贴多个单元格”的情况。如果你没用这个判断,用户把一整片区域粘贴进来,Target会变成一个类似于$B$2:$D$4的Range对象,后面的Target.Address = "$B$2"永远不会成立,代码就会直接跳过同步逻辑,看起来像“粘贴没反应”。
On Error GoTo CleanFail加上后面恢复事件的语句,是防御性的写法。万一同步过程中出现未知错误,也能保证Application.EnableEvents恢复为True,否则你运行出错后,工作簿会一直处于“门铃关闭”状态,宏失效,连你自己都找不回原因。这一点我会在第5节详细展开。
3.3 两对以上单元格怎么扩展
业务一般不会只有两个单元格要联动。如果你有三对、四对甚至更多的配对,逐行写If...ElseIf当然可以,但代码会越来越臃肿。更推荐用Select Case写:
Private Sub Worksheet_Change(ByVal Target As Range) If Target.Cells.Count > 1 Then Exit Sub Application.EnableEvents = False On Error GoTo CleanFail Select Case Target.Address Case "$B$2" Range("D4").Value = Target.Value Case "$D$4" Range("B2").Value = Target.Value Case "$B$5" Range("D7").Value = Target.Value Case "$D$7" Range("B5").Value = Target.Value End Select Application.EnableEvents = True Exit Sub CleanFail: Application.EnableEvents = True End SubSelect Case的好处是配对关系一目了然,后面加新对子,直接在下面加两行case即可。不过这样写到几百行也会很痛苦,那就需要进入第4节讲的“配置表方案”了。
4. 进阶用法:跨工作表与规则化联动
4.1 跨工作表同步:使用Workbook_SheetChange
上面的代码只能同步同一个工作表内的单元格。一旦涉及两张工作表互相同步,代码的位置就要从“工作表模块”移到ThisWorkbook模块里,并且改用Workbook_SheetChange事件。
这个事件比Worksheet_Change多了一个参数Sh,用来告诉我们到底是哪张工作表被改了。举个实际例子:源表和工作表Sheet1里的B2,和目标表Sheet2里的D4,要实现双向同步。
在ThisWorkbook代码区粘贴这段:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range) ' 只关心源表和目标表,其他表的修改一律忽略 If Sh.Name <> "Sheet1" And Sh.Name <> "Sheet2" Then Exit Sub Application.EnableEvents = False On Error GoTo CleanFail If Sh.Name = "Sheet1" Then If Target.Address = "$B$2" And Target.Cells.Count = 1 Then ThisWorkbook.Worksheets("Sheet2").Range("D4").Value = Target.Value End If End If If Sh.Name = "Sheet2" Then If Target.Address = "$D$4" And Target.Cells.Count = 1 Then ThisWorkbook.Worksheets("Sheet1").Range("B2").Value = Target.Value End If End If Application.EnableEvents = True Exit Sub CleanFail: Application.EnableEvents = True End Sub这段代码的逻辑跟单表版本几乎一样,区别就在于每次同步前都要先判断“这次改动发生在哪张表”,然后才知道该往哪张表里写。判断标准就是Sh.Name。如果你有Sheet3、Sheet4也想参与同步,改成Select Case Sh.Name再往里面加分支就行。
4.2 同表同行多组联动:按行号处理
表格里另一类常见需求是“每一行的两个字段互相同步”。比如每行B列是“数量A”,E列是“数量B”,改同一行的任意一个数,另一个数跟着变。这种情况如果逐单元格去判断地址,那有多少行就要写多少条规则,不可维护。
正确思路是直接判断“修改发生在B列还是E列”,然后通过Target.Row拿到行号,再写入同一行对应列:
Private Sub Worksheet_Change(ByVal Target As Range) ' 只处理单格修改,而且必须发生在B列或E列 If Target.Cells.Count > 1 Then Exit Sub If Target.Column <> 2 And Target.Column <> 5 Then Exit Sub If Target.Row < 2 Then Exit Sub Dim r As Long r = Target.Row Application.EnableEvents = False On Error GoTo CleanFail If Target.Column = 2 Then Cells(r, 5).Value = Target.Value ElseIf Target.Column = 5 Then Cells(r, 2).Value = Target.Value End If Application.EnableEvents = True Exit Sub CleanFail: Application.EnableEvents = True End Sub这段代码我实际用了很多次,效果很稳定。它不依赖具体单元格地址,所以无论表扩展到几百行,只要改动落在B列或E列,事件就能正确地把值写到同行另一列。注意我用Target.Row < 2跳过标题行,防止你在B1写个标题也触发逻辑。
4.3 把配对规则做成配置表
如果你觉得自己维护Select Case还是麻烦,或者你是给不会写代码的同事做工具,那就直接把配对规则写进工作表,让代码去读表,彻底消灭“改一次需求改一次代码”的工作。
做法是在工作簿里新建一张“规则配置”表,A列写源单元格地址(带工作表名),B列写目标单元格地址(带工作表名),比如:
| A列(源) | B列(目标) |
|---|---|
| Sheet1!B2 | Sheet1!D4 |
| Sheet1!B5 | Sheet2!D7 |
然后在ThisWorkbook里写事件,每次单元格变化后,遍历这张配置表,判断当前修改的位置是否命中某一行的源地址,命中就把值写到对应的目标地址:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range) Dim cfgSheet As Worksheet Set cfgSheet = ThisWorkbook.Worksheets("规则配置") ' 配置表本身发生变化时不处理,防止死循环 If Sh.Name = cfgSheet.Name Then Exit Sub If Target.Cells.Count > 1 Then Exit Sub Dim lastRow As Long lastRow = cfgSheet.Cells(cfgSheet.Rows.Count, 1).End(xlUp).Row If lastRow < 2 Then Exit Sub Dim i As Long Dim fromSheet As String Dim fromAddr As String Dim toSheet As String Dim toAddr As String Application.EnableEvents = False On Error GoTo CleanFail For i = 2 To lastRow fromSheet = Split(cfgSheet.Cells(i, 1).Value, "!")(0) fromAddr = Split(cfgSheet.Cells(i, 1).Value, "!")(1) toSheet = Split(cfgSheet.Cells(i, 2).Value, "!")(0) toAddr = Split(cfgSheet.Cells(i, 2).Value, "!")(1) If Sh.Name = fromSheet And Target.Address = fromAddr Then ThisWorkbook.Worksheets(toSheet).Range(toAddr).Value = Target.Value End If Next i Application.EnableEvents = True Exit Sub CleanFail: Application.EnableEvents = True End Sub这套方案最大的优势是“无代码化维护”。下次业务方说“加一组联动”,你只需要在配置表里加一行,不用打开VBA编辑器,也不会误伤已有代码。我在实际帮同事做工具时,几乎都用这个模式收尾。
4.4 联动时的格式问题:日期、文本数字和合并单元格
代码虽然能把值同步过去,但Excel自动做的类型转换有时候会坑人。
日期在Excel内部存的是序列号,如果你的目标单元格格式没设置成日期,同步过去之后就变成一串41763之类的数字。文本型编号也是重灾区,比如编号“000123”,如果你在源单元格里是用文本格式输入的,同步到目标单元格后如果目标单元格是常规格式,它会变成123。解决方法是同步的同时连格式一起复制,比如:
Range("D4").Value = Target.Value Range("D4").NumberFormat = Target.NumberFormat再把这两行拼在一起简单得多。合并单元格也要小心。如果目标单元格是合并区域,直接给Range("D4").Value赋值通常没问题,但如果你的源单元格也是合并区域,用Target.Address判断时,地址是合并区域左上角的地址,而Target.Value返回的其实是合并区域第一个单元格的值,这些细节在调试时要特别留意,遇到“同步过来是空值”时优先怀疑这个原因。
5. 副作用与常见坑:完整排查链路还原
5.1 “粘贴一大片区域后没反应”:区域的Target认识错误
这是所有新手在事件方案里遇到频次最高的一个问题,也是最容易跟“Excel Ctrl+V失效”混淆的一种现象。
形式和现象是这样的:VBA事件同步已经部署好,单独改B2单元格时D4会跟着变,一切正常。同事复制了一片A1:C10的数据进来,准备批量粘贴,粘贴完成后你发现——只有个别单元格同步了,或者干脆一个都没同步。然后同事向你反馈:“Excel粘贴不好使了,Ctrl+V没反应。”
这里要郑重说一句:大概率不是粘贴功能坏了,而是你的事件代码没有把“区域粘贴”当成有效修改处理。
原因在于:你单独点一个单元格,Target是一个单元格对象,它的Address是$B$2;但你一次性粘贴整个区域,Target是一个多单元格区域,它的Address变成了$A$1:$C$10,甚至是一个Union区域。代码里Target.Address = "$B$2"这个判断,对一个区域地址来说永远不成立,于是整个粘贴过程在你的事件逻辑看来,跟没发生一样。
排查链路也可以按这个顺序走一遍:先看修改后Target.Address在代码里输出的到底是什么,再判断是“区域地址不匹配”还是“事件根本没触发”。很多情况下,问题出在前者而不是宏被禁用。
想支持区域粘贴,要么改成按行遍历,逐单元格处理;要么把判断逻辑放宽为“判断Target是否包含我们关注的单元格”,再取交集部分处理。我个人更建议保持简单:明确告诉使用方,这个同步仅针对单格编辑或逐格粘贴,批量粘贴大数据时暂停事件或者分批处理,方案更可控。
5.2 大面积粘贴导致脚本运行缓慢
如果确实有大区域粘贴的需求,还要考虑性能。给Target里每一个单元格写回配对的单元格,循环次数多了之后,Excel会一帧一帧地刷新屏幕,看起来特别卡。
缓解办法有两个:一是写入过程中先关掉屏幕刷新Application.ScreenUpdating = False,写完再开;二是用Union把所有需要写入的目标单元格收集起来,最后一次性赋值。实测下来,几十行数据差别不大,几百行以上建议必须处理一下,否则用户会以为程序死了。
5.3 同步后Ctrl+Z不能撤销的体感与对策
第2节已经说了原理。被这个坑卡过的人,体验都是“改了一个数,自动把我别的地方改了,我想撤销回去,结果只撤销了我的第一步操作,联动写入的那一步根本撤销不了”。
我的对策分两种。如果只是内部使用,我倾向于在接受这个限制的同时,把保护做到位——值班表、状态表这种低频改动场景,影响有限。如果是给业务同事用的工具,我会做一个简单的自定义撤销:在事件触发时,先把目标单元格的旧值存入一个全局变量,然后在工作表里放一个按钮,点击后执行一个宏,把这个旧值写回目标单元格。这样至少能实现“手动撤销到上一个同步点”,比Ctrl+Z还要更可控。
5.4 文件发给同事后宏被禁用:信任中心与加载项的问题源头
事件方案做好后,最让人头疼的不是开发,而是分发。同事拿到你发的.xlsm文件,双击打开,Excel顶部弹出“宏已禁用”的安全警告。如果他还点了不启用,你的代码一行都不会执行,看起来就像“Excel加载项被禁用”。
正确做法是提前告诉对方怎么处理:在信任中心里把“启用所有宏”打开,或者把你的文件所在目录加入“受信任位置”。公司内部局域网分发时,还可以用证书做宏签名。这里再强调一下文件格式:必须是.xlsm,如果保存成.xlsx,Excel会直接提示“文件无法打开,因为文件格式或文件扩展名无效”,这句话不是Excel坏了,是它不认识没有VBA工程的扩展名。
5.5 受保护工作表和合并单元格的连带问题
事件代码往目标单元格写入值时,如果目标工作表被保护了,会弹“运行时错误1004”,而On Error逻辑如果没有恢复好,还会把Application.EnableEvents卡在False状态,越修越乱。
处理方式很简单:写入之前临时解除保护,写完再恢复:
Worksheets("Sheet2").Unprotect Password:="123456" Worksheets("Sheet2").Range("D4").Value = Target.Value Worksheets("Sheet2").Protect Password:="123456"不过把密码硬编码在VBA里并不安全,防君子不防小人。我的习惯是保护工作表的主要目的是防止误操作,而不是防破解,所以密码强度不用太高,知道的人能改代码就行。另外合并单元格的联动问题前面已经提过,排查时遇到“同步后是空白”,第一时间检查合并区域变故。
5.6 死循环仍然出现时的排查链路
如果你跑起来之后发现Excel在疯狂闪动或者已经卡死,说明死循环还是发生了。这时别急着关文件骂人,用“Ctrl+Break”可以中断VBA执行,然后沿着这条链路排查:
第一步,确认Application.EnableEvents = False是否真的在写入目标单元格之前执行了。第二步,确认写入目标单元格后,是否在某个分支里被提前Exit Sub跳过了恢复。第三步,确认On Error GoTo CleanFail里有没有把开关重新打开。第四步,检查你的事件代码是否存在“同步源也属于目标区域”的逻辑错误。
我见过最多的原因,是有人在两个宏之间共享了全局变量,或者在Workbook_SheetChange里忘了判断Sh.Name,导致配置表自己也被当成同步对象,改配置表又触发同步,同步又改配置表,车轱辘话来回跑。写好前四步,这套方案基本不会出乱子。
6. 你可能根本不需要VBA:替代方案选型清单
6.1 单向同步时,回归公式和名称管理
虽然这篇讲的是双向同步,但我还是要兜一盆冷水:如果你的业务根本不需要两端都修改,那回去用等号引用,配合名称管理器,维护成本更低,也不会有宏安全、撤销失效这些破事。工具选型的第一原则永远是“复杂度匹配需求”。一个只读汇总需求被做成双向VBA,属于过度设计。
6.2 固定选项的状态联动:数据验证加公式就够了
如果同步的字段只有“进行中”“已完成”“已驳回”这几个固定选项,方案可以简化为:在一张隐藏状态表里维护选项,业务表用数据验证做下拉列表,其他报表里需要显示同一个状态的地方,用公式去引用这张隐藏表。这样虽然没有双向,但“唯一的维护口径 + 多处自动展示”已经能满足大多数场景,而且完全绕开了VBA的所有副作用。
6.3 汇总统计场景:交给Power Query
不同人维护多张明细表,需要合并成一张汇总表。这种场景属于“从多处到一处”的单向聚合,用Power Query把多张明细表合并查询,输出一张汇总表,比VBA双向同步更规范。Power Query不需要写复杂代码,界面式操作,也不存在事件触发问题。局限在于它不能反向写回源表,但大部分汇总场景本来也不需要反写。
6.4 高频多用户并发:Excel不是合适平台
最后这条建议可能有点扎心,但确实是我在实际项目里的体会。如果多个部门同事同时高频修改同一个字段,且对“一致性”有严格要求,Excel双向同步方案做得再漂亮也只是在悬崖边跳舞。两个同事在同一秒各改一端,事件触发时总有一个人的写入会被后覆盖,谁也说不清哪个值才是对的。这种需求,归根结底应该交给数据库或低代码平台去处理,Excel只适合承载Low并发、结构简单、参与人数不多的场景。认清边界,才能把工具用在自己该用的地方。
我在实际部署这些方案时,最深的体会是:双向同步这件事技术含量不高,难的是把“什么时候该触发、什么时候千万不要触发”这件事想清楚。所以最后再给大家一个小建议,也是我给自己定的规矩——每次部署事件代码前,先把要同步的单元格映射关系列一个清单,再动手写代码;写完保留一个不含代码的原始备份,出问题随时退回。在这个基础上,5.6节那些坑基本都能绕开。