Vue2基础

190 阅读16分钟

第一章 初识Vue

1. Vue概述

  1. Vue是什么

Vue是一套用于构建用户界面的“渐进式”JavaScript框架。

渐进式:Vue可以自底向上逐层的应用,对于简单应用只需一个轻量小巧的核心库即可,对于复杂应用,可以引入各式各样的Vue插件丰富其功能

  1. Vue框架的迭代过程 

  1. Vue框架的特点
  • 组件化模式,提高代码复用率
  • 声明式编码,无需直接操作DOM,提高开发效率

2. 使用Vue框架

  1. 引入vue.js文件
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14/dist/vue.js"></script>
</head>
<body>
  <div id="root">
    <h3>姓名:{{name}}</h3>
    <h3>性别:{{sex}}</h3>
    <h3>年龄:{{age}}</h3>
  </div>

  <script type="text/javascript">
    new Vue({
      el: '#root',
      data:{
        name:"小明",
        sex:"男",
        age:25
      }
    })
  </script>
</body>
</html>
  1. 基于vue-cli搭建项目

第一步:安装vue-cli

npm install -g @vue/cli

第二步:创建一个vue项目

vue create 项目名称

第三步:创建.vue文件

<template>
  <div id="app">
    <h3>姓名:{{name}}</h3>
    <h3>性别:{{sex}}</h3>
    <h3>年龄:{{age}}</h3>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      name: "小明",
      sex: "男",
      age: 25,
    };
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
</style>
  1. Vue框架介面解析

页面容器:<div id="app">。。。</div>

模版:<template> 。。。 </ template>

vue实例:<script>。。。</script>,负责解析模版代码

style样式:<style>。。。</style>,负责为模版代码添加样式

第二章 Vue基础

1. 模版语法

  1. 插值语法

作用:用于解析标签体内容,在插值语法中可以直接读取data中所有的数据

<h3>姓名:{{name}}</h3>
  1. 指令语法

作用:以“v-”开头,部分有简写形式,主要用于解析标签,包括标签属性、标签体内容、绑定事件等

<p v-bind:age="25+1">hello</p> // 解析为<p v-bind:age="26">hello</p>

2. 数据绑定

  1. v-bind

作用:单向数据绑定,数据流为“data --> 页面”

简写:“:”

<input type="text" v-bind:value="msg">
或者
<input type="text" :value="msg">
  1. v-model

作用:双向数据绑定,数据流为“data <--> 页面”,又称为响应式数据绑定

简写:“v-model”,一般作用在表单类元素上,用于收集value属性值,所以可以省略value

<input type="text" v-model:value="msg">
或者
<input type="text" v-model="msg">

修饰符:

  • lazy:失去焦点时收集数据
  • number:将输入值转为有效数值
  • trim:输入值两端空格过滤

3. 定义el的指向

  1. 指定模版
new Vue({
   el: '#root',
})
  1. 通过Vue实例对象挂载模版
const vm = new Vue({}) // Vue构造函数的实例对象
vm.$mount('#root');

4. data创建内部状态

  1. 对象类型

多次调用一个组件时,data对象中的属性相互影响

data:{...}
  1. 函数类型

组件中的data必须是一个函数,每次调用函数都会返回一个新的对象,互不影响

data(){
  return{...}
}

5. MV*模式

  1. MVC模式

一种架构设计模式,其强制将业务数据(Model)与用户界面(View)隔离,用控制器(Controller)管理逻辑和用户输入,MVC 是单向通信,View 跟 Model 必须通过 Controller 来承上启下

Model的作用:

  • 负责保存应用数据,与后端交互并同步应用数据,校验数据等
  • 当Model改变时,会通过其观察者(视图)作出响应反应

View的作用:

  • 表示当前状态的视图
  • 用户可以与View进行交互来实现读取编辑Model

Controller的作用:

负责连接View和Model,Model的任何改变会显示在View中,View的任何操作会应用到Model中

MVC模式缺点:一个View可能对应多个Model,一个Model可能对应多个View,逻辑复杂且不易于维护

  1. MVVM

在MVC的基础上,将Controller替换为ViewModel,实现了双向的数据绑定,即View的数据改变可以直接影响ViewModel,反之亦然

视图模型 ViewModel 是 MVVM 模式的核心,它是连接 View 和 Model 的桥梁,即Vue的实例对象

  • 将模型 Model 转化成视图 View,实现的方式是:数据绑定
  • 将视图 View 转化成模型 Model,实现的方式是:DOM事件监听

当这两个方向的数据转换都实现时,我们称之为数据的双向绑定

6. 数据代理

  1. 原理

Vue通过Object.definedPrototype把data对象中的所有的属性添加到new Vue()返回的实例对象上,并为每一个属性都添加了一个getter和setter,分别操作属性内部的读/写操作,并完成Vue的界面更新操作。

  1. Vue实例对象

7. 事件处理

  1. 绑定事件
<button v-on:click="handleClick">按钮</button>
<button @click="handleClick">按钮</button>
  1. 配置函数
methods:{
   handleClick(){},
   ... ...
}
  1. 传递参数
<button v-on:click="handleClick($event,'2222')">按钮</button>

handleClick(e,value){
   console.log(e,value); // e为事件对象,value为传递的参数
}
  1. this指向
  • methods中的函数如果为普通函数,则Vue会统一管理其this,使其指向vm或者vc(vue component)
  • methods中的函数如果为箭头函数,则this指向Window全局对象
  1. 事件修饰符

prevent:阻止默认事件

<button @click.prevent="handleClick">按钮</button>

stop:阻止冒泡

<button @click.stop="handleClick">按钮</button>

once:事件只触发一次

<button @click.once="handleClick">按钮</button>

capture:使用事件的捕获模式

<button @click.capture="handleClick">按钮</button>

self:只有当event.target是当前操作的元素时才触发事件

<button @click.self="handleClick">按钮</button>

passive:事件的默认行为立即执行,无需等待事件回调执行完毕

<button @click.passive="handleClick">按钮</button>

注意:事件修饰符可以连用,如.stop.prevent....

8. 键盘事件

  1. 按键别名
@keydown.键盘别名 = “事件名称”
  • 回车:enter
  • 删除:delete
  • 退出:esc
  • 空格:space
  • 换行:tab(必须配合keydown使用)
  • 上:up
  • 下:down
  • 左:left
  • 右:right
  1. 事件分类

keydown:键盘按下时触发

<input type="text" v-model="msg" @keydown.ctrl="handleClick">

keyup:键盘抬起时触发

<input type="text" v-model="msg" @keyup.ctrl="handleClick">
  1. 系统修饰键

系统修饰键包括:ctr、alt、meta、shift

  • 配合keydown使用时正常触发
  • 配合keyup使用时,必须同时按下其他键,然后释放其他键时才能触发事件

9. 计算属性

// 使用
<h3>{{aDouble}}</h3>

// 创建
computed: {
    // 仅读取
    aDouble: function () {
      return this.a * 2
    },
    // 读取和设置
    aPlus: {
      get: function () {
        return this.a + 1
      },
      set: function (v) {
        this.a = v - 1
      }
    }
}
  1. 作用

基于data对象中的属性创建一个新的属性,该属性具有缓存机制,该属性会被挂载到vm实例上

computed: {
   newAge() {
       return this.age + this.name;
   },
},

  1. 原理

计算属性底层依赖Object.definedProperty方法提供的getter和setter

  1. get函数执行时机
  • 初次读取计算属性时
  • 当依赖的数据发生改变时
  1. this指向
  • computed中的函数如果为普通函数,则Vue会统一管理其this,使其指向vm或者vc(vue component)
  • computed中的函数如果为箭头函数,则this指向Window全局对象

10. 监视属性

  1. 使用
<div id="root">
    <h3>年龄:{{age}}</h3>
    <button @click="updateAge">按钮</button>
</div>
data: {
     age: 25,
},
watch:{
      // 简写形式——函数
      age(newValue,oldValue){
         console.log('age被修改了',newValue,oldValue);
      }
      // 完整形式——对象
      age:{
          immediate:true,
          handler(newValue,oldValue){
             console.log('age被修改了',newValue,oldValue);
          }
      }
}
// 通过vm实例动态创建属性监视
vm.$watch('被监视的属性',{
 // 相关配置项
})
  1. 触发时机

当被监视的属性变化时,回调函数自动调用,并进行相关操作

  1. 配置项
  • immediate:布尔值,表示是否在初次加载时进行监视,默认是false
  • deep:布尔值,表示是否开启深度监视,默认是false
  1. this指向
  • watch中的函数如果为普通函数,则Vue会统一管理其this,使其指向vm或者vc(vue component)
  • watch中的函数如果为箭头函数,则this指向Window全局对象
  1. watch与computed对比

computed和watch的功能类似,但是computed不可进行异步操作,但是watch可以

11. 绑定样式

  1. class外联样式
<div :class="mood" class="basic">你好</div>

data: {
     mood:"normal1", // 字符串
     mood:['nor1','nor2','nor3'] // 数组
     mood:{ // 对象
          nor1: false,
          nor2: true,
          nor3: false
     }
},
  1. style内联样式
// 动态值
<div style='{fontSize: font + 'px'}'>你好</div>

// 样式对象
<div :style='style1' :style="[style1,style2]">你好</div>

data: {
     style1:{
          fontSize:'40px'
     }
     style2:{
          color: 'red'
     }
},

12. 条件渲染

  1. v-if
<ul>
   <li v-if="age === 25">1</li>
   <li v-else-if="age === 26">2</li>
   <li v-else>3</li>
</ul>

适用场景:切换频率较低的场景,元素不会显示在DOM树上,无法通过JS获取元素。

  1. v-show
<ul>
   <li v-show="age <= 30">1</li>
   <li v-show="age > 30">2</li>
</ul>

适用场景:切换频率较高的场景,元素会出现在DOM树上,通过添加或移除display:none实现显示和隐藏,可以通过JS获取元素。

13. 列表渲染

  1. 使用
// 可以遍历数组、对象、字符串、指定次数
<div v-for="(value,index) in list" :key="index">{{value}}</div>
  1. key的作用

key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新的数据生成新的虚拟DOM,随后Vue会进行新虚拟DOM和旧虚拟DOM的对比,过程如下:

1)旧虚拟DOM中找到和新虚拟DOM相同的key

若虚拟DOM中内容没变,直接使用之前的虚拟DOM

若虚拟DOM中内容改变,则生成新的真实DOM,并替换之前的真实DOM

2)旧虚拟DOM中没有找到和新虚拟DOM相同的key

创建新的虚拟DOM,随后重新渲染页面

3)如果使用index作为key,带来的问题

若对数据进行逆序添加、逆序删除等破坏顺序的操作,会产生不必要的更新

若包含表单元素,会导致DOM元素更新错误

14. Vue监测数据

  1. 对象

Vue会监测data中所有层次的数据,会在底层转换为响应式数据,无需手动处理

Vue.set或者vm.$set

// set函数绑定在Vue构造函数上
Vue.set(target,property/index,value)

// set函数绑定在Vue原型上
vm.$set(target,property/index,value)
  • target:要更改的数据源,对象或数组
  • property/index:要更改的具体数据,对象属性或数组下标
  • value:重新赋予的值

作用:向响应式对象(不能是Vue实例,data,已有属性)中添加一个响应式属性/元素,并且保证这个数据是响应式的,可以触发视图更新。

data(){
    return {
      goodList: [
        {name: '书本', value: '200元', id: 1},
        {name: '玩偶', value: '200元', id: 2},
        {name: '薯片', value: '300元', id: 3}
      ]
    }
},
methods:{
    handleClick(){
        this.$set(this.goodList, 3, {name: '口罩', value: '409元', id: 4})
    }
}
  1. 数组

vue中对于数组元素没有实现响应式处理,如果更改数组元素时希望视图刷新,需要使用封装后的Arr原始API,包括push、pop、shift、unshift、splice、sort、reverse。这些API在底层共做了两件事:

第一步:调用原生方法,对数组进行操作 第二步:重新解析模版,进而更新视图

15. 收集表单数据

  1. text输入框,v-model等于value值
<input type="text" v-model="msg"/>
  1. radio单选框,v-model等于标签配置的value值
<input type="radio" v-model="msg"/>
  1. checkbox,无value属性或者value属性值非数组时,v-model等于checked勾选状态;否则为value数组值
<input type="checkbox" v-model="msg"/>

16. 过滤器

  1. 局部过滤器
filters:{
    timefilter(value){
      return value.format('YYYY年MM月DD日 HH:mmss');
    }
    mySort(value){
      return value.slice(0,5)
    }
}
  1. 全局过滤器
Vue.filter('mySort',function(value){...});
  1. 作用

对显示的数据进行特定格式化后在显示,不会改变原有数据,会产生一个新的数据。

过滤器可以接收参数,也可以将多个过滤器串联使用

<div>现在的时间:{{time | timefilter | mySort}}</div>
// 或者
<input type="text" v-bind:value="msg | mySort">

17. 其他v-指令

  1. v-text

向其所在节点中渲染文本内容,会替换掉节点中的内容,插值语法会整合节点中的内容

<div v-text="msg"></div>
  1. v-html

向指定节点中渲染包含html结构的内容,可以识别html结构,会替换掉节点中的内容。由于可以解析html结构,所以存在一定的安全问题,容易引发xss攻击。

<div v-html="msg"></div>
  1. v-cloak

其本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。可以配合css使用,解决网速慢时显示{{xxx}}的中间态。

<div v-cloak>{{name}}</div>

[v-cloak]{
    display:none
}
  1. v-once

所在节点在初次动态渲染后,就改为静态内容,即使数据更新,所在节点也不会随之更新了,一般用于性能优化

<div v-once>{{name}}</div>
  1. v-pre

所在节点会跳过编译过程,缩短编译时间,一般用于无任何语法的节点的性能优化

<div v-pre>xxx系统</div>

18. 自定义指令

  1. 使用
// 局部指令——完整形式
new Vue({
    directives:{
      指令名:{
          bind(element,binding){} // 指令与元素成功绑定时触发
          insered(element,binding){} // 指令所在元素被插入页面时触发
          update(element,binding){} // 指令所在模版被重新解析时触发
      }
    }
})

// 局部指令——简写形式

new Vue({
    directives:{
      指令名(){}
    }
})

// 全局指令
Vue.directive(指令名,配置对象)

Vue.directive(指令名,回调函数)
  1. 配置对象
bind(element,binding){} // 指令与元素成功绑定时触发
insered(element,binding){} // 指令所在元素被插入页面时触发
update(element,binding){} // 指令所在模版被重新解析时触发
  1. 注意事项
  • 指令定义时不需要添加v-前缀,使用时需要添加
  • 指令名有多个时,需要使用kebab-case形式命令,禁止使用驼峰命名法

19. ref属性

// 绑定
<div ref='title'>{{name}}</div>

// 获取
this.$refs.title

作用:给元素或者是子组件注册引用信息

返回值:应用在html标签上,获取到的是真实DOM元素,应用在组件标签上,获取到的是子组件实例对象

第三章 Vue生命周期

1. 完整流程

init Events&Lifecycle:初始化生命周期、事件,但是数据代理还未开始

beforeCreate:将要创建vm,此时不能通过vm访问到data中的数据、methods中的方法

init injections&reactivity:初始化,数据监测、数据代理

created:vm创建完毕,此时可以通过vm访问到data中的数据、methods中配置的方法

beforeMount:将要挂载vm,页面呈现的是未经Vue编译的DOM结构,在此阶段所有对DOM的操作最钟都不生效

mounted:vm挂载完毕,页面呈现的是经过Vue编译的DOM结构,在此阶段所有对DOM的操作均有效,但不推荐。一般在此阶段开启定时器、发送网络请求、订阅消息、绑定自定义事件等初始化操作

beforeUpdate:将要更新vm,数据已更新,但是页面未同步

updated:vm更新完毕,数据和页面均更新完毕

beforeDestory:将要销毁vm,页面销毁前,一般在此阶段关闭定时器、取消订阅消息、解绑自定义事件等收尾操作

destoryed:vm销毁完毕,此时自定义事件会失效,原生事件依然可用,数据更新也不会触发视图更新

2. 常用钩子函数

mounted:发送ajax请求,启动定时器,绑定自定义事件,订阅消息

beforeDestory:关闭定时器、取消订阅消息、解绑自定义事件

beforeCreate(){},
created(){},
beforeMount(){},
mounted(){},
beforeUpdate(){},
updated(){},
beforeDestroy(){}

3. this指向

普通函数:指向vm实例对象

箭头函数:指向undefined

第四章 组件化模式

1. Vue组件化

实现应用中局部功能代码和资源的集合

2. 组件嵌套

  1. 父组件
<template>
  <div id="app">
    <h2>{{ title }}</h2>
    <HelloWorld/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: "App",
  components:{HelloWorld},
  data() {
    return {
      title:'臭臭系统'
    };
  },
};
</script>
  1. 子组件
<template>
  <div>
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data(){
    return{
      msg:"臭臭爱吃猫条"
    }
  },
}
</script>

3. props属性

props只读属性,禁止对其进行修改。

简单接收:

props:['name','age','sex']

对数据进行类型限制:

props:{
    name: string,
    age:number,
    sex:string
}

限制数据类型、必要性及默认值:

props:{
    name:{
        type:string,
        required: true,
        defaultValue:'臭臭' 
    }
}

传递数据:

<HelloWorld name="name"/>

4. mixin混入

作用:可以把多个组件共用的配置提取成一个混入对象

定义混合:

{
    data(){},
    methods:{},
    ... ...
}

使用混合:

// 全局混入
Vue.mixin(xxx)

// 局部混入
mixins:['xxx']

5. 插件

作用:提取全局功能与逻辑等,用于增强Vue

定义插件:

export default {
  install(Vue){
    // 全局过滤器
    Vue.filter('mySlice',function(value){
      return value.slice(0,4);
    });
    // 全局指令
    Vue.directive('fbind',{
      bind(element,binding){
        element.value = binding.value;
      },
      inserted(element,binding){
        element.focus();
      },
      update(element,binding){
        element.value = binding.value;
      },
    });
    // 混入
    Vue.mixin({
      data(){
        return{
          x:100,
          y:200
        }
      }
    });
    // Vue原型添加方法(vm与vc共享)
    Vue.prototype.hellow = function(){
      console.log('hellow');
    };

  }
}

调用插件:

import Vue from 'vue';
import plugin from './plugins';

Vue.use(plugin);

接收参数:

export default {
  install(Vue,options){} // Vue:实例,options:传递参数
}

6. 子组件给父组件传参

  1. 父组件给子组件传递函数类型的props
————————父组件—————————
<template>
  <div id="app">
    <HelloWorld :outChangeMsg="outChangeMsg" />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: "App",
  components:{HelloWorld},
  methods:{
    outChangeMsg(value){
      console.log('子组件给父组件传递了参数',value);
    }
  },
};
</script>

——————子组件——————————
<template>
  <div>
    <button @click="chengeMsg">按钮</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props:['outChangeMsg'],
  methods:{
    chengeMsg(){
      this.outChangeMsg('酸酸');
    }
  }
}
</script>
  1. 父组件调用子组件时,给子组件绑定一个自定义事件(v-on或者@)
————————父组件—————————
<template>
  <div id="app">
    <HelloWorld @outChangeMsg="outChangeMsg" /> // 绑定自定义事件
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: "App",
  components:{HelloWorld},
  methods:{
    outChangeMsg(value){
      console.log('子组件给父组件传递了参数',value);
    }
  },
};
</script>

——————子组件——————————
<template>
  <div>
    <button @click="chengeMsg">按钮</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  methods:{
    chengeMsg(){
      this.$emit('outChangeMsg','酸酸'); // 触发自定义事件
    }
  }
}
</script>
  1. 父组件调用子组件时,给子组件绑定一个自定义事件(ref)
————————父组件—————————
<template>
  <div id="app">
    <HelloWorld ref="hellowWorld" />
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue';

export default {
  name: "App",
  components:{HelloWorld},
  methods:{
    outChangeMsg(value){
      console.log('子组件给父组件传递了参数',value);
    }
  },
  mounted(){
    setTimeout(()=>{
      this.$refs.hellowWorld.$on('outChangeMsg',this.outChangeMsg)
    },1000)
  }
};
</script>

——————子组件——————————
<template>
  <div>
    <button @click="chengeMsg">按钮</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  methods:{
    chengeMsg(){
      this.$emit('outChangeMsg','酸酸');
    }
  }
}
</script>
  1. 子组件解绑自定义事件
<template>
  <div>
    <button @click="chengeMsg">按钮</button>
    <button @click="offChangeMsg">解绑</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  methods:{
    chengeMsg(){
      this.$emit('outChangeMsg','酸酸');
    },
    offChangeMsg(){
      this.$off('outChangeMsg'); // 字符串类型,解绑一个自定义事件
      this.$off(['outChangeMsg','outChangeName']); // 数组类型,解绑多个个自定义事件
      this.$off(]); // 无参数,解绑当前组件绑定的所有自定义事件
    }
  }
}
</script>

7. 全局事件总线

作用:一种组件间通信的方式,适用于任意组件间通信

安装全局事件总线:

new Vue((
    .....
    beforeCreate(){
        Vue.prototype.$bus = this // 安装全局事件总线,$bus就是当前应用的vm
    }  
    ......
})

接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身

methods(){
    demo(data){......) // 处理接收到的数据
}
mounted(){
    this.$bus.$on('xxxx' ,this.demo) // 接收数据
}

传递数据:B组件提供数据源,并向外传递

this.$bus.$emit('xxxx',数据源);

解绑事件:在beforeDestroy钩子中用$off解绑当前组件用到的事件

beforeDestroy(){
    this.$off();
}

8. 消息订阅与发布

作用:一种组件间通信的方式,适用于任意组件间通信,可以使用第三方库如pubsub实现

使用方式:

第一步:安装pubsub

npm i pubsub-js

第二步:引入

import pubsub from 'pubsub-js'

第三步:接收数据

// A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身
methods(){
    demo(data){}
}
mounted(){
    const pid = pubsub.subscribe('xxx',this.demo) // 订阅消息
    this.pid = pid // 保存订阅事件,方便取消订阅使用
}

第四步:提供数据

pubsub.publish('xxx',数据)

第五步:取消订阅

beforeDestroy(){
    Pubsub.unsubscribe(this.pid) // 去取消订阅
}

9. nextTick

this.$nextTick(回调函数)

作用: 在下一次 DOM 更新结束后执行其指定的回调

适用场景:当改变数据后,要基于更新后的新DOM进行某些操作时,可以在nextTick所指定的回调函数中执行。

10. vue脚手架配置代理

  1. proxy单一配置方式

工作原理:当请求了前端不存在的资源时,那么该请求会转发给服务器 (优先匹配前端资源)

// vue.config.js
devServer:{
    proxy:'http://localhost:5000' // 服务器地址
}

优点:配置简单,请求资源时直接发给前端 (8080) 即可

缺点:不能配置多个代理,不能灵活的控制请求是否走代理

  1. proxy多配置方式
// vue.config.js
module.exports = {
    devServer: {
        proxy: {
            '/api1': { // 匹配所有以'/api1'开头的请求路径
                target:"http://localhost:5008', // 代理目标的基础路径
                changeOrigin: true, 
                pathRewrite: {'^/api1': ''}
            '/api2': { // 匹配所有以/api2'开头的请求路径
                target:http://localhost:5001', // 代理目标的基础路径
                changeOrigin: true,
                pathRewrite: { '^/api2': ''}
             }
        } 
    } 
}  
  • changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000,默认值为true
  • changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080

优点:可以配置多个代理,且可以灵活的控制请求是否走代理

缺点:配置略微繁琐,请求资源时必须加前缀

11. 插槽

作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式

分类:默认插槽、具名插槽、作用域插槽

  1. 默认插槽
// 外层组件调用内层组件时传递数据
<Test1>
     <h2>{{ msg }}</h2>
</Test1>

// 内层组件通过slot插槽接收数据,并且可以设置默认值状态
<div>
    <h1>Test1</h1>
    <slot>loading... ...</slot> 
</div>
  1. 具名插槽
// 外层组件调用内层组件时传递数据,通过slot指定插槽
<Test1>
     <h2 slot="name">{{ name }}</h2>
     // template作为外层容器,不会出现在DOM树上
     <template v-slot:footer>
         <div class="foot">
            <a href="http://www.atguigu.com">经典</a>
            <a href="http://www.atguigu.com">热门</a>
            <a href="http://www.atguigu.com">推荐</a>
         </div>
         <h4>欢迎前来观</h4>
     </tempIate>
</Test1>

// 内层组件通过slot插槽接收数据,可以设置默认值和插槽名称name
<div>
   <h1>Test1</h1>
   <slot name="name">loading... ...</slot>
   <slot name="footer">loading... ...</slot>
</div>
  1. 作用域插槽

外层组件控制插槽的结构,内层组件控制插槽中需要的数据,内层组件需要通过插槽传递数据给外层组件

// 外层组件
<Test1>
    <template scope="value"
        <ul>
           <li v-for="(g,index) in value.games" :key="index">{{g}}</li>
       </ul>
    </template>
</Test1>

// 内层组件
<div>
   <h1>Test1</h1>
   <slot :games="games">以我是默认的一些内容</slot>
</div>

第五章 Vuex

1. Vuex简介

概念:专门在 Vue 中实现集中式状态 (数据) 管理的一个 Vue 插件,对vue 应用中多个组件的共享状态进行集中式的管理 (读/写),也是一种组件间通信的方式,适用于任意组件间通信。

适用场景:多个组件依赖于同一状态、来自不同组件的行为需要变更同一状态

2. 搭建Vuex环境

  1. 创建vuex.js文件
// 引入Vuex
import Vue from 'vue';
import Vuex from 'vuex';

// 使用Vuex插件
Vu e.use(Vuex);

//准备actions,用于响应组件中的动作
const actions = {};

//准备mutations,用于操作数据 (state)
const mutations = {};

//准备state,用于存储数据
const state = {};

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
});
  1. 在main.js中传入store配置项
//引入store
import store from "./store"

new Vue({
    el:'#app',
    render: h => h(App),
    store
});
  1. 组件中读取vuex中的数据
this.$store.state.sum
  1. 组件中修改vuex中的数据
this.$store.dispatch('action中的方法名', 数据)
this.$store.commit('mutations中的方法名', 数据)

注意:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写 dispatch,直按编写 commit

3. getters

作用:可以使用getters加工state中的数据后再使用,通过缓存优化性能

  1. 在store.js 中追加 getters 配置
const getters = {
    bigSum(state){
        return state.sum * 19
    }
}

//创建并暴露store
export default new Vuex.Store({
    getters
})
  1. 组件中读取数据
this.$store.getters.bigSum

4. mapState、mapGetters、mapActions、mapMutations

mapState:映射state中的数据为计算属性

computed: {
   // 借助 mapstate生成计算属性: sum、school、subject (对象写法)
   ...mapState({sum: 'sum', school: ' school', subject: ' subject'})
   // 借助 mapState生成计算属性: sum、school、subject (数组写法)
   ...mapState(['sum', ' school', ' subject']),

mapGetters:映射 getters 中的数据为计算属性

computed: {
    // 借助 mapGetters生成计算属性:bigSum (对象写法)
    ...mapGetters((bigSum: 'bigSum']),
    // 借助 mapGetters生成计算属性: bigSum (数组写法)
    ...mapGetters(['bigSum'])
}

mapActions:生成与 actions 对话的方法,即包含 $store.dispatch(xxx)的函数

methods:{
    // 依赖 mapActions生成: incrementOdd、incrementWait (对象形式)
    ...mapActions({incrementOdd:'jiaodd',incrementWait:'jiaWait'})
    //依赖 mapActions生成: incrementOdd、incrementWait (数组形式)
    ...mapActions(["jiaodd',"jiawait'])
}

mapMutations:生成与mutations 对话的方法,即包含$store.commit(xxx)的函数

methods:{
    //依赖 mapActions生成: increment、decrement (对象形式)
    ...mapMutations(fincrement:"JIA' ,decrement:'JIAN'])
    //依赖 mapMutations生成: JIA、JIAN (对象形式)
    ...mapMutations(['JIA', JIAN']),
}

备注: mapActions与mapMutations使用时,若需要传递参数,则需要在模板中绑定事件时传递好参数,否则接收到的参数是事件对象

5. Vuex模块化

目的:让代码更好维护,让多种数据分类更加明确

// store.js
const countAbout = {
    namespaced:true, //开启命名空间
    state:{x:1},
    mutations: { ... },
    actions: { ... },
    getters: {
        bigSum(state){
            return state.sum * 10
        }
    },
}

const personAbout = {
    namespaced:true, //开启命名空间
    state:{ ... },
    mutations: {...},
    actions: {...},
}

const store = new Vuex.Store({
    modules: {
        countAbout,
        personAbout
    }
});

开启命名空间后,组件中读取state数据:

// 直接读取
this.$store.state.personAbout.list;

// 借助mapstate
...mapState('countAbout',[ 'sum', 'school', 'subject']);

开启命名空间后,组件中读取getters数据:

// 直接读取
this.$store.getters['personAbout/firstPersonName'];

// 借助mapGetters
...mapGetters('countAbout',['bigSum']);

开启命名空间后,组件中调用dispatch:

// 直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person);

// 借助mapActions
...mapActions('countAbout',[incrementOdd:'jiaodd',incrementwait:'jiawait'));

开启命名空间后,组件中调用commit:

// 直接commit
this.$store.commit('personAbout/ADD_PERSON',person);

// 借助mapMutations
..mapMutations('countAbout',(increment:'JIA',decrement:'JIAN']);

第六章 vue-router

1. vue-router简介

  1. SPA 应用概念

SPA又名单页Web 应用(single page web application,SPA) ,整个应用只有一个完整的页面 index.html,页面所需数据需要通过 ajax 请求获取。点击页面中的导航链接不会刷新页面,只会做页面的局部更新。

  1. 路由分类

后端路由:value 是function,用于处理客户端提交的请求。服务器接收到一个请求时,根据请求路径找到匹配的函数来处理请求,返回响应数据。

前端路由:value 是component,用于展示页面内容。当浏览器的路径改变时,对应的组件就会显示。

  1. 路由

一个路由 (route) 就是一组映射关系 (key - value) ,多个路由需要路由器 (router) 进行管理。

前端路由中key是路径,value是组件。

// 安装vue-router
npm i vue-router

// 应用插件
Vue.use(VueRouter)

// 引入VueRouter
import VueRouter from 'vue-router';

// 引入组件
import About from '../components/About'
import Home from '../components/Home'

// 创建router实例对象,去管理一组一组的路由规则
const router = new VueRouter({
    routes:[
        {
           path: ' /about',
           component: About
        },
        {
           path: ' /home' ,
           component :Home
         }
    ]
})

//暴露router
export default router

实现切换 (active-class可配置高亮样式):

<router-link active-class="active"to="/about">About</router-link>

展示位置:

<router-liew></router-view>

使用注意事项:

  • 路由组件通常存放在pages 文件夹,一般组件通常存放在 components 文件夹
  • 通过切换,被“隐藏”的路由组件默认被销毁掉,需要的时候再去挂载
  • 每个组件都有自己的 $route 属性,里面存储着组件自己的路由信息
  • 整个应用只有一个router,可以通过组件的 $router 属性获取到

2. 多级嵌套路由

// 使用children配置多级嵌套路由
routes:[
    {
        path:'/about',
        component:About,
    },
    {
        path: '/home',
        component:Home,
        children:[
            { 
                 path:'news',//此处一定不要写: /news
                 component:News
            },
            {
                 path:'message',//此处一定不要写: /message
                 component:Message
            }
        ]
    }
]

多级路由跳转(要写完整路径) :

<router-link to="/home/news">News</router-link>

3. 路由的query参数

传递参数(字符串或者对象):

// 跳转并携带query参数,to的字符串写法 
<router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>

// 跳转并携带query参数,to的对象写法
<router-link
   :to="{
      path: '/home/message/detail' 
      query: {
         id:666,
         title:"你好"
      }
   }">
跳转</router-link>

接收参数:

this.$route.query.id
this.$route.query.title

4. 命名路由

作用:简化路由的跳转操作

第一步:路由命名

{
    path: '/demo',
    component: Demo,
    children: [
        {
            path: "test",
            component: Test,
            children:[
                {
                    name: "hello", //给路由命名
                    path: "welcome",
                    component: Hello,
                }
             ]
         }
     ]
}

第二步:简化路由跳转

// 简化前,需要写完整的路径
<router-link to="/demo/test/welcome”>跳转</router-link>

// 简化后,直接通过名字跳转
<router-link :to="{name: 'hello'}">跳转</router-link>

// 简化写法配合传递参数
<router-link:to="{
    name: ' hello',
    query:{ id:666, title:'你好'}
}>
跳转</router-link>

5. 路由的params参数

声明接收params参数:

{
   path: '/home',
   component: Home,
   children:[
       {
           component: Message,
           children:[
              {
                  name: 'xiangging',
                  path: 'detail/:id/:title', // 使用占位符声明接收params参数
                  component: Detail
              }
           ]
      }
   ]
}

传递参数:

// 跳转并携带params参数,to的字符串写法
<router-link :to="/home/message/detail/666/你好">跳转</router-link>

// 跳转并携带params参数,to的对象写法
<router-link
   :to="{
      name: 'xiangqing',
      params:{ id: 666, title:"你好"}
   }"
>
跳转</router-link>

注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置

接收参数:

this.$route.params.id
this.$route.params.title

6. 路由的props配置

作用:让路由组件更方便的收到参数

{
   name:'xiangqing,
   path:'detail/:id',
   component:Detail,
   // props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
   props:{ a:900 },

   // props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
   props:true,

   // props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
   props(route){ 
      return {
         id:route.query.id,
         title:route.query.title
      }
   }

7. router-link的replace属性

作用:控制路由跳转时操作浏览器历史记录的模式

浏览器的历史记录有两种写入方式: push 和 replace,路由跳转时候默认为push

  • push 是追加历史记录
  • replace 是替换前记录
// 开启replace模式
<router-link replace>News</router-link>

8. 编程式路由导航

作用:不借助实现路由跳转,让路由跳转更加灵活

// push和replace
this.$router.push({
   name: 'xiangqing',
   params: { id: xxx, title:xxx }
})

this.$router.replace({
   name: 'xiangqing',
   params: { id:xxx, title:xxx }
})

this.$router.forward() //前进
this.$router.back() // 后退
this.$router.go() // 跳转

9. 缓存路由组件

作用:让不展示的路由组件保持挂载,不被销毁

<keep-alive include="News">
   <router-view></router-view>
</keep-alive>

生命周期钩子函数:路由组件所独有的两个钩子,用于捕获路由组件的激活状态

  • activated 路由组件被激活时触发
  • deactivated 路由组件失活时触发

10. 路由守卫

作用:对路由进行权限控制

分类:全局守卫、独享守卫、组件内守卫

  1. 全局前置守卫

执行时机:初始化时、每次路由切换前

router.beforeEach((to, from, next)=>{
    console.log('beforeEach' ,to,from);
    if(to.meta.isAuth){ 
        //判断当前路由是否需要进行权限控制
        if(localStoragegetItem('school') ===atguigu'){
           //权限控制的具体规则
            next() //放行
        }else{
            alert('暂无权限查看)
        }
    }else{
        next() //放行
    }
});
  1. 全局后置守卫

执行时机:初始化时、每次路由切换后

router.afterEach((to, from)=>{
   console.log('afterEach',to,from);
   if(to.meta.title){
      document.title = to.meta.title //修改网页的title
   }else{
      document,title = "vue test
   }
});
  1. 组件独享守卫

执行时机:路由切换后

// routes字典内配置
beforeEnter((to, from, next){
   console.log('beforeEnter',to,from);
   if(to.meta.isAuth){ 
      //判断当前路由是否需要进行权限控制
      if(localstorage.getItem('school') === 'atguigu'){
         next();
      }else{
         alert("暂无权限查看");
   }else{
      next()
   }
});
  1. 单个组件内守卫

进入守卫:通过路由规则,进入该组件时被调用

beforeRouteEnter (to, from, next){}

离开守卫:通过路由规则,离开该组件时被调用

beforeRouteLeave (to, from, next){}

11. 路由器模式

  1. hash模式

url中#后面的内容即为hash值,hash值不会包含在 HTTP 请求中。

特点:

  • 地址中永远带着#号,不美观
  • 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法
  • 兼容性较好
  1. history模式
  • 地址干净,美观
  • 兼容性和hash模式相比略差
  • 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题