vue进阶笔记

本文详细介绍了Vue的自定义指令、动画、组件定义、插槽、Prop与自定义事件等核心概念,涵盖从基本使用到高级技巧,帮助开发者深入理解Vue的组件化和动态特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义指令

自定义全局指令

  • 第一个参数:指令名称。
  • 第二个参数:一个对象,该对象有一下指令相关的钩子函数。
  • 注意:定义的时候,指令的名称前面不需加 v- 前缀,但调用的时候需要。
Vue.directive('指令名称', { /*钩子函数*/ });

自定义私有指令(directives属性)

  • 在声明vm对象中通过 directives 选项定义。
directives: {
  // focus为指令名
  focus: {
    // 指令的定义,inserted为钩子函数
    inserted: function (el) {
      el.focus();
    }
  }
}

使用自定义指令

  • 与官方定义指令使用一样,也可传参数。
<input v-focus>
<!-- 带参数 -->
<div id="app" v-demo="message"></div>

钩子函数

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。与JS行为相关的操作,最好在inserted去执行。
  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是可以通过比较更新前后的值来忽略不必要的模板更新。
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind:只调用一次,指令与元素解绑时调用。

钩子函数参数

指令钩子函数会被传入以下参数: (加粗为重要(或常用)属性)

  • el:指令所绑定的元素,可以用来直接操作 DOM 。
  • binding:一个对象,包含以下属性:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive=“1 + 1” 中,绑定值为 2。
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。

<div id="app" v-demo="message"></div>
Vue.directive('demo', {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify;
    el.innerHTML =
      'name: '       + s(binding.name) + '<br>' +
      'value: '      + s(binding.value) + '<br>' +
      'expression: ' + s(binding.expression) + '<br>' +
      'argument: '   + s(binding.arg) + '<br>' +
      'modifiers: '  + s(binding.modifiers) + '<br>' +
      'vnode keys: ' + Object.keys(vnode).join(', ');
  };
})

new Vue({
  el: '#app',
  data: {
    message: 'hello!'
  }
});

函数简写

在很多时候,可能想在 bindupdate 时触发相同行为,而不关心其它的钩子。可以采用以下方式:

Vue.directive('color-swatch', function (el, binding) {
  el.style.backgroundColor = binding.value;
});

对象字面量

如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) {
  console.log(binding.value.color); // => "white"
  console.log(binding.value.text);  // => "hello!"
})

Vue中的动画 <transition>

使用<transition>包裹需要动画的元素。

过渡类名

  • v-enter:定义进入过渡的开始状态。在元素被插入之前生效 。
  • v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。
  • v-enter-to2.1.8版及以上 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成之后移除。
  • v-leave:定义离开过渡的开始状态。
  • v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。
  • v-leave-to2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成之后移除。
    在这里插入图片描述

使用过渡类名

对于这些在过渡中切换的类名来说,**如果使用一个没有定义name属性的 <transition>,则 v- 是这些类名的默认前缀。**如果你使用了 <transition name="fade">,那么 v-enter 会替换为 fade-enter

<style>
	/* 定义进入和离开时候的过渡状态 */
    .fade-enter-active,
    .fade-leave-active {
      transition: all 0.2s ease;
      position: absolute;
    }
    /* 定义进入过渡的开始状态 和 离开过渡的结束状态 */
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
      transform: translateX(100px);
    }
</style>
<div id="app">
    <input type="button" value="动起来" @click="myAnimate">
    <!-- 使用 transition 将需要过渡的元素包裹起来 -->
    <transition name="fade">
      <div v-show="isshow">动画哦</div>
    </transition>
</div>
<script>
var vm = new Vue({
  el: '#app',
  data: {
    isshow: false
  },
  methods: {
    myAnimate() {
      this.isshow = !this.isshow;
    }
  }
});
</script>

过渡模式mode

  • in-out:新元素先进行过渡,完成之后当前元素过渡离开。
  • out-in:当前元素先进行过渡,完成之后新元素过渡进入。

例:

<transition name="fade" mode="out-in">
  <!-- ... the buttons ... -->
</transition>

使用第三方css动画库(自定义过渡的类名)

  • enter-class
  • enter-active-class
  • enter-to-class (2.1.8+)
  • leave-class
  • leave-active-class
  • leave-to-class (2.1.8+)
  • 他们的优先级高于普通的类名,含义对应上面的过渡类名 。
<link href="https://cdn.jsdelivr.net/npm/animate.css@3.5.1" rel="stylesheet" type="text/css">
<div id="example-3">
  <button @click="show = !show" value="Toggle render"></button>
  <transition
    name="custom-classes-transition"
    enter-active-class="animated tada"
    leave-active-class="animated bounceOutRight"
  >
    <p v-if="show">hello</p>
  </transition>
</div>
<script>
new Vue({
  el: '#example-3',
  data: {
    show: true
  }
});
</script>

显性的过渡持续时间

  • <transition> 组件上的 duration 属性定制一个显性的过渡持续时间 。

  • <transition :duration="数值">...</transition>

  • <transition :duration="{ enter: 数值, leave: 数值}">...</transition> 定制进入和移出的持续时间 。

JavaScript钩子 实现动画

可以在属性中声明 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) { ... },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  enter: function (el, done) { /* ... */ done(); },
  afterEnter: function (el) { ... },
  enterCancelled: function (el) { ... },
  // 离开时
  // --------
  beforeLeave: function (el) { ... },
  // 当与 CSS 结合使用时
  // 回调函数 done 是可选的
  leave: function (el, done) { /* ... */  done(); },
  afterLeave: function (el) { ... },
  // leaveCancelled 只用于 v-show 中
  leaveCancelled: function (el) { ... }
}

ps:

  • 当只用 JavaScript 过渡的时候,在 enter 和 leave 中必须使用 done 进行回调。否则,它们将被同步调用,过渡会立即完成。
  • 推荐对于仅使用 JavaScript 过渡的元素添加 v-bind:css="false",Vue 会跳过 CSS 的检测。这也可以避免过渡过程中 CSS 的影响。

初始渲染的过渡

可以通过 appear 特性设置节点在初始渲染的过渡 。

<transition appear>
  <!-- ... 初始化过渡默认和进入/离开过渡一样 -->
</transition>

列表过渡

使用 <transition-group> 组件。

  • 不同于 <transition>,它会以一个真实元素呈现:**默认为一个 <span>。可以通过 tag 特性更换为其他元素。
  • 内部元素 总是需要 提供唯一的 key 属性值。
  • 过渡模式不可用
<transition-group tag="ul">
        <li v-for="(item, i) in list" :key="item.id" @click="del(i)">
            {{item.id}}---{{item.name}}
        </li>
</transition-group>

列表的排序过渡

新增的 v-move 特性,它会在元素的改变定位的过程中应用。像之前的类名一样,可以通过 name 属性来自定义前缀。

Vue定义组件

组件化和模块化的不同:

  • 模块化: 是从代码逻辑的角度进行划分的;方便代码分层开发,保证每个功能模块的职能单一;
  • 组件化: 是从UI界面的角度进行划分的;前端的组件化,方便UI组件的重用;

全局组件定义的三种方式

  1. 使用 Vue.extend 配合 Vue.component 方法:
    • Vue.extend{ template: 'HTMLCode' } 返回一个组件模板对象。
    • Vue.component('组件名称', 创建出来的组件模板对象)
var login = Vue.extend({
      template: '<h1>登录</h1>'
});
Vue.component('login', login);
  1. 直接使用 Vue.component 方法:
    • Vue.component('组件名称', { template: 'HTMLCode' })
Vue.component('register', {
      template: '<h1>注册</h1>'
});
  1. 将模板字符串,定义到script标签(也可以是<template> 标签,但要放到被控制的组件外使用)中:(常用)
<script id="tmpl" type="x-template">
      <div><a href="#">登录</a> | <a href="#">注册</a></div>
</script>

同时,需要使用 Vue.component 来定义组件:

  • Vue.component('组件名称', 模板id选择器)
Vue.component('account', {
      template: '#tmpl'
});

注意: 组件中的DOM结构,有且只能有唯一的根元素(Root Element)来进行包裹!

组件使用

**如果使用 Vue.component 定义全局组件的时候,组件名称使用了驼峰命名,则在引用组件的时候,需要把大写的驼峰改写为小写的字母,两个单词见使用-链接。**如果不使用驼峰,则直接那名称来使用即可。

<account></account>

定义私有组件(components属性)

  • 在声明vm对象中通过 components 选项定义。
...
components: {
    account: {
        template: '<div><a href="#">登录</a> | <a href="#">注册</a></div>'
    }
}

组件中的data(注意)

  • 在组件中,data需要被定义为一个方法,且必须return对象。
  • 在子组件中,如果将模板字符串,定义到了script标签中,那么,要访问子组件身上的data属性中的值,需要使用this来访问.
Vue.component('account', {
      template: '#tmpl',
      data: function() {
        return {
          msg: '大家好!'
        }
      },
      methods:{
        login: function() {
          alert('点击了登录按钮');
        }
      }
});

使用components属性定义局部子组件

  1. 组件实例定义方式:
<script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      components: { // 定义子组件
        account: { // account 组件
          template: '<div><h1>这是Account组件{{name}}</h1><login></login></div>'
        }, // 在这里使用定义的子组件
      components: { // 定义子组件的子组件
        login: { // login 组件
          template: "<h3>这是登录组件</h3>"
        }
      }
    }
});
</script>
  1. 引用组件:
<div id="app">
    <account></account>
</div>

使用flag标识符结合v-ifv-else切换组件

  1. 页面结构:
<div id="app">
    <input type="button" value="toggle" @click="flag=!flag">
    <my-com1 v-if="flag"></my-com1>
    <my-com2 v-else="flag"></my-com2>
</div>
  1. Vue实例定义:
<script>
    Vue.component('myCom1', {
      template: '<h3>奔波霸</h3>'
    })

    Vue.component('myCom2', {
      template: '<h3>霸波奔</h3>'
    })

    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        flag: true
      },
      methods: {}
    });
  </script>

动态组件&异步组件

  • Vue提供了 compontent,来展示对应名称的组件。
  • component是一个占位符, :is属性可以用来指定要展示的组件的名称。
<style>
   .v-enter, .v-leave-to {
        opacity: 0;
        transform: translateX(150px);
    }
    .v-enter-active, .v-leave-active {
        transition: all 0.5s ease;
    }
</style>
<div id="app">
    <a href="#" @click.prevent="comName='login'">登录</a>
    <a href="#" @click.prevent="comName='register'">注册</a>
    <!--通过mode属性,设置组件切换时候的模式-->
    <transition mode="out-in">
        <component :is="comName"></component>
    </transition>
</div>
<script>
    Vue.component('login', {
        template: '<h3>登录组件</h3>'
    });

    Vue.component('register', {
        template: '<h3>注册组件</h3>'
    });
    var vm = new Vue({
        el: '#app',
        data: {
            // 存放is指定的名称
            comName: 'login'
        },
        methods: {}
    })
</script>

在动态组件使用<keep-alive>

当在这些组件之间切换的时候,都会重新渲染一遍;但有种时候切换回来时想保持第一次切换前的画面,则需要在第一次他们被创建的时候缓存下来,可以用一个 <keep-alive> 元素将其动态组件包裹起来。

<!-- 失活的组件将会被缓存!-->
<keep-alive>
  <component v-bind:is="currentTabComponent"></component>
</keep-alive>

注意这个 <keep-alive> 要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项还是局部/全局注册。

异步组件

一般用于大型项目,使用的组件足够多的时候,就需要考虑性能问题,则这时候就好使用异步组件。

Vue.js允许将组建定义为一个工厂函数,动态地解析组件。Vue.js 只是组件需要渲染时触发工厂函数,并且把结果缓存起来,用于后面的再次渲染。

Vue.component('async-example', function (resolve, reject) {
  setTimeout(function () {
    // 向 `resolve` 回调传递组件定义
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

注意:当使用webpack和.vue单文件的时候使用异步组件,要注意webpack的配置问题。

处理加载状态

2.3.0+新增

这里的异步组件工厂函数也可以返回一个如下格式的对象:

const AsyncComponent = () => ({
  // 需要加载的组件 (应该是一个 `Promise` 对象)
  component: import('./MyComponent.vue'),
  // 异步组件加载时使用的组件
  loading: LoadingComponent,
  // 加载失败时使用的组件
  error: ErrorComponent,
  // 展示加载时组件的延时时间。默认值是 200 (毫秒)
  delay: 200,
  // 如果提供了超时时间且组件加载也超时了,
  // 则使用加载失败时使用的组件。默认值是:`Infinity`
  timeout: 3000
})

插槽v-slot

在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot 指令)。它取代了 slotslot-scope
Vue 2.x 提供了用来访问被slot分发的内容的方法$slot,用法:this.$slot.具名slot,不常用。

编译作用域

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

<navigation-link url="/profile">
  Clicking here will send you to: {{ url }}
  <!--
  这里的 `url` 会是 undefined,因为 "/profile" 是
  _传递给_ <navigation-link> 的而不是
  在 <navigation-link> 组件*内部*定义的。
  -->
</navigation-link>

后备内容(slot的默认内容)

后备内容,只会在没有提供内容的时候被渲染:

例如:

<submit-button></submit-button>
<script>
	var btn = Vue.component('submit-button', {
        template: 
        `<button type="submit">
  			<slot>submit</slot>
		</button>`
    });
    
    var app = new Vue({
        el: '#app',
    });
</script>

将被渲染为:

<button type="submit">
  Submit
</button>

具名插槽

当想把具体模板插入具体位置,就需用到具名插槽v-slot

任何没有被包裹在带有 v-slot<template> 中的内容都会被视为默认插槽的内容,即没有name的slot。

  1. 在组件模板中的slot提供name
  2. 使用组件的时候,内部内容添加v-slot

v-slot:插槽名字name

补充:不添加v-slot,其实内部默认添加v-slot:default

例如:

<base-layout>
  <template v-slot:header>
    <h1>Here might be a page title</h1>
  </template>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <template v-slot:footer>
    <p>Here's some contact info</p>
  </template>
</base-layout>

<script>
	var btn = Vue.component('base-layout', {
        template: 
        `<div class="container">
  			<header>
    			<slot name="header"></slot>
  			</header>
  			<main>
    			<slot></slot>
  			</main>
  			<footer>
    			<slot name="footer"></slot>
  			</footer>
		</div>`
    });
    
    var app = new Vue({
        el: '#app',
    });
</script>

将被渲染为:

<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>

作用域插槽

有时让插槽内容能够访问子组件中才有的数据是很有用的。例如,设想一个带有如下模板的 <current-user> 组件:

<span>
  <slot>{{ user.lastName }}</slot>
</span>
<current-user>
  {{ user.firstName }}
</current-user>

只有<current-user> 组件可以访问到 user ,而user.firstName 是在父组件中渲染的,模板中slot访问的user却是子组件的数据(user)。

<div id="app">
    <current-user>
        <template v-slot="userSlot">
            {{ userSlot.user.firstName }}
        </template>
    </current-user>
</div>
<!-- 省略引入vue.js -->
<script type="text/javascript">
    Vue.component('current-user', {
        template: 
        `<span>
                <slot :user='user'>{{ user.lastName }}</slot>
            </span>`,
        data: function () {
            return {
                user: {
                    firstName: '好看',
                    lastName: '好好好'
                }
            }
        },
    });

    var vm = new Vue({
        el: '#app',
        data: {}
    });
</script>

解析:
在slot元素上有一个类似 props 传递数据给组件的写法 v-bind:属性名 = 'xxx' 将数据传到了插槽。在父组件中使用 v-slot = props,这里的props是一个临时变量,保存传递过来的属性,用它来访问来自子组件传递过来的数据。

注意:v-slot = props 等价于 v-slot:default = props (默认插槽)。2.6版本之前可以使用 slot-scope = props ,现已废弃但还可以使用。

具名插槽的缩写

v-onv-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

和其它指令一样,该缩写只在其有参数的时候才可用。即#="{user}",会报警告

Prop(父向子传值)

  1. 子组件中,默认无法访问到父组件中的data上的数据和methods中的方法。
  2. 组件中的所有props中的数据,都是通过父组件传递给子组件的。
  3. props中的数据都是只读的,data中的数据可读可写的
  4. props的值可以是两种,一种是字符串数组,一种是对象。
  • 父组件,可以在引用子组件的时候,通过属性绑定(v-bind)的形式,把需要传递给子组件的数据,以属性绑定的形式,传递给子组件内部,供子组件使用。

由于HTML特性不区分大小写,当使用DOM模板时,驼峰命名的props名称要转为短横分隔命名。
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变这个对象或数组本身将会影响到父组件的状态。

<div id="app">
    <com1 v-bind:parentmsg="msg"></com1>
</div>
  • 子组件通过 props 获取数据。
var vm = new Vue({
    el: '#app',
    components: {
        com1: {
            template: '<h1>这是子组件 --- {{parentmsg}}</h1>',
            props: ['parentmsg']
        }
    }
});

字符串数字写法

props: ['warningText']

对象写法

Vue.component('my-component', {
    props: {
        // 必须是数字类型
        propA: Number,
        // 必须是字符串或数字类型
        propB: [String, Number],
        // 布尔值,如果没有定义,默认值就是true
        propC: {
            type: Boolean,
            default: true
        },
        // 数字,而且是必传
        propD: {
            type: Number,
            required: true
        },
        // 如果是数组或对象,默认值必须是一个函数来返回
        propE: {
            type: Array,
            default: function () {
                return [];
            }
        },
        // 自定义一个验证函数
        propF: {
            validator: function (value) {
                return value > 10;
            }
        }
    }
});

单向数据流

Vue.2.x 与 Vue 1.x 比较大的一个改变就是,Vue 2.x 通过 props 传递数据时单向的,也就是父组件数据变化时会传递给子组件,但反过来是不行的。而Vue 1.x 里提供了 .sync 的修饰符来支持双向绑定。

非 Prop 的特性

一个非 prop 特性是指传向一个组件,但是该组件并没有相应 prop 定义的特性。

因为显式定义的 prop 适用于向一个子组件传入信息,然而组件库的作者并不总能预见组件会被用于怎样的场景。这也是为什么组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上。

例如,想象一下你通过一个 Bootstrap 插件使用了一个第三方的 <bootstrap-date-input> 组件,这个插件需要在其 <input> 上用到一个 data-date-picker 特性。我们可以将这个特性添加到你的组件实例上:

<bootstrap-date-input data-date-picker="activated"></bootstrap-date-input>

然后这个 data-date-picker="activated" 特性就会自动添加到 <bootstrap-date-input> 的根元素上。

替换/合并已有的特性

对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。比如传入 type="text" 就会替换掉 type="date" 并把它破坏!classstyle 特性除外,即两边的值会被合并起来。

例:

  • <bootstrap-date-input> 的模板
<input type="date" class="form-control">
  • 然后为这个组件添加一个类
<bootstrap-date-input
  data-date-picker="activated"
  class="date-picker-theme-dark"
></bootstrap-date-input>

禁用特性继承

如果希望组件的根元素继承特性,可以在组件的选项中设置 inheritAttrs: false。例如:

Vue.component('my-component', {
  inheritAttrs: false,
  // ...
})

这尤其适合配合实例的 $attrs 属性使用,该属性包含了传递给一个组件的特性名和特性值,例如:

{
  required: true,
  placeholder: 'Enter your username'
}

有了 inheritAttrs: false$attrs,就可以手动决定这些特性会被赋予哪个元素。在撰写基础组件的时候是常会用到的:

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on:input="$emit('input', $event.target.value)"
      >
    </label>
  `
})

自定义事件(子向父传值)

  1. 原理:父组件将方法的引用,传递到子组件内部,子组件在内部调用父组件传递过来的方法,同时把要发送给父组件的数据,在调用方法的时候当作参数传递进去。
  2. 父组件将方法的引用传递给子组件,其中,show是父组件中methods中定义的方法名称,func是子组件调用传递过来方法时候的方法名称。
<div id="app">
    <!--父组件向子组件传递方法,使用的是 事件绑定机制,v-on,当自定义一个 事件属性之后,子组件就能够通过$emit方法获取-->
    <com2 @func="show"></com2>
</div>
  1. 子组件内部通过this.$emit('方法名', 要传递的数据)方式,来调用父组件中的方法,同时把数据传递给父组件使用。
    • $emit第一个参数是父组件传过来的方法,后面的参数可选,为父组件传过来的方法传参。
<div id="app">
    <com2 @func="show"></com2>
</div>
<template id="tmp1">
    <div>
        <h1>这是子组件</h1>
        <input type="button" value="子组件的按钮,点击触发父组件传递过来的func方法" @click="myclick">
    </div>
</template>

<script>
    var com2 = {
        template: '#tmp1',
        data: function () {
            return {
                sonmsg: {name: '小头儿子', age: 6}
            }
        },
        methods: {
            myclick: function () {
                // $emit第一个参数是父组件传过来的方法,后面的参数可选,为父组件传过来的方法传参
                this.$emit('func', this.sonmsg);
            }
        }
    }

    var vm = new Vue({
        el: '#app',
        data: {
            datamsgFormSon: null
        },
        methods: {
            show: function (data) {
                this.datamsgFormSon = data;
            }
        },
        components: {
            'com2': com2
        }
    });
</script>

非父子组件通信

Vue 1.x 版本

  • 在 Vue 1.x 中,除了 $emit 方法外,还提供了 $dispatch()$boroadcast() 这两个方法。$dispatch() 用来向上级派发事件,只要是它的父级(一级或多级以上),都可以在Vue实例的events选项内接收。
  • $broadcast() 是由上级向下级广播事件的,用法与 $dispatch() 一致,只是方向相反。
  • 这两个方法一旦发出事件后,任何组件都是可以接收到的,就近原则,而且会在第一次接收到后停止冒泡,除非返回true。
    例:(了解)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
	<!-- 注意:该实例需使用Vue.js 1.x版本 -->
    <div id="app">
        {{message}}
        <my-component></my-component>
    </div>
    <script type="text/javascript" src="lib/vue.js"></script>
    <script type="text/javascript">
    Vue.component('my-component', {
        template: '<button @click="handleDispatch">派生事件</button>',
        methods: {
            handleDispatch: function() {
                this.$dispatch('on-message', '来自内部组件的数据');
            }
        }
    });

    var app = new Vue({
        el: '#app',
        data: {
            message: ''
        },
        events: {
            'on-message': function(msg) {
                this.message = msg;
            }
        }
    })
    </script>
</body>
</html>

Vue 2.x 版本

  • 在vue 2.x 中,使用一个空的Vue 实例座位中央事件总线(bus),也就是一个中介。
  • bus 可以实现任何组件间的通信,包括父子、兄弟、跨级。当项目比较大的时候,推荐使用vuex。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>

<body>
    <div id="app">
    	{{message}}
    	<component-a></component-a>
    </div>
    <script type="text/javascript" src="lib/vue.js"></script>
    <script type="text/javascript">
    var bus = new Vue();
    Vue.component('component-a', {
        template: '<button @click="handleEvent">传递事件</button>',
        methods: {
            handleEvent: function() {
                bus.$emit('on-message', '来自组件component-a的内容');
            }
        }
    });
    var app = new Vue({
        el: '#app',
        data: {
            message: ''
        },
        mounted: function() {
            var _this = this;
            // 在实例初始化时,监听来自bus实例的事件
            bus.$on('on-message', function(msg) {
                _this.message = msg;
            });
        }
    });
    </script>
</body>
</html>

处理边界情况

访问根实例

在所有子组件中,其根实例可以通过 $root 属性进行访问。

推荐大项目还是使用Vuex来管理。

访问父级组件实例

$parent 属性可以用来从一个子组件访问父组件的实例。

访问子组件实例或子元素

通过 ref 特性为子组件赋予一个 ID 引用(类似html的id)。

<h3 id="myh3" ref="myh3">我怎么这么好看,这么好看怎么办?</h3>
this.$refs.ref名字   // 获取到该组件的DOM对象

$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。应该避免在模板或计算属性中访问 $refs

依赖注入

使用 $parent 属性无法很好的扩展到更深层级的嵌套组件上(比如突然加多一层父组件则又需要加多一层$parent)。

依赖注入使用到两个选项:provideinject

  • provide 选项允许指定想要提供给后代组件的数据/方法。

    provide: function () {
      return {
        getMap: this.getMap
      }
    }
    
  • 在任何后代组件里,可以使用 inject 选项来接收指定的想要添加在这个实例上的属性:

    inject: ['getMap']
    

缺点:依赖注入将应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的。

混入

基础

混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。

例:

// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}

// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component(); // => "hello from mixin!"

Vue.extend() 局部注册

选项合并

当组件和混入对象含有同名选项时,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。(methodscomponentsdirectives 都是如此)

例:

var mixin = {
  data: function () {
    return {
      message: 'hello',
      foo: 'abc'
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: 'goodbye',
      bar: 'def'
    }
  },
  created: function () {
    console.log(this.$data)
    // => { message: "goodbye", foo: "abc", bar: "def" }
  }
})

同名钩子函数将合并为一个数组,因此都将被调用。而且混入对象的钩子将在组件自身钩子之前调用。

var mixin = {
  created: function () {
    console.log('混入对象的钩子被调用')
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log('组件钩子被调用')
  }
})

// => "混入对象的钩子被调用"
// => "组件钩子被调用"

全局混入(谨慎使用)

混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。

// 为自定义的选项 'myOption' 注入一个处理器。
Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: 'hello!'
})
// => "hello!"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值