Vue: [feature] Ability to disable Vue observation

Created on 7 Apr 2016  ·  50Comments  ·  Source: vuejs/vue

Update:
If anyone ends up needing this functionality, I've released it as vue-nonreactive with the appropriate admonitions and everything.


We have some non-plain models where we need to disable Vue's observation and walking. An example is a resource model that has access to a cache so that it can lookup related resources. This causes all of the objects in the cache to become watched (probably inefficient), as well as some additional interactions with other code. Currently, we get around this by setting a dummy Observer on the cache. Something similar to...

import get from 'http';
import Resource from 'resource';


new Vue({
    data: { instance: {}, },
    ready() { this.fetch(); },

    methods: {
        fetch() {
            const Observer = Object.getPrototypeOf(this.instance.__ob__).constructor;

            get('/api/frobs')
            .then(function(data) {
                // initialize Resource w/ JSON document
                const resource = new Resource(data);

                // Protect cache with dummy observer
                resource.cache.__ob__ = new Observer({});

                this.instance = resource;
            });
        },
    },
});

This does work, but

  • relies on vue's internals
  • requires an already observed object since we cannot import the Observer class directly.

Proposal:
Add an official method for explicitly disabling Vue's observation/walking. eg, something like...

const someThing = {
  nestedThing: {},
};

// make entire object non-reactive
Vue.nonreactive(someThing);

// make nested object non-reactive
Vue.nonreactive(someThing.nestedThing);
vm.$set('key.path', someThing);

Considerations:

  • What should happen if a user set a reactive key path to an non-reactive object? Should vue warn the user? eg,

``` js
vm.$set('a', Vue.nonreactive({});

// different from..
vm.$set('a', {
someKey: Vue.nonreactive({}),
});
```

  • Should an already reactive object warn the user if attempted to be made non-reactive? eg,

``` js
// error
Vue.nonreactive(vm.$data.a)

// valid
Vue.nonreactive(_.clone(vm.$data.a));
```

Most helpful comment

  1. If you need to skip observation for an object/array in data, use Object.freeze() on it;
  2. You don't need to put an object in data in order to access it on this. If you simply attach it to this in the created() hook, it doesn't get observed at all.

All 50 comments

Won't Object.freeze() not work in your case? It's supported since v1.0.18

  1. If you need to skip observation for an object/array in data, use Object.freeze() on it;
  2. You don't need to put an object in data in order to access it on this. If you simply attach it to this in the created() hook, it doesn't get observed at all.
  • Object.freeze doesn't work here. The cache is updated over time.
  • The main resource _is_ reactive. I'm mostly interested in making the nested cache object non-reactive.

Then maybe it's time to rethink your model design. Why nest those things under something to be observed?

Because the cache is used to dynamically lookup related resources.

eg, we could have Author and Post models. The author model defines a to-many relationship called posts to the post model. This cache contains the relationship data, as well as the related collection.

calling author.posts gets the posts from the cache.

By design, Vue discourages putting complex objects with their own state-mutating mechanism into Vue instance's data. You should be only putting pure state as observed data into Vue instances. You may manipulate these state anyway you want, but the objects responsible for such manipulations should not be part of Vue instance's state.

First, clarifying question - what exactly do you mean by pure state? We have two types of state:

  • model state (permanent, data synchronized with a store. eg, a todo)
  • vue state (temporary, data that controls view behavior. eg, collapse/show the todo list)

But anyway:
That's fair. The model is definitely 'complex', so this request goes against current best practices. Also, my initial example isn't very good - it's just what worked to disable observation. This is more representative of our current setup w/ possible usage:

<!-- layout -->
<post :post="post"></post>
<author :author="author" ><author>
<comments :comments="comments"></comments>
import post from 'components/post';
import author from 'components/author';
import comments from 'components/comments';
/* post = {
 *     template: '...'
 *     props: ['post'],
 *     data: () => {collapsed: false},
 *     ...
 * };  */

new Vue({
    el: 'body',
    data() { 
        instance = postStore.fetch({include: ['author', 'comments.author']})
        Vue.nonreactive(instance.cache)

        return {post: instance, },
    },
    components: {
        post,
        author,
        comments,
    },
    ...
});

Basically, we have a parent vue responsible for placing reusable components in a layout and fetching/binding data to the associated components. The child components don't fetch their own data because the data is different in different contexts. eg, a list of a _user's_ comments vs. a list of a _post's_ comments.

The model is fairly 'dumb' with the exception that related objects are not nested ({post: {author: {}, comments: []}}), but are instead looked up from the cache. eg, post.comments[2].author may be the same exact object as post.author. So, instead of having multiple copies of the author object, we just have one that's looked up from the cache. There isn't any mutation in the above - all data is made on the initial fetch.

Also, not that the request is relevant any more, but an alternative might be to not observe 'private' object members. This could be members with a leading single or maybe double underscore. Downside to this approach is that it would be breaking change.

If anyone ends up needing this functionality, I've released it as vue-nonreactive with the appropriate admonitions and everything.

@rpkilby thanks for sharing!

@rpkilby One way I copy an object and remove the observable/reactivity

var newObj = JSON.parse(JSON.stringify(obj))

Really useful since I want to keep an array of "states" and implement a state history object in vuex.

Edit: This solution was specific to my case. I had an object where I only needed a copy of property values at a moment in time. I did not care about references, dynamic updates etc.

Right now freezing the object is not a long-term solution. [Vue-nonreactive] has Vue as a dependency which is overkill when it comes to doing something that straight forward. Wouldn't a simple check in the code such as instance.__ob__ !== false be enough? This'd allow libraries to ensure that things like caches won't be observed.

class Unobservable {
  construtor() {
    Object.defineProperty(this, '__ob__', {  
      enumerable: false,  configurable: false,
      writable: false, value: false,
    });
  }
}

This is mostly an issue for libraries used in Vue applications (at least for me).

How to tell Vue to only watch (defineProperty to) the 1-level depth of data?

My case is, I want Vue to get notified when data.curObj changed,
but do not with curObj.position, curObj.rotation, etc.

I used to use Object.freeze, but in this case, it would cause Error when three.js tries to assign values to the object.

Do I have to do the below?
(actually I did it in another similar place)

data () {
  return {
    wrapper: Object.freeze({
      actual: [bigData]
    })
  }
},
methods: {
  operation () {
    this.wrapper = Object.freeze({
      actual: [newBigData]
    })
  }
}

// core/observer/watch.js
function _traverse (val: any, seen: ISet) {
  let i, keys
  const isA = Array.isArray(val)
  if ((!isA && !isObject(val)) || !Object.isExtensible(val)) {
    return
  }
  // ...
// core/observer/index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    observerState.shouldConvert &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  // ...
> curObj PerspectiveCamera {uuid: "BD3C14DF-8C2B-4B96-9900-B3DD0EAC1163", name: "PerspectiveCamera", type: "PerspectiveCamera", parent: null, children: Array(0), …}

> Lodash.isPlainObject(curObj) false
> Vue.isPlainObject(curObj) true
  1. Can we add another condition for user to disable observing than just by Object.isExtensible (Object.freeze)?
  2. Can we improve the Vue.isPlainObject detection?

You can use destructuring

var newObj = { ...obj };

This should fix it. It will make the isPlainObject method return false.

/**
 * Makes an object and it's children unobservable by frameworks like Vuejs
 */
class Unobservable {
  /**
   * Overrides the `Object.prototype.toString.call(obj)` result
   * @returns {string} - type name
   * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag}
   */
  get [Symbol.toStringTag]() {
    // Anything can go here really as long as it's not 'Object'
    return 'ObjectNoObserve';
  }
}
>> Object.prototype.toString.call(new Unobservable());
   "[object ObjectNoObserve]"

Hi all, one point that has been lost in the responses is that the data in the original comment is not pure state. In my case, I have a model instance with a private reference to a relationship lookup cache. For example, an article can lookup its author or comments. When I call article.author, this is a dynamic property lookup into that relationship cache and is not a simple attribute access. A few things to consider:

  • The cache is not pure state, so I don't want it to be observed by Vue, as it's a waste of resources.
  • The cache cannot be discarded, as I still need a reference to perform these dynamic lookups/updates.
  • The cache is effectively a singleton and may be updated externally. The app updates accordingly.

In response to some suggestions:

  • Using JSON stringify/parse or object destructuring is not sufficient, as this both duplicates the object & cache, as well as breaking the reference to the original cache. Updating the original cache reference will no longer update the app's instances. Without these dynamic lookups and updates the cache is basically pointless, and it would be better to make the related objects simple attributes on the original model. Also worth noting, these suggestions break the instance methods of these objects.
  • The suggestions by @Mechazawa make sense if you control type creation, and the types are built for use with Vue. In my case, this is an external library that is not tied to Vue, and I don't want to go to the trouble of altering the types in the application. It's much more straightforward for the vue layer to simply mark certain known properties as unobservable.

My only criticism:

Vue-nonreactive has Vue as a dependency which is overkill when it comes to doing something that straight forward.

I'm not sure why this would be a bad thing? You're already using Vue in your application, and the plugin is specific to Vue. If I'm not mistaken, most build tools are smart enough to not create bundles with duplicate dependencies. Regardless, this isn't correct. There is a dev dependency, but not a runtime dependency.


Anyway, I'm glad to see this post has garnered some interest, and I'm sure some of these other solutions will work in a variety of other cases. I simply want to highlight the requirements from my original comment and why the suggestions aren't suitable alternatives for that case.

So, I recently ran into this, and discovered that there's a much easier way to short-circuit Vue's observation logic: _Define a property as non-configurable._

Background

In my application, I have to work with a 3rd party library (OpenLayers), which creates class instances that hold data, and doesn't support any reactivity system. Trying to shoehorn one in has caused so many headaches, let me tell you. So, the only viable solution for a large scale application using this library is to let OpenLayers have things the way it wants, and for me to make Vue play nicer with these horribly nested, uber objects of doom. Prior to finding this issue, my application was using about 3 gigs of ram (on our largest dataset), all of it caused by Vue making these objects reactive. Also, it was really slow when loading. I tried Vue-nonreactive, and it helped, but only to get us down to about 1 gig. Prior to using Vue, the application was sitting around 350megs.

Solution

Anything you don't want to be reactive, simply mark as configurable: false. It's as simple as:

Object.defineProperty(target, 'nested', { configurable: false });

(This stops the nested property, and all of it's properties from being observed.)

That's it! No Vue dependency, and arguably not even incorrect. With that, my application is down to 200megs with our largest dataset. It's simple, and easy, and requires only a documentation change on Vue's side to make it an 'official' way of making something non-reactive.

Interesting - definitely seems like a viable alternative.

Is there a way to temporarily pause the observation reactive and unpause it later?

I have a prop watcher, within that I update a huge object where I don't want to trigger the DOM update only after the whole data preparation is finished.

@intijk Not exactly. See, it depends on what you're trying to do; Vue eventually has to apply your state, so simply pausing while it's calculated doesn't help much. If, instead, you're trying to skip intermediate states, and only apply the final state, just start with a new object, and then assign that object at the end.

For example (psuedocode):

doUpdate()
{
   const state = _.cloneDeep(this.myState);

  // Do intermediate state updates

  this.myState = state;
}

(Normal Vue caveats about object reactivity apply.)

My recommendation would be to use the above configurable trick to skip the sections of your large object that doesn't need to be reactive. If all of it _does_ need to be reactive, I recommend using something like vuex.

@Morgul I already used this trick for long, but the fact is that I no longer want to use this trick anymore.
In my case, the data object is now kind of big, range from 2M to 100M, perform deep copy on such an object is quite painful.

@intijk That sounds incredibly complex for something to bind Vue to. What's the use case here?

@Morgul
I don't think the case is complex, case itself is simple, just data is kind of big. Each time the network will load some indexed visualization log file, and I have a visualization component to display it.

Anyone have thoughts on defining a non-reactive field within a computed property? My first idea depends on non-reactivity of assignment to array...

template: '<div v-html="markdown.render(input, env)"></div>',
props: ['id', 'input'],
computed: {
  env:      function() { return { reactive:this.id, non_reactive:[] } },
  markdown: function() { return Markdown },
},

// within markdown.render():
  env.non_reactive[0] = internal_data;

But that's not exactly self-documenting :-)

Hey guys. I just found this issue and found that I'm facing a problem which is pretty like rpkilby's problem: my project construct a series of Vue Virtual DOMs(or called vnode) from a JSON object. I will use this JSON object to construct a android app. Anyway, this JSON object can be of great size, and when I use this JSON in Vue, it will be observed by Vue. I tried the rpkilby's and Morgul's way, but it doesn't work.(BTW I am in a team, while some guys may not pretty familiar with Vue, and they will probably cause the JSON observed, and my Vue Version is 2.5.16). I'm wondering if we can do this in Vue traverse:
function _traverse (val, seen) {
var i, keys;
var isA = Array.isArray(val);
if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode
|| (val && val['__vueNonReactive__'])) {
return
}
...
As you can see, I add the "val && val['__vueNonReactive__']". Then I modify my JSON object to have the "__vueNonReactive__ = true" with the root node of JSON, and this solve my problem.
I'm wondering if this may cause any problem? And will this be considered as a new feature in Vue that enable the developer can configure a object to be observed by Vue or not by configuring a property of the object?(Object.freeze may change the object to be a immutable object, so it can't fit every situation)

consider this https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true can escape from vue observe procedures.

Today I met a case that, Vue observe a map instance of mapbox-gl, then weird things happed, the map get lighter. But the map instance need to be passed between vue instances. After I add map._isVue = true, problem resolved.

+1 to support that officially. I'm using a large object that don't need reactivity in a component, and disabling unused reactvity reduced the memory object size from 800MB to 43MB
I'm using @magicdawn solution for compatibility issue, but @Mechazawa is the best solution here I think.
For the solution of setting __ob__'s configurable to false works but makes Vue crash when it tries to set the real __ob__.

I've build a Vue plugin that makes it possible to make Vue variables non-reactive (it uses the beforeCreate hook).

This is cleaner than vue-nonreactive - @rpkilby, please look at this comment - your solution won't work for the next version of Vue.


Please look at Vue-Static for a way to make variables non-reactive.

<script>
export default {
    static() {
        return {
            map: null,
        };
    },
    mounted() {
        this.map = new mapboxgl.Map({...}); /* something heavy */
    },
};
</script>

Hi @samuelantonioli - it looks like vue-static does something slightly different, disabling reactivity for an entire object. In contrast, vue-nonreactive is able to disable observation for a single property, while keeping the rest of the object reactive.

That said, it looks like the intents are slightly different. The static properties don't observe changes, but are intended to be rendered to a template. Non-reactive properties aren't meant to be observed, nor are they meant to be rendered.

e.g., my model instance has a reference to an object cache that enables related object lookups. I want to observe/render the instance and the related instance.author, but not the instance._cache.

new Vue({
    data() {
        const instance = postStore.fetch({include: ['author', 'comments.author']})
        Vue.nonreactive(instance._cache)

        return {post: instance, },
    },
    ...
});

Either way, thanks for the heads up. I'll have to look into how the next version is using proxies and see if there's a way to dupe the observer/proxy creation.

@LinusBorg - I don't see an experimental branch. Where is development taking place for the next version?

We're still he concept/experiwmnt stage and haven't published a branch yet. The serious work on this won't begin before we have the 2.6 update out the door, which itself will take some work after we hopefully have vue-cli 3.0 out very soon

Thanks for the update.

So I'm not sure that the problem exists in the same scope once the ES6 Proxies are introduced. In my application I make heavy use of them, and their overhead vs the overhead of vue's current observation seems much smaller. The devil will be in the details, I suspect.

The problem I have with Vue-Static is that it feels redundant. I can build my object in a JS module, import him, and then return his value from a computed function; since it won't be observed, it doesn't matter that the computed function's value will never be recalculated. And, that's much better separation of concerns, since I'm not doing business logic type things in my vue components.

In any event, my trick of setting properties as non-configurable is still the least invasive, least Vue dependent way to handle the problem. There's also no reason to assume that it would break with the ES Proxies; you still probably don't want to observe non-configurable properties. I could be entirely wrong, but we _know_ __ob__ is going away... we don't know about the check for a property being configurable.

Plus, it's been working like a champ in our production code for 8+ months. ;) (We have a similar problem space to @samuelantonioli; we have an OpenLayers map we need to work with inside Vue, without having Vue balloon our memory to 2.4 gigs...)

I agree, if you use another pattern e.g. import modules and use computed properties, you don't need Vue-Static. I just needed a pattern that I could teach to my employees that is easy for them to understand and use. The import-module-and-use-computed-properties pattern isn't that clear IMO.


A bit OT: I'm pretty sure that ES6 Proxies are a good way to go, but I have some concerns about browser compatibility (IE11 and below don't support it). I'm interested if there will be a compatibility layer / some type of polyfill so we can use Vue for projects with stricter browser requirements.

How to tell Vue to only watch (defineProperty to) the 1-level depth of data?

+1 for this idea, it will be easier to structure the data for using Vue with external Graphic lib (which normally has multi-level nested large objects).

What about only specify some properties to be reactive?

I might be missing something obvious here, but using this.propertyName = { /* something big here */ };
in the mounted() hook isn't a solution on having non-observed properties?

set _isVue will make vue-devtool crash, use this function instead

export default function setIsVue(val) {
    if (!val) return

    Object.defineProperty(val, '_isVue', {
        value: true,
        enumerable: false,
        configurable: true,
    })

    // vue-devtool
    // https://github.com/vuejs/vue-devtools/blob/c309065c57f6579b778341ea37042fdf51a9fc6c/src/backend/index.js#L616
    // 因为有 _isVue 属性
    if (process.env.NODE_ENV !== 'production') {
        if (!val.$options) {
            Object.defineProperty(val, '$options', {
                value: {},
                enumerable: false,
                configurable: true,
            })
        }

        if (!val._data) {
            Object.defineProperty(val, '_data', {
                value: {},
                enumerable: false,
                configurable: true,
            })
        }
    }

    return val
}

It got lost in the noise but marking a property as non-configurable really seems like the fix.

Object.keys(scene).forEach((key)=>{
  Object.defineProperty(target, 'nested', { configurable: false });
});

Really nice when I need to pass around a THREE.Scene but don't want literally the entire scene graph to become a mess of observables. And then I can still pass around the original object and it can be reactive based on that. Perfect!

Still a problem.
I have objects containing many nested levels of properties which i want to hold non-reactive.
1

Even if i use

It got lost in the noise but marking a property as non-configurable really seems like the fix.

Object.keys(scene).forEach((key)=>{
  Object.defineProperty(target, 'nested', { configurable: false });
});

Really nice when I need to pass around a THREE.Scene but don't want literally the entire scene graph to become a mess of observables. And then I can still pass around the original object and it can be reactive based on that. Perfect!

or

consider this https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true can escape from vue observe procedures.

Today I met a case that, Vue observe a map instance of mapbox-gl, then weird things happed, the map get lighter. But the map instance need to be passed between vue instances. After I add map._isVue = true, problem resolved.

Properties in nested objects become reactive.

I tried to do it recursively, but Maximum call stack size exceeded, and it causes more lags.

@Mitroright You will have to do this recursively, but I suspect your approach might not have quite been correct.

The problem here is how Vue handles Arrays; simply marking the geoJsonData property nonconfigurable likely isn't going to work (I've had issues with that, but never dug into the 'why'.)

Try something like this:

function makeArrayNonConfigurable(objects)
{
    objects.forEach((obj) =>
    {
        Object.keys(obj).forEach((key) =>
        {
            Object.defineProperty(obj, key, { configurable: false });
        });
    });
}

We're only going one level deep, because that's all we need to do; Vue won't look in nested object properties. However, it does seem to look at objects inside of arrays on properties marked as non-configurable, hence the problem you're running into.

Now, I will tell you that with 10,000 objects to go through this will churn for a few seconds or so the first time it goes through this array; this should be viewed as just part of the cost of retrieving this data. Frankly, I recommend consuming those objects with a class (I use a class that returns a proxy from it's constructor), and then caching those objects by a unique id, if you're loading them more than a single time during the application's life-span. But that's really a design detail, and not exactly related to your issue.

I found solution here:
https://medium.com/@deadbeef404/tell-vue-js-to-stop-wasting-time-and-render-faster-7c3f7d2acaab

In short, make utility function:

import Vue from 'vue';

const Observer = (new Vue()).$data.__ob__.constructor;

export function makeNonreactive(obj) {
    obj.__ob__ = new Observer({});
}

Hi @Mitoright. Just for reference, the article is describing the guts of vue-nonreactive. The difference is whether you want to use the code as a plugin (via vue-nonreactive) or as a helper function. Also, vue-nonreactive is mentioned in the update right at the top of this issue's description.

As vue-devtool got update again, https://github.com/vuejs/vue/issues/2637#issuecomment-434154442 will make vue-devtool crash again

I suggest vue-nonreactive like solutions 😆

to make __ob__ none enumerable, use defineProperty

vue-free.js

import Vue from 'vue'

const Observer = new Vue().$data.__ob__.constructor

function prevent(val) {
    if (val) {
        // Set dummy observer on value
        Object.defineProperty(val, '__ob__', {
            value: new Observer({}),
            enumerable: false,
            configurable: true,
        })
    }

    return val
}

// vue global
Vue.VUE_FREE = prevent

// window
global.VUE_FREE = prevent

// default export
export default prevent

Figure i would give my 2 cents and solution on this.

I also had similar issues implementing both the Freeze concept and fake Observer. My data comes from the server and is a recursive TreeNode scenario, My project is also using vuex in this case which added a layer to the issues seen. I constantly got Maximum call stack size exceeded due to vues object.keys loop. I had tried Freeze and setting data inside a fake VNode, but neither seemed to stop the recursion issues.

I finally stepped back and wrapped my "non-reactive" properties using the classic Revealing Module Pattern

this is the class (ES6/typescript) but same can be applied in plain vue as well

import {  first, forEach } from 'lodash';

export class TreeNode {
    internalRefsInstance: () => { getParent: () => TreeNode; setParent: (parent: TreeNode) => void; getChildNodes: () => TreeNode[]; setChildNode: (childNode: TreeNode) => number; };

    get visitedDate(): string | undefined {
        return this._visitedDates.get(this.id) || undefined;
    }

    isSelectedTreeNode: boolean = false;
    showSubheader: boolean = false;
    showHelp: boolean = false;
    treeNodeIconName: string = 'empty';
    childTreeNodeCount: number = 0;

    constructor(public id: string,
        public componentName: string,
        private _visitedDates: Map<string, string>,
        public isActive: boolean = true,
        public nextFlow?: string,
        public prevFlow?: string,
        parent: TreeNode | undefined = undefined) {

        //invoke the internal refs module to create our static instance func to get the values from
        this.internalRefsInstance = this.nonReactiveModule();
        this.internalRefsInstance().setParent(parent);
    }
    nonReactiveModule = () => {
        let _parent: TreeNode | undefined = undefined;
        let _childNodes: TreeNode[] = [];
        const _getParent = (): TreeNode | undefined => {
            return _parent;
        };
        const _setParent = (parent: TreeNode | undefined): void => {
            _parent = parent;
        };
        const _getChildNodes = (): TreeNode[] => {
            return _childNodes || [];
        };
        const _setChildNode = (childNode: TreeNode): number => {
            if (!_childNodes) {
                _childNodes = [];
            }
            _childNodes.push(childNode);
            return _childNodes.length;
        };
        const returnObj = {
            getParent: _getParent,
            setParent: _setParent,
            getChildNodes: _getChildNodes,
            setChildNode: _setChildNode,
        };
        return () => { return returnObj; };
    }

    getParent(): TreeNode | undefined {
        return this.internalRefsInstance().getParent();
    }

    getChildNodes(): TreeNode[] {
        return this.internalRefsInstance().getChildNodes();
    }

    setChildNode(childFlow: TreeNode): void {
        this.childTreeNodeCount = this.internalRefsInstance().setChildNode(childFlow);
    }

    clone(parent: TreeNode | undefined = undefined): TreeNode {
        const newInstance = new TreeNode(this.id, this.componentName, this._visitedDates, this.isActive, this.nextFlow, this.prevFlow, parent);
        newInstance.showHelp = this.showHelp;
        newInstance.showSubheader = this.showSubheader;
        newInstance.isSelectedTreeNode = this.isSelectedTreeNode;
        forEach(this.getChildNodes(), (flow: TreeNode) => {
            newInstance.childTreeNodeCount = newInstance.internalRefsInstance().setChildNode(flow.clone(newInstance));
        });
        return newInstance;
    }

    setVisitedDates(visitedDates: Map<string, string>): void {
        this._visitedDates = visitedDates;
        forEach(this.getChildNodes(), (flow: TreeNode) => {
            flow.setVisitedDates(visitedDates);
        });
    }

    setAsSelected(setParent: boolean = true, setAllFirstChildren: boolean = true): void {
        this.isSelectedTreeNode = true;
        if (setAllFirstChildren) {
            const firstChildFlow = first(this.getChildNodes());
            if (firstChildFlow) {
                firstChildFlow.setAsSelected(false, true);
            }
        }

        if (setParent && this.getParent()) {
            this.getParent()!.setAsSelected(setParent);
        }
    }
    resetSelected(resetChildren: boolean = true): void {
        this.isSelectedTreeNode = false;
        if (resetChildren) {
            forEach(this.getChildNodes(), (flow: TreeNode) => {
                flow.resetSelected(resetChildren);
            });
        }
    }
}

Computed did not work in my case cause i still needed the full object and not a handler sending me the change on the computed. At least if im understanding how a deep watcher would work against a computed subsetted result.

I think taking this a next level up would be to create a injected nonrecursive helper or decorator to speed up the process. Any useful feed back would be great.

seems like somebody solved this issue already,

hope u check all the details at https://github.com/vuejs/vue/issues/4384

Hi, I created #10265 especially as I was not aware of this former issue.
I'm just wondering what would be the advised solution to be future proof and stay compatible with Vue when it will use Proxy.
Using Object.defineProperty with configurable: false works well (but it does not prevent a property having existing setter/getter to become reactive).
Will this technique still be usable with Vue 3 ?
Thanks

@colin-guyon configurable: false would probably _not_ work, especially since there seems to be https://github.com/vuejs/vue-next/blob/d9c6ff372c10dde8b496ee32f2b9a246edf66a35/packages/reactivity/src/reactive.ts#L159. If it ends up in Vue 3.x, there would be an official API to mark an object non-reactive.

Note that just like the new proposed Vue.observable, when a property is set on a reactive object, that _new_ value wouldn't be tainted and just be left as-is. Instead, the _getter_ would return a reactive proxy for it, creating one if it doesn't already exist in the cache.

A fortunate news is that, as long as you don't do much on _that_ reactive proxy you should probably be fine. If memory hog or interoperability is a concern then you certainly don't need to worry about it since whatever the object is — a giant collection of data or some foreign object from a library which behavior is unpredictable should reactivity be applied on it, you say — _nothing_ about it gets touched, after all. In this sense Vue 3.x actually solves a lot of corner cases where this feature would otherwise be useful.

At the moment, the vue-next code seems to exclude symbol keys from being reactive, just like what the current version of Vue does.

consider this https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true can escape from vue observe procedures.

Today I met a case that, Vue observe a map instance of mapbox-gl, then weird things happed, the map get lighter. But the map instance need to be passed between vue instances. After I add map._isVue = true, problem resolved.

I was using this method until I got bitten by Vue dev tools bugging out because it sees _isVue is true and it thinks the object is a Vue component instance but it isn't.

The only hack I've seen with no serious side effects seems to be OP's approach with the vue-nonreactive library.

Are there alternative solutions in V3 for this?

@HunderlineK shallowRef, shallowReactive, markRaw

Was this page helpful?
0 / 5 - 0 ratings