中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue定義在computed的變量無法更新如何解決

發布時間:2023-01-11 09:25:58 來源:億速云 閱讀:202 作者:iii 欄目:開發技術

今天小編給大家分享一下vue定義在computed的變量無法更新如何解決的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    vue定義在computed的變量無法更新

    情境是這是線上商城的詳情頁面,商品詳情是items數組,點擊分類頁面的商品,路由跳轉到詳情頁面,路由參數是商品在items中的序號。

    但是問題是只有第一次點擊商品i的時候可以正常加載items[i]的數據到html中,退出后點擊商品j,發現加載的還是商品i的信息,只有刷新后才會更新成商品j的信息。

    部分代碼:

            <shopPanel
                :key = "nums"
                :goodname="item.text"
                :tag="item.tag"
                :sale="item.sale"
                :price="item.price"
                :ori="item.ori_price"
            >
            </shopPanel>

    和computed定義的,注意這里定義的數據item,是不能夠修改的,想修改只能用set來修改。

    我這里測試時發現明明item關聯的兩個屬性this.focus和this.idx改變了,那為什么item沒有改變呢?

    而且是刷新后就能改變。

            computed: mapState({
                items:'items', //商品詳情信息
                item(){
                    console.log(this.focus+" "+this.idx);
                    return this.items[this.focus].children[this.idx]
                }
            }),

    最后的原因竟然是定義路由的時候選擇了keepalive,頁面不會銷毀,改成false就行。

            path: '/item/item01',
            name:'itemdetail',
            meta: {
                title: '商品1',
                keepAlive: false,
                showTab: false
            },
            component: (resolve) => require(['../page/itemdetail/item01'], resolve),

    vue computed依賴收集與更新原理

    今天面了家小公司,上來直接問 computed 底層原理,面試官是這樣問的,data 中定義了 a 和 b 變量。computed 里面定義了 c 屬性,c 的結果依賴與 a 和 b,模板中使用了變量 c。假設改變了 a,請問底層是如何收集依賴,如何觸發更新的?

    <div>{{ c }}</div>
    data(){
        return {
            a: 'foo',
            b: 'bar'
        }
    },
    computed: {
        c() { 
            return this.a + ' - ' + this.b;
         }
    },
    mounted(){
        setTimeout(() => { this.a = 'FOO' }, 1000)
    }

    頁面效果:先顯示了 foo - bar,一秒之后顯示 FOO - bar

    這里查源碼后,對computed原理簡述如下

    1.initState 函數中初始化了 initComputed

    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)
      }
    }

    2.initComputed 函數中遍歷 computed 中每一個屬性,并且 new Watcher(computed watcher),可以看到傳入 Watcher 構造函數的 cb 是 noop,它是一個空函數。并且 defineComputed

    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 (process.env.NODE_ENV !== 'production' && getter == null) {
          warn(
            `Getter is missing for computed property "${key}".`,
            vm
          )
        }
    
        if (!isSSR) {
          // create internal watcher for the computed property.
          watchers[key] = new Watcher(
            vm,
            getter || noop,
            noop,
            computedWatcherOptions
          )
        }
    
        // component-defined computed properties are already defined on the
        // component prototype. We only need to define computed properties defined
        // at instantiation here.
        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)
          } else if (vm.$options.methods && key in vm.$options.methods) {
            warn(`The computed property "${key}" is already defined as a method.`, vm)
          }
        }
      }
    }

    3.defineComputed 中使用了 Object.defineProperty 對屬性進行劫持,獲取是返回對應的 getter 即 createComputedGetter

    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
      }
      if (process.env.NODE_ENV !== 'production' &&
          sharedPropertyDefinition.set === noop) {
        sharedPropertyDefinition.set = function () {
          warn(
            `Computed property "${key}" was assigned to but it has no setter.`,
            this
          )
        }
      }
      Object.defineProperty(target, key, sharedPropertyDefinition)
    }
    
    function createComputedGetter (key) {
      return function computedGetter () {
        const watcher = this._computedWatchers && this._computedWatchers[key]
        if (watcher) {
          if (watcher.dirty) {
            watcher.evaluate()
          }
          if (Dep.target) {
            watcher.depend()
          }
          return watcher.value
        }
      }
    }

    看到這里,我們大致可以理解,Vue 組件在初始化時,initState -> initComputed -> new Watcher() 計算watcher,傳入的 callback 是 computer 屬性的 getter,由于 computed 是 lazy: true watcher,所以 new Watcher 時并不會立即執行 watcher 實例上的 get(), 而是在 defineComputed 函數里面調用 createComputedGetter 函數,在 createComputedGetter 函數執行了 watcher.evaluate() 進而執行了 watcher.get().

    執行 watcher.get() 首先 pushTarget(this),將 Dep.target 賦值為當前的 computed watcher,然后執行 this.getter也就是執行 computer 屬性配置的 getter,執行getter 就會訪問所依賴的每一個值,就會被 Object.defineProperty 劫持到進入 get ,執行 dep.depend() , 會為每一個屬性對應的 dep 實例添加一個 computed watcher,同時這個 computed watcher 也會保存對應的 dep。

    說了這么多都在講 computed watcher,那修改 this.a 頁面為什么會發生更新呢?

    答案:因為 this.a 的依賴中不僅有 computed watcher 還有一個 render watcher

    原因:

    $mount 是會執行 mountComponent,會創建一個 render watcher,它會立即執行 cb(目前 Dep.target 是 render watcher),調用 render 函數在底層編譯模板,最后訪問屬性計算屬性 c,訪問計算屬性 c 就必定會訪問 a,當訪問 a 時會觸發 defineComputed 中的 Object.defineProperty 進而劫持調用 createComputedGetter,進而調用 watcher.depend(),這個 watcher 是 computed watcher

    function createComputedGetter (key) {
      return function computedGetter () {
        const watcher = this._computedWatchers && this._computedWatchers[key]
        if (watcher) {
          if (watcher.dirty) {
            watcher.evaluate()
          }
          if (Dep.target) {
            watcher.depend()
          }
          return watcher.value
        }
      }
    }
    // Watcher.js
    depend () {
      let i = this.deps.length
      while (i--) {
        this.deps[i].depend()
      }
    }

    調用 watcher.depend() , this 指的是 computed watcher,會將 computed watcher 里面的 deps 保存在所有依賴調用 deps[i].depend(),進而調用 Dep 類中的 Dep.target.addDep(this),使得 render watcher 中保存了當前的 dep,dep 中同時保存了 render watcher。

    dep 中同時保存了 render watcher。就可以看出,示例中的屬性 a 的 dep 中也會保存 render watcher,所以 a 屬性的 dep 中有兩個 watcher: [computedWatcher, renderWatcher]

    所以,修改 a 屬性的值,最后 notify 會清空這個 保存 watcher 的隊列,進行頁面更新!Okay! 

    以上就是“vue定義在computed的變量無法更新如何解決”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    五大连池市| 吴旗县| 石城县| 榆树市| 马龙县| 都江堰市| 浦北县| 金湖县| 乌拉特后旗| 二手房| 泸溪县| 吐鲁番市| 闽侯县| 绥阳县| 广西| 宁国市| 白朗县| 广平县| 衡山县| 常宁市| 广元市| 赫章县| 巴中市| 六盘水市| 平塘县| 新蔡县| 东莞市| 鹿邑县| 福鼎市| 明水县| 玉龙| 彝良县| 庆云县| 新绛县| 崇信县| 夏河县| 南城县| 大厂| 湖南省| 南和县| 孟津县|