Vue: [recurso] Capacidade de desativar a observação Vue

Criado em 7 abr. 2016  ·  50Comentários  ·  Fonte: vuejs/vue

Atualizar:
Se alguém acabar precisando dessa funcionalidade, eu a lancei como


Temos alguns modelos não planos onde precisamos desativar a observação e caminhada de Vue. Um exemplo é um modelo de recursos que tem acesso a um cache para que possa pesquisar recursos relacionados. Isso faz com que todos os objetos no cache sejam observados (provavelmente ineficientes), bem como algumas interações adicionais com outro código. Atualmente, contornamos isso definindo um Observador fictício no cache. Algo semelhante 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;
            });
        },
    },
});

Isso funciona, mas

  • depende dos componentes internos da vue
  • requer um objeto já observado, pois não podemos importar a classe Observer diretamente.

Proposta:
Adicione um método oficial para desativar explicitamente a observação / caminhada de Vue. por exemplo, 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);

Considerações:

  • O que deve acontecer se um usuário definir um caminho de chave reativa para um objeto não reativo? O vue deve avisar o usuário? por exemplo,

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

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

  • Um objeto já reativo deve alertar o usuário se houver tentativa de torná-lo não reativo? por exemplo,

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

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

Comentários muito úteis

  1. Se você precisar pular a observação de um objeto / array em data , use Object.freeze() nele;
  2. Você não precisa colocar um objeto em data para acessá-lo em this . Se você simplesmente anexá-lo a this no gancho created() , ele não será observado de forma alguma.

Todos 50 comentários

Object.freeze() não funcionará no seu caso? É compatível desde v1.0.18

  1. Se você precisar pular a observação de um objeto / array em data , use Object.freeze() nele;
  2. Você não precisa colocar um objeto em data para acessá-lo em this . Se você simplesmente anexá-lo a this no gancho created() , ele não será observado de forma alguma.
  • Object.freeze não funciona aqui. O cache é atualizado com o tempo.
  • O recurso principal _é_ reativo. Estou principalmente interessado em tornar o objeto de cache aninhado não reativo.

Então talvez seja hora de repensar o design do seu modelo. Por que aninhar essas coisas sob algo a ser observado?

Porque o cache é usado para pesquisar recursos relacionados dinamicamente.

por exemplo, poderíamos ter modelos Author e Post . O modelo do autor define um relacionamento para muitos chamado posts para o modelo de postagem. Este cache contém os dados de relacionamento, bem como a coleção relacionada.

chamar author.posts obtém as postagens do cache.

Por design, Vue desencoraja colocar objetos complexos com seu próprio mecanismo de mutação de estado em data da instância Vue. Você deve apenas colocar o estado puro como dados observados nas instâncias do Vue. Você pode manipular esses estados da maneira que quiser, mas os objetos responsáveis ​​por tais manipulações não devem fazer parte do estado da instância do Vue.

Em primeiro lugar, uma questão esclarecedora - o que exatamente você quer dizer com estado puro ? Temos dois tipos de estado:

  • estado do modelo (permanente, dados sincronizados com uma loja, por exemplo, um todo)
  • estado vue (temporário, dados que controlam o comportamento da visualização. por exemplo, recolher / mostrar a lista de tarefas)

Mas mesmo assim:
Isso é justo. O modelo é definitivamente 'complexo', então esta solicitação vai contra as melhores práticas atuais. Além disso, meu exemplo inicial não é muito bom - é apenas o que funcionou para desativar a observação. Isso é mais representativo de nossa configuração atual com possível 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,
    },
    ...
});

Basicamente, temos um vue pai responsável por colocar componentes reutilizáveis ​​em um layout e buscar / vincular dados aos componentes associados. Os componentes filhos não buscam seus próprios dados porque os dados são diferentes em contextos diferentes. por exemplo, uma lista de comentários de _usuários_ vs. uma lista de comentários de _postagens_.

O modelo é bastante 'burro' com a exceção de que os objetos relacionados não são aninhados ( {post: {author: {}, comments: []}} ), mas sim pesquisados ​​no cache. por exemplo, post.comments[2].author pode ser exatamente o mesmo objeto que post.author . Portanto, em vez de ter várias cópias do objeto de autor, temos apenas uma que é consultada no cache. Não há nenhuma mutação acima - todos os dados são feitos na busca inicial.

Além disso, não que a solicitação seja mais relevante, mas uma alternativa pode ser não observar os membros do objeto 'privado'. Podem ser membros com um sublinhado inicial simples ou duplo. O lado negativo dessa abordagem é que ela interromperia a mudança.

Se alguém acabar precisando dessa funcionalidade, eu a lancei como

@rpkilby obrigado por compartilhar!

@rpkilby Uma maneira de copiar um objeto e remover o observável / reatividade

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

Muito útil, pois quero manter uma matriz de "estados" e implementar um objeto de histórico de estado em vuex.

Edit : Esta solução era específica para o meu caso. Eu tinha um objeto onde precisava apenas de uma cópia dos valores das propriedades em um determinado momento. Eu não me importava com referências, atualizações dinâmicas etc.

No momento, congelar o objeto não é uma solução de longo prazo. [Vue-nonreactive] tem Vue como uma dependência que é um exagero quando se trata de fazer algo tão direto. Uma simples verificação no código, como instance.__ob__ !== false seria suficiente? Isso permitiria que as bibliotecas garantissem que coisas como caches não fossem observadas.

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

Este é principalmente um problema para bibliotecas usadas em aplicativos Vue (pelo menos para mim).

Como dizer ao Vue para observar apenas (defineProperty para) a profundidade de dados de 1 nível?

Meu caso é, quero que Vue seja notificado quando data.curObj mudar,
mas não faça com curObj.position , curObj.rotation , etc.

Eu costumava usar Object.freeze , mas neste caso, causaria Erro quando three.js tentasse atribuir valores ao objeto.

Eu tenho que fazer o seguinte?
(na verdade eu fiz em outro lugar semelhante)

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 adicionar outra condição para o usuário desabilitar a observação além de Object.isExtensible ( Object.freeze )?
  2. Podemos melhorar a detecção do Vue.isPlainObject?

Você pode usar a desestruturação

var newObj = { ...obj };

Isso deve consertar. Isso fará com que o método isPlainObject retorne 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]"

Oi pessoal, um ponto que foi perdido nas respostas é que os dados no comentário original não são de estado puro . No meu caso, tenho uma instância de modelo com uma referência privada para um cache de pesquisa de relacionamento. Por exemplo, um article pode pesquisar seu author ou comments . Quando eu chamo article.author , esta é uma pesquisa de propriedade dinâmica naquele cache de relacionamento e não é um simples acesso de atributo. Algumas coisas a considerar:

  • O cache não está em estado puro, então não quero que seja observado pelo Vue, pois é um desperdício de recursos.
  • O cache não pode ser descartado, pois ainda preciso de uma referência para realizar essas pesquisas / atualizações dinâmicas.
  • O cache é efetivamente um singleton e pode ser atualizado externamente. O aplicativo é atualizado de acordo.

Em resposta a algumas sugestões:

  • Usar stringify / parse JSON ou desestruturação de objeto não é suficiente, pois isso duplica o objeto e o cache, além de quebrar a referência ao cache original. Atualizar a referência de cache original não atualizará mais as instâncias do aplicativo. Sem essas pesquisas e atualizações dinâmicas, o cache é basicamente inútil e seria melhor tornar os objetos relacionados atributos simples no modelo original. Também digno de nota, essas sugestões quebram os métodos de instância desses objetos.
  • As sugestões de @Mechazawa fazem sentido se você controlar a criação de tipos e os tipos forem construídos para uso com o Vue. No meu caso, esta é uma biblioteca externa que não está vinculada ao Vue, e não quero me dar ao trabalho de alterar os tipos no aplicativo. É muito mais simples para a camada vue simplesmente marcar certas propriedades conhecidas como não observáveis.

Minha única crítica:

Vue-nonreactive tem Vue como uma dependência que é um exagero quando se trata de fazer algo tão direto.

Não tenho certeza de por que isso seria uma coisa ruim? Você já está usando o Vue em seu aplicativo e o plug-in é específico para o Vue. Se não me engano, a maioria das ferramentas de construção são inteligentes o suficiente para não criar pacotes com dependências duplicadas. Independentemente disso, isso não é correto . Existe uma dependência dev, mas não uma dependência de tempo de execução.


De qualquer forma, fico feliz em ver que este post atraiu algum interesse e tenho certeza que algumas dessas outras soluções funcionarão em uma variedade de outros casos. Eu simplesmente quero destacar os requisitos de meu comentário original e por que as sugestões não são alternativas adequadas para esse caso.

Então, recentemente me deparei com isso e descobri que há uma maneira muito mais fácil de causar um curto-circuito na lógica de observação do Vue: _Defina uma propriedade como não configurável._

Fundo

Em meu aplicativo, tenho que trabalhar com uma biblioteca de terceiros (OpenLayers), que cria instâncias de classe que armazenam dados e não oferece suporte a nenhum sistema de reatividade. Tentar calçar um já causou tantas dores de cabeça, deixe-me dizer-lhe. Portanto, a única solução viável para um aplicativo em grande escala usando esta biblioteca é permitir que o OpenLayers tenha as coisas da maneira que quiser e, para mim, fazer com que Vue jogue melhor com esses horrivelmente aninhados e superobjetos da destruição. Antes de encontrar esse problema, meu aplicativo estava usando cerca de 3 GB de RAM (em nosso maior conjunto de dados), tudo causado pelo Vue tornando esses objetos reativos. Além disso, era muito lento durante o carregamento. Eu tentei Vue-nonreactive, e ajudou, mas apenas para nos baixar para cerca de 1 giga. Antes de usar o Vue, o aplicativo tinha cerca de 350 megs.

Solução

Qualquer coisa que você não queira que seja reativo, simplesmente marque como configurable: false . É tão simples como:

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

(Isso impede que a propriedade nested e todas as suas propriedades sejam observadas.)

É isso! Nenhuma dependência Vue e, sem dúvida, nem mesmo incorreta. Com isso, meu aplicativo caiu para 200 megs com nosso maior conjunto de dados. É simples e fácil, e requer apenas uma mudança na documentação do lado do Vue para torná-lo uma forma 'oficial' de fazer algo não reativo.

Interessante - definitivamente parece uma alternativa viável.

Existe uma maneira de pausar temporariamente a observação reativa e retomá-la mais tarde?

Eu tenho um observador de suporte, dentro do qual eu atualizo um objeto enorme onde não quero acionar a atualização do DOM somente depois que toda a preparação dos dados for concluída.

@intijk Não exatamente. Veja, depende do que você está tentando fazer; Vue eventualmente precisa aplicar seu estado, então simplesmente pausar enquanto ele é calculado não ajuda muito. Se, em vez disso, você estiver tentando pular os estados intermediários e aplicar apenas o estado final, apenas comece com um novo objeto e atribua esse objeto no final.

Por exemplo (psuedocode):

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

  // Do intermediate state updates

  this.myState = state;
}

(Aplicam-se advertências normais do Vue sobre a reatividade do objeto.)

Minha recomendação seria usar o truque configurable para pular as seções do seu objeto grande que não precisam ser reativas. Se tudo isso _ser_ precisa ser reativo, recomendo usar algo como vuex .

@Morgul já usei esse truque a muito tempo, mas o fato é que não quero mais usar esse truque.
No meu caso, o objeto de dados agora é meio grande, varia de 2M a 100M, realizar uma cópia profunda em tal objeto é bastante doloroso.

@intijk Isso parece incrivelmente complexo para vincular Vue. Qual é o caso de uso aqui?

@Morgul
Não acho que o caso seja complexo, o caso em si é simples, apenas os dados são meio grandes. Cada vez que a rede carrega algum arquivo de log de visualização indexado, eu tenho um componente de visualização para exibi-lo.

Alguém tem ideias sobre como definir um campo não reativo dentro de uma propriedade computada? Minha primeira ideia depende da não reatividade da atribuição ao 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;

Mas isso não é exatamente autodocumentado :-)

Ei pessoal. Acabei de descobrir esse problema e descobri que estou enfrentando um problema muito parecido com o problema de rpkilby: meu projeto constrói uma série de Vue Virtual DOM (ou chamados de vnode) a partir de um objeto JSON. Vou usar este objeto JSON para construir um aplicativo Android. De qualquer forma, este objeto JSON pode ser de grande tamanho, e quando eu usar este JSON no Vue, ele será observado pelo Vue. Eu tentei o método rpkilby's e Morgul's, mas não funciona. (A propósito, eu estou em uma equipe, enquanto alguns caras podem não estar muito familiarizados com o Vue, e eles provavelmente causarão o JSON observado, e minha versão do Vue é 2.5.16 ) Estou me perguntando se podemos fazer isso no Vue transversal:
function _traverse (val, visto) {
var i, chaves;
var isA = Array.isArray (val);
if ((! isA &&! isObject (val)) || Object.isFrozen (val) || val instanceof VNode
|| (val && val ['__ vueNonReactive__'])) {
Retorna
}
...
Como você pode ver, adiciono o "val && val ['__ vueNonReactive__']". Então eu modifico meu objeto JSON para ter o "__vueNonReactive__ = true" com o nó raiz do JSON, e isso resolve meu problema.
Estou me perguntando se isso pode causar algum problema? E isso será considerado como um novo recurso no Vue que permite ao desenvolvedor configurar um objeto a ser observado pelo Vue ou não configurando uma propriedade do objeto? (Object.freeze pode transformar o objeto em um objeto imutável, então não cabe em todas as situações)

considere isso https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true pode escapar de vue observar procedimentos.

Hoje conheci um caso que, Vue observou uma instância de mapa de mapbox-gl, então coisas estranhas aconteceram, o mapa ficou mais claro. Mas a instância do mapa precisa ser passada entre as instâncias vue. Depois de adicionar map._isVue = true , o problema foi resolvido.

1 para apoiar isso oficialmente. Estou usando um objeto grande que não precisa de reatividade em um componente, e desabilitar a reatividade não utilizada reduziu o tamanho do objeto de memória de 800 MB para 43 MB
Estou usando a solução @magicdawn para problemas de compatibilidade, mas acho que @Mechazawa é a melhor solução aqui.
Pois a solução de definir __ob__ 's configurable como falso funciona, mas faz o Vue travar ao tentar definir o __ob__ real.

Eu construí um plugin Vue que torna possível tornar as variáveis ​​Vue não reativas (ele usa o gancho beforeCreate).

Isso é mais limpo do que o vue- nonreactive - este comentário - sua solução não funcionará para a próxima versão do Vue.


Por favor, olhe para Vue-Static para uma maneira de tornar as variáveis ​​não reativas.

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

Olá @samuelantonioli - parece que o vue-static faz algo um pouco diferente, desativando a reatividade de um objeto inteiro. Em contraste, vue-nonreactive é capaz de desabilitar a observação de uma única propriedade , enquanto mantém o resto do objeto reativo.

Dito isso, parece que as intenções são ligeiramente diferentes. As propriedades estáticas não observam mudanças, mas devem ser renderizadas em um modelo. Propriedades não reativas não devem ser observadas, nem devem ser renderizadas.

por exemplo, minha instância de modelo tem uma referência a um cache de objeto que permite pesquisas de objetos relacionados. Eu quero observar / renderizar o instance e o instance.author , mas não o instance._cache .

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

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

De qualquer forma, obrigado pelo aviso. Terei que ver como a próxima versão está usando proxies e ver se há uma maneira de enganar a criação do observador / proxy.

@LinusBorg - Não vejo um branch experimental. Onde o desenvolvimento está ocorrendo para a próxima versão?

Ainda estamos no estágio de conceito / experiência e ainda não publicamos um branch. O trabalho sério sobre isso não começará antes de lançarmos a atualização 2.6, que por si só levará algum trabalho depois que, esperançosamente, lançarmos o vue-cli 3.0 em breve

Obrigado pela atualização.

Portanto, não tenho certeza se o problema existe no mesmo escopo, uma vez que os proxies ES6 são introduzidos. Em minha aplicação, faço uso intensivo deles, e sua sobrecarga versus a sobrecarga da observação atual do vue parece muito menor. O diabo estará nos detalhes, eu suspeito.

O problema que tenho com Vue-Static é que parece redundante. Posso construir meu objeto em um módulo JS, importá-lo e, em seguida, retornar seu valor de uma função computada; como não será observado, não importa que o valor da função calculada nunca seja recalculado. E essa é uma separação de interesses muito melhor, já que não estou fazendo coisas do tipo lógica de negócios em meus componentes de visão.

Em qualquer caso, meu truque de definir propriedades como não configuráveis ​​ainda é a maneira menos invasiva e menos dependente de Vue de lidar com o problema. Também não há razão para supor que ele quebraria com os Proxies ES; você provavelmente ainda não deseja observar propriedades não configuráveis. Posso estar totalmente errado, mas nós _sabemos_ __ob__ estamos indo embora ... não sabemos se a verificação de uma propriedade pode ser configurada.

Além disso, está funcionando como um campeão em nosso código de produção por mais de 8 meses. ;) (Temos um problema de espaço semelhante a @samuelantonioli; temos um mapa do OpenLayers com o qual precisamos trabalhar dentro do Vue, sem que o Vue amplie nossa memória para 2,4 GB ...)

Eu concordo, se você usar outro padrão, por exemplo, módulos de importação e usar propriedades computadas, você não precisa do Vue-Static . Eu só precisava de um padrão que pudesse ensinar aos meus funcionários e que fosse fácil de entender e usar. O padrão import-module-and-use-computed-properties não é IMO tão claro.


Um pouco OT: Tenho certeza de que os proxies ES6 são um bom caminho a percorrer, mas tenho algumas preocupações sobre a compatibilidade do navegador (IE11 e versões anteriores não suportam). Estou interessado se haverá uma camada de compatibilidade / algum tipo de polyfill para que possamos usar o Vue para projetos com requisitos de navegador mais rígidos.

Como dizer ao Vue para observar apenas (defineProperty para) a profundidade de dados de 1 nível?

+1 para esta idéia, será mais fácil estruturar os dados para usar o Vue com Graphic lib externo (que normalmente tem objetos grandes aninhados de vários níveis).

Que tal especificar apenas algumas propriedades para serem reativas?

Posso estar faltando algo óbvio aqui, mas usando this.propertyName = {/ * algo grande aqui * /};
no gancho montado () não é uma solução para ter propriedades não observadas?

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

definir _isVue fará o vue-devtool travar, use esta função ao invés

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
}

Ele se perdeu no meio do ruído, mas marcar uma propriedade como não configurável realmente parece ser a solução.

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

Muito bom quando preciso passar THREE.Scene mas não quero que literalmente todo o gráfico da cena se transforme em uma confusão de observáveis. E então eu ainda posso passar o objeto original e ele pode ser reativo com base nisso. Perfeito!

Ainda é um problema.
Tenho objetos que contêm muitos níveis aninhados de propriedades que desejo manter como não reativos.
1

Mesmo se eu usar

Ele se perdeu no meio do ruído, mas marcar uma propriedade como não configurável realmente parece ser a solução.

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

Muito bom quando preciso passar THREE.Scene mas não quero que literalmente todo o gráfico da cena se transforme em uma confusão de observáveis. E então eu ainda posso passar o objeto original e ele pode ser reativo com base nisso. Perfeito!

ou

considere isso https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true pode escapar de vue observar procedimentos.

Hoje conheci um caso que, Vue observou uma instância de mapa de mapbox-gl, então coisas estranhas aconteceram, o mapa ficou mais claro. Mas a instância do mapa precisa ser passada entre as instâncias vue. Depois de adicionar map._isVue = true , o problema foi resolvido.

Propriedades em objetos aninhados se tornam reativas.

Tentei fazer isso recursivamente, mas Maximum call stack size exceeded , e causou mais atrasos.

@Mitroright Você terá que fazer isso recursivamente, mas suspeito que sua abordagem pode não ter sido totalmente correta.

O problema aqui é como Vue lida com Arrays; simplesmente marcar a propriedade geoJsonData não configurável provavelmente não funcionará (tive problemas com isso, mas nunca investiguei o 'porquê'.)

Experimente algo assim:

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

Estamos indo apenas um nível de profundidade, porque isso é tudo que precisamos fazer; O Vue não vai olhar nas propriedades do objeto aninhado. No entanto, ele parece olhar para objetos dentro de arrays em propriedades marcadas como não configuráveis, daí o problema que você está enfrentando.

Agora, direi que, com 10.000 objetos para passar por isso, ele se agitará por alguns segundos ou mais na primeira vez que passar por esse array; isso deve ser visto apenas como parte do custo de recuperação desses dados. Francamente, eu recomendo consumir esses objetos com uma classe (eu uso uma classe que retorna um proxy de seu construtor) e, em seguida, armazenar esses objetos em cache por um id único, se você estiver carregando-os mais de uma vez durante a vida do aplicativo. período. Mas isso é realmente um detalhe de design, e não exatamente relacionado ao seu problema.

Encontrei a solução aqui:
https://medium.com/@deadbeef404/tell -vue-js-to-stop-wasting-time-and-render-faster-7c3f7d2acaab

Resumindo, faça a função de utilidade:

import Vue from 'vue';

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

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

Olá @Mitoright. Apenas para referência, o artigo descreve as entranhas de vue-nonreactive . A diferença é se você deseja usar o código como um plugin (via vue-nonreactive ) ou como uma função auxiliar. Além disso, vue-nonreactive é mencionado na atualização logo no início da descrição deste problema.

Como o vue-devtool foi atualizado novamente, https://github.com/vuejs/vue/issues/2637#issuecomment -434154442 fará o vue-devtool travar novamente

Eu sugiro vue-nonreactive como soluções 😆

para tornar __ob__ nenhum enumerável, 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

Figura i daria meus 2 centavos e solução sobre isso.

Eu também tive problemas semelhantes ao implementar o conceito Freeze e o falso Observer. Meus dados vêm do servidor e é um cenário TreeNode recursivo. Meu projeto também está usando vuex neste caso, que adicionou uma camada aos problemas vistos. Eu constantemente recebo Maximum call stack size exceeded devido ao loop de objetos.keys de vues. Eu tentei congelar e definir dados dentro de um VNode falso, mas nenhum dos dois parecia interromper os problemas de recursão.

Finalmente recuei e envolvi minhas propriedades "não reativas" usando o clássico Padrão de Módulo Revelador

esta é a classe (ES6 / typescript), mas o mesmo pode ser aplicado à vista também

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

O computado não funcionou no meu caso porque eu ainda precisava do objeto completo e não de um manipulador que me enviasse a mudança no computado. Pelo menos se eu estou entendendo como um observador profundo funcionaria em relação a um resultado subconjunto calculado.

Acho que levar isso para o próximo nível seria criar um auxiliar ou decorador não recursivo injetado para acelerar o processo. Qualquer feedback útil seria ótimo.

parece que alguém já resolveu esse problema,

espero que você verifique todos os detalhes em https://github.com/vuejs/vue/issues/4384

Olá, criei o nº 10265 especialmente porque não tinha conhecimento desse problema anterior.
Eu só estou me perguntando qual seria a solução recomendada para ser à prova de futuro e permanecer compatível com o Vue quando ele usar o Proxy.
Usar Object.defineProperty com configurable: false funciona bem (mas não impede que uma propriedade com setter / getter existente se torne reativa).
Essa técnica ainda poderá ser usada com o Vue 3?
Obrigado

@ colin-guyon configurable: false provavelmente _não_ funcionaria, especialmente porque parece haver https://github.com/vuejs/vue-next/blob/d9c6ff372c10dde8b496ee32f2b9a246edf66a35/packages/reactivity/src/reactive.ts#L159 . Se acabar no Vue 3.x, haveria uma API oficial para marcar um objeto como não reativo.

Observe que, assim como a nova proposta de Vue.observable , quando uma propriedade é definida em um objeto reativo, aquele _novo_ valor não seria corrompido e apenas deixado como está. Em vez disso, o _getter_ retornaria um proxy reativo para ele, criando um se ainda não existir no cache.

Uma boa notícia é que, contanto que você não faça muito nesse proxy reativo, você provavelmente ficará bem. Se o consumo de memória ou interoperabilidade for uma preocupação, então você certamente não precisa se preocupar com isso, já que qualquer que seja o objeto - uma coleção gigante de dados ou algum objeto estranho de uma biblioteca cujo comportamento é imprevisível se a reatividade for aplicada a ele, você diz - _nada_ sobre isso é tocado, afinal. Nesse sentido, o Vue 3.x na verdade resolve muitos casos em que esse recurso seria útil.

No momento, o código vue-next parece excluir as chaves de símbolo de serem reativas, assim como o que a versão atual do Vue faz.

considere isso https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true pode escapar de vue observar procedimentos.

Hoje conheci um caso que, Vue observou uma instância de mapa de mapbox-gl, então coisas estranhas aconteceram, o mapa ficou mais claro. Mas a instância do mapa precisa ser passada entre as instâncias vue. Depois de adicionar map._isVue = true , o problema foi resolvido.

Eu estava usando esse método até que fui mordido pelas ferramentas de desenvolvimento do Vue, porque ele vê que _isVue é verdadeiro e pensa que o objeto é uma instância do componente Vue, mas não é.

O único hack que vi sem efeitos colaterais graves parece ser a abordagem do OP com a biblioteca vue-nonreactive .

Existem soluções alternativas no V3 para isso?

@HunderlineK shallowRef, shallowReactive, markRaw

Esta página foi útil?
0 / 5 - 0 avaliações