Vue学习(2)计算属性,监听,指令,组件

computed

模板内表达式只能用于简单运算,难以处理复杂逻辑。对于任何复杂逻辑,都应当使用计算属性。

  • HTML例
<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>

在模板中的使用与模板表达式没有区别。

  • JS例
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

全写与简写

完整的计算属性函数包括 getter setter 函数:

computed: {
    
    message: {
        // 计算属性的 getter,返回到模板
        get(){
            return this.message
        }
        // 计算属性的 setter,返回到data
        set(val){
            return val
        }
    }
  }

一般情况下计算属性只有 getter 需求,则可以简写为一个函数:

computed: {
    // 计算属性的 getter
    message() {
        return this.message
    }
  }

和调用方法的区别——缓存

通过在表达式中调用方法可以达到与计算属性同样的效果,两种方式的最终结果确实是完全相同的。
不同点:

  • 计算属性的值依赖计算函数中依赖的其它响应式数据,如果依赖的其它响应式数据没有发生变化,计算属性的值可以缓存,得到结果是最近一次变化产生的值。如果响应式数据没有改变,直接返回之前缓存的结果,不必再次执行函数。
  • 每当触发重新渲染时,不管相关数据变不变,调用方法将总会再次执行函数。

如果计算量大且复杂,用计算属性;如果不希望有缓存,用方法代替。

watch

当需要在数据变化时执行异步或开销较大的操作时,watch监听器(侦听器)是最有用的。

  • HTML例
<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
  • JS例
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    // 如果 `question` 发生改变,这个函数就会运行
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
    }
  },
})

多层监听

对于多层对象数据的监听,可以使用字符串和点的语法。

data: {
    a:{
        b:{
            c:''
        }
    }
}
watch: {
  'a.b.c': function() {
    //...
  }
}

深度监听

默认情况下,watch 只对当前指定的值进行一层监听,如果需要对对象进行深度监听,可以使用下面的形式:

watch: {
  a: {
    handler() {
      console.log('a deep');
    },
    deep: true
  }
}

指令

指令 (Directives) 是特殊 attribute。指令的职责是,当表达式的值改变时,响应式地作用于 DOM,进行更强大的 DOM 操作。

vue 中,指令是一个带有 v- 前缀的属性,与普通属性不一样的地方在于,指令的值是引号括起来的 表达式 ,不同的指令有不同的作用。vue 内置了一些常用的指令,我们还可以自定义属于自己的指令。

注意: 指令值中直接写表达式,不能使用 {{}}

内置指令

功能 指令 功能和用法
- 显示与隐藏 v-show 根据表达式的值(布尔值),切换元素的显示与隐藏(display 属性)。适用于状态切换比较频繁的情况
- 条件渲染 v-if / v-else / v-else-if 根据表达式的值(布尔值),创建或销毁元素。适用于状态切换不频繁的情况
- 列表渲染 v-for 根据数据循环渲染 v-for 指令所在的元素及其子元素。可以循环的数据:Array,Object,number,string,Iterable (2.6 新增)
- 属性绑定 v-bind 绑定数据(表达式)到指定的属性上。对应的缩写为:
- 事件 v-on 绑定事件函数(表达式)到指定的属性上。缩写 @
- 双向绑定 v-model 数据 title 更新,视图中 inputvalue 就会更新。同时,当 input 中的 value 更新的时候,数据 title 也会更新。

指令修饰符

一个指令可以包含的内容包括:

  • 指令名称
  • 指令值
  • 指令参数
  • 指令修饰符
<组件 指令:参数.修饰符1.修饰符2="值" />

v-model的修饰符

修饰符 功能和用法
.lazy 取代 input 监听 change 事件
.number 输入字符串转为有效的数字
.trim 输入首尾空格过滤

v-on的修饰符

修饰符 功能和用法
.stop 调用 event.stopPropagation()。
.prevent 调用 event.preventDefault()。
.capture 添加事件侦听器时使用 capture 模式。
.self 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.once 只触发一次回调。
.passive (2.3.0) 以 { passive: true } 模式添加侦听器

自定义指令

我们还可以通过 Vue 提供的方法来自定义指令。

注册指令

vue 提供了两种指令注册方式

  • 全局指令
  • 局部指令

全局指令注册

Vue.directive('指令名称', {指令配置});

局部指令注册

new Vue({
  el: '#app',
  directives: {
    '指令名称': {指令配置}
  }
});

在使用指令的时候,需要使用 v-指令名称 的方式来调用,注册的时候不需要。

指令生命周期(钩子函数)

指令的运行方式很简单,它提供了一组指令生命周期钩子函数,我们只需要在不同的生命周期钩子函数中进行逻辑处理就可以了

  • <u>bind</u> : 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
  • <u>inserted</u> : 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
  • <u>update</u> : 所在组件更新的时候调用(但是可能发生在其子 VNode 更新之前)。
  • <u>componentUpdated</u> : 所在组件及其子组件更新完成后调用
  • <u>unbind</u> : 只调用一次,指令与元素解绑时调用
Vue.directive('drag', {
  bind(el, binding) {

  },
  inserted(el, binding) {

  },
  update(el, binding) {

  },
  componentUpdated(el, binding) {

  }
  unbind(el, binding) {

  },
});

指令钩子函数会被传入以下参数(主要):

  • <u>el</u> : 指令所绑定的元素,可以用来直接操作 DOM
  • <u>binding</u> : 一个对象,包含以下属性:
    • <u>name</u> : 指令名,不包括 v- 前缀
    • <u>value</u> : 指令的绑定值(作为表达式解析后的结果)
    • <u>expression</u> : 指令绑定的表达式(字符串)
    • <u>arg</u> : 传给指令的参数,可选
    • <u>modifiers</u> : 传给指令的修饰符组成的对象,可选,每个修饰符对应一个布尔值
    • <u>oldValue</u> : 指令绑定的前一个值,仅在 <u>update</u> 和 <u>componentUpdated</u> 钩子中可用,无论值是否改变都可用

组件

组件是可复用的 Vue 实例,且带有一个名字。自定义的组件可以像HTML标签一样使用。

  • 示例:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
// 通过 new Vue 创建的 Vue 根实例
new Vue({ el: '#components-demo' })

我们可以在一个通过 new Vue 创建的 Vue 根实例中,把这个组件作为自定义元素来使用:

<div id="components-demo">
  <button-counter></button-counter>
</div>

组件注册

2-1、全局定义

Vue.component('组件名称', {/*组件选项*/})

可以在任意位置(多个不同的 Vue 应用中)使用。

2-2、局部定义

new Vue({
  // ...,
  components: {
    '组件名称': {/*组件选项*/}  
    }
})

局部组件只能在其定义的组件内使用,不能在其子组件内部使用。

组件的数据

组件内部私有数据——data

组件的 data 必须是函数,且该函数必须返回一个对象作为组件最终的 data,这样每个实例才可以维护一个独立的被返回对象:

data: function () {
  return {
    count: 0
  }
}

组件外部传入数据——props

如同一个函数一样,函数除了可以定义内部私有变量,有时候为了提高函数的复用性,我们通过会通过参数来接收外部传入的数据。组件内部私有数据存储在 data 中;外部传入的数据,则通过 props 选项接收。

组件内部通过 props 来定义可以接收的数据名称,就相当于是函数的形参。然后,在使用该组件的时候可以通过标签属性的方式进行传参(可配合 v-bind 传入表达式)。

注意事项

  • 如果传入的 props 值为一个表达式,则必须使用 v-bind
  • 组件中的 dataprops 数据都可以通过组件实例进行直接访问
  • data 中的 keyprops 中的 key 不能冲突

Prop

通过 Prop 向子组件传递数据

  • 示例:
Vue.component('blog-post', {
  props: ['title'],
  template: '<h3>{{ title }}</h3>'
})

一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data 中的值一样。一个 prop 被注册之后,你就可以像这样把数据作为一个自定义 attribute 传递进来:

<blog-post title="My journey with Vue"></blog-post>
<blog-post title="Blogging with Vue"></blog-post>
<blog-post title="Why Vue is so fun"></blog-post>

Prop 验证

组件的 props 就是组件的参数,为了确保传入的数据在可控的合理范围内,我们需要对传入的 props 的值类型进行必要的验证。

Vue.component('my-component', {
  props: {
    // 基础的类型检查 (`null` 和 `undefined` 会通过任何类型验证)
    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 规定的名称
      validator: function (value) {
        // 这个值必须匹配下列字符串中的一个
        // return value > 10 && value < 100;
        return ['success', 'warning', 'danger'].indexOf(value) !== -1
      }
    }
  }
})

type 可以是下列原生构造函数中的一个:

  • String
  • Number
  • Boolean
  • Array
  • Object
  • Date
  • Function
  • Symbol

当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。

组件生命周期

组件生命周期指的是组件从创建到销毁的过程,在这个过程中的一些不同的阶段,vue 会调用指定的一些组件方法。

基本生命周期函数有下面几个阶段,每一个阶段都对应着 之前之后 两个函数:

阶段 函数 介绍
- 创建阶段 beforeCreate 在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。初始化阶段,应用不多。
created 在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el property 目前尚不可用。
- 挂载阶段 beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted 该阶段执行完了模板解析,以及挂载。同时组件根组件元素被赋给了 $el 属性,该阶段可以通过 <u>DOM</u> 操作来对组件内部元素进行处理了。
- 更新阶段 beforeUpdate 数据更新时调用,但是还没有对视图进行重新渲染,这个时候,可以获取视图更新之前的状态。
updated 由于数据的变更导致的视图重新渲染,可以通过 <u>DOM</u> 操作来获取视图的最新状态。
- 卸载阶段 beforeDestroy 实例销毁之前调用,移除一些不必要的冗余数据,比如定时器。
destroyed Vue 实例销毁后调用。
- 其它 .$nextTick 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
errorCaptured 当捕获一个来自子孙组件的错误时被调用,此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,794评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,050评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,587评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,861评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,901评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,898评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,832评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,617评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,077评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,349评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,483评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,199评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,824评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,442评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,632评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,474评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,393评论 2 352