Comunicação do componente pai-filho do Vue (dez tipos)

Entrevistador: Quais são as maneiras de se comunicar entre os componentes pai e filho no Vue?

Pense sobre isso por um minuto.

É inegável que grandes e pequenas fábricas já usaram o framework Vue.js. É simples e fácil de usar, sem falar em tutoriais detalhados, comunidades ativas e muitos kits de terceiros. É realmente uma habilidade essencial para desenvolvedores front-end. E na entrevista, várias perguntas sobre Vue são frequentemente feitas, e a maioria dos entrevistadores fará as perguntas acima.

Recentemente, tenho feito otimizações no nível de código de projetos Vue. Para ser honesto, otimizar os códigos de outras pessoas é realmente uma coisa dolorosa. Se você não falar sobre a implementação da função, posso escrever outro artigo sobre a especificação do código. Realmente não há padrão e nem raio, é muito importante regular isso! É meio que uma piada, de volta ao assunto, tosse, tosse, vamos falar sobre o meu entendimento das perguntas da entrevista acima, a redação é limitada e há algo errado. Bem-vindo a deixar um comentário no final do artigo.

Visão geral

Vários métodos de comunicação nada mais são do que o seguinte:

  • Prop(Comumente usado)

  • $emit (Mais usado para embalagens de componentes)

  • .syncAçúcar sintático (menos)

  • $attrs E  $listeners (mais usado para embalagens de componentes)

  • provide E  inject (componentes de alta tecnologia / bibliotecas de componentes são mais usados)

  • Outra comunicação

Detalhe

Vamos apresentá-los um por um, por favor, circule.

1. Prop
pronúncia britânica: [prɒp]. Isso é muito usado em nosso desenvolvimento diário. Em termos simples, podemos passar dados para componentes filhos por meio do Prop. Para usar uma metáfora vívida, a transferência de dados entre os componentes pai e filho é equivalente a um cano de esgoto de cima para baixo, que só pode fluir de cima para baixo, não a montante. Este é exatamente o fluxo de dados unilateral da filosofia de design do Vue. Prop é apenas uma interface entre o pipeline e o pipeline, para que a água (dados) possa fluir para baixo. Dito isso, observe o código:

<div id="app">
 
  <child :content="message"></child>
 
</div>
 
// Js
 
let Child = Vue.extend({
    
    
 
  template: '<h2>{
    
    { content }}</h2>',
 
  props: {
    
    
 
    content: {
    
    
 
      type: String,
 
      default: () => {
    
     return 'from child' }
 
    }
 
  }
 
})
 
 
 
new Vue({
    
    
 
  el: '#app',
 
  data: {
    
    
 
    message: 'from parent'
 
  },
 
  components: {
    
    
 
    Child
 
  }
 
})

Resultado do navegador:

from parent

 
  
  

2. A
pronúncia britânica de $ emit : [iˈmɪt]. A declaração oficial é para acionar um evento na instância atual. Parâmetros adicionais serão passados ​​para o retorno de chamada do ouvinte . Pelo que entendi, não sei se posso explicar para você. Vamos dar uma olhada rápida no código:

<div id="app">
 
  <my-button @greet="sayHi"></my-button>
 
</div>
 
let MyButton = Vue.extend({
    
    
 
  template: '<button @click="triggerClick">click</button>',
 
  data () {
    
    
 
    return {
    
    
 
      greeting: 'vue.js!'
 
    }
 
  },
 
  methods: {
    
    
 
    triggerClick () {
    
    
 
      this.$emit('greet', this.greeting)
 
    }
 
  }
 
})
 
 
 
new Vue({
    
    
 
  el: '#app',
 
  components: {
    
    
 
    MyButton
 
  },
 
  methods: {
    
    
 
    sayHi (val) {
    
    
 
      alert('Hi, ' + val) // 'Hi, vue.js!'
 
    }
 
  }
 
})

A lógica geral é a de Jianger: quando eu clico no botão na página, eu aciono o MyButtonevento de escuta no componente greete passo os parâmetros para a função de retorno de chamada sayHi. Para ser franco, antes de emitir (distribuir) um evento do componente filho, ele internamente ativou (ouviu) o evento e seu callback de ouvinte na fila de eventos antecipadamente. Na verdade, é equivalente à seguinte escrita:

vm.$on('greet', function sayHi (val) {
    
    
 
  console.log('Hi, ' + val)
 
})
 
vm.$emit('greet', 'vue.js')
 
// => "Hi, vue.js"

3. modificador .sync
Esse cara costumava ser uma função de ligação bidirecional em [email protected], ou seja, o componente filho pode modificar o valor no componente pai. Por violar o conceito de design de fluxo de dados unilateral, ele foi eliminado em [email protected]. No entanto, esse modificador .sync foi reintroduzido em [email protected]+ e superior. Mas desta vez ele existe apenas como um açúcar sintático de tempo de compilação. Ele será expandido em um ouvinte v-on que atualiza automaticamente as propriedades do componente pai. Para ser franco, vamos atualizar manualmente o valor no componente pai para tornar a origem da alteração de dados mais óbvia. Aqui está um parágrafo da introdução oficial:

Em alguns casos, podemos precisar "vincular de duas vias" um adereço. Infelizmente, a verdadeira ligação bidirecional trará problemas de manutenção, porque o componente filho pode modificar o componente pai e não há nenhuma fonte óbvia de mudança no componente pai e no componente filho.

Por se tratar de um açúcar sintático, deve ser uma forma abreviada de uma determinada forma de escrever. Que forma de escrever? Veja o código:

Portanto, podemos usar o .syncaçúcar sintático para abreviar o seguinte:

<text-document
 
  v-bind:title="doc.title"
 
  v-on:update:title="doc.title = $event">
 
</text-document>
<text-document v-bind:title.sync="doc.title"></text-document>

Tanto absurdo, como conseguir "ligação bidirecional"? Vamos entrar no anúncio, vai ficar mais emocionante depois do anúncio! ... Ok, bem-vindo de volta. Suponha que desejemos obter esse efeito: alterar o valor na caixa de texto do componente filho também altera o valor do componente pai. Como fazer isso? Pense nisso primeiro. Olhe para o código primeiro:

<div id="app">
 
  <login :name.sync="userName"></login> {
    
    {
    
     userName }}
 
</div>
 
let Login = Vue.extend({
    
    
 
  template: `
 
    <div class="input-group">
 
      <label>姓名:</label>
 
      <input v-model="text">
 
    </div>
 
  `,
 
  props: ['name'],
 
  data () {
    
    
 
    return {
    
    
 
      text: ''
 
    }
 
  },
 
  watch: {
    
    
 
    text (newVal) {
    
    
 
      this.$emit('update:name', newVal)
 
    }
 
  }
 
})
 
 
 
new Vue({
    
    
 
  el: '#app',
 
  data: {
    
    
 
    userName: ''
 
  },
 
  components: {
    
    
 
    Login
 
  }
 
})

Abaixo está o ponto-chave, há esta frase no código:

this.$emit('update:name', newVal)

 
  
  

A sintaxe oficial é: update:myPropNameque myPropNamerepresenta o valor da proposta a ser atualizado. Obviamente, se você usar. $ Emit acima sem açúcar .sync, o mesmo efeito pode ser obtido. É isso aí!

4. $attrse$listeners

  • $attrsA explicação no site oficial é a seguinte:

Contém associações de propriedade ( classe styleexceções) que não são reconhecidas (e adquiridas) como props no escopo pai . Quando um componente não declara nenhum prop, ele contém todas as ligações de escopo pai ( classe styleexceções) e pode v-bind="$attrs"passar componentes internos - muito úteis ao criar componentes de alto nível.

  • $listenersA explicação no site oficial é a seguinte:

Contém ouvintes de eventos no escopo pai (sem .nativedecoradores) v-on. Pode ser v-on="$listeners"passado em componentes internos - muito útil ao criar componentes de nível superior.

Acho que $attrse os $listenersatributos são como duas caixas de armazenamento, uma é responsável por armazenar atributos e a outra é responsável por armazenar eventos, ambas armazenando dados na forma de objetos. Veja a seguinte explicação do código:

<div id="app">
 
  <child
 
    :foo="foo"
 
    :bar="bar"
 
    @one.native="triggerOne"
 
    @two="triggerTwo">
 
  </child>
 
</div>

Como você pode ver no Html, existem dois atributos e dois métodos.A diferença é que o atributo é uma propdeclaração e o evento é um .nativemodificador.

let Child = Vue.extend({
    
    
 
  template: '<h2>{
    
    { foo }}</h2>',
 
  props: ['foo'],
 
  created () {
    
    
 
    console.log(this.$attrs, this.$listeners)
 
    // -> {
    
    bar: "parent bar"}
 
    // -> {
    
    two: fn}
 
 
 
    // 这里我们访问父组件中的 `triggerTwo` 方法
 
    this.$listeners.two()
 
    // -> 'two'
 
  }
 
})
 
 
 
new Vue({
    
    
 
  el: '#app',
 
  data: {
    
    
 
    foo: 'parent foo',
 
    bar: 'parent bar'
 
  },
 
  components: {
    
    
 
    Child
 
  },
 
  methods: {
    
    
 
    triggerOne () {
    
    
 
      alert('one')
 
    },
 
    triggerTwo () {
    
    
 
      alert('two')
 
    }
 
  }
 
})
可以看到,我们可以通过$attrs$listeners进行数据传递,在需要的地方进行调用和处理,还是很方便的。当然,我们还可以通过v-on="$listeners"一级级的往下传递,子子孙孙无穷尽也!

一个插曲!

当我们在组件上赋予了一个非Prop 声明时,编译之后的代码会把这些个属性都当成原始属性对待,添加到 html 原生标签上,看上面的代码编译之后的样子:

<h2 bar="parent bar">parent foo</h2>
这样会很难看,同时也爆了某些东西。如何去掉?这正是 inheritAttrs 属性的用武之地!给组件加上这个属性就行了,一般是配合$attrs使用。看代码:

// 源码
 
let Child = Vue.extend({
    
    
 
  ...
 
  inheritAttrs: false, // 默认是 true
 
  ...
 
})

Como você pode ver, é muito conveniente para nós repassar $attrse $listenerstransferir dados, ligar e processar quando necessário. Claro, também podemos passar v-on="$listeners"para o primeiro nível, descendentes infinitos!

Um episódio!

Quando atribuímos uma declaração não-Prop ao componente, o código compilado tratará esses atributos como atributos originais e os adicionará às tags nativas html e verá como fica o código acima após a compilação:

<h2 bar="parent bar">parent foo</h2>

 
  
  

Isso vai ser feio e vai explodir alguma coisa. Como removê-lo? É aqui que entra o atributo inheritAttrs! Basta adicionar este atributo ao componente, geralmente usado em conjunto $attrs. Veja o código:


 
  
  
  1. // Código fonte
  2. let Child = Vue.extend ({
  3. ...
  4. inheritAttrs: false , // O padrão é verdadeiro
  5. ...
  6. })

Compile novamente:

<h2>parent foo</h2>

 
  
  

5. provide/inject
Eles são muito misteriosos para o CP. Dê uma olhada na descrição oficial de fornecer / injetar:

provideE injectfornece principalmente casos de uso para bibliotecas de plug-ins / componentes de última geração. Não é recomendado para uso direto no código do aplicativo. E esse par de opções precisa ser usado em conjunto para permitir que um componente ancestral injete uma dependência em todos os seus descendentes, não importa quão profundo seja o nível do componente, e isso sempre terá efeito quando o relacionamento upstream e downstream for estabelecido.

Depois de ler a descrição, sou um pouco ignorante! O resumo de uma frase é: Quando você era jovem, seu pai guardava tudo para você. Quando você crescer, deveria se casar com uma mulher. Você quer uma casa ou um carro para você, contanto que ele tenha tudo o que puder. Aqui está a explicação do código desta frase:

<div id="app">
 
  <son></son>
 
</div>
 
let Son = Vue.extend({
    
    
 
  template: '<h2>son</h2>',
 
  inject: {
    
    
 
    house: {
    
    
 
      default: '没房'
 
    },
 
    car: {
    
    
 
      default: '没车'
 
    },
 
    money: {
    
    
 
      // 长大工作了虽然有点钱
 
      // 仅供生活费,需要向父母要
 
      default: '¥4500'
 
    }
 
  },
 
  created () {
    
    
 
    console.log(this.house, this.car, this.money)
 
    // -> '房子', '车子', '¥10000'
 
  }
 
})
 
 
 
new Vue({
    
    
 
  el: '#app',
 
  provide: {
    
    
 
    house: '房子',
 
    car: '车子',
 
    money: '¥10000'
 
  },
 
  components: {
    
    
 
    Son
 
  }
 
})

6. Outros métodos de comunicação
Além dos cinco métodos acima, existem na verdade:

  • EventBus

A ideia é declarar uma variável de instância global do Vue EventBuse armazenar todos os dados de comunicação e monitoramento de eventos nesta variável. Desta forma, o compartilhamento de dados entre componentes é alcançado, semelhante ao Vuex. Mas esse método só é adequado para projetos muito pequenos, e Vuex é recomendado para projetos complexos. Aqui está o código simples para implementar EventBus:

<div id="app">
 
  <child></child>
 
</div>
 
// 全局变量
 
let EventBus = new Vue()
 
 
 
// 子组件
 
let Child = Vue.extend({
    
    
 
  template: '<h2>child</h2>',
 
  created () {
    
    
 
    console.log(EventBus.message)
 
    // -> 'hello'
 
    EventBus.$emit('received', 'from child')
 
  }
 
})
 
 
 
new Vue({
    
    
 
  el: '#app',
 
  components: {
    
    
 
    Child
 
  },
 
  created () {
    
    
 
    // 变量保存
 
    EventBus.message = 'hello'
 
    // 事件监听
 
    EventBus.$on('received', function (val) {
    
    
 
      console.log('received: '+ val)
 
      // -> 'received: from child'
 
    })
 
  }
 
})
  • Vuex

Recomendado oficialmente, Vuex é um modo de gerenciamento de estado desenvolvido especificamente para aplicativos Vue.js.

  • $ parent

A instância pai, se a instância atual tiver uma. A interação entre os dados também pode ser realizada acessando a instância pai, mas em casos muito raros os dados no componente pai serão modificados diretamente.

  • $ root

A instância raiz do Vue da árvore de componentes atual. Se a instância atual não tiver uma instância pai, esta instância será ela mesma. A interação entre os dados também pode ser realizada acessando o componente raiz, mas em casos muito raros os dados no componente pai serão modificados diretamente.

  • transmitir / despachar

Eles são métodos em [email protected], que são transmissão e envio de eventos. Embora [email protected] tenha sido excluído, esses dois métodos podem ser simulados. Pode aprender com a Element para alcançar. Às vezes, é muito útil, como quando estamos desenvolvendo componentes de árvore.

Resumindo

Depois de tanto prolixo, espero ver os alunos mais ou menos ganhando. Por favor, deixe um comentário se algo estiver errado, fico muito grato. Na verdade, existem muitos tipos de comunicação entre os componentes pai e filho, dependendo das circunstâncias que você usa. Diferentes cenários são tratados de maneira diferente. A premissa é que você tem que saber! Ainda há um longo caminho a percorrer pelo Grande Deus. Contanto que você olhe para a comunidade todos os dias, olhe a documentação, escreva demos e faça um pequeno progresso a cada dia, você sempre ganhará.

 

Entrevistador: Quais são as maneiras de se comunicar entre os componentes pai e filho no Vue?

Acho que você gosta

Origin blog.csdn.net/weixin_46034375/article/details/108541546
Recomendado
Clasificación