Vue教学+实战(更新中)

导读:本篇文章讲解 Vue教学+实战(更新中),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

声明

本文没有细讲,是为了帮助工作需要的朋友们快速入门Vue写的,或者是复习用的文章,所以不要说:啊!这里也不说因为啥,就这么写
别杠,杠就是你对,出门左转谢谢,求你给我写一篇细节点的好吗,我必给你点赞
此文会一直更到实战项目完事,附带代码,有错误欢迎指出,有问题留言,期间我会不断补充,细节一下文章内容的,大家一起加油吧

总览

在这里插入图片描述

在这里插入图片描述

Vue起步

Hello World

在官网下载vue.js(百度Vue进入官网)
我们先用原生来写一段代码

<body>
  <div id="app"></div>
  <script>
    var dom = document.getElementById('app')
    dom.innerHTML = 'hello world'
  </script>
</body>

现在我们要用vue来写了,我们要引入vue.js,然后创建vue实例
Vue.js在官网可以下载,我也会直接给大家

<body>
  <div id="app">{{ content }}</div>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        content: 'hello world'
      }
    })
  </script>
</body>

el配置项:实例负责管理的区域
我们如果要延迟两秒显示内容的话需要用setTimeout

<body>
  <div id="app">{{ content }}</div>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        content: 'hello world'
      }
    })
    setTimeout(function () {
      app.$data.content = 'bye world'
    }, 2000)
  </script>
</body>

这里$data理解为data的别名

我们用原生和Vue追重要的区别是,我们不需要DOM操作了

使用Vue.js实现TodoList(例子)

<body>
  <div id="app">
    <input type="text">
    <button>提交</button>
    <ul>
      <li>第一课的内容</li>
      <li>第二课的内容</li>
    </ul>
  </div>
</body>

现在我有个需求,输入框输入内容,提交后呈现在li
我们现在,要利用vue.js不再让我们的数据是死数据
具体代码如下

<body>
  <div id="app">
    <input type="text">
    <button>提交</button>
    <ul>
      <li v-for="item in list">{{item}}</li>
    </ul>
  </div>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        list: ['第一课的内容', '第二课的内容']
      }
    })
  </script>
</body>

效果和原生是一样的
v-for指令:类似于for,就是用来遍历渲染的
v-on:绑定事件
v-model:数据的双向绑定

<body>
  <div id="app">
    <input type="text" v-model="inputValue">
    <button v-on:click="handleBtnClick">提交</button>
    <ul>
      <li v-for="item in list">{{item}}</li>
    </ul>
  </div>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        list: ['第一课的内容', '第二课的内容'],
        inputValue: ''
      },
      methods: {
        handleBtnClick: function () {
        }
      },
    })
  </script>
</body>

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这就是双向的数据绑定

<body>
  <div id="app">
    <input type="text" v-model="inputValue">
    <button v-on:click="handleBtnClick">提交</button>
    <ul>
      <li v-for="item in list">{{item}}</li>
    </ul>
  </div>
  <script>
    var app = new Vue({
      el: '#app',
      data: {
        list: [],
        inputValue: ''
      },
      methods: {
        handleBtnClick: function () {
          this.list.push(this.inputValue)
          this.inputValue = ''
        }
      },
    })
  </script>
</body>

MVVM模式

在MVVM模式之前,前端采用的是MVP的模式
在这里插入图片描述

<body>
  <div>
    <input type="text">
    <button id="btn">提交</button>
    <ul></ul>
  </div>
  <script>
    function Page() { }
    $.extend(Page.prototype, {
      init: function () {
        this.bindEvents()
      },
      bindEvents: function () {
        var btn = $('#btn')
        btn.on('click', $.proxy(this.handleBtnClick,
          this))//proxy是jquery的一个方法,可以改变this指向
      },
      handleBtnClick: function () {
        alert('123')
      }
    })
    var page = new Page()
    page.init()
  </script>
</body>

这是一段基础的代码

<body>
  <div>
    <input id="input" type="text">
    <button id="btn">提交</button>
    <ul id="ul"></ul>
  </div>
  <script>
    function Page() { }
    $.extend(Page.prototype, {
      init: function () {
        this.bindEvents()
      },
      bindEvents: function () {
        var btn = $('#btn')
        btn.on('click', $.proxy(this.handleBtnClick,
          this))
      },
      handleBtnClick: function () {
        var inputValue = $("#input").val()
        var ulElem = $("#ul")
        ulElem.append('<li>' + inputValue + '</li>')
        $("#input").val('')
      }
    })
    var page = new Page()
    page.init()
  </script>
</body>

这是我们的效果代码
可以达到跟vue一样的效果
M:模型,在这个代码里不涉及Ajax,所以不涉及M层
V:视图
P:控制器

MVVM呢
在这里插入图片描述
注意,我们需要关注的是view和model层
VM层不需要我们去操作,它是Vue自动操作的

  1. M层:就是我们写的数据
  2. V层:就是视图
  3. VM层:就是我们数据变,视图就跟着变,这个不需要我们操作

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

前端组件化

每个组件就是每个页面上的一个区域,类似于原生里面,我们不也是用各个div盒子来进行分区吗
我们在Vue里,不同的区域我们会有对应的组件

使用组件化思想修改TodoList

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


<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
</head>


<body>
  <div id="app">
    <input type="text" v-model="inputValue">
    <button v-on:click="handleBtnClick">提交</button>
    <ul>
      <!-- <li v-for="item in list">{{item}}</li> -->
      <todo-item v-bind:content="item" v-for="item in list"></todo-item>
    </ul>
  </div>
  <script>
    //Vue.component用来注册全局组件
    Vue.component("TodoItem", {
      props: ['content'],
      template: "<li>{{content}}</li>"
    })
    var app = new Vue({
      el: '#app',
      data: {
        list: [],
        inputValue: ''
      },
      methods: {
        handleBtnClick: function () {
          this.list.push(this.inputValue)
          this.inputValue = ''
        }
      },
    })
  </script>
</body>


</html>

以上的是定义了一个全局组件,现在我们想定义一个局部组件呢
最主要的区别就是,我们需要去注册组件,用components去注册

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


<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
</head>


<body>
  <div id="app">
    <input type="text" v-model="inputValue">
    <button v-on:click="handleBtnClick">提交</button>
    <ul>
      <!-- <li v-for="item in list">{{item}}</li> -->
      <todo-item v-bind:content="item" v-for="item in list"></todo-item>
    </ul>
  </div>
  <script>
    //Vue.component用来注册全局组件
    // Vue.component("TodoItem", {
    //   props: ['content'],
    //   template: "<li>{{content}}</li>"
    // })
    // 注册局部组件
    var TodoItem = {
      props: ['content'],
      template: "<li>{{content}}</li>"
    }
    var app = new Vue({
      el: '#app',
      components: {
        TodoItem: TodoItem
      },
      data: {
        list: [],
        inputValue: ''
      },
      methods: {
        handleBtnClick: function () {
          this.list.push(this.inputValue)
          this.inputValue = ''
        }
      },
    })
  </script>
</body>


</html>

简单的组件间传值

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


<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
</head>


<body>
  <div id="app">
    <input type="text" v-model="inputValue">
    <button v-on:click="handleBtnClick">提交</button>
    <ul>
      <!-- <li v-for="item in list">{{item}}</li> -->
      <todo-item v-bind:content="item"
       v-bind:index="index"
        v-for="(item,index) in list" 
        @delete="handleItemDelete">
      </todo-item>
    </ul>
  </div>
  <script>
    //Vue.component用来注册全局组件
    // Vue.component("TodoItem", {
    //   props: ['content'],
    //   template: "<li>{{content}}</li>"
    // })
    // 注册局部组件
    var TodoItem = {
      props: ['content', 'index'],
      template: "<li @click='handleItemClick'>{{content}}</li>",
      methods: {
        handleItemClick: function () {
          this.$emit("delete", this.index)
        }
      }
    }
    var app = new Vue({
      el: '#app',
      components: {
        TodoItem: TodoItem
      },
      data: {
        list: [],
        inputValue: ''
      },
      methods: {
        handleBtnClick: function () {
          this.list.push(this.inputValue)
          this.inputValue = ''
        },
        handleItemDelete: function (index) {
          this.list.splice(index, 1)
        }
      },
    })
  </script>
</body>


</html>

Vue基础

Vue实例

Vue 的每个组件实际上都是一个Vue的实例,我们官方用vm

Vue2生命周期

生命周期函数就是Vue实例在某一个时间点会自动执行的函数,通过不同的时间点,可以做不同的时间,比较常用的是created和mounted,可以用来发axios和操作DOM
在这里插入图片描述

计算属性,方法与侦听器

<body>
  <div id="app">
    {{first + " " + last}}
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        first: "Jin",
        last: "Lin"
      }
    })
  </script>
</body>

我想让页面上显示Jin Lin,我在插值中是这个写法,可以发现,他成语句了,也就是有逻辑了,但是我们不想在模版的插值中去写逻辑,怎么办
我们引出一个概念:计算属性

<body>
  <div id="app">
    {{full}}
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        first: "Jin",
        last: "Lin"
      },
      computed: {
        full: function () {
          console.log('计算了');
          return this.first + " " + this.last
        }
      }
    })
  </script>
</body>

计算属性和别的最重要的区别就是:它还是属性,它和data里的属性最大的区别就是,它是通过计算得来的
计算属性有一个缓存的机制:就是计算属性依赖的值不发生改变,计算属性就不会发生计算

在这里插入图片描述
除了计算属性,我们还可以通过方法来达到相同的效果

<body>
  <div id="app">
    {{full()}}
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        first: "Jin",
        last: "Lin"
      },
      methods: {
        full: function () {
          return this.first + " " + this.last
        }
      },
    })
  </script>
</body>

只不过我们需要用()调用一下
但是这种方法,是没有计算属性有效的,为什么这么说呢
在这里插入图片描述
除了以上的两种,还有一种,叫做侦听

<body>
  <div id="app">
    {{full}}
    {{age}}
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        first: "Jin",
        last: "Lin",
        full: "Jin Lin",
        age: "18"
      },
      watch: {
        first: function () {
          console.log('计算了一次');
          this.full = this.first + "" + this.last
        },
        last: function () {
          console.log('计算了一次');
          this.full = this.first + "" + this.last
        }
      }
    })
  </script>
</body

在这里插入图片描述

watch也有缓存

在这里插入图片描述
那么,watch和computed都具有缓存的机制,推荐使用哪个呢
computed呗,因为简单呗

watch和computed区别:
watch可以开启异步任务,computed不可以开启异步,因为它依靠返回值
computed可以完成的功能,watch都可以完成

计算属性的getter和setter

<body>
  <div id="app">
    {{full}}
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        first: "Jin",
        last: "Lin",
      },
      computed: {
        full: {
          get: function () {
            return this.first + " " + this.last
          },
          set: function (value) {
            var arr = value.split(" ")
            this.first = arr[0]
            this.last = arr[1]
          }
        }
      }
    })
  </script>
</body>

Vue的样式绑定

<body>
  <div id="app">
    <div @click="hand" :class="{activated:isActivated}">
      hello world
    </div>
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        isActivated: false
      },
      methods: {
        hand: function () {
          this.isActivated = true
        }
      },
    })
  </script>
</body>

通过这个,我们就可以实现,点击变颜色了

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


<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
  <style>
    .activated {
      color: red;
    }
  </style>
</head>
<body>
  <div id="app">
    <div @click="hand" :class="{activated}">
      hello world
    </div>
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        activated: ""
      },
      methods: {
        hand: function () {
          this.activated = "activated"
        }
      },
    })
  </script>
</body>
</html>

我们想再点击变黑,互相切换呢

        hand: function () {
          this.activated = this.activated === "activated" ? "" : "activated"
        }

可以用if else,但是三元表达式不是更简便,看着更牛b嘛,嘿嘿
那么,如果我们直接绑定style呢,两种写法:对象形式,数组形式
对象:

<body>
  <div id="app">
    <div :style="styleObj">
      hello world
    </div>
  </div>
  <script>
    //创建了一个Vue实例
    var vm = new Vue({
      el: '#app',
      data: {
        styleObj: {
          color: "red"
        }
      },
      methods: {
      },
    })
  </script>
</body>

变色的

<body>
  <div id="app">
    <div :style="styleObj" @click="hand">
      hello world
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        styleObj: {
          color: "red"
        }
      },
      methods: {
        hand: function () {
          this.styleObj.color = this.styleObj.color === "red" ? "black" : "red"
        }
      },
    })
  </script>
</body>

数组模式

<body>
  <div id="app">
    <div :style="[styleObj,{fontSize:'20px'}]" @click="hand">
      hello world
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        styleObj: {
          color: "red"
        }
      },
      methods: {
        hand: function () {
          this.styleObj.color = this.styleObj.color === "red" ? "black" : "red"
        }
      },
    })
  </script>
</body>

字符串写法:适用于样式的类名不确定,需要动态指令
数组写法:要绑定的样式个数不确定,名字也不确定
对象写法:要绑定的样式个数不确定,名字也不确定,但要动态决定用不用

Vue中的条件渲染

<body>
  <div id="app">
    <div v-if="show">{{message}}
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: false,
        message: "Jin ❤ Lin"
      }
    })
  </script>
</body>


这是v-if,同时还有一个指令v-show

<body>
  <div id="app">
    <div v-if="show">{{message}}</div>
    <div v-show="show">{{message}}</div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: false,
        message: "Jin ❤ Lin"
      }
    })
  </script>
</body>

在这里插入图片描述

注意这里的区别,v-if是直接没有,而v-show是用了display:none,也就是说,这两者在根本上就不同
显然,我们v-show的性能更高一些,因为它不用频繁地销毁和生产DOM
但是,v-if能和v-else配合使用,当然也可以和v-else-if使用,需要注意的是,中间不能被打断
还有一点需要注意的:v-show不能和template配合使用

Vue列表渲染

<body>
  <div id="app">
    <div v-for="item of list">
      {{item}}
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        list: [
          "Jin",
          "Lin"
        ]
      }
    })
  </script>
</body>

item of list中的of可以改成in

<body>
  <div id="app">
    <div v-for="(item,index) of list">
      {{item}}
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        list: [
          "Jin",
          "Lin"
        ]
      }
    })
  </script>
</body>

index是索引下标
在这里插入图片描述
为了提高性能,我们会给循环加一个key值(体现在虚拟DOM上)
这里不推荐用index,正常开发后端都会有一个id,或者是什么别的东西

<body>
  <div id="app">
    <div v-for="(item,index) of list" :key="item.id">
      {{item}} ---- {{index}}
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        list: [
          {
            id: '001',
            name: 'Jin'
          },
          {
            id: '002',
            name: 'Lin'
          }
        ]
      }
    })
  </script>
</body>

现在页面是这个效果
在这里插入图片描述
我们需要进行一下改进
在这里插入图片描述
我们还可以遍历对象

<body>
  <div id="app">
    <div v-for="item of userInfo">
      {{item}}
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        userInfo: {
          name: "shaka",
          age: 18,
          gender: "男"
        }
      }
    })
  </script>
</body>

我们可以加一个key

<body>
  <div id="app">
    <div v-for="(item,key) of userInfo">
      {{item}}----{{key}}
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        userInfo: {
          name: "shaka",
          age: 18,
          gender: "男"
        }
      }
    })
  </script>
</body>

在这里插入图片描述
我们可以发现这个key就是键名,那么item就是键值
当然,我们还可以加index
在这里插入图片描述

Vue中的set方法

在这里插入图片描述
这是对象的set方法
我们要是操作数组对象的话
我们可以用push、pop、shift等数组的api,这里也可以用set
首先,如果我们直接改的话

<body>
  <div id="app">
    <div v-for="(item,index) of userInfo">
      {{item}}
    </div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        userInfo: [1, 2, 3, 4]
      }
    })
  </script>
</body>

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

Vue中的事件绑定

<body>
  <div id="app">
    <button @click="hand">Button</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      methods: {
        hand: function (e) {
          console.log(e);
        }
      },
    })
  </script>
</body>

在这里插入图片描述
这是我们正常的写法
在这里插入图片描述
在这里插入图片描述
那么我们应该怎么办呢
在这里插入图片描述
这种写法有什么好处呢
在这里插入图片描述
它可以让我们传递一些额外的参数
这就是绑定事件的两种写法
看下面的带代码

<body>
  <div id="app">
    <form action="/abc" @click="hand">
      <input type="submit">
    </form>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      methods: {
        hand: function (e) {
          console.log(e);
        }
      },
    })
  </script>
</body>

在这里插入图片描述
在这里插入图片描述
那么,我们想阻止表单提交的默认行为,怎么去做
在这里插入图片描述
这么写未免麻烦,简写形式呢
在这里插入图片描述
这个prevent叫做事件修饰符,同样的修饰符还有好几个
在这里插入图片描述
在这里插入图片描述

<body>
  <div id="app">
    <input @keydown="hand">
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      methods: {
        hand: function (e) {
          console.log(e.target.value);
        }
      },
    })
  </script>
</body>

在这里插入图片描述
我们想输入回车的时候再输出到控制台,这个时候就需要按键修饰符了
在这里插入图片描述
还有系统修饰符需要注意,比如ctrl,它代表你必须一边按住ctrl一边输入才行
还有监测鼠标右键:@click.right 就可以了

<body>
  <div id="app">
    <div @click.right="hand">click</div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      methods: {
        hand: function (e) {
          console.log("right");
        }
      },
    })
  </script>
</body>

Vue中的表单绑定

核心就是v-model指令,之前介绍双向绑定的时候已经给大家介绍过了,这里是想说,不止可以应用在input框中,还可以应用在别的地方
textarea,checkbox都可以应用

<body>
  <div id="app">
    <select v-model="value">
      <option disabled>--请选择--</option>
      <option>A</option>
      <option>B</option>
      <option>C</option>
    </select>
    {{value}}
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        value: ""
      }
    })
  </script>
</body>

需要注意的是,我们给option加value呢
在这里插入图片描述
这样页面会优先显示value的值,如果没有value才会显示text的内容
当然,v-model也有修饰符,比如lazy(失去焦点发生反应),number
就是input框有一个问题,就是我们不管输入字符串还是数字,它都会返给我们一个字符串

<body>
  <div id="app">
    <input type="text" v-model="value">
    {{value}}
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        value: ""
      },
      watch: {
        value: function () {
          console.log(typeof this.value);
        }
      }
    })
  </script>
</body>

在这里插入图片描述
在这里插入图片描述
还有一个trim,就是去空格,这个就不多说了

Vue组件

Vue组件的细节点

在这里插入图片描述
这是我们正常去写一个表格
那我们想用组件去写呢

<body>
  <div id="app">
    <table>
      <tbody>
        <row></row>
        <row></row>
        <row></row>
      </tbody>
    </table>
  </div>
  <script>
    Vue.component('row', {
      template: '<tr><td>this is row</td></tr>'
    })
    var vm = new Vue({
      el: '#app'
    })
  </script>
</body>

但是会出bug,违背了我们h5的规范
在这里插入图片描述
那么我们就会用is来解决这个问题

<body>
  <div id="app">
    <table>
      <tbody>
        <tr is="row"></tr>
        <tr is="row"></tr>
        <tr is="row"></tr>
      </tbody>
    </table>
  </div>
  <script>
    Vue.component('row', {
      template: '<tr><td>this is row</td></tr>'
    })
    var vm = new Vue({
      el: '#app'
    })
  </script>

用is属性,不会出bug
既能保证tr里是我们的组件,又能保证它符合h5的编码规范,不会出bug

在这里插入图片描述
同理,ul,li也是这个道理
我现在想对代码进行改进
在这里插入图片描述
这样可以吗
在这里插入图片描述
答案是不可以的,这是为什么
这里要说的是,我们虽然在vm(根组件)里面的data可以写成对象形式,但是在非根组件(也就是子组件)中是不可以的,我们要求必须是个函数
在这里插入图片描述
这样写就不会发生问题了
这里就要说,为什么要是个函数了

ref:用来操作DOM

<body>
  <div id="app">
    <div ref="hello" @click="hand">hello world</div>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      methods: {
        hand: function () {
          console.log(this.$refs.hello);
        }
      },
    })
  </script>
</body>

在这里插入图片描述

父子组件的数据传递

首先,我们先注册局部组件

<body>
  <div id="app">
    <counter></counter>
    <counter></counter>
  </div>
  <script>
    //局部组件
    var counter = {
      template: '<div>0</div>'
    }
    var vm = new Vue({
      el: '#app',
      components: {
        counter: counter
      }
    })
  </script>
</body>

父组件通过属性的形式向子组件传递数据

<body>
  <div id="app">
    <counter :count="0"></counter>
    <counter :count="1"></counter>
  </div>
  <script>
    //局部组件
    var counter = {
      props: ['count'],
      template: '<div>{{count}}</div>'
    }
    var vm = new Vue({
      el: '#app',
      components: {
        counter: counter
      }
    })
  </script>
</body>

在这里插入图片描述
我现在想做这么一个效果,就是点击后自加1

<body>
  <div id="app">
    <counter :count="0"></counter>
    <counter :count="1"></counter>
  </div>
  <script>
    //局部组件
    var counter = {
      props: ['count'],
      template: '<div @click="hand">{{count}}</div>',
      methods: {
        hand: function () {
          this.count++
        }
      },
    }
    var vm = new Vue({
      el: '#app',
      components: {
        counter: counter
      }
    })
  </script>
</body>

这样可以实现,但是这样是不被允许的
这是一个在Vue里面被称作单项数据流的东西,就是,我们只能用父组件传递过来的值,而不能去修改它
在这里插入图片描述

所以我们采用这种方式,组件中的this,永远指向自身。即便是父组件传递过来的数据,vue底层也帮你都转到自组件的属性上去了。
那我们,子组件向父组件传值呢:自定义事件

<body>
  <div id="app">
    <counter :count="3" @inc="handleIncrease"></counter>
    <counter :count="2" @inc="handleIncrease"></counter>
    <div>{{total}}</div>
  </div>
  <script>
    //局部组件
    var counter = {
      props: ['count'],
      data() {
        return {
          number: this.count
        }
      },
      template: '<div @click="hand">{{number}}</div>',
      methods: {
        hand: function () {
          this.number = this.number + 2
          this.$emit('inc', 2)//可以传递参数
        }
      },
    }
    var vm = new Vue({
      el: '#app',
      data: {
        total: 5
      },
      components: {
        counter: counter
      },
      methods: {
        handleIncrease: function (step) {//step就是接受inc的参数
          // alert(step)
          this.total += step
        }
      }
    })
  </script>
</body>

组件参数校验与非props特性

<body>
  <div id="app">
    <child content="hello world"></child>
  </div>
  <script>
    Vue.component('child', {
      props: ['content'],
      template: '<div>{{content}}</div>'
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>


这是一段正常的父传值给子的代码,我们现在有一个需求,子在接受父的数据的时候,我们想对其进行约束,我们应该怎么去做呢
比如,我们只接受字符串
在这里插入图片描述
那我们的需求是,字符串或者数字呢
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
那么什么是非props特性,就是子组件没人接收数据
在这里插入图片描述

给组件绑定原生事件

<body>
  <div id="app">
    <child @click="hand"></child>
  </div>
  <script>
    Vue.component('child', {
      template: '<div>Child</div>'
    })
    var vm = new Vue({
      el: '#app',
      methods: {
        hand: function () {
          alert('Jin❤Lin')
        }
      },
    })
  </script>
</body>

我们发现它是无法生效的
在这里插入图片描述
这样才属于我们绑定了原生的事件,可以触发
在这里插入图片描述
那如果我想要触发自定义的事件呢
在这里插入图片描述
那如果我们不想这么写,我们可以用一个native修饰符

<body>
  <div id="app">
    <child @click.native="hand"></child>
  </div>
  <script>
    Vue.component('child', {
      template: '<div>Child</div>',
    })
    var vm = new Vue({
      el: '#app',
      methods: {
        hand: function () {
          alert('Jin❤Lin')
        }
      },
    })
  </script>
</body>

也可以达到效果

非父子组件间的传值

我们知道组件的拆分,积类似于原生的div

在这里插入图片描述
我们知道了父子组件之间的传递,但是如果我们不是父子之间的传递呢,比如兄弟之间的传值,隔辈之间的传值呢
我们的确可以通过父子传值的方式,一直反复下去,可以达到效果,但是这样太费劲了
那么怎么做到简单的方式呢?

vuex和总线的机制(发布订阅模式、观察者模式)

vuex在后面单说,这里先介绍总线

<body>
  <div id="app">
    <child content="Jin"></child>
    <child content="Lin"></child>
  </div>
  <script>
    Vue.prototype.bus = new Vue()
    Vue.component('child', {
      props: {
        content: String
      },
      template: '<div @click="hand">{{content}}</div>',
      methods: {
        hand: function () {
          this.bus.$emit('change', this.content)
        }
      },
      mounted: function () {
        var this_ = this
        this.bus.$on('change', function (msg) {
          this_.content = msg
        })
      }
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>

但是我们之前说过了,我们Vue有单向数据流,不能直接修改根组件传过来的值
所以要进行改进
在这里插入图片描述
这样就没问题了

在Vue中使用插槽

<body>
  <div id="app">
    <child></child>
  </div>
  <script>
    Vue.component('child', {
      template: '<div><p>hello</p></div>',
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>

这是一段正常的代码,相信大家到这里,是没什么问题了
页面显示如下
在这里插入图片描述
现在,我们对代码进行更改
在这里插入图片描述
在这里插入图片描述
我们发现页面出现了一些问题,我们发现父组件传过来的p标签被转义了,那么我们不想让它转义怎么办
我们进行如下的改进就可以了
在这里插入图片描述
在这里插入图片描述
所以,显然这么写是有问题的
同时,如果用这种方法传的值过多的话,代码就不好看了
这样,我们就需要一个新概念了:插槽(这里是默认插槽)

<body>
  <div id="app">
    <child>
      <p>Lin</p>
    </child>
  </div>
  <script>
    Vue.component('child', {
      props: ['content'],
      template: `<div>
                     <p>Jin</p>
                    <slot></slot>
                </div>`,
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>

也就是说,通过插槽,我们可以更方便地向自组件传递DOM元素
同时,slot还可以设置默认值
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
我们现在看这段代码
在这里插入图片描述

这是它的页面效果
那么,我现在不想要这个效果呢,想一一对应着来呢
这就需要具名插槽了

<body>
  <div id="app">
    <child>
      <div class="header" slot="header">header</div>
      <div class="footer" slot="footer">footer</div>
    </child>
  </div>
  <script>
    Vue.component('child', {
      template: `<div>
                    <slot name="header"></slot>
                    <slot name="footer"></slot>
                </div>`,
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>

在这里插入图片描述

这样就OK了
这样的好处是,如果我们插入多个DOM结构
具名插槽也可以有默认值,这里不演示了

Vue中的作用域插槽

<body>
  <div id="app">
    <child>
    </child>
  </div>
  <script>
    Vue.component('child', {
      data() {
        return {
          list: [1, 2, 3, 4]
        }
      },
      template: `<div>
                    <ul>
                      <li v-for="item of list">
                        {{item}}
                      </li>  
                    </ul>
                </div>`,
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>

在这里插入图片描述
现在是这个效果

当一些时候,DOM结构不确定的时候,我们的DOM结构想从外部传递进来的时候,就需要作用域插槽了

<body>
  <div id="app">
    <child>
      <template slot-scope="props">
        <li>{{props.item}}</li>
      </template>
    </child>
  </div>
  <script>
    Vue.component('child', {
      data: function () {
        return {
          list: [1, 2, 3, 4]
        }
      },
      template: `<div>
                  <ul>
                    <slot>
                      v-for="item of list"
                      :item=item
                    </slot>
                  </ul>
                </div>`,
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>

这里写成:item = item。其中第一个item自定义名字,第二个是for循环中的item。这里是插槽中有数据需要向外暴露出去,由外部决定的渲染样式,必须通过属性绑定的形式。也可以近似的理解为父子组件的传值,因为template的相当于是去填充插槽的内容,这和子组件去填充父组件也是异曲同工之妙。props属性绑定的形式传值,都是近似一种父子关系。

动态组件与v-once指令

<body>
  <div id="app">
    <child-one></child-one>
    <child-two></child-two>
  </div>
  <script>
    Vue.component('child-one', {
      template: '<div>child-one</div>'
    })
    Vue.component('child-two', {
      template: '<div>child-two</div>'
    })
    var vm = new Vue({
      el: '#app',
    })
  </script>
</body>

这是我的页面效果
在这里插入图片描述
现在我要做一个功能
在这里插入图片描述
当我点击这个按钮的时候,一会child-one显示,一会child-two显示
大家可能首先会想到条件渲染(v-if),代码如下

<body>
  <div id="app">
    <child-one v-if="type === 'child-one'"></child-one>
    <child-two v-if="type === 'child-two'"></child-two>
    <button @click="hand">Change</button>
  </div>
  <script>
    Vue.component('child-one', {
      template: '<div>child-one</div>'
    })
    Vue.component('child-two', {
      template: '<div>child-two</div>'
    })
    var vm = new Vue({
      el: '#app',
      data: {
        type: 'child-one'
      },
      methods: {
        hand: function () {
          this.type = this.type === 'child-one' ? 'child-two' : 'child-one'
        }
      },
    })
  </script>
</body>

是可以做到的,没有问题
现在我们还可以通过动态组件达到这种效果
在这里插入图片描述
我们发现,效果是一样的
就是动态组件会通过is属性里面的内容,加载不同的组件

v-once表示模板只渲染一次

在这里插入图片描述

vue中的动画特效

Vue中CSS动画原理

<body>
  <div id="app">
    <div v-if="show">Jin ❤ Lin</div>
    <button @click="hand">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        hand: function () {
          this.show = !this.show
        }
      },
    })
  </script>
</body>

现在,我有这么一段代码,我现在想给它加一个动画效果
那我们就要在div外层包一个transition标签
在这里插入图片描述
但是我们加完后,发现效果并没有出现
我们加了这个代码之后就会有效果了,这是因为什么
注意这里的fade是我们自己起的名字,默认是v-enter
在这里插入图片描述
原理图
在这里插入图片描述
它的原理就是给我们div动态添加了一个class
这都是我们显示时候的效果,那么隐藏时候的效果呢
在这里插入图片描述
这样就OK啦
在这里插入图片描述
其实是同样的道理
当然,这是可以简写,看着舒服一些的
在这里插入图片描述

在Vue中使用Animate.css库

我们先在Vue中使用keyframs创建一个动画效果
在这里插入图片描述
那我们可以不用默认的名字,自己起名字吗

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


<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
  <style>
    @keyframes bounce-in {
      0% {
        transform: scale(0);
      }


      50% {
        transform: scale(1.5);
      }


      100% {
        transform: scale(1);
      }
    }


    .active {
      transform-origin: left center;
      animation: bounce-in 1s;
    }


    .leave {
      transform-origin: left center;
      animation: bounce-in 1s reverse;
    }
  </style>
</head>


<body>
  <div id="app">
    <transition name="fade" enter-active-class="active" leave-active-class="leave">
      <div v-if="show">Jin ❤ Lin</div>
    </transition>
    <button @click="hand">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        hand: function () {
          this.show = !this.show
        }
      },
    })
  </script>
</body>


</html>

在这里插入图片描述
现在我们要用animate.css来做动画的效果(详情看官网文档)
在这里插入图片描述

具体的animate.css看官网的api文档就行

在Vue中同时使用过渡和动画

我们想让页面的内容第一次出现的时候也有动画效果
在这里插入图片描述
这样就可以了
现在我有另一个需求,我要求不仅要有css3的动画效果,还有一个过渡的动画效果呢
在这里插入图片描述
先加上这两个字段,然后我们继续去写style样式

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./animate.min.css">
  <script src="./vue.min.js"></script>
  <style>
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
    }


    .fade-enter-active,
    .fade-leave-active {
      transition: opacity 3s;
    }
  </style>
</head>


<body>
  <div id="app">
    <transition name="fade" appear appear-active-class="animate__animated animate__swing"
      enter-active-class=" animate__animated animate__shakeX fade-enter-active"
      leave-active-class="animate__animated animate__swing fade-leave-active">
      <div v-if="show">Jin ❤ Lin</div>
    </transition>
    <button @click="hand">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        hand: function () {
          this.show = !this.show
        }
      },
    })
  </script>
</body>
</html>

那如果重复了,我们以那个为准呢,是以keyframe,还是以transition为准
以transition为准
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Vue中的Js动画与Velocity.js的结合

我们在此之前都是通过CSS实现一些动画效果,现在我们要通过JS来实现一些动画效果
Vue之中,提供给了我们很多关于动画的钩子
在这里插入图片描述
这个el指的就是动画包裹的div标签

当@before-enter这个事件触发之后,就会进入真正的动画时间,也就会进入到另一个事件@enter
在这里插入图片描述

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


<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
  <style>
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
    }

    .fade-enter-active,
    .fade-leave-active {
      transition: opacity 3s;
    }
  </style>
</head>


<body>
  <div id="app">
    <transition name="fade" @before-enter="handBefore" @enter="handEnter">
      <div v-if="show">Jin ❤ Lin</div>
    </transition>
    <button @click="hand">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        hand: function () {
          this.show = !this.show
        },
        handBefore: function (el) {
          el.style.color = 'red'
        },
        handEnter: function (el, done) {
          setTimeout(() => {
            el.style.color = 'yellow'
          }, 2000);
        }
      },
    })
  </script>
</body>


</html>

在这里插入图片描述
在这里插入图片描述
就是告诉Vue一声,诶,这个动画我执行完了
当done这个函数执行完,我们又会进入到一个事件@after-enter

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
  <style>
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
    }

    .fade-enter-active,
    .fade-leave-active {
      transition: opacity 3s;
    }
  </style>
</head>

<body>
  <div id="app">
    <transition name="fade" @before-enter="handBefore" @enter="handEnter" @after-enter="handAfter">
      <div v-if="show">Jin ❤ Lin</div>
    </transition>
    <button @click="hand">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        hand: function () {
          this.show = !this.show
        },
        handBefore: function (el) {
          el.style.color = 'red'
        },
        handEnter: function (el, done) {
          setTimeout(() => {
            el.style.color = 'yellow'
          }, 2000);
          setTimeout(() => {
            done()
          }, 4000);
        },
        handAfter: function (el) {
          el.style.color = '#000'
        }
      },
    })
  </script>
</body>

</html>

在这里插入图片描述
那么我除了入场动画,还有对应的出场动画,就是把对应的enter改成leave就可以了,这里就不过多叙述了

加入Velocity.js怎么去写呢

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./vue.min.js"></script>
  <script src="http://files.cnblogs.com/files/xiaohuochai/velocity.min.js"></script>
</head>

<body>
  <div id="app">
    <transition name="fade" @before-enter="handBefore" @enter="handEnter" @after-enter="handAfter">
      <div v-show="show">Jin ❤ Lin</div>
    </transition>
    <button @click="hand">切换</button>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        show: true
      },
      methods: {
        hand: function () {
          this.show = !this.show
        },
        handBefore: function (el) {
          el.style.opacity = 0
        },
        handEnter: function (el, done) {
          Velocity(el, { opacity: 1 }, { duration: 1000 })
        },
        handAfter: function (el) {
        }
      },
    })
  </script>
</body>

</html>

在这里插入图片描述
然后我们需要一个配置项,来告诉Vue我们的动画完成了
在这里插入图片描述
在这里插入图片描述
我们来试验一下
在这里插入图片描述
OK,这样就完成了

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/79708.html

(0)

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!