Vue组件

VUE

##组件Component
组件是 Vue最强大的功能之一,组件可以扩展 HTML 元素,封装可重用的代码。组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树

Alt text

优点

  • 代码复用
  • 便于维护

组件要求

  • data必须为Function类型
  • 每个组件必须只有一个根元素,否则报错
  • 注册时组件名可以是kebab-case或PascalCase,但在html页面上使用时,必须写成遵循W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)

组件注册

组件是可复用的 Vue 实例,带有一个名字,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等(el选项除外)

全局组件

使用Vue.component()创建的为全局组件,所有的Vue实例都可以使用

    Vue.component('my-component', {
      // ... options ...
      template:'<p>我是全局组件</p>'
    })
局部组件

在某个Vue实例中通过components属性注册的组件为局部组件,只有当前实例能使用

    
    var Child = {
        data(){
            return {
                name:'我是局部组件'
            }
        },
        template: '<h1>hello, {{name}}</h1>'
    }
     
    // 创建根实例
    new Vue({
      el: '#app',
      components: {
        Child
      }
    });
使用组件

使用组件时,组件template中的内容会替换调组件所在位置

    <div id="app">
      <my-component></my-component>
      <child></child>
    </div>

注意:由于Vue 只有在浏览器解析和标准化 HTML 后才能获取模板内容,所以把组件嵌套在某些特定的元素(如table,ul,ol,select等)上时,会导致解析错误

  <!-- table为已经存在html页面上的元素 -->
  <table>
    <my-row></my-row>
  </table>
  Vue.component('my-row',{
    template:'<tr><td>test</td></tr>'
  });

以上解析的结果为,tr被鸡西到了table外面,解决方式也很简单,利用特殊的is属性实现()

  <table>
    <tr is="my-row"></tr>
  </table>

组件通讯

父组件->子组件:

组件实例的作用域是孤立的。这意味着不能(也不应该)在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,需要通过子组件的props选项

  • props声明属性:声明的属性会自动成为组件实例的属性(可通过this访问)

(PS:组件中的属性如果不利用props声明,则自动称为组件根元素的属性)

  • prop是单向绑定的,当父组件的属性变化时,将传导给子组件,但是不会反过来
    <blog-post mytitle="静态数据"></blog-post>
    Vue.component('blog-post', {
      props: ['mytitle'],
      template: '<h3>{{ mytitle }}</h3>'
    })
   <blog-post :msg="message"></blog-post>

    <!-- 传入一个对象 -->
   <blog-post v-bind:author="{ name: 'laoxie', age:18 }"></blog-post>
    Vue.component('blog-post', {
      props: ['msg','author'],
      template: '<h3>{{ mytitle }}</h3>'
    });

    let vm = new Vue({
      data:{
        message:'hello laoxie',
        post:{
          id: 1,
          title: 'My Journey with Vue'
        }
      }
    })
  • 非prop属性:不通过props声明的属性

此类属性会自动成为组件根节点的属性

Prop数据验证
  Vue.component('my-component', {
    props: {
      // 基础的类型检查 (`null` 匹配任何类型)
      propA: Number,
      // 多个可能的类型
      propB: [String, Number],
      // 必填的字符串
      propC: {
        type: String,
        required: true
      },
      // 带有默认值的数字,无propD属性传入时,默认得到100
      propD: {
        type: Number,
        default: 100
      },
      // 带有默认值的对象
      propE: {
        type: Object,
        // 对象或数组默认值必须从一个工厂函数获取
        default: function () {
          return { message: 'hello' }
        }
      },
      // 自定义验证函数
      myscore: {
        validator: function (value) {
          // 这个值必须大于等于60,否则报错
          return val>=60
        }
      }
    }
  })
子组件->父组件: 自定义事件的系统 + $emit()

PS:Vue不允许在子组件中直接修改props传入的父组件数据,正确的做法是把它们作为子组件数据的初始值

  • 方式一(推荐):

    1. 子组件上自定义事件(如:show),并使用父组件的事件处理函数(handler)

    <mycom v-on:show="handler" />

    1. 子组件内部触发自定义事件并传递参数

    this.$emit(‘show’,100)
    会触发父组件的事件处理函数,从而实现数据传递

  • 方式二:

    1. 可以利用.sync修饰符(如下color属性)
    2. 子组件触发更新this.$emit('update:xx',val)
  <div id="app">
    <p :style="{fontSize:fontSize+'px'}">字体大小:{{fontSize}}</p>
    <btn-change :font-size="fontSize" @bigger="changeFontSize" :color.sync="color"></btn-change>
  </div>

  <template id="myButton">
    <button @click="change">改变字体大小</button>
  </template>

  <script>
    new Vue({
      el:'#app',
      data:{
          fontSize:16,
          color:'red'
      },
      components:{
        btnChange:{
          props:['fontSize'],
          data(){
            return {
              // 把传入的fontSize做为子组件的初始值
              initFontSize:this.fontSize
            }
          },
          template:'#myButton',
          methods:{
            change(){
              this.initFontSize++;

              // 手动触发自定义事件并传递修改后的值
              this.$emit('bigger',this.initFontSize);
            }
          }
        }
      },
      methods:{
        changeFontSize(val){
          // 触发自定义事件的事件处理函数,并修改字体大小
          this.fontSize = val;
        }
      }
    })
  </script>
兄弟组件通信
  • 组件A -> 父组件 -> 组件B
  • 利用中间桥梁(Vue实例)
    • 接收方:自定义事件
    • 传输方:$emit()
    let bus = new Vue();

    //组件A
    let comA = {
        data(){
            return {
            msg:'I am A'
            }
        },
        template:`<div>
            <p>{{msg}}</p>
            <button @click="sendToB">传数据到B组件</button>
        </div>`,
        methods:{
            sendToB(){
                bus.$emit('toB',this.msg);
            }
        }
    }

    // 组件B
    let comB = {
        data:()=>({
            msg:'I am B'
        }),
        mouted(){
            bus.$on('toB',val=>this.msg = val)
        },
        template:`<div><p>{{this.msg}}</p></div>`
    }

    // 创建实例,并注册子组件
    new Vue({
        el:'#app',
        components:{
            comA,
            comB
        }
    });
利用插槽分发内容
  • 把组件内容传入组件模板

在组件模板中利用<slot></slot>来承载组件内容,否则它的内容都会被忽略(被模板内容覆盖)

  <!-- 使用指令 -->
  <nav-link url="/home">
    首页
  </nav-link>

  <!-- 定义指令 -->
  <script>
    Vue.component('nav-link',{
      props:['url']
      //以下写法,组件内容“首页”,会被template的内容覆盖掉,
      //最终解析为:<a href="/home"><span>Home</span></a>
      //template:`<a :href="url"><span>Home</span></a>`

      // 可以用<slot></slot>保留内容和设置默认值
      // 最终解析为:<a href="/home"><span>首页</span></a>
      template:`<a :href="url"><slot>Home</slot></a>`
    });
  </script>
  • 具名插槽:给slot设置name属性,实现内容精准显示到模板具体位置
  <Child>
    <span slot="header">这里的内容显示到name为header的插槽</span>
    <span>这里的内容显示到默认插槽</span>
    <span slot="footer">这里的内容显示到name为footer的插槽</span>
  </Child>
  <template id="myTest">
    <div>
      <slot  name="header">这是拥有命名的slot的默认内容</slot>
      <slot>这是拥有命名的slot的默认内容</slot>
      <slot  name="footer">这是拥有命名的slot的默认内容</slot>
    </div>
  </template>
  • 作用域插槽:slot-scope

Vue编译规则:父组件模板的所有东西都会在父级作用域内编译;子组件模板的所有东西都会在子级作用域内编译

利用作用域插槽实现把组件模板的数据传到组件内容中处理,实现特殊定制

  <!-- 组件模板 -->
  <div class="box">
    <slot :msg="msg" :username="username">{{username}},{{msg}}</slot>
  </div>

  <!-- 组件内容 -->
  <mynav>
    <!-- props为传过来的数据组成的对象 -->
    <div slot-scope="props">
      {{props.msg}},{{props.username}}
    </div>
  </mynav>

内置组件

  • <component> 动态组件
    • is:指定渲染的组件
  <component v-bind:is="currentTabComponent"></component>
  • <keep-alive> 缓存组件
    如果把切换出去的组件保留在内存中,可以保留它的状态或避免重新渲染可以添加一个 keep-alive

包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们,主要用于保留组件状态或避免重新渲染

* include : 指定缓存组件名
* exclude : 指定不缓存的组件名
  <keep-alive>
     <component v-bind:is="currentTabComponent"></component>
  </keep-alive>
  • <slot> 内容分发(详情
过渡动画

<transition> & <transition-group>
Vue只有在插入,更新或者移除DOM元素时才会应用过渡效果,过渡效果的应用可以通过不同方式实现:

  1. 在CSS过渡和动画中自动应用class;
  2. 配合使用第三方的CSS动画库,如Animate.css;
  3. 在过渡钩子函数中使用JavaScript直接操作DOM;
  4. 配合使用第三方JavaScript动画库,如Velocity;
CSS过渡
  • CSS过渡类名

组件过渡过程中,会有四个CSS类名进行切换,这四个类名与transition的name属性有关,比如name=“fade”,会有如下四个CSS类名:

* fade-enter:进入过渡的开始状态,元素被插入时生效,只应用一帧后立即删除;
* fade-enter-active:进入过渡的结束状态,元素被插入时就生效,在过渡过程完成之后移除;
* fade-leave:离开过渡的开始状态,元素被删除时触发,只应用一帧后立即删除;
* fade-leave-active:离开过渡的结束状态,元素被删除时生效,离开过渡完成之后被删除;

![transition](./img/transition.png "Optional title")
  • 自定义过渡类名

通过enter-class、enter-active-class、leave-class、leave-active-class这四个特性来定义,可配合animate.css框架实现

JavaScript过渡(钩子函数)
    <transition
      v-on:before-enter="beforeEnter"
      v-on:enter="enter"
      v-on:after-enter="afterEnter"
      v-on:enter-cancelled="enterCancelled"
      v-on:before-leave="beforeLeave"
      v-on:leave="leave"
      v-on:after-leave="afterLeave"
      v-on:leave-cancelled="leaveCancelled"
    >
    </transition>
    methods: {
      // 过渡进入
      // 设置过渡进入之前的组件状态
      beforeEnter: function (el) {
        // ...
      },
      // 设置过渡进入完成时的组件状态
      enter: function (el, done) {
        // ...
        done()
      },
      // 设置过渡进入完成之后的组件状态
      afterEnter: function (el) {
        // ...
      },
      enterCancelled: function (el) {
        // ...
      },
      // 过渡离开
      // 设置过渡离开之前的组件状态
      beforeLeave: function (el) {
        // ...
      },
      // 设置过渡离开完成时地组件状态
      leave: function (el, done) {
        // ...
        done()
      },
      // 设置过渡离开完成之后的组件状态
      afterLeave: function (el) {
        // ...
      },
      // leaveCancelled 只用于 v-show 中
      leaveCancelled: function (el) {
        // ...
      }
    }

【案例】

  • 利用动态组件事件Tab标签切换
  • 实现一个可复用的搜索组件
  • 开发goTop 返回顶部组件

【练习】

  • 封装一个step步骤条

stpes

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值