Суть коммуникации компонента VUE.js (базовая)

Vue.js
Суть коммуникации компонента VUE.js (базовая)

предисловие

Содержание, включенное в статью, может быть неполным, но его много, и его нужно читать медленно. Мне потребовалось много времени, чтобы организовать и поделиться своим опытом с сердцем, надеясь помочь всем. Но это неизбежно, что будут опечатки или недоразумения.Я надеюсь, что вы можете написать мне по электронной почте ghui_master@163.com, если вы их найдете, и я внесу изменения вовремя.Я просто надеюсь помочь вам, спасибо.

Мы можем интуитивно понять, что сложная страница разделена на несколько отдельных компонентов.

Каждый компонент является экземпляром Vue, тогда этот компонент также имеет свой жизненный цикл, и есть данные, вычисляемые, методы, наблюдайте за этими свойствами, каждый компонент имеет свои собственные частные данные, также может принимать входящие компоненты из топовых данных;

Преимущества компонентной разработки

  • Повышение эффективности разработки
  • Удобно и повторно повторно
  • Способствовать совместной разработке
  • легче управлять и обслуживать
  • Каждый компонент VUE является независимым индивидуумом (независимый экземпляр ВМ): ДАННЫЕ независимы (ДАННЫЕ разных компонентов не должны мешать друг другу), имеют полный жизненный цикл, методы и т. д. также независимы
  • Способен добиться вложенности компонентов: нужно освоить информационную связь между компонентами
  • ....

Компоненты в ВУЭ

  • Компоненты в Vue - это пользовательские теги, которые могут расширить нативные элементы HTML и инкапсулировать многоразовый код
  • Пользовательский тег vue будет рассматривать его как компонент (теги, отличные от правил w3c), пользовательские теги не имеют практического значения, но vue придаст этим тегам определенное значение.

Глобальные компоненты и локальные компоненты

  • Глобальные компоненты: вы можете напрямую вызвать глобальные компоненты в больших компонентах без ссылки или настраивания их отдельно

    Как правило, при написании плагинов используются более глобальные компоненты.

    vue.filters также находится в глобале, не нужно объявлять его напрямую.

    Глобальные компоненты объявлены в классе vue.

    <!-- IMPORT CSS-->
    <body>
    <div id="app">
        <h3 v-text="title"></h3>
        <my-handlesome></my-handlesome>
        <!--
        我希望 my-handlesome 实现效果和下面的div实现同样的效果
    
        <div> 我很帅气!!!</div>
        -->
        </div>
        <!-- IMPORT JS-->
        <script src="./20191007/node_modules/vue/dist/vue.js"></script>
        <script>
            /*
            * 全局组件:无需单独引用或者配置,直接在大组件中调取全局组件即可
            *  Vue.component(componentName,options)
    	   *     + options可以使用的有VM实例具备的大部分(DATA、METHODS、生命周期函数...)
    	   *     + 每调用一次组件都是创建一个单独的VUE实例(VueComponent -> Vue)
    	   *
            * 组件命名的规范:
            *     1.组件名不要带有大写  多个单词用 — 中划线分隔  kebab-case
    	   *     2.只要组件名和定义名字相同是可以的 (只有首字母可以大写) PasalCase
            *     3.html采用短横线隔开命名法js中转驼峰也是可以的
            * */
            Vue.component('my-handlesome',{ //=> 一个对象可以成一个组件
                /* 相当于用  template 中的 div 标签替换 我们自定义的 my-handlesome 标签*/
                template:'<div>{{msg}}</div>',
                //=> 组件是中data数据必须是一个函数 ,返回一个实例(对象)作为组件的数据
                data(){
                    return{
                        msg:'我很帅气!!!'
                    }
                }
            });
            let vm = new Vue({
               el:'#app',
                data:{
                   title:"你自认为自己颜值???"
                }
            });
        </script>
    </body>
    
  • Локальный компонент: Создать 1-2-3-ссылочный регистр

    должен сказать, кому принадлежит эта группа

    Локальные компоненты объявлены на экземпляре vm

    Компоненты независимы друг от друга и не могут напрямую пересекать области действия. Экземпляр (vm) также является компонентом, и компонент имеет функции жизненного цикла.

    <!-- IMPORT CSS-->
    <body>
    <div id="app">
        <h3 v-text="title"></h3>
        <handelesome1></handelesome1>
        <handelesome2></handelesome2>
    
        </div>
        <!-- IMPORT JS-->
        <script src="./20191007/node_modules/vue/dist/vue.js"></script>
        <script>
            /*
            *  局部组件-使用三部曲:
            *     1. 创建组件
            *     2. 注册组件
            *     3. 引用组件
            *  组件是相互独立的  不能直接跨作用域 实例(vm)也是一个组件, 组件中拥有生命周期函数
            *
            * */
            let obj ={ face:'丑到爆' }; //=> 如果组件共用了数据,会导致数据同时更新 (=错误写法=) (但是组件 特点 独立性)
            /*
             子组件不能直接使用父组件的数据 (组件之间的数据交互)
             组件理论上可以无限嵌套
             */
    
            //=> 1. 创建这个组件
            let handelesome1 ={
                template:`<div @click="fn">1.帅气 {{face}} {{a}}</div>`,
                /* face='颜值爆表' === @click="fn"*/
                data(){ //=> 这里的data 必须是一个函数
                    return obj
                },
                methods:{
                    fn(){
                        //=> this 为当前的组件
                        this.face = '颜值爆表'
                    }
                }
    
            } ;
            let handelesome2 ={
                template:'<div>2.霸气 {{face}}{{a}}</div>',
                data(){
                    return obj
                }
            } ;
    
            let vm = new Vue({
               el:'#app',
                data:{ //=> 只有这里的data是 对象
                   title:"你自认为自己颜值???",
                    //=> 这里的a 属性 在 局部组件中调用引发报错
                    //=>
                    a:1
    
    
                },
    
                components:{
                    //=> 2. 注册这个组件  ES6名字一样写一个
                    handelesome1,
                    handelesome2
                }
            });
        </script>
    
    </body>
    

вложенные компоненты

  • 1- создать 2- зарегистрироваться 3- сослаться
  • Если вы хотите использовать один компонент в другом,
    • 1. Сначала убедитесь, что используемые компоненты настоящие,
    • 2. Зарегистрируйте этот компонент через компоненты на экземпляре, который должен ссылаться на этот компонент,
    • 3. Компоненты нужно вводить в виде тегов в родительском шаблоне
<body>
<div id="app">

</div>

<!-- IMPORT JS-->
<script src="./20191007/node_modules/vue/dist/vue.js"></script>
<script>
   /*
   最终页面渲染效果
   parent
   son
   grandson
   */
   
   //=> 首先创建三个 组件 顺序颠倒过来  父亲 儿子 孙子   、
     
   /*
   * 如果要在一个组件使用另一个组件,
   *     1.先保证使用的组件得是真实存在,
   *     2.在需要引用这个组件的实例上通过components注册这个组件,
   *     3.组件需要在父级的模板中通过标签的形式引入
   * */
   //=> 孙子
   let grandson = { template:'<div>grandson</div>'};
   //=> 儿子
   let son = { template:'<div>son <grandson></grandson></div>',
       components:{
           grandson
       }
   };
    //=> 父亲
    let parent = {
        template:'<div>parent <son></son></div>',
        components: {
            son
        }
    };
    let vm = new Vue({
        el:'#app',
        //=> 在此处注册组件
        template:"<parent></parent>",
        components:{
            parent
        },
        data:{

        }
    });

</script>
</body>

Родитель-потомок-реквизит - передача собственности

  • props:[] способ передачи свойств и объектный способ передачи свойств
  • Параметрическая проверка свойств объекта пройдена
    • type: [] Тип четности
    • По умолчанию: 0, настройка по умолчанию
<body>
<div id="app">
    <!--父亲基于胡子语法取自己的 money 属性 -->
    父亲:{{money}}
    <!--
        当前组件的属性 = 父级的值
        给儿子加了一个m属性,属性对应的数据是属于父亲的  传递给儿子,可以传递多个
    -->
    <child :m="money"></child> <!--如果想传的是一个数字就加 : 不然是字符串-->
    <!--<child> </child> //=>这样才是没有传 而不是 :m="" -->
</div>
<!-- IMPORT JS-->
<script src="./20191007/node_modules/vue/dist/vue.js"></script>
<script>
    <!--vm 组件也是根实例-->
    //=> 父传递子 属性传递
    let vm = new Vue({ //=> parent
        el:'#app',
        data:{
            money:400
        },
        components:{
            child:{
                /*
                 template:'<div>儿子 {{money}}</div>'
                 儿子 组件取 父亲的 money 属性
                 属性没有拿到还引发了报错
                 因为 两个组件间作用域是独立的
                 
                 */
                 
                props:{ //=> 对象的形式可以对传的值进行检验 ,校验时不能阻断代码执行,只是阻断而已
                    m:{ //=> 校验属性的类型 不带 : 号得到的是字符串money  :m='1' :m='true'

                        type:[String,Function,Boolean,Array,Number],
                      /*  default:0, //=> 可以给m设置默认值,如果不传默认调用 default*/
                        required: true, //=> 此属性是必须传递,但是不能和default同时使用
                        validator(val){ //=>  val===money第一个参数是当前传递的值,返回true表示通过 false 反之
                            return val>300//=> 自定义校验器 

                        }
                    }
                },
                 //=> 基于 props:[]属性 儿子这里要接受一下父亲的’money‘
               /* props:['m'], 
               //=> this.m = 100;会在当前 子组件实例上 声明一个 m的属性,存的父亲传过来的money
                    也和data一样会影响视图渲染吗,但是子组件不能基于this.xxx来直接修改属性
                    
               */
                template:'<div>儿子:{{m}}</div>'
            }
        }
    });
</script>
</body>

Реализация информационного обмена родительско-дочерними компонентами на основе ref

  • Если используется в общем элементе ref DOM, эталонной точкой являются элементы DOM; если используется в подсборке, ссылка на экземпляр компонента на основе этого быстрого доступа к данным и операции подсборки
  • parent和

emit  订阅on

Модель подписки-публикации определяет зависимость «один ко многим», позволяя нескольким объектам-подписчикам одновременно прослушивать объект темы.

  1. Создание экземпляра эквивалентно передаче очереди задач
  2. vm.$on(выполняемое пользовательское событие, метод) Подписаться на пользовательские события и методы в очередь задач; стоит отметить, что когда мы вызываем дочерний компонент, основываясь на @xxx='func также эквивалентно подписке метода на очередь задач экземпляра, в котором находится подкомпонент.
  3. vm.$emit (пользовательское событие, информация, которую нужно передать) поместить указанное пользовательское событие в Метод инициирует выполнение, и соответствующая информация может быть передана в метод.

Односторонний поток данных vue

Все реквизиты образуют одностороннюю связь между их родительским и дочерним реквизитами: обновления родительского реквизита передаются дочернему компоненту, но не наоборот.

Порядок выполнения функции обработчика жизненного цикла родительского компонента и дочернего компонента Vue можно разделить на следующие 4 части:

  • Загрузить процесс рендеринга: родитель beforeCreate -> создан родитель -> родитель beforeMount -> дочерний beforeCreate -> создан дочерний элемент -> дочерний beforeMount -> смонтирован дочерний элемент -> смонтирован родительский

  • Процесс обновления подкомпонентов: Отец перед обновлением -> вложенный перед обновлением -> обновленный вложенный элемент -> обновленный родитель

  • Процесс обновления родительского компонента: Отец до обновления -> Отец обновлен

  • Процесс уничтожения: родитель перед уничтожением -> дочерний перед уничтожением -> дочерний уничтожен -> родительский уничтожен

Каждый раз, когда родительский компонент обновляется, все реквизиты в дочернем компоненте будут обновляться до последнего значения. Это означает, что вы не должны изменять свойства внутри дочернего компонента. Если вы это сделаете, Vue выдаст предупреждение в консоли браузера. Когда дочерний компонент хочет изменить, он может отправить пользовательское событие только через $emit.После того, как родительский компонент получит его, он будет изменен родительским компонентом.

Существует два распространенных сценария попытки изменить реквизит:

  • Это свойство передается как необработанное значение и должно быть преобразовано. В этом случае лучше определить вычисляемое свойство, используя значение этой опоры.
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<div id="app">
  <son :msg="pmsg"></son>
</div>

<script src="vue.js"></script>
<script>
  let son = {
    data() {
      return {
        hello: 'xxxx'
      }
    },
    props: ['msg', 'changePMSG'],
    template: '<span>{{msg}} <button @click="fn">dddd</button></span>',
    methods: {
      fn() {
        this.msg = 1233; // props 中的数据也会代理到子组件的实例身上,可以直接通过 this 访问
      }
    }
  };

  let vm = new Vue({
    el: '#app',
    data: {
      pmsg: 'msg from parent'
    },
    methods: {
      changeMsg() {
        this.pmsg = 123445
      }
    },
    components: {
      son
    }
  });
  // 1. 子组件中的数据不能全是写死的,而是有一部分从父组件传递过来的
  // 2. 为了把父组件的数据传递给子组件,子组件在标签上动态绑定一个属性,这个属性绑定父组件的数据,并且在子组件的props中注册这个属性
  // 3. 子组件如果想使用父组件的数据,就使用对应的 props 就可以(父传子用props)

  // 单向数据流:数据只能通过父组件传递给子组件,而不能直接从子组件传给父组件,子组件也不能直接修改父组件的数据;当父组件的数据发生改变之后,子组件的收到的数据也会跟着变化;如上面的例子,直接修改从父组件中的数据会引发Vue的报错;
  // 如果子组件想修改父组件的数据,只能通知父组件,让父组件修改数据;
</script>
</body>
</html>

Передача родителем-ребенку - выдано уведомление $ emit

  • on 及emit
<body>

<div id="app">
    父亲:{{money}}
    <!--
    儿子里面绑定了一个事件 child.$on('child-msg', things) 绑定的事叫做 child-msg ,事情主体是父亲的 things
    此时我们已经在儿子上绑定了一个child-msg ,当我们点击的时候让child-msg触发,触发时可以传参,
     -->

    <child :m="money" @child-msg="things"></child>

</div>
<!--
    父亲绑定一些事件,儿子触发这个事件将参数传递过去 ,
    单项数据流, 父亲数据刷新,儿子就刷新
-->
<!-- IMPORT JS-->
<script src="./20191007/node_modules/vue/dist/vue.js"></script>

<script>
    //=>
    let vm = new Vue({ //=> parent
        el:'#app',
        data:{
            money:400
        },
        methods:{
            //=> 儿子要触发这个事件,并且传一个参数 , 将此次事件绑定给

            things(val){ //=> 相当于 $on('xxx',things)

                //=>儿子通知父亲多给点钱  当父亲接收到过来的800 更新自己的价格
                //=> this为父亲
                this.money=val;
            }
        },
        components:{
            child:{
                props:['m'],

                template:'<div>儿子:{{m}}  <button @click="getMoney()">多要钱</button></div>',
                methods: {
                    getMoney(){
                        /*
                         this为当前的 child
                         当点击儿子组件时触发的事件child-msg ,
                         当child-msg事件触发的时候,会触发 things
                         并且将参数 800 传给 父亲的things 的 val
                         */
                        this.$emit('child-msg',800);//=> 触发自己的自定义事件,让父亲的方法执行 ,方法是父亲的,属性永远是当前组件的
                    }
                }

            }
        }
    });

</script>
</body>

  • Каждый компонент является экземпляром Vue и не может обмениваться данными друг с другом;

  • Чтобы изменить данные, вы должны уведомить, поэтому найдите третью сторону, позвольте третьей стороне прослушать событие и выполните соответствующую операцию по изменению данных при запуске события.Эта третья сторона — шина событий;

Использование шины событий

  • Создайте пустой экземпляр Vue, пусть eventBus = новый Vue ();

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="app">
  <prev></prev>
  <next></next>
</div>

<script src="vue.js"></script>
<script>
  // 每个组件都是一个 Vue的实例,相互之间不能互通数据;
  // 要修改数据一定要通知,所以找一个第三方,让第三方监听事件,在事件触发时执行对应的修改数据的操作;
  // eventBus.$on(自定义事件名, 事件函数) 监听事件
  // eventBus.$emit(事件名) 触发事件

  let eventBus = new Vue(); // 这第三方就是一个空的 Vue 实例,叫做事件总线 EventBus
  let prev = {
    data() {
      return {
        color: 'green'
      }
    },
    created() {
      eventBus.$on('changeRed', this.toRed) // 监听 changeRed 事件,当事件触发时,会执行 this.toRed 方法
    },
    methods: {
      toRed(x) {
        console.log(x); x 是事件触发时,传递的数据
        this.color = 'red';
      }
    },
    template: `<div :style="{background: color}">{{color}}</div>`
  };
  let next = {
    data () {
      return {
        color: '红色'
      }
    },
    methods: {
      red() {
        eventBus.$emit('changeRed', 'hahaha')
      }
    },
    template: `<div><button @click="red">变红</button></div>`
  };

  let vm = new Vue({
    el: '#app',
    data: {},
    components: {
      prev,
      next
    }
  });

  // 兄弟组件之间通信,通过 EventBus,谁的数据需要被改变,谁监听事件,谁发起改变谁触发事件;例如本例中,next 要修改 prev 的数据,所以在 prev 的 created 中 eventBus.$on() 监听事件,而 next 发起改变,所以在 next 中 $emit() 事件;

  // 哥哥改弟弟同理;

</script>
</body>
</html>

Что такое слот?

При ссылке на компонент мы можем встроить содержимое в тег компонента. Они могут быть встроены в подкомпоненты, но должны использовать механизм слота, т.е. слот;

Как использовать слоты

  • Во-первых, вам нужно объявить заполнитель тега слота при создании дочернего компонента;

  • Тег встроенный контент в компоненте

Именованный слот и анонимный слот

  • Анонимный слот, не пишут атрибут имени при объявлении слота, и теги, встроенные в компонентную тег без атрибута слота, будут размещены в анонимном слоте.

  • Именованный слот, напишите атрибут name при объявлении слота; содержимое, встроенное в тег компонента, должно указывать slot="имя слота"

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div id="app">
    <panel>
        <div>这是默认的内容</div>
        <p>hahahah </p>
        <div slot="header">这是个头</div>
        <div slot="body">主体</div>
        <div slot="footer">尾部</div>
    </panel>
</div>

<template id="tpl">
    <div>
        <slot></slot>
        <slot name="header"></slot>
        <slot name="body"></slot>
        <slot name="footer"></slot>
    </div>
</template>

<script src="vue.js"></script>
<script>
    let panel = {
        template: '#tpl',
        data() {
            return {
                x: 1
            }
        }
    };

    let vm = new Vue({
        el: '#app',
        data: {},
        components: {
            panel
        }
    });

    // 如果要想子组件中嵌入内容,需要使用插槽 slot;并且需要在子组件中提前用 slot 标签占位;
    // slot 分为匿名 slot 和具名 slot
</script>

</body>
</html>

Связь между предками и потомками на основе предоставления и внедрения

  • Компоненты-предки регистрируют данные, которые должны использоваться компонентами-потомками на основе предоставления
{
    provide:{ //=>对象或者返回对象的函数都可以(属性值如果是data中的数据,则必须使用函数的方法进行处理)
        name:'zhufeng',
        year:10
    },
    ...
}
  • Компонент-потомок объявляет данные, которые необходимо использовать, на основе инъекции и вызывает их для использования.
{
    inject:['name'],
    methods:{
        func(){
            let name=this.name;
        }
    }
}