-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Private scope for non-reactive data and methods #1988
Comments
For now the easy way to do it is just attach whatever you want in {
data: ...,
created: function () {
this.myPrivateStuff = {
...
}
}
} Note all Vue api/internal properties start with either |
Using the latest version (1.0.17), it seems properties created within the hook are still reactive after setting? I'm doing this as a work-around:
|
You can do this with created(those private props can't be track by vue), but I don't think it is reasonable that all props defined in vue data, props, methods are in one scope, and for vue 2.x if you define data with _ prefix, vue will tell you can't find this prop. |
I am still really unclear on how to do this/best practices in vue 2. I am loading the Stripe JS client inside a component and want it accessible anywhere within the component. It doesn't need to be available globally though that would be OK if that is the best option. The following code as well as numerous variations doesn't work:
|
I ended up getting it to work with
Not sure if there is a better way though |
Yup, the basic principle is to set the property up later and not declare it in the initialized vue data itself. Do note that if such non-reactive data is "private' to begin with, why expose it to Vue? Why not just keep the data "privately" in the current file module you are working and rely on One approach i've been using so far (sort of like singleton/local services or lazily instatiated stuff..) is to resort to a Example here: (note: ....not plain JS, but can be easily adopted in vanilla JS) |
@Glidias Simple example for you. import Cesium from 'cesium';
export default {
name: 'CesiumViewer',
data(){ return { /* some data */ }; },
created(){ this._cesium = new Cesium.Viewer(this.$el); },
methods: {
drawPath(points){
/* some actions with this._cesium */
}
}
} That's why I need a way to store internal variable of Cesium instance. And this instance is low-level and need no to be reactive. |
My usecase is identical to @DeShadow, only for lunrjs. I initialize the lunr index object and use it throughout the lifetime of my component, however I don't need it reactive itself. |
Is there anything wrong with using
|
No, there's nothing wrong with that really. Many plugins use those as well, i.e. vue-i18n, vue-apollo, vuefire etc. |
I think the bigger problem here is the lack of a clear pattern. Doing init in The logic in OTOH using A better solution IMO would be to have an explicit syntax to declare non reactive component state. Personally I really like the first idea @ardoramor had of using underscores for non reactive properties in |
Well, I can only repeat myself in saying that I don't feel that it is useful/important enough that it's worth adding a dedicated API for this.
Anything in
Again, nothing in We are not React and want to provide abstractions where the use case justfies the added technical debt / maintaince burden. Personally, I just don't feel like this is the case here.
That would be breaking change, so that's a definite "no". |
Not being reactive is the natural state of things in JavaScript. Does that justify sticking non reactive component state anywhere that is non reactive?
It's quite contradictory that you'd argue for putting component state directly in Why does reactive component state live in I can only assume that
It makes sense. I'm only arguing that something need to be done here. |
The passage you quoted explicitly says that - in my experience - nonreactive "state" is usually just constant and options. In that sense it is not state at all, it won't change, it's just some constant you use in your code. And if you talk about it being "directly" in $options, you can put it in a dedicated object, which again, is an established pattern many, many plugins and 3rd party components use to define config options, constants etc. pp. Also, we have flags like "functional:true", "inheritAttrs: false" that live "directly" in $options as well. I don't doubt that there are instances where one has actual component state that can be mutated, but should not be reactive (e.g. for performance reasons), but that, in my experience, is an edge case not deserving its own API.
And I'm arguing that the issue you are seeing is not severe enough to warrant action that leads to an additional API. Not everything needs its own API. Some things can be sufficiently solved by establishing a pattern. I personally think this is one of those cases. So we could add something in the docs about it to define some "official" pattern about how to deal with that,, but extending the API surface won't get my vote at least. If we can agree on looking for an "offical" pattern, we should switch to the vuejs.org repo and open an issue about it there. |
That's not what state means, but ok.
Yes, but that doesn't solve the problem of having a consistent and official pattern for solving this.
Ok then. |
The most 'official' way of declaring non-reactive state is: created () {
this.$_foo = 'bar'
} |
Given this base component...
Is it possible to extend/mixin a component with custom options (in my case
|
I too would really like an explicit place (that isn't manually done on I'm considering doing this:
and then in my component definitions I can do:
|
If using |
It's actually possible to do that, Ams documented here: https://vuejs.org/v2/guide/typescript.html#Augmenting-Types-for-Use-with-Plugins |
I don't see a big problem here. // in utils/no-react.js
import Vue from 'vue'
function VueNoReact(obj) {
// https://github.com/rpkilby/vue-nonreactive
var Observer = (new Vue()).$data.__ob__.constructor;
obj.__ob__ = new Observer({});
return obj;
}
export default VueNoReact Usage: <template>...</template>
<script>
import VueNoReact from '@/utils/no-react'
export default {
data() {
return {
non_tracked_variable: null,
};
},
mounted() {
this.non_tracked_variable = VueNoReact(google_map_or_something_else);
},
};
</script> I think this functionality can be easily implemented using the function above. I don't think it's necessary to include it into the core of Vue. I'm against The good thing about this way is that you can't add reactivity to non-reactive objects by accident. |
While this is a clever hack, it's a hack - you're using an internal API that coudl break anytime, so I won't recommend this as a nice workaround ... |
If @yyx990803 could add this as a official Vue plugin, it would be ok IMHO. As soon as the core changes, the plugin gets an update, too. This is the easiest solution with least effort for him. Could you imagine to use this way when it gets supported officially (and is not considered a hack anymore)? |
I probably don't think so. We are already working on the |
I think it's clear that we have to find a solution for non-reactive data.
The psychological implications of framework development are definitely to consider when changing the approach. When everyone thinks that When something like export default {
static() {
// like data(), but for non-reactive properties
return {
test: null,
};
},
mounted() {
// I can directly use it
this.test = {};
},
}; gets added, it can definitely work as a best practice. But I'm not a core developer for Vue, so I would like to give solutions that are of least effort for the core developers (like a plugin solution). Open question for |
@LinusBorg Should developers assume that the structure/properties of I might feel more secure with this weaker-than-API / stronger-than-convention approach if there was a dedicated property i.e |
I wouldn't like to use something like |
This is the default approach that countless plugins are using to have developers define per-instance options for their plugins, like vue-i18n, vue-apollo, vuelidate etc. pp. |
@samuelantonioli You have this example of a desired official API: export default {
static() {
// like data(), but for non-reactive properties
return {
test: null,
};
},
mounted() {
// I can directly use it
this.test = {};
},
}; Here's aminimal implementation: Vue.mixin({
beforeCreate() {
const static = this.$options.static
if (static && typeof static === 'function') {
staticData = static()
if (typeof staticData === 'object') {
Object.assign(this, staticData)
}
}
}
}) An "official" implementatio would use $options just the same. |
So is it best practice that we use something like this? <template>
<div>
Test: {{$options.static.test}}
</div>
</template>
<script>
export default {
static: {
test: null,
},
mounted() {
this.$options.static.test = google_map_or_something_else;
},
};
</script> If yes, I would love to see it in the guides or the docs so framework users know what the best practice is (which leads to trust and a supported way). |
As mentioned by Evan right at the beginning of this duscussion, people usually just add those properties in |
Ok, looks good. It seems that it wouldn't be too hard to support non-reactive data in the Vue core. Do you plan to add it to the core (e.g. called Otherwise I would write a plugin that injects a global mixin which adds I don't have a problem with any solution (even using |
So basically: // your idea
// as a plugin
// in plugins/static.js
function install(Vue) {
Vue.mixin({
beforeCreate() {
const static = this.$options.static;
if (static && typeof(static) === 'function') {
Object.assign(this, static.apply(this));
} else if (static && typeof(static) === 'object') {
Object.assign(this, static);
}
},
});
}
export default install; And done. I think this could be perfectly added to a guide. Would you approve this as a best practice? |
I've created a plugin and published it on npm which adds support for See Vue-Static or the npm package. Now I can use this as a best practice for my employees. Thanks very much, @LinusBorg ! So for the use case of @CrescentFresh and @DeShadow <script>
import Cesium from 'cesium';
export default {
name: 'CesiumViewer',
data() {
return {
/* some data */
};
},
static() {
return {
_cesium: new Cesium.Viewer(this.$el),
};
},
methods: {
drawPath(points){
/* some actions with this._cesium */
},
},
};
</script> I think this is a clear and easy-to-learn pattern. |
The approaches listed here dont solve the problem when using typescript without vue-class-component. I want to store a timeout handle in the component instance. But just assigning a variable in beforeCreated/created hook doesnt work, because typescript thinks it isnt declared. Having a One could say: "Just use class style components", but while looking pretty sweet, in the background a lot of hackyish stuff gets done. I wonder if there is a plan to fully support class based components without reflecting over the class? Something like this: class MyComp extends Vue {
constructor() {
super({ /* component options not expressable via class constructs */ });
}
}
new MyComp().$mount('#app'); |
Here is a helper function to create a non-reactive object function NonReactive<T>(obj: T) {
return (new Proxy(obj as any, {
get(oTarget, sKey) {
return oTarget[sKey];
},
set(oTarget, sKey, vValue, receiver) {
oTarget[sKey] = vValue;
return true;
},
defineProperty(oTarget, sKey, oDesc) {
return true;
},
}) as any) as T;
} use it like this $store.volatile = NonReactive({
state: 0,
}); Then you can edit $store.volatile.state = 100 |
Is it possible to make nested property to be non reactive ? even with dirty hack ? |
Initializing non-reactive data in the created method. Reference -- this rather long-ish thread about Vue.js scope vuejs/vue#1988
So ... what is the official way to create non-reactive internal data in typescript with
UPDATE Here's a thought: doc says "undefined will not be reactive" -- so perhaps we can do: @Component
export default class Foo extends Vue {
private _internal: string = undefined as any
created () {
this._internal = 'foo'
}
myMethod () {
if (this._internal === 'foo') { ... } // type checked correctly
...
}
} Will that work? |
@shaunc that would work. @Component
export default class Foo extends Vue {
$options!: ComponentOptions<Vue> & {
internal: string
}
beforeCreate() {
this.$options.internal = 'foo'
}
myMethod () {
if (this.$options.internal === 'foo') { ... } // type checked correctly
...
}
} |
Why would someone need this? Can being reactive value cause what problem? |
@kispi at the moment, creating reactive data requires some processing and increases memory consumption. So if you have, for instance, a large array of objects, hard-coded, that has to be displayed on your app, Vue would iterate over each of the objects and create reactive properties even though they would never change. This is expected to be less of an issue with the upcoming release of Vue 3, though, as it'll use Proxies instead of the current Getter/Setter observable. |
@matheusgrieger Never realized that someone might have needed this since I've never needed it. For the case you mentioned, I think I saw the post that he removed reactivity from the large dataset by using Object.freeze inside the mutation. |
With the typescript:
And still the initial value of |
Wouldn't this be a good approach for typescript? import Vue from 'vue'
declare module 'vue/types/vue' {
interface Vue {
myVariable: MyType
}
}
export default Vue.extend({
...
created () {
Object.defineProperty(this, 'myVariable', { writable: true })
},
methods: {
myMethod () {
// use this.myVariable
}
}
}) |
And yet another typescript approach could be (original idea - https://stackoverflow.com/a/60072530/7023723): interface NonReactive {
myVariable: MyType
}
export default Vue.extend({
...
data () {
return {
nonReactive: {} as NonReactive
}
},
created () {
// initialize if needed
this.nonReactive.myVariable = new MyType()
},
methods: {
myMethod () {
// use this.nonReactive.myVariable
}
}
}) This approach utilizes the fact that properties that are not initially described in |
Any Vue 3 composition API suggestions? |
I also came across this looking for a Vue3 TS Composition API suggestion. |
Looks like the old underscores are back. |
Some data that is being kept track of by a component does not need to be made reactive. Currently, there is no one place to organize private data like that. Values stores in
data
, even if prefixed with underscore (e.g.,_privateStuff
), get methods added for every key of the object. I would like a dedicated space designated by Vue for private data and functions that will not be made reactive. I realize that I even now can create something likeprivate
or_private
but there is no guarantee that it will not clash with Vue's future development.It would be great to have a
$private
namespace allocated. It can be populated fromprivate
key of the component configuration object:{
data: function () {...},
props: {},
methods: {},
private: {
// private variables that are not made reactive and are not watched
}
}
The text was updated successfully, but these errors were encountered: