vue钩子函数原理-Vue 钩子函数原理
Vue 的钩子函数到底是个啥玩意儿?别整那些教科书式的大道理,咱就顺着内存和事件去摸鱼。 想象一下,你是一段正在后台跑图的代码,你抓不住,但它随时都可能停下来。Vue 的响应式系统就是那个掌控全局的摄像头,它时刻监控着哪些变量动了。当你写的 `data` 要么 `computed` 里的东西变了,Vue 会立马触发一次“快照”,就像你在原地喊了声“嘿,我动了”,然后浏览器拿着这份快照去同步你的网页。
这时候,`v-for` 里的循环变量、`v-if` 里的条件逻辑,就连你刚刚写的那行 `const x = 10`,都会被这个快照锁住。
要是这行代码是循环里的第一步,那一轮循环终止后,浏览器拿着新的快照把整个 DOM 结构重新渲染一遍。 可是,要是这行代码是循环里的第一步,这第一步实际上还没启动呢。就像你在跑步,还没冲过起点。
要是这行代码是循环里的第三步,那你前两步就已经跑完了,浏览器拿着前两步的快照持续渲染。
这种机制叫“增量渲染”,它是 Vue 最核心的魔法,也是它充足快的根本。 那具体如何触发的呢?核心就在那三个函数:`data`、`computed`、`watch`(还有它们各自的 `toJson` 和 `fromJson`)。数据变了,触发的是 `toJson`;计算出来的值变了,触发的是 `computed` 的 `toJson`。
这两者底层逻辑是一样的:先把变量 ID 转成字符串,比如 `10` 变成 `x`,再把这个字符串放进一个临时的哈希表里。 再看 `methods`。当你在方式里用了 `this`,Vue 会把 `this` 对象的属性变成字符串,比如 `data`、`computed`、`watch`、`methods`,然后按顺序拼成 `dataComputedWatchMethods` 这个超级长的字符串。字符串一旦变,哈希表就变了。 故此,最直接的触发器实际上是 `isChanged`。它有两个状态:`dataChanged` 和 `computedChanged`。当 `dataChanged` 为真时,浏览器拿着 `dataComputedWatchMethods` 这个哈希表,去遍历 DOM 节点,把 `v-for` 里的变量 ID 和 DOM 节点里的原始值串起来。
要是串起来了,说明变量 ID 变了,那就重新渲染。 再说说 `watch`。watch 就像是一个定时炸弹。当你把这个 watch 绑定了某个变量,比如变量的 `a` 变了,它会立马执行。它这里有个小细节,watch 里也会触发 `computedChanged`。
也就是说,你的 watch 逻辑里写的那一行代码,判断变量 `a` 变了,然后去触发 `computedChanged`。
这就像你在自己家里打了一盆水,水洒出来,顺便把整个客厅的开关也切换了一下。 再讲讲 `methods` 里的特殊写法。
要是你写的是 `computed = 10`,Vue 会把它当成 `this.computed = 10`。
这时候,`computed` 这个变量本身就没有变,但它的 `toJson` 变了。
这会让 `computedChanged` 触发。
反之,要是写的是 `this.computed = 10`,Vue 是在 `this.computed` 这个对象上赋值,对象变了,自然触发 `computedChanged`。 还有 `data` 里的陷阱。大量人当作 `data` 变了会触发 `computedChanged`,实际上不对。`data` 变触发的是 `dataChanged`。`data` 变的时候,会去遍历 `this.data` 里的值,要是值变了,就触发 `dataChanged`。而 `computedChanged` 是专门针对 `computed` 变量的。 `watch` 和 `data` 的区别在于一个单向,一个双向。`data` 变,`computed` 会变(单向),`watch` 变,`computed` 不变(双向)。但 `watch` 内部调用 `computed` 的时候,会触发 `computedChanged`。
故此,`data` 变了,`computed` 变了,`computedChanged` 被触发,然后 `computed` 内部调用的东西会修改自己的 ID,进而触发 `dataChanged`。
这是一个整个的闭环。 举个例子,假设你在写一个列表,每个元素里都有个工夫。 ```javascript data = { list: [ { id: '1', time: Date.now() }, { id: '2', time: Date.now() } ] } ``` 然后有个 watch: ```javascript watch: { list: { handler(newVal) { // 这里把 newVal 里的每个工夫 ID 都转成了字符串,放进临时哈希表 // newVal[0].time 变成了 t1, newVal[1].time 变成了 t2 // t1 和 t2 的 ID 变了,故此 dataChanged 触发 // 浏览器拿着 t1 和 t2 的 ID,去 DOM 里找到对应的元素, // 发现 DOM 里的工夫字符串 ID 没变,那就重新渲染列表 const temp = {} newVal.forEach(item => { temp[item.time] = item }) // 这里的 temp 变化触发了 computedChanged // computedChanged 触发后,computed 里的 items 变了 // items 变了,又触发了 dataChanged }, deep: true } } ``` 在这个例子中,`list` 变,触发 `dataChanged` 和 `computedChanged`。
要是 `computed` 里用的是 `computedData` 这个变量指代 `list`,那么 `computedData` 变了,自然触发 `dataChanged`。 再给个更生活化的例子。你写了一个 `setup`,里面有个 `computed` 算出身高,然后 `data` 里有个身高变量。 ```js export default { computed: { height: function() { return 180 } }, data() { return { height: 180 } }, methods() { return { changeHeight: function(newHeight) { this.height = newHeight // 这行代码本身没变,但 this.height 是对象属性 // 这行代码触发了 computedChanged } } } } ``` 当你调用 `changeHeight` 传入 `190` 时,Vue 执行这行代码。`this.height = 190` 执行了。出于 `height` 是 `computed` 算出来的,故此 `computedChanged` 被触发了。触发 `computedChanged` 后,`computed` 里的 `height` 变量 ID 变了(假设 ID 是 `h`),然后 `computed` 内部调用的 `methods.changeHeight` 里的 `this.height` 也被替换成了新的 ID,便再次触发 `dataChanged`。 这就解释了为啥 `computed` 的 `toJson` 变化那么关键。它不只是是数据变了,它是数据“讲话”的方式变了。 最终总结一下。Vue 的钩子函数本质上就是一个监控系统。它监控的是变量 ID 和变量值的映射关系。一旦这个映射关系变了,浏览器就会拿着新的映射关系去刷新 DOM。`data` 变触发 `dataChanged`,`computed` 变触发 `computedChanged`,`watch` 内部调用 `computed` 会触发 `computedChanged`。 故此,别总想着去写那些复杂的逻辑去“看”Vue 到底如何运行的。
只要你把变量 ID 转成字符串,放进一个临时的哈希表里,哪儿变了,哪儿就重新渲染了。
这就是 Vue 一切的原理,好办,直接,就像你在原地打转一样。
声明:演示网站所有内容,若无特殊说明或标注,均来源于网络转载,仅供学习交流使用,禁止商用。若本站侵犯了你的权益,可联系本站删除。
