vue组件

177 阅读5分钟

vue父子组件间数据传递

父组件传子组件

父组件可以通过属性的方式,单向传递数据给子组件,但是子组件接收到的数据不能直接修改,这会影响其他父组件的数据。子组件可通过克隆的方式,将父组件传过来的值在data里克隆一份,然后修改克隆后的数据,这样不会影响父组件的数据。

<body>
    <div id="root">
        <counter :count="1"></counter>
        <counter :count="1"></counter>
    </div>
    <script>
        // 点击子组件,数字自增
        // 父组件向子组件传值,子组件通过data将父组件传过来的值复制一遍再使用
        var counter = {
          props: ['count'],
          data: function() {
            return {
              num: this.count
            };
          },
          template: "<div @click='handleClick'>{{num}}</div>",
          methods: {
            handleClick: function() {
              this.num ++;
            }
          }
        }
        var vm = new Vue({
          el: "#root",
          components: {
            counter: counter
          }
        })
    </script>
</body>

子组件传父组件

子组件通过事件触发的形式,向父组件传值。$emit(事件名, 参数)。

<body>
  <div id="root">
    <counter :count="2" @change="handleChange"></counter>
    <counter :count="3" @change="handleChange"></counter>
    {{total}}
  </div>
  <script>
    // 父组件向子组件传值,子组件通过data将父组件传过来的值复制一遍再使用
    var counter = {
      props: ['count'],
      data: function() {
        return {
          num: this.count
        };
      },
      template: "<div @click='handleClick'>{{num}}</div>",
      methods: {
        handleClick: function() {
          this.num = this.num + 2;
          this.$emit("change", 2); // change事件名、2传递参数,单词累加步长
        }
      }
    }
    var vm = new Vue({
      el: "#root",
      components: {
        counter: counter
      },
      data: {
        total: 5
      },
      methods: {
        handleChange: function(step) { // step接收子组件传递过来的参数2
          this.total += step;
        }
      }
    })
  </script>
</body>

组件参数校验与非Props特性

props特性

父组件传递给子组件的数据,可以通过pros设置参数类型、是否必传、默认值、长度要求等,达到校验的目的。

<body>
  <div id="root">
    <child count="1"></child>
  </div>

  <script>
    Vue.component("child", {
      props: {
        // count: [Number, String] // 参数校验,只能是数字或字符串类型
        count: {
          type: Number,   // 类型为字符串
          required: true, // true表示 count为必传属性
          default: 'default value', // 默认参数,如果count未传,则为默认值
          validator: function(val) { // 长度须大于5
            return (val.length > 5) 
          }
        }
      },
      template: '<div>{{count}}</div>'
    })

    var vm = new Vue({
      el: "#root"
    })
  </script>
</body>

非Props特性

  1. 非props特性,即父组件传递的参数,子组件不使用props声明,这时直接使用会报错。
  2. 父组件传递参数的属性,会加入到子组件最外地标签上,同样以属性的方式存在。props特性则不会显示在子组件标签上

给组件绑定原生事件

在父组件自定义事件名后加上native修饰符即可,将自定义事件变为原生事件。

<body>
  <div id="root">
    <child @click.native="handleClick"></child>
  </div>
  
  <script>
    Vue.component("child", {
      template: '<div>child</div>'
    })
    var vm = new Vue({
      el: "#root",
      methods: {
        handleClick: function() {
          alert('click');
        }
      }
    })
  </script>
</body>

非父子组件间传值问题

  1. Vuex
  2. 总线机制(Bus/总线/发布订阅模式/观察着模式)
<body>
  <div id="root">
    <child content="2019"></child>
    <child content="2020"></child>
  </div>

  <script>
    // 兄弟组件传值:点击组件a,组件b的内容也变成a的,点击b同理
    Vue.prototype.bus = new Vue()

    Vue.component("child", {
      data: function() {
        return {
          selfContent: this.content
        }
      },
      props:{
        content: String
      },
      template: '<div @click="handleClick">{{selfContent}}</div>',
      methods: {
        handleClick: function() {
          // alert(this.content)
          this.bus.$emit("change", this.selfContent) // 事件发布
        }
      },
      mounted: function() {
        var _this = this;
        this.bus.$on('change', function(msg) {  // 事件订阅
          // alert(msg)
          _this.selfContent = msg;
        })
      }
    })

    var vm = new Vue({
      el: "#root",
    })

  </script>
</body>

slot插槽

具名插槽

元素可以用一个特殊的属性name,来配置如何分发内容。多个插槽可以有不同的名字,具名插槽将匹配内容片段中有对应 slot 特性的元素。

<body>
  <div id="root">
    <child>
      <div slot="header">header</div>
      <div slot="footer">footer</div>
    </child>
  </div>

  <script>
    Vue.component("child", {
      template: 
            `<div>
              <slot name="header"></slot>
              <div>content</div>
              <slot name="footer"></slot>
            </div>`
    })

    var vm = new Vue({
      el: "#root",
    })
  </script>
</body>

作用域插槽

使用场景:当子组件的部分DOM结构需要外部传入的时候,或者子组件内部循环。 当使用作用域插槽的时候,子组件可以向父组件插槽传递数据,父组件接收传递的数据需要在外层包裹template标签,同时使用slot-scoped="属性名",接收传递过来的数据,然后使用数据。

<body>
  <div id="root">
    <child>
      <!-- 父组件调用子组件的时候,给子组件传了一个作用域插槽,以template标签包裹,并且在其插槽上声明slot-scope="props" 要从子组件接收的数据 放在props里 -->
      <template slot-scope="props">
        <!-- li标签 展示从子组件接收的数据 -->
        <li>{{props.item}}</li>
      </template>
    </child>
  </div>

  <script>
    Vue.component("child", {
      data: function() {
        return {
          List: [1,2,3,4,5]
        }
      },
      template: `<div>
                  <ul>
                    <slot v-for="item of List" :item=item></slot>
                  </ul>
                </div>`
    })
    var vm = new Vue({
      el: "#root",
    })
  </script>
</body>

动态组件与v-once指令

动态组件component

v-once

如果有多个组件,需要在页面切换展示,如果不使用v-once,每次页面重新渲染的时候,都需要重新加载组件。而组件上加v-once,当组件第一次被渲染的时候,会将组件缓存,下次渲染的时候,无须重新加载。

<body>
  <div id="root">
    <!-- 动态组件component,通过:is="属性名"切换队形组件 -->
    <!-- <component :is="type"></component> -->
    <child-one v-if="type === 'child-one'"></child-one>
    <child-two v-if="type === 'child-two'"></child-two>
    <!-- 点击change 实现child-one和child-two的切换 -->
    <button @click="btnClick">change</button>
  </div>

  <script>
    Vue.component("child-one", {
      template: '<div v-once>child-one</div>'
    })
    Vue.component("child-two", {
      template: '<div v-once>child-two</div>'
    })
    var vm = new Vue({
      el: "#root",
      data: function() {
        return {
          type: "child-one"
        }
      },
      methods: {
        btnClick: function() {
          this.type = this.type === "child-one" ? "child-two" : "child-one";
        }
      }
    })
  </script>
</body>