Vue.js 2.0 手把手入门笔记

1 介绍

是一套用于构建用户界面的渐进式框架。与其它大型框架不一样的是,Vue 被设计为能够自底向上逐层应用。Vue 的核心库只关注视图层,不只易于上手,还便于与第三方库或既有项目整合。javascript

2 特色:

  • 核心只关注视图层(view)
  • 灵活、轻量、灵活的特色
  • 适用于移动端项目
  • 渐进式框架

3 什么是库,什么是框架?

  • 库是将代码集合成一个产品,库是咱们调用库中的方法实现本身的功能
  • 框架则是为解决一类问题而开发的产品,框架是咱们在指定的位置编写代码,框架帮咱们调用。

框架是库的升级版css

4 渐进式

  • 声明式渲染(无需关心如何实现)
  • 组件系统
  • 客户端路由(vue-router)
  • 大规模状态管理(vuex)
  • 构建工具(vue-cli)

5 Vue的两个核心点

  1. 响应的数据变化
  2. 当数据发生改变->视图的自动更新
  3. 组合的视图组件
  4. ui页面映射为组件树
  5. 划分组件可维护、可复用、可测试

6 MVC(backbone,react)

  • model 数据
  • view  视图
  • controller 控制器

7 MVVM(angular,vue) 双向

  • model 数据
  • view  视图
  • viewModel视图模型

8 Object.defineProperty(es5)没有替代方案

  • 不支持ie8<=

2 vue基础指令

2.1 安装vue

  • cdn方式
  • npm 方式

2.2 简单的尝试

这里使用cdn方便测试html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>

<body>
    <div id="content">
        <!-- moustache 小胡子语法 表达式 能够放赋值 取值 三元-->
        {{ msg }}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<!-- 使用官网的vue地址 -->
<script> // 引用vue后会给一个vue构造函数 var vm = new Vue({ // vm === viewModel el: '#content', // 告诉vue管理哪一部分,querySelector "document.querySelector("#content")" data: { // data中的数据会被vm所代理 msg: 'Hello Vue!' // 能够经过vm.msg获取对应的呢日用 } })// Object.defineProperty vm.msg = "wjw" // 修改视图 </script>
</html>
复制代码

image.png

2.3 模板语法

综上所属得出了一套模板语法前端

2.3.1 文本

<span>Message:{{msg}}</span>
复制代码

2.3.2 表达式

{{number + 1}}
{{ok?'YES':'NO'}}
{{message.split('').reverse().join('')}}
复制代码

可是vue的表单元素 input checkbox textarea radio select 非文本处理vue

vue的指令 directive 只是dom上的行间属性,vue给这类属性赋予了一些意义,来实现特殊功能全部指令都以v-开头value属性默认状况下回vue忽略掉 selected checked 都没有意义java

2.3.3表单输入

v-model 会将msg赋予输入框,输入框的值改变会影响数据node

<input v-model="msg">
<input type="checkbox" v-model="msg1" value="登山">
复制代码

2.3.4 原始HTML

<p>Using mustache:<span v-html='rawHtml'></spn></p>
复制代码

2.3.5 指令

<p v-if='seen'>如今看到我了</p>
复制代码

2.3.6 特性

<div v-bind:id='dynamicld'></div>
复制代码

2.4 Object.defineProperty原理

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="content"></div>
    <input type="text" id="input">
</body>
  
<script> let obj = {} let temp = {}; document.getElementById("content").innerHTML = obj.name // 'name' 表明属性 Object.defineProperty(obj,'name',{ configurable:false, //是否可删除 // writable:true,// 是否可赋值(若是使用set方法,则不能使用) enumerable:true, // 是否可枚举,也是就for..in.. // value:1,// 值(若是使用get方法,则不能使用) get(){ // 取obj的name会触发get方法 return temp['name'] }, set(val){// 给obj赋值会触发get方法 // console.log(val); temp['name'] = val // 改变temp的结果 input.value = val // 将值赋值给输入框 } }); input.value = obj.name // 页面一加载,会将调用get方法 input.addEventListener('input',function(){ // 等待输入框的变化 obj.name = this.value // 当值变化时会调用set方法 document.getElementById("content").innerHTML = obj.name }) </script>

</html>
复制代码

image.png

最后能够实现双向绑定的雏形react

3 数据响应的变化

vue会循环data中的数据(数据劫持) 依次的增长getter和settergit

let vm = new Vue({
   el:'#content',
   data:{
     a:{}
   }
 })
复制代码

可是这时候我想添加一个school方法,发现没有产生getter和settergithub

1.1 方法一 $set

使用变量时 先要初始化,不然新加的属性不会致使页面刷新

vm.$set(vm.a,"school",'1')// 此方法能够给对象添加响应式的变化
复制代码

1.2 方法二 替换原对象

vm.a = {"school":"heihei",age:8};
复制代码

1.3 数组问题

去改变数组中的某一项监控不到的,也不能改变数组的长度方法

let vm = new Vue({
  el:'#content',
  data:{
    a:[1,2,3,4,5,6]
  }
})
复制代码

错误方法

vm.a[0] =100
vm.a.length -=2 
复制代码

变异方法:pop push shift unshit sort reserve splice

vm.a = vm.a.map(item=>item*3) 
复制代码

4 数组的循环v-for

vue 提供了一个v-for 解决循环问题 更高效 会复用原有结构

4.1 代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="content">
        <!--要循环谁就在谁身上增长v-for属性,相似于for...in..-->
        <!--默认是value of 数组/ (value,index) of 数组-->
        <li v-for="(todo,index) in todos">
 <!-- 会改变原始数组的方法,为变异方法 例如push(),pop()等; 非变异方法,不会改变原始数组,可是会返回一个新数组 -->
            {{ todo.text }} {{index+1}}
        </li>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<!-- 使用官网的vue地址 -->
<script> let vm = new Vue({ el:'#content', data:{ todos: [ { text: '学习 JavaScript' }, { text: '学习 Vue' }, { text: '整个牛项目' } ] } }) </script>
</html>
复制代码

v-for循环数组 当用for来更新已被渲染的元素时,vue的“就地复用”机制 是不会改变数据项的顺序的。要想从新排序,需为每项添加key属性(也就是每项惟一的id)

想要改变

会改变原始数组的方法,为变异方法 例如push(),pop()等;  非变异方法,不会改变原始数组,可是会返回一个新数组

4.2 为何v-for必定要有key

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <div>
            <input type="text" v-model="name">
            <button @click="add">添加</button>
        </div>
        <ul>
            <li v-for="(item, i) in list">
                <input type="checkbox"> {{item.name}}
            </li>
        </ul>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<!-- 使用官网的vue地址 -->
<script> // 建立 Vue 实例,获得 ViewModel var vm = new Vue({ el: '#app', data: { name: '', newId: 3, list: [ { id: 1, name: '蔬菜' }, { id: 2, name: '奶酪' }, { id: 3, name: '肉' } ] }, methods: { add() { //注意这里是unshift this.list.unshift({ id: ++this.newId, name: this.name }) this.name = '' } } }); </script>
  </div>

</html>
复制代码

image.png

当你输入汤时

image.png

就会变成这个样子  =>

image.png

可是当你换成了key

能够简单的这样理解:加了key(必定要具备惟一性) id的checkbox跟内容进行了一个关联。是咱们想达到的效果

vue和react的虚拟DOM的Diff算法大体相同,其核心是基于两个简单的假设
首先讲一下diff算法的处理方法,对操做先后的dom树同一层的节点进行对比,一层一层对比,以下图:

image.png

当某一层有不少相同的节点时,也就是列表节点时,Diff算法的更新过程默认状况下也是遵循以上原则。
好比一下这个状况:
image.png

咱们但愿能够在B和C之间加一个F,Diff算法默认执行起来是这样的:
image.png

即把C更新成F,D更新成C,E更新成D,最后再插入E,是否是很没有效率?
因此咱们须要使用key来给每一个节点作一个惟一标识,Diff算法就能够正确的识别此节点,找到正确的位置区插入新的节点。

image.png

vue中列表循环需加:key="惟一标识" 惟一标识能够是item里面id index等,由于vue组件高度复用增长Key能够标识组件的惟一性,为了更好地区别各个组件 key的做用主要是为了高效的更新虚拟DOM

5 事件

5.1 定义&缩写

事件定义以及缩写

<div id="app">
	<button @click="msg"></button>
	<button @mousedown="add"></button>
  <!--若是不传递参数,则不要写括号会自动传入事件源,若是写括号了,要手动传入$event属性-->
</div>

let vm = new Vue({
		el:"#app",
    methods:{
    	msg(){
        console.log(Math.random());
      }
    }
})
复制代码

methods和data中的数据会所有放在vm上,并且名字不能冲突,冲突会报错,methods中的this指向的都是实例

5.2 mousedown

当鼠标指针移动到元素上方,并按下鼠标按键(左、右键都可)时,会发生 mousedown 事件。
与 click 事件不一样,mousedown 事件仅须要按键被按下,而不须要松开便可发生。

5.3 mouseup

当在元素上松开鼠标按键(左、右键都可)时,会发生 mouseup 事件。
与 click 事件不一样,mouseup 事件仅须要松开按钮。当鼠标指针位于元素上方时,放松鼠标按钮就会触发该事件。

5.4 click

当鼠标指针停留在元素上方,而后按下并松开鼠标左键时,就会发生一次 click 事件。
注意:触发click事件的条件是按下并松开鼠标左键!,按下并松开鼠标右键并不会触发click事件。
三个事件的触发顺序

5.5 总结

若在同一个元素上按下并松开鼠标左键,会依次触发mousedown、mouseup、click,前一个事件执行完毕才会执行下一个事件
若在同一个元素上按下并松开鼠标右键,会依次触发mousedown、mouseup,前一个事件执行完毕才会执行下一个事件,不会触发click事件

6 事件修饰符的使用

1 事件处理

若是须要在内联语句处理器中访问原生DOM事件。可使用特殊变量$event,把它传入到methods中的方法中。
     在Vue中,事件修饰符处理了许多DOM事件的细节,让咱们再也不须要花大量的时间去处理这些烦恼的事情,而能有更多的精力专一于程序的逻辑处理。在Vue中事件修饰符主要有:

  • .stop:等同于JavaScript中的event.stopPropagation(),防止事件冒泡
  • .prevent:等同于JavaScript中的event.preventDefault(),防止执行预设的行为(若是事件可取消,则取消该事件,而不中止事件的进一步传播)
  • .capture:与事件冒泡的方向相反,事件捕获由外到内
  • .self:只会触发本身范围内的事件,不包含子元素
  • .once:只会触发一次

1.1 stop 防止事件冒泡

冒泡事件:嵌套两三层父子关系,而后全部都有点击事件,点击子节点,就会触发从内至外  子节点-》父节点的点击事件

<!-- HTML --> 

<div id="app"> 
 &emsp;<div class="outeer" @click="outer"> 
   &emsp;<div class="middle" @click="middle"> 
     &emsp;<button @click="inner">点击我(^_^)</button>
     </div>
   </div> 
 &emsp;<p>{{ message }}</p> 
</div>

 let app = new Vue({
	 el: '#app', 
   data () { 
   &emsp;return { message: '测试冒泡事件' } 
   }, 
 &emsp;methods: { 
   &emsp;inner: function () {
       this.message = 'inner: 这是最里面的Button' 
   &emsp;}, 
   &emsp;middle: function () { 
     &emsp;this.message = 'middle: 这是中间的Div' 
   &emsp;}, 
   &emsp;outer: function () { 
     &emsp;this.message = 'outer: 这是外面的Div' 
   &emsp;} 
 &emsp;} 
})
复制代码

防止冒泡事件的写法是:在点击上加上.stop至关于在每一个方法中调用了等同于event.stopPropagation(),点击子节点不会捕获到父节点的事件

<!-- HTML --> 

<div id="app"> 

 &emsp;<div class="outeer" @click.stop="outer"> 

   &emsp;<div class="middle" @click.stop="middle"> 

     &emsp;<button @click.stop="inner">点击我(^_^)</button>

     </div>

   </div> 

</div>
复制代码

1.2 prevent取消默认事件

.prevent等同于JavaScript的event.preventDefault(),用于取消默认事件。好比咱们页面的<a href="#">标签,当用户点击时,一般在浏览器的网址列出#

1.3 .capture 捕获事件

捕获事件:嵌套两三层父子关系,而后全部都有点击事件,点击子节点,就会触发从外至内  父节点-》子节点的点击事件

<!-- HTML --> 
<div id="app"> 
 &emsp;<div class="outeer" @click.capture="outer"> 
   &emsp;<div class="middle" @click.capture="middle"> 
     &emsp;<button @click.capture="inner">点击我(^_^)</button>
     </div>
   </div> 
</div>
复制代码


 

1.4 .self

修饰符.self只会触发本身范围内的事件,不会包含子元素。

<!-- HTML --> 
<div id="app"> 
 &emsp;<div class="outeer" @click.self="outer"> 
   &emsp;<div class="middle" @click.self="middle"> 
     &emsp;<button @click.stop="inner">点击我(^_^)</button>
     </div>
   </div> 
</div>
复制代码

1.5 .once 只执行一次点击

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

2 键盘修饰符

在JavaScript事件中除了前面所说的事件,还有键盘事件,也常常须要监测常见的键值。在Vue中容许v-on在监听键盘事件时添加关键修饰符。记住全部的keyCode比较困难,因此Vue为最经常使用的键盘事件提供了别名:

  • .enter:回车键
  • .tab:制表键
  • .delete:含deletebackspace
  • .esc:返回键
  • .space: 空格键
  • .up:向上键
  • .down:向下键
  • .left:向左键
  • .right:向右键

3 鼠标修饰符

鼠标修饰符用来限制处理程序监听特定的滑鼠按键。常见的有:

  • .left:鼠标左键
  • .middle:鼠标中间滚轮
  • .right:鼠标右键

4 修饰键

能够用以下修饰符开启鼠标或键盘事件监听,使在按键按下时发生响应:

  • .ctrl
  • .alt
  • .shift
  • .meta

5 自定义按键修饰符别名

在Vue中能够经过config.keyCodes自定义按键修饰符别名。例如,因为预先定义了keycode 116(即F5)的别名为f5,所以在文字输入框中按下F5,会触发prompt方法,出现alert

<!-- HTML -->

<div id="app">

    <input type="text" v-on:keydown.f5="prompt()">

</div>



Vue.config.keyCodes.f5 = 116;



let app = new Vue({

    el: '#app',

    methods: {

        prompt: function() {

            alert('我是 F5!');

        }

    }

});
复制代码

6 总结

在Vue中,使用v-on来给元素绑定事件,而为了更好的处理逻辑方面的事物,Vue提供了一个methods。在methods中定义一些方法,这些方法能够帮助咱们处理一些逻辑方面的事情。而在这篇文章中,咱们主要介绍了一些事件的修饰符,好比常见的阻止事件冒泡,键盘修饰符等。除此以外,还提供了config.keyCodes提供自定义按键修饰符别名。

7 缩写

7.1 指令缩写

<a v-bind:href='url'></a>
<a :href='url'></a>
<a v-on:click='doSomething'></a>
<a @click='doSomething'></a>
复制代码

7.2 函数缩写

image.png

缩写后

image.png

8 组件化管理

1.组件化开发

咱们能够很直观的将一个复杂的页面分割成若干个独立组件,每一个组件包含组件的逻辑和样式,再将这些独立组件完成一个复杂的页面。这样既减小了逻辑复杂度,又实现了代码的重用。页面是组件的容器,组件自动组合造成完整的界面,当不须要某个组件时,或者想要替换某个组件时,能够随时进行替换和删除,而不影响整个应用的运行。

二、组件化开发的好处

  • 提升开发效率
  • 方便重复使用
  • 便于协同开发
  • 更容易被管理和维护

在vue中例如div、span均可以看作一个组件

三、全局组件

  • 全局组件:能够声明一次在任何地方使用
  • 局部组件:必须告诉这个组件属于谁

通常写插件的时候全局组件使用的多一些

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>

<body>
    <div id="app">
        <my-handsom></my-handsom>
        <my-handsom></my-handsom>
        <my-handsom></my-handsom>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script> Vue.component("my-handsom",{ //一个对象能够当作一个组件 data: function () { return { count: 0 } }, template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>' }) var vm = new Vue({ el: '#app' }) </script>
</html>
复制代码

image.png

  • 组件名不要带大写,多组件使用 -
  • 只要组件和定义相同是能够的(首字母能够大写)
  • html采用短横线隔开命名法js中转驼峰也是能够的

深刻了解组件

props

组件的参数传递

slot

插槽在组件抽象设计中的应用

自定义事件

父子组件的通讯方式

9 全局api- Vue.extend

使用基础 Vue 构造器,建立一个“子类”。参数是一个包含组件选项的对象。

data 选项是特例,须要注意 - 在 Vue.extend() 中它必须是函数

<div id="mount-point"></div>
复制代码
// 建立构造器
var demo = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})

// 建立 Profile 实例,并挂载到一个元素上。
new demo().$mount('#mount-point')
复制代码

10 全局api-nextTick

官方说明

参数

  • {Function} [callback]
  • {Object} [context]

用法

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

// 修改数据
vm.msg = 'Hello'

// DOM 尚未更新
Vue.nextTick(function () {
  // DOM 更新了
})

// 做为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick()
  .then(function () {
    // DOM 更新了
})
复制代码

2.1.0 起新增:若是没有提供回调且在支持 Promise 的环境中,则返回一个 Promise。请注意 Vue 不自带 Promise 的 polyfill,因此若是你的目标浏览器不原生支持 Promise (IE:大家都看我干吗),你得本身提供 polyfill。

示例

先来一个示例了解下关于Vue中的DOM更新以及nextTick的做用。
模板

<div class="app">
  <div ref="msgDiv">{{msg}}</div>
  <div v-if="msg1">Message got outside $nextTick: {{msg1}}</div>
  <div v-if="msg2">Message got inside $nextTick: {{msg2}}</div>
  <div v-if="msg3">Message got outside $nextTick: {{msg3}}</div>
  <button @click="changeMsg">
    Change the Message
  </button>
</div>
复制代码

Vue实例
**

new Vue({
  el: '.app',
  data: {
    msg: 'Hello Vue.',
    msg1: '',
    msg2: '',
    msg3: ''
  },
  methods: {
    changeMsg() {
      this.msg = "Hello world."
      this.msg1 = this.$refs.msgDiv.innerHTML
      this.$nextTick(() => {
        this.msg2 = this.$refs.msgDiv.innerHTML
      })
      this.msg3 = this.$refs.msgDiv.innerHTML
    }
  }
})
复制代码

点击前

image.png

点击后

image.png

从图中能够得知:msg1和msg3显示的内容仍是变换以前的,而msg2显示的内容是变换以后的。其根本缘由是由于Vue中DOM更新是异步的(详细解释在后面)。

应用场景

下面了解下nextTick的主要应用的场景及缘由。

  • 在Vue生命周期的created()钩子函数进行的DOM操做必定要放在Vue.nextTick()的回调函数中

created()钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操做无异于徒劳,因此此处必定要将DOM操做的js代码放进Vue.nextTick()的回调函数中。与之对应的就是mounted()钩子函数,由于该钩子函数执行时全部的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操做都不会有问题 。

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

具体缘由在Vue的官方文档中详细解释:

Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的全部数据改变。若是同一个 watcher 被屡次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免没必要要的计算和 DOM 操做上很是重要。而后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工做。Vue 在内部尝试对异步队列使用原生的 Promise.thenMessageChannel,若是执行环境不支持,会采用 setTimeout(fn, 0)代替。 例如,当你设置vm.someData = 'new value',该组件不会当即从新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数状况咱们不须要关心这个过程,可是若是你想在 DOM 状态更新后作点什么,这就可能会有些棘手。虽然 Vue.js 一般鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,可是有时咱们确实要这么作。为了在数据变化以后等待 Vue 完成更新 DOM ,能够在数据变化以后当即使用Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。

11 全局api-set

官网说明

Vue.set( target, propertyName/index, value )

  • 参数
    • {Object | Array} target
    • {string | number} propertyName/index
    • {any} value
  • 返回值:设置的值。
  • 用法
    向响应式对象中添加一个属性,并确保这个新属性一样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新属性,由于 Vue 没法探测普通的新增属性 (好比 this.myObject.newProperty = 'hi')

注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。

示例

<div id="div">  
	<p >{{items}}</p>
</div>
 
<script>
 
var vm = new Vue({
el:"#div",
  data: {
    items: ['a', 'b', 'c']
  }
});
 
Vue.set(vm.items,2,"ling")
 
</script>
复制代码

1 设置数组元素

Vue.set(vm.items,2,"ling") : 表示 把vm.items  这个数组的下标为2 的元素,改成"ling"
把数组  ["a","b","c"] 修改 后是 ["a","b","ling"] 

image.png

2 向响应式对象添加属性

<div id="div">  
	<p>{{person}}</p>
</div>
 
<script>
var vm = new Vue({
el:"#div",
data: {
   person:{
			name:"ling",
			job:"engineer"
   }
},
created:function(){
		alert(this.person.age)
  }
});
 
Vue.set(vm.person,"age","26")
</script>
复制代码

注意:person 是data 里面的子对象,因此可使用 Vue.set( ) 方法。data 这个根对象就不能使用 set 方法

image.png

image.png

说明:控制台能够在person 里找到age 这个属性,说明添加成功 (响应式)

**

对比非响应式方法

vm.food="chocolate"
alert(vm.food)

image.png

控制台和网页上的 {{person}} 都没有显示food 这个属性,说明food 这个属性没有被添加 (非响应式)


image.png
**

12 全局api-delete

Vue.delete( target, propertyName/index )

  • 参数
    • {Object | Array} target
    • {string | number} propertyName/index

仅在 2.2.0+ 版本中支持 Array + index 用法。

  • 用法
    删除对象的属性。若是对象是响应式的,确保删除能触发更新视图。这个方法主要用于避开 Vue 不能检测到属性被删除的限制,可是你应该不多会使用它。

在 2.2.0+ 中一样支持在数组上工做。

  • 目标对象不能是一个 Vue 实例或 Vue 实例的根数据对象。
data:{
   namelist : {
     id : 1, 
       name : '叶落森'
   }       
}
复制代码
// 删除name
delete this.namelist.name;//js方法
Vue.delete(this.namelist,'name');//vue方法
复制代码

13 全局api-fifer过滤器

8.1 介绍

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

8.2 优点

一、在Vue中使用过滤器(Filters)来渲染数据是一种颇有趣的方式。
二、首先咱们要知道,Vue中的过滤器不能替代Vue中的methodscomputed或者watch
三、过滤器不改变真正的data,而只是改变渲染的结果,并返回过滤后的版本。
四、在不少不一样的状况下,过滤器都是有用的,好比尽量保持API响应的干净,并在前端处理数据的格式。
五、在你但愿避免重复和链接的状况下,它们也能够有效地封装成可重用代码块背后的全部逻辑。

8.3 过滤器例子

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <div>{{ message | capitalize }}</div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { message: 'world' }, filters: { // 能够有好多的自定义过滤器 capitalize(value) { // 这里的this指向的window if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } } }); </script>
</html>
复制代码

8.4 过滤器串连

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        {{ message | filterA | filterB }}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { message: 'world' }, filters: { // 能够有好多的自定义过滤器 filterA(value){ return value.split('').reverse().join(''); }, filterB(value){ return value.charAt(0).toUpperCase() + value.slice(1) } } }); </script>
</html>
复制代码

8.5 过滤器传参

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        {{ message | filterA('hello',hi) }}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { hi:'!', message: 'world' }, filters: { // 能够有好多的自定义过滤器 filterA(value1,value2,value3){ return `${value2} ${value1} ${value3}`; } } }); </script>
</html>
复制代码

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

14 插槽-slot

老版本vue

模板中只能有一个根元素

HTML内容模板(template)元素是一种用于保存客户端内容机制,该内容在加载页面时不会呈现,但随后能够在运行时使用JavaScript实例化。

<div id="app">
   <modal></modal> 
</div>

<template id="modal">
   <div>
     <h1>是否删除</h1>
  </div>
</template>
复制代码
let modal = {
 template:"#modal"
}

const app = new Vue({
 el:'#app',
 components:{
   modal
 },
 data:{
 }
})
复制代码

咱们一般是想把h1的值动态放入,因此就要用到插槽

单个插槽 | 默认插槽 | 匿名插槽

首先是单个插槽,单个插槽是vue的官方叫法,可是其实也能够叫它默认插槽,或者与具名插槽相对,咱们能够叫它匿名插槽。由于它不用设置name属性。 单个插槽能够放置在组件的任意位置,可是就像它的名字同样,一个组件中只能有一个该类插槽。相对应的,具名插槽就能够有不少个,只要名字(name属性)不一样就能够了。

<div id="app">
  <modal>
    <h1>插入成功</h1>
  </modal>
</div>

<template id="modal">
  <div>
    <slot></slot>
  </div>
</template>
复制代码

image.png

当咱们看到插入成功的时候,匿名插入就实现了

具名插槽

匿名插槽没有name属性,因此是匿名插槽,那么,插槽加了name属性,就变成了具名插槽。具名插槽能够在一个组件中出现N次,出如今不一样的位置。下面的例子,就是一个有两个具名插槽单个插槽的组件,这三个插槽被父组件用同一套css样式显示了出来,不一样的是内容上略有区别。

简单的来讲,就是,咱们可能遇到一个问题 咱们想插入不一样的插槽内的内容不同

在 2.6.0+ 中已弃用

<div id="app">
    <modal>
        <h1>插入成功</h1>
        <h2 slot="title">标题</h2>
        <h2 slot="content">内容</h2>
    </modal>
</div>

<template id="modal">
  <div>
    <slot name="default"></slot>
    <slot name="title"></slot>
    <slot name="content"></slot>
  </div>
</template>
复制代码

咱们能够发现没有name的状况下,默认就是default

做用域插槽 | 带数据的插槽

最后,就是咱们的做用域插槽。这个稍微难理解一点。官方叫它做用域插槽,实际上,对比前面两种插槽,咱们能够叫它带数据的插槽。什么意思呢,就是前面两种,都是在组件的template里面写

在 2.6.0+ 中已弃用


```html Vue做用域插槽
```

这种写法,习惯了element-ui的朋友必定就很熟悉了。

总结: 
1 . 使用slot能够在自定义组件内插入原生HTML元素,须要搭配使用name和slot属性,不然多个slot可能会返回重复的HTML元素。
2 . 使用slot-scope能够将slot内部的做用域指向该子组件,不然默认做用域指向调用slot的父组件。

新版本的 v-slot

vue@2.6.x 开始,Vue 为具名和范围插槽引入了一个全新的语法,即咱们今天要讲的主角:v-slot 指令。目的就是想统一 slotscope-slot 语法,使代码更加规范和清晰。既然有新的语法上位,很明显,slotscope-slot 也将会在 vue@3.0.x 中完全的跟咱们说拜拜了。而从 vue@2.6.0 开始,官方推荐咱们使用 v-slot 来替代后二者。


#### 具名插槽 > 实例化一个vue
// 组件
Vue.component('lv-hello', {
  template: ` <div> <slot name="header"></slot> <h1>个人天呀</h1> </div>`
})

new Vue({
  el: '#app1',
  data: {

  }
});
复制代码

老版本

<div id="app1">
  <!-- 老版本使用具名插槽 -->
  <lv-hello>
    <p slot="header">我是头部</p>
  </lv-hello>
</div>
复制代码

新版本的变化

<!-- 新版本使用具名插槽 -->
  <lv-hello>
    <!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 -->
    <template v-slot:header>
      <p>我是头部</p>
    </template>
  </lv-hello>
</div>
复制代码

具名插槽的缩写

v-slot: 替换成 #

<div id="app">
  <lv-hello>
    <template #header>
      <p>我是头部</p>
    </template>
    <!-- 注意: #号后面必须有参数,不然会报错。即使是默认插槽,也须要写成 #default -->
    <template #default>
      <p>我是默认插槽</p>
    </template>
  </lv-hello>
</div>
复制代码

做用域插槽

所谓做用域插槽,就是让插槽的内容可以访问子组件中才有的数据。

Vue.component('lv-hello', {
  data: function () {
    return {
      firstName: '张',
      lastName: '三'
    }
  },

  template: ` <div> <slot name="header" :firstName="firstName" :lastName="lastName"></slot> <h1>个人天呀</h1> </div> `
})
复制代码
<div id="app">
  <!-- 老版本使用具名插槽 -->
  <lv-hello>
  	<p slot="header" slot-scope="hh">我是头部 {{ hh.firstName }} {{ hh.lastName }}</p>
	</lv-hello>
<!-- 新版本使用具名插槽 -->
    <lv-hello>
      <!-- 注意:这块的 v-slot 指令只能写在 template 标签上面,而不能放置到 p 标签上 -->
      <template v-slot:header="hh">
         <p>我是头部 {{ hh.firstName }} {{ hh.lastName }}</p>
      </template>
  	</lv-hello>
</div>
复制代码

15 动态绑定样式-v-bind

13.1 对象语法

:class 绑定的样式和class绑定的不冲突

13.1.1 直接绑定一个data

<div v-bind:class="{ active: isActive }"></div>
复制代码

active 这个 class 存在与否将取决于数据属性 isActive 的 布尔值

<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
复制代码

13.1.2 data中使用一个对象绑定

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}
复制代码

13.1.3 计算属性中绑定

data: {
  isActive: true,
  error: null
},
computed: {
  classObject: function () {
    return {
      active: this.isActive && !this.error,
      'text-danger': this.error && this.error.type === 'fatal'
    }
  }
}
复制代码

13.2 数组语法

<div v-bind:class="[activeClass, errorClass]"></div>
复制代码

13.2.1 直接动态绑定一个class

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}
复制代码

13.2.2 三元表达式

<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
复制代码

不过,当有多个条件 class 时这样写有些繁琐。因此在数组语法中也可使用对象语法:

<div v-bind:class="[{ active: isActive }, errorClass]"></div>
复制代码

16 数据-计算属性(computed)

1 什么是计算属性

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

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <div id="example">
           {{ message.split('').reverse().join('') }}
       </div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { message: 'Hello' } }); </script>
  </div>

</html>
复制代码

image.png

这里的表达式包含3个操做,并非很清晰,因此遇到复杂逻辑时应该使用Vue特带的计算属性computed来进行处理。

2 计算属性的用法

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <div id="example">
           {{getMessage}}
       </div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { message: 'Hello' }, computed: { // 放在computed中最后也会放在vm上,不能和methods与data重名 getMessage() { return this.message.split('').reverse().join('') } } }); </script>
  </div>

</html>
复制代码

3 计算属性使用技巧

计算属性能够依赖其余计算属性
计算属性不只能够依赖当前Vue 实例的数据,还能够依赖其余实例的数据

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app1"></div>
    <div id="app2">
         {{getMessage}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app1', data: { message: 'World' } }); var vm2 = new Vue({ el: '#app2', data: { message: 'Hello' }, computed: { getMessage() { return `${this.message} ${vm.message}` } } }); </script>
  </div>
</html>
复制代码

4 getter和setter

每个计算属性都包含一个getter 和一个setter ,咱们上面的两个示例都是计算属性的默认用法, 只是利用了getter 来读取。
在你须要时,也能够提供一个setter 函数, 当手动修改计算属性的值就像修改一个普通数据那样时,就会触发setter 函数,执行一些自定义的操做

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="getMessage"> <--模拟修改--!>
        {{getMessage}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm= new Vue({ el: '#app', data: { hi:'Hello', message: 'World' }, computed:{ getMessage:{ //get,set方法 // getter get(){ return this.hi + ' ' + this.message }, // setter set(newValue){ console.log('===================================='); console.log(newValue); console.log('===================================='); var names = newValue.split(' '); this.hi = names[0]; this.message = names[names.length - 1]; } } } }); </script>
</html>
复制代码


绝大多数状况下,咱们只会用默认的getter 方法来读取一个计算属性,在业务中不多用到setter,因此在声明一个计算属性时,能够直接使用默认的写法,没必要将getter 和setter 都声明。

5 质疑什么不直接用methods

咱们能够将同一函数定义为一个方法而不是一个计算属性,两种方式的最终结果确实是彻底相同的。只是一个使用getMessage()取值,一个使用getMessage取值。
然而,不一样的是计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会从新求值。
这就意味着只要 hi尚未发生改变,屡次访问 getMessage计算属性会当即返回以前的计算结果,而没必要再次执行函数。

<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div>{{getMessage}}</div> <div> {{getMessage1()}}</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm= new Vue({ el: '#app', data: { hi:'Hello', message: 'World' }, computed:{ getMessage(){ //get,set方法 return this.hi + ' ' + this.message //而使用计算属性,只要title没变,页面渲染是不会从新进这里来计算的,而是使用了缓存。 } }, methods:{ getMessage1(){ return this.hi + ' ' + this.message //进这个方法,再次计算。不是刷新,而是只要页面渲染,就会进方法里从新计算。 } } }); </script> </html> 复制代码

17 数据-观察(watch)

一个对象,键是须要观察的表达式,值是对应回调函数。值也能够是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每个属性。

为何必定要有watch,不用能够吗?咱们已经有了computed,能不能不去使用?

1 watch的出现

作一个实验

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="a">
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ a:"1" }, computed: { a(){ setTimeout(() => { this.a=1; }, 500); } } }) </script>
</html>
复制代码

不难发如今_异步的状况下就很差使用了_

2 代码实现

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <input type="text" v-model="a">
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script>
    var vm = new Vue({
        el:'#app',
        data:{
            a:""
        },
        watch: { // 只有值变化的时候才会触发 支持异步了,其余状况咱们更善于使用
            a(newVal,oldVal){ // watch的属性名字要和观察的人的名字一致
                console.log(newVal);
                console.log(oldVal);
            }
        },
    })
</script>
</html>
复制代码

3 computed与watch的区别

Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变更:侦听属性。当你有一些数据须要随着其它数据变更而变更时,你很容易滥用 watch

<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> {{ fullName }} </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> </html> 复制代码
var vm = new Vue({
    el: '#app',
        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
    }
  }
})
复制代码

是否是感受优雅不少

4 侦听器

虽然计算属性在大多数状况下更合适,但有时也须要一个自定义的侦听器。这就是为何 Vue 经过 watch 选项提供了一个更通用的方法,来响应数据的变化。当须要在数据变化时执行异步或开销较大的操做时,这个方式是最有用的。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <input type="text" v-model="something">
       {{somethingShow}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { something: '', somethingShow:'' }, watch: { something(val){ this.somethingShow = "loading" this.getSomething() } }, methods:{ getSomething(){ setTimeout(() => { this.somethingShow = "hello" }, 1000);// 咱们使用延迟模拟一个网络请求 } } }) </script>
</html>
复制代码

5 vm.$watch

vm.$watch( expOrFn, callback, [options] )

观察 Vue 实例变化的一个表达式或计算属性函数。回调函数获得的参数为新值和旧值。表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
       <input type="text" v-model="something">
       {{somethingShow}}
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el: '#app', data: { something: '', somethingShow:'' } }) vm.$watch('something',(newVal,oldVal)=>{// watch的属性名要和观察的人名字一致 vm.somethingShow = "loading" console.log('===================================='); console.log(newVal); console.log('===================================='); vm.somethingShow = newVal }) </script>
</html>
复制代码

18 数据-属性(props)

组件接受的选项之一 props 是 Vue 中很是重要的一个选项。父子组件的关系能够总结为: props down, events up 父组件经过 props 向下传递数据给子组件;子组件经过 events 给父组件发送消息。

父子级组件

好比咱们须要建立两个组件 parent 和 child。须要保证每一个组件能够在相对隔离的环境中书写,这样也能提升组件的可维护性。
这里咱们先定义父子两个组件和一个 Vue 对象

var childNode = {
  template: `
        <div>childNode</div>
        `
};
var parentNode = {
  template: `
        <div>
          <child></child>
          <child></child>
        </div>
        `,
  components: {
    child: childNode
  }
};
new Vue({
  el: "#example",
  components: {
    parent: parentNode
  }
});
复制代码
<div id="example">
  <parent></parent>
</div>
复制代码

这里的 childNode 定义的 template 是一个 div,而且内容是"childNode"字符串。 而在 parentNode 的 template 中定义了 div 的 class 名叫 parent 而且包含了两个 child 组件。

静态 props

组件实例的做用域是孤立的。这意味着不能(也不该该)在子组件的模板中直接引用父组件的数据。要让子组件使用父组件的数据,须要经过子组件的 props 选项。 父组件向子组件传递数据分为两种方式:动态和静态,这里先介绍静态方式。 子组件要显示的用 props 声明它指望得到的数据 修改上例中的代码,给 childNode 添加一个 props 选项和须要的forChildMsg数据; 而后在父组件中的占位符添加特性的方式来传递数据。

var childNode = {
  template: ` <div> {{forChildMsg}} </div> `,
  props: ["for-child-msg"] // 直接把参数做为数组放进去
};
var parentNode = {
  template: ` <div> <p>parentNode</p> <child for-child-msg="aaa"></child> <child for-child-msg="bbb"></child> </div> `,
  components: {
    child: childNode
  }
};
复制代码

命名规范
**

对于 props 声明的属性,在父组件的 template 模板中,属性名须要使用中划线写法; 子组件 props 属性声明时,使用小驼峰或者中划线写法均可以;而子组件的模板使用从父组件传来的变量时,须要使用对应的小驼峰写法。别担忧,Vue 可以正确识别出小驼峰和下划线命名法混用的变量,如这里的forChildMsgfor-child-msg是同一值。

动态props

原则上很简单,for-child-msg做为一个变量

var parentNode = {
  template: ` <div> <p>parentNode</p> <child :for-child-msg="childMsg1"></child> <child :for-child-msg="childMsg2"></child> </div> `,
  components: {
    child: childNode
  },
  data: function() {
    return {
      childMsg1: "child-1",
      childMsg2: "child-2"
    };
  }
};
复制代码

在父组件的 data 的 return 数据中的 childMsg1 和 childMsg2 会被传入子组件中

props 验证

验证传入的 props 参数的数据规格,若是不符合数据规格,Vue 会发出警告。

能判断的全部种类(也就是 type 值)有: String, Number, Boolean, Function, Object, Array, Symbol

Vue.component("example", {
  props: {
    // 基础类型检测, null意味着任何类型都行
    propA: Number,
    // 多种类型
    propB: [String, Number],
    // 必传且是String
    propC: {
      type: String,
      required: true
    },
    // 数字有默认值
    propD: {
      type: Number,
      default: 101
    },
    // 数组、默认值是一个工厂函数返回对象
    propE: {
      type: Object,
      default: function() {
        console.log("propE default invoked.");
        return { message: "I am from propE." };
      }
    },
    // 自定义验证函数
    propF: {
      isValid: function(value) {
        return value > 100;
      }
    }
  }
});

let childNode = {
  template: "<div>{{forChildMsg}}</div>",
  props: {
    "for-child-msg": Number
  }
};

let parentNode = {
  template: ` <div class="parent"> <child :for-child-msg="msg"></child> </div>`,
  components: {
    child: childNode
  },
  data() {
    return {
      // 当这里是字符串 "123456"时会报错
      msg: 123456
    };
  }
};
复制代码

还能够在 props 定义的数据中加入自定义验证函数,当函数返回 false 时,输出警告。 好比咱们把上述例子中的 childNode 的for-child-msg修改为一个对象,并包含一个名叫validator的函数,该命名是规定叫validator的,自定义函数名不会生效

let childNode = {
  template: "<div>{{forChildMsg}}</div>",
  props: {
    "for-child-msg": {
      validator: function(value) {
        return value > 100;
      }
    }
  }
};
复制代码

在这里咱们给for-child-msg变量设置了validator函数,而且要求传入的值必须大于 100,不然报出警告。

单向数据流

props 是单向绑定的:当父组件的属性变化时,将传导给子组件,可是不会反过来。这是为了防止子组件五一修改父组件的状态。
因此不该该在子组件中修改 props 中的值,Vue 会报出警告。

let childNode = {
  template: `<div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  }
};

let parentNode = {
  template: ` <div class="parent"> <div> <span>父组件数据</span> <input v-model="msg"/> </div> <p>{{msg}}</p> <child :for-child-msg="msg"></child> </div>`,
  components: {
    child: childNode
  },
  data() {
    return {
      msg: "default string."
    };
  }
};
复制代码

传递的过程将短横分割命名,转成驼峰命名法便可

这里咱们给父组件和子组件都有一个输入框,而且显示出父组件数据和子组件的数据。当咱们在父组件的输入框输入新数据时,同步的子组件数据也被修改了;这就是 props 的向子组件传递数据。而当咱们修改子组件的输入框时,浏览器的控制台则报出错误警告

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "forChildMsg"

修改 props 数据

一般有两种缘由:

  1. prop 做为初始值传入后,子组件想把它当作局部数据来用

  2. prop 做为初始值传入后,由子组件处理成其余数据输出

  3. 定义一个局部变量,并用 prop 的值初始化它

可是因为定义的 ownChildMsg 只能接受 forChildMsg 的初始值,当父组件要传递的值变化发生时,ownChildMsg 没法收到更新。

let childNode = {
  template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  },
  data() {
    return { ownChildMsg: this.forChildMsg };
  }
};
复制代码

这里咱们加了一个

用于查看 ownChildMsg 数据是否变化,结果发现只有默认值传递给了 ownChildMsg,父组件改变只会变化到 forChildMsg,不会修改 ownChildMsg。


  1. 定义一个计算属性,处理 prop 的值并返回

因为是计算属性,因此只能显示值,不能设置值。咱们这里设置的是一旦从父组件修改了 forChildMsg 数据,咱们就把 forChildMsg 加上一个字符串"---ownChildMsg",而后显示在屏幕上。这时是能够每当父组件修改了新数据,都会更新 ownChildMsg 数据的。

let childNode = {
  template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  },
  computed: {
    ownChildMsg() {
      return this.forChildMsg + "---ownChildMsg";
    }
  }
};
复制代码
  1. 更加妥帖的方式是使用变量存储 prop 的初始值,并用 watch 来观察 prop 值得变化。发生变化时,更新变量的值。
let childNode = {
  template: ` <div class="child"> <div> <span>子组件数据</span> <input v-model="forChildMsg"/> </div> <p>{{forChildMsg}}</p> <p>ownChildMsg : {{ownChildMsg}}</p> </div>`,
  props: {
    "for-child-msg": String
  },
  data() {
    return {
      ownChildMsg: this.forChildMsg
    };
  },
  watch: {
    forChildMsg() {
      this.ownChildMsg = this.forChildMsg;
    }
  }
};
复制代码

19 生命周期

1 vue生命周期简介

image.png

image.png

2 生命周期探究

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">{{message}}</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var app = new Vue({ el: '#app', data: { message: "hello is world" }, beforeCreate() { console.group('beforeCreate 建立前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //undefined  console.log("%c%s", "color:red", "message: " + this.message) }, created() { console.group('created 建立完毕状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //undefined console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化  console.log("%c%s", "color:red", "message: " + this.message); //已被初始化 }, beforeMount() { console.group('beforeMount 挂载前状态===============》'); console.log("%c%s", "color:red", "el : " + (this.$el)); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化  console.log("%c%s", "color:red", "message: " + this.message); //已被初始化  }, mounted() { console.group('mounted 挂载结束状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); //已被初始化 console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); //已被初始化 console.log("%c%s", "color:red", "message: " + this.message); //已被初始化  }, beforeUpdate() { console.group('beforeUpdate 更新前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, updated() { console.group('updated 更新完成状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, beforeDestroy() { console.group('beforeDestroy 销毁前状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message); }, destroyed() { console.group('destroyed 销毁完成状态===============》'); console.log("%c%s", "color:red", "el : " + this.$el); console.log(this.$el); console.log("%c%s", "color:red", "data : " + this.$data); console.log("%c%s", "color:red", "message: " + this.message) } }) </script>
</html>
复制代码

chrome浏览器里打开,F12console就能发现

image.png

3 beforecreated

el 和 data 并未初始化

4 created

完成了 data 数据的初始化,el没有

5 beforeMount

完成了 el 和 data 初始化

6 mounted

完成挂载

7 update

在console控制台中输入

app.message= 'hello!!';
复制代码

image.png

8 destroy

咱们在console里执行下命令对 vue实例进行销毁。销毁完成后,咱们再从新改变message的值,vue再也不对此动做进行响应了。可是原先生成的dom元素还存在,能够这么理解,执行了destroy操做,后续就再也不受vue控制了。

app.$destroy();
复制代码

image.png

9 生命周期总结

9.1 beforecreate

能够在这加个loading事件,加载的动画

9.2 created

在这结束loading,还作一些初始化,实现函数自执行

9.3 mounted

在这发起后端请求,拿回数据,配合路由钩子作一些事情

9.4 beforeDestroy

你确认删除XX吗? destroyed :当前组件已被删除,清空相关内容

20 指令-条件判断(v-if&v-show)

1 v-if&v-show

  • 条件渲染 (使用 v-if)
  • 条件展现 (使用 v-show)

if操做的是dom show 操做的样式 若是频繁切换dom使用v-show,当数据一开时就肯定下来使用v-if更好一些,若是if经过内部指令不会执行了 只有dom从显示到隐藏 或者隐藏到显示 才能使用vue的动画

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<body>
    <div id="app">
        <span v-if="flag">你看的见我</span>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script>
</html>
复制代码

2 区别总结

  • v-show:操做的是元素的display属性
  • v-if:操做的是元素的建立和插入
  • 相比较而言v-show的性能要高

21 内置组件-动画(transition)

1 组件的过渡

Vue 提供了 transition 的封装组件,在下列情形中,能够给任何元素和组件添加进入/离开过渡

image.png

在进入/离开的过渡中,会有 6 个 class 切换。

  1. v-enter:定义进入过渡的开始状态。在元素被插入以前生效,在元素被插入以后的下一帧移除。
  2. v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入以前生效,在过渡/动画完成以后移除。这个类能够被用来定义进入过渡的过程时间,延迟和曲线函数。
  3. v-enter-to2.1.8版及以上 定义进入过渡的结束状态。在元素被插入以后下一帧生效 (与此同时 v-enter 被移除),在过渡/动画完成以后移除。
  4. v-leave: 定义离开过渡的开始状态。在离开过渡被触发时马上生效,下一帧被移除。
  5. v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时马上生效,在过渡/动画完成以后移除。这个类能够被用来定义离开过渡的过程时间,延迟和曲线函数。
  6. v-leave-to2.1.8版及以上 定义离开过渡的结束状态。在离开过渡被触发以后下一帧生效 (与此同时 v-leave 被删除),在过渡/动画完成以后移除。

1.1 初步代码实现

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<style> div>div{ width:100px;height: 100px;background: red; } .v-enter{ opacity: 1; } /* 激活的时候 */ .v-enter-avtive{ opacity: 0; transition: 1s linear; } /* 离开 */ .v-leave-active{ opacity: 0; background: black; transition: 1s linear; } </style>
<body>
    <div id="app">
        <button @click="flag=!flag">切换</button>
        <!-- vue自定义的组件 -->
        <transition>
            <div v-show="flag"></div>
        </transition>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script>
</html>
复制代码

1.2 多个transition

赶上了多个transition的时候,同一个class确定是会冲突的,那么如何处理呢

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<style> div>div{ width:100px;height: 100px;background: red; } .jw-enter-active { transition: all .3s ease; } .jw-leave-active { transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0); } .jw-enter, .jw-leave-to { transform: translateX(10px); opacity: 0; } </style>
<body>
    <div id="app">
        <button @click="flag=!flag">切换</button>

        <!-- vue自定义的组件 -->
        <transition name="jw">
            <div v-show="flag"></div>
        </transition>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ el:'#app', data:{ flag:true } }) </script>
</html>
复制代码

简单的理解就是就 transition有一个name属性
在css中name-状态便可调用

22 自定义指令-directives

1 介绍

Vue 也容许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的状况下,你仍然须要对普通 DOM 元素进行底层操做,这时候就会用到自定义指令。

举一个栗子:

<!DOCTYPE html>
<html> <head> <meta charset="utf-8" /> <title>vue</title> </head> <body> <div id="app"> <div v-color='flag'>123</div> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script> var vm = new Vue({ directives:{ color(el,bindings){ //el值指代的是button按钮 console.log(arguments); el.style.background = bindings.value; } }, el: '#app', data: { flag: 'red' }, methods:{ getSomething(){ return "hello" } } }) </script> </html> 复制代码

出现如图状况

image.png

image.png

再来个栗子

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>vue</title>
</head>
<style> .a{ position: absolute;width: 100px;height: 100px;background: red; } </style>
<body>
    <div id="app">
       <div class="a" v-drag></div>
    </div>
</body>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> 
<script> var vm = new Vue({ directives:{ drag(el){ el.onmousedown = function (e) { var disx = e.pageX - el.offsetLeft; var disy = e.pageY - el.offsetTop; document.onmousemove = function (e) { el.style.left = e.pageX - disx +'px'; el.style.top = e.pageX - disy + 'px'; } document.onmouseup = function (e) { document.onmousemove = document.onmousemove = null; } e.preventDefault(); } } }, el: '#app', data: { flag: 'red' }, methods:{ getSomething(){ return "hello" } } }) </script>
</html>
复制代码

image.png

能够拖动

2 钩子函数

一个指令定义对象能够提供以下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用。在这里能够进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不必定已被插入文档中)。

update:所在组件的 VNode 更新时调用,可是可能发生在其子 VNode 更新以前。指令的值可能发生了改变,也可能没有。可是你能够经过比较更新先后的值来忽略没必要要的模板更新 (详细的钩子函数参数见下)。

3 钩子函数参数

  • el:指令所绑定的元素,能够用来直接操做 DOM 。
  • binding:一个对象,包含如下属性:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。不管值是否改变均可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1"中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

23 实例属性-$ref

官网针对-ref的解释

  • 预期string
    ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。若是在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;若是用在子组件上,引用就指向组件实例:
<!-- `vm.$refs.p` will be the DOM node -->
<p ref="p">hello</p>
<!-- `vm.$refs.child` will be the child component instance -->
<child-component ref="child"></child-component>
复制代码
  • 当 v-for 用于元素或组件的时候,引用信息将是包含 DOM 节点或组件实例的数组。
    关于 ref 注册时间的重要说明:由于 ref 自己是做为渲染结果被建立的,在初始渲染的时候你不能访问它们 - 它们还不存在!$refs 也不是响应式的,所以你不该该试图用它在模板中作数据绑定。

操做dom

若是咱们用jQuery的话,通常性均可以操做dom

$("#id").text('xxx')   // 使用Jquery
document.getElementById("id")  // 使用原生Dom
复制代码

如今咱们牛逼了,咱们用vue。那vue中,若是我要获取Dom,该怎么作?
这就进入本文的主题ref, $refs,官网解释:

<div id="app">
   <div>{{msg}}</div>
</div>
复制代码

在JavaScript中咱们习惯了使用document.getElementsByTagName


```javascript var vm = new Vue({ el: '#app', data:{ msg:'hello' }, mounted() { console.log(document.getElementsByTagName("div")[0].innerHTML); } }) ```

vue操做dom

那么咱们在vue中呢

<div id="app">
   <div ref="msg">{{msg}}</div>
</div>
复制代码
var vm = new Vue({
      el: '#app',
      data:{
        msg:'hello'
      },
      mounted() {
        // console.log(document.getElementsByTagName("div")[0].innerHTML);
        console.log('====================================');
        console.log(this.$refs.msg);
        console.log('====================================');
      }
    })
复制代码
相关文章
相关标签/搜索