0614乱七八糟的笔记,乱乱乱~

140 阅读13分钟

watch 是一个侦听某一个ref对象 或 reactive 的对象 的变化 测试 watch 一定要注意 不能够在回调期间 如果发生变化,所执行的函数中 再次使用这个侦听对象

1、watch、watchEffect 侦听函数,它侦听的仅仅是数值的变化,如果数值是不变化的,那么它就没有必要进行刷新执行

2、为什么test_watch++这个侦听函数是执行的,但是为什么for 循环中为什么就不执行了? 侦听函数的特性,在不给它任何选项的情况下,watch、watchEffect 它都是惰性的

watch、watchEffect 侦听函数,它侦听的仅仅是数值的变化,如果这个数值是不变化的,那么它就没有必要进行刷新执行。为什么 test_watch++ 这个侦听函数是执行,但为什么 for 循环中为什么就不执行了呢?浏览器什么时候更新刷新这是一个定时的。它是根据 计算机系统设置的刷新频率 同步刷新的。在同一刷新时间内,多次调用 侦听 函数,那么它将以最后一项为准。但有的时候这个惰性原理,其实它不是我们想要的结果,我们想一次性发送批量请求,那么就是改变 侦听 函数的 选项

watch 我们知道它是没有初始化特征的,但是 watchEffect 它是具有初始化这个特点,但是很多时候这并不是我们想要的结果,我们希望 watchEffect 和 watch 函数一样,仅在 相关联对象发生改变时才触发它,那么可以使用 flush: 'post'

watchEffect 默认它 flush 选项 值 'ref' 它是具有初始化的。 更改 post 后,初始化将不再执行。 watch 它自身就没有初始化这个特性,即便你将 flush 选项修改为 ref 也不会有初始化特性。

侦听多个数据 watch 当中去侦听多个 ref 对象或者属性, 但是属性 需要使用 getter 函数来进行返回,多个属性之间,在调用回调函数时的参数,是可以自定义格式的, 你可以定义为一个对象,也可以定义一个列表。

  <p>如果说想把这个全称 显示在页面上 computed 主动调用 并且 它会产生一个实际可以使用 响应式 对象。 </p>
  <p>fullName = Vue.computed(()=> ref_first_name + ref_last_name )  template 当中 直接输出 fullName</p>
  <p>如果说我并不想把这个 fullName 直接显示在页面上,而是 通过这个合成的 名称 调用某一个查询函数,然后将 这个人的相关资料显示在 页面上</p>
  <p>如果说相关资料是复杂的,那我可能会使用 子组件来显示 相关资料 把 异步请求函数的返回值,使用props 传递子组件,并且显示出来.</p>
  <p>页面就不需直接使用  fullName 来显示。仅需要一个合成的名称 ,甚至连变量都不用了.  watch、watchEffect</p>
  <p>Vue.watch([ref_first_name, ref_last_name], newValue => { request_person_data(newValue) })</p>
  <p>promise.then(data => { ref_data = data } )</p>
  <p>template :data="ref_data" 因为它是响应式的 ref 对象 所以 这个信息将自动更新至 子组件上。</p>
  <p>还会对当前名称 进行归类 会以一个常量的形式来标注一些其它 信息,但是这些信息并不在  侦听范围之内。</p>
  <p>这种情况下就必须使用 watch,因为如果你使用 watchEffect 将导致 这个没有用信息,在侦听范围之内。</p>
  <p>如果说这些信息,并不是在页面上去显示的情况下,可以使用普通变量,而不是响应式的 ref 对象。</p>
  
 <h3>侦听响应式对象</h3>
  <p>
    <button @click="test_array.push('测试元素')">添加一个元素</button>
    <button @click="test_array.pop()">删除一个元素</button>
  </p>
  {{ test_object }};
  <p><button @click="test_object.friend.push('傻大木'); test_object.family.father = '李雷的爹'">点击修改</button></p>

  <p>watch 监听的属性 在使用 getter 函数,返回要监听 的属性时,直接 返回一个 reactive 的话,深层的属性的监听  默认是无效的。</p>
  <p>但是如果说你直接 使用一个 reactive 对象的话,对深层的监听,就是有效的了.</p>

写一个模拟搜索的函数

js部分

async function get_product(name)
    {
      const time = Math.floor(Math.random() * 4) + 2;
      const message = `商品${name}的搜索共计有${Math.floor(Math.random() * 10000)} 件.`;
      const reply_promise = new Promise(resolve => setTimeout(resolve, time * 1000 , { time: time + 's', message }));
      // 从之到上面函数的开始,同步执行
      await reply_promise;  // 开始 await 等待 这个 promise 处于 落定 resolve 状态
      // 下面是在 promise 落定之后才执行的 操作.
      // 我们就可以对这个返回值进行操作了, 操作 返回时间
      reply_promise.then(response => response['replyTime'] = new Date());
      return reply_promise;  // return 是当时就返回的,返回值就是这个 replay_promise 但是它现在还是一个 pending 状态;
      // 在这个函数的返回值 接收之后,再 then 也就是这个 promise 在resolve 状态后,再执行什么东西!
    }
    //==========================================
    function select_start()
    {
      // 开始搜索
      // 禁用按钮
      button_disabled.value = true;
      select_status.value = `正在搜索[${select_product.value}]请稍后....`;
      // 执行搜索
      const reply_promise = get_product(select_product.value);  // 商品名称搞进去
      reply_promise.then(data => {
        // 更新 搜索状态
        select_status.value = `${data.message} 共计搜索用时: ${data.time}!`;
        if(product_name.value === '')
        {
          // 说明用户没有输入,用的是推荐列表中的
          // 这个 product_name_index 必须使用 ref 如果不是ref 对象那么它就没有办法及时给 computed 计算属性发送更新信号.
          product_name_index.value = product_name_index.value + 1 >= product_list.length?0:product_name_index.value + 1;
        }else
        {
          product_name.value = '';
        }
        // 添加搜索记录
        // 异步函数因为不知道什么时候返回,所以返回后的处理都在异步函数中执行。
        // 更新全部数组
        // product_record.value = product_record.value.concat([`${data.message} 用时: ${data.time}`]);
        // product_record.value.reverse();
        // 局部更新数组
        product_record.push(`${data.message} 用时: ${data.time}`);
        // 解禁按钮
        button_disabled.value = false;
      });
      // 成功以后怎么样,失败之后又怎么样~~
      // 如果这个 product_name 如果为 '' 情况下 那么我们就让 product_name_index++ 下一个搜索, 如果说 product_name 存在 说明用户自己定义的搜索项,清空这个搜索就ok了`
    }
    //==========================
    function animation_enter(el, done)
    {
      gsap.set(el, {
        x: 100,
        opacity: 0
      });
      gsap.to(el, {
        x: 0,
        opacity: 1,
        duration: 0.5,
        onComplete: done
      })
    }
    function test_func()
    {
      console.log('Run >>>> 被节流函数被运行!');
    }
    // 被节流的函数
    const test_func_throttle = lodash.throttle(test_func, 5000, { leading: false, trailing: true });
    function test_throttle()
    {
      console.log('测试防抖、节流函数!');
      // test_func();
      // 这个节流函数,是以上一次调用该函数的时间为准 开始计算间隔时间
      // 和我们和次自己写的那个不一样,每一次调用 都重置 间隔时间

      // 调用函数是否允许出现在节流时间之前
      // leading 设置为 true 的情况下 将允许在节流时间之前 即可调用. 即时点击,即时就调用一次,然后开始重新计时
      test_func_throttle();

    }
    function input_event(event)
    {
      console.log(event.target.value);
      // 2秒 之后直接 调用 搜索 功能
      // 直接拿这个字符串来调用搜索
      // 这个功能是避免 在短时间内进行重复性的搜索

      // 如果说在这个位置调用 搜索 功能 而这个搜索功能 它是一个异步函数 大概4-6秒钟的时间,才会返回 返回值
      // 2秒的防抖其实对于搜索框的更新没有什么太大的价值。
      // 它的价值在 短时间内不会重复的调用 搜索 函数

    }
    const input_event_throttle = lodash.throttle(input_event, 2000, { leading: false });

    const input_value = Vue.ref('');

    function test_select_func(select_str)
    {
      return  new Promise(resolve => setTimeout(resolve, (Math.floor(Math.random() * 5) + 2) * 1000, `搜索函数被调用,搜索值:${select_str}!`));
    }
    // 怎么去触发这个 搜索 函数呢?
    // test_select_func('家电').then(console.log);

    // Vue.watchEffect()  // 初始
    /*
    Vue.watch(input_value, (newValue, _, onInvalidate) => {
      test_select_func(newValue).then(console.log);
      // 什么监听周期  当一个值被改变后,运行当前函数,当前函数如果未执行完成,则进入了下一个监听周期,那么 就像这个动画一样,它就被强制取消掉了.
      // 我们怎么才能 监听强制取消 事件?
      // 使用 回调函数中的 第三个参数 onInvalidate 来注册 强制取消侦听事件的回调
      onInvalidate(() => {console.log('侦听事件被强制中断!')});
      // 虽然说它可以检测到 侦听事件的被强制中断 但是它并不能 改变 异步函数的运行。
    }
    );
     */
    Vue.watch(input_value, async (newValue, _, onInvalidate) => {
      // 同步执行
      let print = true;
      onInvalidate(() => print = false);  // 如果代码被强制中断,则我就取消这一次输出.
      const select_str = await test_select_func(newValue);  // 返回一个 promise  resolve 调用值
      // 下面的代码,将在 promise 处于 resolve 以后才会被执行.
      if(print)
      {
        console.log(select_str);
      }else {
        console.log('已被丢弃的搜索:', select_str);
      }
    })
    // 这个整个的搜索框 它是个线性的

    // =============================================================
    // 侦听函数的惰性特点
    const test_watch = Vue.ref(0);
    Vue.watch(test_watch, newValue => console.log(newValue), {flush: 'sync'});
    // flush 共有三个 选项: ref(默认)  post 延时  sync 同步
    // 设置为同步的情况下 sync 将关闭 侦听函数默认的 惰性原理
    // 这个 flush 选项 watch、 watchEffect 都是一样的.

    // post 指的是延时 延到什么?  延时到 mounted 生命周期之后.

    // Vue.watchEffect(onInvalidate => {}, { flush: 'sync'});
    function test_watch_func()
    {
      // test_watch ref对象 它的值被改变了 10 次
      for(let index = 1; index <= 10; index++)
      {
        test_watch.value = index;
      }
      /*
      test_watch.value = 1;
      test_watch.value = 2;
      test_watch.value = 3;
      test_watch.value = 4;
      test_watch.value = 5;
      test_watch.value = 6;
       */
    }

    const test_watch_two = Vue.ref(0);
    // Vue.watch(test_watch_two, newValue => console.log('检测到 test_watch_two ref对象被改变,值为:', newValue), { flush: 'post' });
    Vue.watchEffect(() => {console.log('检测到 test_watch_two ref对象被改变,值为:', test_watch_two.value)}, { flush: 'post' })
    // 说明这个Vue.watch 侦听事件被创建后就马上 起效了

    // Vue.watchEffect flush 为 post 的情况下,它将不再对 ref 或 reactive 进行初始化处理。像 watch 函数一样,仅在某一侦听对象被改变时才会触发。
    test_watch_two.value = 999;

    const test_watch_reactive_object = Vue.reactive({ num1: 10, num2: 100, num3: 1000 });
    // reactive 它是递归所有深度的.
    // Vue.watch(() => {return {num1: test_watch_reactive_object.num1, num2: test_watch_reactive_object.num2 } }, (newValue) => {console.log(newValue) });
    Vue.watch(() => {return [test_watch_reactive_object.num1, test_watch_reactive_object.num2 ] }, (newValue) => {console.log(newValue) }, { flush: 'sync' });

    // 侦听器可以侦听 单个对象 还可以侦听 单个对象属性
    // 多个 ref 对象  多个对象属性,必须是 reactive 对象属性


    // 列表进行监听
    const test_array = Vue.reactive([]); // {} 其实它就是一个对象
    // Vue.watch(test_array, (newValue, oldValue) => { console.log('new value:', newValue, 'old value:', oldValue) });
    // 回调函数中的 newValue oldValue 一毛一样.
    Vue.watch( () => Array.from(test_array), (newValue, oldValue) => {
      console.log('new value:', newValue);
      console.log('old value:', oldValue);
    });
    // test_array 被解构以后,就好了.
    // watch 当中的这个 [...test_array] 其实就是一个字页面字义方式,换句话说,它就是创建一个新的 array, 和当前array是一致的.

    // 为什么直接监听 test_array  newValue oldValue 为什么值是一样的.
    // Vue.reactive 先是创建了一个,更新即可以发送 被更新信号 的对象。
    // 这个列表在更新时,将会发送一个被更新的信号.

    // 侦听一个数组是没有太大的意义的

    const test_object = Vue.reactive({ name: '李雷', friend: ['小红', '吉姆'], family: { father: '李雷的爸', mather: '李雷的妈'}});

    /*
    Vue.watch(() => ({friend: [...test_object.friend], father: test_object.family.father}),(newValue, oldValue) => {
      console.log('new value:', newValue);
      console.log('old value:', oldValue);
    })
     */

    // 大部分场景下是用不到 整个对象的监听的
    /*
    Vue.watch(() => test_object, (newValue, oldValue) => {
      console.log('new value:', newValue.friend, newValue.family.father);
      console.log('old value:', oldValue.friend, oldValue.family.father);
    }, { deep: true });
     */
    // Vue.reactive 将会递归所有深度上的 对象吧?  在修改这个对象上的任何属性,包括 深层属性,都将会发送一个 自身属性被修改的 信号。 watch 将捕获这个信号 进行处理。
    // newValue 和 oldValue 的输出,就证明 reactive 对象已经把信号发送出来了,而且 watch 已经执行了.

    // 虽然说 watch 可以直接对 reactive 深层对象进行侦听,但是会遇到一个问题。
    // oldValue 和 newValue 一致的问题。

    // 解决这个问题的方案 就是将 某一个对象 再重新  复制一份. 数组 可以通过 Array.from 解构等这种方法 将数组进行一次 复制。
    // 深层嵌套的对象怎么解决类?
    Vue.watch(() => {
      // 这个位置返回的,不应该是原对象,而是原来一个对象的 幅本。
      // 更关键的问题是,你不能直接使用 Object.assign 这种东西,因为这么复制出来的是 浅复制。
      // 如何进行深 复制呢?
      // 通过原生 js  JSON
      // return JSON.parse(JSON.stringify(test_object));  // 对这个对象进行 先JSON序列化,然后再将JSON转换为对象
      // 序列化 JSON 有一个弊端 JSON 并不是一个专业 对 javascript 对象 进行字符串序列的工具。它是一种数据类型,用于 各种语言中的 数据类型,进行字符串处理,便于传输。
      // 对象中如果存在 方法、函数 那就不行了。 因为JSON 不能转换函数

      // 以后记着一个问题,监听的数据,不要放函数
      // 如果说有函数,那就 () => { 从这里边 返回单个 对象的属性 进行监听 }
      return lodash.cloneDeep(test_object);

    }, (newValue, oldValue) => {
      console.log('new value:', newValue.friend, newValue.family.father);
      console.log('old value:', oldValue.friend, oldValue.family.father);
    }, { deep: true });

    // newValue 传递的是 整个对象
    test_watch_reactive_object.num1 = 777;
    test_watch_reactive_object.num2 = 888;
    return {
      select_product,
      select_list,
      product_list,
      select_start,
      product_record,
      select_status,
      animation_enter,
      disabled: button_disabled,
      test_throttle,
      test_func_throttle,
      input_event: input_event_throttle,
      input_value,
      test_watch_func,
      test_watch,
      test_array,
      test_object
    }
  },
  mounted()
  {
    console.log(this);
  }
    
    

html部分

  <p>
    <label>搜索商品: <input type="text" size="5" v-model.lazy="select_product"></label>
    &nbsp;
    <button @click="select_start" :disabled="disabled">搜索商品</button></p>
  <p class="select_status">{{ select_status }}</p>
  <p>搜索记录: </p>
  <p v-if="!product_record.length">暂无记录</p>
  <ul v-else class="record-box">
    <!--    <li v-for="(record, index) in product_record" :key="index" >{{ record }}</li>-->
    <transition-group @enter="animation_enter" :css="false" :appear="true">
    <li
        v-for="index in product_record.length"
        :key="product_record.length - index"
    >{{ index }}、{{ product_record[product_record.length - index] }}</li>
    <!--  index 将 从1开始至 product_record.length  -->
    </transition-group>
    <!--  使用这个 index 就要造成 VNode 更新(不会触发动画) 生成添加VNode的时候才会触发动画.  -->
  </ul>

测试防抖

 <p>测试一下 lodash 模块中的 throttle 防抖节流函数:</p>
  <button @click="test_throttle">点击测试</button>
  &nbsp;
  <button @click="test_func_throttle.cancel">取消调用</button>
  &nbsp;
  <button @click="test_func_throttle.flush">立即调用</button>

针对不停的点击搜索框按钮问题 有两个解决办法 1、 禁用按钮 搜索过程中不让点 2、利用防抖函数

  <p>就写这个例子:</p>
  <p>搜索推荐1: <input type="text" @input="input_event.cancel();input_event($event);"></p>
  <p>调用两个函数1 清除到之前的 防抖调用,2 创建新的防抖调用</p>
  <p>在输入框中输入文字,首先要提取到输入的文字.</p>
  <p>这个例子的特点是,在Vue中没有任何的变量来存储当前 搜索的字符串.</p>
  <p>搜索推荐2: <input type="text" @input="input_value=$event.target.value"></p>
  <p>{{ input_value }}</p>
  <p>整个搜索结果的 显示框  线性的可以分为以下几个步骤:</p>
  <ol>
    <li>获取当前用户输入</li>
    <li>用户输入事件频繁调用,所以需要添加 防抖函数 来解决 频繁调用 select 搜索函数.</li>
    <li>已经防抖过滤掉的 用户输入 调用异步搜索函数,返回结果需要时间.</li>
    <li>在异步返回值 未返回之前,如果重复调用 搜索函数,之前的搜索应该是被丢弃掉的。</li>
    <li>我们现在把调用 搜索 异步函数操作放在 watch 侦听事件当中了。</li>
    <li>在这一次侦听事件 回调函数 完成调用之前,如果再次调用了 侦听函数(在改变侦听变量被改变之后,调用 回调函数,但是在回调函数未完成之前,又一次侦听到 改变了。)</li>
    <li>那么上一次的侦听事件 将强制被取消掉。 使用 watch 的第三个参数 onInvalidate 注册 侦听事件被强制中断后的  回调函数。</li>
  </ol>
  <p>watch 它的 onInvalidate 它是 回调函数中的第三个参数</p>
  <p>watchEffect 它的 onInvalidate</p>
  <p>Vue.watchEffect(onInvalidate => {}) 自动检测当前回调函数当中,相关 ref对象 reactive 对象 是否发生改变。</p>

  <p>侦听函数的 起效时间</p>
  <p>侦听函数的特性,在不给它任何选项的情况下,watch、watchEffect 它都是惰性的</p>
  <p>
    <button @click="test_watch_func">点击执行 ref 对象的 for循环</button>
    &nbsp;
    <button @click="test_watch++">点击 test_watch++</button>
  </p>
  <p>它仅输出一个 10</p>
  <p>这是为什么呢?这就是它的惰性原理!</p>
  <ol>
    <li>watch、watchEffect 侦听函数,它侦听的仅仅是数值的变化,如果这个数值是不变化的,那么它就没有必要进行刷新执行。</li>
    <li>为什么 test_watch++ 这个侦听函数是执行,但为什么 for 循环中为什么就不执行了呢?</li>
    <li>浏览器什么时候更新刷新这是一个定时的。它是根据 计算机系统设置的刷新频率 同步刷新的。</li>
    <li>在同一刷新时间内,多次调用 侦听 函数,那么它将以最后一项为准。</li>
  </ol>
  <p>但有的时候这个惰性原理,其实它不是我们想要的结果,我们想一次性发送批量请求。</p>
  <p>那么就是改变 侦听 函数的 选项</p>
  <p>watch 我们知道它是没有初始化特征的,但是 watchEffect 它是具有初始化这个特点,但是很多时候这并不是我们想要的结果,我们希望 watchEffect 和 watch 函数一样,仅在 相关联对象发生改变时才触发它,那么可以使用 flush: 'post'</p>
  <p>watchEffect 默认它 flush 选项 值 'ref' 它是具有初始化的。 更改 post 后,初始化将不再执行。  watch 它自身就没有初始化这个特性,即便你将 flush 选项修改为 ref 也不会有初始化特性。</p>


  <p>侦听多个数据  watch 当中去侦听多个 ref 对象或者属性, 但是属性 需要使用 getter 函数来进行返回</p>
  <p>多个属性之间,在调用回调函数时的参数,是可以自定义格式的, 你可以定义为一个对象,也可以定义一个列表。</p>

  <p>firstName  lastName 名 姓  名+姓 = 全称</p>
  <p>上午 input 是一样的道理</p>
  <p>如果说想把这个全称 显示在页面上 computed 主动调用 并且 它会产生一个实际可以使用 响应式 对象。 </p>
  <p>fullName = Vue.computed(()=> ref_first_name + ref_last_name )  template 当中 直接输出 fullName</p>
  <p>如果说我并不想把这个 fullName 直接显示在页面上,而是 通过这个合成的 名称 调用某一个查询函数,然后将 这个人的相关资料显示在 页面上</p>
  <p>如果说相关资料是复杂的,那我可能会使用 子组件来显示 相关资料 把 异步请求函数的返回值,使用props 传递子组件,并且显示出来.</p>
  <p>页面就不需直接使用  fullName 来显示。仅需要一个合成的名称 ,甚至连变量都不用了.  watch、watchEffect</p>
  <p>Vue.watch([ref_first_name, ref_last_name], newValue => { request_person_data(newValue) })</p>
  <p>promise.then(data => { ref_data = data } )</p>
  <p>template :data="ref_data" 因为它是响应式的 ref 对象 所以 这个信息将自动更新至 子组件上。</p>
  <p>还会对当前名称 进行归类 会以一个常量的形式来标注一些其它 信息,但是这些信息并不在  侦听范围之内。</p>
  <p>这种情况下就必须使用 watch,因为如果你使用 watchEffect 将导致 这个没有用信息,在侦听范围之内。</p>
  <p>如果说这些信息,并不是在页面上去显示的情况下,可以使用普通变量,而不是响应式的 ref 对象。</p>

  <h3>侦听响应式对象</h3>
  <p>
    <button @click="test_array.push('测试元素')">添加一个元素</button>
    <button @click="test_array.pop()">删除一个元素</button>
  </p>
  {{ test_object }};
  <p><button @click="test_object.friend.push('傻大木'); test_object.family.father = '李雷的爹'">点击修改</button></p>

  <p>watch 监听的属性 在使用 getter 函数,返回要监听 的属性时,直接 返回一个 reactive 的话,深层的属性的监听  默认是无效的。</p>
  <p>但是如果说你直接 使用一个 reactive 对象的话,对深层的监听,就是有效的了.</p>

ref用于setup函数 中对于基础数据对象的传递 为了使数据具有哦一定的响应性,所以需要将数据进行对象包装,所以要在setup函数中使用ref对象需要使用.value操作