Vue.js 2.0 手把手入门笔记

栏目: JavaScript · 发布时间: 6年前

内容简介:是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。框架是库的升级版这里使用cnd方便测试

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

2 特点:

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

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

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

框架是库的升级版

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

  • cnd方式
  • npm 方式

2.2 简单的尝试

这里使用cnd方便测试

<!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>
复制代码
Vue.js 2.0 手把手入门笔记

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的指令 directive 只是dom上的行间属性,vue给这类属性赋予了一些意义,来实现特殊功能所有指令都以v-开头value属性默认情况下回vue忽略掉 selected checked 都没有意义

2.3.3表单输入

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

<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>
复制代码
Vue.js 2.0 手把手入门笔记

最后可以实现双向绑定的雏形

3 数据响应的变化

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

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

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

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>
复制代码
Vue.js 2.0 手把手入门笔记

当你输入汤时

Vue.js 2.0 手把手入门笔记

就会变成这个样子  =>

Vue.js 2.0 手把手入门笔记

但是当你换成了key

可以简单的这样理解:加了key(一定要具有唯一性) id的checkbox跟内容进行了一个关联。是我们想达到的效果

vue和react的虚拟DOM的Diff算法大致相同,其核心是基于两个简单的假设

首先讲一下diff算法的处理方法,对操作前后的dom树同一层的节点进行对比,一层一层对比,如下图:

Vue.js 2.0 手把手入门笔记

当某一层有很多相同的节点时,也就是列表节点时,Diff算法的更新过程默认情况下也是遵循以上原则。

比如一下这个情况:

Vue.js 2.0 手把手入门笔记

我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:

Vue.js 2.0 手把手入门笔记

即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?

所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。

Vue.js 2.0 手把手入门笔记

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"><div class="outeer" @click="outer"><div class="middle" @click="middle"><button @click="inner">点击我(^_^)</button>
     </div>
   </div><p>{{ message }}</p> 
</div>

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

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

<!-- HTML --> 

<div id="app"><div class="outeer" @click.stop="outer"><div class="middle" @click.stop="middle"><button @click.stop="inner">点击我(^_^)</button>

     </div>

   </div> 

</div>
复制代码

1.2 prevent取消默认事件

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

1.3 .capture 捕获事件

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

<!-- HTML --> 
<div id="app"><div class="outeer" @click.capture="outer"><div class="middle" @click.capture="middle"><button @click.capture="inner">点击我(^_^)</button>
     </div>
   </div> 
</div>
复制代码
Vue.js 2.0 手把手入门笔记

1.4 .self

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

<!-- HTML --> 
<div id="app"><div class="outeer" @click.self="outer"><div class="middle" @click.self="middle"><button @click.stop="inner">点击我(^_^)</button>
     </div>
   </div> 
</div>
复制代码
Vue.js 2.0 手把手入门笔记

1.5 .once 只执行一次点击

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

2 键盘修饰符

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

  • .enter :回车键
  • .tab :制表键
  • .delete :含 deletebackspace
  • .esc :返回键
  • .space : 空格键
  • .up :向上键
  • .down :向下键
  • .left :向左键
  • .right :向右键
Vue.js 2.0 手把手入门笔记

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 函数缩写

Vue.js 2.0 手把手入门笔记

缩写后

Vue.js 2.0 手把手入门笔记

8 组件化管理

1.组件化开发

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

2、组件化开发的好处

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

在vue中例如div、span都可以看做一个组件

3、全局组件

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

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

<!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>
复制代码
Vue.js 2.0 手把手入门笔记
  • 组件名不要带大写,多组件使用 -
  • 只要组件和定义相同是可以的(首字母可以大写)
  • 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
    }
  }
})
复制代码

点击前

Vue.js 2.0 手把手入门笔记

点击后

Vue.js 2.0 手把手入门笔记

从图中可以得知: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"] 

Vue.js 2.0 手把手入门笔记

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 方法

Vue.js 2.0 手把手入门笔记
Vue.js 2.0 手把手入门笔记

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

**

对比非响应式方法

vm.food="chocolate"

alert(vm.food)

Vue.js 2.0 手把手入门笔记

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

Vue.js 2.0 手把手入门笔记

**

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 优势

1、在Vue中使用过滤器(Filters)来渲染数据是一种很有趣的方式。

2、首先我们要知道,Vue中的过滤器不能替代Vue中的 methodscomputed 或者 watch

3、过滤器不改变真正的 data ,而只是改变渲染的结果,并返回过滤后的版本。

4、在很多不同的情况下,过滤器都是有用的,比如尽可能保持API响应的干净,并在前端处理数据的格式。

5、在你希望避免重复和连接的情况下,它们也可以有效地封装成可重用代码块背后的所有逻辑。

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>
复制代码
Vue.js 2.0 手把手入门笔记

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

具名插槽

匿名插槽没有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>
复制代码
Vue.js 2.0 手把手入门笔记

这里的表达式包含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生命周期简介

Vue.js 2.0 手把手入门笔记
Vue.js 2.0 手把手入门笔记

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 就能发现

Vue.js 2.0 手把手入门笔记

3 beforecreated

el 和 data 并未初始化

4 created

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

5 beforeMount

完成了 el 和 data 初始化

6 mounted

完成挂载

7 update

在console控制台中输入

app.message= 'hello!!';
复制代码
Vue.js 2.0 手把手入门笔记

8 destroy

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

app.$destroy();
复制代码
Vue.js 2.0 手把手入门笔记

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 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡

Vue.js 2.0 手把手入门笔记

在进入/离开的过渡中,会有 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>
复制代码

出现如图情况

Vue.js 2.0 手把手入门笔记
Vue.js 2.0 手把手入门笔记

再来个栗子

<!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>
复制代码
Vue.js 2.0 手把手入门笔记

可以拖动

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  钩子中可用。
Vue.js 2.0 手把手入门笔记

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('====================================');
      }
    })
复制代码

以上所述就是小编给大家介绍的《Vue.js 2.0 手把手入门笔记》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro JavaScript Techniques

Pro JavaScript Techniques

John Resig / Apress / 2006-12-13 / USD 44.99

Pro JavaScript Techniques is the ultimate JavaScript book for the modern web developer. It provides everything you need to know about modern JavaScript, and shows what JavaScript can do for your web s......一起来看看 《Pro JavaScript Techniques》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具