Vue 学习笔记

1. 自定义事件

Vue 实例内部已经实现发布订阅功能,直接用即可。当任意的两个组件想要通信时,可以利用自定义事件做到。

加入 A 组件想给 B 组件传送数据。A 组件可以这么写:

<template>
    <h2 @mouseenter="change('Hello!')">组件A</h2>
</template>

<script>
import event from '../event';

export default {
  name: 'A',
  methods: {
    change(msg){
      // 传送数据,myEvent 是自定义事件的事件名称,msg 是数据
      event.$emit('myEvent', msg);
    }
  }
}
</script>

B 组件:

<template>
  <h1>组件 B</h1>
</template>

<script>
import event from "../event";
export default {
  name: 'B',
  methods: {
    //   事件函数
    handler(data){
      console.log(data);
    }
  },
  props: {
    msg: String
  },
  mounted(){
    //   组件挂载后,注册事件
    event.$on('myEvent', this.handler);
  },
  beforeDestroy(){
    //   组件卸载时解除事件
    event.$off('myEvent', this.handler);
  }
}
</script>

event 对象如下:

import Vue from "vue";
export default new Vue;

父子组件通信

子组件想要获取到父组件的状态,可以在 props 中获取,子组件调用父组件的方法时可以使用 $emit 的形式,也可以使用 props 的方式。例如:

<template>
  <div>
    <button @click="toggle">Click</button>
  </div>
</template>

<script>

export default {
  name: 'B',
  props: {
    add: Function,
    isShow: Boolean,
  },
  methods: {
    toggle(){
      // add 是传入的 props 方法
      this.$props.add(2);
      // 调用父组件传入的自定义事件
      this.$emit('toggle');
    }
  }
}
</script>

这个例子中,子组件 B 的按钮点击时组件 A 会展示与隐藏来回的切换。

父组件如下:

<template>
  <div id="app">
    <A v-if="isShow" :num="num" />
    <B @toggle="toggle" :add="add" />
  </div>
</template>

需要注意的:@toggle 的写法应使用 this.$emit 的方式调用;:add 的方式会把数据挂载到 props 上。@ 的方式相当于自定义事件。对于 props 上的方法,有时并没必要使用 this.$props 来获取,在 JSX 中直接使用即可。

<template>
  <button @click="add(2)">Click +2</button>
</template>

2. 自定义 v-model

以双向绑定输入框为例。App 组件是父组件,A 和 B 都是其子组件,A 组件负责输入内容,B 组件负责展示输入的内容。JSX 如下:

<template>
  <div id="app">
    <B :msg="msg"/>
    <!-- msg 是双向绑定的数据 -->
    <A title="A --> 组件A" v-model="msg" />
  </div>
</template>

B 组件接收的 msg 是 App 组件的数据,msg 由 A 组件输入的内容提供。v-model 用于双向绑定,尤其是表单中很方便使用。这里为 A 组件进行双向绑定。A 组件的内容如下:

<template>
  <input type="text" 
    :value="val"
    @input="$emit('change',$event.target.value)"
  />
</template>

<script>
export default {
  name: 'A',
  props: {
    val: String     // val 对应于父组件中的 msg
  },  // modle 是用于双向绑定的特殊对象
  model: {
    // prop 的值应与 props 中的 val 和 JSX 中的 :value 中的 val 名称一样
    prop: 'val',
    // 双向绑定时的事件函数,当有一方数据变化时这个函数就会触发
    event: 'change'
  }
}
</script>

3. 作用域插槽

插槽是为了能让父组件往子组件里插入一些东西。比如:

<template>
    <div id="app">
        <A title="组件A"><!-- 组件 A 里可以嵌套 JSX -->
        <B :msg="msg"/>
            <!-- v-slot 是具名插槽,v-slot:desc 也可以简写成 #desc -->
            <template v-slot:desc>
                <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Ab, possimus modi repudiandae nulla soluta quisquam voluptas qui cupiditate laudantium doloribus porro dicta necessitatibus quibusdam distinctio mollitia quod libero numquam doloremque?</p>
            </template>
        </A>
    </div>
</template>

然后是组件 A,如下:

<template>
    <div>
        <!-- solt 中间的内容是默认值,当 A 组件中不传入 JSX 时,会展示这里面的内容 -->
        <slot>OK!</slot>
        <hr />
        <h2>{{ title }}</h2>
        <!-- name 对应于上面的 template v-slot 值 -->
        <slot name="desc"></slot>
    </div>
</template>

<script>
export default {
    name: 'A',
    props: {
        title: String
    }
}
</script>

插槽除了上面的用法之外,还由一种“作用域插槽”(slot-scope)。这种插槽父组件可以获取子组件 slot 中的一些数据。例如:

<template>
    <div id="app">
        <A title="组件A">
            <!-- 作用域插槽使用 slot-scope 属性定义 -->
            <template slot-scope="slotProps">
                <!-- 将获取到的插槽数据传递给 B 组件 -->
                <B :msg="slotProps.slotData"/>
            </template>
        </A>
    </div>
</template>

A 组件:

<template>
    <div>
        <!-- 定义插槽 -->
        <slot :slotData="msg"></slot>
        <hr />
        <h2>{{ title }}</h2>
    </div>
</template>
<script>
export default {
    name: 'A',
    props: {
        title: String
    },
    data () {
        return {
            msg: '你好!'
        }
    }
}
</script>

4. $nextTick

Vue 在更新 DOM 时是异步执行的。数据改变后,DOM 不会立即渲染,想要拿到更新后的 DOM 或它的状态做点什么,这是不容易做到的。比如下面的例子,每次点击按钮都会增加列表元素,添加后我们想要获取到添加后的列表长度。

<template>
  <div id="app">
    <ul ref="ul">
      <li v-for="(item, index) in list" :key="index">{{ item }}</li>
    </ul>
    <button @click="add">add item</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    add(){
        this.list.push('🍌');
        this.list.push('🍊');
        // 获取列表个数
        console.log(this.$refs.ul.children.length);
    }
  },
  data() {
    return {
      list: ['🍎']
    }
  }
}
</script>

当点击后,控制台打印的列表长度总是上一次的。如果要打印这一次的长度,可以使用 $nextTick 方法。它会在 DOM 渲染之后触发。如下:

add(){
    this.list.push('🍌');
    this.list.push('🍊');
    this.$nextTick(() => {
        console.log(this.$refs.ul.children.length);
    });
}

在 Vue 中,因为是异步渲染,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。对于多次数据变更,只会被推入到队列中一次。这样可以避免不必要的计算和 DOM 操作。

5. keep-alive

keep-alive 可以缓存组件。例如一些频繁切换的组件或者不需要重新渲染的场景。比如一些很复杂的组件,当重新渲染时可能会影响用户体验,这时就可以选择把这个组件缓存下来。

<keep-alive>
    <A v-if="isShow" />
</keep-alive>

keep-alive 有两个属性:excludeinclude。这两个属性会有条件地缓存。值可以用逗号分隔字符串、正则表达式或一个数组来表示。设置后会匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的父组件 components 选项的键值。匿名组件不能被匹配。例如:

<keep-alive :include="['a', 'b']">
  <component :is="view"></component>
</keep-alive>

当组件在 <keep-alive> 内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行。生命周期触发顺序:

  • 初次进入时:created -> mounted -> activated;退出后触发 deactivated;
  • 再次进入:会触发 activated;事件挂载的方法等,只执行一次的放在 mounted 中;组件每次进去执行的方法放在 activated 中;

<keep-alive> 用在其一个直属的子组件被开关的情形。如果在其中有 v-for 则不会工作。

6. 动态组件

动态组件书写格式:

<component :is="view" />

根据 is 的值动态渲染组件。component 主要用于动态渲染,组件类型不确定的场景当中。

例如:

<template>
  <div id="app">
    <!-- 渲染变量 b 对应的组件名 -->
    <component :is="b" />
    <!-- 渲染变量 a 对应的组件名 --> 
    <component :is="a" />
  </div>
</template>

<script>
import A from './components/A';
import B from './components/B';
export default {
  name: 'App',
  components: {
    A,
    B
  },
  data(){
    return {
      a: 'A',   // a 对应于 A 组件
      b: 'B'    // b 对应于 B 组件
    }
  }
}
</script>

动态组件可以与 keep-alive 配合,优化渲染。

7. 异步组件

异步组件是优化页面性能的重要手段,比如点击某个按钮,这个组件会展示,这时才发起网络请求获取该组件。这种方式被称为“按需加载”。在组件的 components 属性中定义。书写如下:

components: {
    LazyComponent: () => import('./LazyComponent'),
}

8. mixin

多个组件有相同的逻辑时,可以使用 mixin 将公共的部分抽离出来,复用代码。

mixin 中的代码与 Vue 中 script 中的一样,例如:

export default {
  methods: {
    add (step) {
      this.number += step
    }
  },
  data () {
    return {
      number: 1
    }
  },
  mounted () {
    console.log('mixin mounted');
  }
}

可以有方法、计算属性、生命周期等。使用时引入即可:

import A from './components/A';
import B from './components/B';
import mixin from './mixin';  // 引入 mixin
export default {
  name: 'App',
  mixins: [mixin],  // 注册 mixin
  components: {
    A,
    B
  },
  data () {
    return {
      msg: 'The number is: '
    };
  }
}

这样 App 组件就可以使用 mixin 中的数据或者方法。

<template>
  <div id="app">
    <A :add="add" />
    <B :msg="msg" :number="number" />
  </div>
</template>

mixin 的不足

  1. 变量来源不明确,比如上面的代码,add 函数和 number 变量我们并没有显示的导入,这就导致可能不太明白这两个数据是怎么来的,用途是什么;
  2. 导入多个 mixin 可能会有冲突,数据可能被覆盖;
  3. mixin 和组件可能出现多对多的关系,这就会导致复杂度提高,不利于维护。

9. 事件

  • 如何传入 event 对象?

在 Vue 中绑定的事件监听函数是很智能的。绑定的是函数执行,Vue 会把传入的实参传入到我们定义的函数中。例如:

<button @click="add(2)">click</button>
// ... 

{
  method: {
    add (step) {
      this.num += step;
    }
  }
}

当传入的只是一个函数时(并没有执行),Vue 默认会把 event 对象传入。

<button @click="add">click</button>
// ... 

{
  method: {
    add (event) {
      console.log(event);
      this.num += step;
    }
  }
}

如果要显式的传入 event 对象,可以这么做:

<button @click="add(2, $event)">click</button>

$event 是 Vue 内置的事件对象。在 Vue 中的 event 对象是原生的事件对象。并且事件被挂载到当前的元素上。可以通过下面的方式验证:

console.log(event.currentTarget);

currentTarget 总是绑定事件的那个元素。而 e.target 是当前触发事件的元素。

10. 修饰符

Vue 内部实现了许多修饰符,比如事件修饰符。如下:

<button @click.stop="handle">Click</button>

@click.stop 会阻止事件冒泡。除此之外还有一些可能常用的修饰符:

  • @click.capture 使用事件捕获模式;
  • @submit.prevent 提交事件触发时不再重载页面;
  • @click.stop.prevent 不冒泡也不重载页面;
  • @click.self 只有当 event.target 是当前元素(绑定click事件的元素)是才触发(这样的话,当点击该元素的子元素时,只会触发子元素绑定的事件;当点击该元素时,子元素也不会触发事件,这与阻止冒泡相似,只是阻止冒泡在子元素上设置)。

除此之外,还有按键修饰符。例如:

  • @click.ctrl 当鼠标点击并且按下 ctrl 键时才会触发事件(Alt 或者 shift 也一起按下时也会触发事件);
  • @click.ctrl.exact 当点击数据并且按下 ctrl 的按键时才触发事件(当 alt 或者 shift 按键也被一起按下时,不会触发事件);
  • @click.exact 除了系统修饰符(如ctrlshiftalt 键)之外的按键按下并且鼠标点击时才触发事件;
  • @click.shift 点击并且按下 shift 按键时才触发事件;

当然,除了上述的 click 与按键组合之外,也可以与别的事件组合。 更多可以参考官网:按键修饰符

除了事件修饰符以外,v-model 也有几个修饰符:

  • v-model.trim 输入的字符串空格会被过滤掉;
  • v-model.lazy 在监听时用 change 事件代替 input 事件(内容发生变化,并且失去焦点时触发 change 事件);
  • v-model.number 将输入的字符串传为有效的数字。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值