精华内容
下载资源
问答
  • iPod可以说是在iPhone...假如你是一个iPod的死忠粉丝,那么这个小玩意可以让你如愿以偿,它竟然可以把Apple Watch一秒变成iPod。把你的Apple Watch变成iPod这是新款Apple Watch最酷也是最“怀旧”的外壳了! Joyce...

    iPod可以说是在iPhone出现之前,苹果最经典的产品。只可惜随着手机和智能手表的出现,让随身听音乐这件事儿变得更加简单,iPod的生存空间也越来越狭窄,直至最后走向没落。假如你是一个iPod的死忠粉丝,那么这个小玩意可以让你如愿以偿,它竟然可以把Apple Watch一秒变成iPod。

    把你的Apple Watch变成iPod

    这是新款Apple Watch最酷也是最“怀旧”的外壳了! Joyce Kang和CO设计实验室的Pod Case为Apple Watch上了一堂历史课,向经典iPod系列表示致敬的作品。其尺寸和iPad Nanos中的屏幕尺寸相匹配,而它的机身仅略微厚一点。 这款Pod手机壳采用硅胶制成,可滑过Apple Watch机身,为其带来时髦的回归感,同时让您可以随意使用手表。

    硅胶外壳可以随时切换

    显然,前面的微动轮是假的(虽然它可以通过蓝牙连接,但它可以工作),但手表可以很容易地使用触摸屏导航。当然最重要的一点,它配备扬声器!

    设计师:Joyce Kang&C.O设计实验室

    其尺寸和iPad Nanos差不多

    样子还是很酷的

    向经典iPad致敬

    不妨碍无线充电

    很小巧很便携

    按键是虚拟的,一切都在屏幕操控

    还是很独特的创意

    不同的颜色可选

    Joyce Kang&C.O设计实验室作品

    02Apple Watch Series 4 44mm详细参数

    展开全文
  • 本次文章的主题是 computed(计算属性) 和 watch(侦听器),代码基于 Vue 2.6.11 版本,和 2.5 还是有一些区别。 用法回顾 我们先来简单回顾下 computed 和 watch 的用法。 computed export default { name: .

    学如逆水行舟,不进则退!当今社会,很美好,也很残酷。一般软件或互联网公司,大多数的业务其实是没有很高的难度,所以员工也没有什么机会去提升自己。不过值得庆幸的是,编程界开源风气盛行,功夫秘籍随手可得,而代价仅是你的时间。

    本次文章的主题是 computed(计算属性) 和 watch(侦听器),代码基于 Vue 2.6.11 版本,和 2.5 还是有一些区别。

    用法回顾

    我们先来简单回顾下 computed 和 watch 的用法。

    computed

    export default {
        name: 'App',
        data() {
            return {
                user: {
                    name: 'Jack',
                    job: 'UI',
                },
            };
        },
        computed: {
            name() {
                return 'I am ' + this.user.name;
            },
        },
    };
    

    在 computed 属性中,我们定义了 name,其值来自 data 中 user.name。只要 user.name 的值发生了变化,那么 computed 中的 name 就行改变。

    定义 computed 的好处就是我们可以定一个变量,很方便引用“其它的对象数据封装处理”后的值。并且 computed 中的属性是响应式的,值改变了,会触发 DOM 的更新。如果依赖的值没有改变,computed 的值不会重新计算,而是复用上一次缓存的值。

    watch

    export default {
        name: 'App',
        data() {
            return {
                color: 'red'
            };
        },
        watch: {
            color(newVal) {
                alert('Color is changed', newVal)
            }
        }
    };
    

    computed 主要关注的是值改变后的新值和缓存功能,而 watch 主要关注值改变后,我要做什么动作。

    源码分析

    如果读者从来没有阅读过 Vue 相关的源码,接下来的内容可能有点云里雾里。博主会尽量把内容放在 computed 和 watch 这一块的流程上,辅助少量的其他模块的细节。

    computed

    Vue 在初始化时,会执行 _init,该方法在 Vue 原型对象上定义的。

    Vue.prototype._init = function (options?: Object) {
      const vm: Component = this
    
      // 省略了一大波细节
    
      // expose real self
      vm._self = vm
      initLifecycle(vm)
      initEvents(vm)
      initRender(vm)
      callHook(vm, 'beforeCreate')
      initInjections(vm) // resolve injections before data/props
      initState(vm)
      initProvide(vm) // resolve provide after data/props
      callHook(vm, 'created')
    
      if (vm.$options.el) {
        vm.$mount(vm.$options.el)
      }
    }
    

    这个方法里面有执行一个函数 initState(vm) ,包含如 data、computed、watch 等属性的处理逻辑。

    export function initState (vm: Component) {
      vm._watchers = []
      const opts = vm.$options
      if (opts.props) initProps(vm, opts.props)
      if (opts.methods) initMethods(vm, opts.methods)
      if (opts.data) {
        initData(vm)
      } else {
        observe(vm._data = {}, true /* asRootData */)
      }
      if (opts.computed) initComputed(vm, opts.computed)
      if (opts.watch && opts.watch !== nativeWatch) {
        initWatch(vm, opts.watch)
      }
    }
    

    从上面代码中,我们在最后几行代码可以看到 computed 和 watch 的处理。顺便一提,这里的 nativeWatch 判断主要是用来处理火狐浏览器上的一个兼容性问题,火狐浏览器中其默认对象的原型上有一个原生的 watch 属性。

    我们先来看看 initComputed() 这个方法。

    function initComputed (vm: Component, computed: Object) {
      // $flow-disable-line
      const watchers = vm._computedWatchers = Object.create(null)
      // computed properties are just getters during SSR
      const isSSR = isServerRendering()
    
      for (const key in computed) {
        const userDef = computed[key]
        const getter = typeof userDef === 'function' ? userDef : userDef.get
    
        if (!isSSR) {
          // create internal watcher for the computed property.
          watchers[key] = new Watcher(
            vm,
            getter || noop,
            noop,
            computedWatcherOptions
          )
        }
    
        if (!(key in vm)) {
          defineComputed(vm, key, userDef)
        } else if (process.env.NODE_ENV !== 'production') {
          if (key in vm.$data) {
            warn(`The computed property "${key}" is already defined in data.`, vm)
          } else if (vm.$options.props && key in vm.$options.props) {
            warn(`The computed property "${key}" is already defined as a prop.`, vm)
          }
        }
      }
    }
    

    首先遍历所有的计算属性,为每个属性定一个 watch,这里的 watch 是 计算 watch,而非 渲染 watch。接下来,再来看下 defineComputed 方法。

    export function defineComputed (
      target: any,
      key: string,
      userDef: Object | Function
    ) {
      const shouldCache = !isServerRendering()
      if (typeof userDef === 'function') {
        sharedPropertyDefinition.get = shouldCache
          ? createComputedGetter(key)
          : createGetterInvoker(userDef)
        sharedPropertyDefinition.set = noop
      } else {
        sharedPropertyDefinition.get = userDef.get
          ? shouldCache && userDef.cache !== false
            ? createComputedGetter(key)
            : createGetterInvoker(userDef.get)
          : noop
        sharedPropertyDefinition.set = userDef.set || noop
      }
      Object.defineProperty(target, key, sharedPropertyDefinition)
    }
    

    注意 Object.defineProperty(target, key, sharedPropertyDefinition) 这行代码,就是进行数据代理,让我们可以通过 vm 实例上的同名属性来访问 computed 中定义的属性。

    这里比较复杂的就是 sharedPropertyDefinition 的 get 方法,即 createComputedGetter 方法的返回值。

    function createComputedGetter (key) {
      return function computedGetter () {
        const watcher = this._computedWatchers && this._computedWatchers[key]
        if (watcher) {
          // 脏数据条件下,会重新计算数据属性的值
          // 这是计算属性缓存的核心
          if (watcher.dirty) {
            watcher.evaluate()
          }
          // Dep.target 为渲染 watch,先不用搞明白为什么
          // 这里是计算属性为什么会影响数据变化的核心
          if (Dep.target) {
            watcher.depend()
          }
          return watcher.value
        }
      }
    }
    

    先来看下 watcher.evaluate() 的逻辑

    export default class Watcher {
      /**
       * Evaluate the value of the watcher.
       * This only gets called for lazy watchers.
       */
      evaluate () {
        this.value = this.get()
        this.dirty = false
      }
    }
    

    首先对计算属性就行求值,在我们例子中就是 return 'I am ' + this.user.name;,然后将 dirty 设为 false。这样,如果再次访问计算属性的值时,就不会重新求值了(会看上面 computedGetter 的逻辑)。至于 dirty 什么时候会改变,我们后面再说。

    接下来,我们看下 this.get() 的逻辑。为了关注主线,代码做了简化。

    export default class Watcher {
      /**
       * Evaluate the getter, and re-collect dependencies.
       */
      get () {
        // 将 Dep.target 的值设为 计算 watch
        pushTarget(this)
        let value
        const vm = this.vm
        try {
          value = this.getter.call(vm, vm)
        } finally {
          // 将 Dep.target 的值还原
          popTarget()
          this.cleanupDeps()
        }
        return value
      }
    }
    

    当我们执行 this.getter.call(vm, vm) 时,会访问 this.user.name,所以会触发其依赖收集(这是 Vue data 响应式原理的一部分,如果不懂,建议先找资料看一看)。而这时候 Dep.target 的值为 计算 watch,依赖收集完后,双方互相保持对方的引用。即 计算 watch 中有 this.user.name 的 dep,而 this.user.name 的 dep 中有 计算 watch

    export function defineReactive (
      obj: Object,
      key: string,
      val: any,
      customSetter?: ?Function,
      shallow?: boolean
    ) {
      const dep = new Dep()
    
      Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          const value = getter ? getter.call(obj) : val
          // 这里收集依赖
          // 也就是为什么 name 的值改变,会通知计算属性
          // 因为计算 watch 也加入到了 dep 的列表中
          if (Dep.target) {
            dep.depend()
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          dep.notify()
        }
      })
    }
    

    当 name 值改变了,会触发 set,然后通知 计算 watch,执行 update 方法。

    export default class Watcher {
      update () {
        /* istanbul ignore else */
        if (this.lazy) {
          this.dirty = true
        } else if (this.sync) {
          this.run()
        } else {
          queueWatcher(this)
        }
      }
    }
    

    因为 计算 watch 的 lazy 为 true,所以会执行 this.dirty = true。这里大家注意,它仅仅把 dirty 设为了 true,并没有真正重新求值。只有当计算属性再次被访问到时,才会走求值的逻辑。

    执行完了 evaluate,我们再来看看 depend。

    function computedGetter () {
      const watcher = this._computedWatchers && this._computedWatchers[key]
      if (watcher) {
        if (watcher.dirty) {
          watcher.evaluate()
        }
        // 注意这个时候的 Dep.target 为 渲染 watch
        if (Dep.target) {
          watcher.depend()
        }
        return watcher.value
      }
    }
    

    这个时候的 Dep.target 为 渲染 watch,可能有些人不太明白,这里稍微补充下。前面在执行 watcher.evaluate() 是会走到 watch.get 方法,而 get 方法调用了 pushTarget(this)

    Dep.target = null
    const targetStack = []
    
    export function pushTarget (target: ?Watcher) {
      targetStack.push(target)
      Dep.target = target
    }
    
    export function popTarget () {
      targetStack.pop()
      Dep.target = targetStack[targetStack.length - 1]
    }
    

    这里 Vue 用一个栈维护了多个 watch 的状态,大致的流程是:

    1. []
    2. [渲染 watch]
    3. [渲染 watch, 计算 watch]
    4. [渲染 watch]
    5. []

    因为读取都是从 mount 之后开始,所以 渲染 watch 被第一个加进去。

    我们知道是 Dep.target 为 渲染 watch 之后,再来看下 watcher.depend() 的逻辑。

    export default class Watcher {
      depend () {
        let i = this.deps.length
        while (i--) {
          this.deps[i].depend()
        }
      }
    }
    

    这里直接是在 name 的 dep 中加上了 渲染 watch。如果模板中同时存在了 data 的 name 和计算属性的 name,渲染 watch 只会添加一次。

    这里和 2.5 版本有比较大的区别,因为 2.5 版本是为计算属性加了一个 dep,让渲染 watch 去订阅这个依赖。

    有了这段逻辑,就可以让计算属性变成响应式的了。

    watch

    说完 computed ,我们再来看看使用频率也比较多的 watch 实现原理。

    function initWatch (vm: Component, watch: Object) {
      for (const key in watch) {
        const handler = watch[key]
        if (Array.isArray(handler)) {
          for (let i = 0; i < handler.length; i++) {
            createWatcher(vm, key, handler[i])
          }
        } else {
          createWatcher(vm, key, handler)
        }
      }
    }
    

    和 computed 一样,先是遍历所有属性,然后判断属性的值是否为数组。平时我们肯定很少用数组形式,不过 Vue 是支持在一个属性上定一个多个 handle 的。

    function createWatcher (
      vm: Component,
      expOrFn: string | Function,
      handler: any,
      options?: Object
    ) {
      if (isPlainObject(handler)) {
        options = handler
        handler = handler.handler
      }
      if (typeof handler === 'string') {
        handler = vm[handler]
      }
      return vm.$watch(expOrFn, handler, options)
    }
    

    createWatcher 也比较简单,因为我们 watch 的属性可以是函数、对象,也可以是字符串,这里做了一层统一处理,核心在 $watch 上。$watch 是在 Vue 原型上定义的一个方法。

    export function stateMixin (Vue: Class<Component>) {
      Vue.prototype.$watch = function (
        expOrFn: string | Function,
        cb: any,
        options?: Object
      ): Function {
        const vm: Component = this
        if (isPlainObject(cb)) {
          return createWatcher(vm, expOrFn, cb, options)
        }
        options = options || {}
        // 这是一个 用户 watch
        options.user = true
        // 最终是实例化了一个 Watch 对象
        const watcher = new Watcher(vm, expOrFn, cb, options)
        if (options.immediate) {
          try {
            cb.call(vm, watcher.value)
          } catch (error) {
            handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)
          }
        }
        return function unwatchFn () {
          watcher.teardown()
        }
      }
    }
    

    从代码中,我们不难发现,watch 属性最终也是实例化了一个 watch 对象,不同的是,这里是一个 用户 watch。我们平时在项目中,也是可以自己手动调用 this.$watch 方法。所以这里取名 user watch,也是很贴合实际使用的。

    export default class Watcher {
      constructor (
        vm: Component,
        expOrFn: string | Function,
        cb: Function,
        options?: ?Object,
        isRenderWatcher?: boolean
      ) {
        // parse expression for getter
        if (typeof expOrFn === 'function') {
          this.getter = expOrFn
        } else {
          // 用户 watch 会走这部分逻辑
          this.getter = parsePath(expOrFn)
          if (!this.getter) {
            this.getter = noop
          }
        }
        this.value = this.lazy
          ? undefined
          : this.get()
      }
    }
    

    实例化 watch 时,会执行 this.getter = parsePath(expOrFn)

    const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`)
    export function parsePath (path: string): any {
      if (bailRE.test(path)) {
        return
      }
      const segments = path.split('.')
      return function (obj) {
      	// 这里的 for 循环主要处理嵌套属性,如:'list.user.name'
        for (let i = 0; i < segments.length; i++) {
          if (!obj) return
          obj = obj[segments[i]]
        }
        return obj
      }
    }
    

    parsePath 也很简单,通过函数柯里化,返回了一个新的函数。新函数 obj 参数在 watch 构造函数中是 vm 实例。

    之后执行 this.get(),把 用户 watch 添加到依赖的数据的 dep 中。顺便一提,computed 的属性也是可以 watch 的。

    export default class Watcher {
      get () {
        pushTarget(this)
        let value
        const vm = this.vm
        try {
          value = this.getter.call(vm, vm)
        } catch (e) {
          if (this.user) {
            handleError(e, vm, `getter for watcher "${this.expression}"`)
          } else {
            throw e
          }
        } finally {
          // 相信很多初学者,遇到过这个问题
          // 监听某个复杂对象时,改变了深层的属性,没有触发 watch 的回调
          // 在定义时,我们需要把 deep 设为 true
          if (this.deep) {
            traverse(value)
          }
          popTarget()
          this.cleanupDeps()
        }
        return value
      }
    }
    

    如果依赖的数据变化,就会触发 watch 的 update 方法。

    export default class Watcher {
      update () {
        /* istanbul ignore else */
        if (this.lazy) {
          this.dirty = true
        } else if (this.sync) {
          this.run()
        } else {
          // 默认情况会进入这段逻辑
          queueWatcher(this)
        }
      }
    
      run () {
        if (this.active) {
          const value = this.get()
          if (
            value !== this.value ||
            isObject(value) ||
            this.deep
          ) {
            // set new value
            const oldValue = this.value
            this.value = value
            // 如果是 用户 watch,那么就会执行传入的回调函数。
            if (this.user) {
              try {
                this.cb.call(this.vm, value, oldValue)
              } catch (e) {
                handleError(e, this.vm, `callback for watcher "${this.expression}"`)
              }
            } else {
              this.cb.call(this.vm, value, oldValue)
            }
          }
        }
      }
    }
    

    因为是 用户 watch,最终会执行 queueWatcher(this),这个方法就不展开讲了,读者可以自行查看源码。这个方法内部会执行 watch.run() 方法,最终执行在 watch 上定义的处理函数。

    总结

    不管是 computed,还是 watch,最终都是通过实例化不同类型的 Watch 对象来实现数据监听的。目前来看,Vue 主要有三种 Watch:

    1. 渲染 Watch
    2. 计算 Watch
    3. 用户 Watch

    搞清楚了这些 Watch 的区别,那么 Vue 的响应式部分基本就理解了。

    Vue 设计不得不说确实很精妙,有很多学习的地方。但是随着功能的增加,边界情况和兼容性问题的处理,以及更精简的封装,让初学者越来越难以阅读。

    展开全文
  • vue3中的watch

    2021-12-07 11:10:11
    vue3中的watch比起vue2中的watch有了些许的变化,我们使用composition api来写 基本数据类型的监视: <template> 当前求和为:{{ sum }} <button @click="sum++">点我++</button> </template...

    vue3中的watch比起vue2中的watch有了些许的变化,我们使用composition api来写

    基本数据类型的监听,在vue3中被ref定义的数据通常在使用时需要在后面加一个.value,但是这里需要监听整个RefImpl对象才能起作用,所以说在写watch时不需要加.value

    <template>
      当前求和为:{{ sum }}
      <button @click="sum++">点我++</button>
    </template>
    
    <script>
    import { ref, watch } from "vue";
    export default {
      name: "App",
      setup() {
        let sum = ref(0);
        watch(sum, (newValue, oldValue) => {
          console.log("sum变了", newValue, oldValue);
        },{immediate:true});
        return {
          sum,
        };
      },
    };
    </script>
    

    vue3中的watch变成了函数形式,有三个参数,第一个是监听的对象,第二个是监听的回调,由于setup不需要考虑this的指向问题,所以可以直接在watch中写箭头函数,第三个是监听的配置项;如果有两个数据需要监听可以直接写成两个watch函数

    watch(sum, (newValue, oldValue) => {
     	console.log("sum变了", newValue, oldValue);
    });
    watch(msg, (newValue, oldValue) => {
      	console.log("msg变了", newValue, oldValue);
    });
    

    或者把监听的对象写成一个数组,这样写的话,oldValue和newValue全都会变成数组

    watch([sum,msg], (newValue, oldValue) => {
       	console.log("sum或msg变了", newValue, oldValue);
    });
    

    对象使用watch和基本数据类型是一样的,如果使用reactive定义的对象可以直接写,但是使用ref定义的对象需要加.value,这是因为监视对象时要对这个对象的Proxy对象生效,如果用ref来定义对象它的value属性才是这个对象的Proxy。如果写了.value那么他对Proxy的监视默认是深度监视,且无法关闭,配置deep:false无效,如果不写.value这里可以在后面加上deep:true,实现深度监视。

    <template>
      <h2>姓名:{{ obj.name }}</h2>
      <h2>年龄:{{ obj.age }}</h2>
      <button @click="obj.age++">年龄加加</button>
      <h2>朋友名字:{{obj.friend.name}}</h2>
      <h2>朋友年龄:{{obj.friend.age}}</h2>
      <button @click="obj.friend.name += '@'">朋友名字变化</button>
    </template>
    
    <script>
    import { reactive, watch, ref } from "vue";
    export default {
      setup() {
        let obj = reactive({
          name: "张三",
          age: 20,
          friend:{
            name:'李四',
            age:21
          }
        });
        watch(obj, (newValue, oldValue) => {
          console.log("obj改变了", newValue, oldValue);
        });
        // let obj = ref({
        //   name: "张三",
        //   age: 20,
        //   sex: "男",
        //   friend:{
        //     name:'李四',
        //     age:21
        //   }
        // });
        //如果使用ref来定义对象,watch需要写成以下两种形式才能生效
        // watch(()=>obj.value, (newValue, oldValue) => {
        //   console.log("年龄改变了", newValue, oldValue);
        // });
        // watch(()=>obj, (newValue, oldValue) => {
        //   console.log("年龄改变了", newValue, oldValue);
        // },{deep:true});
        return {
          obj,
        };
      },
    };
    </script>
    
    

    但是这里对对象的监听又有点问题,数据改变时无法拿到oldValue,使用reactive定义的对象会出现这个问题,由于ref在定义响应式对象时也调用了reactive中的方法,所以这个问题目前还无法直接解决,可以通过直接监听对象中的属性来解决,或者把想要监听的属性通过ref来定义
    数据改变但是oldValue和newValue是相同的
    如果只对对象中的某个属性进行监听,需要把第一个参数写成函数的形式才会生效

    watch(()=>obj.age, (newValue, oldValue) => {
     	console.log("年龄改变了", newValue, oldValue);
    });
    

    如果要监听对象中的多个属性可以把第一个参数写成数组的形式

    watch([()=>obj.age,()=>obj.name], (newValue, oldValue) => {
     	console.log("年龄或者名字改变了", newValue, oldValue);
    });
    

    但是如果对象是嵌套多层的,直接监听最外侧对象可以检测到整个对象中的数据的变化

     watch(obj, (newValue, oldValue) => {
      	console.log("对象改变了", newValue, oldValue);
     });
    

    如果想要检测对象中的对象是否发生变化,直接写是无效的

     watch(()=>obj.friend, (newValue, oldValue) => {
       	console.log("年龄改变了", newValue, oldValue);
     });//无法检测friend对象中的数据变化
    

    此时需要加上配置项deep:true,监听奏效

    watch(()=>obj.friend, (newValue, oldValue) => {
      	console.log("朋友改变了", newValue, oldValue);
    },{deep:true});
    

    vue3中的监听最好用的还是watchEffect,可以自动识别回调中使用了哪个数据来对该数据进行监听,有点像计算属性,但是computed注重的是计算产生的值,所以需要写返回值,watchEffect注重数据变化时回调函数的执行,不需要返回值。

    watchEffect(()=>{
    	//可以直接写回调的逻辑,用到了哪个数据对哪个数据实现监听,只要回调中写到的数据发生变化,方法就会重新执行一次      
    })
    
    展开全文
  • etcd watch原理

    千次阅读 2021-04-11 10:57:01
    etcd watch原理服务初始化接收 watch 请求1.接收 watch 请求 recvLoop2. 接收 watch 请求 sendLoopMVCC watchwatch方法:MVCC 消息生成newWatchableStore方法慢速处理 粗浅了解,如有错误之处,请指教! 服务初始...


    在这里插入图片描述

    粗浅了解,如有错误之处,请指教!
    在这里插入图片描述

    服务初始化

    etcd 启动时会注册 WatchServer[1], pb.WatchServer 用于处理 watch 请求

    接收 watch 请求

    1. 每一个 watch 流都创建一个 serverWatchStream 结构体
    2. 开启两个 goroutine, sendLoop 用于发送 watch 消息到流中,recvLoop 接受请求
    3. select 阻塞直到流关闭,或是超时退出。

    1.接收 watch 请求 recvLoop

    recvLoop 从 gRPCStream 读出 req, 然后分别处理类型为 CreateRequest, CancelRequest, ProgressRequest 的情况

    1. CreateRequest: 监听的可能是一个范围,所以构建 key 和 RangeEnd. 处理 StartRevision, 如果为 0, 那么使用当前 系统最新的 Rev+1. 调用 mvcc 层的 watchStream.Watch, 返回一个 watchid, 将这个 id封装到watchResponse,再将watchResponse 写到 ctrlStream
    2. CancelRequest: 还是调用 mvcc 层的 watchableStore.Cancel 取消订阅,然后清除状态信息
    3. ProgressRequest: broadcast 广播当前系统的 Rev 版本

    2. 接收 watch 请求 sendLoop

    在 watchid 生成前,可能就有消息触发了,此时还没有 id, 所以消息会堆积到 pending 中。整个函数主要从 mvcc.watchStream.Chan() 中处理读取订阅的消息,处理 ctrlStream 控制消息和处理 progressTicker

    1. Chan(): 如果 needPrevKV, 需要填充。watchid 不存在的话,暂时移到 pending 队列中。Fragment 查看是否需要分包,这里阈值是 1.5M, 不需要的话直接调用 sws.gRPCStream.Send 发送即可。如果有数据发送的情况,sws.progress[wresp.WatchID] 置为 false, 不用发进度消息
    2. ctrlStream: 读取控制消息,这里只要是获取 watchid, 然后发送堆积的 pending 消息
    3. progressTicker: 定期调用 RequestProgress 生成进度消息,把当前 Rev 发给 client

    MVCC watch

    这一块主要是看 mvcc.watchStream, 看下 Watch 如何实现
    主要是用来生成 watchid, 自增就可以了。再调用watch方法得到一个watcher和cancelFunc,将watcher和cancelFunc放入watchStream

    watch方法:

    WatchStream的watchableStore 一共有三个 group: synced, unsyncedvictims, 当 client watch 时是从历史记录开始的,也就是说此时有一堆消息待发送给 client, 那么将 watcher 结构体扔到 unsynced 组中,否则扔到 synced 组中。为什么这么做呢?因为消息处理有快慢,后面具体代码再讲,只要记住 watcher 会在这三个组中流转即可,当然理想情况一直待在 synced 组中

    MVCC 消息生成

    底层 Txn 用 watchableStoreTxnWrite 封装了一下,在调用 End 提交事务前,调用 notify 将变更的消息发送出去。
    遍历 changes, 判断类型 mvccpb.DELETE 或是 mvccpb.PUT, 然后封装成 envs 事件,调用 tw.s.notify 发送出去后提交。
    newWatcherBatch 用于从 synced 组中获取待发送的 watcher, 然后调用 w.send 发送到 channel 里面,如果 channel 满了,那么说明发送不出去,将 watcher 从 synced 组中删除,并添加到 victim 组中,等后后续异步 goroutine syncVictimsLoop 处理。我们看一下,newWatcherBatch 实现
    watcherSetByKey 用于返回满足 ev.Kv.Key 的 watcher, 这里内部实现使用 adt 红黑树,可以做到快速的范围匹配。感兴趣的可以看源代码。
    send 函数先 apply filter 过滤一遍,然后发送到 w.ch 中,如果满了则返回 false. 这个 w.ch 就是 v3rpc 使用的 channel, 有数据后就发送 http2 stream …

    newWatchableStore方法

    etcd服务启动创建watchableStore,在 newWatchableStore 时,会生成两个异步 goroutine, syncWatchersLoop 用于将 unsynced 的 watcher 变成 synced watcher, syncVictimsLoop 用于将 victims 的消息尽可能的发送出。

    慢速处理

    1.慢速处理 victim
    调用 moveVictims 尝试去发送堆积的消息
    代码很简单,先尝试发送 victims 这些消息,如果失败了,再放到 victims 中。成功了的话,还要看当前系统中的 Rev 是否与该 watcher.minRev 相等,再考滤放到 synced 组还是 unsynced 组中。
    2.慢速处理 unsynced
    syncWatchersLoop 函数循环调用 syncWatchers 处理 unsynced 组数据

    1. choose 从 unsynced 中选择待发送数据的 watcher groups, 只看版本是否可用,即处于 [compactRev, curRev]
    2. UnsafeRange 从 boltdb 中获取所有满足条件的 keys/values
    3. 遍历 watchers,开始发送符合条件的 keys/values, 成功了那么从 unsynced 中删除,再加到 synced 中,否则加到 victims 队列中

    参考链接: 董泽润的技术笔记:一文了解 etcd watch 实现

    展开全文
  • 1、watch 两个小“坑”: 监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。参考情况三和情况六 监视reactive定义的响应式数据中某个属性时:deep配置有效。...
  • 1)开启事务 multi 命令用于开启事务,实现代码如下: > multi OK multi 命令可以让客户端从非事务模式状态,变为事务模式状态,如下图所示: 注意:multi 命令不能嵌套使用,如果已经开启了事务的情况下,再执行...
  • 问题在项目中遇到一个问题,父组件向子组件传值,子组件监听传入对象的某个属性的时候,发现子组件使用deep watch都不能监听到属性的变化。今天终于在网上找到了答案,在这里把方法记录下来。参考网址 ...
  • Consul 之 Watch

    千次阅读 2021-05-27 11:19:58
    如何部署 Watch 1. 创建通知程序 本篇讲解,只将通知信息写入日志,应用可根据实际对接通知SDK。 创建 .net core 3.1 web api public static ILog _log = new Log4Factory().GetLogger(); [HttpPost("/notice")...
  • 解决Vue watch里调用方法的坑

    千次阅读 2021-01-13 21:49:23
    这里是说watch调用methods里方法的时候,页面经常会报找不到方法这个时候一定要在watch里去输出一下this,看看this包裹的壳是不是多了好多层,所以找不到方法,虽然我到现在还没理解为啥有时候会出现一层或几层壳的...
  • 发现子组件使用deep watch都不能观察到对象中属性的变化,今天终于找到为啥出现这种问题和解决办法了。解决为啥出现这种问题?受 ES5 的限制,Vue.js 不能检测到对象属性的添加或删除。解决方式:通过vue的this.$set...
  • 版本日期备注1.02020.4.8文章首发1.12020.4.18优化小结...用过zookeeper的同学都知道watch是一个非常好用的机制,今天我们就来看看它的实现原理。在正文开始前,我们先来简单回忆一下watch是什么?zk提供了分布式...
  • immediate属性发现watch的一个特点是最初绑定的时候不执行改变即第一次直接赋值不会监听到,这里需要用到immediate属性,默认值为false,我们需要这样写immediate:true,即立刻执行。deep属性watch的另一个属性是deep...
  • 在vue项目中,有以下代码: Save Cancel 点击save的时候,取反vuex... computed: { ...mapGetters([ 'isValidate', 'ordinryAttrbuteInfoVO' ]) } watch: { isValidate(old, newVal) { console.log(old, newVal) }) } }
  • zookeeper watch机制与客户端实现原理本文讨论如何用zookeeper做服务发现,zookeeper 的watch实现原理和机制,以及python使用kazoo客户端库连接zookeeper时如何在数据变化后更新数据,保证数据安全。1. 服务发现你有...
  • 父组件传递的对象中属性值初始值为undefined,子组件watch监测不到该对象变化。 原因: 受ES5的限制,Vue.js不能检测到对象属性的添加或删除 解决办法: 通过Object.assign()重新创建一个对象,例如this....
  • 通过redis事务及事务回滚机制这篇,记录了redis事务的大题使用方式,这篇记录一下redis的watch命令在事务中的应用,各位看到此博客的小伙伴,如有不对的地方请及时通过私信我或者评论此博客的方式指出,以免误人子弟...
  • 引言 C代码中经常会有大量的指针在很多不同函数中传来传去,甚至还有强制类型转换,让阅读代码和调试BUG的人苦不堪言。...watch是gdb众多命令中的一个,用来检测变量,当被监测的变量被修改时,...
  • }, 上面的 watchArr 变成 vue 无法监听数组变化的解决方案 this.$set(arr, index, newVal); data () {return{ watchArr: [{ name:'krry', }], }; }, watchArr (newVal) { console.log('监听:' +newVal); }, ...
  • watch监听对象的变化时,打印出来的变化后的值和变化前的值(newVal)和旧值(oldVal)是一样的,如何解决? 先看之前的代码 父组件App.vue给子组件传递过来一个对象tempObj, <template> <div id="app"&...
  • 请勿打扰模式并不是在 Apple Watch 上管理通知的唯一方式;设置应用程序中有更多选项。无论是关于您即将到来的约会、来自朋友的消息,还是来自您最喜欢的天气应用程序的通知说要下雨,在您的手腕上接收通知都可以...
  • // 两个参数变为数组 }) // 监视用法三:监视reactive定义全部数据【深度监视】,watch监听的reactive的数据,无法获取到oldVal;watch监听的reactive的数据强制开启了深度监视 watch(data,(newVal,oldVal)=>{ ...
  • 需要用到watch监听的,直接看文章了。 1.监听一个ref的值,非常简单: const title = ref("") watch(title, (n, o) => { console.log("title改变了", n, o) }) 2.需要监听多个ref的值,写法多了点,逻辑是一样...
  • ref 名, // 那么 `$refs.myRef` 会变成一个数组。 refInFor: true } 第三个参数是传入其子元素,下面demo直接写入行内的文本作为子元素 demo 该demo动态的传入上级的标签 ul 和 foods 数据,同时数据对象中设置 ...
  • 目录 我刚开始对数组里的对象属性监听 监听一个对象及属性 同样也可以设置计算属性...watch:{ 'listMenu[4].value':{ handler(newValue, oldValue) {  console.log(newValue)  } } 直接报错Watcher only acce.
  • 选项式API与之前写法相同,本篇文章主要通过 Options API 和 Composition API 对比 watch 的使用方法,让您快速掌握 vue3 中 watch 新用法。建议收藏! 一、watch 新用法 选项式API中,watch 使用 watch:{ mood...
  • 如果你的Apple Watch无法充电该怎么办呢?小编给大家带来了详细的解决方法...)手表充电时,红色闪电图标会变为绿色闪电图标 。如果屏幕是空白的,或者您在屏幕上看到Apple 磁力充电线图标和一个红色闪电图标,请为手.
  • Vue3复习和总结(base)Watch与指令 代码全部放在->github仓库:https://github.com/zyugat/vue-review-all 前言:分为base、Component、router、Vuex、组合式API。5各部分。 base和Component中的例子不使用脚手架...
  • 踩坑不断的watch

    2021-08-19 10:39:23
    watch 今天看教程,发现vue3里面的watch坑超级多,特此记录下。 语法 vue3组件改为组合式组件,所需的配置大都需要自己去引入,watch也是如此。 watch(attr, callBack(newV, oldV), obj) vue3将计算属性(computed...
  • vue之watch总结

    2021-01-09 20:27:16
    val }, immediate: true } 修改之后再次查看页面我们发现fullName对应的值就变成了zhanghang了。 2、deep属性 我们在data里面定义一个obj对象,obj里面有个值为helllo的a属性,我们想在watch里面监听obj的a属性 ...
  • 1.将父组件数据赋值给子组件数据,子组件间接使用,数据仅在mounted中渲染,父组件数据改变子组件数据不改变,需要用watch监听,可以深度监听 2.子组件直接使用父组件传递的数据,父组件数据改变子组件改变,不管...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,136
精华内容 16,054
关键字:

watch变为