尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MySQL事务底层机制:从redo/undo到MVCC与锁的实践解析
先用一个场景把问题说清楚你在秒杀系统里扣库存业务逻辑往往是“先查库存再写订单最后扣减库存”。如果这三步中间任何一步失败前面成功的数据都要一并撤销否则就会出现“订单生成了库存却扣不动”或者“库存扣了订单不存在”这类资损事故。这就是事务存在的最直接理由。而要真正搞清楚“MySQL是怎么实现事务的”不能只看几条命令得深入到InnoDB引擎的日志、锁和版本链里去看。这篇文章我不打算把官方文档复述一遍。我会结合自己实际跑过的压测、排查过的事故把MySQL事务从底层机制到生产实践拆开来讲。适合正在做后端开发、数据库运维、或者对InnoDB原理感兴趣的同学看完之后你会明白四个问题事务凭什么能回滚、凭什么能持久化、凭什么读写不冲突、以及它在分布式环境下为什么不够用。1. 先从一场“库存扣减事故”说起为什么需要事务1.1 没有事务的并发现场先假设MySQL没有事务你的库存表里有条记录stock 10。两个用户同时下单各自执行“库存 - 1”用户AUPDATE stock_table SET stock stock - 1 WHERE sku xxx; 用户BUPDATE stock_table SET stock stock - 1 WHERE sku xxx;两条SQL同时对同一行做“读改写”如果没有加锁和隔离机制可能出现的结果是A读到10B也读到10A写回9B写回9最终库存只剩8但实际卖了2件。这就是经典的“丢失更新”。可能有人会说“我给UPDATE加个条件WHERE stock 0不就行了”但问题没这么简单——如果用户A下完单后要写订单表还要扣用户积分积分表写入失败了库存这边已经扣掉。整个流程没有一个统一的“要么全成功、要么全失败”的边界数据依然会乱。1.2 ACID到底在保护什么事务要解决的核心问题就是让一组操作具备原子性Atomicity、一致性Consistency、隔离性Isolation和持久性Durability。很多人背这四个词很熟问到底怎么实现的就卡住了。简单对应关系是这样的原子性依赖undo log。事务执行过程中所有对数据的修改都会被记录成 undo 日志一旦发生回滚就按 undo 里的旧值把数据恢复回去。持久性依赖redo log和binlog。事务提交时不是直接刷数据页而是先把日志落盘。即使数据库崩溃重启后也能通过日志重放找回已经提交的数据。隔离性依赖锁和MVCC。锁控制“同一时刻只允许一个事务修改一行”MVCC 则让“读”和“写”尽量不互相阻塞。一致性是最上层的目标靠前面三者共同约束同时还需要业务代码本身遵守约束条件比如金额不能为负、库存不能超卖。1.3 事务和一条SQL不是一回事还有一个常见的误区认为“每条SQL天然就是事务”。严格来说MySQL里有一条autocommit参数默认是开启的。开启状态下每条SQL确实会被当作独立事务自动提交。但真正的业务事务通常是一段代码块START TRANSACTION; UPDATE account SET balance balance - 100 WHERE user_id 1; UPDATE account SET balance balance 100 WHERE user_id 2; COMMIT;只有把多条SQL包在START TRANSACTION和COMMIT之间它们才算是一个整体。很多事故的根源就在于代码里开了Transactional注解但底层因为各种原因没有真正走同一个数据库连接导致事务失效。这一点先记住后面讲长事务的时候还会提到。2. redo log与undo log事务持久性和回滚的物理基石2.1 为什么先写日志、再动数据文件直接修改磁盘数据文件当然可以但很慢。你想想一次UPDATE可能涉及聚簇索引页、二级索引页每个页还要保证落盘时数据页里的校验值正确。如果每次更新都做一次随机磁盘写并发一高磁盘IO就成为瓶颈。InnoDB的做法是先把“内存里改完的数据页”标记为脏页同时把这次修改的顺序写入到redo log。因为日志是顺序追加写的磁盘顺序写的速度远快于随机写所以整体性能能大幅提升。这个思路就是常说的WALWrite-Ahead Logging——先写日志再写数据页。2.2 redo log的写入时机与崩溃恢复逻辑redo log 有几个很重要的细节它是物理日志记录的是“在某个页的某个偏移量上改成了什么值”。所以它不关心SQL语句本身只关心页的最终变化。它有LSNLog Sequence Number用来标识日志写入的递增位置。数据页上也有LSN落盘时如果发现页的LSN比日志LSN旧说明这个页还没跟上崩溃恢复时就需要重放。刷盘时机由innodb_flush_log_at_trx_commit控制取值行为说明安全性0事务提交时不主动刷redo log由后台线程每秒刷一次宕机会丢最多1秒数据1每次事务提交都把redo log刷到磁盘不丢已提交事务2提交时写入操作系统缓存由系统决定何时刷盘宕机不丢但宕操作系统可能丢生产环境里如果追求数据零丢失一般设成1。如果严格要求性能、能接受最多1秒数据丢失可以设成0或2。这里需要特别提醒innodb_flush_log_at_trx_commit 0不是“事务没持久化”而是“提交时晚一点刷盘”一旦进程崩溃已提交事务可能还没落盘就会出现数据丢失。崩溃恢复时InnoDB会从redo log的检查点checkpoint开始把后续所有日志应用到对应数据页。所谓checkpoint就是“这些LSN之前的数据页已经刷入磁盘了”的标记点。恢复过程只是把脏页补上不会回滚已提交事务这保证了持久性。2.3 undo log的年轮与回滚行为undo log 和 redo log 正好相反redo 记录“怎么重做”undo 记录“怎么撤销”。它属于逻辑日志里面存的是“之前的值”。举个例子你执行UPDATE user SET age 25 WHERE id 100;假设原来的age是22InnoDB在修改之前会在 undo log 里写入一条操作记录“id100这行的age旧值是22update之前还有哪些历史版本”。一旦事务需要回滚就直接拿着undo里的旧值生成反向操作把数据恢复成原来的样子。这里有个重点undo log 不只用于回滚它还是MVCC版本链的数据来源。后续要判断“某个事务能不能看到某条数据”就要借助undo log 找到该行的历史版本。所以 imagine 一条记录如果被反复更新多次undo log 会形成一个长链链越长查询时判断版本的代价就越高。2.4 一条UPDATE语句在事务引擎里的完整路线可以把一次UPDATE放到事务引擎里的执行链路串起来事务开始分配一个事务IDtrx_id。找到目标行的聚簇索引记录加上锁这里暂时假设是排他锁。把修改前的旧值写入 undo log并让行上的回滚指针指向这个undo记录。在内存里修改数据页同时记录 redo log。更新二级索引等相关结构同样记录redo。事务提交时redo log 按配置策略落盘然后写 binlog这里还会发生 redo 和 binlog 的两阶段提交保证两份日志一致。如果事务回滚则根据undo log 做反向操作如果崩溃则根据redo log 做前滚恢复。这条链路上每一环缺一不可。之前我见过一个项目DBA发现磁盘IO异常高排查下来是innodb_flush_log_at_trx_commit被设置成0后经常丢数据业务方又要求强一致双方没对齐参数语义导致频繁回滚重建数据。所以说日志参数的背后其实是性能和可靠性的权衡。3. InnoDB锁的设计与隔离级别并发控制里的取舍3.1 行锁、间隙锁、next-key lock各管哪一段InnoDB的锁是挂在索引记录上的不是挂在表上。大致分几类记录锁Record Lock锁住一条具体的索引记录。间隙锁Gap Lock锁住一个范围但不锁记录本身目的是防止别的事务在这个范围内插入新记录。临键锁Next-Key Lock记录锁和间隙锁的结合体左开右闭区间比如(10, 20]。间隙锁和临键锁是理解“可重复读为什么能防幻读”的关键。举个例子一张表里有记录id10、20、30。在可重复读隔离级别下执行一条带条件的当前读SELECT * FROM t WHERE id 15 FOR UPDATE;InnoDB会给(15, 20]、(20, 30]以及(30, ∞)这些区间加上间隙锁。另一个事务试图插入一条 id25 的记录必须先在 id20 和 id30 之间的间隙申请插入意向锁结果发现这个间隙被锁住了只能等待。这样就阻止了新记录的插入也就是阻断了“幻影行”的产生。3.2 隔离级别真正改的是什么MySQL提供四种隔离级别区别在于“不同事务之间能互相看到什么程度的数据”隔离级别脏读不可重复读幻读读未提交RU可能可能可能读已提交RC不会可能可能可重复读RRMySQL默认不会不会基本不会串行化S不会不会不会很多人把隔离级别当成一个纯粹的配置项其实它对底层实现影响很大读未提交SELECT 读到别人未提交的数据。也就是说InnoDB的快照读逻辑基本被绕过直接读最新版本。这种场景一般只用在“丢弃数据也无所谓”的统计查询里。读已提交每次SELECT都会生成新的Read View事务内两次读取可能因为其他事务提交了而看到不同结果。可重复读事务第一次SELECT时固定Read View后续读取都基于这个快照。在MySQL里默认用它对标Oracle的隔离标准。串行化读写都要加锁性能最差只适合极低并发的强一致场景。3.3 可重复读为什么能防住幻读MySQL在可重复读级别下使用MVCC 临键锁配合来解决幻读。普通SELECT走快照读基于undo版本链看到的是一个固定快照如果别的事务插入了一行快照里看不见也不会产生“本次事务内多出一行”的错乱。如果执行的是SELECT ... FOR UPDATE、UPDATE、DELETE这类当前读就必须走真实索引记录加临键锁把扫描区间的间隙全部堵住。这样其他事务无法插入新行幻读问题也就被锁机制挡住了。需要说明的是MySQL的可重复读并没有像SQL标准那样彻底封死所有幻读场景但在绝大多数业务场景下它表现的是“没有幻读”。实际项目里不建议去挑战这个边界比如先做一次快照读合否定某个数据不存在再依赖别的事务不会插入数据这种假设很容易踩坑。3.4 锁等待和死锁的真实案例我处理过很多次死锁最典型的小案例是两个事务互相等待事务A持有id1的行的锁想要id2事务B持有id2的行的锁想要id1。两边互不相让就形成死锁。InnoDB会检测到死锁并回滚代价较小的事务。在应用层经常看到这类错误ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction解决死锁的经验是让所有事务都按照同一个顺序访问资源。比如“先扣库存再写订单”所有请求都严格走这个顺序死锁概率会大幅下降再把事务做小缩短锁持有时间。另外一个容易忽略的情况是一个事务里执行了外部接口调用比如HTTP请求手里握着数据库锁等待外部返回此时另一个事务也在等这把锁很容易触发超时和死锁。4. MVCC版本链普通SELECT为什么不阻塞4.1 隐藏列和版本链怎么串起来MVCCMulti-Version Concurrency Control是InnoDB实现隔离的核心机制。它让“读”走快照“写”走当前版本从而读写不互相阻塞。每一行记录除了业务字段还有几个隐藏列DB_TRX_ID最近一次修改该行的事务ID。DB_ROLL_PTR回滚指针指向undo log中该行的上一个版本。DB_ROW_ID如果表没有主键InnoDB会生成一个递增的行ID。每次UPDATE先把旧版本连同事务ID挂到undo链上再在数据页上写入新值。多次更新后这行数据在内存里只有一个最新值但通过DB_ROLL_PTR可以一路找到所有历史版本。这就是“版本链”。4.2 Read View是怎么裁剪版本的当一个事务执行快照读时InnoDB会生成一个 Read View它包含三个关键部分所有未提交事务的ID集合。min_trx_id未提交事务中最小的ID。max_trx_id下一个将要分配的事务ID也就是“当前系统见过的最新事务ID”。判断某行版本是否可见的逻辑是如果行上的DB_TRX_ID小于min_trx_id说明这个版本在Read View创建之前就提交了可见。如果DB_TRX_ID大于等于max_trx_id说明这个版本是Read View创建之后才产生的不可见。如果DB_TRX_ID在min_trx_id和max_trx_id之间需要判断它是否还处于活跃事务中活跃则不可见已提交则可见。如果DB_TRX_ID等于当前事务ID自己改的数据当然可见。如果可见性判断失败就沿着DB_ROLL_PTR找上一个版本继续判断直到找到满足条件的版本。4.3 快照读与当前读一个反直觉的例子很多人以为“可重复读就是所有读都会重复”其实不对。看这个例子事务A开始先执行快照读SELECT * FROM t WHERE id 1读到的余额是100。与此同时事务B把余额改成0并提交。然后事务A再执行SELECT * FROM t WHERE id 1 FOR UPDATE这属于当前读走的是最新数据读到的余额是0不是100。也就是说普通SELECT是快照读FOR UPDATE、LOCK IN SHARE MODE、UPDATE、DELETE都是当前读。如果业务在同一个事务里“先查后改”尤其依赖查到的旧值做判断就可能在两步之间出现数据已经变化的情况。解决方法是把查询改成FOR UPDATE或者用条件UPDATE直接锁住目标行。4.4 一致性读影响业务的三种情况实际项目中因为一致性读造成的“闹鬼”现象往往和可重复读配合出现事务A第一次查询得到结果集事务B插入新行并提交事务A再查同一范围结果仍是旧集合。这符合隔离要求但如果你拿着这个快照去统计数据、去跑二次核对可能永远等不到最新数据。事务A读了某行事务B删除该行并提交事务A再读还是能读到那条已删除的记录。因为快照里保留着旧版本。长期开着事务不提交会导致undo链不断增长无法清理进而让磁盘膨胀查询变慢。这些现象不是Bug是MVCC的设计结果。理解之后你才会知道什么时候该用SELECT FOR UPDATE什么时候该用普通SELECT以及为什么长事务会拖垮实例。5. 生产环境里最实用的事务经验长事务、大事务与死锁排查5.1 大事务为什么会拖垮主从大事务指的不一定是“数据量大”而是“持有锁的时间长、执行步骤多、涉及数据行数多”的事务。它带来的问题有锁持有过久同一张表或同一批行的其他更新全部排队。回滚成本高事务回滚时需要根据undo log逆向恢复所有数据。大事务回滚的时间可能比正常执行还长。主从延迟binlog里记录的是一个个事务。主库提交一个超大事务后从库要按顺序把整个事务重放到本地。在从库执行期间从库的SQL线程只有这一个任务在跑其他事务全被堵在后面于是出现明显延迟。内存压力事务执行过程涉及的数据页都驻留在内存的缓冲池里。大事务产生的脏页过多会挤占缓存影响整体命中率。之前处理过一个极端的例子某订单导出功能在事务里做了全表扫描并批量更新了几十万行结果主库执行了十来秒从库延迟从几秒涨到了十几分钟。后来拆成一批批的短事务延迟才降下来。5.2 长事务排查从information_schema看起长事务指的是“长时间不提交也不回滚”的事务。它可能没有锁住任何行但只要它开启过它的Read View就会一直存在导致undo log无法清理历史版本一直保留。排查长事务我最常用的是这几条SQLSELECT * FROM information_schema.innodb_trx WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) 60\G看trx_started事务开始时间、trx_stateRUNNING还是LOCK WAIT、trx_mysql_thread_id线程ID可以关联到 processlist。要处理长事务第一步是找到发起会话确认它在等待什么SELECT * FROM performance_schema.data_lock_waits\G SELECT * FROM sys.innodb_lock_waits\G如果确定是死等或失控事务就要在应用层杀掉对应线程。直接KILL线程会造成事务回滚但短痛优于一直卡着系统。这里有个特别注意点很多长事务不是业务没提交而是事务代码里混入了“发消息”“写文件”“调用外部接口”等操作。事务应该只包含数据库操作外部调用一律放到事务提交之后。5.3 死锁日志的阅读姿势MySQL的innodb_print_all_deadlocksON打开之后死锁信息会记录到错误日志。日志里通常有两段信息LATEST DETECTED DEADLOCK包含两个事务的加锁顺序。每个事务的WAITING FOR THIS LOCK TO BE GRANTED和HOLDING THE LOCK(S)。阅读时关注的不是哪条SQL写错了而是“锁的申请顺序”。比如日志显示事务A持有行锁在等待表级锁事务B持有表级锁在等待行锁那就说明两边获取资源的顺序不一致。解决方式可以是统一加锁顺序也可以把其中一个事务改成快照读避免申请写锁。还有一个小技巧如果死锁频繁出现不要只盯着死锁本身先看索引是不是合理。没有索引的UPDATE会把扫描范围内所有行都锁住形成“表锁效果”死锁概率会指数级上升。加一个合适的二级索引把更新范围缩小很多死锁能直接消失。5.4 我总结的事务边界设计清单综合考虑上面的经验我在实际项目里会要求团队遵守这套清单事务范围最小化只把必须保证原子性的写操作放进事务读多写少的查询尽量放到事务外。事务内禁止外部调用HTTP、RPC、文件操作、消息发送全部放到事务提交后。控制事务内数据量批量更新建议分批例如每批500~1000行分批提交。优先使用主键或唯一索引更新避免大范围扫描造成的间隙锁扩散。设置合理超时innodb_lock_wait_timeout默认50秒业务侧一般配置为小一点的数值比如3秒或5秒避免请求无限期悬挂。统一资源访问顺序涉及多行更新的场景先按主键排序再逐个更新能显著减少死锁。开启监控sys.schema_unused_indexes能帮你找到没被利用的索引锁范围过大的问题往往从这里暴露。6. 事务的横向延伸单机事务到了分布式环境怎么走6.1 为什么说“单机无忧分布式处处是坑”在单机MySQL里redo log、undo log、锁、MVCC是一套完整的闭环由InnoDB统一管理。一旦拆成多个服务比如订单服务用订单库、库存服务用库存库、用户服务用用户库每个数据库都有自己的事务单机事务就管不住“跨库一致性”了。举个最常见的例子一个下单操作要扣库存数据库的库存同时写订单数据库的订单。库存库的事务已提交订单库的事务失败回滚两个库的状态就永远对不上了。这不是MySQL本身能解决的问题它只能保证单个节点上的ACID。6.2 XA两阶段提交和它的问题MySQL本身是支持XA的原理是引入一个协调者让所有参与节点分成两阶段准备阶段协调者通知各个节点执行事务执行完成但先不提交各节点返回“准备好了”。提交阶段协调者收到所有“准备好”后再通知所有节点真正提交。XA的优点是强一致性有保障但它有个致命弱点在准备阶段所有参与者都要持有资源锁协调者在提交阶段如果宕机所有参与者都会卡在“已准备等待提交”的状态释放不了锁。此时整个系统可用性大打折扣。纯XA在跨服务间用得不多更多是同一套MySQL主从环境做跨实例数据迁移时用。生产环境里大家更愿意接受“最终一致”而非“强一致”。6.3 柔性事务方案本地消息表、事务消息、TCC既然强一致代价太高业务侧一般退让为最终一致。常见方案有三种本地消息表把“写业务数据”和“写消息记录”放在同一个本地事务里。业务提交后通过一个定时任务把消息表里未发送的消息推送到MQ消费方处理成功后回执发送方再更新消息状态。两个系统各自保证本地事务最终靠消息重试对齐。事务消息以消息队列自带的事务消息机制为例发送方先发一个“半消息”业务逻辑执行完后提交事务再确认这个消息可投递。如果事务回滚半消息会被删除消费者自然收不到。这样避免了业务数据与消息状态“两张皮”。TCCTry-Confirm-Cancel把每个服务操作拆成预留Try、确认Confirm、取消Cancel三步。Try阶段只做资源预留全部预留成功后再执行Confirm任何一个节点失败则执行Cancel回滚。TCC需要业务代码配合实现侵入性较强但能覆盖绝大多数跨服务一致性场景。我对TCC的体会是它把“事务”下沉到了业务层对每个参与方的接口设计能力要求很高。如果团队对业务边界不够清晰强行上TCC代码复杂度会成倍增加维护成本远高于普通接口调用。多数项目其实用不到TCC本地消息表加上消息补偿任务已经能覆盖99%的订单类场景。6.4 有时候不强一致才是正确的降级关于分布式事务我的另一个观点是不是所有场景都必须保证一致。比如“用户修改个人资料”和“生成推荐位缓存”两者之间没有严格的数据依赖完全可以拆成两个独立操作用一个异步任务去刷新缓存。如果因为“想保证强一致”而强制引入分布式事务结果往往是把简单问题复杂化。判断要不要引入分布式事务可以先问三个问题如果数据不一致用户是否立刻感知不一致能否在短时间内自动纠正恢复手段是人工补偿还是系统补偿只要允许短时间不一致并且有可靠的补偿流程最终一致就是更优的架构选择。我自己在实际项目里通常的做法是把核心链路上的强一致需求降到最低对必须强一致的少量场景采用“本地事务 事务消息”的方式并且为所有外部调用准备幂等键。这样既避免了XA的性能风险也能在大多数情况下保证上下游数据收敛。如果你正打算为系统引入分布式事务建议先不要急着选框架把业务里真正需要强一致的点找出来很可能发现根本不需要那么复杂。再补充一个日常调优经验不少年轻人一开始做MySQL事务调优喜欢盯着锁、隔离级别、参数一顿猛调但往往忽略了一个基础问题——SQL没走索引导致锁扩散。先把慢日志打开把long_query_time设为1秒每周检查一次TOP慢SQL优先解决全表扫描再谈锁优化。我见过很多“死锁频繁”的问题调完索引之后直接就消失了连事务代码都没动。这个顺序如果搞反会走很多弯路。
RELATED

相关推荐

大模型重塑移动端自动化测试:从痛点破局到智能落地实践

大模型重塑移动端自动化测试:从痛点破局到智能落地实践

1. 痛点复盘:自动化测试投入产出比为何逐年恶化做了几年移动端测试的同学,多半都会有同一个感受:自动化框架越搭越完善,脚本数量越来越多,但真正节省的时间反而越来越少,维护成本倒是肉眼可见地往上涨。这个…

📅 2026/10/19 14:34:17
Windows Server 2019域控搭建与Kali Linux入域验证指南

Windows Server 2019域控搭建与Kali Linux入域验证指南

最近有个搞运维的朋友问我,说想把Windows Server 2019主域控制器在自己虚拟机里跑起来,又不确定Kali Linux在整套环境里能干什么。我干脆把前两天刚搭好的一套环境拿出来讲透:从Windows Server 2019那边装AD DS、提升域控,到Kali作…

📅 2026/10/19 14:34:17
CentOS 7 系统镜像制作:Mondo Rescue 整机备份与恢复实战

CentOS 7 系统镜像制作:Mondo Rescue 整机备份与恢复实战

1. 为什么还要折腾 Mondo 做系统镜像1.1 这个需求到底从哪来机房里有几台老业务机,跑的是 CentOS 7,系统盘上装了一堆自编译的中间件、定制过的内核模块、还有几个早就不维护但业务离不开的 Python 2 脚本。业务方要求:万一机器挂了&#xff…

📅 2026/10/19 14:34:17
MORE NEWS

更多资讯

📰

138个企业Agent翻车案例揭示的生产环境避坑清单

这两年我一直在做企业Agent落地的技术顾问,手里攒了138个在生产环境里翻车的真实案例,从库存预测、财务审核到客服工单处理都有。老实说,每一次翻车看起来各有各的原因,但把日志和复盘记录摊开之后,翻来覆去就是那几类…

📰

企业AI Agent落地难在哪?数据、流程与用户采纳的三重挑战

落地企业 AI Agent 一年,我发现难的不是模型,是这三件事 去年这个时候,我所在团队正式启动了一个企业级 AI Agent 项目,目标很朴素:让内部员工通过自然语言完成日常高频工作,比如查资料、填报表、走审批、拉…

📰

基于JavaWeb的校园卡服务管理系统设计与实现全解析

校园卡服务管理系统这个题,在课程设计和毕业设计里算得上常青树级别的选题了。我前前后后带过不少学生做类似的系统,自己也完整写过一版,就拿基于Javaweb的校园卡服务管理系统的设计与实现这一套来说,它既有足够的功能深度去体现业…

📰

open-code-review:一套高确定性自动化代码评审工具的设计与实践

1. 起因:一次让我崩溃的代码评审经历几个月前,我负责维护的一个项目收到了一条代码评审意见,大意是"这个函数名取的不好,建议改名"。问题是,那位评审人没有给出任何备选名称,也没有说明哪里不清晰…

📰

Java super关键字详解:从内存视角到构造器调用链与多态陷阱

写 Java 的人迟早要和 super 打交道,尤其是刚把继承、重写、多态这几个概念混在一起的时候。很多人第一反应是把它当成和 this 一样的东西——this 指自己,super 指父类,两个都是引用嘛。这个理解大方向没错,但细节上差得很远。等…

📰

Vue面试题核心考点全解析:响应式原理、虚拟DOM与组件通信

1. 为什么Vue面试题总让人“会又不会”1.1 会写页面不等于能答对题目很多朋友找我聊工作机会的时候都会带上一句:“Vue我挺熟的,项目里一直在用,但一看到面试题就没底。”这个感受其实非常普遍,因为Vue是一个上手门槛很低的框架&a…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬