导航栏自定义按钮 文字修改

导航栏自定义按钮,通过dom获取到该按钮,修改文本,数据是修改了,但是视图没有改过来,使用了nexttick也不可以。

img

img

img

1.使用Vue提供的$forceUpdate()方法强制刷新视图

mounted() {
  // 获取自定义按钮节点
  const btn = document.querySelector('.custom-btn')
  
  // 修改按钮文本
  btn.innerText = '新文本'
  
  // 强制刷新视图
  this.$forceUpdate()
}


  • 你可以看下这个问题的回答https://ask.csdn.net/questions/7776831
  • 我还给你找了一篇非常好的博客,你可以看看是否有帮助,链接:$nextTick作用过程、使用场景及使用方法详解
  • 除此之外, 这篇博客: 根据$nextTick一个怪异的现象经过窥探源码发现vue惊天地泣鬼神的神来之笔中的 那么问题来了: 既然$nextTick会在dom更新后执行, 为何第一个中打印dom的值依旧没有发生改变呢?既然没改变就意味着他没有在dom更新后执行啊? 这到底啥情况 部分也许能够解决你的问题, 你可以仔细阅读以下内容或跳转源博客中阅读:
  • 如果你不看源码 一定是百思不得其解的, 因为vue有时候确实设计的非常精妙

    笔者来用自己的方法给你写一写你能够看的明白的$nextTick, 跟着注释看我相信你是不会迷路的

    const nextTick = (function() {
       let callbacks = [];  // 最后所有在nextTick中传递过来的函数都会进入这个数组 
       
       let timerHandler = () => { // 这个函数用来延迟nexTick传递进来的函数的执行
         // Promise.resolve这句话往这里一站, 你就知道这哥们后面的那行then代码要等待了,
           const p = Promise.resolve(); 
           p.then(releaseCallbacks); // 等同步任务执行完毕这哥们会执行
       }
       
      function releaseCallbacks() { // 作为p.then的回调 releaseCallbacks肯定也会在微队列中等待
          
          for(let i = 0; i < callbacks.length; i++) {
              callbacks[i](); // 所有存储进callbacks的函数挨个执行
          }
          callbacks.length = 0;
      }
    
      // 真正暴露给用户的回调函数
      return function(cb) {
          callbacks.push(cb);
          timerHandler();
      } 
    }())
    

    调用我们自己的的nextTick方法,其他语句都不变 我们走一遍输出发现输出结果如下

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M06r2HMg-1591801267706)(./自己写的nextTick.png)]

    确实发现所有交付给nextTick的函数都按照异步执行了, 但是并没有如我们所想象的那样, 相反连nextTick真正的作用都发挥不上了, 我们不再可以监听到msg被更改, 于是我们来看看被笔者进行注释过后的真正的$nextTick源码(当然, 前提是上面笔者的这份简化版源码你已经看懂了, 不然vue源码会更加头大)

    export let isUsingMicroTask = false // 这是vue用来判断是否启用微任务的锁, 如果不懂没关系他不重要
    
    const callbacks = [] // 同样, 最后所有在nextTick中传递过来的函数都会进入这个数组
    let pending = false // 异步锁, 如果同步任务未执行完, 异步锁肯定是锁住的
    
    function flushCallbacks () { // 最终执行callbacks的函数
      pending = false // 重置异步锁
    
      // 这里我们发现将callbacks复制了一份给copies, 最终循环操作的也是copies, 这是因为不想造成nextTick嵌套调用的冲突
      const copies = callbacks.slice(0) 
      callbacks.length = 0
      for (let i = 0; i < copies.length; i++) {
        copies[i]()
      }
    }
    
    let timerFunc // 相当于上面的timerHandler
    
    
    // 判断当前环境支不支持原生的Promise构造函数
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
        // 如果支持会走上面的timerHandler的流程
      const p = Promise.resolve()
      timerFunc = () => {
        p.then(flushCallbacks)
        
        if (isIOS) setTimeout(noop)
      }
      isUsingMicroTask = true
    } else if (!isIE && typeof MutationObserver !== 'undefined' && (
      isNative(MutationObserver) ||
      // PhantomJS and iOS 7.x
      MutationObserver.toString() === '[object MutationObserverConstructor]'
    )) {
        // 判断是不是IE
      let counter = 1
      const observer = new MutationObserver(flushCallbacks)
      const textNode = document.createTextNode(String(counter))
      observer.observe(textNode, {
        characterData: true
      })
      timerFunc = () => {
        counter = (counter + 1) % 2
        textNode.data = String(counter)
      }
      isUsingMicroTask = true
    } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
      // Fallback to setImmediate.
      // Technically it leverages the (macro) task queue,
      // but it is still a better choice than setTimeout.
      timerFunc = () => {
        setImmediate(flushCallbacks)
      }
    } else {
      // Fallback to setTimeout.
      timerFunc = () => {
        setTimeout(flushCallbacks, 0)
      }
    }
    
    // 真正暴露出去的nextTick方法
    export function nextTick (cb?: Function, ctx?: Object) {
      let _resolve
      callbacks.push(() => {
        if (cb) {
          try {
            cb.call(ctx)
          } catch (e) {
            handleError(e, ctx, 'nextTick')
          }
        } else if (_resolve) {
          _resolve(ctx)
        }
      })
      if (!pending) {
        pending = true
        timerFunc()
      }
      // $flow-disable-line 这个是新加的, 如果没有传递cb参数则返回一个新的promoise出去
      if (!cb && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
          _resolve = resolve
        })
      }
    }
    

    抛开一些兼容性写法和一些容错机制来说, vue的nextTick和我们写的nextTick没有什么差别, 但是为什么会产生截然不同的效果呢?

    继续阅读源码笔者有发现, vue中还存在一个queueWatcher方法, 如下

      function queueWatcher (watcher) {
        var id = watcher.id;
        if (has[id] == null) {
          has[id] = true;
          if (!flushing) {
            queue.push(watcher);
          } else {
            // if already flushing, splice the watcher based on its id
            // if already past its id, it will be run next immediately.
            var i = queue.length - 1;
            while (i > index && queue[i].id > watcher.id) {
              i--;
            }
            queue.splice(i + 1, 0, watcher);
          }
          // queue the flush
          if (!waiting) {
            waiting = true;
    
            if (!config.async) {
              flushSchedulerQueue();
              return
            }
            nextTick(flushSchedulerQueue);
          }
        }
      }
    

    你不用将他看懂, 但是笔者可以告诉你这哥们的作用就是用来更改nextTick的执行顺序的

    本身我们执行nextTick他的效果跟一般的异步任务没什么太大的区别, 无非就是nextTick会被置于微任务, 而queueWatcher方法和他带来的一些骚操作则改变了nextTick的运行轨迹

    1. 如果在$nextTick前没有更改vue监控的属性值的情况发生, 那么nextTick中的代码按照正常异步微任务走掉

    2. 如果在$nextTick前有更改了vue所监控的属性值的情况, 则queueWatcher会调换nextTick的执行顺序, nextTick将会在下一次事件循环vue刷新页面后执行

    有些东西你不看源码是真的想破头都想不出来他到底是什么原因, 这也是我们作为开发者一直要追逐的事情, 共勉

  • 以下回答由chatgpt基于相关博客总结生成:

    回答:

    在Vue.js中,使用$nextTick函数可以在下次DOM更新周期中获取数据的最新状态。但是在该问题中,即使使用了$nextTick函数,修改按钮文字后视图仍然没有改变。这可能是由于Vue.js采用了异步更新队列来进行更新DOM,所以在同一轮事件循环中,如果有多个数据发生了变化,组件的watcher会受到多次通知,但虚拟DOM只会进行一次渲染,从而导致视图没有改变。

    解决该问题的方法是,在修改按钮文字的代码后直接触发一次重新渲染。在Vue.js中,可以调用$forceUpdate()方法手动强制更新渲染视图。代码如下所示:

    this.btnText = 'New Text'; // 修改按钮文字
    this.$forceUpdate(); // 强制更新视图
    

    该方法会触发整个组件的重新渲染流程,更新视图中的所有数据,也可以在$nextTick函数的回调函数中执行。完整代码如下:

    // 修改按钮文字
    this.btnText = 'New Text';
    this.$nextTick(() => {
      this.$forceUpdate(); // 强制更新视图
    });
    

    这样就能及时更新DOM,使视图与数据同步。