vue的基础使用

目录

1.vue指令合集

1.1 插值表达式 又叫: 声明式渲染/文本插值

1.2 v-bind

1.3vue指令-v-on

1.4vue指令 v-model

1.5vue指令 v-text和v-html

1.6vue指令 v-show和v-if

1.7vue指令-v-for

1.8vue指令v-pre

1.9 vue指令v-cloak

1.10vue指令v-once

1.11vue指令v-slot

2.vue 动态指令

2.1vue基础 动态class

2.2vue基础-动态style

3.vue过滤器

4.计算属性

4.1计算属性 缓存

4.2计算属性的完整写法

5vue侦听器-watch

5.1vue侦听器-深度侦听和立即执行

6.vue组件(重点中的重点)

6.1组件的基础使用

6.2vue组件-scoped作用

6.3vue组件通信_父向子-props (重点)

6.4vue组件通信_单向数据流

6.5vue组件通信_子向父

6.6兄弟通信(使用较少)

7.生命周期

8.axios

8.1axios的基本使用 -全局配置

9.$refs与$nextTick

9.1$refs获取dom元素

9.2 $refs-获取组件对象

9.3 $nextTick使用

9.4 组件name属性使用

10.组件进阶

10.1动态组件

10.2组件缓存

10.3组件插槽(重点)

10.4插槽默认内容

10.5具名插槽

10.6作用域插槽

11自定义指令

11.1自定义指令注册

11.2自定义传值

12.vue路由

12.1路由器的基本介绍

12.2.vue路由-声明式导航

12.3vue路由的-重定向和模式

12.4编程式导航

12.5 VUE路由的嵌套和守卫


1.vue指令合集

1.1 插值表达式 又叫: 声明式渲染/文本插值

语法: {{ 表达式 }}

1.2 v-bind

目标: 给标签属性设置vue变量的值

  • 语法:v-bind:属性名="vue变量"

  • 简写::属性名="vue变量"

1.3vue指令-v-on

目标: 给标签绑定事件

  • 语法

    • v-on:事件名="要执行的==少量代码=="

    • v-on:事件名="methods中的函数"

    • v-on:事件名="methods中的函数(实参)"

  • 简写: @事件名="methods中的函数"

1.3.1vue指令-v-on事件对象

目标: vue事件处理函数中, 拿到事件对象

  • 无传参, 通过形参直接接收

  • 传参, 通过$event指代事件对象传给事件处理函数

1.3.2vue指令-v-on修饰符

目的: 在事件后面.修饰符名 - 给事件带来更强大的功能

语法:

@事件名.修饰符="methods里函数"

  • .stop - 阻止事件冒泡

  • .prevent - 阻止默认行为

  • .once - 程序运行期间, 只触发一次事件处理函数

1.3.3vue指令-v-on按键修饰符

目标: 给键盘事件, 添加修饰符, 增强能力

语法:

  • @keyup.enter - 监测回车按键

  • @keyup.esc - 监测返回按键

1.4vue指令 v-model

目标: 把value属性和vue数据变量, 双向绑定到一起

语法: v-model="vue数据变量"

双向数据绑定

数据变化 -> 视图自动同步

视图变化 -> 数据自动同步

1.4.1v-model修饰符

目标: 让v-model拥有更强大的功能

  • 语法:

    • v-model.修饰符="vue数据变量"

      • .number 以parseFloat转成数字类型

      • .trim 去除首尾空白字符

      • .lazy 在change时触发而非inupt时

1.5vue指令 v-text和v-html

目的: 更新DOM对象的innerText/innerHTML

  • 语法:

    • v-text="vue数据变量"

    • v-html="vue数据变量"

  • 注意: 会覆盖插值表达式

1.6vue指令 v-show和v-if

目标: 控制标签的隐藏或出现

  • 语法:

    • v-show="vue变量"

    • v-if="vue变量"

  • 原理

    • v-show 用的display:none隐藏 (频繁切换使用)

    • v-if 直接从DOM树上移除

  • v-if之下 还可以写v-else-if 和v-else

1.7vue指令-v-for

目标: 列表渲染, 所在标签结构, 按照数据数量, 循环生成

  • 语法

    • v-for="(值, 索引) in 目标结构"

    • v-for="值 in 目标结构"

  • 目标结构:

    • 可以遍历数组 / 对象 / 数字 / 字符串 (可遍历结构)

  • 注意:

    v-for的临时变量名不能用到v-for范围外

1.8vue指令v-pre

目标: 跳过该元素的渲染 直接显示其字符串

语法

  • v-pre

1.9 vue指令v-cloak

目标:如果vue没有加载完成则元素不会显示,直到编译结束。

1.10vue指令v-once

目标:只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。

1.11vue指令v-slot

  • 缩写#

  • 预期:可放置在函数参数位置的 JavaScript 表达式 (在支持的环境下可使用解构)。可选,即只需要在为插槽传入 prop 的时候使用。

  • <template>

  • 组件: (对于一个单独的带 prop 的默认插槽)

  • 用法

    提供具名插槽或需要接收 prop 的插槽。

2.vue 动态指令

2.1vue基础 动态class

目标: 用v-bind给标签class设置动态的值

语法:

  • :class="{类名: 布尔值}"

 <p :class="{red_str: bool}">动态class</p>

2.2vue基础-动态style

目标: 给标签动态设置style的值

语法

  • :style="{css属性: 值}"

<p :style="{backgroundColor: colorStr}">动态style</p>

3.vue过滤器

vue过滤器-定义使用

目的: 转换格式, 过滤器就是一个函数, 传入值返回处理后的值

过滤器只能用在, ==插值表达式和v-bind表达式==

Vue中的过滤器场景

  • 字母转大写, 输入"hello", 输出"HELLO"

  • 字符串翻转, "输入hello, world", 输出"dlrow ,olleh"

语法:

  • Vue.filter("过滤器名", (值) => {return "返回处理后的值"})

  • filters: {过滤器名字: (值) => {return "返回处理后的值"}

例子:

  • 全局定义字母都大写的过滤器

  • 局部定义字符串翻转的过滤器

<template>
  <div>
    <p>原来的样子: {{ msg }}</p>
    <!-- 2. 过滤器使用
      语法: {{ 值 | 过滤器名字 }}
     -->
    <p>使用翻转过滤器: {{ msg | reverse }}</p>
    <p :title="msg | toUp">鼠标长停</p>
  </div>
</template>
​
<script>
export default {
  data(){
    return {
      msg: 'Hello, Vue'
    }
  },
  // 方式2: 局部 - 过滤器
  // 只能在当前vue文件内使用
  /*
     语法: 
     filters: {
       过滤器名字 (val) {
         return 处理后的值
       }
     }
  */
  filters: {
    toUp (val) {
      return val.toUpperCase()
    }
  }
}
</script>
​
<style>
​
</style>

4.计算属性

目标: 一个数据, 依赖另外一些数据计算而来的结果

语法:

computed: {
    "计算属性名" () {
        return "值"
    }
}

实例:

需求: 求2个数的和显示到页面上

<template>
  <div>
    <p>{{ num }}</p>
  </div>
</template>
​
<script>
export default {
  data(){
    return {
      a: 10,
      b: 20
    }
  },
  // 计算属性:
  // 场景: 一个变量的值, 需要用另外变量计算而得来
  /*
    语法:
    computed: {
      计算属性名 () {
        return 值
      }
    }
  */
 // 注意: 计算属性和data属性都是变量-不能重名
 // 注意2: 函数内变量变化, 会自动重新计算结果返回
  computed: {
    num(){
      return this.a + this.b
    }
  }
}
</script>
​
<style>
​
</style>

4.1计算属性 缓存

目标: 计算属性是基于它们的依赖项的值结果进行缓存的,只要依赖的变量不变, 都直接从缓存取结果

会减少函数的调取次数

// 计算属性优势: // 带缓存 // 计算属性对应函数执行后, 会把return值缓存起来 // 依赖项不变, 多次调用都是从缓存取值 // 依赖项值-变化, 函数会"自动"重新执行-并缓存新的值

4.2计算属性的完整写法

export default {
    computed: {
        full: {
            // 给full赋值触发set方法
            set(val){
                console.log(val)
            },
            // 使用full的值触发get方法
            get(){
                return "无名氏"
            }
        }
    }
}

5vue侦听器-watch

目标: 可以侦听data/computed属性值改变

语法

watch: {
    "被侦听的属性名" (newVal, oldVal){
        
    }
}

实例:

<template>
  <div>
    <input type="text" v-model="name">
  </div>
</template>
​
<script>
export default {
  data(){
    return {
      name: ""
    }
  },
  // 目标: 侦听到name值的改变
  /*
  语法:
    watch: {
      变量名 (newVal, oldVal){
        // 变量名对应值改变这里自动触发
      }
    }
  */
  watch: {
    // newVal: 当前最新值
    // oldVal: 上一刻值
    name(newVal, oldVal){
      console.log(newVal, oldVal);
    }
  }
}
</script>
​
<style>
​
</style>

5.1vue侦听器-深度侦听和立即执行

目标: 侦听复杂类型, 或者立即执行侦听函数

语法:

watch: {
    "要侦听的属性名": {
        immediate: true, // 立即执行
        deep: true, // 深度侦听复杂类型内变化
        handler (newVal, oldVal) {
            
        }
    }
}

实例:

<template>
  <div>
    <input type="text" v-model="user.name">
    <input type="text" v-model="user.age">
  </div>
</template>
​
<script>
export default {
  data(){
    return {
      user: {
        name: "",
        age: 0
      }
    }
  },
  // 目标: 侦听对象
  /*
  语法:
    watch: {
      变量名 (newVal, oldVal){
        // 变量名对应值改变这里自动触发
      },
      变量名: {
        handler(newVal, oldVal){
​
        },
        deep: true, // 深度侦听(对象里面层的值改变)
        immediate: true // 立即侦听(网页打开handler执行一次)
      }
    }
  */
  watch: {
    user: {
      handler(newVal, oldVal){
        // user里的对象
        console.log(newVal, oldVal);
      },
      deep: true,
      immediate: true
    }
  }
}
</script>
​
<style>
​
</style>

6.vue组件(重点中的重点)

组件是可复用的 Vue 实例, 封装标签, 样式和JS代码

组件化 :封装的思想,把页面上 可重用的部分 封装为 组件,从而方便项目的 开发 和 维护

一个页面, 可以拆分成一个个组件,一个组件就是一个整体, 每个组件可以有自己独立的 结构 样式 和 行为(html, css和js)

6.1组件的基础使用

步骤:

  1. 创建组件 components/Pannel.vue

  2. 注册组件: 创建后需要注册后再使用

    注册组件分为两种 一种是全局组件 一种是局部组件

    全局 - 注册使用

    全局入口在main.js, 在new Vue之上注册

    语法:

    import Vue from 'vue' import 组件对象 from 'vue文件路径'

    Vue.component("组件名", 组件对象)

    // 目标: 全局注册 (一处定义到处使用)
    // 1. 创建组件 - 文件名.vue
    // 2. 引入组件
    import Pannel from './components/Pannel'
    // 3. 全局 - 注册组件
    /*
      语法: 
      Vue.component("组件名", 组件对象)
    */
    Vue.component("PannelG", Pannel)

全局注册PannelG组件名后, 就可以当做标签在任意Vue文件中template里用

单双标签都可以或者小写加-形式, 运行后, 会把这个自定义标签当做组件解析, 使用==组件里封装的标签替换到这 个位置==

局部 - 注册使用

语法:

import 组件对象 from 'vue文件路径'

export default { components: { "组件名": 组件对象 } }

<template>
  <div id="app">
    <h3>案例:折叠面板</h3>
    <!-- 4. 组件名当做标签使用 -->
    <!-- <组件名></组件名> -->
    <PannelG></PannelG>
    <PannelL></PannelL>
  </div>
</template>
​
<script>
// 目标: 局部注册 (用的多)
// 1. 创建组件 - 文件名.vue
// 2. 引入组件
import Pannel from './components/Pannel_1'
export default {
  // 3. 局部 - 注册组件
  /*
    语法: 
    components: {
      "组件名": 组件对象
    }
  */
  components: {
    PannelL: Pannel
  }
}
</script>

组件使用总结:

  1. (创建)封装html+css+vue到独立的.vue文件中

  2. (引入注册)组件文件 => 得到组件配置对象

  3. (使用)当前页面当做标签使用

6.2vue组件-scoped作用

目的: 解决多个组件样式名相同, 冲突问题

解决: 给Pannel.vue组件里style标签上加scoped属性即可

6.2.1::v-deep 类名 可以进行样式穿透

可以只app.vue 中style里面 使用样式穿透 修改其他组件里的样式

6.3vue组件通信_父向子-props (重点)

目的:从外面给组件内传值

步骤:

  1. 创建组件components/MyProduct.vue - 复制下面标签

  2. 组件内在props定义变量, 用于接收外部传入的值

  3. App.vue中引入注册组件, 使用时, 传入具体数据给组件显示

components/MyProduct.vue
export default {
  props: ['title', 'price', 'intro']
}
​
​
App.vue中使用并传入数据
    <Product title="好吃的口水鸡" price="50" intro="开业大酬宾, 全场8折"></Product>
    <Product title="好可爱的可爱多" price="20" intro="老板不在家, 全场1折"></Product>
    <Product title="好贵的北京烤鸭" price="290" :intro="str"></Product>

父向子传递参数时可以使用事件循环v-for

6.4vue组件通信_单向数据流

目标: props变量本身是只读不能重新赋值

目标:从==父到子==的数据流向,叫==单向数据流==

原因: 子组件修改, 不通知父级, 造成数据不一致性

6.5vue组件通信_子向父

  • 父: @自定义事件名="父methods函数"

  • 子: this.$emit("自定义事件名", 传值) - 执行父methods里函数代码

总结: 父自定义事件和方法, 等待子组件触发事件给方法传值

6.6兄弟通信(使用较少)

两个组件的关系非常的复杂,通过父子组件通讯是非常麻烦的。这时候可以使用通用的组件通讯方案:事件总线(event-bus)

核心语法

EventBus/index.js- 定义事件总线bus对象

import Vue from 'vue'
// 导出空白vue对象
export default new Vue()

List.vue注册事件 - 等待接收要砍价的值 (==直接复制==) - 准备兄弟页面

<template>
  <ul class="my-product">
      <li v-for="(item, index) in arr" :key="index">
          <span>{{ item.proname }}</span>
          <span>{{ item.proprice }}</span>
      </li>
  </ul>
</template>
​
<script>
export default {
  props: ['arr'],
}
</script>
​
<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

components/MyProduct_sub.vue(==带学生主要写触发eventBus身上事件==)

<template>
  <div class="my-product">
    <h3>标题: {{ title }}</h3>
    <p>价格: {{ price }}元</p>
    <p>{{ intro }}</p>
    <button @click="subFn">宝刀-砍1元</button>
  </div>
</template>
​
<script>
import eventBus from '../EventBus'
export default {
  props: ['index', 'title', 'price', 'intro'],
  methods: {
    subFn(){
      this.$emit('subprice', this.index, 1) // 子向父
      eventBus.$emit("send", this.index, 1) // 跨组件
    }
  }
}
</script>
​
<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

List.vue正确代码(==EventBus接收方==)

<template>
  <ul class="my-product">
    <li v-for="(item, index) in arr" :key="index">
      <span>{{ item.proname }}</span>
      <span>{{ item.proprice }}</span>
    </li>
  </ul>
</template>
​
<script>
// 目标: 跨组件传值
// 1. 引入空白vue对象(EventBus)
// 2. 接收方 - $on监听事件
import eventBus from "../EventBus";
export default {
  props: ["arr"],
  // 3. 组件创建完毕, 监听send事件
  created() {
    eventBus.$on("send", (index, price) => {
      this.arr[index].proprice > 1 &&
        (this.arr[index].proprice = (this.arr[index].proprice - price).toFixed(2));
    });
  },
};
</script>
​
<style>
.my-product {
  width: 400px;
  padding: 20px;
  border: 2px solid #000;
  border-radius: 5px;
  margin: 10px;
}
</style>

总结: 空的Vue对象, 只负责$on注册事件, $emit触发事件, 一定要确保$on先执行

7.生命周期

创建期间的生命周期函数:

初始化阶段

beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性 ​ created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板

挂载阶段(使用多)

beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中 mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示

运行期间的生命周期函数:

更新阶段(使用多)

beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还没有开始重新渲染DOM节点 updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!销毁期间的生命周期函数:

销毁阶段

beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。 ​ destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

更新阶段

activated - 激活

deactivated - 失去激活状态

8.axios

特点

  • 支持客户端发送Ajax请求

  • 支持服务端Node.js发送请求

  • 支持Promise相关用法

  • 支持请求和响应的拦截器功能

  • 自动转换JSON数据

  • axios 底层还是原生js实现, 内部通过Promise封装的

axios的基本使用

axios({
  method: '请求方式', // get post
  url: '请求地址',
  data: {    // 拼接到请求体的参数,  post请求的参数
    xxx: xxx,
  },
  params: {  // 拼接到请求行的参数, get请求的参数
    xxx: xxx 
  }
}).then(res => {
  console.log(res.data) // 后台返回的结果
}).catch(err => {
  console.log(err) // 后台报错返回
})

8.1axios的基本使用 -全局配置

目标: 避免前缀基地址, 暴露在逻辑页面里, 统一设置

axios.defaults.baseURL = "http://123.57.109.30:3006"
​
// 所有请求的url前置可以去掉, 请求时, axios会自动拼接baseURL的地址在前面
getAllFn() {
    axios({
        url: "/api/getbooks",
        method: "GET", // 默认就是GET方式请求, 可以省略不写
    }).then((res) => {
        console.log(res);
    });
    // axios()-原地得到Promise对象
},

9.$refs与$nextTick

9.1$refs获取dom元素

目标: 利用 ref 和 $refs 可以用于获取 dom 元素

<template>
  <div>
      <p>1. 获取原生DOM元素</p>
      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
  </div>
</template>
​
<script>
// 目标: 获取组件对象
// 1. 创建组件/引入组件/注册组件/使用组件
// 2. 组件起别名ref
// 3. 恰当时机, 获取组件对象
export default {
    mounted(){
        console.log(document.getElementById("h")); // h1
        console.log(this.$refs.myH); // h1
    }
}
</script>
​
<style>
​
</style>

9.2 $refs-获取组件对象

目标: 获取组件对象, 调用组件里方法

components/Child/Demo.vue

<template>
  <div>
      <p>我是Demo组件</p>
  </div>
</template>
​
<script>
export default {
    methods: {
        fn(){
            console.log("demo组件内的方法被调用了");
        }
    }
}
</script>

More.vue - 获取组件对象 - 调用组件方法

<template>
  <div>
      <p>1. 获取原生DOM元素</p>
      <h1 id="h" ref="myH">我是一个孤独可怜又能吃的h1</h1>
      <p>2. 获取组件对象 - 可调用组件内一切</p>
      <Demo ref="de"></Demo>
  </div>
</template>
​
<script>
// 目标: 获取组件对象
// 1. 创建组件/引入组件/注册组件/使用组件
// 2. 组件起别名ref
// 3. 恰当时机, 获取组件对象
import Demo from './Child/Demo'
export default {
    mounted(){
        console.log(document.getElementById("h")); // h1
        console.log(this.$refs.myH); // h1
​
        let demoObj = this.$refs.de;
        demoObj.fn()
    },
    components: {
        Demo
    }
}
</script>

总结: ref定义值, 通过$refs.值 来获取组件对象, 就能继续调用组件内的变量

9.3 $nextTick使用

Vue更新DOM-异步的

目标: 点击count++, 马上通过"原生DOM"拿标签内容, 无法拿到新值

实例代码

components/Tick.vue

<template>
  <div>
      <input ref="myInp" type="text" placeholder="这是一个输入框" v-if="isShow">
      <button v-else @click="btn">点击我进行搜索</button>
  </div>
</template>
​
<script>
// 目标: 点按钮(消失) - 输入框出现并聚焦
// 1. 获取到输入框
// 2. 输入框调用事件方法focus()达到聚焦行为
export default {
    data(){
        return {
            isShow: false
        }
    },
    methods: {
        async btn(){
            this.isShow = true;
            // this.$refs.myInp.focus()
            // 原因: data变化更新DOM是异步的
            // 输入框还没有挂载到真实DOM上
            // 解决:
            // this.$nextTick(() => {
            //     this.$refs.myInp.focus()
            // })
            // 扩展: await取代回调函数
            // $nextTick()原地返回Promise对象
            await this.$nextTick()
            this.$refs.myInp.focus()
        }
    }
}
</script>

9.4 组件name属性使用

目标: 可以用组件的name属性值, 来注册组件名字

问题: 组件名不是可以随便写的?

答案: 我们封装的组件-可以自己定义name属性组件名-让使用者有个统一的前缀风格

components/Com.vue

<template>
  <div>
      <p>我是一个Com组件</p>
  </div>
</template>
​
<script>
export default {
    name: "ComNameHaHa" // 注册时可以定义自己的名字
}
</script>

App.vue - 注册和使用

<template>
  <div>
    <h1>1. 生命周期</h1>
    <Life v-if="show"></Life>
    <button @click="show = false">销毁组件</button>
    <hr>
    <h1>2. axios使用</h1>
    <UseAxios></UseAxios>
    <hr>
    <h1>3. $refs的使用</h1>
    <More></More>
    <hr>
    <h1>4. $nextTick使用场景</h1>
    <Tick></Tick>
    <hr>
    <h1>5. 组件对象里name属性</h1>
    <ComNameHaHa></ComNameHaHa>
  </div>
</template>
​
<script>
import Life from './components/Life'
import UseAxios from './components/UseAxios'
import More from './components/More'
import Tick from './components/Tick'
import Com from './components/Com'
export default {
  data(){
    return {
      show: true
    }
  },
  components: {
    Life,
    UseAxios,
    More,
    Tick,
    [Com.name]: Com // 对象里的key是变量的话[]属性名表达式
    // "ComNameHaHa": Com
  }
}
</script>

10.组件进阶

10.1动态组件

目标: 多个组件使用同一个挂载点,并动态切换,这就是动态组件

需求: 完成一个注册功能页面, 2个按钮切换, 一个填写注册信息, 一个填写用户简介信息

  1. 准备被切换的 - UserName.vue / UserInfo.vue 2个组件

  2. 引入到UseDynamic.vue注册

  3. 准备变量来承载要显示的"组件名"

  4. 设置挂载点<component>, 使用is属性来设置要显示哪个组件

  5. 点击按钮 – 修改comName变量里的"组件名"

<template>
  <div>
      <button @click="comName = 'UserName'">账号密码填写</button>
      <button @click="comName = 'UserInfo'">个人信息填写</button>
​
      <p>下面显示注册组件-动态切换:</p>
      <div style="border: 1px solid red;">
          <component :is="comName"></component>
      </div>
  </div>
</template>
​
<script>
// 目标: 动态组件 - 切换组件显示
// 场景: 同一个挂载点要切换 不同组件 显示
// 1. 创建要被切换的组件 - 标签+样式
// 2. 引入到要展示的vue文件内, 注册
// 3. 变量-承载要显示的组件名
// 4. 设置挂载点<component :is="变量"></component>
// 5. 点击按钮-切换comName的值为要显示的组件名
​
import UserName from '../components/01/UserName'
import UserInfo from '../components/01/UserInfo'
export default {
    data(){
        return {
            comName: "UserName"
        }
    },
    components: {
        UserName,
        UserInfo
    }
}
</script>

总结: vue内置component组件, 配合is属性, 设置要显示的组件名字

10.2组件缓存

目标: 组件切换会导致组件被频繁销毁和重新创建, 性能不高

Vue内置的keep-alive组件 包起来要频繁切换的组件

<div style="border: 1px solid red;">
    <!-- Vue内置keep-alive组件, 把包起来的组件缓存起来 -->
    <keep-alive>
        <component :is="comName"></component>
    </keep-alive>
</div>

10.3组件插槽(重点)

目标: 用于实现组件的内容分发, 通过 slot 标签, 可以接收到写在组件标签内的内容

vue提供组件插槽能力, 允许开发者在封装组件时,把不确定的部分定义为插槽

插槽例子:

语法口诀:

  1. 组件内用<slot></slot>占位

  2. 使用组件时<Pannel></Pannel>夹着的地方, 传入标签替换slot

10.4插槽默认内容

目标: 如果外面不给传值, 想给个默认显示内容

口诀: <slot>夹着内容默认显示内容, 如果不给插槽slot传东西, 则使用<slot>夹着的内容在原地显示

<slot>默认内容</slot>

10.5具名插槽

目标: 当一个组件内有2处以上需要外部传入标签的地方

传入的标签可以分别派发给不同的slot位置

要求: v-slot一般用跟template标签使用 (template是html5新出标签内容模板元素, 不会渲染到页面上, 一般被vue解析内部标签)

components/04/Pannel.vue - 留下具名slot

<template>
  <div>
    <!-- 按钮标题 -->
    <div class="title">
      <slot name="title"></slot>
      <span class="btn" @click="isShow = !isShow">
        {{ isShow ? "收起" : "展开" }}
      </span>
    </div>
    <!-- 下拉内容 -->
    <div class="container" v-show="isShow">
     <slot name="content"></slot>
    </div>
  </div>
</template>

views/04_UseSlot.vue使用

<template>
  <div id="container">
    <div id="app">
      <h3>案例:折叠面板</h3>
      <Pannel>
        <template v-slot:title>
          <h4>芙蓉楼送辛渐</h4>
        </template>
        <template v-slot:content>
          <img src="../assets/mm.gif" alt="">
          <span>我是内容</span>
        </template>
      </Pannel>
      <Pannel>
        <template #title>
          <span style="color: red;">我是标题</span>
        </template>
        <template #content>
          <p>寒雨连江夜入吴,</p>
          <p>平明送客楚山孤。</p>
          <p>洛阳亲友如相问,</p>
          <p>一片冰心在玉壶。</p>
        </template>
      </Pannel>
    </div>
  </div>
</template>
​
<script>
import Pannel from "../components/04/Pannel";
export default {
  components: {
    Pannel,
  },
};
</script>

==v-slot可以简化成#使用==

v-bind可以省略成: v-on: 可以省略成@ 那么v-slot: 可以简化成#

总结: slot的name属性起插槽名, 使用组件时, template配合#插槽名传入具体标签

10.6作用域插槽

目标: 子组件里值, 在给插槽赋值时在父组件环境下使用

复习: 插槽内slot中显示默认内容

例子: 默认内容在子组件中, 但是父亲在给插槽传值, 想要改变插槽显示的默认内容

口诀:

  1. 子组件, 在slot上绑定属性和子组件内的值

  2. 使用组件, 传入自定义标签, 用template和v-slot="自定义变量名"

  3. scope变量名自动绑定slot上所有属性和值

components/05/Pannel.vue - 定义组件, 和具名插槽, 给slot绑定属性和值

<template>
  <div>
    <!-- 按钮标题 -->
    <div class="title">
      <h4>芙蓉楼送辛渐</h4>
      <span class="btn" @click="isShow = !isShow">
        {{ isShow ? "收起" : "展开" }}
      </span>
    </div>
    <!-- 下拉内容 -->
    <div class="container" v-show="isShow">
     <slot :row="defaultObj">{{ defaultObj.defaultOne }}</slot>
    </div>
  </div>
</template>
​
<script>
// 目标: 作用域插槽
// 场景: 使用插槽, 使用组件内的变量
// 1. slot标签, 自定义属性和内变量关联
// 2. 使用组件, template配合v-slot="变量名"
// 变量名会收集slot身上属性和值形成对象
export default {
  data() {
    return {
      isShow: false,
      defaultObj: {
        defaultOne: "无名氏",
        defaultTwo: "小传同学"
      }
    };
  },
};
</script>

views/05_UseSlot.vue

<template>
  <div id="container">
    <div id="app">
      <h3>案例:折叠面板</h3>
      <Pannel>
        <!-- 需求: 插槽时, 使用组件内变量 -->
        <!-- scope变量: {row: defaultObj} -->
        <template v-slot="scope">
          <p>{{ scope.row.defaultTwo }}</p>
        </template>
      </Pannel>
    </div>
  </div>
</template>
​
<script>
import Pannel from "../components/05/Pannel";
export default {
  components: {
    Pannel,
  },
};
</script>

总结: 组件内变量绑定在slot上, 然后使用组件v-slot="变量" 变量上就会绑定slot身上属性和值

作用域插槽使用场景

总结: 插槽可以自定义标签, 作用域插槽可以把组件内的值取出来自定义内容

11自定义指令

11.1自定义指令注册

目标: 获取标签, 扩展额外的功能

局部注册和使用

<template>
  <div>
      <!-- <input type="text" v-gfocus> -->
      <input type="text" v-focus>
      
  </div>
</template>
​
<script>
// 目标: 创建 "自定义指令", 让输入框自动聚焦
// 1. 创建自定义指令
// 全局 / 局部
// 2. 在标签上使用自定义指令  v-指令名
// 注意:
// inserted方法 - 指令所在标签, 被插入到网页上触发(一次)
// update方法 - 指令对应数据/标签更新时, 此方法执行
export default {
    data(){
        return {
            colorStr: 'red'
        }
    },
    directives: {
        focus: {
            inserted(el){
                el.focus()
            }
        }
    }
}
</script>
​
<style>
​
</style>

一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。

全局注册

以后随便哪个.vue文件里都可以直接用v-(指令名)指令

需要在mian.js中注册

// 全局指令 - 到处"直接"使用
Vue.directive("gfocus", {
  inserted(el) {
    el.focus() // 触发标签的事件方法
  }
})

11.2自定义传值

需求: 定义color指令-传入一个颜色, 给标签设置文字颜色

main.js定义处修改一下

// 目标: 自定义指令传值
Vue.directive('color', {
  inserted(el, binding) {
    el.style.color = binding.value
  },
  update(el, binding) {
    el.style.color = binding.value
  }
})

Direct.vue处更改一下

<p v-color="colorStr" @click="changeColor">修改文字颜色</p>
​
<script>
  data() {
    return {
      theColor: "red",
    };
  },
  methods: {
    changeColor() {
      this.theColor = 'blue';
    },
  },
</script>

12.vue路由

12.1路由器的基本介绍

12.1.1什么是路由

前端路由的意义是:路径和组件的映射关系

12.1.2为什么使用路由

单页面应用(SPA): 所有功能在一个html页面上实现

前端路由作用: 实现业务场景切换

优点:

  • 整体不刷新页面,用户体验更好

  • 数据传递容易, 开发效率高

缺点:

  • 开发成本高(需要学习专门知识)

  • 首次加载会比较慢一点。不利于seo.

12.1.3vue路由组件 vue-router

vue-router模块包

它和 Vue.js 深度集成

12.1.4路由的分类(经验分类 非官方分类)

src/views(或pages) 文件夹 和 src/components文件夹

  • 页面组件 - 页面展示 - 配合路由用

  • 复用组件 - 展示数据/常用于复用

总结: views下的页面组件, 配合路由切换, components下的一般引入到views下的vue中复用展示数据

12.1.5vue-router的使用方法

1.下载 vue-router
命令: npm i vue-router
​
2.src下新建文件 router
文件夹中新建文件 index.js
​
3.导入文件vue-router 与vue
import Vue from 'vue'
import VueRouter from 'vue-router'
​
4.让router与vue有关系 注册vue-router
Vue.use(VueRouter)
​
5.设置匹配规则 path(路径)===组件   (自己常写内容)
const routes=[
    {
        path:'/路径'
        component:'引入的组件名称'
    }
]
​
6.实例化router
const router = new VueRouter({
  routes, //将匹配规则导入
})
​
7.将router导出 在main.js中引入
export default router
​
​
​
8.mian.js中 在vue的实例对象中挂载router对象
new Vue({
    router,//挂载
    .....其他代码
})
​
9.将components换成router-view
​

12.2.vue路由-声明式导航

vue-router提供了一个全局组件 router-link 一般用于代替a标签作为转跳使用

router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)

router-link提供了声明式导航高亮的功能(自带类名)

常见写法
<template>
  <div>
    <div class="footer_wrap">
      <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/part">朋友</router-link>
    </div>
    <div class="top">
      <router-view></router-view>
    </div>
  </div>
</template>
​
点击高亮的类名与样式
<style scoped>
/* 省略了 其他样式 */
.footer_wrap .router-link-active{
  color: white;
  background: black;
}
</style>

总结: 链接导航, 用router-link配合to, 实现点击切换路由

12.2.1声明式导航 - 跳转传参

目标: 在跳转路由时, 可以给路由对应的组件内传值

router-link上的to属性传值,语法格式如下

  • /path?参数名称=值

  • /path/值-需要路由的对象提前配置path:'/path/参数名称'

对应页面组件接收传递过来的值

  • $route.query.参数名

  • $route.params.参数名

<template>
  <div>
      <p>关注明星</p>
      <p>发现精彩</p>
      <p>寻找伙伴</p>
      <p>加入我们</p>
      <p>人名: {{ $route.query.name }} -- {{ $route.params.username }}</p>
  </div>
</template>

路由定义

{
    path: "/part",
    component: Part
  },
  {
    path: "/part/:username",
 // 有:的路径代表要接收具体的值
    component: Part
  },

3导航转跳

<router-link to="/part?name=小传">朋友-小传</router-link>
<router-link to="/part/小智">朋友-小智</router-link>

总结:

?key=value 用$route.query.key 取值

/值 提前在路由规则/path/:key 用$route.params.key 取值

还有第三种模式

可以再路由定义中添加属性porps

 {
    path: "/part/:username", // 有:的路径代表要接收具体的值
    component: Part
    porps:true
  },

然后再到被传值的页面中声明

porps:{username}

便可以和父传子一样 调用porps中的参数

12.3vue路由的-重定向和模式

12.3.1 路由的重新定向

目标: 匹配path后, 强制切换到目标path上

  • 网页打开url默认hash值是/路径

  • redirect是设置要重定向到哪个路由路径

在路由规则的第一行写上

const routes=[
    {
        path:'/' //默认进入的哈希路径就是/
        redirect:'/find'    //重定向到/find 这个find可以随意更改看你需要什么
    }
]

总结: 强制重定向后, 还会重新来数组里匹配一次规则

12.3.2路由-404页面

目标: 如果路由hash值, 没有和数组里规则匹配

1.创建404页面

2.修改路由配置

import NotFound from '文件的路径'
​
const routes =[
    //..省略了其他配置
    //404的路径配置必须在数组的最后的最后面
    {
        path:'*' //* 代表是选择所有路径 所以如果写在前面了 所有的页面都会报404错误
        component:NotFount
    }
]

总结: 如果路由未命中任何规则, 给出一个兜底的404页面

12.4编程式导航

12.4.1基础使用

目标: 用JS代码来进行跳转

语法:

this.$router.push({
    path: "路由路径", // 都去 router/index.js定义
    name: "路由名"
})

main.js - 路由数组里, 给路由起名字

{
    path: "/find",
    name: "Find",
    component: Find
},
{
    path: "/my",
    name: "My",
    component: My
},
{
    path: "/part",
    name: "Part",
    component: Part
},

App.vue - 换成span 配合js的编程式导航跳转

<template>
  <div>
    <div class="footer_wrap">
      <span @click="btn('/find', 'Find')">发现音乐</span>
      <span @click="btn('/my', 'My')">我的音乐</span>
      <span @click="btn('/part', 'Part')">朋友</span>
    </div>
    <div class="top">
      <router-view></router-view>
    </div>
  </div>
</template>
​
<script>
// 目标: 编程式导航 - js方式跳转路由
// 语法:
// this.$router.push({path: "路由路径"})
// this.$router.push({name: "路由名"})
// 注意:
// 虽然用name跳转, 但是url的hash值还是切换path路径值
// 场景:
// 方便修改: name路由名(在页面上看不见随便定义)
// path可以在url的hash值看到(尽量符合组内规范)
export default {
  methods: {
    btn(targetPath, targetName){
      // 方式1: path跳转
      this.$router.push({
        // path: targetPath,
        name: targetName
      })
    }
  }
};
</script>

12.4.2 编程式导航 - 跳转传参

目标: JS跳转路由, 传参

语法 query / params 任选 一个

this.$router.push({
    path: "路由路径"
    name: "路由名",
    query: {
        "参数名": 值
    }
    params: {
        "参数名": 值
    }
})
​
// 对应路由接收   $route.params.参数名   取值
// 对应路由接收   $route.query.参数名    取值

==格外注意: 使用path会自动忽略params==

App.vue

<template>
  <div>
    <div class="footer_wrap">
      <span @click="btn('/find', 'Find')">发现音乐</span>
      <span @click="btn('/my', 'My')">我的音乐</span>
      <span @click="oneBtn">朋友-小传</span>
      <span @click="twoBtn">朋友-小智</span>
    </div>
    <div class="top">
      <router-view></router-view>
    </div>
  </div>
</template>
​
<script>
// 目标: 编程式导航 - 跳转路由传参
// 方式1:
// params => $route.params.参数名
// 方式2:
// query => $route.query.参数名
// 重要: path会自动忽略params
// 推荐: name+query方式传参
// 注意: 如果当前url上"hash值和?参数"与你要跳转到的"hash值和?参数"一致, 爆出冗余导航的问题, 不会跳转路由
export default {
  methods: {
    btn(targetPath, targetName){
      // 方式1: path跳转
      this.$router.push({
        // path: targetPath,
        name: targetName
      })
    },
    oneBtn(){
      this.$router.push({
        name: 'Part',
        params: {
          username: '小传'
        }
      })
    },
    twoBtn(){
      this.$router.push({
        name: 'Part',
        query: {
          name: '小智'
        }
      })
    }
  }
};
</script>

总结: 传参2种方式

query方式

params方式

12.5 VUE路由的嵌套和守卫

12.5.1vue-路由的路由嵌套(重点)

目标:现在的一级路由之下 ,在嵌套二级路由

router-view嵌套架构图

  1. 创建需要用的所有组件

    src/views/Find.vue -- 发现音乐页

    src/views/My.vue -- 我的音乐页

    src/views/Second/Recommend.vue -- 发现音乐页 / 推荐页面

    src/views/Second/Ranking.vue -- 发现音乐页 / 排行榜页面

    src/views/Second/SongList.vue -- 发现音乐页 / 歌单页面

  2. main.js– 继续配置2级路由

    一级路由path从/开始定义

    二级路由往后path直接写名字, 无需/开头

    嵌套路由在上级路由的children数组里编写路由信息对象

  3. 说明:

    App.vue的router-view负责发现音乐和我的音乐页面, 切换

    Find.vue的的router-view负责发现音乐下的, 三个页面, 切换

配置二级导航和样式(==可直接复制==) - 在Find.vue中

<template>
  <div>
    <!-- <p>推荐</p>
    <p>排行榜</p>
    <p>歌单</p> -->
    <div class="nav_main">
      <router-link to="/find/recommend">推荐</router-link>
      <router-link to="/find/ranking">排行榜</router-link>
      <router-link to="/find/songlist">歌单</router-link>
    </div>
​
    <div style="1px solid red;">
      <router-view></router-view>
    </div>
  </div>
</template>
​
<script>
export default {};
</script>
​
<style scoped>
.nav_main {
  background-color: red;
  color: white;
  padding: 10px 0;
}
.nav_main a {
  text-align: center;
  text-decoration: none;
  color: white;
  font-size: 12px;
  margin: 7px 17px 0;
  padding: 0px 15px 2px 15px;
  height: 20px;
  display: inline-block;
  line-height: 20px;
  border-radius: 20px;
}
.nav_main a:hover {
  background-color: brown;
}
.nav_main .router-link-active{
  background-color: brown;
}
</style>

配置路由规则-二级路由展示

const routes = [
  // ...省略其他
  {
    path: "/find",
    name: "Find",
    component: Find,
    children: [
      {
        path: "recommend",
        component: Recommend
      },
      {
        path: "ranking",
        component: Ranking
      },
      {
        path: "songlist",
        component: SongList
      }
    ]
  }
  // ...省略其他
]
  1. 说明:

  • App.vue, 外层的router-view负责发现音乐和我的音乐页面切换

  • Find.vue 内层的router-view负责发现音乐下的子tab对应的组件切换

  1. 运行 - 点击导航观察嵌套路由在哪里展示

总结: 嵌套路由, 找准在哪个页面里写router-view和对应规则里写children

12.5.2 声明导航 - 类名区别

目标: router-link自带的2个类名的区别是什么

观察路由嵌套导航的样式

  • router-link-exact-active (精确匹配) url中hash值路径, 与href属性值完全相同, 设置此类名

  • router-link-active (模糊匹配) url中hash值, 包含href属性值这个路径

12.5.3 全局前置守卫

目标:在路由转跳之前,先执行一次前置守卫函数,判断是否可以正常转跳

使用例子:在转跳路由之前,判断用户是否满足了某些条件,如果满足才允许转跳 若未满足则弹出警告

1.在路由对象上使用固定方法beforeEach()

//目标:路由守卫
//场景:当你要对路由的权限判断时
//语法(固定):router.beforeEach((to,from,next)=>{
    路由执行之前先执行这里 如果满足转跳
})
//参数1:to 是要转跳到哪里去
//参数2:from是从哪里来
//参数3:next是函数体 next()是进行正常的转跳  next(false)是停留在原地 next('其他转跳地址是强行转跳到该处')
//如果没有写next 不会进行转跳而是停留在原地
​
​
//例子:判断用户是否登录,是否决定转跳
const isLogin = true; // 登录状态(未登录)
router.beforeEach((to, from, next) => {
  if (to.path === "/my" && isLogin === false) {
    alert("请登录")
    next(false) // 阻止路由跳转
  } else {
    next() // 正常放行
  }
})
​

总结: next()放行, next(false)留在原地不跳转路由, next(path路径)强制换成对应path路径跳转

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值