Vue框架学习

271 阅读16分钟

1. 第1章:Vue核心

1.1.、Vue的基本认识

1.1.1、官网

1.1.2、 介绍描述

  • 渐进式JavaScript 框架
  • 作者: 尤雨溪(一位华裔前Google工程师)
  • 作用: 动态构建(显示 )用户界面

1.1.3、 Vue的特点

  • 遵循MVVM模式
  • 编码简洁, 体积小, 运行效率高, 适合移动/PC端开发
  • 它本身只关注UI, 可以轻松引入vue插件或其它第三库开发项目

1.1.4、与其它前端JS框架的关联

  • 借鉴angular的模板数据绑定技术
  • 借鉴react的组件化虚拟* *DOM**技术

1.1.5、Vue扩展插件

  1. vue-cli: vue脚手架
  2. vue-resource(axios): ajax请求
  3. vue-router: 路由
  4. vuex: 状态管理
  5. vue-lazyload: 图片懒加载
  6. vue-scroller: 页面滑动相关
  7. mint-ui: 基于vue的UI组件库(移动端)
  8. element-ui: 基于vue的UI组件库(PC端)

1.2 、Vue的基本使用

1.2.1、编码

`<**div** **id=****"app"**>   <**input** **type=****"text"** **v-model=****"username"**>   <**p**>Hello, {{username}}</**p**>   </**div**>      <**script** **type=****"text/javascript"** **src=****"../js/vue.js"**></**script**>   <**script** **type=****"text/javascript"**>   **new** **Vue**({     **el**: **'#app'**,     **data**: {       **username**: **'atguigu'**     }   })   </**script**>`

1.2.2、 使用vue开发者工具调试

  • 通过chrome应用商店在线下载: Vue.js devtools
  • 通过本地安装文件安装: Vue.js devtools-5.3.3_0.crx

1.2.3、 理解Vue的MVVM

  • MVVM 说的就是双向数据绑定模型
  • M 代表model 就是我们的数据
  • V 代表的view 就是我们的页面
  • VM代表的就是Vue的实例化对象
  • 双向数据绑定 :数据可以从data 流向页面 页面的数据被更新,也会从页面流向data

1. 3、模板语法

1.3.1、模板的理解

  • 动态的html页面
  • 包含了一些JS语法代码
  • 双大括号表达式
  • 指令(以v-开头的自定义标签属性)

1.3.2、双大括号表达式(插值)

  • 语法: {{exp}}
  • 功能: 向页面输出数据
  • 可以调用对象的方法

1.3.3、指令一: 强制数据绑定

  • 功能: 指定变化的属性值

  • 完整写法: v-bind:xxx='yyy' //yyy会作为表达式解析执行

    <a v-bind:href="url">点击去学习</a>
    
  • 简洁写法: :xxx='yyy'

    <a :href="url">点击去学习</a>
    

1.3.4、 指令二: 绑定事件监听

  • 功能: 绑定指定事件名的回调函数

  • 完整写法:

    <button v-on:click="test">点击事件</button>
    <button v-on:keyup="test">键盘事件</button>
    <button v-on:keyup.enter="test">键盘事件+指定enter键</button>
    
  • 简洁写法:

    <button @click="test">点击事件</button>
    <button @keyup="test">键盘事件</button>
    

一旦用了指令,那么这个被指令使用的属性,属性值就是js代码了,而原本的 “” (双引号) 不再代表字符串的边界

1.4.、计算属性和监视

1.4.1、计算属性

  • 在computed属性对象中定义计算属性的方法

  • 在页面中使用{{方法名}}来显示计算的结果

  • computed内部只能是同步返回数据,不能异步返回数据

  • methods和computed的区别

    • 计算属性效率是比较高的,因为有缓存
  • <script>
    computed:{
        //计算属性的完整写法
        fullName:{
          get(){
            return this.firstName + '-' + this.lastName
          },
          set(val){
            //为计算属性添加了监视,如果数据后期修改,会发生响应式的变化
          }
        },
            
        //如果计算属性当中只有get方法,那么可以简写为如下写法
        fullName(){
           return this.firstName + '-' + this.lastName
         },
    </script>
    

1.4.2、监视属性

  • 通过通过vm对象的$watch()或watch配置来监视指定的属性
  • 监视的数据一定是存在的,而且是可以变化的
  • 当属性变化时, 回调函数自动调用, 在函数内部进行计算
  • <script>
        watch:{
            firstName:{
              //这个对象是一个配置对象
              //当数据发生改变的时候会自动调用handler回调
              handler(newVal,oldVal){
                this.fullName2 = newVal + '-' + this.lastName
                setTimeout(() => {
                  // 异步修改数据
                  this.fullName4 = '呵呵'
                }, 1000);
              },
              immediate:true //配置这个配置项的作用是无论监视到属性发生不发生变化,都要强制的执行一次回调
            }
        }
    </script>
    

1.4.3、计算属性和监视属性的区别

  • 同步用计算属性(computed),一般是没有这个值但是想要用这个值,那么根据已有的去做计算
  • 异步用监视属性(watch),一般监视属性,监视的属性以及后期要更改的属性都必须有
  • 通常能用computed的场合都可以使用watch去解决,但是能用watch解决的computed不一定能解决

1.4.4、计算属性高级

  • 通过getter/setter实现对属性数据的显示和监视
  • 计算属性存在缓存, 多次读取只执行一次getter计算

1.4.5、编码

<div id="demo">
  姓: <input type="text" placeholder="First Name" v-model="firstName"><br>
  名: <input type="text" placeholder="Last Name" v-model="lastName"><br>
  姓名1(单向): <input type="text" placeholder="Full Name" v-model="fullName1"><br>
  姓名2(单向): <input type="text" placeholder="Full Name" v-model="fullName2"><br>
  姓名3(双向): <input type="text" placeholder="Full Name2" v-model="fullName3"><br>
</div><script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  var vm = new Vue({
    el: '#demo',
    data: {
      firstName: 'Kobe',
      lastName: 'bryant',
      fullName2: 'Kobe bryant'
    },
​
    computed: {
      fullName: function () {
        return this.firstName + " " + this.lastName
      },
​
      fullName3: {
        get: function () {
          return this.firstName + " " + this.lastName
        },
        set: function (value) {
          var names = value.split(' ')
          this.firstName = names[0]
          this.lastName = names[1]
        }
      }
    },
​
    watch: {
      lastName: function (newVal, oldVal) {
        this.fullName2 = this.firstName + ' ' + newVal
      }
    }
  })
​
  vm.$watch('firstName', function (val) {
    this.fullName2 = val + ' ' + this.lastName
  })

1.5、class 与 style 绑定

1.5.1.、理解

  • 在应用界面中, 某个(些)元素的样式是变化的
  • class/style绑定就是专门用来实现动态样式效果的技术

1.5.2、class绑定

  1. 字符串形式-----类名不确定
  2. 对象形式------类名确定,但是不知道哪个生效
  3. 数组形式------类名有几个都生效
  • 表达式是字符串: 'classA'

    <p :class="classA" class="classB">表达式是字符串</p>
    
  • 表达式是对象: {classA:isA, classB: isB}

    <p :class="{classA:isA,classB:isB}">表达式是对象</p>
    
  • 表达式是数组: ['classA', 'classB']

    <p :class="['classA','classB','classC']">我爱你赵丽颖3</p>
    

1.5.3、style绑定

  • 必须把原来的东西写在对象当中,样式名不变,样式值是动态的

  • 样式名如果不合法,要变为小驼峰写法

    <p style="color:aqua;font-size: 80px;">静态写法</p>
    
    <p :style="{color:myColor,fontSize: mySize}">动态写法</p>
    

1.6、条件渲染

1.6.1、条件渲染指令

  • v-if与v-else (隐藏的时候直接删除元素节点)
  • v-show (隐藏的时候通过css样式去隐藏)

1.6.2、比较v-if与v-show

  • 如果需要频繁切换 v-show 较好
  • 当条件不成立时, v-if的所有子节点不会解析(项目中使用)

1.6.3、实例

<div id="demo">
  <h2 v-if="ok">表白成功</h2>
  <h2 v-else>表白失败</h2>
  <h2 v-show="ok">求婚成功</h2>
  <h2 v-show="!ok">求婚失败</h2>
​
  <br>
  <button @click="ok=!ok">切换</button>
</div><script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript">
  var vm = new Vue({
    el: '#demo',
    data: {
      ok: false
    }
  })
</script>

1.7、响应式数据

在vue当中 一开始data中的属性数据都是响应式的

  • 数组的数据------说的每个数组当中元素整体
  • 对象的数据------说的对象的属性

1.7.1、vue中数组和对象处理响应式数据不同之处

  • 如果修改的是对象的属性,随便改,都是响应式的。因为Vue一开始就为data当中所有的属性通过Object.defineProperty添加了get和set
  • 数组修改的时候,必须使用特定的几个方法才能是响应式,如果直接通过下标操作数组的数据,不是响应式。Vue当中给数组部分方法添加了修改页面的功能(重写数组的方法)

1.7.2、总结

  • 对象是通过Object.defineProperty添加了get和set
  • 数组是 重写数组的方法

1.8、事件处理

1.8.1、绑定监听

  1. 事件监听的完整写法

    <button v-on:click="test1">test1</button>
    
  2. 事件监听的简写

    <button @click="test2()">test2</button>
    
  3. 事件监听的默认参数

    <!--事件回调函数在调用的时候可以传递$event,名字不能随意改,这个东西就是事件对象-->
    <button @click="test2($event)">test2</button>
    
  4. 事件回调,穿自定义参数

    <!--事件在添加的时候如果要传递参数的,1、$event  2、自己的参数-->
    <button @click="test3($event,'ym')">test3</button>
    

1.8.2、事件修饰符

  • .prevent : 阻止事件的默认行为

    <a href="##" @click.prevent="testPrevent">点我去学习</a>
    
    testPrevent(event){
      console.log('i love you~')
      // 或者event.preventDefault();
    },
    
  • .stop : 停止事件冒泡

    <div @click.stop="inner"></div>
    
    inner(event){
      console.log('内部')
      // 或者event.stopPropagation();//阻止冒泡  吓唬
    },
    

1.8.3、按键修饰符

  • .keycode : 操作的是某个keycode值的键
  • .keyName : 操作的某个按键名的键(少部分)
  • <input @keyup.8="test6">
    <input @keyup.enter="test6">
    

1.9、表单输入绑定

v-model默认收集的其实是你表单类元素当中的value值

1.9.1、使用v-model对表单数据自动收集

  • text输入框数据------输入的数据会自动赋值给data的数据

    <label>
    用户名:<input type="text" id="in1" v-model="username">
    </label>
    
  • textarea文本域数据------写上的文本会自动赋值给data的数据

    <textarea cols="30" rows="10" v-model="desc">
    请输入描述...
    </textarea>
    
  • radio单选框数据------选中的value值会自动付给data的数据

    <label>
    男:<input type="radio" name="sex" value="male" v-model="gender">
    </label>
    <label>
    女:<input type="radio" name="sex" value="female" v-model="gender">
    </label>
    
  • checkbox多选框数据------选中的value值会自动添加到data数组当中

    爱好:
    <label>
    篮球<input type="checkbox" value="basketball" v-model="favs">
    </label>
    <label>
    足球<input type="checkbox" value="football" v-model="favs">
    </label>
    
  • select下拉菜单数据------选中的id值会自动赋值给data的数据

    城市:
    <!-- select 这个标签它的value值是 选中的option的value值 -->
    <select v-model="userInfo.cityId">
        <option 
            :value="city.id" 
            v-for="(city,index) in citys" 
            :key="city.id">{{city.name}}
        </option>
    </select>
    

1.10、Vue实例生命周期

1.10.1、生命周期流程图

网址:cn.vuejs.org/v2/guide/in…生命周期图示

1.10.2、vue生命周期分析

  1. 初始化显示

    • beforeCreate()-----初始化之前(数据打印看不到)
    • created()------初始化之后(数据打印可以看到)
  2. 挂载阶段

    • beforeMount()-----挂载之前(初始页面的元素看不到)
    • mounted()------挂载之后(初始页面元素可以看到)
  3. 更新状态: this.xxx = value

    • beforeUpdate()------更新前(数据更新,页面未更新)
    • updated()------更新后(数据更新,页面更新)
  4. 销毁vue实例: vm.$destory()

    • beforeDestory()-----销毁前
    • destoryed()------销毁后

1.10.3、常用的生命周期方法

  • mounted(): 发送ajax请求, 启动定时器等异步任务
  • beforeDestory(): 做收尾工作, 如: 清除定时器

1.11、 过渡&动画

1.11.1、vue动画的理解

  1. 操作css的trasition或animation

  2. vue会给目标元素添加/移除特定的class

  3. 过渡的相关类名

    • xxx-enter-active: 指定显示的transition
    • xxx-leave-active: 指定隐藏的transition
    • xxx-enter/xxx-leave-to: 指定隐藏时的样式

1.11.2、基本过渡动画的编码

  1. 在目标元素外包裹

  2. 定义class样式

    • 指定过渡样式: transition
    • 指定隐藏时的样式: opacity/其它

1.12、 过滤器

1.12.1、理解过滤器

  • 功能: 对要显示的数据进行特定格式化后再显示
  • 注意: 并没有改变原本的数据, 是产生新的对应的数据

1.12.2、定义和使用过滤器

  1. 定义过滤器

    Vue.filter(filterName, function(value[,arg1,arg2,...]){
        // 进行一定的数据处理
        return newValue
    })
    
  2. 使用过滤器

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

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-model双向数据绑定
ref指定唯一标识, vue对象通过$els属性访问这个元素对象
v-cloak防止闪现, 与css配合: [v-cloak] { display: none }

1.13.2、自定义指令

自定义指令分为定义全局指令和局部指令

  • 自定义指令参数:

    1. 指令名称(不包含v- 只能是全小写)

    2. 回调函数

      • 回调函数参数:
      • 1、使用这个指令的那个节点
      • 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.14、自定义插件

1.14.1、说明

  • Vue插件是一个包含install方法的对象
  • 通过install方法给Vue或Vue实例添加方法, 定义全局指令等

1.14.2、实例

  • 定义自定义插件
  • /**
     * 自定义Vue插件
     */
    (function () {
      const MyPlugin = {}
      MyPlugin.install = function (Vue, options) {
        // 1. 添加全局方法或属性
        Vue.myGlobalMethod = function () {
          alert('Vue函数对象方法执行')
        }
        // 2. 添加全局资源
        Vue.directive('my-directive', function (el, binding) {
          el.innerHTML = "MyPlugin my-directive " + binding.value
        })
        // 3. 添加实例方法
        Vue.prototype.$myMethod = function () {
          alert('vue实例对象方法执行')
        }
      }
      window.MyPlugin = MyPlugin
    })()
    
  • 页面使用插件
  • <div id="demo">
      <!--使用自定义指令-->
      <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">
    ​
      //使用自定义插件
      Vue.use(MyPlugin)
    ​
      var vm = new Vue({
        el: '#demo',
        data: {
          msg: 'atguigu'
        }
      })
      //调用自定义的静态方法
      Vue.myGlobalMethod()
      //调用自定义的对象方法
      vm.$myMethod()
    </script>
    

2、 第2章:vue组件化编程

2.1、模块与组件和模块化与组件化的理解

2.1.1、模块

  • 理解: 向外提供特定功能的js程序, 一般就是一个js文件
  • 为什么: js代码更多更复杂
  • 作用: 复用js, 简化js的编写, 提高js运行效率

2.1.2、 组件

  • 理解: 用来实现特定(局部)界面功能效果的代码集合(html/css/js/image)
  • 为什么: 一个界面的功能很复杂
  • 作用: 复用编码, 简化项目编码, 提高运行效率

2.1.3、模块化

  • 当应用的js都以模块来编写的, 这个应用就是一个模块化的应用

2.1.4、组件化

  • 当应用是以多组件的方式实现, 这个应用就是一个组件化的应用, 应用的开发方式就是组件化的

2.2、组件定义与使用1(非单文件)

2.2.1、实例1(完整写法)

  1. 定义组件,本质上是根据一个配置对象定义返回一个函数,后期是当构造函数使用

    const VueComponent = Vue.extend({
       //组件配置对象和Vue的配置对象很相似,除了el
       data(){
         // 组件的配置对象当中data只能写函数
         return {
           count:0
         }
       },
       template:'<div><h2 >我爱你赵丽颖</h2></div>'
     })
    
  2. 注册(全局注册和局部注册) 本质是把一个标签的名称和刚才定义出来的函数绑定在一起

    Vue.component('mybutton',VueComponent)
    
  3. 使用

    <div id="app">
        <mybutton></mybutton>
    </div>
    

2.2.2、实例2(简写)

  1. 定义带注册

    Vue.component('mybutton',{
       //组件配置对象和Vue的配置对象很相似,除了el
       data(){
         // 组件的配置对象当中data只能写函数
         return {
           count:0
         }
       },
       template:'<div><h2>我爱你赵丽颖</h2></div>'
    })
    
  2. 使用

    <div id="app">
        <mybutton></mybutton>
    </div>
    

2.2.3、问题

  • 模板编写没有提示
  • 没有构建过程, 无法将ES6转换成ES5
  • 不支持组件的CSS

2.3、组件定义与使用2(单文件组件)

2.3.1、基本使用

一个.vue文件通常我们说是一个组件,但是本质不是,本质其实是定义组件的配置对象

  1. 创建一个Test.vue文件

    //模板页面
    <template>
      <div>
        <h2>我爱你赵丽颖</h2>
        <button>爱了{{count}}</button>
      </div>
    </template>
    ​
    //js模块对象
    <script>
    export default {
      name: '',
      data(){
        return {
          count:0
        }
      }
    }
    </script>
    ​
    // css样式
    <style scoped>
      h2{
        color:hotpink
      }
    </style>
    
  2. 引入,注册

    <script>
        import Test from './Test.vue'
        new Vue({
          el:'#app',
          components:{
            Test   //定义带注册Test
          },
          template:'<App/>'
        })
    </script>
    
  3. 使用

    <div id="app">
        <Test></Test>
    </div>
    

2.4、组件间通信

2.4.1、组件间通信基本原则

  • 不要在子组件中直接修改父组件的状态数据
  • 数据在哪, 更新数据的行为(函数)就应该定义在哪

2.4.2、组件之间的关系

  1. 父子
  2. 祖孙
  3. 兄弟
  4. 其它

2.4.3、vue组件间通信方式

  1. props
  2. vue的自定义事件
  3. 全局事件总线
  4. 消息订阅与发布
  5. slot
  6. vuex(后面单独讲)

2.5、组件间通信1: props

2.5.1、使用组件标签时

<MyComponent name='tom' :age='3' :setName='setName'></MyComponent>

2.5.2、定义组件时

  1. 在组件内声明所有的props

  2. 方式一: 只指定名称

    props: ['name', 'age', 'setName']
    
  3. 方式二: 指定名称和类型

    props: {
        name: String,
        age: Number,
        setNmae: Function
    }
    
  4. 方式三: 指定名称/类型/必要性/默认值

    props: {
        name: {
            type: String, 
            required: true, 
            default:xxx
        },
    } 
    

2.5.3、总结

  1. 是组件通信最常用最简单的一种方式

  2. 适用场合:适用于父子之间

  3. 父可以给子传递函数数据和非函数数据

    1. 传递非函数数据,本质就是父亲给儿子传数据
    2. 传递函数数据,本质是父亲想要儿子的数据,通过函数调用传参的方式把数据传递给父亲
  4. 问题:

    • 如果需要向非子后代传递数据必须多层逐层传递
    • 兄弟组件间也不能直接props通信, 必须借助父组件才可以

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

2.6.1、vm与组件对象的关系

Vue原型对象就是组件对象的原型对象的原型对象

2.6.2、绑定事件监听

  • 完整写法

    <Header ref="header"/>
    this.$refs.header.$on('addTodo', this.addTodo)
    
  • 简写

    <Header @addTodo="addTodo"/>
    

2.6.3、触发事件

this.$emit('addTodo', todo)

2.6.4、总结

  1. 用来实现子组件向父组件通信的方式, 功能类似于函数类型的props

  2. 隔代组件或兄弟组件间通信此种方式不合适

  3. 实现流程

    1. 父组件: 在父组件当中可以看到子组件对象,给子组件对象绑定自定义事件$on 回调函数的定义在父组件中
    2. 子组件: 在子组件当中,我们需要传递数据的地方,去触发自己身上的事件$emit,调用回调函数中传参给父

2.6.5、自定义事件和系统定义事件的区别

  • 自己定义的事件:事件类型(不限)和回调函数(自己定义自己触发,默认传参是自己传就有,不传就是undefined)
  • 系统定义的事件:事件类型(固定几个)和回调函数(自己定义系统触发,默认参数是事件对象)

2.7、组件间通信3: 全局事件总线

2.7.1、Vue原型对象上包含事件处理的方法

事件描述
$on(eventName, listener)绑定自定义事件监听
$emit(eventName, data):分发自定义事件
$off(eventName)解绑自定义事件监听
$once(eventName, listener):绑定事件监听, 但只能处理一次

2.7.2、所有组件对象的原型对象的原型对象就是Vue的原型对象

  • 所有组件对象都能看到Vue原型对象上的属性和方法
  • Vue.prototype.bus=newVue(),所有的组件对象都能看到bus = new Vue(), 所有的组件对象都能看到bus这个属性对象

2.7.3、全局事件总线

  • 包含事件处理相关方法的对象(只有一个)
  • 所有的组件都可以得到

2.7.4、Vue中全局事件总线_流程分析

2.7.5、全局事件总线流程

  1. 指定事件总线对象

    new Vue({
      // 尽量早的执行挂载全局事件总线对象的操作
      beforeCreate () { 
        Vue.prototype.$Bus = this
      }
      el:'#app',
      render: h=>h(App)
    })
    
  2. 绑定事件监听

    this.$Bus.$on('deleteTodo', this.deleteTodo)
    
  3. 分发事件监听

    this.$Bus.$emit('deleteTodo', this.index)
    
  4. 解绑事件监听

    this.$Bus.$off('deleteTodo')
    

2.7.6、总结

  1. 全局事件总线是任意关系的组件间通信(传值/数据)的解决方案

  2. 全局事件总线是一个对象, 有事件处理的相关方法, 在vue中就是vm对象

  3. 实现流程

    1. 将新创建的vm或最外层已有的vm作为总线对象保存到Vue的原型对象上

    2. 需要传值/数据的组件: 得到总线对象, 调用其$emit()分发事件, 携带数据

    3. 需要接收消息/数据的组件:

      1. 在mounted()中: 得到总线对象, 调用其$on()绑定监听, 接收数据
      2. 在beforeDestroy()中: 得到总线对象, 调用其$off()解绑监听

2.8、组件间通信4: 消息订阅(subscribe)与发布(publish)

2.8.1、理解

  1. 这种方式的思想与全局事件总线很相似

  2. 它包含以下操作:

    1. 订阅消息------对应绑定事件监听
    2. 发布消息------分发事件
    3. 取消消息订阅------解绑事件监听
  3. 需要引入一个消息订阅与发布的第三方实现库: PubSubJS

2.8.2、使用PubSubJS

  1. 在线文档: github.com/mroderick/P…

  2. 下载: npm install -S pubsub-js

  3. 相关语法

    1. 引入

      import PubSub from 'pubsub-js' 
      
    2. 订阅消息, 返回token

      PubSub.subscribe(‘msgName’, functon(msgName, data){ }) 
      
    3. 发布消息, 触发订阅的回调函数调用

       PubSub.publish(‘msgName’, data)
      
    4. 取消消息的订阅

      PubSub.unsubscribe(token/msgName)
      

2.8.3、总结

  1. 消息订阅与发布与全局事件总线一样都可以实现任意组件间通信

  2. 但需要额外引入第三方实现库, 而全局事件总线不用, 一般在vue项目中不用

  3. 实现流程

    1. 在接收数据的组件:

      1. mounted(): 订阅消息, 在回调函数中接收数据并处理
      2. beforeDestroy(): 取消订阅
    2. 在发送数据的组件: 发布消息

2.9、组件间通信5: slot

2.9.1、理解

此方式用于父组件向子组件传递`带数据的标签

2.9.2、命名插槽与默认插槽

  • slot 是 vue 当中内置的组件标签

  • slot 内部的东西 是等待着父组件使用的时候给传过来

  • 子组件

    <slot>
        <!-- 默认插槽 -->
        <span>嘿嘿</span>
    </slot>
    <slot name="xxx">
        <!-- 命名插槽 -->
    </slot>
    
  • 父组件

    <Child>
        <template>
            <button>默认插槽传递内容</button>
        </template>
    ​
        <template slot="xxx">
            <span>命名插槽传递内容</span>
        </template>
    </Child>
    

2.9.3、作用域插槽

  • 数据是由父组件传给子组件去展示的

  • 子组件展示数据的过程当中,数据的结构是由父组件说了算的

  • 子组件

    <li v-for="(todo,index) in todos" :key="todo.id">
        <slot :todo="todo" :index="index">
            {{todo.content}}
        </slot>
      </li>
    
  • 父组件

    <ScopedChild :todos="todos">
      <template slot-scope="scopeProps">
        <span v-if="scopeProps.todo.isOver" style="color:hotpink">√ {{scopeProps.todo.content}}     </span>
      </template>
    </ScopedChild>
    

2.9.4、总结

  1. 当一个组件有不确定的结构时, 就需要使用slot技术了

  2. 注意: 插槽内容是在父组件中编译后, 再传递给子组件

  3. 如果决定结构的数据在父组件, 那用默认slot或命名命名slot

    1. 当只有一个不确定的结构时, 可以使用默认slot
    2. 当有多个不确定的结构时, 可以使用命名slot
  4. 如果决定结构的数据在子组件, 那需要使用作用域slot

3、第3章: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) => {
  // success callback
  console.log(response.data) //返回结果数据
}, (error) => {
  // error callback
  console.log(error.statusText) //错误信息
})

3.3、axios的使用

3.3.1、在线文档

  • https://github.com/axios/axios
    

3.3.2、下载

  • npm install axios --save
    

3.3.3、编码

  • // 引入模块
    import axios from 'axios'// 发送ajax请求
    axios.get(url)
      .then(response => {
        console.log(response.data) // 得到返回结果数据
      })
      .catch(error => {
        console.log(error.message)
      })
    

3.3.4、测试接口

  • 接口1

    https://api.github.com/search/repositories?q=v&sort=stars
    
  • 接口2:

    https://api.github.com/search/users?q=aa
    

4、第4章:vue UI组件库

4.1、常用的vue UI组件库

4.1.1、移动端UI组件库

  • Vant

     https://youzan.github.io/vant/#/zh-CN/
    
  • Cube UI

    https://didi.github.io/cube-ui/#/zh-CN
    
  • Mint UI

    http://mint-ui.github.io/ (经常不能访问)
    

4.1.2、PC端UI组件库

  1. Element UI

    https://element.eleme.cn/#/zh-CN
    
  2. IView UI

    https://www.iviewui.com/
    

4.2、使用 Element UI

4.2.1、下载

npm i element-ui -S

4.2.2、完整引入Element

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

4.2.3、按需引入Element

import {Button} from 'element-ui'
Vue.use(Button)

4.2.3、使用UI组件

  • 参考官网
  • <el-button type="primary">主要按钮</el-button>
    

4.2.4、实现按需打包

  1. 下载

    npm install babel-plugin-component -D
    
  2. 添加babel相关配置: webpack.config.js

    plugins: [
      [
        "component",  // 为babel-plugin-components配置
        {
          "libraryName": "element-ui",
          "styleLibraryName": "theme-chalk"
        }
      ]
    ] // 一旦我们需要一个另外babel插件, 需要在此配置
    

4.2.5、Element UI组件分类

  • 标签组件
  • 非标签组件(函数/对象)

5、第5章:vue-router

5.1、相关理解

5.1.1、vue-router的理解

  1. vue的一个插件库

  2. 专门用来实现一个SPA应用

  3. 基于vue的项目基本都会用到此库

  4. 中文文档:

    http://router.vuejs.org/zh-cn/
    
  5. 下载:

    npm install vue-router -S
    

5.1.2、SPA的理解

  1. 单页Web应用(single page web application,SPA)
  2. 整个应用只有一个完整的页面
  3. 点击页面中的链接不会刷新页面, 本身也不会向服务器发请求
  4. 当点击路由链接时, 只会做页面的局部更新
  5. 数据都需要通过ajax请求获取, 并在前端异步展现

5.1.3、路由的理解

1、什么是路由?
  • 一个路由就是一个映射(对应)关系(key:value)
  • key为路由路径path, value可能是function/component
2、路由分类
  1. 前台路由: 浏览器端路由, value是component, 当请求的是路由path时, 浏览器端没有发送http请求, 但界面会更新显示对应的组件
  2. 后台路由: node服务器端路由, value是function, 用来处理客户端提交的请求并返回一个响应数据
3、 前端路由
  • 注册路由

    {
        path: '/about',
        component: About
    },
    
  • 当浏览器的path变为/about时, 当前路由组件就会变为About组件

4、后台路由
  • 注册路由:

    app.get(path, function(req, res))
    router.get(path, function(req, res))
    
  • 当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

5.1.4、跨域

1、跨域是什么?
  • 浏览器上的同源策略
2、跨域的特点
  1. 跨域只存在于浏览器

  2. 不在浏览器发请求是不会存在跨域问题的

  3. http请求分为两大类: 普通http请求和ajax请求(跨域是出现在ajax请求)

    1. 普通请求 一般只有get(a标签和地址栏输入回车)和 post(form表单) 页面会刷新 不会跨域
    2. ajax请求 一般 get post delete put 一般都是异步发送的 页面不刷新 局部更新
3、在什么地方会出现跨域
  • 浏览器给服务器发ajax请求会跨域 因为跨域(同源策略)只存在于浏览器
  • 服务器给服务器发ajax请求不会
4、什么条件会跨域
  • 同源(协议 ip 端口一致)不跨域
  • 不同源就跨域(三个中间有一个不一样就跨域)
  • http://localhost:8080/ ------- 》 github
5、解决跨域:
  • 前端可以解决、后端解决。一般后端解决比前端解决容易
6、配置代理服务器解决跨域
  1. 本身我们现在就跑在开发服务器 webpack-dev-server,而这个服务器带了一个模块,这个模块可以支持我们使用代理

  2. 原理:

    1. 在浏览器发请求的时候,把这个请求发给服务器上的这个代理模块
    2. 再由这个代理模块转发给我们真正的服务器
    3. 这样的话,我们原来由浏览器直接发送请求到服务器就转化为服务器到服务器之间的请求
  3. 你要让代理转发,那么得告诉代理你的这个请求什么情况需要转发,配置以固定什么开头的路径需要代理转发,代理看到这个路径是以它开头就会帮你转发

  4. 代理转发的时候会把路径交给真正的请求服务器,作为请求路径,需要把固定的开头去除

  5. changeOrigin: true, // 支持跨域, 如果协议/主机也不相同, 必须加上

  6. 编码:

    proxy: {
        "/api": {
            target: "http://localhost:4000",
            pathRewrite: {"^/api" : ""},  //重写路径,去除标识
            changeOrigin:true
        }
    }
    

5.1.5、路由API说明

  1. VueRouter(): 用于创建路由器的构建函数

    new VueRouter({
        // 多个配置项
    })
    
  2. 路由配置

    routes: [
      { // 一般路由
        path: '/about',
        component: About,
        name:'about',  //命名路由
        //子路由(二级)
        children:[
            {path:'message',component:Message,},
            //二级路由重定向   
            {path:'',redirect:'message'}
        ]
      },
      //重定向路由
      { path: '/', redirect: '/about'}
    ]
    
  3. 注册路由器

    import router from './router'
    new Vue({
        router
    })
    
  4. 使用路由组件标签

    1. : 用来生成路由链接

      <router-link to="/xxx">Go to XXX</router-link>
      
    2. : 用来显示当前路由组件界面

      <router-view></router-view>
      

5.1.5、编写使用路由的3步

  1. 定义路由组件

  2. 注册路由

  3. 使用路由

5.2、向路由组件传递数据

5.2.1、方式1: 路由路径携带参数(param/query)

  1. 配置路由

    children: [
        {
          // :msgid是用来接收路径传过来的params参数
          path: 'mdetail/:msgid',
          component: MessageDetail,
          name:'mdetail',  //命名路由
        }
    ]
    
  2. 路由路径

    <!--1,路径的字符串拼接写法-->
    <router-link :to="'/home/message/mdetail/'+m.id+'?content='+m.content">{{m.title}}</router-link><!--2、路径的对象写法-->
    <router-link :to="{name:'mdetail',params:{msgid:m.id},query:{content:m.content}}">{{m.content}}</router-link>
    
  3. 路由组件中读取请求参数

    this.$route.params.msgid
    this.$route.query.content
    

5.2.2、方式2: 属性携带数据

<router-view :msg="msg"></router-view>

5.3、缓存路由组件对象

5.3.1、理解

  • 默认情况下, 被切换的路由组件对象会死亡释放, 再次回来时是重新创建的
  • 如果可以缓存路由组件对象, 可以提高用户体验

5.3.2、编码实现

<keep-alive>
    <router-view></router-view>
</keep-alive>   

5.4、编程式路由导航

5.4.1、相关API

API描述
this.$router.push(path):相当于点击路由链接(可以返回到当前路由界面)
this.$router.replace(path)用新路由替换当前路由(不可以返回到当前路由界面)
this.$router.back()请求(返回)上一个记录路由
this.$router.go(-1)请求(返回)上一个记录路由
this.$router.go(1)请求下一个记录路由

6、第6章:vuex

6.1、vuex理解

6.1.1、vuex是什么

6.1.2、状态自管理应用

  • state: 驱动应用的数据源
  • view: 以声明方式将state映射到视图
  • actions: 响应在view上的用户输入导致的状态变化(包含n个更新状态的方法)

6.1.3、多组件共享状态的问题

  1. 多个视图依赖于同一状态

  2. 来自不同视图的行为需要变更同一状态

  3. 以前的解决办法

    1. 将数据以及操作数据的行为都定义在父组件
    2. 将数据以及操作数据的行为传递给需要的各个子组件(有可能需要多级传递)
  4. vuex就是用来解决这个问题的

6.2、vuex核心概念和API

6.2.1、state

  1. vuex管理的状态对象

  2. 它应该是唯一的

    const state = {
        xxx: initValue
    }
    

6.2.2、mutations

  1. 包含多个直接更新state的方法(回调函数)的对象

  2. 谁来触发: action中的commit('mutation名称')

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

    const mutations = {
        yyy (state, {data1}) { 
            // 更新state的某个属性
        }
    }
    

6.2.3、actions

  1. 包含多个事件回调函数的对象

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

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

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

    const actions = {
        zzz ({commit, state}, data1) {
            commit('yyy', {data1})
        }
    }
    

6.2.4、getters

  1. 包含多个计算属性(get)的对象

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

    const getters = {
        mmm (state, getters) {
            return ...
        }
    }
    

6.2.5、modules

  1. 包含多个module
  2. 一个module是一个store的配置对象
  3. 与一个组件(包含有共享数据)对应

6.2.6、向外暴露store对象

export default new Vuex.Store({
    state,
    mutations,
    actions,
    getters
})

6.2.7、组件中

import {mapState, mapGetters, mapActions} from 'vuex'
export default {
    computed: {
        ...mapState(['xxx']),
        ...mapGetters(['mmm']),
    }
    methods: mapActions(['zzz'])
}
​
{{xxx}} {{mmm}} @click="zzz(data)"

6.2.8、映射store

import store from './store'
new Vue({
    store 
})

6.2.9、store对象

  1. 所有用vuex管理的组件中都多了一个属性$store, 它就是一个store对象

  2. 属性:

    • state: 注册的state对象
    • getters: 注册的getters对象
  3. 方法:

    dispatch(actionName, data): 分发调用action 
    

6.3、vuex结构分析

\