面向数据编程之异步更新 hook

Posted 前端新视野

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向数据编程之异步更新 hook相关的知识,希望对你有一定的参考价值。

  在面向数据编程的前端框架里面,都会出现数据变量更新之后的动作,不管是 vue 里面用来操作 DOM 的 $nextTick 还是 react 组件内部的 setState


首先呢,关于 nextTick,其实熟悉 nodejs 的应该不陌生这个 api 名字

process.nextTick


语法:

process.nextTick(callback[, ...args])  


用途:

将 callback 添加到下一个时间点的队列


它的使用方式,文档也说明的很清晰:

console.log('start');process.nextTick(() => { console.log('nextTick callback');});console.log('scheduled');


最终输出的:

// start// scheduled// nextTick callback


  说了半天,nodejs 和这些 web 端的面向数据编程框架肯定不太一样。

比如 vue 本身自己有一套数据变量发生变化之后的更新策略,我们先看看这个更新流程。


  现在我们在 .vue 文件里面 script 里面:

export default { data () {    return {      msg'xinshiye'    } }}


在 vue 内部:我们先从 initState 函数开始

initState(vm)


在 initState 内部:会调用 initData 函数

function initState (vm{  // ...  if (opts.data) {    initData(vm)  }}


然后看 initData 函数内部:


第一步获取 data 里面的变量,同时也给 vm._data 赋值

vm.$options.data 其实是一个函数

function initData (vm{ var data = vm.$options.data data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {};}


getData 函数内部:

function getData (data, vm{  try {    return data.call(vm, vm)  } catch (e) {   //  } finally {   //  }}


再回到 initData 函数内部:

observe(data, true /* asRootData */);


observe 函数内部:

会判断 value 的类型,比如

不是对象直接 return

是一个 VNode 也直接 return

function observe (value, asRootData) { if (!isObject(value) || value instanceof VNode) {    return }}


class Observer

内部会调用 def 函数

然后判断 value 是数组格式还是其他,我们这边是对象,直接会走 walk

var Observer = function Observer (value{ def(value, '__ob__', this) if (Array.isArray(value)) {  } else {   this.walk(value)  }}


def 函数内部:

它用来 Define a property.

通过 Object.defineProperty 来定义

function def(obj, key, val, enumerable{  Object.defineProperty(obj, key, {    value: val,    enumerable: !!enumerable,    writabletrue,    configurable: true  })}


walk 函数内部:

其实就是循环 obj 对象,然后循环调用 defineReactive$$1

Observer.prototype.walk = function walk (obj) { var keys = Object.keys(obj) for (var i = 0; i < keys.length; i++) { defineReactive$$1(obj, keys[i])  }}


defineReactive$$1 函数内部:

function defineReactive$$1 ( obj, key, val, customSetter,  shallow  ) { var property = Object.getOwnPropertyDescriptor(obj, key)}


这里第一步 property:

{ value: "xinshiye" writable: true enumerable: true configurable: true}


先看看之前有没有定义 get 和 set:


var getter = property && property.get;var setter = property && property.set;


如果没有,取 val:

if ((!getter || setter) && arguments.length === 2) {  val = obj[key];}


然后再设置统一的 get 和 set

Object.defineProperty(obj, key, { enumerable: true,  configurabletrue,  get: function reactiveGetter () {  },  set: function reactiveSetter (newVal) {  }}


我们先看 get:

var value = getter ? getter.call(obj) : valif (Dep.target) {  dep.depend()}return value


depend 函数:

Dep.prototype.depend = function depend () { if (Dep.target) { Dep.target.addDep(this); }}


再看 set 函数:

第一步获取之前的值:

var value = getter ? getter.call(obj) : val


判断新值和老值是否相等

相等的化,直接返回

if (newVal === value || (newVal !== newVal && value !== value)) { return}


然后调用:

dep.notify();


看看 notify 函数:

Dep.prototype.notify = function notify () { var subs = this.subs.slice() for (var i = 0, l = subs.length; i < l; i++) { subs[i].update() }}


update 属于 Watcher,内部调用了 queueWatcher

Watcher.prototype.update = function update ({ queueWatcher(this);}


一个 watcher 大概的样子:

包含 id 来标识



-----------------------------------------------------------------


核心的 nextTick 函数来了:

var callbacks = []
function nextTick (cb, ctx) { callbacks.push(function () {    if (cb) {     try {     } catch (e) {     }    }  })  if (!pending) { pending = true; timerFunc(); }  // ... 后面还有 Promise 和我们的分支判断没有匹配就不放进去了  }



而所谓的 this.$nextTick 其实来自 renderMixin

function renderMixin (Vue{  Vue.prototype.$nextTick = function (fn{ return nextTick(fn, this) }}


这里的 timerFunc 还是很关键的东西,下篇我们再一起分解

以上是关于面向数据编程之异步更新 hook的主要内容,如果未能解决你的问题,请参考以下文章

PHP面向对象之选择工厂和更新工厂

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

响应式编程是一种异步的声名式的面向数据流的编程范式

Web Spider Fiddler - JS Hook 基本使用

前端面试题之手写promise