Vue(二)生命周期、侦听器、动态组件、Prop参数承接


layout: post
title: Vue(二)生命周期、侦听器、动态组件、Prop参数承接
description: Vue(二)生命周期、侦听器、动态组件、Prop参数承接
tag: 前端


v-on绑定事件调用方法

<div id="app-5">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">反转消息</button>//绑定方法reverseMessage
</div>

reverseMessage方法在Vue实例中定义:

var app5 = new Vue({
  el: '#app-5',
  data: {
    message: 'Hello Vue.js!'
  },
  methods: {
    reverseMessage: function () {
      this.message = this.message.split('').reverse().join('')//方法实体,先单个分开成单字符(split)再翻转(reverse),再合并(join)
    }
  }
})

在这里插入图片描述在这里插入图片描述

组件化应用构建

在 Vue 里,一个组件本质上是一个拥有预定义选项的一个 Vue 实例。在 Vue 中注册组件很简单:

// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
  template: '<li>这是个待办项</li>'//模板是列表形式
})

var app = new Vue({
    el: '#app'
})

用它构建另一个组件模板,让他成为无序列表中的一项

<ol>
  <!-- 创建一个 todo-item 组件的实例 -->
  <todo-item id= "app"></todo-item>
</ol>

但是这样会为每个待办项渲染同样的文本,这看起来并不炫酷。我们应该能从父作用域将数据传到子组件才对。让我们来修改一下组件的定义,使之能够接受一个 prop:

Vue.component('todo-item', {
  // todo-item 组件现在接受一个
  // "prop",类似于一个自定义 attribute。
  // 这个 prop 名为 todo。
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'//这里的todo.text表明了todo是一个对象,具体见下边的Vue实例
})

使用 v-bind 指令将待办项传到循环输出的每个组件中:

<div id="app-7">
  <ol>
    <!--
      现在我们为每个 todo-item 提供 todo 对象
      todo 对象是变量,即其内容可以是动态的。
      我们也需要为每个组件提供一个“key”,稍后再
      作详细解释。
    -->
    <todo-item
      v-for="item in groceryList"    
      v-bind:todo="item"
      v-bind:key="item.id"
    ></todo-item> <!--遍历groceryList,绑定列表的每一项(item)和列表的索引(id)-->
  </ol>
</div>
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { id: 0, text: '蔬菜' },
      { id: 1, text: '奶酪' },
      { id: 2, text: '随便其它什么人吃的东西' }
    ]
  }
})

前端显示数据非响应式

vue的数据property都是响应式的,即修改后前端显示会同步相应修改,
使用 Object.freeze(),这会阻止修改现有的 property,也意味着响应系统无法再追踪变化。

实例生命周期

下图展示了实例的生命周期:
在这里插入图片描述生命周期钩子的 this 上下文指向调用它的 Vue 实例,这里先介绍几个常用的钩子:

  • created :组件被创建后
  • mounted:组件被渲染后
  • updated :组件更新后
  • destroye:组件销毁后

计算属性和侦听器

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如:

<div id="example">
  {{ message.split('').reverse().join('') }}
</div>

在这个地方,模板不再是简单的声明式逻辑。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中的多处包含此翻转字符串时,就会更加难以处理。所以,对于任何复杂逻辑,你都应当使用计算属性。

<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})

计算属性缓存 vs 方法:

我们可以通过在表达式中调用方法来达到同样的效果
不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。
这一点可以用时间戳来说明,加入计算属性中使用事件戳,第一次用和第二次用,时间戳是相同的,因为计算结果已经在缓存中,仅当缓存更新时,计算属性值才会更新。而假如表达式中两次使用时间戳,则结果是不同的。

计算属性 vs 侦听属性:

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当你有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch——特别是如果你之前使用过 AngularJS。然而,通常更好的做法是使用计算属性而不是命令式的 watch 回调。

<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

侦听器(*)

<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
<!-- 因为 AJAX 库和通用工具的生态已经相当丰富,Vue 核心代码没有重复 -->
<!-- 提供这些功能以保持精简。这也可以让你自由选择自己更熟悉的工具。 -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
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...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    // `_.debounce` 是一个通过 Lodash 限制操作频率的函数。
    // 在这个例子中,我们希望限制访问 yesno.wtf/api 的频率
    // AJAX 请求直到用户输入完毕才会发出。想要了解更多关于
    // `_.debounce` 函数 (及其近亲 `_.throttle`) 的知识,
    // 请参考:https://lodash.com/docs#debounce
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
    }
  }
})
</script>

事件处理方法

除了直接绑定到一个方法,还可以在内联 JavaScript 语句中调用方法:

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
<!--注意这里click直接是调用的方法-->
new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})

有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event 把它传入方法:

<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
// ...
methods: {
  warn: function (message, event) {
    // 现在我们可以访问原生事件对象
    if (event) {
      event.preventDefault()
    }
    alert(message)
  }
}

事件修饰符

在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。

为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

.stop
.prevent
.capture
.self
.once
.passive
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

通过 Prop 向子组件传递数据

当组件变得越来越复杂的时候,.比如一篇博客,我们的博文不只需要标题和内容,还需要发布日期、评论等等。为每个相关的信息定义一个 prop 会变得很麻烦:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:title="post.title"
  v-bind:content="post.content"
  v-bind:publishedAt="post.publishedAt"
  v-bind:comments="post.comments"
></blog-post>

所以是时候重构一下这个 组件了,让它变成接受一个单独的 post prop:

<blog-post
  v-for="post in posts"
  v-bind:key="post.id"
  v-bind:post="post"
></blog-post>
Vue.component('blog-post', {
  props: ['post'],
  template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <div v-html="post.content"></div>
    </div>
  `
})

现在,不论何时为 post 对象添加一个新的 property,它都会自动地在 内可用。
值得注意的一点是v-html指令,它表示以html语句解析post.content的内容,相应的有v-text指令,同一个字符串“<h1>这是一个h1元素内容</h1>”

  • 使用v-html会解析出一号标题,显示内容“这是一个h1元素内容”
  • 使用v-text则直接会显示字符串本身“<h1>这是一个h1元素内容</h1>”

监听子组件事件

在我们开发 组件时,它的一些功能可能要求我们和父级组件进行沟通。例如我们可能会引入一个辅助功能来放大博文的字号,同时让页面的其它部分保持默认的字号。
在其父组件中,我们可以通过添加一个 postFontSize 数据 property 来支持这个功能:

<div id="blog-posts-events-demo">
  <div :style="{ fontSize: postFontSize + 'em' }">
    <blog-post
            v-for="post in posts"
            v-bind:key="post.id"
            v-bind:post="post"
            v-on:enlarge-text="postFontSize += 0.1"
    ></blog-post>
  </div>
</div>
<!--父级元素blog-posts-events-demo,使用了自定义组件blog-post,组件blog-post内定义有子组件button,有了这个 v-on:enlarge-text="postFontSize += 0.1" 监听器,父级组件就会接收该事件并更新 postFontSize 的值。子组件中通过调用内建的 $emit 方法并传入事件名称来触发-->
Vue.component('blog-post', {
    props: ['post'],
    template: `
    <div class="blog-post">
      <h3>{{ post.title }}</h3>
      <button v-on:click="$emit('enlarge-text')">//子组件可以通过调用内建的 $emit 方法并传入事件名称来触发一个事件
        Enlarge text
      </button>
      <div v-html="post.content"></div>
    </div>
  `
  })
    new Vue({
    el: '#blog-posts-events-demo',
    data: {
      posts:  [
        { id: 1, title: 'My journey with Vue' },
        { id: 2, title: 'Blogging with Vue' },
        { id: 3, title: 'Why Vue is so fun' }
      ],
      postFontSize: 1//字体大小赋初值
    }
  })

总结:自定义父组件中使用v-on绑定事件v-on:enlarge-text=“postFontSize += 0.1”,事件定义中监听数据postFontSize,子组件button的click触发$emit("enlarge-text)

使用事件抛出一个值

有的时候用一个事件来抛出一个特定的值是非常有用的。例如我们可能想让 组件决定它的文本要放大多少。这时可以使用 $emit 的第二个参数来提供这个值:

<button v-on:click="$emit('enlarge-text', 0.1)">
  Enlarge text
</button>

然后当在父级组件监听这个事件的时候,我们可以通过 $event 访问到被抛出的这个值:

<blog-post
  ...
  v-on:enlarge-text="postFontSize += $event"
></blog-post>

总结:自定义父组件中使用v-on绑定事件v-on:enlarge-text=“postFontSize += $ event”,事件定义中监听数据postFontSize,子组件button的click触发$ emit("enlarge-text,0.1),指定事件,以及参数,父组件中$ event接收参数。
或者,如果这个事件处理函数是一个方法:

<blog-post
  ...
  v-on:enlarge-text="onEnlargeText"
></blog-post>

那么这个值将会作为第一个参数传入这个方法:

methods: {
  onEnlargeText: function (enlargeAmount) {
    this.postFontSize += enlargeAmount
  }
}

在组件上使用v-model(*)

自定义事件也可以用于创建支持 v-model 的自定义输入组件。

<input v-model="searchText">

等价于:

<input
  v-bind:value="searchText"
  v-on:input="searchText = $event.target.value"
>

动态组件

有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:
在这里插入图片描述
在这里插入图片描述
上述内容可以通过 Vue 的 元素加一个特殊的 is attribute 来实现:

<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component v-bind:is="currentTabComponent"></component>

在上述示例中,currentTabComponent 可以包括:
已注册组件的名字,或 一个组件的选项对象
动态组件深层应用以后再说,下边是实现tab切换页面的源码,有注释!

<!DOCTYPE html>
<html>
<head>
  <title>Dynamic Components Example</title>
  <script src="https://unpkg.com/vue"></script>
  <style>
    .tab-button {
      padding: 6px 10px;
      border-top-left-radius: 3px;
      border-top-right-radius: 3px;
      border: 1px solid #ccc;
      cursor: pointer;
      /*cursor:pointer 当鼠标悬空在该元素时会显示一个点击的小手*/
      background: #f0f0f0;
      margin-bottom: -1px;
      margin-right: -1px;
    }
    .tab-button:hover {
      background: #e0e0e0;
    }
    .tab-button.active {
      background: red;
    /*  当选中时,按钮的背景色*/
    }
    .tab {
      border: 1px solid #268fd5;
      /*边框的颜色*/
      padding: 10px;
    }
  </style>
</head>
<body>
<div id="dynamic-component-demo" class="demo">
  <button
          v-for="tab in tabs"
          v-bind:key="tab"
          v-bind:class="['tab-button', { active: currentTab === tab }]"
          v-on:click="currentTab = tab"
  >
<!--对tabs遍历,仅当遍历到currentTab时,active为1,active是激活属性,
激活后的渲染为.tab-button.active,在原来基础上.activev-on绑定currentTab为当前点击的元素的tab-->
    {{ tab }}
<!--这里取出元素的tab,显示在按钮上-->
  </button>
<!--至此完成上方的按钮切换-->
  <component v-bind:is="currentTabComponent" class="tab"></component>
<!--用is动态绑定currentTabComponent方法-->
</div>

<script>
  Vue.component("tab-home", {
    template: "<div>Home component</div>"
  });
  Vue.component("tab-posts", {
    template: "<div>Posts component</div>"
  });
  Vue.component("tab-archive", {
    template: "<div>Archive component</div>"
  });
  Vue.component("tab-add", {
    template: "<div>add</div>"
  });
  //定义选项子组件
  new Vue({
    el: "#dynamic-component-demo",
    data: {
      currentTab: "Home",
      tabs: ["Home", "Posts", "Archive","add"]
    },
    computed: {
      currentTabComponent: function() {
        return "tab-" + this.currentTab.toLowerCase();
      }
    }
  });
//  定义父组件,值得注意的是计算属性中,定义了currentTabComponent方法,用于切换动态组件,实际是
//  返回了要切换的组件名,tab-+currentTab,.toLowerCase()是转化为小写的意思
</script>
</body>
</html>

组件注册

W3C 规范中的自定义组件名 (字母全小写且必须包含一个连字符)。这会帮助你避免和当前以及未来的 HTML 元素相冲突。
例如:

Vue.component('my-component-name', { /* ... */ })

全局注册与局部注册

用 Vue.component 来创建组件都是全局注册的,也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。
局部注册的方法:
通过一个普通的 JavaScript 对象来定义组件:

var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }

然后在 components 选项中定义你想要使用的组件:

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

对于 components 对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。
局部注册的组件在其子组件中不可用。
在模块系统中局部注册:

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

这样 ComponentA 和 ComponentC 都可以在 ComponentB 的模板中使用。
总结:1、import组件 2、export的components中声明组件

prop

prop的大小写(*)

HTML 中的 attribute 名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

Vue.component('blog-post', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

prop的类型

通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:

props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

这不仅为你的组件提供了文档,还会在它们遇到错误的类型时从浏览器的 JavaScript 控制台提示用户。

传递静态或动态 Prop

<blog-post title="My journey with Vue"></blog-post>

<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>

<!-- 动态赋予一个复杂表达式的值 -->
<blog-post
  v-bind:title="post.title + ' by ' + post.author.name"
></blog-post>

element-ui+vue快速创建组件

以创建跑马灯图片横幅为例:
新建pmd.Vue文件,在网上找到组件设置的代码粘贴进去:

<template>
  <el-carousel :interval="4000" type="card" height="100px">
    <el-carousel-item v-for="item in imagebox" :key="item.id">
      <img :src="item.idView" class="image">
    </el-carousel-item>
  </el-carousel>
</template>

<script>
export default{
  name: 'pmd',
  data () {
    return {
      imagebox: [{id: 0, idView: require('../../assets/img/band1.png')},
      {id: 1, idView: require('../../assets/img/band2.png')},
      {id: 2, idView: require('../../assets/img/band3.png')},
      {id: 3, idView: require('../../assets/img/band4.png')},
      {id: 4, idView: require('../../assets/img/band5.png')},
      {id: 5, idView: require('../../assets/img/band6.png')}
        // imagebox是assets下一个放图片的文件夹
      ]
    }
  }
}
</script>

<style>
.el-carousel__item h3 {
  color: #475669;
  font-size: 14px;
  opacity: 0.75;
  line-height: 200px;
  margin: 20px 1px 1px 1px;
}

.el-carousel__item:nth-child(2n) {
  background-color: #99a9bf;
  margin: 5px 1px 1px 1px;
}

.el-carousel__item:nth-child(2n+1) {
  background-color: #d3dce6;
  margin: 5px 1px 1px 1px;
}

.image{
/*设置图片宽度和浏览器宽度一致*/
  width:100%;
  height:inherit;
}
</style>


我想要把这个跑马灯组件用到我的登录界面login.Vue,因此在login. vue的JS中导入这个pmd.vue:
在这里插入图片描述在这里插入图片描述
然后就可以在login的视图层html中直接用这个组件:
在这里插入图片描述效果;
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值