Vue整理--感谢穗儿同学

Vue

1. Vue 定义

官网:https://cn.vuejs.org/

Vue (类似于 view) 是一套用于构建用户界面的渐进式框架,Vue 被设计为可以自底向上逐层应用,Vue是mvvm模式的。

2. Vue API

2.1 全局配置

2.2 全局API

2.2.1 filter 过滤器

​ Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示。

<div id="app">
    <ul>
      <li
        v-for="goods in goodsList"
        :key="goods.id"
      >
        <!-- 在双花括号中 -->
        <h5>{{goods.title | toUpper}}</h5>
        <!-- 竖线之后写过滤器的名字,就会把前面值传递给过滤器,
              过滤器处理之后再作为插值表达式来显示 -->
        <p>{{goods.price | toFix}}</p>
      </li>
    </ul>
  </div>

补充:
<!-- 在 `v-bind` 中  待理解-->
<li v-bind:id="rawId | formatId"></li>  
  const app = new Vue({
      el: '#app',
      data: {
        goodsList: [
          {
            id: 1,
            title: '罗技鼠标',
            price: 9.9999999999
          }
        ]
      },
      filters: {
        // 定义一个过滤器,接收要过滤的值,返回过滤的结果
        toFix (val) {
          return val.toFixed(2)
        },
        toUpper (val) {
          return val.toUpperCase()
        }
      }
    })

过滤器是 JavaScript 函数,因此可以接收参数。如:

{{ message | filterA('arg1', arg2) }}

filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,
普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。

2.3 选项 / 数据

2.3.1 data

类型:Object | Function

data => Vue实例中的数据对象,对象必须是纯粹的对象 (含有零个或多个的 key/value 对)。

​ 注意:data 属性使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。

组件的data必须是一个方法。因为每个组件的数据是独立的,使用方法可以保证不共享,在data里return一个对象。复用组件时,data必须是个函数(function)

var data = { a: 1 }

// 直接创建一个实例
var vm = new Vue({
  data: data
})
vm.a // => 1
vm.$data === data 

2.3.2 methods 事件处理器

类型:{ [key: string]: Function }

methods是用来写方法的地方,可以在其他地方调用这里的方法。methods 将被写到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。

​ 注意:不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

var vm = new Vue({
  data: { a: 1 },
  methods: {
    plus: function () {
      this.a++
    }
  }
})
vm.plus() // 调用方法
vm.a // 输出 2  

2.3.3 computed 计算属性

类型:{ [key: string]: Function | { get: Function, set: Function } }

​ 计算属性将被写到 Vue 实例中。所有 get 和 set 的 this 上下文自动地绑定为 Vue 实例。计算属性每次的计算结果都会被缓存,除非依赖的响应式属性变化才会重新计算。注意,如果某个依赖 (比如非响应式属性) 在该实例范畴之外,则计算属性是不会被更新的。

​ 注意:一个计算属性如果使用了箭头函数,则 this 不会指向这个组件的实例,不过你仍然可以将其实例作为函数的第一个参数来访问。

var vm = new Vue({
  data: { a: 1 },
  computed: {
    // 仅读取  
    aDouble: function () {
      return this.a * 2
    },
    // 读取和设置
    aPlus: {
      get: function () {
        return this.a + 1
      },
      set: function (v) {
        this.a = v - 1
      }
    }
  }
})
vm.aPlus   // => 2
vm.aPlus = 3  // 设置计算结果为 3
vm.a         // 所以输出a => 2
vm.aDouble // => 4

2.3.4 watch 侦听属性

类型:{ [key: string]: string | Function | Object | Array }

watch 用于监听(观察)某个属性是否发生改变,发生改变就做出相应的操作来响应这个数据变化。一个对象,键是需要观察的表达式,值是对应回调函数、方法名或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性是否发生数据变化,并做出响应。

​ 注意:不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.updateAutocomplete 将是 undefined。

<div id="app">
    <label>姓:<input type="text" v-model="xing"></label><br>
    <label>名:<input type="text" v-model="ming"></label><br>
    <label>姓名:<input type="text" v-model="name"></label><br>
</div>   
 const app = new Vue({
      el: '#app',
      data: {
        xing: '',
        ming: '',
        name:''
      },
      watch: {
        // 监听值的修改
        // 只要值发生了改变,都会执行这个方法
        xing (nVal, oVal) {
          console.log(nVal, oVal)
          // 姓发生了改变
          this.name = nVal + this.ming
        },
        ming (nVal) {
          this.name = this.xing + nVal
        },
        name (nVal) {
          this.xing = nVal.slice(0, 1)
          this.ming = nVal.slice(1)
        }
      }
    })

2.3.5 props

类型:Array<string> | Object

​ props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。

基于对象的语法使用以下选项:

  • type: 校验数据类型。可以是下列原生构造函数中的一种:StringNumberBooleanArrayObjectDateFunctionSymbol、任何自定义构造函数、或上述内容组成的数组。
  • default: any prop 指定一个默认值。
  • required: Boolean定义该 prop 是否是必填项。
  • validator: Function自定义验证函数会将该 prop 的值作为唯一的参数代入。
props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

​ props 使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

<!-- 在 html 中是 post-title 的 -->

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

​ props 是单向数据流, 所有的prop都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。所以props可用于组件传参(父 => 子)。

2.4 特殊特性

2.4.1 ref

ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs对象上。如果用在子组件上,引用就指向组件实例。

在普通的 DOM 元素上使用,引用指向的就是 DOM 元素。

<div id="app">
    
    <input type="text" ref="add" value="hello">
</div>
new Vue ({
    el: '#app',
    data: {
        
    },
    methods: {
        // 取到input这个DOM元素
        this.$refs.add.focus()
    }
})

3. Vue的基础使用

3.1 引入Vue

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

或者保存 Vue.js 到本地,本地调用也可

3.2 声明式渲染

​ 声明式渲染 就是利用插值表达式进行数据渲染

Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统:

<div id="app">
  {{ message }}    <!-- 插值表达式 -->
</div>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
输出:Hello Vue!

注意:此时数据和 DOM 已经被建立了关联,所有东西都是响应式的。如:修改 app.message 的值,输出信息也会更改。

3.3 Vue 实现双向绑定的原理(插值表达式的原理):

​ Vue的双向绑定是基于defineProperty实现的。流程如下:

注意:defineProperty是用来给一个对象定义属性的。基于defineProperty的get/set实现

3.4 模板语法

​ Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML ,所以能被遵循规范的浏览器和 HTML 解析器解析。

​ 在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应系统,Vue 能够智能地计算出最少需要重新渲染多少组件,并把 DOM 操作次数减到最少。

3.4.1 插值表达式

数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
<div id="app">
  {{ message }}   ——插值表达式
</div>

注:插值表达式里面代表是javascript表达式。所以字符要加引号
  <div id="app">
    {{isHandsome}} <br>
    {{1+1}} <br>
    {{isHandsome && 'you are right'}} <br>  
    {{1 + 1 !== 2 ? '你算对了' : '回去读小班'}} <br>
  </div>

3.4.2 template标签

template标签,HTML5提供的新标签,更加规范和语义化 ;可以把列表项放入template标签中,然后进行批量渲染

<template id="tem">
        <div id="app">
            <h1 id="title">hello world!</h1>
        </div>
</template>

打开网页,会发现在浏览器并没有渲染出任何信息,这是因为template标签内容天生不可见,设置了display:none;属性。

var tem =document.getElementById("tem");//获取template标签
var title = tem.content.getElementById("title"); //在template标签内部内容,必须要用.content属性才可以访问到
console.log(title); //找到h1

template标签中的 元素是被当做一个不可见的包裹元素,主要用于分组的条件判断和列表渲染。

3.5 Vue 指令

​ Vue.js的指令 (Directives) 是带有 v- 前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。

3.5.1 v-text

v-text 更新元素文本内容, 让元素的文本内容显示属性的内容。

<div id="app">
    <!-- 插值表达式不会解析html字符串,当作普通文本在渲染 -->
    {{str}}     输出结果:<b>hello</b>
    
    <!-- 指令,指令里面是javascript 所以str要加引号-->
    <p v-text="str"></p>    输出结果:<b>hello</b>
    
    <!-- 由于v-text里面必须放javascript,所以会把内容当然属性或者方法去读取,因此下面这行代码会报错 -->
   <p v-text="你真帅"></p>   输出结果:报错

    <!-- 当指令和插值表达式同时存在的时候指令生效 -->
    <p v-text="'你真帅'">{{str}}</p>     输出结果:你真帅 
</div>
const app = new Vue({
      el: '#app',
      data: {
        str: '<b>hello</b>'
      }
    })

3.5.2 v-html

​ 双大括号会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,你需要使用 v-html 指令

<div id="app">
    <!-- v-html能解析html字符串 -->
    <p v-html="str"></p>    输出结果:加粗的 hello 
</div>

3.5.3 条件渲染 v-if(else) / v-show

v-if(else)

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 true 值的时候被渲染。

v-if 是通过是否渲染来决定显示隐藏。v-show 是一定会渲染,通过display样式来决定是否显示隐藏。

<div id="app">
    <div v-if="false">这是一个弹框1</div>         false  不渲染显示
    <div v-if="1 + 1 === 2">这是一个弹框2</div>   true   渲染显示
</div>

​ 也可以用 v-else 添加一个“else 块”,v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面,否则它将不会被识别。v-else指令与v-if或者v-show同时使用,v-if条件不成立则会显示v-else内容。

<div id="app">
        <!-- false, 渲染else;true,渲染if -->
    <div v-if="isModelShow">这是一个弹框4</div>  
        <!-- v-else只作用与上一个兄弟元素的v-if -->
    <div v-else>这事跟弹框4相反的一个显示</div>
</div>
const app = new Vue({
      el: '#app',
      data: {
        isModelShow: false
      }
    })

v-show

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

<div id="app">
    <div v-show="true">这是一个弹框5</div>           显示
    <div v-show="1 + 1 === 3">这是一个弹框6</div>    隐藏
</div>

注意:v-show 不支持 <template> 元素,也不支持 v-else

v-ifv-show的区别:

v-show不管条件是否成立,都会渲染html,而v-if只有条件成立才会渲染。

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

​ 非常频繁地切换显示隐藏,则使用 v-show 较好;反之,则使用 v-if 较好。

3.5.4 v-for 列表渲染 (循环)

v-for 指令根据一组数组的选项列表进行渲染。v-for 指令需要使用 item in items 形式的特殊语法,items 是源数据数组并且 item 是数组元素迭代的别名。

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
输出结果: ·Foo
         ·Bar

v-for可以循环字符串 、数字、对象、数组等。

v-for 也可以循环数字。在这种情况下,它将重复多次模板。

<div id="app">
  <span v-for="n in 10">{{ n }} </span>
</div>

例:v-for循环对象,可以遍历 索引、键名、属性。

<div id="app">
    <!-- 给每一个循环动态绑定一个唯一的key,这个key一般是一条数据的id,或者name,title之类的唯一标识     -->
    <div v-for="(value, key, index) in person" v-bind:key="obj.id">
      {{ index }}.{{ key }}: {{ value }}
    </div>

    <!-- 循环data数据,得到索引和元素值,循环渲染当前标签 -->
     <div v-for="(value, key) in person" :key="like.id">
          {{ key }}: {{ value }}
     </div>
</div>
new Vue({
  el: '#app',
  data: {
    person: {
          name: '小明',
          age: 20,
          gender: '男'
        }
  }
})

3.5.5 v-model 表单输入绑定

v-model 指令用于表单 <input><textarea><select> 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。

v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;

  • checkbox 和 radio 使用 checked 属性和 change 事件;

  • select 字段将 value 作为 prop 并将 change 作为事件。

    1. input文本应用
    <!-- 输入框的v-model指令负责绑定value -->
    <!-- 双向绑定,用户在input里输入值会自动绑定到data上 -->
    <div id="app">
        <input type="text" v-model="username">
           {{username}}
    </div>
    
        const app = new Vue({
          el: '#app',
          data: {
            username: 'zhangsan'
          }
        })
    
    2.多行文本 textarea
    <span>Multiline message is:</span>
    <p style="white-space: pre-line;">{{ message }}</p>
    <br>
    <textarea v-model="message" placeholder="add multiple lines"></textarea>
    
    3.复选框 checkbox
    单个复选框,绑定到布尔值  true 选中,false 未选中
    <!-- v-model使用在checkbox上的时候代表多选框的选中状态,绑定的是checked属性 -->
    <input type="checkbox" id="checkbox" v-model="checked">
    <label for="checkbox">{{ checked }}</label>
    
    多个复选框,绑定到同一个数组:
    <!-- v-model可以使用一个数组来绑定多选按钮,选中的value值就会存在这个数组里 -->
    <div id='example-3'>
      <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
      <label for="jack">Jack</label>
      <input type="checkbox" id="john" value="John" v-model="checkedNames">
      <label for="john">John</label>
      <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
      <label for="mike">Mike</label>
      <br>
      <span>Checked names: {{ checkedNames }}</span>
    </div>
    
    
    new Vue({
      el: '#example-3',
      data: {
        checkedNames: []
      }
    })
    
    4.单选按钮 radio
    <div id="example-4">
      <input type="radio" id="one" value="One" v-model="picked">
      <label for="one">One</label>
      <br>
      <input type="radio" id="two" value="Two" v-model="picked">
      <label for="two">Two</label>
      <br>
      <span>Picked: {{ picked }}</span>
    </div>
    
    new Vue({
      el: '#example-4',
      data: {
        picked: ''
      }
    })
    
    5.下拉菜单 select
    <!-- v-model用在select上,绑定的就是选中的那个option的value -->
    <div id="example-5">
      <select v-model="selected">
        <option disabled value="">请选择</option>
        <option>A</option>
        <option>B</option>
        <option>C</option>
      </select>
      <span>Selected: {{ selected }}</span>
    </div>
    
    new Vue({
      el: '...',
      data: {
        selected: ''
      }
    })
    

3.5.6 v-clock

v-clock指令可以解决使用插值表达式页面闪烁问题(解决页面卡顿给用户带来不好体验)。

方法:将该指令加在html标签中时,可以在该文件中加style属性为display:none。

[v-cloak] {
    display: none;
  }
<!--  加上v-cloak这个指令以后,在vue实例化之前div身上就会有v-cloak这个属性,实例化之后这个属性就会被移除
-->
<div id="app" v-cloak>
    {{msg}}
</div>
 const app = new Vue({
      el: '#app',
      data: {
        msg: 'hello world'
      }
    })

3.5.7 v-bind 属性绑定

v-bind 用于动态地绑定一个或多个特性,v-bind用来绑定一个属性,属性值作为JavaScript去执行。

可以在其名称后面带一个参数,中间放一个冒号隔开,这个参数通常是HTML元素的特性(attribute),如v-bind: class 、v-bind: src 等。 注意:class可以和v-bind:class同时存在,叠加渲染。

1. 绑定属性 (如:href、src)

<div id="app">
    <a v-bind:href = "baiduLink">百度一下</a>
    <!-- v-bind 简写为 : -->
    <a :href = "baiduLink">百度一下</a>  
    
    <img :src="imgUrl" alt="">
</div>
const app = new Vue({
      el: '#app',
      data: {
        baiduLink:'https://www.baidu.com/',
        imgUrl:'https://ss3.baidu.com/-rVXeDTa2gU2pMbgoY3K/it/u=1488861817,1113726833&fm=202'

      }
    })

2. 绑定 style - Class

.box {
    width: 200px;
    height: 200px;
    background: red;
    margin-bottom: 10px;
  }
  .ac {
    background: green;
  }
<div id="app">
    <!-- 绑定class名称 -->
    <div :class="'box'"></div>
    <div :class="className"></div>

    <!-- 可以绑定一个对象,通过isAc的boolean来决定ac是否存在 -->
    <div :class="{ac: isAc}"></div>
    <!-- 普通class和绑定class可以共存,最后把两部分叠加渲染 -->
    <div class="box" :class="{ac: isAc}"></div>
    <!-- 这个是最复杂的用法,绑定一个数组,数组元素可以直接是class名称就能直接绑定,如果另外一个class根         据数据决定,那就再写一个对象 -->
    <div :class="[className, {ac: isAc}]"></div>

    <p :style="style">Lorem ipsum dolor</p>

</div>
const app = new Vue({
      el: '#app',
      data: {
        className: 'box',
        isAc: false,
        style: {
          width: '100px',
          height: '100px',
          color: 'red'
        }
      }
    })

3.5.8 v-on 事件监听

v-on 指令用于监听 DOM 事件,并在触发时运行一些 JavaScript 代码。例如给button添加点击事件

<button v-on:click="show">
    <!-- v-on:click指令可以简写为@click,修改代码:-->
<button @click="show">

​ 许多事件处理逻辑会更为复杂,所以直接把 JavaScript 代码写在 v-on 指令中是不可行的。因此 v-on 还可以接收一个需要调用的方法名称。

<div id="app">
    <button v-on:click="onNumDecrease">decrease</button>
    {{num}}
    <button @click="onNumAdd(12, $event)">Add</button>
</div>
    const app = new Vue({
      el: '#app',
      data: {
        num: 1
      },
      methods: {
        // 定义当前vue实例的方法
        onNumDecrease (e) {
          // 事件处理函数在v-on监听的时候不写括号,函数的第一个参数就是事件对象
          console.log(e)
          // this.$data.num
          this.num--
        },
        onNumAdd (n, e) {
          // 事件处理函数在v-on监听的时候写了括号,那就可以传参,而且传一个特殊变量$event就是事件对象
          console.log(n)
          console.log(e)
        }
      }
    })

3.5.9 事件修饰符

​ 事件处理程序中经常需要处理默认事件等,为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。事件修饰符监听事件的同时就阻止默认事件等。

​ 常用事件修饰符有:.stop 、 .prevent 、 .capture 、.self 、.once 、.passive 。

.stop 防止事件冒泡

​ 冒泡事件:嵌套两三层父子关系,然后所有都有点击事件,点击子节点,就会触发从内至外 子节点 => 父节点的点击事件。

<!-- 阻止单击事件继续传播 阻止冒泡 -->
<a v-on:click.stop="doThis"></a>

.prevent 阻止默认事件

.prevent等同于JavaScript的event.preventDefault(),用于取消默认事件。

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

 <!-- 点击右键不再出现菜单 阻止右键菜单-->
<p @contextmenu.prevent="onCM">Lorem ipsum dolor</p>

.capture 捕获事件

捕获事件:嵌套两三层父子关系,然后所有都有点击事件,点击子节点,就会触发从外至内 父节点 => 子节点的点击事件

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

.self 触发事件

.self只会触发自己范围内的事件,不会包含子元素。

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
注意:
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,
用 v-on:click.prevent.self 会阻止所有的点击,
而 v-on:click.self.prevent 只会阻止对元素自身的点击。

.once 只执行一次点击

如果我们在@click事件上添加.once修饰符,只要点击按钮只会执行一次。

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

.passive 新增

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

这个 .passive 修饰符能够提升移动端的性能。

注意:不要把 .passive.prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。

3.5.10 v-on:keyup 按键修饰符

在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符。可以使用按键名,也可使用按键码。

常用按键名:enter 、tab 、delete 、esc 、space 、left 、up 、right 、down

常用按键码:13(enter ) 、 37-40(左上右下)

<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<!-- 按下 enter,提交表单 -->
<input @keyup.enter="submit">
<!-- 按下 enter,提交表单 -->
<input v-on:keyup.13="submit">

4. Vue 组件

4.1 组件概念

​ 组件是可复用的 Vue 实例,且带有一个名字。我们可以在一个通过 new Vue 创建的 Vue 根实例(父组件)中,把这个组件作为自定义元素来使用。

data 方法

一个组件的 data 选项必须是一个函数,函数体内 return一个对象。因为组件里的数据是独立的,使用方法可以保证不共享,其他地方不能调用这个data的数据。

data () {
    return {
        
    }
}

4.2 组件注册

为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册局部注册

4.2.1 全局注册

​ 用 Vue.component 来创建的组件,叫做全局组件,全局组件可以在全局内使用。全局组件可以用在任何新创建的 Vue 根实例 (new Vue) 的模板(包括组件树中的所有子组件的模板)中。

创建方法:

// Vue.component 的第一个参数是组件名,第二个参数是一个对象
Vue.component('my-component-name', {
   // ... 选项 ...
})

// 组件名可以驼峰命名,也可以中线连接(在HTML中使用组件时驼峰命名需变为 my-component-name)
Vue.component('MyComponentName', { /* ... */ })

范例:

<div id="app">
    <hello-world></hello-world>
</div>

<script>
// 注册全局组件
Vue.component('HelloWorld', {
    template: '<div>{{msg}}</div>',
    // 组件的data必须是一个方法,因为每个组件的数据是独立的,使用方法可以保证不共享,return一个对象
    data () {
        return {
            msg: 'hello'
        }
    }
})

const app = new Vue({
    el: '#app',
    data: {
        msg1: 'hello'
    }
})
</script>

4.2.2 局部注册

​ 局部注册组件就只能在当前实例里面使用,局部注册的组件在其他子组件中不可用。

创建方法:

// 局部注册组件
var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
      // 局部注册的组件在其他子组件中不可用
    'component-a': ComponentA
  },
  // ...
}

// new 一个Vue实例
new Vue({
  el: '#app',
  components: {
      // 在父组件中使用子组件 A B
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})

范例:

<div id="app">
    <!-- 这里打印出 hello world -->
    <hello-world></hello-world>
    <!-- 下面这个地方不能解析,因为这个组件是app1局部的 -->
    <hello-vue></hello-vue>
</div>
<div id="app1">
    <!-- 这里打印出 hello world -->
    <hello-world></hello-world>
    <!-- 这里打印出 hello vue -->
    <hello-vue></hello-vue>
</div>

<script>
    Vue.component('HelloWorld', {
      template: '<div>hello world</div>'
    })
    // 定义一个组件
    const HelloVue = {
      template: '<h2>hello Vue</h2>'
    }
    const app = new Vue({
      el: '#app',
      data: {
        
      }
    })
    const app1 = new Vue({
      el: '#app1',
      components: {
        // 这里用到解构赋值
        HelloVue
      }
    })
</script>

注意:注册的组件大驼峰命名,在html里面使用这个组件的时候需要把大写变为小写,然后使用中线连接

​ 每个template里面一般都有一个div进行外层包裹

4.3 组件传递数据

4.3.1 通过 Prop 向子组件传递数据(父 => 子)

props

​ 处理数据传输,使用这个组件的时候把父组件定义的msg传递给组件内部,在组件内部通过props来接收, 组件内部的props可以直接作为data使用

当以绑定的方式传递,这个时候会把app实例中的data的数据传递过去。

注意:每个子组件都必须在父组件里进行注册

示例:

<div id="app">
    <!-- 打印出 hello component -->
    <hello-world msg="hello component"></hello-world>
    <!-- 打印出 hello -->
    <hello-world :msg="msg1"></hello-world>
</div>

<script>
    const HelloWorld = {
      template: '<div>{{msg}}</div>',
      props: ['msg']
    }
    const app = new Vue({
      el: '#app',
      data: {
        msg1: 'hello'
      },
      components: {
        HelloWorld
      }
    })
  </script>

如果要传递数字boolean需要绑定,否则传递过去后会解析成字符串。​

props配置属性时,可以校验数据类型,是否必须传 required: true;或者default:0 默认值。

示例:

    <!-- 传递数字或者boolean需要绑定,否则传递过去以后解析成字符串 -->
    <hello-world msg="hello component" :num="3"></hello-world>

    <!-- props中配置属性 -->
      props: {
        msg: {
          type: String,
          required: true // 这里代表必传
        },
        num: {
          type: Number,
          default: 0 // 这里代表不传时,默认为0
        },
        isCompleted: Boolean
      }
    }

props绑定驼峰方式要改写成中线;html属性名用中线连接js使用驼峰

示例:

<div id="app">
    <!-- html属性名用中线 -->
    <hello-world :user-name="userName"></hello-world>
  </div>

  <script>
    const HelloWorld = {
      template: '<div>{{userName}}</div>',
      // 这里的javascript使用驼峰
      props: ['userName']
    }
    const app = new Vue({
      el: '#app',
      data: {
        userName: 'xiaoming'
      },
      components: {
        HelloWorld
      }
    })
  </script>

4.3.2 通过$emit 向父组件传递数据(子 => 父)

$emit

emit 监听子组件事件

​ 由于props单向的数据流,只能单向响应父组件向子组件的数据传递。不能响应子组件向父组件的数据传递。

​ 所以如果需要子组件向父组件进行数据响应,就得使用$emit这个自定义属性,利用这个自定义属性发出一个emit事件,事件命名要用中线连接而不用驼峰。

然后在父组件中监听子组件的自定义事件,当子组件emit这个事件的时候,父组件的这个方法就会响应执行;

父组件中能响应子组件对数据的修改,并且函数方法的第一个参数就是自定义事件传递过来的参数。

注意:子组件的props一般不允许修改。

示例:

<div id="app">
                        <!-- 监听 num-add 这个事件 父组件响应事件的方法onParentAddNum-->
    <hello-world :num="num" @num-add="onParentAddNum"></hello-world>
    {{num}}
</div>

<script>
    const HelloWorld = {
      template: '<div>{{num}}<button @click="onAddNum">Add</button></div>',
      props: ['num'],
      methods: {
        onAddNum () {
            // 自定义emit事件 事件名 num-add
          this.$emit('num-add', {
            num: this.num + 1
          })
        }
      } 
    }
    const app = new Vue({
      el: '#app',
      data: {
        num: 1
      },
      methods: {
          // 监听事件 响应方法
        onParentAddNum (data) {
          this.num = data.num
        }
      },
      components: {
        HelloWorld
      }
    })
</script>

4.3.3 通过 bus 向兄弟组件传递数据(兄 => 弟)

event bus 事件总线

事件总线就是利用空的Vue实例来进行两个兄弟组件之间的数据传输

方法

  1. 创建一个空的Vue实例 const bus = new Vue();

  2. 在第一个兄弟组件中使用bus触发一个emit自定义事件 bus.$emit;

  3. 在第二个兄弟组件中使用$bus.on进行监听(这个方法写在created(){}中);

    解释:created() 这里的代码是Vue实例创建之后执行的

  4. 在点击的时候就会触发这个事件,第二个兄弟组件就能响应这个事件;

  5. 第二个兄弟组件一上来就需要监听事件总线的事件,响应第一个兄弟组件里通过bus给它的emit的这个事件,就可以接收到传递给它的参数data

示例:

<div id="app">
    <ge></ge>
    <di></di>
</div>

<script>
    const bus = new Vue()
    const ge = {
      template: '<div><span @click="onDawodi">打我弟</span></div>',
      methods: {
        onDawodi () {
            // 
          bus.$emit('wuwuwu', {
            kusheng: 'yinyinyin'
          })
        }
      }
    }
    const di = {
      template: '<div><span>{{ku}}</span></div>',
      data () {
        return {
          ku: ''
        }
      },
      created () {
           // 初始化 就监听 bus 事件总线
          // 触发 bus.$emit 的点击事件 onDawodi,马上响应 wuwuwu 这个自定义事件
         // 接收传递过来的data,把data的数据赋给当前data方法中的对象
        // 数据传输成功
        bus.$on('wuwuwu', (data) => {
          this.ku = data.kusheng
        })
      }
    }
    const app = new Vue({
      el: '#app',
      components: {
        ge,
        di
      }
    })
</script>

4.4 template 标签

​ 可以在html中写一个template标签,并给这个标签起一个id,在创建组件的时候使用template关联这个id;在这个标签里就放DOM元素。

在DOM元素div(id = "app")标签中写入组件标签,在组件的标签中对父组件的数据进行绑定。

示例:

<!-- html 'app' 中使用组件 product -->
<div id="app">
    <product
      :price="price"
      :title="title"
      :src="src"
    ></product>
</div>

<!-- 创建组件 product 的template -->
<template id="product">
    <div>
      <div class="img">
        <img :src="src" alt="">
      </div>
      <p>{{title}}</p>
      <p>¥<span>{{price}}</span></p>
    </div>
</template>

<script>
    // 定义一个子组件Product
    const Product = {
      template: '#product',
      props: {
        src: String,
        title: String,
        price: Number
      }
    }
    // Vue实例
    const app = new Vue({
      el: '#app',
      data: {
        title: '这是最贵的小姐姐',
        price: 99,
        src: 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2151614847,425289411&fm=111&gp=0.jpg'
      },
        // 在实例中使用子组件 Product
      components: {
        Product
      }
    })
</script>

4.5 动态组件

动态组件就是在不同组件之间进行动态切换,这在实际项目中是常见的。

实现方法

可以通过 Vue 的 <component> 元素加一个特殊的 is 特性来实现,这个is特性可以绑定父组件中data数据,然后通过点击时改变这个数据来达到动态切换

示例:

 <template id="guonei">
    <div>华为手机要升级了</div>
  </template>

  <template id="guoji">
    <div>apple 倒闭了</div>
  </template>

  <div id="app">
    <div>
      <span @click="news = 'guonei'">国内新闻</span>
      <span @click="news = 'guoji'">国际新闻</span>
    </div>
    <!-- 这里直接使用component标签和它的is特性 -->
    <component :is="news"></component>
  </div>

  <script>
    const guonei= {
      template: '#guonei'
    }
    const guoji= {
      template: '#guoji'
    }
    const app = new Vue({
      el: '#app',
      data: {
        news: 'guoji'
      },
      components: {
        guonei,
        guoji
      }
    })
  </script>

4.6 slot 插槽

slot(插槽)放在template标签中占位置,在使用组件的时候,去决定是否需要某个DOM元素,需要的话就写在组件内部(如下图<hello>标签),在template标签里面的slot标签就会被这个dom元素自动替换。

当写多个slot时,需要写上name属性,使用时添加属性 slot="slot标签的name值"就可以对应起来了。

示例:

<template id="hello">
    <div>
      <slot name="a"></slot>
      商品名称
      <slot name="b"></slot>
    </div>
</template>

<div id="app">
    <hello>
        <!-- slot 的值一一对应 slot标签的name值 -->
        <b slot="b">包邮</b>
        <b slot="a">打九折</b>
    </hello>
</div>

<script>
    const Hello = {
        template: '#hello'
    }
    const app = new Vue({
        el: '#app',
        components: {
            Hello
        }
    })
</script>

4.7 过渡 & 动画

transtion

4.7.1 概述

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。

包括以下工具:

  • 在 CSS 过渡和动画中自动应用 class
  • 可以配合使用第三方 CSS 动画库,如 Animate.css
  • 在过渡钩子函数中使用 JavaScript 直接操作 DOM
  • 可以配合使用第三方 JavaScript 动画库,如 Velocity.js

4.7.2 方法

4.7.2.3 animate.css 动画库

参考网站:https://daneden.github.io/animate.css/?

这个是常用的外部工具,里面有很多动画效果,简单易用。使用时只需将文件引入,然后结合自定义过渡类名将属性名改为此工具中对应的属性名,就可以达到相应的动画效果了。

4.7.2.4 过渡模式

当同时生效的进入和离开的过渡不能满足所有要求,这时 Vue 提供了 过渡模式

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

transition过渡只能作用于一个元素;多个元素需要过渡时(列表过渡),需要使用 transition-group 组件,其他的和一般过渡一致。

4.8 Vue 生命周期(lifecycle )

​ 所有的生命周期钩子自动绑定 this 上下文到实例中,因此可以直接访问数据,对属性和方法进行运算。

注意不能使用箭头函数来定义一个生命周期方法 (例如 created: () => this.fetchTodos())。

图示:[图片上传失败...(image-ebc822-1566829451674)]

生命周期总体来说有四段

  1. create创建vue实例

    ​ beforecreate:创建之前

    ​ created:创建之后

  2. mount 将挂载

    mount 解析实例中的数据,通过插值表达式把数据渲染到DOM元素上(挂载到DOM元素上)。

    ​ beforemount:挂载之前

    ​ mounted:挂载之后

  3. update更新

    在updated处,根据虚拟DOM更新真实DOM(做数据更新)

    ​ beforeupdate:创建之前

    ​ updated:创建之后

  4. destroy销毁

    ​ beforedestroy:创建之前

    ​ destroyed:创建之后

注意ajax 数据请求一般在 created或者 beforemount去发送。

4.9 虚拟DOM

​ 基于真实dom,构建的一个虚拟的dom,数据操作时是对虚拟dom进行操作。它有一个脏检查,用来检查旧的数据和新的数据有什么区别,然后对旧的数据进行更新。

原理

  1. 根据真实DOM构建一个虚拟DOM(js对象)。

  2. 当真实DOM要修改的时候;会先修改虚拟DOM;然后用修改之后的虚拟DOM跟真实的DOM进行脏检查(比对)。

  3. 把修改过后的那一部分重新渲染到真实DOM上。

    注意:脏检查运用的算法 — diff算法

好处:虚拟DOM可以避免多次的DOM操作,提高性能。(频繁请求DOM影响性能)

查询 super() 用法

5. router 路由

网址 :https://router.vuejs.org/zh/installation.html

5.1 使用router的步骤

1. 写一个 router.js 文件 引入需要配置的组件,再使用router ;

​ 得到一个router的实例,并导出 ;通过routers字段配置路由;

​ 跳转的路由路径,给组件起个名字,对应的组件

// 定义路由,导出路由
// 1. 引入核心模块(包)
import Vue from 'vue'
import Router from 'vue-router'

// 2. 引入需要配置的组件
import Home from './views/Home.vue'
import About from './views/about.vue'

// 3. 使用 router (类似于中间件)
Vue.use(Router)
// 4. new 一个router实例,并导出
export default new Router({
// 5. 通过routes字段配置路由
  routes: [
    {
      path: '/',       // 跳转的路由路径
      name: 'home',    // 给组件取个名字
      component: Home  // 这个路由对应的组件
    },
    {
      path: '/about',
      name: 'about',
      component: About
    }
  ]
})

2. main.js 引入router.js

​ 实例化vue的时候把router配置进来,我们#app这个实例中就可以使用配置好的router了。

import Vue from 'vue'
import App from './App.vue'
// 引入了 router.js
import router from './router'

Vue.config.productionTip = false

new Vue({
    // 实例化Vue 的时候吧router配置进来,我们#app这个实例里就能使用配置好的 router了
  router,
  render: h => h(App)
}).$mount('#app')

3. App.vue

​ <router-link to="组件名"/>会渲染成a标签,to就相当于href,就是要跳转的那个组件的path

​ <router-view/>必须的,组件跳转的显示位置,即点击home这儿就显示home组件

<template>
  <div id="app">
    <div id="nav">
        <!-- router-link默认会渲染成 a标签,to 相当于 href,就是要跳转的那个组件的path -->
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
    </div>
      <!-- router-view 必须写的!组件跳转的显示位置 作用:显示跳转组件的位置-->
       <!-- 点击home这儿就显示home组件。即点击谁就显示相对应的组件 -->
    <router-view/>
  </div>
</template>

嵌套路由

​ 在router.vue文件中配置路由时,当一个组件中还有其他组件视图,就需要进行路由嵌套。在当前配置路由的地方加上children属性,在其中配置子路由。

5.2 router 组件跳转

一级路由

组件之间需要跳转时,就要用到路由来进行解决(利用路由进行跳转)。

二级路由

=> 动态路由 + 传参 ,实现子组件跳转

路由嵌套,

传参方式: 传参方式: 1. to="/list/man" 这是通过path进行路由传参。只能传一个参数

​ 传参方式: 2. :to="{ name: 'category', params: {cateName: 'woman'}}" 这是通过绑定 to 后,给这个对象传组件名称,通过组件名来跳转,params 也是个对象,可以实现传多个参数。start这个参数未在路由内配置,但是能够被接收,这个叫做隐式传参。

​ 3. :to="{ name: 'Category', params: {cateName: 'kid', start: 0}, query: { end: 200 }} query传参,通过 ? 连接参数,显示在url上。

跳转方式: router-link + to ,声明式

​ router.push({ path: '/' } }) 在js中编写, 编程式 (如:返回首页)

router-view 命名视图

重定向

点击该路由,跳转到其他页面(非该路由对应页)去。

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})

重定向也可以是一个命名路由

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})

也可以是一个方法,动态返回重定向目标

const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to => {
      // 方法接收 目标路由 作为参数
      // return 重定向的 字符串路径/路径对象
    }}
  ]
})

5.3 导航守卫

5.3.1 全局配置导航守卫

​ 在每一次导航发生改变(路由跳转)之前,都会进入这个钩子函数,在这个函数里只有调用了 next 才会跳转。一般用于全局的权限验证。

// router.beforeEach
// 注册一个全局导航守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next)=> {
    在进入 to 之前进行登录(权限)验证
    if(to.){
       
       } else {
        
    }
    
})
// router.afterEach
// 注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身
router.afterEach(()=> {
    if(){
       
       } else {
        
    }
    
})

()中有三个参数,分别是:to from next

解释: from表示当前路由路径

​ to表示即将跳转至的路由路劲

​ next()需要跳转时,必须调用

注意beforeEach指路由跳转之前,这时this指向undifend

​ 在每一次导航发生之前都会进入这个钩子函数,在这个函数里只有调用了next才会跳转,一般用于全局 的权限验证(比如登录)。判断当前要跳转的那个组件是否需要权限;再验证是否已登录,没有登录就中断当前导航,进入新的导航;不需要权限验证就直接进入当前导航。

补充:

to和from是将要进入和将要离开的路由对象,路由对象指的是平时通过this.$route获取到的路由对象。

next:Function 这个参数是个函数,且必须调用,否则不能进入路由(页面空白)。

  • next() 进入该路由。

  • next(false): 取消进入路由,url地址重置为from路由地址(也就是将要离开的路由地址)。

  • next 跳转新路由,当前的导航被中断,重新开始一个新的导航。

    我们可以这样跳转:next('path地址')或者next({path:''})或者next({name:''})
    且允许设置诸如 replace: true、name: 'home' 之类的选项
    以及你用在router-link或router.push的对象选项。

5.3.2 路由独享导航守卫

beforeEnter()

参数:to from next (同上)

// 路由配置上直接定义 beforeEnter 守卫
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

5.3.3 组件内的守卫

​ 路由组件内直接定义以下路由导航守卫:

  • beforeRouteEnter
  • beforeRouteUpdate (2.2 新增)
  • beforeRouteLeave

beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。

在下列组件外层套一个组件。因为这个是组件内的守卫。

<!-- 进入之前 -->
<!-- 第一次进入使用 beforeRouteEnter 只是在进入时调用一次  -->
beforeRouteEnter (to, from, next){
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不能获取组件实例 this
    // 因为守卫执行前,组件实例还没有被创建
    
    // 在next 里可以传递一个回调函数,这个回调函数的第一个形参就是this,即VM = this
    next(vm => {
        
    })
}
<!-- 修改更新 之后每次修改更新都用 beforeRouteUpdate -->
beforeRouteUpdate (to, from, next){
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
}
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

5.4 路由懒加载

​ 路由懒加载 => 能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,更加高效。

问题:当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。

解决方法:

​ 结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

1.通过注释语法,可以把添加相同注释语法的组件打包到一个js 文件里。

7. Vuex

网址:https://vuex.vuejs.org/zh/

​ Vuex 的状态存储是响应式的。Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态。全局状态管理Vuex.Store是一个单例

状态自管理应用包含以下几个部分:

  • state,驱动应用的数据源;
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化。

“单向数据流”理念的简单示意:

img

多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态。

7.1 vuex使用

7.1.1 安装

npm install vuex --save
// 引入
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

7.1.2 创建 store

安装 Vuex 之后,让我们来创建一个 store。创建过程直截了当——仅需要提供一个初始 state 对象和一些 mutation

// 模块化环境中
Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

新建一个store目录---创建index.js---编写内容state、getters、actions、mutations、modules

提交一个mutation,这个mutation在store的实例中定义的 state只能在mutation中mutate

只能传一个参数,可以传对象或数组。

通过 store.state 来获取状态对象,以及通过 store.commit 方法触发状态变更:

store.commit('increment')

7.2 核心概念

  • state 数据源

  • mutation 修改数据,提交mutation修改

  • getter state 中派生出一些状态,相当于 store 的计算属性,根据state计算

  • action 类似mutation,mutation中只能写同步;action可以包含任意异步操作,action提交的是mutation,而不是直接变更状态

    参数:context,可以理解为store,通过commit提交mutation context.commit('方法')

    如果需要通过异步去修稿state,那么就需要在action中写异步代码,异步返回的时候再提交mutation,进行数据处理 。

mapState 返回一个对象,可以将其映射到computed中

...mapState([‘count’])

...mapMutations(['count'])


7.3 规则

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

8. 生命周期 !!!

8.1 Vue的生命周期

  • create 创建

  • mount 挂载

  • update 更新

  • destroy 销毁

    在前面加上 before ,表示在 * *之前; 过去时态,表示在 * * 之后

8.2 导航守卫的生命周期

全局守卫:

  1. router.beforeEach 全局前置守卫 进入路由之前
  2. router.beforeResolve 全局解析守卫(2.5.0+) 在beforeRouteEnter调用之后调用
  3. router.afterEach 全局后置钩子 进入路由之后

路由组件内的守卫:

  1. beforeRouteEnter 进入路由前
  2. beforeRouteUpdate (2.2) 路由复用同一个组件时
  3. beforeRouteLeave 离开当前路由时

8.3 keep-alive 生命周期

Vue提供了一个内置组件keep-alive缓存组件内部状态,避免重新渲染文档网址

<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。

keep-alive大多数使用场景就是这种。

    <keep-alive>
      <component :is="view"></component>
    </keep-alive>
    
   // 或者 
    <keep-alive>
        <router-view></router-view>
    </keeo-alive>

生命周期:

在被keep-alive包含的组件/路由中,会在原有的Vue生命周期前提下,多出两个生命周期的钩子:activateddeactivated

  • activated在组件第一次渲染时会被调用,之后在每次缓存组件被激活时调用
  • deactivated:组件被停用(离开路由)时调用

使用了keep-alive就不会调用 beforeDestroy (组件销毁前钩子)和destroyed(组件销毁),因为组件没被销毁,被缓存起来了。

这个钩子可以看作beforeDestroy的替代。

MVVM

1 mvvm模式

​ MVVM是Model-View-ViewModel的简写,即模型-视图-视图模型。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,让我们将视图 UI业务逻辑分开。这些事是 ViewModel自动处理的,viewmodel 可以在取出 Model 的数据同时帮忙处理 View 中需要涉及的业务逻辑。

​ 它有两个方向:一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。

​ 二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。这两个方向都实现的,我们称之为数据的双向绑定

2 MVVM模式的组成部分

  • 模型 (model)

    【模型】指的是后端传递的数据。模型 是指代表真实状态内容的领域模型(面向对象),或指代表内容的数据访问层(以数据为中心)。

  • 视图 (view)

    【视图】指的是所看到的页面。就像在MVCMVP模式中一样,视图是用户在屏幕上看到的结构布局外观(UI)

  • 视图模型 (viewModel)

    【视图模型】mvvm模式的核心,主要处理业务逻辑和获取数据,它是连接view和model的桥梁。视图模型 是暴露公共属性和命令的视图的抽象。MVVM没有MVC模式的控制器,也没有MVP模式的presenter,有的是一个绑定器。在视图模型中,绑定器在视图和数据绑定器之间进行通信

  • 绑定器

    声明性数据和命令绑定隐含在MVVM模式中。在Microsoft解决方案堆中,绑定器是一种名为XAML标记语言。绑定器使开发人员免于被迫编写样板式逻辑来同步视图模型和视图。在微软的堆之外实现时,声明性数据绑定技术的出现是实现该模式的一个关键因素。

http://cn.vuejs.org/images/mvvm.png

ViewModel是Vue.js的核心,它是一个Vue实例。Vue和React都是MVVM模式的框架。

3 MVVM优点

MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点:

1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。

3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xaml代码。

4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

4 MVVM模式的框架

  • Vue
  • React

5 MVC与MVVM区别

  1. MVVM是MVC是改进版,MVVM中ViewModel存在目的在于抽离Controller中展示的业务逻辑,而不是替代Controller。MVVM实现的是业务逻辑组件的重用。
  2. 使用MVC的目的就是将M和V的代码分离。‘MVC是单向通信。也就是View跟Model,必须通过Controller来承上启下。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • vue概述 在官方文档中,有一句话对Vue的定位说的很明确:Vue.js 的核心是一个允许采用简洁的模板语法来声明...
    li4065阅读 7,185评论 0 25
  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 2,198评论 0 6
  • 一:什么是闭包?闭包的用处? (1)闭包就是能够读取其他函数内部变量的函数。在本质上,闭包就 是将函数内部和函数外...
    xuguibin阅读 9,513评论 1 52
  • 一、了解Vue.js 1.1.1 Vue.js是什么? 简单小巧、渐进式、功能强大的技术栈 1.1.2 为什么学习...
    蔡华鹏阅读 3,311评论 0 3
  • Vue知识点的总结 Vue中的指令及其基本语法: 第一步:从官网上下载vue开发版本的js文件 引入js文件 ...
    往前走莫回头_2cd6阅读 1,457评论 0 1