Vue的基础入门及使用

Vue的基础入门及使用

第一章 Vue核心

1.1 Vue的基本认识

1.1.1 官网
  • 英文官网:https://vuejs.org/
  • 中文官网:https://cn.vuejs.org/
1.1.2 介绍描述
  • 渐进式JavaScript框架
  • 作者:尤雨溪(一位华裔前Google工程师)
  • 作用:动态 构建用户界面
1.1.3 Vue特点
  • 1.遵循MVVM模式
    • M:Model,模型,数据对象(data)
    • V:View,视图,模板页面
    • VM:ViewModel,视图模型(Vue的实例)
  • 2.编码简洁,体积小,运行效率高,适合移动/PC端开发
  • 3.它本身只关注UI,可以轻松引入Vue插件或其他第三库开发项目
1.1.4 Vue的对象
  • el:

    • 指定dom标签容器的选择器
    • Vue就会管理对应的标签及其子标签
  • data:

    • 对象或函数类型
    • 指定初始化状态属性数据的对象
    • vm也会自动拥有data中所有属性
    • 页面中可以直接访问使用
    • 数据代理:由vm对象来代理对data中所有属性的操作(读/写)
  • methods:

    • 包含多个方法的对象
    • 供页面中的时间指令来绑定回调
    • 回调函数默认有event参数,但也可以指定自己的参数
    • 所有的方法由vue对象来调用,访问data中的属性可以直接使用this.xxx
  • computed:

    • 包含多个方法的对象
    • 对状态属性进行计算返回一个新的数据,供页面获取显示
    • 一般情况下是相当于是一个只读的属性
    • 利用set/get方法来实现属性数据的计算读取,同时监视属性数据的变化
    • 如何给对象定义get/set属性
    • 在创建对象时指定:get name(){return xxx}/set name(value) {}
    • 对象创建之后指定:Object.defineProperty(obj,age,{get(){},set(value){}})
  • watch

    • 包含多个属性监视的对象

    • 分为一般监视和深度监视

      xxx:function(value){}
      xxx:{
      	deep:true,
      	handler:fun(value)
      }
      
    • 另一种添加监视方法:vm.$watch(‘xxx’,function(value){})

1.1.5 与其他前端JS框架的关联
  • 借鉴Angular的模板数据绑定技术
  • 借鉴React的组件虚拟DOM技术
1.1.6 Vue扩展插件
  • vue-cli:vue脚手架

  • vue-resouce(axios):ajax请求

  • vue-route:路由

  • vuex:状态管理

  • vue-lazyload:图片懒加载

  • vue-scoller:页面滑动相关

  • mint-ui:基于vue的UI组件库(移动端)

  • element-ui:基于vue的UI组件库(PC端)

1.2 Vue的基本使用

1.2.1 永远的HelloWorld
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>01_HelloWorld</title>
</head>
<body>
<!--
  1. 引入Vue.js
  2. 创建Vue对象
    el : 指定根element(选择器)
    data : 初始化数据(页面可以访问)
  3. 双向数据绑定 : v-model
  4. 显示数据 : {{xxx}}
  5. 理解vue的mvvm实现
-->
<!--模板-->
<div id="test">
  <input type="text" v-model="msg"><br><!--指令:element.value='xxx'-->
  <input type="text" v-model="msg"><!--指令-->
  <p>Hello {{msg}}</p><!--大括号表达式-->
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  const vm = new Vue({ // 配置对象 options
    // 配置选项(option)
    el: '#test',  // element: 选择器,指定用vue来管理页面中的哪个标签区域
    data: {
      msg: 'World'
    }
  })
</script>
</body>
</html>
1.2.2 Vue的调试工具

在这里插入图片描述

打开Vue页面,F12进入开发者调试,选择Vue,即可使用该调试工具。

1.2.3 Vue的MVVM

在这里插入图片描述

1.3 Vue的模板语法

1.3.1 模板的理解:
  • 动态的HTML页面
  • 包含了一些JS语法代码
  • 大括号表达式
  • 指令(以v-开头的自定义标签属性)
1.3.2 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模板语法</title>
</head>
<body>
<!--
1. 模板的理解:
  动态的html页面
  包含了一些JS语法代码
    大括号表达式
    指令(以v-开头的自定义标签属性)
2. 双大括号表达式
  语法: {{exp}} 或 {{{exp}}}
  功能: 向页面输出数据
  可以调用对象的方法
3. 指令一: 强制数据绑定
  功能: 指定变化的属性值,比如将HTML语言改变为字符串
  完整写法:
    v-bind:xxx='yyy'  //yyy会作为表达式解析执行
  简洁写法:
    :xxx='yyy'
4. 指令二: 绑定事件监听
  功能: 绑定指定事件名的回调函数
  完整写法:
    v-on:click='xxx'
  简洁写法:
    @click='xxx'
-->

<div id="app">
  <h2>1. 双大括号表达式</h2>
  <p>{{content}}</p>
  <p>{{content.toUpperCase()}}</p>

  <h2>2. 指令一: 强制数据绑定</h2>
  <a href="url">访问指定站点</a><br>
  <a v-bind:href="url">访问指定站点2</a><br>
  <a :href="url">访问指定站点2</a><br>
  <h2>3. 指令二: 绑定事件监听</h2>
  <button v-on:click="test">点我</button>
  <button @click="test">点我</button>
</div>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#app',
    data: {
      content: 'NBA I Love This Game',
      url: 'http://www.baidu.com'
    },
    methods: {
      test () {
        alert('好啊!!!')
      }
    }
  })
</script>
</body>
</html>

1.4 计算属性和监视

1.4.1 计算属性
  • 在computed属性对象中定义计算属性的方法
  • 在页面中使用{{方法名}}来显示计算的结果
1.4.2 监视属性
  • 通过VM对象的$watch()或watch配置来监视指定的属性
  • 当属性变化时,回调函数自动调用,在函数内部进行计算
1.4.3 计算属性高级
  • 通过getter/setter实现对属性数据的显示和监视
  • 计算属性存在缓存,多次读取只执行一次getter计算
1.4.5 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>计算属性和监视</title>
</head>
<body>
<!--
1. 计算属性
  在computed属性对象中定义计算属性的方法
  在页面中使用{{方法名}}来显示计算的结果
2. 监视属性:
  通过通过vm对象的$watch()或watch配置来监视指定的属性
  当属性变化时, 回调函数自动调用, 在函数内部进行计算
3. 计算属性高级:
  通过getter/setter实现对属性数据的显示和监视
  计算属性存在缓存, 多次读取只执行一次getter计算
-->
<div id="demo">
  姓: <input type="text" placeholder="First Name" v-model="firstName"><br>
  名: <input type="text" placeholder="Last Name" v-model="lastName"><br>
  <!--fullName1是根据fistName和lastName计算产生-->
  姓名1(单向): <input type="text" placeholder="Full Name1" v-model="fullName1"><br>
  姓名2(单向): <input type="text" placeholder="Full Name2" v-model="fullName2"><br>
  姓名3(双向): <input type="text" placeholder="Full Name3" v-model="fullName3"><br>

  <p>{{fullName1}}</p>
  <p>{{fullName1}}</p>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  const vm = new Vue({
    el: '#demo',
    data: {
      firstName: 'A',
      lastName: 'B',
       fullName2: 'A-B'
    },

    // 计算属性配置: 值为对象
    computed: {
      //什么时候执行:初始化显示/相关的data属性发生变化
      fullName1 () { // 属性的get()
        console.log('fullName1()', this)
        return this.firstName + '-' + this.lastName
      },

      fullName3: {
        // 回调函数的三个属性:1.你定义的  2.你没有调用  3.但是它执行了
        // 回调函数:什么时候调用?用来做什么
        // 当获取当前属性值时自动调用, 将返回值(根据相关的其它属性数据)作为属性值
        get () {
          console.log('fullName3 get()')
          return this.firstName + '-' + this.lastName
        },
        // 当属性值发生了改变时自动调用, 监视当前属性值变化, 同步更新相关的其它属性值
        set (value) {// fullName3的最新value值  A-B23
          console.log('fullName3 set()', value)
          // 更新firstName和lastName
          const names = value.split('-')
          this.firstName = names[0]
          this.lastName = names[1]
        }
      }
    },

    watch: {
      // 配置监视firstName
      firstName: function (value) { // firstName发生了变化,相当于属性的set
        console.log('watch firstName', value)
        // 更新fullName2
        this.fullName2 = value + '-' + this.lastName
      }
    }
  })

  // 监视lastName
  vm.$watch('lastName', function (value) {
    console.log('$watch lastName', value)
    // 更新fullName2
    this.fullName2 = this.firstName + '-' + value
  })

</script>
</body>
</html>

1.5 class与style绑定

1.5.1 理解
  • 在应用界面上,某个(些)元素的样式是变化的
  • class/style绑定就是专门用来实现动态样式效果的技术
1.5.2 class绑定
  • :class=‘xxx’
  • 表达式是字符串:‘classA’
  • 表达式是对象:{classA:isA,classB:isB}
  • 表达式是数组:[‘classA’,‘classB’]
1.5.3 style绑定
  • :style="{color:activeColor,fontSize:fontSize+‘px’}"
  • 必须是对象
  • 其中activeColor/fontSize 是data属性
1.5.4 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>class与style绑定</title>
  <style>
    .classA {
      color: red;
    }
    .classB {
      background: blue;
    }
    .classC {
      font-size: 20px;
    }
  </style>
</head>
<body>

<!--
1. 理解
  在应用界面中, 某个(些)元素的样式是变化的
  class/style绑定就是专门用来实现动态样式效果的技术
2. class绑定: :class='xxx'
  xxx是字符串
  xxx是对象
  xxx是数组
3. style绑定
  :style="{ color: activeColor, fontSize: fontSize + 'px' }"
  其中activeColor/fontSize是data属性
-->

<div id="demo">
  <h2>1. class绑定: :class='xxx'</h2>
  <p :class="myClass">xxx是字符串</p>
  <p :class="{classA: hasClassA, classB: hasClassB}">xxx是对象</p>
  <p :class="['classA', 'classB']">xxx是数组</p>

  <h2>2. style绑定</h2>
  <p :style="{color:activeColor, fontSize}">:style="{ color: activeColor, fontSize: fontSize + 'px' }"</p>

  <button @click="update">更新</button>

</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#demo',
    data: {
      myClass: 'classA',
      hasClassA: true,
      hasClassB: false,
      activeColor: 'red',
      fontSize: '20px'
    },

    methods: {
      update () {
        this.myClass = 'classB'
        this.hasClassA = !this.hasClassA
        this.hasClassB = !this.hasClassB
        this.activeColor = 'yellow'
        this.fontSize = '30px'
      }
    }
  })
</script>
</body>
</html>

1.6 条件渲染

1.6.1 条件渲染指令
  • v-if与v-else
  • v-show
1.6.2 比较v-if与v-show
  • 如果需要频繁切换,v-show较好
  • 当条件不成立时,v-if的所有子节点不会解析(项目中使用)
1.6.3 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>条件渲染</title>
</head>
<body>
<!--
1. 条件渲染指令
  v-if
  v-else
  v-show
2. 比较v-if与v-show
  如果需要频繁切换 v-show 较好
-->

<div id="demo">
  <p v-if="ok">表白成功</p>
  <p v-else>表白失败</p>

  <hr>
  <p v-show="ok">求婚成功</p>
  <p v-show="!ok">求婚失败</p>

  <button @click="ok=!ok">切换</button>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#demo',
    data: {
      ok: true,
    }
  })
</script>
</body>
</html>

1.7 列表渲染

1.7.1 列表显示指令
  • 数组:v-for/index
  • 对象:v-for/key
1.7.2 列表的更新显示
  • 删除item
  • 替换item
1.7.3 列表的高级处理
  • 列表过滤
  • 列表排序
1.7.4 列表显示代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>列表渲染</title>
</head>
<body>

<!--
1. 列表显示
  数组: v-for / index
  对象: v-for / key
2. 列表的更新显示
  删除item
  替换item
-->

<div id="demo">
  <h2>测试: v-for 遍历数组</h2>
  <ul>
    <li v-for="(p, index) in persons" :key="index">
      {{index}}--{{p.name}}--{{p.age}}
      --<button @click="deleteP(index)">删除</button>
      --<button @click="updateP(index, {name:'Cat', age: 16})">更新</button>
    </li>
  </ul>
  <button @click="addP({name: 'xfzhang', age: 18})">添加</button>

  <h2>测试: v-for 遍历对象</h2>

  <ul>
    <li v-for="(item, key) in persons[1]" :key="key">{{key}}={{item}}</li>
  </ul>

</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#demo',
    data: {
      persons: [
        {name: 'Tom', age:18},
        {name: 'Jack', age:17},
        {name: 'Bob', age:19},
        {name: 'Mary', age:16}
      ]
    },

    methods: {
      deleteP (index) {
        this.persons.splice(index, 1) // 调用了不是原生数组的splice(), 而是一个变异(重写)方法
              // 1. 调用原生的数组的对应方法
              // 2. 更新界面
      },

      updateP (index, newP) {
        console.log('updateP', index, newP)
        // this.persons[index] = newP  // vue根本就不知道
        this.persons.splice(index, 1, newP)
        // this.persons = []
      },

      addP (newP) {
        this.persons.push(newP)
      }
    }
  })
</script>
</body>
</html>
1.7.5 列表的搜索排序
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>过滤与排序</title>
</head>
<body>
<!--
1. 列表过滤
2. 列表排序
-->

<div id="demo">
  <input type="text" v-model="searchName">
  <ul>
    <li v-for="(p, index) in filterPersons" :key="index">
      {{index}}--{{p.name}}--{{p.age}}
    </li>
  </ul>
  <div>
    <button @click="setOrderType(2)">年龄升序</button>
    <button @click="setOrderType(1)">年龄降序</button>
    <button @click="setOrderType(0)">原本顺序</button>
  </div>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#demo',
    data: {
      searchName: '',
      orderType: 0, // 0代表不排序, 1代表降序, 2代表升序
      persons: [
        {name: 'Tom', age:18},
        {name: 'Jack', age:17},
        {name: 'Bob', age:19},
        {name: 'Mary', age:16}
      ]
    },

    computed: {
      filterPersons () {
//        debugger
        // 取出相关数据
        const {searchName, persons, orderType} = this
        let arr = [...persons]
        // 过滤数组
        if(searchName.trim()) {
          arr = persons.filter(p => p.name.indexOf(searchName)!==-1)
        }
        // 排序
        if(orderType) {
          arr.sort(function (p1, p2) {
            if(orderType===1) { // 降序
              return p2.age-p1.age
            } else { // 升序
              return p1.age-p2.age
            }

          })
        }
        return arr
      }
    },

    methods: {
      setOrderType (orderType) {
        this.orderType = orderType
      }
    }
  })
</script>
</body>
</html>

1.8 事件处理

1.8.1 绑定监听
  • v-on:xxx=“fun”
  • @xxx=“fun”
  • @xxx=“fun(参数)”
  • 默认事件形参:event
  • 隐含属性对象:$event
1.8.2 事件修饰符
  • .prevent:阻止事件的默认行为event.preventDefault()
  • .stop:停止事件冒泡 event.stopPropagation()
1.8.3 按键修饰符
  • .keycode:操作的是某个keycode值的键
  • .keyName:操作的是某个按键名的键(少部分)
1.8.4 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>07_事件处理</title>
</head>
<body>
<!--
1. 绑定监听:
  v-on:xxx="fun"
  @xxx="fun"
  @xxx="fun(参数)"
  默认事件形参: event
  隐含属性对象: $event
2. 事件修饰符:
  .prevent : 阻止事件的默认行为 event.preventDefault()
  .stop : 停止事件冒泡 event.stopPropagation()
3. 按键修饰符
  .keycode : 操作的是某个keycode值的健
  .enter : 操作的是enter键
-->

<div id="example">

  <h2>1. 绑定监听</h2>
  <button @click="test1">test1</button>
  <button @click="test2('abc')">test2</button>
  <button @click="test3('abcd', $event)">test3</button>

  <h2>2. 事件修饰符</h2>
  <a href="http://www.baidu.com" @click.prevent="test4">百度一下</a>
  <div style="width: 200px;height: 200px;background: red" @click="test5">
    <div style="width: 100px;height: 100px;background: blue" @click.stop="test6"></div>
  </div>

  <h2>3. 按键修饰符</h2>
  <input type="text" @keyup.13="test7">
  <input type="text" @keyup.enter="test7">

</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#example',
    data: {

    },
    methods: {
      test1(event) {
        alert(event.target.innerHTML)
      },
      test2 (msg) {
        alert(msg)
      },
      test3 (msg, event) {
        alert(msg+'---'+event.target.textContent)
      },

      test4 () {
        alert('点击了链接')
      },

      test5 () {
        alert('out')
      },
      test6 () {
        alert('inner')
      },

      test7 (event) {
        console.log(event.keyCode)
        alert(event.target.value)
      }
    }
  })
</script>
</body>
</html>

1.9 表单输入绑定

1.9.1 使用v-model对表单数据自动收集
  • text/textarea 文本
  • checkbox 多选
  • radio 单选
  • select 选择
1.9.2 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>表单输入绑定</title>
</head>
<body>
<!--
1. 使用v-model(双向数据绑定)自动收集数据
  text/textarea
  checkbox
  radio
  select
-->
<div id="demo">
  <form action="/xxx" @submit.prevent="handleSubmit">
    <span>用户名: </span>
    <input type="text" v-model="username"><br>

    <span>密码: </span>
    <input type="password" v-model="pwd"><br>

    <span>性别: </span>
    <input type="radio" id="female" value="" v-model="sex">
    <label for="female"></label>
    <input type="radio" id="male" value="" v-model="sex">
    <label for="male"></label><br>

    <span>爱好: </span>
    <input type="checkbox" id="basket" value="basket" v-model="likes">
    <label for="basket">篮球</label>
    <input type="checkbox" id="foot" value="foot" v-model="likes">
    <label for="foot">足球</label>
    <input type="checkbox" id="pingpang" value="pingpang" v-model="likes">
    <label for="pingpang">乒乓</label><br>

    <span>城市: </span>
    <select v-model="cityId">
      <option value="">未选择</option>
      <option :value="city.id" v-for="(city, index) in allCitys" :key="city.id">{{city.name}}</option>
    </select><br>
    <span>介绍: </span>
    <textarea rows="10" v-model="info"></textarea><br><br>

    <input type="submit" value="注册">
  </form>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#demo',
    data: {
      username: '',
      pwd: '',
      sex: '男',
      likes: ['foot'],
      allCitys: [{id: 1, name: 'BJ'}, {id: 2, name: 'SS'}, {id: 3, name: 'SZ'}],
      cityId: '2',
      info: ''
    },
    methods: {
      handleSubmit () {
        console.log(this.username, this.pwd, this.sex, this.likes, this.cityId, this.info)
        alert('提交注册的ajax请求')
      }
    }
  })
</script>
</body>
</html>

1.10 Vue实例生命周期

1.10.1 生命周期流程图

在这里插入图片描述

1.10.2 vue生命周期分析
  • 1.初始化显示
    • beforeCreate()
    • created()
    • beforeMount()
    • mounted()
  • 2.更新状态:this.xxx=value
    • beforeUpdate()
    • updated()
  • 3.销毁vue实例:vm.$destory()
    • beforeDestory()
    • destoryed()
1.10.3 常用的生命周期方法
  • created()/mounted():发送ajax请求,启动定时器等异步任务
  • beforeDestory():做收尾工作,如:清除定时器
1.10.4 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>生命周期</title>
</head>
<body>
<!--
1. vue对象的生命周期
  1). 初始化显示
    * beforeCreate()
    * created()
    * beforeMount()
    * mounted()
  2). 更新状态
    * beforeUpdate()
    * updated()
  3). 销毁vue实例: vm.$destory()
    * beforeDestory()
    * destoryed()
2. 常用的生命周期方法
  created()/mounted(): 发送ajax请求, 启动定时器等异步任务
  beforeDestory(): 做收尾工作, 如: 清除定时器
-->

<div id="test">
  <button @click="destroyVue">destory vue</button>
  <p v-if="isShow">尚硅谷IT教育</p>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#test',
    data: {
      isShow: true
    },

    beforeCreate() {
      console.log('beforeCreate()')
    },

    created() {
      console.log('created()')
    },

    beforeMount() {
      console.log('beforeMount()')
    },

    mounted () {
      console.log('mounted()')
      // 执行异步任务
      this.intervalId = setInterval(() => {
        console.log('-----')
        this.isShow = !this.isShow
      }, 1000)
    },


    beforeUpdate() {
      console.log('beforeUpdate()')
    },
    updated () {
      console.log('updated()')
    },


    beforeDestroy() {
      console.log('beforeDestroy()')
      // 执行收尾的工作
      clearInterval(this.intervalId)
    },

    destroyed() {
      console.log('destroyed()')
    },

    methods: {
      destroyVue () {
        this.$destroy()
      }
    }
  })
</script>
</body>
</html>

1.11 过渡&动画

1.11.1 vue动画的理解
  • 操作css的trasition或animation
  • vue会给目标元素添加/移除特定的class
  • 过渡的相关类名
    • xxx-enter-active:指定显示的transition
    • xxx-leave-action:指定隐藏的transition
    • xxx-enter/xxx-leave-to:指定隐藏时的样式
      在这里插入图片描述
1.11.2 基本过渡动画的编码
  • 在目标元素外包裹
  • 定义calss样式
    • 指定过渡样式:transition
    • 指定隐藏时的样式:opacity/其他
1.11.3 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>过渡&动画</title>
  <style>
    /*指定过渡样式*/
    .xxx-enter-active, .xxx-leave-active {
      transition: opacity 1s
    }
    /*指定隐藏时的样式*/
    .xxx-enter, .xxx-leave-to {
      opacity: 0;
    }


    .move-enter-active {
      transition: all 1s
    }

    .move-leave-active {
      transition: all 3s
    }

    .move-enter, .move-leave-to {
      opacity: 0;
      transform: translateX(20px)
    }
  </style>
</head>
<body>
<!--
1. vue动画的理解
  操作css的trasition或animation
  vue会给目标元素添加/移除特定的class
2. 基本过渡动画的编码
  1). 在目标元素外包裹<transition name="xxx">
  2). 定义class样式
    1>. 指定过渡样式: transition
    2>. 指定隐藏时的样式: opacity/其它
3. 过渡的类名
  xxx-enter-active: 指定显示的transition
  xxx-leave-active: 指定隐藏的transition
  xxx-enter: 指定隐藏时的样式
-->



<div id="demo">
  <button @click="show = !show">Toggle</button>
  <transition name="xxx">
    <p v-show="show">hello</p>
  </transition>
</div>

<hr>
<div id="demo2">
  <button @click="show = !show">Toggle2</button>
  <transition name="move">
    <p v-show="show">hello</p>
  </transition>
</div>


<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#demo',
    data: {
      show: true
    }
  })

  new Vue({
    el: '#demo2',
    data: {
      show: true
    }
  })

</script>
</body>
</html>

1.12 过滤器

1.12.1 理解过滤器
  • 功能:对要显示的数据进行特定格式化后在显示
  • 注意:并没有改变原本的数据,可以产生新的对应的数据
1.12.2 定义和使用过滤器
  • 定义过滤器
Vue.filter(filterName,function(value[,arg1,arg2,....])){
// 进行一定的数据处理
return new Value
}
  • 使用过滤器
<div>{{myData|filterName}}</div>
<div>{{myData|filterName(arg)}}</div>
1.12.3 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>过滤器</title>
</head>
<body>
<!--
1. 理解过滤器
  功能: 对要显示的数据进行特定格式化后再显示
  注意: 并没有改变原本的数据, 可是产生新的对应的数据
2. 编码
  1). 定义过滤器
    Vue.filter(filterName, function(value[,arg1,arg2,...]){
      // 进行一定的数据处理
      return newValue
    })
  2). 使用过滤器
    <div>{{myData | filterName}}</div>
    <div>{{myData | filterName(arg)}}</div>
-->
<!--需求: 对当前时间进行指定格式显示-->
<div id="test">
  <h2>显示格式化的日期时间</h2>
  <p>{{time}}</p>
  <p>最完整的: {{time | dateString}}</p>
  <p>年月日: {{time | dateString('YYYY-MM-DD')}}</p>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/moment.js/2.22.1/moment.js"></script>
<script>
  // 定义过滤器
  Vue.filter('dateString', function (value, format='YYYY-MM-DD HH:mm:ss') {

    return moment(value).format(format);
  })


  new Vue({
    el: '#test',
    data: {
      time: new Date()
    },
    mounted () {
      setInterval(() => {
        this.time = new Date()
      }, 1000)
    }
  })
</script>
</body>
</html>

1.13 内置指令与自定义指令

1.13.1 常用内置指令
  • v:text:更新元素的textContent
  • v-html:更新元素的innerHTML
  • v-if:如果为true,当前标签才会输出到页面
  • v-else:如果为false,当前标签才会输出到页面
  • v-show:通过控制display样式来控制显示/隐藏
  • v-for:遍历数组/对象
  • v-on:绑定事件监听,一般简写为@
  • v-bind:强制绑定解析表达式,可以省略v-bind
  • v-model:双向数据绑定
  • ref:指定唯一标识,vue对象通过$els属性访问这个元素对象
  • v-cloak:防止闪现,与css配合:[v-cloak]{display:none}
1.13.2 自定义指令
  • 注册全局指令
Vue.directive('my-directive',function(el,binding){
    el.innerHTML = binding.value.toupperCase()
})
  • 注册局部指定
directives:{
    'my-directive':{
        bind(el,binding){
            el.innerHTML = binding.value.toupperCase()
        }
    }
}
  • 使用自定义指令
v-my-directive='xxx'
1.13.5 内置编码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>内置指令</title>
  <style>
    [v-cloak] { display: none }
  </style>
</head>
<body>
<!--
常用内置指令
  v:text : 更新元素的 textContent
  v-html : 更新元素的 innerHTML
  v-if : 如果为true, 当前标签才会输出到页面
  v-else: 如果为false, 当前标签才会输出到页面
  v-show : 通过控制display样式来控制显示/隐藏
  v-for : 遍历数组/对象
  v-on : 绑定事件监听, 一般简写为@
  v-bind : 强制绑定解析表达式, 可以省略v-bind
  v-model : 双向数据绑定
  ref : 为某个元素注册一个唯一标识, vue对象通过$refs属性访问这个元素对象
  v-cloak : 使用它防止闪现表达式, 与css配合: [v-cloak] { display: none }
-->
<div id="example">
  <p v-cloak>{{content}}</p>
  <p v-text="content"></p>   <!--p.textContent = content-->
  <p v-html="content"></p>  <!--p.innerHTML = content-->
  <p ref="msg">abcd</p>
  <button @click="hint">提示</button>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  new Vue({
    el: '#example',
    data: {
      content: '<a href="http://www.baidu.com">百度一下</a>'
    },
    methods: {
      hint () {
        alert(this.$refs.msg.innerHTML)
      }
    }
  })
</script>
</body>
</html>
1.13.6 自定义指令编码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>自定义指令</title>
</head>
<body>

<!--
1. 注册全局指令
  Vue.directive('my-directive', function(el, binding){
    el.innerHTML = binding.value.toupperCase()
  })
2. 注册局部指令
  directives : {
    'my-directive' : {
        bind (el, binding) {
          el.innerHTML = binding.value.toupperCase()
        }
    }
  }
3. 使用指令
  v-my-directive='xxx'
-->
<!--
需求: 自定义2个指令
  1. 功能类型于v-text, 但转换为全大写
  2. 功能类型于v-text, 但转换为全小写
-->

<div id="test">
  <p v-upper-text="msg"></p>
  <p v-lower-text="msg"></p>
</div>

<div id="test2">
  <p v-upper-text="msg"></p>
  <p v-lower-text="msg"></p>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  // 注册一个全局指令
  // el: 指令所在的标签对象
  // binding: 包含指令相关数据的容器对象
  Vue.directive('upper-text', function (el, binding) {
    console.log(el, binding)
    el.textContent = binding.value.toUpperCase()
  })
  new Vue({
    el: '#test',
    data: {
      msg: "I Like You"
    },
    // 注册局部指令
    directives: {
      'lower-text'(el, binding) {
        console.log(el, binding)
        el.textContent = binding.value.toLowerCase()
      }
    }

  })

  new Vue({
    el: '#test2',
    data: {
      msg: "I Like You Too"
    }
  })
</script>
</body>
</html>

1.14 自定义插件

1.14.1 说明
  • Vue插件是一个包含install方法的对象
  • 通过install方法给Vue或Vue实例添加方法,定义全局指令等
1.14.2 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>插件</title>
</head>
<body>

<div id="test">
  <p v-my-directive="msg"></p>
</div>

<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="vue-myPlugin.js"></script>
<script type="text/javascript">
  // 声明使用插件(安装插件: 调用插件的install())
  Vue.use(MyPlugin) // 内部会调用插件对象的install()

  const vm = new Vue({
    el: '#test',
    data: {
      msg: 'HaHa'
    }
  })
  Vue.myGlobalMethod()
  vm.$myMethod()

  new Object()
</script>
</body>
</html>

第二章 Vue组件化编码

2.1 使用vue-cli创建模板项目

2.1.1 说明
  • vue-cli是vue官方提供的脚手架工具
  • github:https://github.com/vuejs/vue-cli
  • 作用:从https://github.com/vuejs-templates下载模板项目
2.1.2 创建vue项目
  • npm install -g vue-cli :全局安装vue-cli
  • vue init webpack vue_demo :当前目录下创建项目名称为vue_demo的webpack模板
  • cd vue_demo :进入vue_demo文件夹
  • npm install :安装
  • npm run serve :运行该项目
  • 访问http://localhost:8080 :项目访问地址
2.1.3 项目结构
  • build:webpack相关的配置文件夹(基本上不需要修改)
    • dev-serve.js:通过express启动后台服务器
  • config:webpack相关的配置文件夹(基本上不需要修改)
    • index.js:指定的后台服务的端口号和静态资源文件夹
  • node_modules:vue的依赖
  • src:源码文件夹
    • components:vue组件及其相关资源文件夹
    • App.vue:应用根主组件
    • main.js:应用入口js
  • static:静态资源文件夹
  • .babelrc:babel的配置文件
  • .selintignore:eslint检查忽略的配置
  • .eslintrc.js:eslint检查的配置
  • .gitignore:git版本管制忽略的配置
  • index.html:主页面的文件
  • package.json:应用包配置文件
  • README.md:应用描述说明的readme文件

2.2 项目的打包与发布

2.2.1 打包
  • npm run build
2.2.2 发布1:使用静态服务器工具包(nginx等—)
  • npm install -g serve
  • serve dist
  • 访问:http://localhost:5000
2.2.3 发布2:使用动态web服务器(tomcat)
  • 修改配置:webpack.prod.conf.js
output:{
    publicPath:'/xxx/'   //打包文件夹的名称
}
  • 重新打包
    • npm run build
  • 修改dist文件夹为项目名称:xxx
  • 将xxx拷贝到运行的tomcat的webapps目录下
  • 访问:http://localhost:8080/xxx

2.3 eslint

2.3.1 说明
  • ESLint是一个代码规范检查工具
  • 它定义了很多特定的规则,一旦你的代码违背了某一规则,eslint会作出非常有用的提示
  • 官网:http://eslint.org/
  • 基本已经替代以前的JSLint
2.3.2 ESLint提供以下支持
  • ES
  • JSX
  • style检查
  • 自定义错误和提示
2.3.3 ESLint提供以下几种校验
  • 语法错误校验
  • 不重要或丢失的标点符号,如分号
  • 没法运行到的代码块
  • 未被使用的参数提醒
  • 确保样式的统一规则,如sass或者less
  • 检查变量的命名
2.3.4 规则的错误等级有三种
  • 0:关闭规则
  • 1:打开规则,并且作为一个警告(信息打印黄色字体)
  • 2:打开规则,并且作为一个错误(信息打印红色字体)
2.3.5 相关配置文件
  • .eslintrc.js:全局规则配置文件
'rules':{
    'no-new':1
}
  • 在js/vue文件中修改局部规则
/* eslint-disable no-new*/
new Vue({
    el:'#body',
    components:{App}
})
  • .eslintignore:指令检查忽略的文件
*.js
*.vue

2.4 组件定义与使用

2.4.1 vue文件的组成(3个部分)
  • 模板页面
<template>
    页面模板
</template>
  • JS模块对象
<script>
    export default{
		data(){//模板里面只能写方法
            return:{
   
            }
        },
        methods:{},
        computed:{}.
        components:{}
	}
</script>
  • 样式
<style>
    样式定义
</style>
2.4.2 基本使用
  • 引入组件
  • 映射成标签
  • 使用组件标签
<template>
    <div>
      <img src="./assets/logo.png" alt="logo" />
      <!-- 3.使用组件标签 -->
      <HelloWorld />
    </div>
</template>

<script>
  //1.引入组件
  import HelloWorld from './components/HelloWorld.vue'
    export default {
        // 2.映射组件标签
      components:{
        HelloWorld
      }
    }
</script>
<style scoped>
</style>

2.4.3 关于标签名与标签属性名书写问题
  • 写法一:一模一样
  • 写法二:大写变小写,并用-连接

2.5 组件间通信

2.5.1 组件间通信基本原则
  • 不要在子组件中直接修改父组件的状态数据
  • 数据在哪,更新数据的行为(函数)就应该定义在哪
2.5.2 vue组件间通信方式
  • props
  • vue的自定义事件
  • 消息订阅与发布(如:pubsub库)
  • slot
  • vuex(后面单独)

2.6 组件间通信1:props

2.6.1 使用组件标签
<my-component name='tom' :age='3' :set-name='setName'></my-component>
2.6.2 定义MyComponent时
  • 在组件内声明所有的props
  • 方式一:只指定名称
props:['name','age','setName']
  • 方式二:指定名称和类型
props:{
    name:String,
    age:Number,
    setName:Function
}
  • 方式三:指定名称/类型/必要性/默认值
props:{
    name:{type:String,required:true,default:xxx}
}
2.6.3 注意
  • 此方式用于父组件向子组件传递数据
  • 所有标签属性都会成为组件对象的属性,模板页面可以直接引用
  • 问题:
    • 如果需要向非子后代传递数据,必须多层逐层传递
    • 兄弟组件间也不能直接pops通信,必须借助父组件才可以

2.7 组件间通信2:vue自定义事件

2.7.1 绑定事件监听
//方式一:通过v-on绑定
@delete_to="deleteTodo"
//方式二:通过$on()
this.$refs.xxx.$on('delete_todo',function(todo)){
                   this.deleteTodo(todo)
                   }
2.7.2 触发事件
//触发事件
this.$emit(eventName,data)
2.7.3 注意
  • 此方式只适用于子组件向父组件发送消息(数据)
  • 问题:隔代组件或兄弟组件间通信此种方式不合适

2.8 组件间通信3:消息订阅与发布

2.8.1 订阅消息
PubSub.subscribt('msg',function(msg,data){})
2.8.2 发布新消息
PubSub.publish('msg',data)
2.8.3 注意

优点:此方式可实现任意关系组件间的通信(数据)

2.8.4 事件的两个重要操作
  • 绑定事件监听(订阅消息)
    • 目标:标签元素
    • 事件名(类型):click/focus
    • 回调函数:function(event){}
  • 触发事件(发布消息)
    • DOM事件:用户在浏览器上对应的界面上做对应的操作
    • 自定义:编码手动触发

2.9 组件间通信4:slot

2.9.1 理解

此方式用于父组件向子组件传递“标签数据”

2.9.2 子组件:Child.vue
<template>
    <div>
        <slot name="xxx">不确定的标签结构1</slot>
        <div>
            组件不确定的标签结构
        </div>
        <slot name="yyy">不确定的标签结构2</slot>
    </div>
</template>
2.9.3 父组件:Parent.vue
<child>
	<div slot="xxx">
        xxx对应的标签结构
    </div>
    <div slot="yyy">
        yyy对应的标签结构
    </div>
</child>

第三章 Vue-Ajax

3.1 Vue项目中常用的2个ajax库

3.1.1 Vue-Resource

vue插件,非官方库,vue1.x使用广泛

3.1.2 axios

通用的Ajax请求库,官方推荐,vue2.x使用广泛

3.2 Vue-Resource的使用

3.2.1 在线文档

https://github.com/pagekit/vue-resource/blob/develop/docs/http.md

3.2.2 下载

npm install vue-resource --save

3.2.3 代码使用
// 引入模块
import VueResource from 'vue-resource'
// 使用插件
Vue.use(VueResource)

// 通过vue组件对象发送ajax请求
this.$http.get('/someURL').then(response)=>{
    //请求成功的数据
    console.log(response.data)//返回结果数据
},(response)=>{
    //错误信息
    console.log(response.statusText)//错误信息
}

3.3 axios的使用

3.3.1 下载

npm install axios --save

3.3.2 代码实现
// 引入模块
import axios from 'axios'

//发送Ajax请求
axios.get(url).then(response=>{
    console.log(response.data)//得到返回结果数据
}).catch(error=>{
    console.log(error.message)//错误信息
})

第四章 Vue UI组件库

4.1 常用的UI组件库

  • Mint UI:
    • 饿了么开源的基于vue的移动端UI组件库
  • Elment UI:
    • 饿了么开源的基于vue的PC端UI组件库

4.2 使用Mint UI

4.2.1 下载

npm install --save mint-ui

4.2.2 实现按需打包
// 下载
npm install --save-dev babel-plugin-component
// 修改babel配置
"plugins":["transform-runtime",["component",{
    "libraryName":"mint-ui",
    "style":true
}]]
4.2.3 mint-ui组件分类
  • 标签组件
  • 非标签组件
4.2.4 使用mint-ui
  • index.html
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimm-scale=1,user-scalable=no" />
<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>
<script>
	if('addEventListener' in document){
        document.addEventListener('DOMContentLoaded',function(){
            FastClick.attach(document.body);
        },false);
    }
    if(!window.Promise){
        document.writelv('<script src="https://as.alipayobjects.com/g/component/fastclick/1.0.6/fastclick.js"></script>')
    }
</script>
  • main.js
import {Button} from 'mint-ui'
Vue.component(Button.name,Button)
  • App.vue
<template>
	<mt-button @click="handleClick" type="primary" style="width:100%">Test</mt-button>
</template>
<script>
	import {Toast} from 'mint-ui'
    export default{
        methods:{
            handleClick(){
                Toast('点击了测试');
            }
        }
    }
</script>

第五章 Vue-router

5.1 理解

5.1.1 说明
  • 官方提供的用来实现SPA(单页)的vue插件
  • github:https://github.com/vuejs/vue-router
  • 中文文档:http://router.vuejs.org/zh-cn/
  • 下载:npm install vue-router --save
5.1.2 相关API说明
  • VueRouter():用于创建路由器的构建函数
new VueRouter({
    //多个配置项
})
  • 路由配置
routes:[
    {//一般路由
       path:'/about',
        component:About
    },
    {//自动跳转路由
        path:'/',
        redirect:'/about'
    }
]
  • 注册路由器
import router from './router'
new Vue({
    router
})
  • 使用路由组件标签
1.<router-link>:用来生成路由链接
    <router-link to='/xxx'>Go To XXX</router-link>
2.<router-view>:用来显示当前路由组件界面
    <router-view></router-view>

5.2 基本路由

5.2.1 路由组件

文件夹命名:pages/views

示例路由组件:Home.vue/About.vue

5.2.2 应用组件:App.vue
<div>
    <!-- 路由链接 -->
    <router-linke to="/about">About</router-linke>
</div>
5.2.3 路由器模块:src/router/index.js
export default new VueRouter({
	routes:[
	{
	 path:'/',
	redirect:'/about'
	},
	{
	path:'/about',
	component:About
	},
	{
	path:'/home',
	component:Home
	}
	]
})
5.2.4 注册路由器:main.js
import Vue from 'vue'
import router from './router'
// 创建vue配置路由器
new Vue({
    el:'#app',
    router,
    render:h=>h(app)
})
5.2.5 优化路由器配置

linkActiveClass:‘active’;//指定选中的路由链接的class

5.2.6 总结:编写使用路由的3步
  • 定义路由组件
  • 注册路由
  • 使用路由
    • <router-link .>
    • <router-view .>
5.3.1 子路由组件

News.vue

Message.vue

5.3.2 配置嵌套路由:router.js
path:'/home',
component:hoem,
children:[
    {
        path:'/home/news',
        component:News
    },
    {
        path:'message',//简化写法	
        component:Message
    }
]
5.3.4 路由链接:Home.vue
<router-link to="/home/news">News</router-link>
<router-link to="/home/message">Message</router-link>
<router-view></router-view>

5.4 缓存路由组件对象

5.4.1 理解
  • 默认情况下,被切换的路由组件对象会死亡释放,再次回来时是重新创建的
  • 如果可以缓存路由组件对象,可以提高用户体验
5.4.2 编码实现
<keep-alive>
	<router-view></router-view>
</keep-alive>

5.5 向路由组件传递数据

5.5.1 方式1:路由路径携带参数(param/query)
  • 配置路由
children:[
    {
        path:'mdetail/:id',
        component:MessageDetail
    }
]
  • 路由路径
<router-link :to="'/home/message/mdetail/'+m.id">{{m.title}}</router-link>
  • 路由组件中读取请求参数
this.$route.params.id
5.5.2 方式2:属性携带数据

5.6 编程式路由导航

相关API
  • this.$router.push(path):相当于点击路由链接(可以返回当前路由界面)
  • this.$router.replace(path):用新路由替换当前路由(不可以返回当前路由界面)
  • this.$router.back():请求(返回)上一个记录路由
  • this.$router.go(-1):请求(返回)上一个记录路由
  • this.$router.go(1):请求下一个记录路由
5.6.2 代码实现
  pushShow (id) {
      this.$router.push(`/home/message/detail/${id}`)
      }

6.1 说明

  • 分析vue作为一个MVVM框架的基本实现原理
    • 数据代理
    • 模板解析
    • 数据绑定
  • 不直接看vue.js的源码
  • 剖析github上仿vue实现的MVVM库
  • 地址:https://github.com/DMQ/mvvm

6.2 准备知识

  • [].slice.call(lis):将伪数组转换为真数组
  • node.noteType:得到节点类型
  • Object.defineProperty(obj,propName,{}):给对象添加/修改属性(指定描述符)
    • configurable:true/false 是否可以重新define
    • enumerable:true/false 是否可以枚举(for…in/keys())
    • value:指定初始值
    • writable:true/false value是否可以修改
    • get:回调函数,用来得到当前属性值
    • set:回调函数,用来监视当前属性值的变化
  • Object.keys(obj):得到对象自身可枚举的属性名的数组
  • DocumentFragment:文档碎片(高效批量更新多个节点)
  • obj.hasOwnProperty(prop):判断prop是否是obj自身的属性
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<div id="test">MVVMd</div>

<ul id="fragment_test">
  <li>test1</li>
  <li>test2</li>
  <li>test3</li>
</ul>


<script type="text/javascript">
  //1. [].slice.call(lis): 根据伪数组生成对应的真数组
  const lis = document.getElementsByTagName('li') // lis是伪数组(是一个特别的对象, length和数值下标属性)
  console.log(lis instanceof Object, lis instanceof Array)
  // 数组的slice()截取数组中指定部分的元素, 生成一个新的数组  [1, 3, 5, 7, 9], slice(0, 3)
  // slice2()
  Array.prototype.slice2 = function (start, end) {
    start = start || 0
    end = start || this.length
    const arr = []
    for (var i = start; i < end; i++) {
      arr.push(this[i])
    }
    return arr
  }
  const lis2 = Array.prototype.slice.call(lis)  // lis.slice()
  console.log(lis2 instanceof Object, lis2 instanceof Array)
  // lis2.forEach()

  //2. node.nodeType: 得到节点类型
  const elementNode = document.getElementById('test')
  const attrNode = elementNode.getAttributeNode('id')
  const textNode = elementNode.firstChild
  console.log(elementNode.nodeType, attrNode.nodeType, textNode.nodeType)

  //3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
  const obj = {
    firstName: 'A',
    lastName: 'B'
  }
  //obj.fullName = 'A-B'
  Object.defineProperty(obj, 'fullName', {
    // 属性描述符:

    // 数据描述符

    //访问描述符
    // 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this为obj
    get () {
      return this.firstName + "-" + this.lastName
    },
    // 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this为obj
    set (value) {
      const names = value.split('-')
      this.firstName = names[0]
      this.lastName = names[1]
    }
  })

  console.log(obj.fullName) // A-B
  obj.fullName = 'C-D'
  console.log(obj.firstName, obj.lastName) // C D

  Object.defineProperty(obj, 'fullName2', {
    configurable: false, //是否可以重新define
    enumerable: true, // 是否可以枚举(for..in / keys())
    value: 'A-B', // 指定初始值
    writable: false // value是否可以修改
  })
  console.log(obj.fullName2)  // A-B
  obj.fullName2 = 'E-F'
  console.log(obj.fullName2) // A-B
  /*Object.defineProperty(obj, 'fullName2', {
    configurable: true,
    enumerable: true,
    value: 'G-H',
    writable: true
  })*/

  //4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
  const names = Object.keys(obj)
  console.log(names)
  //5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
  console.log(obj.hasOwnProperty('fullName'), obj.hasOwnProperty('toString'))  // true false

  //6. DocumentFragment: 文档碎片(高效批量更新多个节点)
  // document: 对应显示的页面, 包含n个elment  一旦更新document内部的某个元素界面更新
  // documentFragment: 内存中保存n个element的容器对象(不与界面关联), 如果更新framgnet中的某个element, 界面不变
  /*
  <ul id="fragment_test">
    <li>test1</li>
    <li>test2</li>
    <li>test3</li>
  </ul>
   */
  const ul = document.getElementById('fragment_test')
  // 1. 创建fragment
  const fragment = document.createDocumentFragment()
  // 2. 取出ul中所有子节点取出保存到fragment
  let child
  while(child=ul.firstChild) { // 一个节点只能有一个父亲
    fragment.appendChild(child)  // 先将child从ul中移除, 添加为fragment子节点
  }

  // 3. 更新fragment中所有li的文本
  Array.prototype.slice.call(fragment.childNodes).forEach(node => {
    if (node.nodeType===1) { // 元素节点 <li>
      node.textContent = 'atguigu'
    }
  })

  // 4. 将fragment插入ul
  ul.appendChild(fragment)

</script>
</body>
</html>

6.3 数据代理

  • 数据代理:通过一个对象代理对另一个对象(在前一个对象内部)中属性的操作(读/写)
  • vue数据代理:通过vm对象来代理data对象中所有属性的操作
  • 好处:更方便的操作data中的数据
  • 基本实现流程
    • 通过Object.defineProperty()给vm添加与data对象的属性对应的属性描述符
    • 所有添加的属性都包含getter/setter
    • getter/setter内部去操作dta中对应的属性数据

代码实现:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>数据代理</title>
</head>
<body>

<!--
1. vue数据代理: data对象的所有属性的操作(读/写)由vm对象来代理操作
2. 好处: 通过vm对象就可以方便的操作data中的数据
3. 实现:
  1). 通过Object.defineProperty(vm, key, {})给vm添加与data对象的属性对应的属性
  2). 所有添加的属性都包含get/set方法
  3). 在get/set方法中去操作data中对应的属性
-->
<div id="test"></div>

<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
  const vm = new MVVM({
    el: "#test",
    data: {
      name: '张三2'
    }
  })
  console.log(vm.name)  // 读取的是data中的name,  vm代理对data的读操作
  vm.name = '李四2' // 数据保存到data中的name上, vm代理对data的写操作
  console.log(vm.name, vm._data.name)
</script>
</body>
</html>

6.4 模板解析

6.4.1 模板解析的基本流程
  • 将el的所有子节点取出,添加到一个新建的文档fragment对象中
  • 对fragment中的所有层次子节点递归进行编译解析处理
    • 对大括号表达式文本节点进行解析
    • 对元素节点的指令属性进行解析
      • 事件指令解析
      • 一般指令解析
  • 将解析后的fragment添加到el中显示
6.4.2 模板解析1:大括号表达式解析
  • 根据正则对象得到匹配出的表达式字符串:子匹配/RegExp.$1 name
  • 从data中取出表达式对应的属性值
  • 将属性值设置为文本节点的textContent
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模板解析_表达式_vue</title>
</head>
<body>
<div id="test">
  <p>{{name}}</p>
</div>

<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
  new MVVM({
    el: '#test',
    data: {
      name: 'SADAMU'
    }
  })
</script>
</body>
</html>
6.4.3 模板解析2:事件指令解析
  • 从指令名中取出事件名
  • 根据指令的值(表达式)从methods中得到对应的事件处理函数对象
  • 给当前元素节点绑定指定事件名和回调函数的dom事件监听
  • 指令解析完后,移除此指令属性
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模板解析_事件指令</title>
</head>
<body>
  <div id="test">
    <button v-on:click="test">测试</button>
  </div>

  <script type="text/javascript" src="js/mvvm/compile.js"></script>
  <script type="text/javascript" src="js/mvvm/mvvm.js"></script>
  <script type="text/javascript" src="js/mvvm/observer.js"></script>
  <script type="text/javascript" src="js/mvvm/watcher.js"></script>
  <script type="text/javascript">
    new MVVM({
      el: '#test',
      data: {
        msg: 'hello atguigu'
      },
      methods: {
        test () {
          alert(this.msg)
        }
      }
    })
  </script>
</body>
</html>
6.4.4 模板解析3:一般指令解析
  • 得到指令名和指令值(表达式) text/html/class msg/myClass
  • 从data中根据表达式得到对应的值
  • 根据指令名确定需要操作元素节点的什么属性
    • v-text—textContent属性
    • v-html—innerHTML属性
    • v-class—className属性
  • 将得到的表达式的值设置到对应的属性上
  • 移除元素的指令属性
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模板解析_一般指令</title>
  <style>
    .aclass {
      color: red;
    }
    .bClass {
      font-size: 30px;
    }
  </style>
</head>
<body>
<div id="test">
  <p v-text="msg"></p>
  <p v-html="msg"></p>
  <p class="bClass" v-class="myClass">xxxxxx</p>
</div>

<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
  new MVVM({
    el: '#test',
    data: {
      msg: '<a href="http://www.atguigu.com">尚硅谷</a>',
      myClass: 'aclass'
    },
    methods: {
      test () {
        alert(this.msg)
      }
    }
  })
</script>
</body>
</html>

6.5 数据绑定

6.5.1 数据绑定

一旦更新了data中的某个属性数据,所有界面上直接使用或间接使用此属性的节点更新

6.5.2 数据劫持
  • 数据劫持是vue中用来实现数据绑定的一种技术
  • 基本思想:通过defineProperty()来监视data中所有属性(任意层次)数据的变化,有变化就去更新界面
6.5.3 四个重要对象
  • Observer

    • 用来对data所有属性数据进行劫持的构造函数
    • 给data中所有属性重新定义属性描述(get/set)
    • 为data中的每个属性创建对应的dep对象
  • Dep(Depend)

    • data中的每个属性(所有层次)都对应一个dep对象

    • 创建的时机

      • 在初始化define data中各个属性时创建对应的dep对象
      • 在data中的某个属性值被设置为新的对象时
    • 对象的结构

      {
          id,//每个dep都有一个唯一的id
          subs//包含n个对应watcher的数组(subscribes的简写)
      }
      
    • subs属性说明

      • 当watcher被创建时,内部将当前watcher对象添加到对应的dep对象的subs中
      • 当此data属性的值发生改变时,subs中所有的watcher都会受到更新的通知,从而最终更新对应的界面
  • Compiler

    • 用来解析模板页面的对象的构造函数(一个实例)
    • 利用compile对象解析模板页面
    • 每解析一个表达式(非事件指令)都会创建一个对应的watcher对象,并建立watcher与dep的关系
    • complie与watcher的关系:一对多的关系
  • Watcher

    • 模板中每个非事件指令或表达式都对应一个watcher对象

    • 监视当前表达式数据的变化

    • 创建的时机:在初始化编译模板时

    • 对象的组成

      {
          vm,//vm对象
          exp,//对应指令的表达式
          cb,//当表达式所对应的数据发生改变的回调函数
          value,//表达式当前的值
          depIds//表达式中各级属性所对应的dep对象的集合对象
          	  //属性名为dep的id,属性值为dep
      }
      
  • 总结:dep与watcher的关系:多对多

    • data中的一个属性对应一个dep,一个dep中可能包含多个watcher(模板中有几个表达式使用到了同一个属性)
    • 模板中一个非事件表达式对应一个watcher,一个watcher中可能包含多个dep(表达式是多层的:a.b)
    • 数据绑定使用到2个核心技术
      • defineProperty()
      • 消息订阅与发布

代码实现

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>数据劫持-数据绑定</title>
  <!--
    1. 数据绑定
      * 初始化显示: 页面(表达式/指令)能从data读取数据显示 (编译/解析)
      * 更新显示: 更新data中的属性数据==>页面更新
  -->
</head>
<body>

<div id="test">
  <p>{{name}}</p>
  <p v-text="name"></p>
  <p v-text="wife.name"></p>
  <button v-on:click="update">更新</button>
</div>

<!--
dep
  与data中的属性一一对应  (4)
watcher
  与模板中一般指令/大括号表达式一一对应 (3)

1. 什么时候一个dep中关联多个watcher?
  多个指令或表达式用到了当前同一个属性  {{name}} {{name}}
2. 什么时候一个watcher中关联多个dep?
  多层表达式的watcher对应多个dep    {{a.b.c}}
-->


<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
  new MVVM({
    el: '#test',
    data: {
      name: 'sadamu',  // dep0
      wife: { // dep1
        name: 'binbin', // dep2
        age: 18 // dep3
      }
    },
    methods: {
      update () {
        this.name = 'avatar'
      }
    }
  })
</script>
</body>

</html>

6.6 MVVM原理分析

在这里插入图片描述

6.7 双向数据绑定

  • 双向数据绑定是建立在单向数据绑定(model=>View)的基础之上的
  • 双向数据绑定的实现流程
    • 在解析v-model指令时,给当前元素添加input监听
    • 当input的value发生改变时,将最新的值赋值给当前表达式所对应的data属性

代码实现

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>数据双向绑定</title>
</head>
<body>
<div id="test">
  <input type="text" v-model="msg">
  <p>{{msg}}</p>
</div>

<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
  new MVVM({
    el: '#test',
    data: {
      msg: 'haha'
    }
  })
</script>
</body>
</html>

第七章 Vuex

7.1 Vuex理解

7.1.1 vuex是什么
  • github站点:https://github.com/vuejs/vuex
  • 在线文档:https://vuex.vuejs.org/zh-cn/
  • 简单来说,对vue应用中多个组件的共享状态进行集中式的管理(读/写)
7.1.2 状态自管理应用
  • state:驱动应用的程序源
  • view:以声明方式将state映射到视图
  • actions:响应在view上的用户输入导致的状态变化(包含n个更新状态的方法)
    在这里插入图片描述
7.1.3 多组件共享状态的问题
  • 多个视图依赖于同一状态
  • 来自不同视图的行为需要变更同一状态
  • 以前的解决办法
    • 将数据以及操作数据的行为都定义在父组件
    • 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
  • vuex就是用来解决这个问题的

7.2 vuex核心概念和API

7.2.1 state
  • vuex管理的状态对象

  • 它应该是唯一的

    const state={
        xxx:initValue
    }
    
7.2.2 mutations
  • 包含多个直接更新state的方法(回调函数)的对象

  • 谁来触发:action中的commit(‘mutation名称’)

  • 只能包含同步的代码,不能写异步代码

    const mutations={
        yyy(state,{data1}){
            //更新state的某个属性
        }
    }
    
7.2.3 actions
  • 包含多个事件回调函数的对象

  • 通过执行:commit()来触发mutation的调用,间接更新state

  • 谁来触发:组件中:$store.dispatch(‘action名称’,data1) //‘zzz’

  • 可以包含异步代码(定时器,ajax)

    const actions={
        zzz({commit,state},data1){
            commit('yyy',{data1})
        }
    }
    
7.2.4 getters
  • 包含多个计算属性(get)的对象

  • 谁来读取:组件中:$store.getters.xxx

    const getters={
        mmm(state){
            return ...
        }
    }
    
7.2.5 modules
  • 包含多个module
  • 一个module是一个store的配置对象
  • 与一个组件(包含有共享数据)对应
7.2.6 向外暴露store对象
export default new Vuex.Store({
    state,
    mutations,
    actions,
    getters
})
7.2.7 组件中
import {mapState,mapGetters,mapActions} from 'vuex'
export default{
    computed:{
        ...mapState(['xxx']),
        ...mapGetters(['mmm']),
    },
    methods:mapActions(['zzz'])
}

{{xxx}}{{mmm}}@click="zzz(data)"
7.2.8 映射store
import store from './store'
new Vue({
    store
})
7.2.9 store对象
  • 所有用vuex管理的组件中都躲了一个属性store,它就是一个store对象
  • 属性:
    • state:注册的state对象
    • getters:注册的getters对象
  • 方法:
    定是建立在单向数据绑定(model=>View)的基础之上的
  • 双向数据绑定的实现流程
    • 在解析v-model指令时,给当前元素添加input监听
    • 当input的value发生改变时,将最新的值赋值给当前表达式所对应的data属性

代码实现

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>数据双向绑定</title>
</head>
<body>
<div id="test">
  <input type="text" v-model="msg">
  <p>{{msg}}</p>
</div>

<script type="text/javascript" src="js/mvvm/compile.js"></script>
<script type="text/javascript" src="js/mvvm/mvvm.js"></script>
<script type="text/javascript" src="js/mvvm/observer.js"></script>
<script type="text/javascript" src="js/mvvm/watcher.js"></script>
<script type="text/javascript">
  new MVVM({
    el: '#test',
    data: {
      msg: 'haha'
    }
  })
</script>
</body>
</html>

第七章 Vuex

7.1 Vuex理解

7.1.1 vuex是什么
  • github站点:https://github.com/vuejs/vuex
  • 在线文档:https://vuex.vuejs.org/zh-cn/
  • 简单来说,对vue应用中多个组件的共享状态进行集中式的管理(读/写)
7.1.2 状态自管理应用
  • state:驱动应用的程序源
  • view:以声明方式将state映射到视图
  • actions:响应在view上的用户输入导致的状态变化(包含n个更新状态的方法)

[外链图片转存中…(img-44Q93wcS-1590936591436)]

7.1.3 多组件共享状态的问题
  • 多个视图依赖于同一状态
  • 来自不同视图的行为需要变更同一状态
  • 以前的解决办法
    • 将数据以及操作数据的行为都定义在父组件
    • 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
  • vuex就是用来解决这个问题的

7.2 vuex核心概念和API

7.2.1 state
  • vuex管理的状态对象

  • 它应该是唯一的

    const state={
        xxx:initValue
    }
    
7.2.2 mutations
  • 包含多个直接更新state的方法(回调函数)的对象

  • 谁来触发:action中的commit(‘mutation名称’)

  • 只能包含同步的代码,不能写异步代码

    const mutations={
        yyy(state,{data1}){
            //更新state的某个属性
        }
    }
    
7.2.3 actions
  • 包含多个事件回调函数的对象

  • 通过执行:commit()来触发mutation的调用,间接更新state

  • 谁来触发:组件中:$store.dispatch(‘action名称’,data1) //‘zzz’

  • 可以包含异步代码(定时器,ajax)

    const actions={
        zzz({commit,state},data1){
            commit('yyy',{data1})
        }
    }
    
7.2.4 getters
  • 包含多个计算属性(get)的对象

  • 谁来读取:组件中:$store.getters.xxx

    const getters={
        mmm(state){
            return ...
        }
    }
    
7.2.5 modules
  • 包含多个module
  • 一个module是一个store的配置对象
  • 与一个组件(包含有共享数据)对应
7.2.6 向外暴露store对象
export default new Vuex.Store({
    state,
    mutations,
    actions,
    getters
})
7.2.7 组件中
import {mapState,mapGetters,mapActions} from 'vuex'
export default{
    computed:{
        ...mapState(['xxx']),
        ...mapGetters(['mmm']),
    },
    methods:mapActions(['zzz'])
}

{{xxx}}{{mmm}}@click="zzz(data)"
7.2.8 映射store
import store from './store'
new Vue({
    store
})
7.2.9 store对象
  • 所有用vuex管理的组件中都躲了一个属性store,它就是一个store对象
  • 属性:
    • state:注册的state对象
    • getters:注册的getters对象
  • 方法:
    • dispatch(actionName,data):分发调用action

小结

这篇权当是学习Vue的一个基础,更多内容,我打算做一个网站,遇到了再更新吧。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ruozhuliufeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值