vue幼功知识点,深刻浅析Vue

官网上的说明


一、前言#

每个 Vue 应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:

在之前的前端开发中,为了实现我们的需求,通常采用的方案是通过 JS/Jquery
直接操纵页面的 DOM 元素,得益于 Jquery 对于 DOM
元素优异的操作能力,我们可以很轻易的对获取到的 DOM
元素进行操作。但是,当我们开始在前端项目中使用 Vue 这类的 MVVM
框架之后,对于 DOM
的操作我们就应当完全的交给框架,而我们只需要关注于数据。难道,在 Vue
中就不能手动获取到页面上的 DOM 元素了吗,答案当然是可以手动获取到 DOM
元素的,在 Vue 中我们可以通过使用 ref 实现获取 DOM
元素的功能,当然,这也只是 ref 其中一项的功能。本章,我们就来学习 Vue 中
ref 的相关使用。

<script>
    //      Vue实例
    new Vue({   //创建一个Vue的实例
        el: "#app", //挂载点是id="app"的地方
        data: {     //数据
            ditu:true,
            wangge:false
        },
        methods:{
            toggleWangge:function(){
                this.wangge = !this.wangge;
                if(this.wangge == true){
                    this.wangText = "隐藏网格";
                    this.$refs.abc.style.backgroundColor = "#262C30";
                    this.$refs.abc.style.color = "red";
                    this.$refs.def.style.backgroundColor = "#D91A29";
                    this.$refs.def.style.color = "#fff";
                    this.$refs.ghi.style.backgroundColor = "#D91A29";
                    this.$refs.ghi.style.color = "#fff";
                    this.$refs.jkl.style.backgroundColor = "#D91A29";
                    this.$refs.jkl.style.color = "#fff";
                    this.allText = "显示全部";
                    this.zhongText = "显示中级物资";
                    this.gaoText = "显示高级物资";
                    this.zongwuzi = false;
                    this.zhongjiwuzi = false;
                    this.gaojiwuzi = false;
                }
                if(this.wangge == false){
                    this.$refs.abc.style.backgroundColor = "#D91A29";
                    this.$refs.abc.style.color = "#fff";
                    this.wangText = "显示网格";
                }
            }

仓储地址:

实例生命周期钩子


每个 Vue
实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到
DOM 并在数据变化时更新 DOM
等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

图片 1

vue实例生命周期.png

beforeCreate
在实例初始化之后,数据观测(data observer) 和 event/watcher
事件配置之前被调用。

created
vue幼功知识点,深刻浅析Vue。实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data
observer),属性和方法的运算, watch/event
事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。

beforeMount
在挂载开始之前被调用:相关的 render 函数首次被调用。

mounted
el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root
实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。

beforeUpdate
数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。

updated
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM
的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。

该钩子在服务器端渲染期间不被调用。

beforeDestroy
实例销毁之前调用。在这一步,实例仍然完全可用。

destroyed
Vue 实例销毁后调用。调用后,Vue
实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
该钩子在服务器端渲染期间不被调用。

二、干货合集#

看一下Vue中所有的生命周期怎么用的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue入门之生命周期</title>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
    <p>{{ number }}</p>
    <input type="text" name="btnSetNumber" v-model="number">
</div>
<script>
    var app = new Vue({
        el: '#app',
        data: {
            number: 1
        },
        beforeCreate: function () {
            console.log('beforeCreate 钩子执行...');
            console.log(this.number)
        },
        cteated: function () {
            console.log('cteated 钩子执行...');
            console.log(this.number)
        },
        beforeMount: function () {
            console.log('beforeMount 钩子执行...');
            console.log(this.number)
        },
        mounted: function () {
            console.log('mounted 钩子执行...');
            console.log(this.number);
            console.log("后边的钩子函数都不会执行");
        },
        beforeUpdate: function () {
            console.log('beforeUpdate 钩子执行...');
            console.log(this.number)
        },
        updated: function () {
            console.log('updated 钩子执行...');
            console.log(this.number)
        },
        beforeDestroy: function () {
            console.log('beforeDestroy 钩子执行...');
            console.log(this.number)
        },
        destroyed: function () {
            console.log('destroyed 钩子执行...');
            console.log(this.number)
        },
    });
</script>
</body>
</html>

再看一个综合的实战的例子,可能涉及到ajax和组件,不过先看一下vue的生命周期的例子的用法:

import Axios from 'axios'       // 这是一个轻量级的ajax库,import是es6模块导入的语法。
export default {                // 这是一个vue的模块,后面讲奥。
  name: 'app',
  components: {
  },
  data: function () {
    return {
      list: []
    }
  },
  mounted: function () {          // 挂在完成后的生命周期钩子注册。
    this.$nextTick(function () {  // 等待下一次更新完成后执行业务处理代码。
      Axios.get('/api/menulist', {// 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新
        params: {
        }
      }).then(function (res) {
        this.list = res.data
      }.bind(this))
    })
  }
}

ref 在 Vue 中是用来给元素或是子组件注册引用信息到父组件或是 Vue
实例上,注册后的引用信息都会呈现在父组件/Vue 实例的 $.refs
上,这时,我们就可以通过$.refs获取到引用的 DOM 对象或是子组件信息。

全局 API


例如,我们可以获取到页面上添加了 ref 的 input
输入框的值,对于子组件来说,我们可以直接获取到子组件 data
选项中的数据,或是直接调用子组件的方法。

Vue.nextTick()

下次 DOM
更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的
DOM。

官方文档的这句话的侧重点在最后那半句获取更新后的DOM,获取更新后的DOM言外之意就是什么操作需要用到了更新后的DOM而不能使用之前的DOM或者使用更新前的DOM或出问题,所以就衍生出了这个获取更新后的DOM的Vue方法。所以放在Vue.nextTick()回调函数中的执行的应该是会对DOM进行操作的
js代码

在我们使用 JS/Jquery 直接对 DOM
元素进行操作时,不管是对元素样式的修改还是对页面中的某些布局进行动态调整,这都会造成页面的重新渲染,从而影响我们网站的性能。而在
Vue 中,通过在内存中生成与真实 DOM
与之对应的数据结构,当页面发生变化时,通过新的虚拟 DOM 树与旧的虚拟 DOM
树进行比对,就能很快的找出差异点,从而得出应施加到真实 DOM 上的改动。

什么时候需要用的Vue.nextTick()

1.你在Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中。原因是什么呢,原因是在created()钩子函数执行的时候DOM
其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题

2.在数据变化后要执行的某个操作,而这个操作需要使用随数据改变而改变的DOM结构的时候,这个操作都应该放进Vue.nextTick()的回调函数中。

原因是,Vue是异步执行dom更新的,一旦观察到数据变化,Vue就会开启一个队列,然后把在同一个事件循环
(event loop) 当中观察到数据变化的 watcher
推送进这个队列。如果这个watcher被触发多次,只会被推送到队列一次。这种缓冲行为可以有效的去掉重复数据造成的不必要的计算和DOm操作。而在下一个事件循环时,Vue会清空队列,并进行必要的DOM更新。
当你设置 vm.someData = ‘new value’,DOM
并不会马上更新,而是在异步队列被清除,也就是下一个事件循环开始时执行更新时才会进行必要的DOM更新。如果此时你想要根据更新的
DOM 状态去做某些事情,就会出现问题。。为了在数据变化之后等待 Vue
完成更新 DOM ,可以在数据变化之后立即使用 Vue.nextTick(callback)
。这样回调函数在 DOM 更新完成后就会调用。

2、使用 ref 获取页面 DOM 元素#

vue.directive( id, [definition] )

注册或获取全局指令。
例子:

Vue.directive('my-directive', {
 bind: function(){
 //做绑定的准备工作
 //比如添加事件监听器,或是其他只需要执行一次的复杂操作
 },
 inserted: function(){
 //...
 },
 update: function(){
 //根据获得的新值执行对应的更新
 //对于初始值也会调用一次
 },
 componentUpdated: function(){
 //...
 },
 unbind: function(){
 //做清理操作
 //比如移除bind时绑定的事件监听器
 }
当指令的定义对象中只使用update时,只需直接传入函数即可,如下:
Vue.directive('my-directive', function(){
 //...
})

应用
我们往往自定义指令都是定义到全局,方式如下:
第一步:建立一个全局的命令文件例如:directive/directives.js.
第二步:利用Vue.directive()建立一个全局命令,并将它暴露出来,例如一个focus
让表单自动聚焦.
directives.js

import Vue from 'vue';
Vue.directive('focus',{
 //当绑定元素插入到DOM中
 inserted: function(el){
  el.focus(); //元素聚焦
  el.setAttribute('placeholder','自定义内容');
 }
})
Vue.directive('***',{
 inserted: function(el){
  //....
 }
})
export {Vue}

第三步:在main.js(入口JS文件)中将它引入,可以省略文件后缀.

import directive from './components/global/directives';

这样任何一个Vue文件只要这样v-focus(命令名),就可以很方便的用到了

<el-input v-model="input" placeholder="" v-focus></el-input>

如果指令需要传值或者多个值

<body id="example">
 <div id="demo" v-demo="{color : 'white',text : 'hello!'}"></div>
</body>
<script>
 Vue.directive('demo',function(value){
  console.info(value.color); //white
  console.info(value.text) // hello!
 })
 var demo = new Vue({
  el : '#demo'
 })
</script>

在使用 JS/Jquery 获取页面的 DOM 元素时,我们一般是根据
id、class、标签、属性等其它标识来获取到页面上的 DOM
元素。嗯,可以说,我们很难抛弃 Jquery
的一个重大原因,就是当我们需要获取到页面上的 DOM 元素时,使用 Jquery 的
API 相比于原生的 JS 代码,简单到极致,有木有。

Vue.filter( id, [definition] )

2.0中已经废弃了过滤器,需要我们自定义

<div id="app">
    {{message|uppercase}}
</div>

//过滤器
Vue.filter('uppercase', function(value) {
  if (!value) { return ''}
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

var vm = new Vue({
  el:'#app',
  data: {
    message: 'test'
  }
})

document.getElementById.value => $

Vue.component

组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML
元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js
的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is
特性进行了扩展的原生 HTML 元素。

所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象
(除了一些根级特有的选项) 并提供相同的生命周期钩子
组件在注册之后,便可以作为自定义元素
<my-component></my-component>
在一个实例的模板中使用。注意确保在初始化根实例之前注册组件:

<div id="example">
  <my-component></my-component>
</div>

// 注册

Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})

// 创建根实例

new Vue({
  el: '#example'
})

那么,难道我们在 Vue 中获取 DOM 元素还是采用这样的方式?

基础


  1. 通过使用 v-once
    指令,你也能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上的其它数据绑定:

这个将不会改变: {{ msg }}
  1. 原始HTML
    双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的
    HTML,你需要使用 v-html 指令:

<p>Using mustaches: {{ rawHtml }}</p>
<p>Using v-html directive: </p>  // 会将对应的color属性等解析出来,不再是纯粹的字符串

这个 span 的内容将会被替换成为属性值 rawHtml,直接作为
HTML——会忽略解析属性值中的数据绑定。注意,你不能使用 v-html
来复合局部模板,因为 Vue 不是基于字符串的模板引擎。反之,对于用户界面
(UI),组件更适合作为可重用和可组合的基本单位。

  1. 使用JavaScript表达式
    迄今为止,在我们的模板中,我们一直都只绑定简单的属性键值。但实际上,对于所有的数据绑定,Vue.js
    都提供了完全的 JavaScript 表达式支持。

{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

<div v-bind:id="'list-' + id"></div>

这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript
被解析。有个限制就是,每个绑定都只能包含单个表达式,所以下面的例子都不会生效。

<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}

<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
  1. 指令
    指令 (Directives) 是带有 v- 前缀的特殊属性。指令属性的值预期是单个
    JavaScript 表达式 (v-for
    是例外情况,稍后我们再讨论)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于
    DOM。回顾我们在介绍中看到的例子:

<p v-if="seen">现在你看到我了</p>

这里,v-if 指令将根据表达式 seen 的值的真假来插入/移除 <p> 元素。

答案当然是否定的,这种直接操纵 DOM 元素的方式,与我们使用 Vue
的初衷不符,虽然能达成效果,但是却不提倡,这里我们就可以使用 ref
来获取页面上的 DOM 元素。

参数

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind
指令可以用于响应式地更新 HTML 属性:

<a v-bind:href="url">...</a>

在这里 href 是参数,告知 v-bind 指令将该元素的 href 属性与表达式 url
的值绑定。
另一个例子是 v-on 指令,它用于监听 DOM 事件:

<a v-on:click="doSomething">...</a>

在下面的代码中,我在 input 上添加了一个 ref 属性,之后,我们就可以在 Vue
实例中获取到这个 input 输入框的值。这里,我在 beforeMount、mounted
这两个 Vue 中的生命周期钩子函数以及一个按钮的点击事件中尝试获取到这个
input 输入框的值。

修饰符

修饰符 (Modifiers) 是以半角句号 .
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。例如,.prevent
修饰符告诉 v-on 指令对于触发的事件调用 event.preventDefault():

<form v-on:submit.prevent="onSubmit">...</form>
  1. 缩写

v-bind:
<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

v-on:
<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

它们看起来可能与普通的 HTML 略有不同,但 : 与 @
对于特性名来说都是合法字符,在所有支持 Vue.js
的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的,但随着你更深入地了解它们的作用,你会庆幸拥有它们。

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

div id="app">
    <p>{{ message }}</p>
    <p>{{ reversedMessage }}</p>  // olleH
    <p>{{ reversedMessage2}}</p>  // 579
</div>
<script>
    new Vue({
        el: '#app',
        data: {
            message: 'Hello'
        },
        computed: {
            // 计算属性的 getter
            reversedMessage: function () {
                // `this` 指向 vm 实例
                return this.message.split('').reverse().join('')
            },
            reversedMessage2: function () {
                // `this` 指向 vm 实例
                return 123 + 456
            }
        }
    });
</script>
  1. ###### 计算属性缓存 vs 方法

你可能已经注意到我们可以通过在表达式中调用方法来达到同样的效果:

<p>Reversed message: "{{ reversedMessage() }}"</p>

// 在组件中
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}
  获取元素值 var vm = new Vue({ el: "#app", data: { msg: 'Hello ref' }, beforeMount() { console.log('beforeMount: ' + this.$refs.msgText.value) }, mounted() { console.log('mounted: ' + this.$refs.msgText.value) }, methods: { getElement() { console.log(this.$refs.msgText.value) } } });
结论:

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要
message 还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。

相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。

我们为什么需要缓存?假设我们有一个性能开销比较大的的计算属性
A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于
A 。如果没有缓存,我们将不可避免的多次执行 A 的
getter!如果你不希望有缓存,请用方法来替代。

  1. ###### 计算属性 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
    }
  }
})

watch下边的属性值就是要检测的变量

上面代码是命令式且重复的。将它与计算属性的版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

好得多了,不是吗?

  1. ###### 计算属性的 setter

计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter :

// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

10 绑定 HTML
Class

运行代码,从结果中可以看到,在 beforeMount
这个钩子函数中,我们是无法获取到这个 DOM 元素的值,结合之前学习的 Vue
生命周期的相关知识,当执行到 beforeMount 钩子函数时,Vue
虽然已经将模板编译完成,但是尚未挂载到页面 DOM 元素上,因此我们可以得出
ref 是在页面渲染完成后才被创建的。

对象语法

可以看到,当我们在 input 输入框中添加了 ref 属性后,在当前的 Vue 实例的
$.refs 上就挂载了当前的 input 框对象。

数组语法

3、使用 ref 获取子组件对象#

用在组件上

同使用 ref 获取页面的 DOM
元素相似,当我们需要获取子组件时,只需要将使用到子组件上的地方添加 ref
属性即可。在下面的示例代码中,我添加了一个子组件,当我们点击 Vue
实例上的按钮时,会先调用子组件的方法,然后获取子组件的数据。

对象语法
  获取元素值     获取当前时间  var vm = new Vue({ el: "#app", data: { msg: 'Hello ref' }, mounted() { console.log('mounted: ' + this.$refs.msgText.value) }, methods: { getElement() { console.log('input &#36755;&#20837;&#26694;&#30340;&#20540;&#20026;&#65306;' + this.$refs.msgText.value) this.$refs.childComponent.getLocalData() console.log('&#23376;&#32452;&#20214; input &#36755;&#20837;&#26694;&#30340;&#20540;&#20026;&#65306;' + this.$refs.childComponent.local) } }, components: { 'child': { template: '#child', data() { return { local: '' } }, methods: { getLocalData() { var date = new Date() this.local = date.toLocaleString;
数组语法

可以看到,当我们将 ref 添加到子组件上,我们就可以在 Vue
实例上获取到这个注册的组件引用,同注册的 DOM
元素一样,我们都可以使用添加的 ref 属性值作为 key
获取到注册的对象。此时,我们就可以获取到这个子组件上的 data 选项和
methods 选项。

自动添加前缀

三、总结#

多重值
  1. ### v-if

在 Vue 中,我们使用 v-if 指令实现同样的功能

<h1 v-if="ok">Yes</h1>

也可以用 v-else 添加一个“else 块”:

<h1 v-if="ok">Yes</h1>
<h1 v-else>No</h1>
  1. <template> 元素上使用 v-if
    条件渲染分组
    因为 v-if
    是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个
    <template> 元素当做不可见的包裹元素,并在上面使用
    v-if。最终的渲染结果将不包含 <template> 元素。

<template v-if="ok">
  <h1>Title</h1>
  <p>Paragraph 1</p>
  <p>Paragraph 2</p>
</template>

因为 Vue 采用 Virtual DOM 的做法渲染网页,如果我们直接操作
DOM,很容易产生实际网页跟 Vue 产生的 Virtual DOM
不同步的问题,而通过使用 ref 属性之后,在一些需要获取 DOM
元素的情况下,我们就可以很方便的获取 DOM
元素。当然,当我们决定在项目中使用 Vue,还是需要转变我们的思路,将操作
DOM 转变成操作数据。同样的,通过将 ref
属性添加到子组件上,我们就可以很轻松的获取到子组件的相关信息,这无疑给父组件获取子组件数据、调用子组件的方法提供了一种新的思路。

v-else

你可以使用 v-else 指令来表示 v-if 的“else 块”:

<div v-if="Math.random() > 0.5">
  Now you see me
</div>
<div v-else>
  Now you don't
</div>

v-else 元素必须紧跟在带 v-if 或者 v-else-if
的元素的后面,否则它将不会被识别。

  1. key
    管理可复用的元素
    Vue
    会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使
    Vue
    变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address">
</template>

这样也不总是符合实际需求,所以 Vue
为你提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的
key 属性即可:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

现在,每次切换时,输入框都将被重新渲染。请看:
意,<label> 元素仍然会被高效地复用,因为它们没有添加 key 属性。

以上所述是小编给大家介绍的Vue 中 ref
的使用,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

1. v-show

另一个用于根据条件展示元素的选项是 v-show 指令。用法大致一样:

<h1 v-show="ok">Hello!</h1>

不同的是带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display。

v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。

v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。

相比之下,v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS
进行切换。

一般来说,v-if 有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if 较好。

  1. ###### 一个对象的 v-for

<div v-for="(value, key, index) in object">
  {{ index }}. {{ key }}: {{ value }}
</div>
  1. ###### 使用 Prop 传递数据

父组件传值到子组件:

组件实例的作用域是孤立的。这意味着不能 (也不应该)
在子组件的模板内直接引用父组件的数据。父组件的数据需要通过 prop
才能下发到子组件中。
子组件要显式地用 props
选项声明它预期的数据:

子组件的写法:(props是用来接收数据的)

Vue.component('child', {
  // 声明 props
  props: ['message'],
  // 就像 data 一样,prop 也可以在模板中使用
  // 同样也可以在 vm 实例中通过 this.message 来使用
  template: '{{ message }}'
})

在父组件里边引用子组件的写法

<child message="hello!"></child>

结果:

hello!
  1. ###### 自定义事件

子组件传值到父组件
(https://cn.vuejs.org/v2/guide/components.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6
“自定义事件”)
我们知道,父组件使用 prop
传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue
的自定义事件系统就派得上用场了。

另外,父组件可以在使用子组件的地方直接用 v-on
来监听子组件触发的事件(子组件传值到父组件)
在子组件里边

export default{
    methods: {
          onfilter(){
             this.$emit("listenTochildEvent","browse");
          },
          }
}

在父组件:

<privateScoreTop  :msg ="Widget" v-on:listenTochildEvent="showMessageFromChild"></privateScoreTop>
 showMessageFromChild(data){
        alert(data);
     }

注意:不能用 $on 监听子组件释放的事件,而必须在模板里直接用 v-on 绑定

  1. ###### 给组件绑定原生事件

有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 v-on
的修饰符 .native。例如:

<my-component v-on:click.native="doTheThing"></my-component>
  1. ###### 非父子组件的通信

有时候,非父子关系的两个组件之间也需要通信。在简单的场景下,可以使用一个空的
Vue 实例作为事件总线:

// 根组件(this.$root)
new Vue({
 el: '#app',
 router,
 render: h => h(App),
 data: {
  // 空的实例放到根组件下,所有的子组件都能调用
  Bus: new Vue()
 }
})

// 触发组件 A 中的事件
<button @click="submit">提交<button>

methods: {
  submit() {
   // 事件名字自定义,用不同的名字区别事件
   this.$root.Bus.$emit('eventName', 123)
  }
 }

// 在组件 B 创建的钩子中监听事件
// 当前实例创建完成就监听这个事件
created(){
 this.$root.Bus.$on('eventName', value => {
  this.print(value)
 })
},

methods: {
 print(value) {
  console.log(value)
 }
},

// 在组件销毁时别忘了解除事件绑定
beforeDestroy() {
  this.$root.Bus.$off('eventName')
},
  1. ###### 使用插槽分发内容

<div id="app">
    <h1>我是父组件的标题</h1>
    <childcomponent>
        <!--如果组件里边没有值,slot就会插入进来,如果有,就不会插入,类似placeholder的占位符-->
        <!--<p>我不是slot,而是插值</p>-->
    </childcomponent>
</div>
<script>
    Vue.component('childcomponent',{
        template:'<div><h2>我是子组件的标题</h2><slot><div>我是子组件中slot值</div></slot></div>'
    });
    new Vue({
        el:'#app'
    })
</script>

结论:当没有插值时就解析slot中的代码,有插值时就不解析slot中的代码。最初在
<slot>
标签中的任何内容都被视为备用内容。备用内容在子组件的作用域内编译,并且只有在宿主元素为空,且没有要插入的内容时才显示备用内容。

  1. ###### 单个插槽

  2. ###### 具名插槽

  3. ###### 作用域插槽

  4. #### keep-alive

1.基本用法:vue2.0提供了一个keep-alive组件
用来缓存组件,避免多次加载相应的组件,减少性能消耗

<keep-alive>
<component>
  <!-- 组件将被缓存 -->
</component>
</keep-alive>

有时候 可能需要缓存整个站点的所有页面,而页面一般一进去都要触发请求的
在使用keep-alive的情况下

<keep-alive><router-view></router-view></keep-alive>
将首次触发请求写在created钩子函数中,就能实现缓存,
比如列表页,去了详情页 回来,还是在原来的页面

2.缓存部分页面或者组件
(1)使用router. meta属性

// 这是目前用的比较多的方式
<keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

router设置:

  routes: [
    { path: '/', redirect: '/index',  component: Index, meta: { keepAlive: true }},
    {
      path: '/common',
      component: TestParent,
      children: [
        { path: '/test2', component: Test2, meta: { keepAlive: true } } 
      ]
    }
    // 表示index和test2都使用keep-alive

(2).使用新增属性inlcude/exclude
2.1.0后提供了include/exclude两个属性 可以针对性缓存相应的组件

<!-- comma-delimited string -->
<keep-alive include="a,b">
  <component :is="view"></component>
</keep-alive>
<!-- regex (use v-bind) -->
<keep-alive :include="/a|b/">
  <component :is="view"></component>
</keep-alive>

//其中a,b是组件的name

注意:这种方法都是预先知道组件的名称的
(2)动态判断

<keep-alive :include="includedComponents">
  <router-view></router-view>
</keep-alive>

includedComponents动态设置即可

  1. ###### 自定义指令

除了核心功能默认内置的指令 (v-model 和 v-show),Vue
也允许注册自定义指令。注意,在 Vue2.0
中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通
DOM
元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下:
当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari
上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

如果想注册局部指令,组件中也接受一个 directives 的选项:

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

然后你可以在模板中任何元素上使用新的 v-focus 属性,如下:

<input v-focus>

在Vue 中可以把一系列复杂的操作包装为一个指令。

什么是复杂的操作?
我的理解是:复杂逻辑功能的包装、违背数据驱动的 DOM 操作以及对一些 Hack
手段的掩盖等。我们总是期望以操作数据的形式来实现功能逻辑。

钩子函数

对于自定义指令的定义,Vue2 有 5 个可选的钩子函数。

bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。
componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
unbind: 只调用一次,指令与元素解绑时调用。

template

<div id="app">
 <my-comp v-if="msg" :msg="msg"></my-comp>
 <button @click="update">更新</button>
 <button @click="uninstall">卸载</button>
 <button @click="install">安装</button>
</div>

script(通过输出的结果判断触发的时机)

Vue.directive('hello', {
 bind: function (el) {
  console.log('bind')
 },
 inserted: function (el) {
  console.log('inserted')
 },
 update: function (el) {
  console.log('update')
 },
 componentUpdated: function (el) {
  console.log('componentUpdated')
 },
 unbind: function (el) {
  console.log('unbind')
 }
})
var myComp = {
 template: '<h1 v-hello>{{msg}}</h1>',
// 传递数据
 props: {
  msg: String
 }
}
new Vue({
 el: '#app',
 data: {
  msg: 'Hello'
 },
 components: {
  myComp: myComp
 },
 methods: {
  update: function () {
   this.msg = 'Hi'
  },
  uninstall: function () {
   this.msg = ''
  },
  install: function () {
   this.msg = 'Hello'
  }
 }
})

结论:

页面加载时:bind inserted

点击“更新”按钮,更改数据触发组件更新。
update
componentUpdated

卸载组件时
点击“卸载”按钮,数据置空否定判断以触发组件卸载。
unbind

卸载组件时
点击“卸载”按钮,数据置空否定判断以触发组件卸载。
unbind

重新安装组件时
点击“安装”按钮,数据赋值肯定判断以触发组件重新安装。
bind
inserted

区别:

从案例的运行中,对 5
个钩子函数的触发时机有了初步的认识。存疑的也就是bind和inserted、update和componentUpdated的区别了。
bind 和 inserted:
据文档所说,插入父节点时调用 inserted,来个测试。

bind: function (el) {
 console.log(el.parentNode) // null
 console.log('bind')
},
inserted: function (el) {
 console.log(el.parentNode) // <div id="app">...</div>
 console.log('inserted')
}

分别在两个钩子函数中输出父节点:bind 时父节点为 null,inserted
时父节点存在。

update 和 componentUpdated
关于这两个的介绍,从字眼上看感觉是组件更新周期有关,继续验证。

update: function (el) {
 console.log(el.innerHTML) // Hello
 console.log('update')
},
componentUpdated: function (el) {
 console.log(el.innerHTML) // Hi
 console.log('componentUpdated')
}

没毛病,update 和 componentUpdated 就是组件更新前和更新后的区别。

最佳实践

根据需求的不同,我们要选择恰当的时机去初始化指令、更新指令调用参数以及释放指令存在时的内存占用等。
比较常见的场景是:用指令包装一些无依赖的第三方库以扩展组件功能。而一个健壮的库通常会包含:初始化实例、参数更新和释放实例资源占用等操作。

Vue.directive('hello', {
 bind: function (el, binding) {
  // 在 bind 钩子中初始化库实例
  // 如果需要使用父节点,也可以在 inserted 钩子中执行
  el.__library__ = new Library(el, binding.value)
 },
 update: function (el, binding) {
  // 模版更新意味着指令的参数可能被改变,这里可以对库实例的参数作更新
  // 酌情使用 update 或 componentUpdated 钩子
  el.__library__.setOptions(Object.assign(binding.oldValue, binding.value))
 },
 unbind: function (el) {
  // 释放实例
  el.__library__.destory()
 }
})

这是一个使用了这些属性的自定义钩子样例:

<div id="hook-arguments-example" v-demo:foo.a.b="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: '#hook-arguments-example',
  data: {
    message: 'hello!'
  }
})
函数简写

在很多时候,你可能想在 bind 和 update
时触发相同行为,而不关心其它的钩子。比如这样写:

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 添加全局功能。插件的范围没有限制——一般有下面几种:

  1. 添加全局方法或者属性,如:
    vue-custom-element

  2. 添加全局资源:指令/过滤器/过渡等,如
    vue-touch

  3. 通过全局 mixin 方法添加一些组件选项,如:
    vue-router

  4. 添加 Vue 实例方法,通过把它们添加到 Vue.prototype 上实现。

  5. 一个库,提供自己的 API,同时提供上面提到的一个或多个功能,如
    vue-router

Vue.js 的插件应当有一个公开方法 install 。这个方法的第一个参数是 Vue
构造器,第二个参数是一个可选的选项对象:

MyPlugin.install = function (Vue, options) {
  // 1. 添加全局方法或属性
  Vue.myGlobalMethod = function () {
    // 逻辑...
  }

  // 2. 添加全局资源
  Vue.directive('my-directive', {
    bind (el, binding, vnode, oldVnode) {
      // 逻辑...
    }
    ...
  })

  // 3. 注入组件
  Vue.mixin({
    created: function () {
      // 逻辑...
    }
    ...
  })

  // 4. 添加实例方法
  Vue.prototype.$myMethod = function (methodOptions) {
    // 逻辑...
  }
}
使用插件

通过全局方法 Vue.use() 使用插件:

// 调用 `MyPlugin.install(Vue)`
Vue.use(MyPlugin)

也可以传入一个选项对象:

Vue.use(MyPlugin, { someOption: true })

Vue.use 会自动阻止多次注册相同插件,届时只会注册一次该插件。

Vue.js 官方提供的一些插件 (例如 vue-router) 在检测到 Vue
是可访问的全局变量时会自动调用 Vue.use()。然而在例如 CommonJS
的模块环境中,你应该始终显式地调用 Vue.use():

// 用 Browserify 或 webpack 提供的 CommonJS 模块环境时
var Vue = require('vue')
var VueRouter = require('vue-router')

// 不要忘了调用此方法
Vue.use(VueRouter)

awesome-vue
集合了来自社区贡献的数以千计的插件和库。

发表评论

电子邮件地址不会被公开。 必填项已用*标注