Vue: [característica] Posibilidad de deshabilitar la observación de Vue

Creado en 7 abr. 2016  ·  50Comentarios  ·  Fuente: vuejs/vue

Actualizar:
Si alguien termina necesitando esta funcionalidad, la publiqué como


Tenemos algunos modelos que no son simples en los que necesitamos desactivar la observación y el caminar de Vue. Un ejemplo es un modelo de recurso que tiene acceso a un caché para que pueda buscar recursos relacionados. Esto hace que todos los objetos de la caché se vean (probablemente ineficaces), así como algunas interacciones adicionales con otro código. Actualmente, solucionamos esto configurando un Observador ficticio en el caché. Algo parecido a ...

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

Esto funciona, pero

  • se basa en los componentes internos de vue
  • requiere un objeto ya observado ya que no podemos importar la clase Observer directamente.

Propuesta:
Agregue un método oficial para deshabilitar explícitamente la observación / caminar de Vue. por ejemplo, algo como ...

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

Consideraciones:

  • ¿Qué debería suceder si un usuario establece una ruta de clave reactiva a un objeto no reactivo? ¿Debería vue advertir al usuario? p.ej,

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

// diferente de ..
vm. $ set ('a', {
someKey: Vue.nonreactive ({}),
});
''

  • ¿Debe un objeto ya reactivo advertir al usuario si se intenta convertirlo en no reactivo? p.ej,

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

// válido
Vue.nonreactive (_. Clone (vm. $ Data.a));
''

Comentario más útil

  1. Si necesita omitir la observación de un objeto / matriz en data , use Object.freeze() en él;
  2. No es necesario poner un objeto en data para acceder a él en this . Si simplemente lo adjunta a this en el gancho created() , no se observará en absoluto.

Todos 50 comentarios

¿No funcionará Object.freeze() en su caso? Es compatible desde v1.0.18

  1. Si necesita omitir la observación de un objeto / matriz en data , use Object.freeze() en él;
  2. No es necesario poner un objeto en data para acceder a él en this . Si simplemente lo adjunta a this en el gancho created() , no se observará en absoluto.
  • Object.freeze no funciona aquí. La caché se actualiza con el tiempo.
  • El principal recurso _es_ reactivo. Lo que más me interesa es hacer que el objeto de caché anidado no sea reactivo.

Entonces tal vez sea el momento de repensar el diseño de su modelo. ¿Por qué anidar esas cosas debajo de algo para ser observado?

Porque la caché se usa para buscar dinámicamente recursos relacionados.

por ejemplo, podríamos tener modelos Author y Post . El modelo de autor define una relación a muchos llamada posts con el modelo de publicación. Esta caché contiene los datos de la relación, así como la colección relacionada.

llamar a author.posts obtiene las publicaciones del caché.

Por diseño, Vue desaconseja poner objetos complejos con su propio mecanismo de mutación de estado en la instancia de Vue data . Solo debe colocar datos de estado puro como observados en instancias de Vue. Puede manipular estos estados de la forma que desee, pero los objetos responsables de tales manipulaciones no deben ser parte del estado de la instancia de Vue.

Primero, pregunta aclaratoria: ¿qué quiere decir exactamente con estado puro ? Tenemos dos tipos de estado:

  • estado del modelo (permanente, datos sincronizados con una tienda, por ejemplo, una tarea)
  • estado vue (temporal, datos que controlan el comportamiento de la vista, por ejemplo, contraer / mostrar la lista de tareas pendientes)

Pero de todos modos:
Eso es justo. El modelo es definitivamente 'complejo', por lo que esta solicitud va en contra de las mejores prácticas actuales. Además, mi ejemplo inicial no es muy bueno, es solo lo que funcionó para deshabilitar la observación. Esto es más representativo de nuestra configuración actual con posible uso:

<!-- 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,
    },
    ...
});

Básicamente, tenemos un vue padre responsable de colocar componentes reutilizables en un diseño y obtener / vincular datos a los componentes asociados. Los componentes secundarios no obtienen sus propios datos porque los datos son diferentes en diferentes contextos. por ejemplo, una lista de los comentarios de un _usuario_ frente a una lista de los comentarios de una _ publicación_.

El modelo es bastante 'tonto' con la excepción de que los objetos relacionados no están anidados ( {post: {author: {}, comments: []}} ), sino que se buscan en la caché. por ejemplo, post.comments[2].author puede ser exactamente el mismo objeto que post.author . Entonces, en lugar de tener múltiples copias del objeto de autor, solo tenemos una que se busca en el caché. No hay ninguna mutación en lo anterior: todos los datos se realizan en la búsqueda inicial.

Además, no es que la solicitud ya sea relevante, pero una alternativa podría ser no observar a los miembros del objeto "privado". Estos podrían ser miembros con un guión bajo simple o doble al principio. La desventaja de este enfoque es que sería un cambio radical.

Si alguien termina necesitando esta funcionalidad, la publiqué como

@rpkilby gracias por compartir!

@rpkilby Una forma de copiar un objeto y eliminar el observable / reactividad

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

Realmente útil ya que quiero mantener una matriz de "estados" e implementar un objeto de historial de estado en vuex.

Editar : esta solución fue específica para mi caso. Tenía un objeto en el que solo necesitaba una copia de los valores de propiedad en un momento determinado. No me importaban las referencias, las actualizaciones dinámicas, etc.

En este momento, congelar el objeto no es una solución a largo plazo. [Vue-nonreactive] tiene a Vue como una dependencia que es excesiva cuando se trata de hacer algo tan sencillo. ¿No sería suficiente una simple verificación en el código como instance.__ob__ !== false ? Esto permitiría a las bibliotecas asegurarse de que no se observarán cosas como los cachés.

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

Esto es principalmente un problema para las bibliotecas utilizadas en aplicaciones Vue (al menos para mí).

¿Cómo decirle a Vue que solo mire (defineProperty a) la profundidad de datos de 1 nivel?

Mi caso es que quiero que Vue reciba una notificación cuando data.curObj cambió,
pero no con curObj.position , curObj.rotation , etc.

Solía ​​usar Object.freeze , pero en este caso, causaría un error cuando three.js intenta asignar valores al objeto.

¿Tengo que hacer lo siguiente?
(de hecho lo hice en otro lugar similar)

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. ¿Podemos agregar otra condición para que el usuario deshabilite la observación que solo con Object.isExtensible ( Object.freeze )?
  2. ¿Podemos mejorar la detección de Vue.isPlainObject?

Puedes usar desestructuración

var newObj = { ...obj };

Esto debería arreglarlo. Hará que el método isPlainObject devuelva falso.

/**
 * Makes an object and it's children unobservable by frameworks like Vuejs
 */
class Unobservable {
  /**
   * Overrides the `Object.prototype.toString.call(obj)` result
   * <strong i="6">@returns</strong> {string} - type name
   * <strong i="7">@see</strong> {<strong i="8">@link</strong> 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]"

Hola a todos, un punto que se ha perdido en las respuestas es que los datos del comentario original no son un estado puro . En mi caso, tengo una instancia de modelo con una referencia privada a un caché de búsqueda de relaciones. Por ejemplo, un article puede buscar su author o comments . Cuando llamo a article.author , esta es una búsqueda de propiedad dinámica en esa caché de relación y no es un simple acceso a atributos. Algunas cosas a considerar:

  • El caché no es de estado puro, por lo que no quiero que Vue lo observe, ya que es un desperdicio de recursos.
  • El caché no se puede descartar, ya que todavía necesito una referencia para realizar estas búsquedas / actualizaciones dinámicas.
  • La caché es efectivamente un singleton y puede actualizarse externamente. La aplicación se actualiza en consecuencia.

En respuesta a algunas sugerencias:

  • El uso de JSON stringify / parse o la desestructuración de objetos no es suficiente, ya que esto duplica el objeto y la caché, además de romper la referencia a la caché original. La actualización de la referencia de caché original ya no actualizará las instancias de la aplicación. Sin estas búsquedas y actualizaciones dinámicas, la caché es básicamente inútil, y sería mejor convertir los objetos relacionados en atributos simples en el modelo original. También vale la pena señalar que estas sugerencias rompen los métodos de instancia de estos objetos.
  • Las sugerencias de @Mechazawa tienen sentido si controlas la creación de tipos, y los tipos están diseñados para usarse con Vue. En mi caso, esta es una biblioteca externa que no está vinculada a Vue, y no quiero tomarme la molestia de alterar los tipos en la aplicación. Es mucho más sencillo para la capa vue marcar simplemente ciertas propiedades conocidas como no observables.

Mi única crítica:

Vue-nonreactive tiene a Vue como una dependencia que es excesiva cuando se trata de hacer algo tan sencillo.

No estoy seguro de por qué esto sería algo malo. Ya estás usando Vue en tu aplicación y el complemento es específico de Vue. Si no me equivoco, la mayoría de las herramientas de compilación son lo suficientemente inteligentes como para no crear paquetes con dependencias duplicadas. Independientemente, esto no es correcto . Hay una dependencia de desarrollo, pero no una dependencia de tiempo de ejecución.


De todos modos, me alegra ver que esta publicación ha despertado cierto interés y estoy seguro de que algunas de estas otras soluciones funcionarán en una variedad de otros casos. Simplemente quiero resaltar los requisitos de mi comentario original y por qué las sugerencias no son alternativas adecuadas para ese caso.

Entonces, recientemente me encontré con esto y descubrí que hay una manera mucho más fácil de cortocircuitar la lógica de observación de Vue: _Definir una propiedad como no configurable._

Fondo

En mi aplicación, tengo que trabajar con una biblioteca de terceros (OpenLayers), que crea instancias de clase que contienen datos y no admite ningún sistema de reactividad. Tratar de calzar uno ha causado tantos dolores de cabeza, déjame decirte. Entonces, la única solución viable para una aplicación a gran escala que usa esta biblioteca es permitir que OpenLayers tenga las cosas de la manera que quiera, y que yo haga que Vue juegue mejor con estos súper objetos de fatalidad horriblemente anidados. Antes de encontrar este problema, mi aplicación estaba usando aproximadamente 3 gigas de RAM (en nuestro conjunto de datos más grande), todo causado por Vue que hacía que estos objetos fueran reactivos. Además, fue muy lento al cargar. Probé Vue-no reactivo, y ayudó, pero solo para reducirnos a aproximadamente 1 concierto. Antes de usar Vue, la aplicación tenía alrededor de 350 megas.

Solución

Todo lo que no desee que sea reactivo, simplemente márquelo como configurable: false . Es tan simple como:

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

(Esto evita que se observen la propiedad nested y todas sus propiedades).

¡Eso es! Sin dependencia de Vue, y posiblemente ni siquiera sea incorrecto. Con eso, mi aplicación se ha reducido a 200 megas con nuestro conjunto de datos más grande. Es simple y fácil, y solo requiere un cambio de documentación por parte de Vue para que sea una forma 'oficial' de hacer algo no reactivo.

Interesante: definitivamente parece una alternativa viable.

¿Hay alguna manera de pausar temporalmente la observación reactiva y reanudarla más tarde?

Tengo un observador de accesorios, dentro del cual actualizo un objeto enorme en el que no quiero activar la actualización DOM solo después de que finaliza toda la preparación de datos.

@intijk No exactamente. Mira, depende de lo que estés intentando hacer; Vue eventualmente tiene que aplicar su estado, por lo que simplemente hacer una pausa mientras se calcula no ayuda mucho. Si, en cambio, está tratando de omitir estados intermedios y solo aplica el estado final, simplemente comience con un nuevo objeto y luego asigne ese objeto al final.

Por ejemplo (psuedocode):

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

  // Do intermediate state updates

  this.myState = state;
}

(Se aplican las advertencias normales de Vue sobre la reactividad del objeto).

Mi recomendación sería utilizar el truco configurable para omitir las secciones de su objeto grande que no necesitan ser reactivas. Si todo _de_ necesita ser reactivo, recomiendo usar algo como vuex .

@Morgul Ya usé este truco durante mucho tiempo, pero el hecho es que ya no quiero usar este truco.
En mi caso, el objeto de datos ahora es bastante grande, varía de 2M a 100M, realizar una copia profunda en un objeto de este tipo es bastante doloroso.

@intijk Eso suena increíblemente complejo para vincular a Vue con algo. ¿Cuál es el caso de uso aquí?

@Morgul
No creo que el caso sea complejo, el caso en sí es simple, solo los datos son un poco grandes. Cada vez que la red cargará algún archivo de registro de visualización indexado, tengo un componente de visualización para mostrarlo.

¿Alguien tiene alguna idea sobre la definición de un campo no reactivo dentro de una propiedad calculada? Mi primera idea depende de la no reactividad de la asignación a la matriz ...

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;

Pero eso no es exactamente autodocumentarse :-)

Hola chicos. Acabo de encontrar este problema y descubrí que estoy enfrentando un problema que se parece bastante al problema de rpkilby: mi proyecto construye una serie de DOM virtuales Vue (o llamado vnode) a partir de un objeto JSON. Usaré este objeto JSON para construir una aplicación de Android. De todos modos, este objeto JSON puede ser de gran tamaño, y cuando use este JSON en Vue, será observado por Vue. Probé el método de rpkilby y Morgul, pero no funciona. (Por cierto, estoy en un equipo, mientras que algunos tipos pueden no estar muy familiarizados con Vue, y probablemente causarán el JSON observado, y mi versión de Vue es 2.5.16 ). Me pregunto si podemos hacer esto en Vue traverse:
función _traverse (val, visto) {
var i, claves;
var isA = Array.isArray (val);
if ((! isA &&! isObject (val)) || Object.isFrozen (val) || val instancia de VNode
|| (val && val ['__ vueNonReactive__'])) {
regreso
}
...
Como puede ver, agrego el "val && val ['__ vueNonReactive__']". Luego modifico mi objeto JSON para tener "__vueNonReactive__ = true" con el nodo raíz de JSON, y esto resuelve mi problema.
Me pregunto si esto puede causar algún problema. ¿Y se considerará esto como una nueva característica en Vue que permitirá que el desarrollador pueda configurar un objeto para ser observado por Vue o no configurando una propiedad del objeto? (Object.freeze puede cambiar el objeto para que sea un objeto inmutable, por lo que no se adapta a todas las situaciones)

considere esto https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true puede escapar de los procedimientos de vue observe.

Hoy conocí un caso que, Vue observó una instancia de mapa de mapbox-gl, luego sucedieron cosas extrañas, el mapa se volvió más claro. Pero la instancia del mapa debe pasarse entre instancias de vue. Después de agregar map._isVue = true , el problema se resolvió.

+1 para apoyar eso oficialmente. Estoy usando un objeto grande que no necesita reactividad en un componente, y deshabilitar la reactividad no utilizada redujo el tamaño del objeto de memoria de 800 MB a 43 MB
Estoy usando la solución @magicdawn por problemas de compatibilidad, pero creo que
Para la solución de configurar __ob__ configurable en falso funciona, pero hace que Vue se bloquee cuando intenta configurar el __ob__ real.

He creado un complemento de Vue que hace posible que las variables de Vue no sean reactivas (usa el gancho beforeCreate).

Esto es más limpio que vue- nonreactive : este comentario : su solución no funcionará para la próxima versión de Vue.


Consulte Vue-Static para ver una forma de hacer que las variables no sean reactivas.

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

Hola @samuelantonioli : parece que vue-static hace algo ligeramente diferente, deshabilitando la reactividad para un objeto completo. Por el contrario, vue-nonreactive puede deshabilitar la observación para una sola propiedad , mientras mantiene el resto del objeto reactivo.

Dicho esto, parece que las intenciones son ligeramente diferentes. Las propiedades estáticas no observan cambios, pero están diseñadas para ser representadas en una plantilla. Las propiedades no reactivas no están destinadas a ser observadas, ni tampoco para ser renderizadas.

por ejemplo, mi instancia de modelo tiene una referencia a un caché de objetos que permite búsquedas de objetos relacionados. Quiero observar / representar el instance y el instance.author , pero no el instance._cache .

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

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

De cualquier manera, gracias por el aviso. Tendré que investigar cómo la próxima versión usa proxies y ver si hay una manera de engañar a la creación del observador / proxy.

@LinusBorg : no veo una rama experimental. ¿Dónde se está desarrollando la próxima versión?

Todavía estamos en la etapa de concepto / experiencia y aún no hemos publicado una rama. El trabajo serio en esto no comenzará antes de que tengamos la actualización 2.6, que a su vez requerirá algo de trabajo después de que, con suerte, tengamos vue-cli 3.0 muy pronto.

Gracias por la actualización.

Por lo tanto, no estoy seguro de que el problema exista en el mismo ámbito una vez que se introduzcan los proxies ES6. En mi aplicación, los utilizo mucho, y su sobrecarga frente a la sobrecarga de la observación actual de vue parece mucho menor. El diablo estará en los detalles, sospecho.

El problema que tengo con Vue-Static es que se siente redundante. Puedo construir mi objeto en un módulo JS, importarlo y luego devolver su valor desde una función calculada; dado que no se observará, no importa que el valor de la función calculada nunca se vuelva a calcular. Y esa es una separación de preocupaciones mucho mejor, ya que no estoy haciendo cosas de tipo lógico empresarial en mis componentes vue.

En cualquier caso, mi truco de establecer propiedades como no configurables sigue siendo la forma menos invasiva y menos dependiente de Vue de manejar el problema. Tampoco hay razón para suponer que rompería con los Proxies ES; probablemente aún no desee observar propiedades no configurables. Podría estar completamente equivocado, pero _know_ __ob__ va a desaparecer ... no sabemos si el cheque de una propiedad es configurable.

Además, ha estado funcionando como un campeón en nuestro código de producción durante más de 8 meses. ;) (Tenemos un espacio de problemas similar a @samuelantonioli; tenemos un mapa de OpenLayers con el que necesitamos trabajar dentro de Vue, sin que Vue aumente nuestra memoria a 2.4 gigas ...)

Estoy de acuerdo, si usa otro patrón, por ejemplo, importar módulos y usa propiedades calculadas, no necesita Vue-Static . Solo necesitaba un patrón que pudiera enseñar a mis empleados y que fuera fácil de entender y usar. El patrón de importación-módulo-y-uso-propiedades calculadas no es tan claro en mi opinión.


Un poco más: estoy bastante seguro de que los proxies ES6 son un buen camino a seguir, pero tengo algunas preocupaciones sobre la compatibilidad del navegador (IE11 y versiones anteriores no lo admiten). Me interesa si habrá una capa de compatibilidad / algún tipo de polyfill para que podamos usar Vue para proyectos con requisitos de navegador más estrictos.

¿Cómo decirle a Vue que solo mire (defineProperty a) la profundidad de datos de 1 nivel?

+1 para esta idea, será más fácil estructurar los datos para usar Vue con una biblioteca gráfica externa (que normalmente tiene objetos grandes anidados de varios niveles).

¿Qué tal si solo se especifican algunas propiedades para que sean reactivas?

Puede que me esté perdiendo algo obvio aquí, pero usando this.propertyName = {/ * algo grande aquí * /};
en el gancho montado () ¿no es una solución tener propiedades no observadas?

Hola @vlahanas. Consulte https://github.com/vuejs/vue/issues/2637#issuecomment -403630456.

set _isVue hará que vue-devtool se bloquee, use esta función en su lugar

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
}

Se perdió en el ruido, pero marcar una propiedad como no configurable realmente parece ser la solución.

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

Realmente agradable cuando necesito pasar un THREE.Scene pero no quiero que literalmente todo el gráfico de escena se convierta en un lío de observables. Y luego todavía puedo pasar el objeto original y puede ser reactivo basado en eso. ¡Perfecto!

Sigue siendo un problema.
Tengo objetos que contienen muchos niveles anidados de propiedades que quiero mantener no reactivos.
1

Incluso si uso

Se perdió en el ruido, pero marcar una propiedad como no configurable realmente parece ser la solución.

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

Realmente agradable cuando necesito pasar un THREE.Scene pero no quiero que literalmente todo el gráfico de escena se convierta en un lío de observables. Y luego todavía puedo pasar el objeto original y puede ser reactivo basado en eso. ¡Perfecto!

o

considere esto https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true puede escapar de los procedimientos de vue observe.

Hoy conocí un caso que, Vue observó una instancia de mapa de mapbox-gl, luego sucedieron cosas extrañas, el mapa se volvió más claro. Pero la instancia del mapa debe pasarse entre instancias de vue. Después de agregar map._isVue = true , el problema se resolvió.

Las propiedades de los objetos anidados se vuelven reactivas.

Traté de hacerlo de forma recursiva, pero Maximum call stack size exceeded , y causa más retrasos.

@Mitroright Tendrá que hacer esto de forma recursiva, pero sospecho que su enfoque podría no haber sido del todo correcto.

El problema aquí es cómo Vue maneja las matrices; simplemente marcar la propiedad geoJsonData no configurable probablemente no funcionará (he tenido problemas con eso, pero nunca profundicé en el 'por qué').

Prueba algo como esto:

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

Solo vamos a un nivel de profundidad, porque eso es todo lo que tenemos que hacer; Vue no buscará en las propiedades de los objetos anidados. Sin embargo, parece mirar objetos dentro de matrices en propiedades marcadas como no configurables, de ahí el problema con el que se está encontrando.

Ahora, les diré que con 10,000 objetos por recorrer, esto se agitará durante unos segundos más o menos la primera vez que pase por esta matriz; esto debe considerarse solo como parte del costo de recuperar estos datos. Francamente, recomiendo consumir esos objetos con una clase (yo uso una clase que devuelve un proxy de su constructor) y luego almacenar en caché esos objetos con una identificación única, si los está cargando más de una vez durante la vida de la aplicación. lapso. Pero eso es realmente un detalle de diseño y no está exactamente relacionado con su problema.

Encontré una solución aquí:
https://medium.com/@deadbeef404/tell -vue-js-para-detener-perder-tiempo-y-renderizar-más rápido-7c3f7d2acaab

En resumen, haga que la utilidad funcione:

import Vue from 'vue';

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

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

Hola @Mitoright. Solo como referencia, el artículo describe las entrañas de vue-nonreactive . La diferencia es si desea utilizar el código como un complemento (a través de vue-nonreactive ) o como una función auxiliar. Además, vue-nonreactive se menciona en la actualización justo en la parte superior de la descripción de este problema.

Como vue-devtool se actualizó nuevamente, https://github.com/vuejs/vue/issues/2637#issuecomment -434154442 hará que vue-devtool se bloquee nuevamente

Sugiero vue-nonreactive como soluciones 😆

para hacer que __ob__ ninguno sea 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

Imagino que daría mis 2 centavos y una solución a esto.

También tuve problemas similares al implementar tanto el concepto Freeze como el Observer falso. Mis datos provienen del servidor y es un escenario TreeNode recursivo. Mi proyecto también está usando vuex en este caso, lo que agregó una capa a los problemas observados. Constantemente obtengo Maximum call stack size exceeded debido al bucle vues object.keys. Había intentado Freeze y estableciendo datos dentro de un VNode falso, pero ninguno parecía detener los problemas de recursividad.

Finalmente di un paso atrás y envolví mis propiedades "no reactivas" usando el patrón de módulo revelador clásico

esta es la clase (ES6 / mecanografiado) pero lo mismo se puede aplicar en vue simple también

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 no funcionó en mi caso porque todavía necesitaba el objeto completo y no un controlador que me enviara el cambio en el calculado. Al menos si entiendo cómo funcionaría un observador profundo frente a un resultado subconjunto calculado.

Creo que llevar esto al siguiente nivel sería crear un ayudante o decorador no recursivo inyectado para acelerar el proceso. Cualquier retroalimentación útil sería genial.

parece que alguien ya resolvió este problema,

Espero que compruebes todos los detalles en https://github.com/vuejs/vue/issues/4384

Hola, creé # 10265 especialmente porque no estaba al tanto de este problema anterior.
Me pregunto cuál sería la solución recomendada para ser preparada para el futuro y seguir siendo compatible con Vue cuando use Proxy.
Usar Object.defineProperty con configurable: false funciona bien (pero no evita que una propiedad que tenga un setter / getter existente se vuelva reactiva).
¿Esta técnica todavía se podrá utilizar con Vue 3?
Gracias

@ colin-guyon configurable: false probablemente _no_ funcionaría, especialmente porque parece haber https://github.com/vuejs/vue-next/blob/d9c6ff372c10dde8b496ee32f2b9a246edf66a35/packages/reactivity/src/reactive.ts#L159 . Si termina en Vue 3.x, habría una API oficial para marcar un objeto como no reactivo.

Tenga en cuenta que al igual que el nuevo Vue.observable , cuando se establece una propiedad en un objeto reactivo, ese valor _nuevo_ no se corrompe y se deja como está. En cambio, el _getter_ devolvería un proxy reactivo para él, creando uno si aún no existe en el caché.

Una noticia afortunada es que, siempre que no haga mucho en _ ese_ proxy reactivo, probablemente debería estar bien. Si el acaparamiento de memoria o la interoperabilidad es una preocupación, entonces ciertamente no necesita preocuparse por eso, ya que sea cual sea el objeto: una colección gigante de datos o algún objeto extraño de una biblioteca cuyo comportamiento es impredecible si se le aplica reactividad, dice - Nada de eso se toca, después de todo. En este sentido, Vue 3.x realmente resuelve muchos casos de esquina en los que esta característica sería útil de otra manera.

Por el momento, el código vue-next parece excluir que las claves de símbolos sean reactivas, al igual que lo hace la versión actual de Vue.

considere esto https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true puede escapar de los procedimientos de vue observe.

Hoy conocí un caso que, Vue observó una instancia de mapa de mapbox-gl, luego sucedieron cosas extrañas, el mapa se volvió más claro. Pero la instancia del mapa debe pasarse entre instancias de vue. Después de agregar map._isVue = true , el problema se resolvió.

Estaba usando este método hasta que las herramientas de desarrollo de Vue me mordieron porque ve que _isVue es cierto y cree que el objeto es una instancia de componente de Vue, pero no lo es.

El único truco que he visto sin efectos secundarios graves parece ser el enfoque de OP con la biblioteca vue-nonreactive .

¿Hay soluciones alternativas en V3 para esto?

@HunderlineK shallowRef, shallowReactive, markRaw

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

yyx990803 picture yyx990803  ·  48Comentarios

RashadSaleh picture RashadSaleh  ·  51Comentarios

yyx990803 picture yyx990803  ·  36Comentarios

smolinari picture smolinari  ·  116Comentarios

karevn picture karevn  ·  42Comentarios