Vue基础入门

入门

介绍

渐进式JavaScript框架

作用:动态构建(显示)用户界面

特点

遵循MVVM模式

编码简洁,体积小,运行效率高,适合移动/pc端开发

它本身只关注UI,可以轻松引入VUE插件或其他第三方库开发项目

借鉴angular的模板和数据绑定技术

借鉴react的组件化和虚拟DOM技术

MVC/MVVM

  • MVC
    • Model 数据层
    • View 视图层
    • Controller 操作层

​ 数据由控制层去操作,读取数据层的数据,渲染到视图层的页面上,返回

  • MVVM

    • Model 数据层(data)
    • View 视图层
    • ViewModel 视图模型层(vm)

    数据由ViewModel控制,加载到视图层上,返回.

    也可以从视图层上读取修改的数据,修改数据层的数据,从而达到数据双向绑定(响应式)

引入问题

  • vue.min.js生产环境/压缩版本,不能使用vue开发调试工具
  • 测试工具Vue Devtools
https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js
https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.js

表达式与语句

if里面可以放的就是表达式

  • 表达式

    有返回值(返回值可以是undefined)

    没有分号

  • 语句

    没有返回值

    末尾有分号(编译会自动加上)


      function fn() {} // 语句
      fn // 表达式
      fn() // 表达式

      var fn = function () {} // 函数表达式语句

      if (a) {} // 语句
      for () {} // 语句

数据代理/响应式数据

  • 在数据代理(将data中的数据代理到this上)
  • 实例对象上方法一般都有$(数据代理除外)
  • data上:响应式数据,响应式数据就是更新值用户界面会发生变化
  • this:普通数据,更新数据,用户页面不会发生变化

计算属性会监视内部使用过的属性的变化, 一旦发生变化, 计算属性就要重新计算
vue内部会对data/methods/computed等属性进行数据代理(将对象中的属性挂载到vm上),就能直接通过vm/this直接访问属性
==vue可以直接更新data数据, 同时用户界面也会发生变化(响应式) ==

内存泄漏/溢出

  • 没有清除定时器叫泄露(占用空间,但实际没有用,也就是没有释放)
  • 泄露多了容易内存溢出(实际使用超过了额定内存)

Vue实例

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

当创建一个 Vue 实例时,你可以传入一个选项对象

方式一

<div id="root">
    <h1>{{msg}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
    // vm就是Vue实例对象
    const vm = new Vue({
        //element元素,获取DOM元素
        el: "#root",
         // 数据:用来渲染到页面上
        data: {
            msg: "hello world"
        }
    })
</script>

方式二

<div id="root">
    <h1>{{msg}}</h1>
</div>
<script src="../js/vue.js"></script>
<script>
    const vm = new Vue({
        data: {
            msg: "hello world"
        }
    })
    //不通过el指定DOM,也可以通过$mount挂载DOM元素方式来操作DOM元素
    vm.$mount("#root")
    console.log(vm)
</script>

在这里插入图片描述

配置对象

配置对象:属性名固定的对象

DOM

el

只在用 new 创建实例时生效,提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标。可以是 CSS 选择器,也可以是一个 HTMLElement 实例.在实例挂载之后,元素可以用 vm.$el 访问。如果在实例化时存在这个选项,实例将立即进入编译过程,否则,需要显式调用 vm.$mount() 手动开启编译。

语法:el:“css选择器”

数据

data

Vue实例的数据对象,Vue的实例会代理data中的所有属性,所以可以通过Vue实例对象直接获取属性

语法:data:{零个或多个键值对}

methods

放置绑定事件的回调函数,注意不能使用箭头函数,this指向会有问题

语法:methods:{零个或多个回调函数}

computed

计算属性,不仅可以计算值,也可以通过函数来过滤,类似fillter等。计算的结果会被缓存,直到依赖项变化

  • 第一种
computed: {
    // 可读可写
    fullName1: {
        get() {
            return this.firstName + ' ' + this.lastName
        },
        set(newval) {
            const [firstName, lastName] = newval.split(' ')
            this.firstName = firstName
            this.lastName = lastName
        }
    }
},
  • 第二种
computed: {
    // 只读
    fullName2() {
        return this.firstName + ' ' + this.lastName
    }
}

watch

监听某个数据的变化,从而触发对应的回调函数,只有当监听的值发生改变,才会触发回调函数

语法:监听的数据值:function(){}

模板语法

也叫模板页面:HTML+CSS+JS

插值

  • 文本

    数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:

    <span>Message: {{ msg }}</span>
    

    Mustache 标签将会被替代为对应数据对象上 msg property 的值。无论何时,绑定的数据对象上 msg property 发生了改变,插值处的内容都会更新。

  • Attribute

    Mustache 语法不能作用在 HTML attribute 上,遇到这种情况应该使用 v-bind指令

    <div v-bind:id="dynamicId"></div>
    
  • 使用javascript表达式

    {{ number + 1 }}
    
    {{ ok ? 'YES' : 'NO' }}
    
    {{ message.split('').reverse().join('') }}
    
    <div v-bind:id="'list-' + id"></div>
    

指令

指令 (Directives) 是带有 v- 前缀的特殊 attribute。指令 attribute 的值预期是单个 JavaScript 表达式 v-for 是例外情况

v-model  用作双向数据绑定(数据既能从JS流向页面,也能从页面流向JS)
v-bind   用来强制绑定数据,单向数据绑定
v-on     用来绑定DOM事件

v-model

  • 双向数据绑定

v-bind

  • 强制绑定解析表达式,可以省略v-bind

v-on

  • 绑定事件监听,一般简写为@

v-for

  • 遍历数组/对象

v-if

  • 如果为true, 当前标签才会输出到页面

v-else/v-else-if

  • 如果为false, 当前标签才会输出到页面

v-show

  • 通过控制display样式来控制显示/隐藏

v-html

  • 更新元素的 类似于innerHTML

          <!-- 会解析 -->
          <p v-html="msg"></p>
    
           data: {
              msg: "<h1>北京上海广州深圳<h1>",
              num:0
            },
    

v-text

  • 更新元素的 相当于textContent

    <!-- 不会解析 -->
    <p v-text="msg"></p>
    
           data: {
              msg: "<h1>北京上海广州深圳<h1>",
              num:0
            },
    

v-cloak

  • 使用它防止闪现表达式, 与css配合: [v-cloak] { display: none },防止闪现,刷新页面的时候会出现{{msg}}这样的结构,网速慢的时候会出现

       <style>
          [v-cloak] {
            display: none;
          }
        </style>
    
    <p v-cloak>{{msg}}</p>
    <p v-once v-cloak>{{num}}</p>
    <p v-pre v-cloak>{{num}}</p>
    

v-once

  • 只渲染一次,只解析一次(后面值更新不会重新解析、渲染)

    <p v-once v-cloak>{{num}}</p>
    

v-pre

  • 会渲染最原始的内容(里面内容不会被vue解析)

    <p v-pre v-cloak>{{num}}</p>
    
    

ref

  • 为某个元素注册一个唯一标识, vue对象通过$els属性访问这个元素对象

  • 设置给DOM元素,获取到的就是DOM元素

  • 设置给组件,获取到的就是组件实例对象

     <button ref="btna" @click="num++">加数字</button>
    
    beforeMount() {
    console.log("beforeMount", this.$refs.btna);
    },
    mounted() {
    console.log("mounted", this.$refs.btna);
    },
    
  • 参数

    一些指令能够接收一个“参数”,在指令名称之后以冒号表示

    <a v-bind:href="url">...</a>
    <a v-on:click="doSomething">...</a>
    
  • 动态参数

    从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数:

    <!--
    注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。
    -->
    <a v-bind:[attributeName]="url"> ... </a>
    

自定义指令

注册全局指令

Vue.directive('my-directive', function(el, binding){
    el.innerHTML = binding.value.toupperCase()
 })

注册局部指令

  • 只对当前实例生效
  directives : {
    'my-directive' : {
        bind (el, binding) {
          el.innerHTML = binding.value.toupperCase()
        }
    }
  }
el 绑定指令的DOM元素
binding 是一个对象,包含指令和表达式等数据
{
    expression: "msg" //表达式
    name: "upper-text" //指令名称
    rawName: "v-upper-text" //指令全称
    value: "Atguigu" //表达式的值
}

使用指令

v-my-directive='xxx'

缩写

Vue 为 v-bindv-on 这两个最常用的指令,提供了特定简写:

  • v-bind 缩写

    强制数据绑定,功能:指定变化的属性值

    <!-- 完整语法 -->
    <a v-bind:href="url">...</a>
    
    <!-- 缩写 -->
    <a :href="url">...</a>
    
    <!-- 动态参数的缩写 (2.6.0+) -->
    <a :[key]="url"> ... </a>
    
  • v-on缩写

    绑定事件监听,功能:绑定指定事件名的回调函数

    <!-- 完整语法 -->
    <a v-on:click="doSomething">...</a>
    
    <!-- 缩写 -->
    <a @click="doSomething">...</a>
    
    <!-- 动态参数的缩写 (2.6.0+) -->
    <a @[event]="doSomething"> ... </a>
    

计算属性和侦听器

计算属性

对于任何复杂逻辑,你都应当使用计算属性

计算属性,在computed属性对象中定义计算属性的方法,在页面中使用{{方法名}}来显示计算的结果

  • 之前的计算方式,也是这个的原理
  • 根据已存在属性,来计算生成一个新的值
  const person = {
  firstName: "chen",
  lastName: "guanxi",
};

// 定义对象的属性的属性(原属性)
Object.defineProperty(person, "fullname", {
  // value:'chen guanxi',  属性的值
  configurable: false, // 是否可以重新配置或者删除
  // writable:true,  // 是否可以枚举
  enumerable: true, // 是否可以枚举,遍历, for in
  get() {
    // 属性读取时会调用的方法(返回值就是属性的值)
    return this.firstName + " " + this.lastName;
  },
  set(newVal) {
    // console.log(newVal);   luo yonghao
    // 利用数组一一对应关系来解构
    const [firstName, lastName] = newVal.split(" ");
    console.log(firstName); // luo
    console.log(lastName); // yonghao
    // console.log(this); { firstName: 'chen', lastName: 'guanxi', fullname: [Getter/Setter] }
    this.firstName = firstName;
    this.lastName = lastName;
  },
});

// 此时会调用get方法
console.log(person.fullname); //chen guanxi

// 此时会调用set方法
person.fullname = "luo yonghao";

console.log(person);
// { firstName: 'chen', lastName: 'guanxi', fullname: [Getter/Setter] } 修改前
// { firstName: 'luo', lastName: 'yonghao', fullname: [Getter/Setter] } 修改后

  • 现在
    <div id="app">:
      <input type="text" placeholder="First Name" v-model="firstname" /><br />: <input type="text" placeholder="Last Name" v-model="lastname" /><br />
      姓名1:
      <input type="text" placeholder="Full Name1" v-model="fullname" /><br />
      单向:
      <input type="text" placeholder="Full Name2" :value="fullname" /><br />
      只读:
      <input type="text" placeholder="Full Name2" :value="fullname" /><br />
      监视
      <input type="text" placeholder="Full Name2" :value="watchdata" /><br />
    </div>

    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
      new Vue({
        el: "#app",
        data: {
          firstname: "chen",
          lastname: "guanxi",
          watchdata: "luo yonghao",
        },
        computed: {
          fullname: {
            get() {
                // this指向的是vm实例
              return this.firstname + " " + this.lastname;
            },
            set(newVal) {
              const [firstname, lastname] = newVal.split(" ");
              this.firstname = firstname;
              this.lastname = lastname;
            },
          },
          fullname1() {
            return this.firstname + " " + this.lastname;
          },
        },
        watch: {
          firstname(newVal) {
            const res = this.watchdata.split(" ");
            res[0] = newVal;
            this.watchdata = res.join(" ");
          },
          lastname(newVal) {
            const res = this.watchdata.split(" ");
            res[1] = newVal;
            this.watchdata = res.join(" ");
          },
        },
      });
    </script>

计算属性高级

通过getter/setter实现对属性数据的显示和监视

计算属性存在缓存, 多次读取只执行一次getter计算

       // 计算属性
        // 计算属性会监视内部使用过的属性的变化,一旦发生变化,计算属性就要重新计算
        computed: {
          // 给vm定义一个属性,叫fullName
          fullName: {
            // fullName属性的读取的方法
            get() {
              // vue内部会对data、methods、computed等属性进行数据代理(将对象中的属性挂在到vm上)
              // 就能直接通过 vm/this 直接访问属性
              return this.firstName + " " + this.lastName;
            },
            // fullName属性的设置的方法
            set(newVal) {
              const [firstName, lastName] = newVal.split(" ");
              // vue可以直接更新data数据,同时用户界面也会发生变化(响应式)
              this.firstName = firstName;
              this.lastName = lastName;
            },
          },

          // 只读的计算属性(不能修改)
          // 这个方法相对于fullName1的get方法,没有set
          fullName1() {
            return this.firstName + " " + this.lastName;
          },
        },

watch侦听

通过通过vm对象的$watch()或watch配置来监视指定的属性,当属性变化时, 回调函数自动调用, 在函数内部进行计算

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

  • 监视已存在属性的变化,一旦变化,会保存数据,发送请求等操作
        watch: {
          // 监视firstName属性的变化,一旦发生变化就会调用函数
          firstName(newVal) {
            // ['li', 'peihua']
            const names = this.fullName3.split(" ");
            // ['wang', 'peihua']
            names[0] = newVal;

            this.fullName3 = names.join(" ");
          },
          lastName(newVal) {
            const names = this.fullName3.split(" ");
            names[1] = newVal;
            this.fullName3 = names.join(" ");
          },
        },

Class与Style绑定

class和style到底用哪种?

  1. 如果样式将来是可变的用style,静态写死就用class
  2. 如果样式将来是可变
    但是只在某个范围变化(red --> green --> red),用class
    但是如果变化时无穷,用st yle
    静态写死就用class

绑定HTML Class

  • 字符串语法

    <p :class="isRed ? 'red':'green'">
    
  • 对象语法

    v-bind:class指令可以与普通的class attribute共存

    <p :class="{red:isRed,green:!isRed}">这是一段文字~</p>
    
  • 数组语法

    数组中带引号的会直接使用,不带引号的属性回去data数据中寻找

    
    

这是一段文字~

这是一段文字~

```

绑定内联样式

  • 对象语法

    <p :style="{color:'blue',fontSize:fontSize+'px'}">这是一段文字~</p>
    

    对象语法常常结合返回对象的计算属性使用

  • 数组语法

    数组语法可以将多个样式对象应用到同一个元素上:

    <div v-bind:style="[baseStyles, overridingStyles]"></div>
    

条件渲染

v-if/v-else

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

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

不推荐同时使用v-if和v-for

  • v-if 隐藏的时候会删除整个元素(元素会移出DOM树)
   <div id="app">
      <p v-if="isShow">北京上海广州深圳</p>
      <p v-else-if="isElse">内蒙新疆哈尔滨</p>
      <p v-else>阿里灵芝</p>

      <button @click="handleShow">点击改变状态</button>
    </div>

    <script type="text/javascript" src="../js/vue.js"></script>
    <script type="text/javascript">
      new Vue({
        el: "#app",
        data: {
          isShow: true,
          isElse:false
        },
        methods: {
          handleShow() {
            this.isShow = !this.isShow;
          },
        },
      });
    </script>

v-show

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

  • 通过display样式来控制隐藏(元素还在DOM树上)
      <p v-if="isShow">北京上海广州深圳</p>
      <p v-show="isShow">北京上海广州深圳</p>

v-if和v-else的区别

v-if 隐藏的时候会删除整个元素(元素会移出DOM树)

v-show 通过display样式来控制隐藏(元素还在DOM树上)

v-if因为要插入和移除DOM,所以性能差

如果需要频繁切换,v-show比较好

如果不频繁,只用一次,v-if

列表渲染

我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名

  • 建议尽可能在使用 v-for 时提供 key attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。

遍历数组

<ul id="example-1">
  <li v-for="item in items" :key="item.message">
    {{ item.message }}
  </li>
</ul>
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
  • 也可以用 of 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法

遍历对象

在遍历对象时,会按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。

<div v-for="(value, name, index) in object">
  {{ index }}. {{ name }}: {{ value }}
</div>

数组方法

  • map 返回新数组特点:长度不变,值变
  • filter 返回新数组特点:长度变,值不变
  • reduce 返回特点:长度变,值也变

事件处理

监听事件

可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

事件处理方法

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

  • 解决:在配置对象中定义一个methods属性,是一个对象,在它里面保存各种方法

内联处理器中的方法

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

<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div><button v-on:click="say('what')">Say what</button>

new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})

$event

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


事件修饰符

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

v-on:click.self.prevent 只会阻止对元素自身的点击。

修饰符是由点开头的指令后缀来表示的。

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

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

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

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

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

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

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


按键修饰符

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

.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
// 使用 keyCode attribute 也是允许的:
<input v-on:keyup.13="submit">

表单输入绑定

基础用法

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

v-model 会忽略所有表单元素的 valuecheckedselected attribute 的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data 选项中声明初始值。

  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。
  • 使用v-model(双向数据绑定)自动收集数据

修饰符

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转为在 change 事件_之后_进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg">

.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number">

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值。

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<input v-model.trim="msg">

生命周期

在这里插入图片描述

初始化显示

beforeCreate()

  • 在数据代理(将data中的数据代理到this上)之前触发

  • 此时不能访问data、methods、computed…数据

created()

  • 在数据代理之后触发

beforeMount()

  • 在挂载之前(页面渲染之前),不能可以操作DOM

mounted()

  • 在挂载之后(页面渲染之后),可以直接操作DOM

更新状态

  • beforeUpdate()
  • updated()

销毁

vm.$destory()

  • beforeDestory()
  • destoryed()
  1. 常用的生命周期方法
    created()/mounted(): 发送ajax请求, 启动定时器等异步任务
    beforeDestory(): 做收尾工作, 如: 清除定时器
   Vue生命周期函数:
      1. 初始化
          beforeCreate  在数据代理之前触发,所以此时不能操作data等数据
          created
          beforeMount 在挂载之前(页面渲染之前),所以此时不能操作DOM
          **mounted**
            重要!
            发送请求、设置定时器、绑定事件等一次性的任务
      2. 更新
          beforeUpdate
          updated
      3. 卸载
          beforeDestroy
            做收尾工作:清除定时器、解绑事件等任务
          destroyed

过渡/动画

在这里插入图片描述

单元素/组件的过渡

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

  • 条件渲染 (使用 v-if)
  • 条件展示 (使用 v-show)
  • 动态组件
  • 组件根节点
<div id="demo">
  <button v-on:click="show = !show">
    Toggle
  </button>
// 如果使用了name 那么将用xxx-enter类名
  <transition name="fade">
    <p v-if="show">hello</p>
  </transition>
</div>

当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:

  1. 自动嗅探目标元素是否应用了 CSS 过渡或动画,如果是,在恰当的时机添加/删除 CSS 类名。
  2. 如果过渡组件提供了 JavaScript 钩子函数,这些钩子函数将在恰当的时机被调用。
  3. 如果没有找到 JavaScript 钩子并且也没有检测到 CSS 过渡/动画,DOM 操作 (插入/删除) 在下一帧中立即执行。(注意:此指浏览器逐帧动画机制,和 Vue 的 nextTick 概念不同)

过渡的类名

在进入/离开的过渡中,会有 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 被删除),在过渡/动画完成之后移除。

对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>,则 v- 是这些类名的默认前缀。如果你使用了 <transition name="my-transition">,那么 v-enter 会替换为 my-transition-enter

动画

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>10_过渡&动画2</title>
    <style>
      #box {
        width: 100px;
        height: 100px;
        background-color: red;
        position: relative;
        left: 0;
        /* transition: all 2s; */
        animation: move 5s linear 1s infinite alternate-reverse;
      }

      @keyframes move {
        0% {
          left: 0;
        }
        50% {
          left: 500px;
        }
        100% {
          left: 100px;
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="left+=100">点击我向右偏移100</button>

      <div id="box" :style='{left:left+"px"}'></div>
    </div>
    <script type="text/javascript" src="../js/vue.js"></script>
    <script>
      new Vue({
        el: "#app",
        data: {
          left: 0,
        },
      });
    </script>
  </body>
</html>

过滤器

  • 专门用来处理

理解

功能:对要显示的数据进行特定格式化后再显示

注意:并没有改变原本的数据,而是会产生新的对应的数据

编码

定义过滤器

Vue.filter("filterName",(value,form,[three...])=>{
    ... 进行一定的数据处理
    return newValue
})

使用过滤器

<div>{{myData | filterName}}</div>
<div>{{myData | filterName(arg)}}</div>

全局过滤 filter

所有的实例都可以使用

Vue.filter("alldate", (value, form, three) => {
    console.log(value, form, three);
    return dayjs(value).format(form);
});

局部过滤器filters

只有当前实例可以使用

filters: {
    newDate(value, form, three) {
        return dayjs(value).format(form);
    },
},

插件

  • jQuery(操作DOM) dayjs(格式化时间) lodash(节流、防抖…) – 提供特定功能

  • 框架

    功能更加强大的库 React Vue

  • 插件

    扩展库、框架的功能
    jQuery插件 扩展jQuery功能
    React插件 React-Router
    Vue插件 Vue-Router Vuex…

自定义插件-方式一

function Myplugin(Vue) {
  // 当你使用插件时,会调用插件函数,传入Vue作为参数

  // 扩展全局功能
  Vue.globalplugin = function () {
    console.log("我是globalplugin插件");
  };

  // 扩展实例对象功能
  Vue.prototype.$instanceMethods = function () {
    console.log("我是instanceMethods");
  };

  // 扩展全局过滤器
  Vue.filter("nowDate", (val, form) => {
    return dayjs(val).format(form);
  });

  // 扩展全局指令
  Vue.directive("lower-text", (el, binding) => {
    el.innerText = binding.value.toLowerCase();
  });
}

// export default MyPlugin
window.Myplugin = Myplugin;

自定义插件-方式二

const Myplugin = {}
Myplugin.install = function (Vue) {
  // 当你使用插件时,会调用插件函数,传入Vue作为参数

  // 扩展全局功能
  Vue.globalplugin = function () {
    console.log("我是globalplugin插件");
  };

  // 扩展实例对象功能
  Vue.prototype.$instanceMethods = function () {
    console.log("我是instanceMethods");
  };

  // 扩展全局过滤器
  Vue.filter("nowDate", (val, form) => {
    return dayjs(val).format(form);
  });

  // 扩展全局指令
  Vue.directive("lower-text", (el, binding) => {
    el.innerText = binding.value.toLowerCase();
  });
}
// export default MyPlugin
window.Myplugin = Myplugin;

使用

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>17_插件</title>
</head>

<body>
  <div id="app">
    <p>{{date | nowDate("YYYY-MM-DD")}}</p>
    <p v-lower-text="msg"></p>
  </div>

  <script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.9.4/dayjs.min.js"></script>
  <script type="text/javascript" src="../js/vue.js"></script>
  <script src="./Myplugin.js"></script>
  <script type="text/javascript">
    // 必须要在new Vue 之前使用插件
    // 使用Vue插件 --> 调用插件函数,传入Vue
    Vue.use(Myplugin);
    new Vue({
      el: "#app",
      data: {
        msg: "BEIJING",
        date: Date.now(),
      },
      mounted() {
        Vue.globalplugin();
        this.$instanceMethods();
      },
    });
  </script>
</body>

</html>

key

测试代码

<div id="demo">
  <div>
    <button @click="add">add first</button>
    &nbsp;
    <button @click="remove">remove first</button>
    &nbsp;
    <button @click="sort">sort</button>
  </div>

  <h2>使用id作为key</h2>
  <ul>
    <li v-for="p in persons" :key="p.id">
      {{p.id}}--{{p.name}}--{{p.age}}--<input/>
    </li>
  </ul>

  <h2>使用index作为key</h2>
  <ul>
    <li v-for="(p, index) in persons" :key="index">
      {{p.id}}--{{p.name}}--{{p.age}}--<input/>
    </li>
  </ul>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#demo',
    data: {
      persons: [
        {id: 1, name: 'Tom', age: 13},
        {id: 2, name: 'Jack', age: 12}
      ]
    },

    methods: {
      add() {
        this.persons.unshift({id: this.persons.length + 1, name: 'Bob', age: 14})
      },

      remove() {
        this.persons.shift()
      },

      sort() {
        this.persons.sort((p1, p2) => p1.age - p2.age)
      }
    }
  })

虚拟DOM的key的作用?

1). react/vue中的key的作用/内部原理

2). 为什么列表的key尽量不要用index

1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用

2). 详细的说: 当列表数组中的数据发生变化生成新的虚拟DOM后, React进行新旧虚拟DOM的diff比较

​ a. key没有变

​ item数据没变, 直接使用原来的真实DOM

​ item数据变了, 对原来的真实DOM进行数据更新

​ b. key变了

​ 销毁原来的真实DOM, 根据item数据创建新的真实DOM显示(即使item数据没有变)

key为index的问题

1). 添加/删除/排序 => 产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低

2). 如果item界面还有输入框 => 产生错误的真实DOM更新 ==> 界面有问题

注意: 如果不存在添加/删除/排序操作, 用index没有问题

解决:

使用item数据的标识数据作为key, 比如id属性值

key的值能用id用id,没有id的话,如果是往数组最后面添加、删除等操作可以用index

响应式

概念

响应式:当你更新数据的时候,数据会变化,同时用户界面也会更新

data中的数据才是响应式数据,compoteds是根据data计算的也是

原来数据的属性就是响应式,新添加的属性不是响应式

        methods: {
          add() {
            // 不是响应式
            this.xxx = 123;
          },

data中普通对象

如果属性一开始存在,它就是响应式的

如果属性是后面新添加,它就不是响应式的

除非通过下面方法添加,才是响应式

this.$set(对象, 属性名, 属性值); 给对象添加响应式属性的方法

Vue.set(对象, 属性名, 属性值);

data中数组

如果通过数组下标添加新元素,不是响应式的
其他情况都是响应式的,vue底层修改了push的方法

Component

定义组件

// 定义组件
    const TextComponent = Vue.extend({
      // 组件的配置对象(和new Vue传入的配置对象基本对象)
      data() {
        // 返回值就是data数据
        return {
          num: 0,
        };
      },
      methods: {
        handle() {
          this.num++;
        },
      },
      // 组件模板页面(组件要渲染的页面)
      template:
        "<div><p>{{num}}</p><button @click='handle'>点我数字加加</button></div>",
    });

注册组件

/*
    组件命名:
    1. test-component 
    使用:<test-component />
    2. TestComponent
    使用:<test-component />
    <TestComponent /> 这种只能在脚手架中生效,页面中不行
*/
   new Vue({
      el: "#app",
      // 注册组件(一旦注册了组件,当前就能使用组件)
      components: {
        //   "text-component": TextComponent,
        TextComponent: TextComponent,
      },
    });

使用组件

<div id="app">
    <text-component></text-component>
</div>

定义全局组件

    // 使用
	<div id="app">
      <h1>北京上海广州深圳</h1>
      <test-component></test-component>
    </div>

    <script src="../js/vue.js"></script>
    <script>
      //   const TestComponent = Vue.extend({
      //     data() {
      //       return {
      //         num: 0,
      //       };
      //     },
      //     methods: {
      //       handle() {
      //         this.num++;
      //       },
      //     },
      //     template:
      //       "<div><p>{{num}}</p><button @click='handle'>点我数字加加</button></div>",
      //   });

      //   Vue.component("test-component", TestComponent);

      Vue.component("test-component", {
        data() {
          return {
            num: 0,
          };
        },
        methods: {
          handle() {
            this.num++;
          },
        },
        template:
          "<div><p>{{num}}</p><button @click='handle'>点我数字加加</button></div>",
      });

      new Vue({
        el: "#app",
      });
    </script>

组件间通讯

data为什么要是函数?

data有两种写法:1. 对象 2. 函数
在普通页面中,两种写法都行
但是在组件中,必须使用函数

如果在组件中,data使用对象,复用组件时这多个组件会共享同一份data数据
如果在组件中,data使用函数,复用组件时这多个组件会调用函数得到一份新data数据,每个组件的data数据都会不一样,所以不会互相影响

1.props(父子组件)

当使用props传数据的时候,如果组件声明接受了,就可以直接通过this使用
如果组件没有声明接受,属性会在this.$attrs上,可以通过$attrs访问

  • 适用场景:父子组件

  • 父传子:将数据通过 props 直接传递

// 父组件传递数据
<Child :name="name" />
// 子组件声明接受props
{
  props: ['name'],
  props: {name: String},
  props: {name: { type: String, required: true, default: xxx, validator() {} }},
}
// 子组件使用
this.xxx
// 如果属性没有通过props声明接受,那么可以通过$attrs来使用
this.$attrs.xxx
  • 子传父
    • 父组件定义更新数据的方法,以 props 方式传递给子组件,子组件声明接受调用函数,传递数据给父组件
    • 子组件调用使用

2.自定义事件(子向父)

作用:用来子组件向父组件通信
给哪个组件绑定自定义事件,就只有那个组件可以触发(使用)

  • 适用场景:子传父

方式一

 @add="add"
    绑定自定义事件
    事件名:add
    事件回调函数:add
// 绑定自定义事件
  <Child @add="add" />
// 触发事件
  this.$listeners.add();
  this.$emit('add')

方式二

  ref 如果设置给普通DOM元素,那么获取到的就是这个真实DOM元素
    如果设置给组件,那么获取带的就是组件实例对象
// 绑定自定义事件
  <Child ref="child" />
  
  mounted() {
    this.$refs.child.$on('add', this.add)
  }

// 触发事件
  this.$emit('add')
  所有组件实例对象都具备以下方法:
    $on(eventName, listener)     绑定自定义事件(持久)
    $once(eventName, listener)   绑定自定义事件(一次性)
    $off(eventName, listener)    解绑事件
    $emit(eventName, data)       触发自定义事件

3.全局事件总线(任意组件)

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

  • 适用场景:任意组件

  • 原理:

    • 本质上就是自定义事件
    • 给 Vue 的原型添加一个可以绑定事件的对象(Vue 的实例 vm、组件的实例 this)
    • 使用 Vue 的实例 vm 在 main.js 文件中最方便
  • 使用方式有两种:

  Vue.prototype.$bus = new Vue()
  beforeCreate() { Vue.prototype.$bus = this }
  • 所有组件实例对象都能通过原型链的方式访问到 $bus 这个对象,所以就能通过它绑定事件或者触发事件
// 绑定事件(接受数据):
this.$bus.$on(eventName, listener)

// 触发事件(发送数据):
this.$bus.$emit(eventName, data)

4.PubSub消息发布订阅(任意组件)

  • 使用场景:任意组件
  • 发布(发送数据):PubSub.publish(msg, data)
  • 订阅(接受数据): PubSub.subscribe(msg, listener)

5.插槽(父给子传递带数据标签)

  • 使用场景:父给子传递带数据的标签

默认插槽

组件写成双标签,里面放入标签数据,那么这个标签数据就会以插槽的方式传递给子组件

// 父组件给子组件传递带数据的标签
<AChild>
  <p>hello vue000</p>
  <p>hello vue11</p>
  <p>{{msg}}</p>
</AChild>
// 子组件使用
// 使用父组件以插槽方式传递的标签数据
<slot></slot>

具名、命名插槽

具名、命名插槽: 给每一个插槽取一个名字

 // 具名、命名插槽: 给每一个插槽取一个名字
 <BChild>
   // 旧语法:slot="名称"
   <template slot="header">
     <header>头部...{{ title }}</header>
   </template>

   // 新语法:v-slot:名称 
   <template v-slot:main>
     <main>内容区...</main>
   </template>

   // 新语法可以简写:#名称 
   <template #footer>
     <footer>底部...</footer>
   </template>
 </BChild>
  • 子组件使用
<template>
  <div>
    // 显示头部 
    // 通过name属性来决定使用哪个具名插槽 
    <slot name="header"></slot>
    <p>---------</p>
    // 显示内容区 
    <slot name="main"></slot>

    <p>---------</p>
    // 显示底部 
    <slot name="footer"></slot>
  </div>
</template>

作用域插槽

  • 父组件
 <CChild>
   // 父组件插槽可以接受子组件通过slot传递的props数据
   <template #list="slotProps">
   <template v-slot:list="slotProps">

   // { person } -> 就是对数据进行解构赋值
   <template #list="{ person }">

   <template #list="{ person: { name, age } }">
     <ul>
       <li>姓名:{{ name }}</li>
       <li>年龄:{{ age }}</li>
     </ul>
   </template>
 </CChild>
  • 子组件
<template>
  <div>
    // 以标签属性(props)方式传递person数据 
    <slot name="list" :person="person"></slot>
  </div>
</template>

<script>
export default {
  name: "CChild",
  data() {
    return {
      person: {
        name: "jack",
        age: 18,
      },
    };
  },
};
</script>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值