Vue组件

作者: 放风筝的小小马 | 来源:发表于2017-08-22 10:05 被阅读41次

    测试代码

    创键组件

    两种方式:
    方法一:使用Vue.extend({})

    var component = Vue.extend({
      template: '<div>component</div>'
    });
    // 里面还可以添加其他的属性
    

    方法二:使用字面量

     var component = {
       template: '<div>component</div>'
    }
    

    注册组件

    全局注册
    语法:

    Vue.component('my-component', {
      // 选项
    })
    

    示例:

    // 创建组件
    var component = {template: '<div>component</div>'}
    // 全局注册组件
    
    Vue.component('my-component', component);
    
    

    局部注册

    // 创建组件
    var component = {template: '<div>component</div>'}
    // 在 Vue组件实例内注册component组件,该组件只能在该实例中使用
    var vm = new Vue({
                el: '#app',
                components: {
                    'mycomponent ': component ,
                }
            })
    

    注意点:

    • HTML 特性是不区分大小写的。所以,当使用的不是字符串模版,camelCased (驼峰式) 命名的 prop 需要转换为相对应的 kebab-case (短横线隔开式) 命名:
    Vue.component('child', {
      // camelCase in JavaScript
      props: ['myMessage'],      // 这里使用的是驼峰式命名
      template: '<span>{{ myMessage }}</span>'
    })
    // 在下面的HTML文件中引用时,需要改为kebab-case的形式,也就是短横线隔开
    <child my-message="hello!"></child>
    

    如果你使用字符串模版,则没有这些限制。

    注意(谨记):在HTML中,属性是不区分大小写的,例如:class、Class或CLASS都是表示class,因此,当在Vue中使用props向子组件传递数据时,如果props是驼峰式命名,那么在HTML文件中使用时,需要修改为kebab-case(短横线隔开式或称为烤肉串)的形式,否则,Vue将会无法解析

    使用props传递数据

    Vue.component('child', {
      // 声明 props
      props: ['message'],
      // 就像 data 一样,prop 可以用在模板内
      // 同样也可以在 vm 实例中像“this.message”这样使用
      template: '<span>{{ message }}</span>'
    })
    // 通过下面这种方式传递数据
    <child message="hello!"></child>
    

    动态prop

    将父组件的数据通过v-bind绑定到子组件上,与绑定HTML特性是一样的;每当父组件的数据变化都会反映到子组件上

    <div>
      <input v-model="parentMsg">
      <br>
      <child v-bind:my-message="parentMsg"></child>
    </div>
    

    字面量语法 VS 动态语法

    初学者常犯的一个错误是使用字面量语法传递数值:

    <!-- 传递了一个字符串 "1" -->
    <comp some-prop="1"></comp>
    

    因为它是一个字面 prop,它的值是字符串 "1" 而不是 number。如果想传递一个实际的 number,需要使用 v-bind,从而让它的值被当作 JavaScript 表达式计算:

    <!-- 传递实际的 number -->
    <comp v-bind:some-prop="1"></comp>
    

    通过v-bind:some-props="1"传递的是一个number,而通过some-prop="1"传递的是一个 字符串

    props数据校验

    Vue.component("example", {
      props: {
        // 基础类型检测 (`null` 意思是任何类型都可以)
        propA: Number,
        // 多种类型
        propB: [String, Number],
        // 必传且是字符串
        propC: {
          type: String,
          required: true
        },
        // 数字,有默认值
        propD: {
          type: Number,
          default: 100
        },
        // 数组/对象的默认值应当由一个工厂函数返回
        propE: {
          type: Object,
          default: function () {
            return { message: 'hello' }
          }
        },
        // 自定义验证函数
        propF: {
          validator: function (value) {
            return value > 10
          }
        }
      }
    });
    

    注意:props会在组件实例创建之前进行校验,所以在default、validator函数里,诸如:data, computedmethods等实例属性都无法使用

    非props特性传递

    在Vue中,在大多数情况下,传递给组件的值会覆盖组件本身就设定的值,例如传递type=large会覆盖组件上的type=small,且有可能破坏该组件,但是,对于class和style特性在传递时,不会发生覆盖,只会合并,假如组件上class=one,又传递class=two,则最终组件的class= one two

    原生事件监听

    通过在事件上添加.native可以增加对原生事件的监听,如下:
    `<my-component v-on:click.native="doTheThing"></my-component>

    .sync修饰符

    一般情况下,子组件对props的修改无法更新到父组件中,但是在2.3.0之后的版本中添加了.sync修饰符,可以在子组件上更新props,同时同步到父组件上,如下:
    <comp :foo.sync="bar"></comp>
    上面这段代码会被扩展为,如下代码:
    <comp :foo="bar" @update:foo=" val => bar = val"></comp>
    如果子组件需要更新,则按如下方式即可:
    this.$emit('update:foo', newValue);

    单向数据流

    prop绑定数据是单向的,当父组件的属性变化时,将传导给子组件,但是不会反过来。这是为了防止子组件无意修改了父组件的状态——这会让应用的数据流难以理解。

    另外,每次父组件更新时,子组件的所有 prop 都会更新为最新值。这意味着你不应该在子组件内部改变 prop。如果你这么做了,Vue 会在控制台给出警告

    使用插槽分发内容(slot)

    单个插槽

    注意几点:

    1. 当子组件没有slot插槽时,父组件调用子组件时,包含的内容都会丢失
    2. 当父组件调用子组件没有包含内容时,显示slot中的内容

    示例1:子组件没有slot插槽时

    <div>
      <h1>我是父组件的标题</h1>
      <c-child>
        <p>这是一些初始内容</p>
        <p>这是更多的初始内容</p>
      </c-child>
    </div>
    
    // 子组件child的模板,在该组件中没有slot,因此父组件调用子组件时包含的内容不会显示出来
    <template id="child">
      <div>
        <h2>我是子组件的标题</h2>
      </div>
    </template>
    
    Vue.component("c-child", {
       template: "#child"
    });
    
    new Vue({
       el: "#app-1"
    });
    

    上面代码输出结果如下:


    没有slot的显示效果

    当在child组件中添加slot,修改为如下代码:

    <template id="child">
      <div>
        <h2>我是子组件的标题</h2>
        <slot></slot>
      </div>
    </template>
    

    显示结果如下:

    添加slot后,父组件调用子组件时包含的内容显示出来了

    示例2:当父组件调用子组件时,没有包含内容,则显示slot中的内容

    <div>
      <h1>我是父组件的标题</h1>
      <c-child>
      </c-child>
    </div>
    
    // 子组件child的模板,在该组件中没有slot,因此父组件调用子组件时包含的内容不会显示出来
    <template id="child">
      <div>
        <h2>我是子组件的标题</h2>
        <slot>显示slot内容</slot>
      </div>
    </template>
    
    Vue.component("c-child", {
       template: "#child"
    });
    
    new Vue({
       el: "#app-1"
    });
    

    如下:


    显示slot内容

    具名插槽

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

    也就是说,在子组件中可以为slot通过name属性定义名字,然后在父组件调用子组件的时候,可以通过slot="header"来为子组件中name="header"的slot指定内容,假如指定的name 在子组件中没有找到,那么该内容将会被抛弃

    // 子组件模板
    <div class="container">
      <header>
        <slot name="header"></slot>
      </header>
      <main>
        <slot></slot>
      </main>
      <footer>
        <slot name="footer"></slot>
      </footer>
    </div>
    
    // 父组件
    <app-layout>
      <h1 slot="header">这里可能是一个页面标题</h1>
      <p>主要内容的一个段落。</p>
      <p>另一个主要段落。</p>
      <p slot="footer">这里有一些联系信息</p>
    </app-layout>
    

    最终的渲染结果如下:

    <div class="container">
      <header>
        <h1>这里可能是一个页面标题</h1>
      </header>
      <main>
        <p>主要内容的一个段落。</p>
        <p>另一个主要段落。</p>
      </main>
      <footer>
        <p>这里有一些联系信息</p>
      </footer>
    </div>
    

    该特性非常有用

    作用域插槽

    作用域插槽是一种特殊类型的插槽,用作一个替换已渲染元素的 (能被传递数据的) 可重用模板。
    在子组件中,只需将数据传递到插槽,就像你将 props 传递给组件一样:

    <div class="child">
      <slot text="hello from child"></slot>
    </div>
    

    在父级中,具有特殊属性 scope 的 <template> 元素必须存在,表示它是作用域插槽的模板。scope 的值对应一个临时变量名,此变量接收从子组件中传递的 props 对象:

    <div class="parent">
      <child>
        <template scope="props">
          <span>hello from parent</span>
          <span>{{ props.text }}</span>
        </template>
      </child>
    </div>
    

    以上代码渲染结果:

    <div class="parent">
      <div class="child">
        <span>hello from parent</span>
        <span>hello from child</span>
      </div>
    </div>
    

    一个示例:

    image.png
    参考:Vue作用域插槽的使用

    动态组件

    Vue中的动态组件需要指定一个挂载点,在该挂载点之下,可以动态的切换组件

    用法:
    通过<component v-bind:is="currentView"></component> 来调用组件,当currentView改变时,就切换组件,

    实例:

    var vm = new Vue({
      el: "#example",
      data: {
         currentView: 'home'
      },
      // 在该实例下的组件
      components: {
          home: { /* home组件 */},
          posts: { /* posts组件 */ },
      }
    });
    
    // 当vm.currentView改变时就进行组件切换,组件就在当前位置进行显示
    <component v-bind:is="currentView">
      <!-- 组件在 vm.currentview 变化时改变! -->
    </component>
    

    在使用动态组件进行切换时,被切换出来的组件会被消除,这样就存在一个问题:当重复的切换组件时,组件不断地被消除,然后又重建,导致性能很差,这时可以使用 keep-alive 来使被切换的组件保持在内存中,避免了重新渲染,keep-alive的用法如下:

    // 只需要添加keep-alive即可
    <keep-alive>
      <component v-bind:is="currentView">
        <!-- 组件在 vm.currentview 变化时改变! -->
      </component>
    </keep-alive>
    

    参考:

    相关文章

      网友评论

          本文标题:Vue组件

          本文链接:https://www.haomeiwen.com/subject/gbmkdxtx.html