A cached WordPress block needs to keep its scripts, styles and behaviour as well as its markup. I compare what a fresh render does with what a cache hit restores before judging the speed improvement. A fast map or product grid is only useful if its controls still work.
The output has neighbours
A block can enqueue a script, register a stylesheet, add interactive state or generate CSS while it renders. If I skip the render on the next request and return only the saved HTML, the page may look right at first but lose its behaviour or styling.
I record the supported render-time effects and replay them on a hit. Handles registered during the render need their definitions restored as well as their names added to a queue. A map with a missing script is not a successful optimisation.
Generated identifiers are part of the problem
WordPress can generate container and element class names during rendering. Combining cached markup from one request with fresh markup from another can introduce collisions or attach styles to the wrong element. I make generated names safe to reuse and retire older entries whenever that naming contract changes.
A cache entry version helps here. When the meaning of the stored payload changes, I want an old entry to become a miss, not to keep limping along until someone notices a layout fault.
Vary by what changes the answer
A block showing different content on different pages cannot always share one cache key. Query context, attributes, pagination and the relevant content version may all matter. Some blocks need a page-specific key; others can safely share output more widely.
Visitor context matters even more. Prices, account information, basket state and personalised content need an explicit eligibility policy. I also skip renders which produce a nonce rather than storing a token for another visitor to receive later.
Invalidation needs the same context
Editing a post should invalidate the fragments which depend on it and the pages containing those fragments. Membership changes can affect a query even when the posts already in its cached result have not changed. A dependency index and a profile version solve different parts of that problem.
I keep the cache lifetime adjustable by block type, since a frequently changing data panel and a stable introduction have different needs. A lifetime change should not require destroying every unrelated cache on the site.
Check behaviour as well as speed
I compare a fresh render with a cache hit, then click the interactive controls, visit a second page and edit the underlying content. I test as a signed-in user and with a basket too. The page is only improved when it is faster and still says and does the right things.