16-Vue-A


typora-copy-images-to: media

入门

课程介绍

前5天: 都在学习Vue基本的语法和概念;打包工具 Webpack , Gulp
后5天: 以项目驱动教学

什么是Vue.js

  • Vue.js 是目前最火的一个前端框架,React是最流行的一个前端框架(React除了开发网站,还可以开发手机App, Vue语法也是可以用于进行手机App开发的,需要借助于Weex)

  • Vue.js 是前端的主流框架之一,和Angular.js、React.js 一起,并称前端三大主流框架!

  • Vue.js 是一套构建用户界面的框架,只关注视图层,它不仅易于上手,还便于与第三方库或既有项目整合。(Vue有配套的第三方类库,可以整合起来做大型项目的开发)

  • 前端的主要工作?主要负责MVC中的V这一层;主要工作就是和界面打交道,来制作前端页面效果

为什么要学习流行框架

  • 企业为了提高开发效率:在企业中,时间就是效率,效率就是金钱;

    企业中,使用框架,能够提高开发的效率;

  • 提高开发效率的发展历程:原生JS -> Jquery之类的类库 -> 前端模板引擎 -> Angular.js / Vue.js(能够帮助我们减少不必要的DOM操作提高渲染效率双向数据绑定的概念【通过框架提供的指令,我们前端程序员只需要关心数据的业务逻辑,不再关心DOM是如何渲染的了】)

  • 在Vue中,一个核心的概念,就是让用户不再操作DOM元素,解放了用户的双手,让程序员可以更多的时间去关注业务逻辑;

  • 增强自己就业时候的竞争力

    人无我有,人有我优

    你平时不忙的时候,都在干嘛?

框架和库的区别

  • 框架:是一套完整的解决方案;对项目的侵入性较大,项目如果需要更换框架,则需要重新架构整个项目。

    • node 中的 express;
  • 库(插件):提供某一个小功能,对项目的侵入性较小,如果某个库无法完成某些需求,可以很容易切换到其它库实现需求。

    1. 从Jquery 切换到 Zepto
    2. 从 EJS 切换到 art-template

Node(后端)中的 MVC 与 前端中的 MVVM 之间的区别

  • MVC 是后端的分层开发概念;
  • MVVM是前端视图层的概念,主要关注于 视图层分离,也就是说:MVVM把前端的视图层,分为了 三部分 Model, View , VM ViewModel
  • 为什么有了MVC还要有MVVM

Vue 基本代码 和 MVVM 之间的对应关系

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <!-- 1. 导入Vue的包 -->
  <script src="./lib/vue-2.4.0.js"></script>
</head>
<body>
  <!-- 将来 new 的Vue实例,会控制这个 元素中的所有内容 -->
  <!-- 注意1:Vue 实例所控制的这个元素区域,就是我们的 V  -->
  <div id="app">
    <p>{{ msg }}</p>
  </div>

  <script>
    // 2. 创建一个Vue的实例
    // 当我们导入包之后,在浏览器的内存中,就多了一个 Vue 构造函数
    //  注意2:我们 new 出来的这个 vm 对象,就是我们 MVVM中的 VM调度者
    var vm = new Vue({
      el: '#app',  // 表示,当前我们 new 的这个 Vue 实例,要控制页面上的哪个区域
      // 注意3:这里的 data 就是 MVVM中的 M,专门用来保存 每个页面的数据的
      // data 属性中,存放的是 el 中要用到的数据
      data: {
        msg: '欢迎学习Vue' // 通过 Vue 提供的指令,很方便的就能把数据渲染到页面上,程序员不再手动操作DOM元素了【前端的Vue之类的框架,不提倡我们去手动操作DOM元素了】
      }
    })
  </script>
</body>
</html>

Vue 指令

Vue基本的代码结构、插值表达式、Vue指令:v-cloak

  • 使用 v-cloak 能够解决 插值表达式闪烁的问题
  <style>
    [v-cloak] {
      display: none;
    }
  </style>
...
  <div id="app">
    <!-- 使用 v-cloak 能够解决 插值表达式闪烁的问题 -->
    <p v-cloak>{{ msg }}</p>
  </div>
...
  <script src="./lib/vue-2.4.0.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        msg: '123'
      }
    })
  </script>

Vue指令:v-text和v-html

  • v-text 、 v-cloak 和 v-html 的区别
    • 默认v-text、v-html没有闪烁问题
    • v-text、v-html会覆盖元素中原本的内容,但是v-cloak只会替换自己的占位符,不会把整个元素的内容清空
    • v-text 、v-cloak不能在浏览器中解析html标签,v-html可以解析html标签(如<h1>...)
  <div id="app">
    <h3 v-text="msg"></h3>
    
    <div v-cloak>{{ msg2 }}</div>
    <div v-text="msg2"></div>
    <div v-html="msg2"></div>
  </div>
...
  <script src="./lib/vue-2.4.0.js"></script>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        msg: '123',
        msg2: '<h1>我是h1标题</h1>'
      }
    })
  </script>

Vue指令:v-bind的三种用法

  1. v-bind: 是Vue中提供的用于属性绑定的指令
  2. 直接使用指令v-bind
  3. 使用简化指令:
  4. v-bind中可以写合法的js表达式。所以在绑定的时候,可以拼接绑定内容::title="btnTitle + ', 这是追加的内容'"
<div id="app">
    <input type="button" value="按钮1" v-bind:title="mytitle">
    <input type="button" value="按钮2" :title="mytitle + ',这是后面添加的内容'">
    <input type="button" value="按钮3" v-bind:title="mytitle + ',这是后面添加的内容'">
</div>
...
<script>
var vm = new Vue({
    el: '#app',
    data: {
        mytitle: '这是自定义的title属性'
    }
})
</script>

Vue指令:v-on和跑马灯效果

v-on

  • v-on:事件绑定指令
  • v-on的缩写:@
<div id="app">
    <input type="button" value="按钮" v-on:click="show">
    <input type="button" value="按钮" v-on:mouseover="show">
  <input type="button" value="按钮" @click="show">
</div>
...
<script>
var vm = new Vue({
    el: '#app',
    // 这个 methods属性中定义了当前Vue实例所有可用的方法
    methods: {
        show: function(){
            alert('Hello');
        }
    }
})
</script>

案例:跑马灯效果

分析:

  1. 给 【浪起来】 按钮,绑定一个点击事件 v-on @
  2. 在按钮的事件处理函数中,写相关的业务逻辑代码:拿到 msg字符串,然后 调用 字符串的 substring来进行字符串的截取操作,把第一个字符截取出来,放到最后一个位置即可
  3. 为了实现点击下按钮,自动截取的功能,需要把 2 步骤中的代码,放到一个定时器中去
  1. HTML结构:
<div id="app">
    <p>{{info}}</p>
    <input type="button" value="开启" v-on:click="go">
    <input type="button" value="停止" v-on:click="stop">
</div>
  1. Vue实例:
    • 注意1:在vm实例中,如果想要获取data上的数据或调用methods中的方法,必须通过this.数据属性名this.方法名 来进行访问,这里的this表示new出来的vm对象
    • 注意2:vm实例会监听自己身上data中所有数据的改变,只要数据一发生变化就会将最新的数据同步到页面中去
    • 注意3:ES6中的箭头函数内部的this指向的是其外部的对象
	// 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        info: '猥琐发育,别浪~!',
        intervalId: null // 在data上定义定时器id
      },
      methods: {
        go() {
          // 如果当前有定时器在运行,则直接return
          if (this.intervalId != null) {
            return;
          }
          // 开始定时器
          this.intervalId = setInterval(() => {
            this.info = this.info.substring(1) + this.info.substring(0, 1);
          }, 500);
        },
        stop() {
          clearInterval(this.intervalId);
          this.intervalId = null;
        }
      }
    });

事件修饰符

  • .stop 阻止冒泡,调用 event.stopPropagation()
  • .prevent 阻止默认事件, 调用 event.preventDefault()
  • .capture 添加事件侦听器时使用事件捕获模式
  • .self 只当事件在该元素本身(比如不是子元素)触发时触发回调
  • .{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调
  • .once 事件只触发一次

注意1:事件修饰符可以串联着写:@click.prevent.once = 'handle' (.prevent也只会阻止一次默认行为)

注意2:.stop 和 .self 的区别

  • .stop是真正会阻止冒泡的行为,只触发被点击元素上的事件,不会向上(外)扩散
  • .self 只会阻止自己身上冒泡行为的触发,并不会真正阻止冒泡的行为
  <div id="app">
    <!-- 使用  .stop  阻止冒泡 -->
    <div class="inner" @click="div1Handler">
      <input type="button" value="戳他" @click.stop="btnHandler">
    </div>

    <!-- 使用 .prevent 阻止默认行为 -->
    <a href="http://www.baidu.com" @click.prevent="linkClick">有问题,先去百度</a>

    <!-- 使用  .capture 实现捕获触发事件的机制 -->
    <div class="inner" @click.capture="div1Handler">
      <input type="button" value="戳他" @click="btnHandler">
    </div>

    <!-- 使用 .self 实现只有点击当前元素时候,才会触发事件处理函数 -->
    <div class="inner" @click.self="div1Handler">
      <input type="button" value="戳他" @click="btnHandler">
    </div>

    <!-- 使用 .once 只触发一次事件处理函数 -->
    <a href="http://www.baidu.com" @click.prevent.once="linkClick">有问题,先去百度</a>


    <!-- 演示: .stop 和 .self 的区别 -->
    
    <!-- .stop是真正会阻止冒泡的行为,只触发被点击元素上的事件,不会向上(外)扩散 -->
    <div class="outer" @click="div2Handler">
      <div class="inner" @click="div1Handler">
        <input type="button" value="戳他" @click.stop="btnHandler">
      </div>
    </div>
    
    <!-- .self 只会阻止自己身上冒泡行为的触发,并不会真正阻止冒泡的行为 -->
    <div class="outer" @click="div2Handler">
      <div class="inner" @click.self="div1Handler">
        <input type="button" value="戳他" @click="btnHandler">
      </div>
    </div>
  </div>
...
  <script>
    var vm = new Vue({
      el: '#app',
      data: {},
      methods: {
        div1Handler() {
          console.log('这是触发了 inner div 的点击事件')
        },
        btnHandler() {
          console.log('这是触发了 btn 按钮 的点击事件')
        },
        linkClick() {
          console.log('触发了连接的点击事件')
        },
        div2Handler() {
          console.log('这是触发了 outer div 的点击事件')
        }
      }
    });
  </script>

Vue指令:v-model和双向数据绑定

  • v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定
  • v-model 指令可以实现表单元素和 Model 中数据的双向数据绑定
  • 注意:v-model 指令只能运用在表单元素中
  <div id="app">
    <h4>{{ msg }}</h4>

    <!-- v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定  -->
    <!-- <input type="text" v-bind:value="msg" style="100%;"> -->

    <!-- 使用  v-model 指令,可以实现 表单元素和 Model 中数据的双向数据绑定 -->
    <!-- 注意: v-model 只能运用在 表单元素中 -->
    <!-- input(radio, text, address, email....)   select    checkbox   textarea   -->
    <input type="text" style="100%;" v-model="msg">
  </div>
...
  <script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        msg: '大家都是好学生,爱敲代码,爱学习,爱思考,简直是完美,没瑕疵!'
      },
      methods: {
      }
    });
  </script>

案例:简易计算器案例

  1. HTML 代码结构
  <div id="app">
    <input type="text" v-model="n1">
    <select v-model="opt">
      <option value="0">+</option>
      <option value="1">-</option>
      <option value="2">*</option>
      <option value="3">÷</option>
    </select>
    <input type="text" v-model="n2">
    <input type="button" value="=" v-on:click="getResult">
    <input type="text" v-model="result">
  </div>
  1. Vue实例代码:
	// 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        n1: 0,
        n2: 0,
        result: 0,
        opt: '0'
      },
      methods: {
        getResult() {
          switch (this.opt) {
            case '0':
              this.result = parseInt(this.n1) + parseInt(this.n2);
              break;
            case '1':
              this.result = parseInt(this.n1) - parseInt(this.n2);
              break;
            case '2':
              this.result = parseInt(this.n1) * parseInt(this.n2);
              break;
            case '3':
              this.result = parseInt(this.n1) / parseInt(this.n2);
              break;
          }
        }
      }
    });

在Vue中使用样式

使用class样式

注意:这里的class需要使用 v-bind 做属性绑定

  1. 数组
<h1 :class="['red', 'thin']">这是一个邪恶的H1</h1>
  1. 数组中使用三元表达式
<h1 :class="['red', 'thin', isactive?'active':'']">这是一个邪恶的H1</h1>
  1. 数组中嵌套对象
<h1 :class="['red', 'thin', {'active': isactive}]">这是一个邪恶的H1</h1>
  1. 直接使用对象
<h1 :class="{red:true, italic:true, active:true, thin:true}">这是一个邪恶的H1</h1>

使用内联样式

  1. 直接在元素上通过 :style 的形式,书写样式对象
<h1 :style="{color: 'red', 'font-size': '40px'}">这是一个善良的H1</h1>
  1. 将样式对象,定义到 data 中,并直接引用到 :style
<!-- 在元素中,通过属性绑定的形式,将样式对象应用到元素中: -->
<h1 :style="h1StyleObj">这是一个善良的H1</h1>

<!-- 在data上定义样式: -->
data: {
        h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' }
}
  1. :style 中通过数组,引用多个 data 上的样式对象
<!-- 在元素中,通过属性绑定的形式,将样式对象应用到元素中: -->
<h1 :style="[h1StyleObj, h1StyleObj2]">这是一个善良的H1</h1>

<!-- 在data上定义样式: -->
data: {
        h1StyleObj: { color: 'red', 'font-size': '40px', 'font-weight': '200' },
        h1StyleObj2: { fontStyle: 'italic' }
}

Vue指令:v-for和key属性

  1. 迭代数组(普通数组、对象数组)
<!-- 只需要值item -->
<ul>
  <li v-for="item in list">姓名:{{item.name}} --- ID:{{item.id}}</li>
</ul>
<!-- 需要索引i,i从0开始 -->
<ul>
  <li v-for="(item, i) in list">索引:{{i}} --- 姓名:{{item.name}} --- ID:{{item.id}}</li>
</ul>
...
data: {
  list: [
    { id: 1, name: 'zs1' },
    { id: 2, name: 'zs2' },
    { id: 3, name: 'zs3' },
    { id: 4, name: 'zs4' }
  ]
}
  1. 迭代对象中的属性
    • 在遍历对象身上的键值对时,除了有val 、key,在第三个位置还有一个索引 i
	<!-- 循环遍历对象身上的属性 -->
	<div v-for="(val, key) in user">值:{{val}} --- 键:{{key}}</div>
    <div v-for="(val, key, i) in user">值:{{val}} --- 键:{{key}} --- 索引:{{i}}</div>
...
    data: {
      user: {
        id: 1,
        name: '托尼·屎大颗',
        gender: '男'
      }
    }
  1. 迭代数字(i 起始从1开始)
<p v-for="i in 10">这是第 {{i}} 个P标签</p>
  1. v-for中使用key的注意事项

    • 2.2.0+ 的版本里,当在组件中使用 v-for 时,key 是必须的。
    • v-for循环的时候,key 属性只能使用number或string类型
    • key 在使用的时候,必须使用v-bind 属性绑定的形式指定 key 的值

    当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用 “就地复用” 策略。如果数据项的顺序被改变,Vue将不是移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。

    为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性。

<div id="app"> 
    <div>
      <label>Id:<input type="text" v-model="id"></label>
      <label>Name:<input type="text" v-model="name"></label>
      <input type="button" value="添加" @click="add">
    </div>
    <!-- 注意: v-for 循环的时候,key 属性只能使用 number获取string -->
    <!-- 注意: key 在使用的时候,必须使用 v-bind 属性绑定的形式,指定 key 的值 -->
    <!-- 在组件中,使用v-for循环的时候,或者在一些特殊情况中,如果 v-for 有问题,必须在使用v-for的同时,指定唯一的字符串/数字类型 :key 值 -->
    <p v-for="item in list" :key="item.id">
      <input type="checkbox">{{item.id}} --- {{item.name}}
    </p>
</div>
<script>
var vm = new Vue({
    el: '#app',
    data: {
    id: '',
    name: '',
    list: [
        { id: 1, name: '李斯' },
        { id: 2, name: '嬴政' }, 
        { id: 3, name: '赵高' },
        { id: 4, name: '韩非' },
        { id: 5, name: '荀子' }
    ]
    },
    methods: {
        add() { // 添加方法
            this.list.unshift({ id: this.id, name: this.name })
        }
    }
});
</script>


Vue指令:v-if和v-show,元素渲染和切换

  • v-if 的特点:每次都会重新删除或创建元素

  • v-show 的特点: 每次不会重新进行DOM的删除和创建操作,只是切换了元素的 display:none 样式

  • v-if 有较高的切换性能消耗

  • v-show 有较高的初始渲染消耗

  • 如果元素涉及到频繁的切换,最好不要使用 v-if, 而是推荐使用 v-show

  • 如果元素可能永远也不会被显示出来被用户看到,则推荐使用 v-if

  <div id="app">
    <input type="button" value="toggle" @click="flag=!flag">
    <h3 v-if="flag">这是用v-if控制的元素</h3>
    <h3 v-show="flag">这是用v-show控制的元素</h3>
  </div>
  <script>
    var vm = new Vue({
      el: '#app',
      data: {
        flag: false
      }
    });
  </script>

案例:品牌管理

  • 前端HTML + Vue代码
    <div class="panel panel-primary">
      <div class="panel-heading">
        <h3 class="panel-title">添加品牌</h3>
      </div>
      <div class="panel-body form-inline">
        <label>
          Id:
          <input type="text" class="form-control" v-model="id">
        </label>
        <label>
          Name:
          <input type="text" class="form-control" v-model="name">
        </label>
        <!-- 在Vue中,使用事件绑定机制,为元素指定处理函数的时候,如果加了小括号,就可以给函数传参了 -->
        <input type="button" value="添加" class="btn btn-primary" @click="add()">
        <label>
          搜索名称关键字:
          <input type="text" class="form-control" v-model="keywords">
        </label>
      </div>
    </div>

    <table class="table table-bordered table-hover table-striped">
      <thead>
        <tr>
          <th>Id</th>
          <th>Name</th>
          <th>Ctime</th>
          <th>Operation</th>
        </tr>
      </thead>
      <tbody>
        <!-- 之前, v-for 中的数据,都是直接从 data 上的list中直接渲染过来的 -->
        <!-- 现在, 我们自定义了一个 search 方法,同时,把 所有的关键字,通过传参的形式,传递给了 search 方法 -->
        <!-- 在 search 方法内部,通过 执行 for 循环, 把所有符合 搜索关键字的数据,保存到 一个新数组中,返回 -->
        <tr v-for="item in search(keywords)" :key="item.id">
          <td>{{ item.id }}</td>
          <td v-text="item.name"></td>
          <td>{{ item.ctime }}</td>
          <td>
            <a href="" @click.prevent="del(item.id)">删除</a>
          </td>
        </tr>
      </tbody>
    </table>
      data: {
        id: '',
        name: '',
        keywords: '', // 搜索的关键字
        list: [
          { id: 1, name: '奔驰', ctime: new Date() },
          { id: 2, name: '宝马', ctime: new Date() }
        ]
      },
  • 添加新品牌
        add() { // 添加的方法
          // 分析:
          // 1. 获取到 id 和 name ,直接从 data 上面获取 
          // 2. 组织出一个对象
          // 3. 把这个对象,调用 数组的 相关方法,添加到 当前 data 上的 list 中
          // 4. 注意:在Vue中,已经实现了数据的双向绑定,每当我们修改了 data 中的数据,Vue会默认监听到数据的改动,自动把最新的数据,应用到页面上;
          // 5. 当我们意识到上面的第四步的时候,就证明大家已经入门Vue了,我们更多的是在进行 VM中 Model 数据的操作,同时,在操作Model数据的时候,指定的业务逻辑操作;
          var car = { id: this.id, name: this.name, ctime: new Date() }
          this.list.push(car)
          this.id = this.name = ''
        }
  • 删除品牌
		del(id) { // 根据Id删除数据
          // 分析:
          // 1. 如何根据Id,找到要删除这一项的索引
          // 2. 如果找到索引了,直接调用 数组的 splice 方法

          /* this.list.some((item, i) => {
            if (item.id == id) {
              this.list.splice(i, 1)
              // 在 数组的 some 方法中,如果 return true,就会立即终止这个数组的后续循环
              return true;
            }
          }) */
          var index = this.list.findIndex(item => {
            if (item.id == id) {
              return true;
            }
          })
          this.list.splice(index, 1)
        }
  • 根据条件筛选品牌
  1. 基础方法

    <label>
        搜索名称关键字:
        <input type="text" class="form-control" v-model="keywords">
    </label>
    ...
    <!-- 之前 v-for 中的数据都是直接从 data 上的list中直接渲染过来的 -->
    <!-- 现在我们自定义了一个 search 方法,同时,把所有的关键字通过传参的形式传递给了 search 方法 -->
    <!-- 在 search 方法内部,通过执行 for 循环, 把所有符合搜索关键字的数据,保存到一个新数组中返回 -->
    <tr v-for="item in search(keywords)" :key="item.id">
        <td>{{ item.id }}</td>
        <td v-text="item.name"></td>
        <td>{{ item.ctime }}</td>
        <td>
            <a href="" @click.prevent="del(item.id)">删除</a>
        </td>
    </tr>
    ...
    <script>
        ...
        // 方法一
        search(keywords) { // 根据关键字,进行数据的搜索
            var newList = []
            this.list.forEach(item => {
                if (item.name.indexOf(keywords) != -1) {
                    newList.push(item)
                }
            })
            return newList
        }
    
        // --------------------------------------------
        // 方法二
        // 注意:forEach some filter findIndex 这些都属于数组的新方法,
        // 都会对数组中的每一项,进行遍历,执行相关的操作
        search(keywords) {
            return this.list.filter(item => {
                // 注意:ES6中,为字符串提供了一个新方法,叫做  String.prototype.includes('要包含的字符串')
                //  如果包含,则返回 true ,否则返回 false
                // if(item.name.indexOf(keywords) != -1)
                if (item.name.includes(keywords)) {
                    return item
                }
            })
        }
    </script>
    

  2. x 版本中的filterBy指令,在2.x中已经被废除:

    filterBy - 指令

    <tr v-for="item in list | filterBy searchName in 'name'">
      <td>{{item.id}}</td>
      <td>{{item.name}}</td>
      <td>{{item.ctime}}</td>
      <td>
        <a href="#" @click.prevent="del(item.id)">删除</a>
      </td>
    </tr>
    
  3. 在2.x版本中手动实现筛选的方式

    1. 筛选框绑定到 VM 实例中的 searchName 属性:

      <hr> 输入筛选名称:
      <input type="text" v-model="searchName">
      

    2. 在使用 v-for 指令循环每一行数据的时候,不再直接 item in list,而是 in 一个 过滤的methods 方法,同时,把过滤条件searchName传递进去:

      <tbody>
            <tr v-for="item in search(searchName)">
              <td>{{item.id}}</td>
              <td>{{item.name}}</td>
              <td>{{item.ctime}}</td>
              <td>
                <a href="#" @click.prevent="del(item.id)">删除</a>
              </td>
            </tr>
          </tbody>
      

    3. search 过滤方法中,使用 数组的 filter 方法进行过滤:

      search(name) {
        return this.list.filter(x => {
          return x.name.indexOf(name) != -1;
        });
      }
      

Vue调试工具vue-devtools的安装步骤和使用

Vue.js devtools - FQ安装方式 - 推荐

过滤器

概念:Vue.js 允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:mustache 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符指示;

  • 可以调用多个过滤器

    <td>{{item.ctime | dataFormat('yyyy-mm-dd') | test }}</td>
    
  • 过滤器可以传递多个参数

    <td>{{item.ctime | dataFormat('yyyy-mm-dd', year) }}</td>
    ...
    Vue.filter('dataFormat', function (input, pattern = '', year) {...}
    
  • 注意:当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:私有过滤器优先于全局过滤器被调用

私有过滤器

  1. 调用过滤器:
<td>{{item.ctime | dataFormat('yyyy-mm-dd')}}</td>
  1. 私有 filters 定义方式:
filters: { // 私有局部过滤器,只能在 当前 VM 对象所控制的 View 区域进行使用
    dataFormat(input, pattern = "") { // 在参数列表中 通过 pattern="" 来指定形参默认值,防止报错
      var dt = new Date(input);
      // 获取年月日
      var y = dt.getFullYear();
      var m = (dt.getMonth() + 1).toString().padStart(2, '0');
      var d = dt.getDate().toString().padStart(2, '0');

      // 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日
      // 否则,就返回  年-月-日 时:分:秒
      if (pattern.toLowerCase() === 'yyyy-mm-dd') {
        return `${y}-${m}-${d}`;
      } else {
        // 获取时分秒
        var hh = dt.getHours().toString().padStart(2, '0');
        var mm = dt.getMinutes().toString().padStart(2, '0');
        var ss = dt.getSeconds().toString().padStart(2, '0');

        return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
      }
    }
  }

使用ES6中的字符串新方法 String.prototype.padStart(maxLength, fillString='') 或 String.prototype.padEnd(maxLength, fillString='')来填充字符串;

全局过滤器

  • 全局过滤器,所有的VM实例都可以共享
// 定义一个全局过滤器,所有的VM实例都可以共享
Vue.filter('dataFormat', function (input, pattern = '') {
  var dt = new Date(input);
  // 获取年月日
  var y = dt.getFullYear();
  var m = (dt.getMonth() + 1).toString().padStart(2, '0');
  var d = dt.getDate().toString().padStart(2, '0');

  // 如果 传递进来的字符串类型,转为小写之后,等于 yyyy-mm-dd,那么就返回 年-月-日
  // 否则,就返回  年-月-日 时:分:秒
  if (pattern.toLowerCase() === 'yyyy-mm-dd') {
    return `${y}-${m}-${d}`;
  } else {
    // 获取时分秒
    var hh = dt.getHours().toString().padStart(2, '0');
    var mm = dt.getMinutes().toString().padStart(2, '0');
    var ss = dt.getSeconds().toString().padStart(2, '0');

    return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
  }
});

键盘修饰符

  • 1.x中自定义键盘修饰符【了解即可】
Vue.directive('on').keyCodes.f2 = 113;
  • 2.x中自定义键盘修饰符

    1. 通过Vue.config.keyCodes.名称 = 按键值来自定义案件修饰符的别名

      Vue.config.keyCodes.f2 = 113;
      

    2. 使用自定义的按键修饰符

      <input type="text" v-model="name" @keyup.f2="add">
      

自定义指令

  1. 自定义全局和私有的自定义指令:

    • 在定义的时候指令的名称前面不需要加v-前缀,但是在调用的时候,必须在指令名称前加上v-前缀来进行调用
    • 在每个函数中,第一个参数永远是el,表示被绑定了指令的那个元素,这个el参数是一个原生的JS对象
    • 在元素刚绑定了指令的时候,还没有插入到DOM中去,这时候调用focus方法没有作用
    // 使用  Vue.directive() 定义全局的指令  v-focus
    // 其中:参数1:指令的名称,注意:在定义的时候指令的名称前面不需要加 v- 前缀,但是:在调用的时候,必须在指令名称前加上 v- 前缀来进行调用
    //      参数2:是一个对象,这个对象身上有一些指令相关的函数,这些函数可以在特定的阶段执行相关的操作
    Vue.directive('focus', {
      // 每当指令绑定到元素上的时候,会立即执行这个 bind 函数,只执行一次
      bind: function (el) {
        // 注意: 在每个函数中,第一个参数永远是el,表示被绑定了指令的那个元素,这个el参数是一个原生的JS对象
        // 在元素刚绑定了指令的时候,还没有插入到 DOM 中去,这时候调用 focus 方法没有作用
        //  因为,一个元素只有插入DOM之后才能获取焦点
        // el.focus()
      },
      // inserted 表示元素插入到DOM中的时候,会执行 inserted 函数【触发1次】
      inserted: function (el) {
        el.focus()
        // 和JS行为有关的操作,最好在 inserted 中去执行,放置 JS行为不生效
      },
      // 当VNode更新的时候,会执行 updated, 可能会触发多次
      updated: function (el) {
    
      }
    })
    

    示例:

    // 自定义全局指令 v-focus,为绑定的元素自动获取焦点:
    Vue.directive('focus', {
      inserted: function (el) { // inserted 表示被绑定元素插入父节点时调用
        el.focus();
      }
    });

    // 自定义私有指令 v-color 和 v-font-weight,为绑定的元素设置指定的字体颜色 和 字体粗细:
      directives: {
        color: { // 为元素设置指定的字体颜色
          bind(el, binding) {
            el.style.color = binding.value;
          }
        },
        'font-weight': function (el, binding2) { // 自定义指令的简写形式,等同于定义了 bind 和 update 两个钩子函数
          el.style.fontWeight = binding2.value;
        }
      }
  1. 自定义指令的使用方式:
<input type="text" v-model="searchName" v-focus v-color="'red'" v-font-weight="900">
  1. Vue 1.x 中自定义元素指令【已废弃,了解即可】
Vue.elementDirective('red-color', {
  bind: function () {
    this.el.style.color = 'red';
  }
});

​ 使用方式:

<red-color>1232</red-color>

vue实例的生命周期

  • 什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期!
  • 生命周期钩子:就是生命周期事件的别名而已;
  • 生命周期钩子 = 生命周期函数 = 生命周期事件
  • 主要的生命周期函数分类:
  • 创建期间的生命周期函数:
    • beforeCreate():实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
    • created():实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始编译模板。此时才可以操作data和methods中的变量和方法
    • beforeMount():此时已经完成了模板的编译,但是还没有挂载到页面中
    • mounted():此时,已经将编译好的模板,挂载到了页面指定的容器中显示。此时才可以操作DOM元素。 此时整个Vue实例已经初始化完毕
  • 运行期间的生命周期函数:
    • beforeUpdate():状态更新之前执行此函数, 此时 data 中的数据被更新了,但是界面上的显示还没有被更新,因为此时还没有开始重新渲染DOM节点
    • updated():实例更新完毕之后调用此函数,此时 data 中的数据和界面上的显示,都已经完成了更新,界面已经被重新渲染好了!
  • 销毁期间的生命周期函数:
    • beforeDestroy():实例销毁之前调用。在这一步,实例仍然完全可用。
    • destroyed():Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

生命周期图示

vue-resource实现ajax请求

除了 vue-resource 之外,还可以使用 axios 的第三方包实现实现数据的请求

  1. 之前的学习中,如何发起数据请求?
  2. 常见的数据请求类型? get post jsonp
  3. 测试的URL请求资源地址:

vue-resource 的配置和使用

  1. 直接在页面中,通过script标签,引入 vue-resource 的脚本文件

    注意vue-resource 依赖于vue,引用的先后顺序是:先引用 Vue 的脚本文件,再引用 vue-resource 的脚本文件

<script src="./lib/vue-2.4.0.js"></script>
<script src="./lib/vue-resource-1.3.4.js"></script>
  1. 设置全局根域名

    // 设置全局域名
    Vue.http.options.root = 'http://www.baidu.com';
    
    // 以下的请求地址someUrl必须是相对路径,不能是绝对路径:'/someUrl',这样全局域名的设置才能生效
    this.$http.get('someUrl').then();
    

  2. 发送GET请求

    语法

    // 全局语法
    Vue.http.get('/someUrl', [config]).then(successCallback, errorCallback);
    // 私有语法
    this.$http.get('/someUrl', [config]).then(successCallback, errorCallback);
    

    示例

    // get方式获取数据
    getInfo() { 
      this.$http.get('http://127.0.0.1:8899/api/getlunbo').then(res => {
        console.log(res.body);
      })
    }
    
  3. 发送POST请求

    • 手动发起的post请求,默认没有表单格式(application/x-www-form-urlencoded),所以有的服务器处理不了
    • 解决以上问题的方法:设置config { emulateJSON:true },指定post提交的编码类型为 application/x-www-form-urlencoded

    语法

    // 全局语法
    Vue.http.post('/someUrl', [body], [config]).then(successCallback, errorCallback);
    // 私有语法
    this.$http.post('/someUrl', [body], [config]).then(successCallback, errorCallback);
    
    // 也可以全局配置 emulateJSON
    Vue.http.options.emulateJSON = true;
    

    示例

    // post方式获取数据
    postInfo() {
      var url = 'http://127.0.0.1:8899/api/post';
      // post 方法接收三个参数:
      // 参数1: 要请求的URL地址
      // 参数2: 要发送的数据对象
      // 参数3: 指定post提交的编码类型为 application/x-www-form-urlencoded
      this.$http.post(url, { name: 'zs' }, { emulateJSON: true }).then(res => {
        console.log(res.body);
      });
    }
    
  4. 发送JSONP请求

    语法

    // 全局语法
    Vue.http.jsonp('/someUrl', [config]).then(successCallback, errorCallback);
    // 私有语法
    this.$http.jsonp('/someUrl', [config]).then(successCallback, errorCallback);
    

    示例

    // JSONP形式从服务器获取数据
    jsonpInfo() { 
      var url = 'http://127.0.0.1:8899/api/jsonp';
      this.$http.jsonp(url).then(res => {
        console.log(res.body);
      });
    }
    
  5. Response

    Property Type Description
    url string Response URL origin
    body Object, Blob, string Response body
    headers Header Response Headers object
    ok boolean HTTP status code between 200 and 299
    status number HTTP status code of the response
    statusText string HTTP status text of the response
    Method Type Description
    text() Promise Resolves the body as string
    json() Promise Resolves the body as parsed JSON object
    blob() Promise Resolves the body as Blob object

JSONP的实现原理

  • 由于浏览器的安全性限制,不允许AJAX访问 协议不同域名不同端口号不同跨域请求数据接口,浏览器认为这种访问不安全

  • 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口的地址,因为script标签不存在跨域限制,这种数据获取方式,称作JSONP(注意:根据JSONP的实现原理,知晓JSONP只支持Get请求

  • 具体实现过程:

    • 先在客户端定义一个回调方法,预定义对数据的操作
    • 再把这个回调方法的名称,通过URL传参的形式,提交到服务器的数据接口
    • 服务器数据接口组织好要发送给客户端的数据,再拿着客户端传递过来的回调方法名称,拼接出一个调用这个方法的字符串,发送给客户端去解析执行
    • 客户端拿到服务器返回的字符串之后,当作Script脚本去解析执行,这样就能够拿到JSONP的数据了
  • 带大家通过 Node.js ,来手动实现一个JSONP的请求例子

    服务器端

   const http = require('http');
   // 导入解析 URL 地址的核心模块
   const urlModule = require('url');

   const server = http.createServer();
   // 监听 服务器的 request 请求事件,处理每个请求
   server.on('request', (req, res) => {
     const url = req.url;

     // 解析客户端请求的URL地址
     var info = urlModule.parse(url, true);

     // 如果请求的 URL 地址是 /getjsonp ,则表示要获取JSONP类型的数据
     if (info.pathname === '/getjsonp') {
       // 获取客户端指定的回调函数的名称
       var cbName = info.query.callback;
       // 手动拼接要返回给客户端的数据对象
       var data = {
         name: 'zs',
         age: 22,
         gender: '男',
         hobby: ['吃饭', '睡觉', '运动']
       }
       // 拼接出一个方法的调用,在调用这个方法的时候,把要发送给客户端的数据,序列化为字符串,作为参数传递给这个调用的方法:
       var result = `${cbName}(${JSON.stringify(data)})`;
       // 将拼接好的方法的调用,返回给客户端去解析执行
       res.end(result);
     } else {
       res.end('404');
     }
   });

   server.listen(3000, () => {
     console.log('server running at http://127.0.0.1:3000');
   });

​ 客户端

    <script>
        function showInfo(data){
            console.log(data);
        }
    </script>
	...
    <script src="localhost:3000?callback=showInfo"></script>

配置本地数据库和数据接口API

  1. 先解压安装 PHPStudy;
  2. 解压安装 Navicat 这个数据库可视化工具,并激活;
  3. 打开 Navicat 工具,新建空白数据库,名为 dtcmsdb4;
  4. 双击新建的数据库,连接上这个空白数据库,在新建的数据库上右键 -> 运行SQL文件,选择并执行 dtcmsdb4.sql 这个数据库脚本文件;如果执行不报错,则数据库导入完成;
  5. 进入文件夹 vuecms3_nodejsapi 内部,执行 npm i 安装所有的依赖项;
  6. 先确保本机安装了 nodemon, 没有安装,则运行 npm i nodemon -g 进行全局安装,安装完毕后,进入到 vuecms3_nodejsapi目录 -> src目录 -> 双击运行 start.bat
  7. 如果API启动失败,请检查 PHPStudy 是否正常开启,同时,检查 app.js 中第 14行 中数据库连接配置字符串是否正确;PHPStudy 中默认的 用户名是root,默认的密码也是root

案例:品牌管理改造

  • 展示品牌列表
  • 添加品牌数据
  • 删除品牌数据

Vue中的动画

过渡 & 动画

为什么要有动画:动画能够提高用户的体验,帮助用户更好的理解页面中的功能

使用过渡类名

  • 使用 transition 将需要过渡的元素包裹起来
  • 自定义2组样式来控制 transition 内部的元素实现动画
  • 通过给 transition 设置不同的name值,可以定义不同的动画样式组
  1. HTML结构:
<div id="app">
    <input type="button" value="动起来" @click="myAnimate">
    <!-- 使用 transition 将需要过渡的元素包裹起来 -->
    <transition name="fade">
      <div v-show="isshow">动画哦</div>
    </transition>
  </div>
  1. VM 实例:
// 创建 Vue 实例,得到 ViewModel
var vm = new Vue({
  el: '#app',
  data: {
    isshow: false
  },
  methods: {
    myAnimate() {
      this.isshow = !this.isshow;
    }
  }
});
  1. 定义两组类样式:
/* 定义进入和离开时候的过渡状态 */
    .fade-enter-active,
    .fade-leave-active {
      transition: all 0.2s ease;
      position: absolute;
    }

    /* 定义进入过渡的开始状态 和 离开过渡的结束状态 */
    .fade-enter,
    .fade-leave-to {
      opacity: 0;
      transform: translateX(100px);
    }

使用第三方 CSS 动画库

使用第三方 CSS 动画库

  1. 导入动画类库:
<link rel="stylesheet" type="text/css" href="./lib/animate.css">
  1. 定义 transition 及属性:
<transition
	enter-active-class="fadeInRight"
    leave-active-class="fadeOutRight"
    :duration="{ enter: 500, leave: 800 }">
  	<div class="animated" v-show="isshow">动画哦</div>
</transition>

使用动画钩子函数

  1. 定义 transition 组件以及三个钩子函数
<div id="app">
    <input type="button" value="切换动画" @click="isshow = !isshow">
    <transition
    @before-enter="beforeEnter"
    @enter="enter"
    @after-enter="afterEnter">
      <div v-if="isshow" class="show">OK</div>
    </transition>
  </div>
  1. 定义三个 methods 钩子方法:
methods: {
  		// 动画进入之前的回调,可以在这里设置动画的 起始样式
        beforeEnter(el) { 
          el.style.transform = 'translateX(500px)';
        },
        // 动画进入完成时候的回调,可以在这里设置动画的 结束样式
        enter(el, done) { 
          el.offsetWidth; // 这句话没有实际的作用,但是如果不写则没有动画效果 el.offset*
          el.style.transform = 'translateX(0px)';
          // el.transition: 'all 0.4s ease';
          done();	// 这里的done,就是afterEnter这个函数,是afterEnter函数的引用
        },
        // 动画进入完成之后的回调
        afterEnter(el) { 
          this.isshow = !this.isshow;
        }
      }
  1. 定义动画过渡时长和样式:
.show{
      transition: all 0.4s ease;
    }

v-for 的列表过渡

v-for 的列表过渡

  1. 定义过渡样式
<style>
    .list-enter,
    .list-leave-to {
      opacity: 0;
      transform: translateY(10px);
    }

    .list-enter-active,
    .list-leave-active {
      transition: all 0.3s ease;
    }
</style>
  1. 定义DOM结构,其中,需要使用 transition-group 组件把v-for循环的列表包裹起来
  <div id="app">
    <input type="text" v-model="txt" @keyup.enter="add">

    <transition-group tag="ul" name="list">
      <li v-for="(item, i) in list" :key="i">{{item}}</li>
    </transition-group>
  </div>
  1. 定义 VM中的结构:
var vm = new Vue({
      el: '#app',
      data: {
        txt: '',
        list: [1, 2, 3, 4]
      },
      methods: {
        add() {
          this.list.push(this.txt);
          this.txt = '';
        }
      }
    });

列表的排序过渡

<transition-group> 组件还有一个特殊之处。不仅可以进入和离开动画,还可以改变定位。要使用这个新功能只需了解新增的 v-move 特性,它会在元素的改变定位的过程中应用

  • v-movev-leave-active 结合使用,能够让列表的过渡更加平缓柔和:
.v-move{
  transition: all 0.8s ease;
}
.v-leave-active{
  position: absolute;
}

相关文章

  1. vue.js 1.x 文档
  2. vue.js 2.x 文档
  3. String.prototype.padStart(maxLength, fillString)
  4. js 里面的键盘事件对应的键码
  5. pagekit/vue-resource
  6. navicat如何导入sql文件和导出sql文件
  7. 贝塞尔在线生成器
原文地址:https://www.cnblogs.com/pikachu/p/14668274.html