Vue的学习路线汇总

165 阅读10分钟

3 Vue 基础语法

1:插值表达式又叫小胡子语法

<template>
 <div>
{{str}}
{{obj.name}}
{{obj.age>18?"成年":"未成年"}}
 </div>
</template>
           
<script>
export default{
    name:'',
    data () {
        return {
          str :'holle Wrod',
          obj:{
            name:'我是vue语法',
            age:5
          }
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
    }
}
</script>
<style lang='less'  scoped>
 
</style>

2 v-bind动态属性

  1. 语法:v-bind语法和简写
  2. 简写: :num="num"
<template>
 <div>
  <a :href='src'>去百度</a>
 </div>
</template>
           
<script>
export default{
    name:'',
    data () {
        return {
          src :'http://www.baidu.com',
          
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
    }
}
</script>
<style lang='less'  scoped>
 
</style>

3 v-on和修饰符

  • 语法 v-on:click='addSum'
  • 简写:@click="addSum"
  • 同时还可以函数传值 @click= "addSum(10)"
  • v-on的修饰符 事件修饰
  • stop阻止事件冒泡
  • pervent 阻止默认行为 按键修饰符
  • keydown 键盘按下
  • keyup 键盘弹起
  • keypress 先按下后弹起
<template>
 <div>
   <!-- v-on用法 -->
   <button v-on:click="addSum">sum+5</button>
   <!-- v-on的简写 -->
   <br>
   <button @click="addSum">sum+10</button>
   <br> 
   <!-- v-on的传值 -->
   <button @click="addSum1(20)">sum+20</button>
{{sum}}
 </div>
</template>
           
<script>
export default{
    name:'',
    data () {
        return {
       sum:0
          
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
addSum(a){

this. sum+=5
this.sum+=a
},
addSum1(a){
this.sum+=a
}
    }
}
</script>
<style lang='less'  scoped>
 
</style>

3 v-model和修饰符

  • 语法:v-model="Vue数据变量"
  • 双向绑定
  • 变量变化==>视图自动同步
  • 视图变化==》变量自动同步 v-model.修饰符="vue数据变量"
  • .number 以parseFloat转成数字类型
  • .trim 去除首尾空白字符
  • .lazy 在change时触发而非inupt时
<template>
  <div>
    <div>
      <!-- (重要)
      遇到复选框, v-model的变量值
      非数组 - 关联的是复选框的checked属性
      数组   - 关联的是复选框的value属性
       -->
      <span>爱好: </span>
      <input type="checkbox" v-model="hobby" value="抽烟">抽烟
      <input type="checkbox" v-model="hobby" value="喝酒">喝酒
      <input type="checkbox" v-model="hobby" value="写代码">写代码
    </div>
    <div>
      <span>性别: </span>
      <input type="radio" value="男" name="sex" v-model="gender">男
      <input type="radio" value="女" name="sex" v-model="gender">女
    </div>
    <div>
      <span>自我介绍</span>
      <textarea v-model="intro"></textarea>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      hobby: [], 
      sex: "",
      intro: "",
    };
    // 总结:
    // 特别注意: v-model, 在input[checkbox]的多选框状态
    // 变量为非数组, 则绑定的是checked的属性(true/false) - 常用于: 单个绑定使用
    // 变量为数组, 则绑定的是他们的value属性里的值 - 常用于: 收集勾选了哪些值
  }
};
</script>

4 v-html和v-text

  • 相同点:v-html和v-text都可以设置标签显示的内容

    不同点:v-html会解析标签,不会输出标签
    v-text会把标签当成普通字符串输

  • 需要注意一点的是:v-html或v-text会覆盖插值表达式

  • 原理:因为插值表达式先被解析,指令晚于插值表达式

<template>
  <div>
    <p v-html="str"></p><br>
    <span v-text="str1"></span>
  </div>
</template>

<script>
export default {
  data() {
    return {
    
      str: "<span>我是一个v-html渲染的标签</span>",
      str1:"<span>我是一个v-text渲染的标签</span>"
    }
  }
}
</script>

111.png

5 v-if和v-show 控制标签的显示和隐藏

  • 语法:
    • v-show="vue变量"
    • v-if="vue变量"
  • 原理
    • v-show 用的display:none隐藏 (频繁切换使用)
    • v-if 直接从DOM树上移除
  • 高级
    • v-else使用
<template>
  <div>
    <h1 v-show="isOk">v-show的盒子</h1>
    <h1 v-if="isOk">v-if的盒子</h1>

    <div>
      <p v-if="age > 18">我成年了</p>
      <p v-else>还得多吃饭</p>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      isOk: true,
      age: 15
    }
  }
}
</script>

5 v-for 语法

  • 语法
    • v-for="(值变量, 索引变量) in 目标结构"
    • v-for="值变量 in 目标结构"
  • 目标结构:
    • 可以遍历数组 / 对象 / 数字 / 字符串
  • 注意: v-for的临时变量名不能用到v-for范围外 同级标签的key值不能重复

遍历数组

<template>
  <div id="app">
      <!-- v-for 把一组数据, 渲染成一组DOM -->
      <!-- 口诀: 让谁循环生成, v-for就写谁身上 -->
      <p>学生姓名</p>
      <ul>
        <li v-for="(item, index) in arr" :key="item">
          {{ index }} - {{ item }}
        </li>
      </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      arr: ["小明", "小欢欢", "大黄"]
  }
}
</script>

遍历对象

<template>
  <div id="app">
      <!-- 省略其他 -->
      <p>学生详细信息</p>
      <ul>
        <li v-for="obj in stuArr" :key="obj.id">
          <span>{{ obj.name }}</span>
          <span>{{ obj.sex }}</span>
          <span>{{ obj.hobby }}</span>
        </li>
      </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // ...省略其他
      stuArr: [
        {
          id: 1001,
          name: "张三",
          sex: "男",
          hobby: "打球",
        },
        {
          id: 1002,
          name: "李四",
          sex: "男",
          hobby: "听音乐",
        }
      ]
    }
  }
}
</script>

v-for 注意点1 避免v-for和v-if在一起使用

Vue 处理指令时,v-for 比 v-if 具有更高的优先级, 虽然用起来也没报错好使, 但是性能不高, 如果你有5个元素被v-for循环, v-if也会分别执行5次.

v-form 注意点2:

v-for更新监测

  • 什么是v-for的就地更新监测呢,就是如果循环这个数组,利用数组的方法对数组进行操作,如果这些方法不 会改变原数组,就算数组数据改了,页面也不会改变的, 所以总结来说,修改数组是否更新页面取决于数组方法是否修改了原数组

例如会修改原数组的方法:push,pop,shift,unshift splice

不会修改原数组:slice/forEach/findeInde/every/map/reduce/indexOf

  • 当然在Vue2的解决方法有:
  • 1:可以将新数组覆盖给原数组即可解决
  • 2:this.$set("数组名","修改哪个值","修改的值")

v-for就地更新

以前原生的for循环渲染的时候在dom页面生成,如果后面添加了一个数据,他会把所有的数据从dom上移除,然后重新for循环,重新生成数据,这时候就会出现一个问题,我每添加一个数据,都会重新销毁在重新生成这个过程,就会引起dom的重绘和回流,大大影响性能。

  • 首先我们先回顾一个知识点:
  • 重绘是什么:简单来说就是,页面元素的颜色/透明度等信息发生变化后触发重绘
  • 回流是什么:页面元素的几何信息(宽高/位置)发生变化时触发回流
  • 结论:回流一定会触发重绘,而重绘不一定会触发回流。
  • 不建议使用js做动画,将来如果要做动画一定要用css

- 好了现在回归到正题,继续看在Vue中,v-for是怎么来更新dom的。

  • 在Vue中v-for更新时, 是如何操作DOM的,然后解决这个问题的 首先了解一个知识点叫虚拟dom:什么是虚拟dom本质是保存节点信息,属性和内容的一个js 对象。
  • 其实就是 循环出新的DOM结构, 和旧的DOM结构对比, 尝试复用标签就地更新内容,说白了,就是对比之后如果没改变就复用之前的标签,不做改动,发现有改动地方,就只需要在新虚拟dom 上修改不同的地方就可以了。

111.png

v-for中key的作用

在上面对比过程中就可以得知,新旧dom的对比,他是如何准确的找到哪些哪些是改变了,哪些是没有改变的,就通过key来查找,key跟key比较就会有唯一性,能准确查找到哪个地方改变了,如果用index来对比,以索引来对比就等同于没有对比,依旧是采用的就地复用策略,而key就不一样了,key值唯一不重复的字符或者数字在日常开发中,我们把key设置为id

333.png

5 动态class和动态style语法

  • 给style赋值和class区别是?
  • :class="{类名: 布尔值}”, true使用, false不用
  • :style="{css属性名: 值}"
<template>
  <div>
 <button @click="btn">改变颜色</button>
  <p :class="{dome:flag}"></p>
  </div>
</template>

<script>
export default {
  data() {
    return {
  flag:false
    }
  },
    methods:{
      btn(){
        this.flag=true
      }
    }
}
</script>
<style scoped>
.dome{
  width: 100px;
  height: 100px;
  background: red;
}
</style>

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

<template>
  <div>
 
  <span :style="{fontSize}">我是span标签</span>
  </div>
</template>

<script>
export default {
  data() {
    return {

  fontSize:"120px"
    }
  },
   
}
</script>
<style scoped>
.dome{
  width: 100px;
  height: 100px;
  background: red;
}
</style>

6 计算属性 computed语法

- 计算属性名(){} 简单写法

  • 必须要有return '值'
  • 应用场景:当变量的值,需要通过的变量计算而得来
  • 计算属性的最大特点:计算属性内部依赖的所有数据,只要修改,会自动重新计算并更新页面
  • 注意一点:用法和data中的数据一模一样,他也是数据,所以不能和data中的数据重名
  • 思考:在下面的代码中用表达式求和,用函数调用求和,都可以实现一样的功能,但是我们为什么要使用计算属性呢 答案:是因为计算属性带缓存,如果依赖的变量不变,计算属性第一次计算后,他会进行缓存,下次再用到求和的时候,他会直接从缓存中拿去,假设这个求和功能在页面中用到了100次,这就是大大提高了性能
<template>
  <div>
 
  <span>计算属性求和:{{num }}</span><br>
  <span>表达式求和{{a+b}}</span><br>
  <span>函数调用{{sum(a,b)}}</span>
  </div>
</template>

<script>
export default {
  data() {
    return {

  a:10,
  b:10
  
    }
   
  },
  // 函数调用
  methods:{
sum (a,b){
  return a+b
}
  },
    // 计算属性名(){}
// 必须要有return '值'
   computed:{
num(){
return this.a+this.b  
}
   }
}
</script>
<style scoped>

</style>


计算属性的完整写法

计算属性简单写法一般很难满足我们日常的开发,所以要学习计算属性的完整写法get和set

  • get当计算属性被访问时触发,有缓存, 必须要返回一个值,内部依赖属性被修改时触发, 将赋的值将以参数的形式带给set方法
  • set 方法:当计算属性被修改时触发,将赋的值以参数的形式带个set方法
 computed:{
  // 计算属性名(){}
// 必须要有return '值'
//

num:{
  set(){

  },
  get(){
    return '值'
  }
}
}
   
}

7 侦听器 watch 语法的使用

<template>
 <div>
 姓名:<input type="text" v-model="name">
 </div>
</template>
           
<script>
export default{
    name:'',
    data () {
        return {
            name:'张三'
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
    },
    watch:{
      // 侦听的属性名 (value1,value2)
      //value1 是新值
      //value2 是旧值
      name(value1,value2){
        console.log(value1,value2,"name被修改了");
      }
    }
}
</script>
<style lang='less'  scoped>
 
</style>

上面侦听只能侦听值类型的简单数据类型,而复杂类型就需要用到深度侦听 深度侦听有两个参数

  • deep:true, 开启深度侦听
  • handler,类型为深度侦听
  • 注: 深度侦听,Vue无法准确把旧值传递过来,这是Vue设计上的缺陷
<template>
 <div>
 姓名:<input type="text" v-model="name"><br>
 年龄:<input type="text" v-model="obj.age">
 </div>
</template>
           
<script>
export default{
    name:'',
    data () {
        return {
            name:'张三',
            obj:{
              age:18
            }
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
    },
    watch:{
      // 侦听的属性名 (value1,value2)
      name(value1,value2){
        console.log(value1+'新值',value2+'旧值',"name被修改了");
      },
      obj:{
        deep:true,
        handler(ageVal,ageVal1){
          // 深度侦听,Vue无法准确把旧值传递过来,这是Vue设计上的缺陷
              console.log(ageVal,ageVal1,"age被修改了");
        }
      
         
      }
    }
}
</script>
<style lang='less'  scoped>
 
</style>

8 组件 语法(组件创建,组件通信)

1:为什么要使用组件

  • 就是同一个样式或者功能,实现不同地方的复用,各自独立,便于复用
  • 组件四个步骤:
  • 1:创建组件
  • 2:引入组件
  • 3:注册组件
  • 4:使用组件

5.png

<template>
 <div>
 //使用组件
<Panel></Panel><hr>
<Panel></Panel><hr>
<Panel></Panel>
 </div>
</template>
           
<script>
//引入组件
import Panel from './components/Panel.vue'
export default{
    name:'',
    //注册组件
    components:{
 Panel
    },
    data () {
        return {
            
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
    }
}
</script>
<style lang='less'  scoped>
 
</style>

介绍一点注册全局组件的方法: 在main.js中,引入

  • import Panel from './components/Panel.vue'
  • 注册 Vue.component("Panel",Panel)

8.1组件的scoped的使用和原理

  • 什么是scoped
  • 在Vue组件内样式,只针对当前组件内标签生效如何做
  • style上添加scoped
<style lang='less' scoped> </style>
  • 原理:他做了两件事
  • 1 scoped给当前组件内标签都被添加了data-v-hash值得属性
  • 2:css选择器都被添加了[data-v-hash]的属性选择器。

8.2:组件的通信

父向子传递 props

<template>
 <div>
<Panel :str='str'></Panel>
 </div>
</template>
============父组件
============子组件
<template>
 <div>
<h1>{{str}}</h1>
 </div>
 
</template>
           
<script>
export default{
 name:'',
    props:{
str:{
    type:String,
    default:''
}
    },  
}
</script>
<style lang='less'  scoped>
 
</style>

8.2.1子向父传值

  • 使用自定义事件
  • 1:在父组件中给子组件绑定自定义事件
  • 2:当子组件执行$emit触发父组件事件时,父组件的事件处理函数会执行。
  • 注:this.$emit 方法触发父组件绑定的自定义事件,他有两个参数
  • 参数1:事件名
  • 参数2:要传递过去的参数
============================子组件
<template>
 <div>
<h1>{{str}}</h1>
<button @click="btn">点我修改父组件的值</button>
 </div>
 
</template>
           
<script>
export default{
 name:'',
    props:{
str:{
    type:String,
    default:''
}

    },  
  methods:{
      btn(){
          this.$emit("dome","大河之水天上来")
      }
  }
}
</script>
<style lang='less'  scoped>
 
</style>
==============================父组件
<template>
 <div>
<Panel :str='str' @dome="upDome"></Panel>
<h1>{{str}}</h1>
 </div>
</template>
           
<script>
import Panel from './components/Panel.vue'
export default{
    name:'',
    components:{
 Panel
    },
    data () {
        return {
            str:'窗前明月光'
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
      upDome(str){
        this.str=str
      }
    }
}
</script>
<style lang='less'  scoped>
 
</style>

8.2.2 单向数据流

  • 问题:当我们子组件接收到了父组件传过来的值,能不能不用自定义事件来,我们能否在子组件这边直接修改他的值呢,
  • 答案就是:不能
  • 原理:从父到子的数据流向,叫单向数据,在Vue中规定,不能直接修改父组件的值,原因就是假设在项目中这个值如果在很多子组件都用到了,各个子组件都在改这个值,一旦这个值出了问题就很排查,数据管理起来很混乱。

9 Vue中的生命周期

9.1 Vue生命周期分为四个周期

1 初始化阶段 2 挂载阶段 3 更新阶段 4 销毁阶段

9.2接下详细讲解这个4个阶段8个方法

444.png

初始化 这个阶段Vue做的事情

  • beforeCreate:初始化事件和生命周期函数,这个时候生命周期钩子函数被执行了
  • created:Vue内部添加了data和methods等实例,实例创建了

挂载阶段 这个阶段Vue做的事情

  • 在beforeMount执行之前,
  • 1:首先会对template标签进行检查
  • 有:编译template返回render渲染函数
  • 无:编译el选项对应标签作为template(要渲染的模板)
  • 2:虚拟dom挂载成真实dom之前
  • 这个时候beforeMount生命周期钩子函数被执行
  • 3:Create 把虚拟dom和渲染的数据一并挂载到真实dom上
  • 5:真实dom挂载完毕,页面已经出来了
  • 6:mounte 生命周期钩子函数被执行,这时候就停在这了

更新阶段 这个阶段Vue做的事情

  • 1 当data里数据改变,更新dom之前
  • 2 beforeUpdate 生命周期钩子函数被执行
  • 3 虚拟dom重新渲染,打补丁到真实dom
  • 4 updated 生命周期钩子函数被执行,当data中数据发生变化并更新页面后执行这个钩子函数。
  • 5 当有data数据改变,重复这个循环

销毁阶段 这个阶段Vue做的事情

  • 1 比如组件dom被移除例如v-if
  • 2 beforeDestroy 生命周期钩子函数被执行
  • 3 拆卸数据监视器,子组件和事件侦听器
  • 4 实例销毁后,最后触发一个钩子函数
  • 5 destroyed 生命周期钩子函数被执行了

10 组件的插槽的使用

  • 为什么要使用插槽 可以让组件内容自定义,极大的提高组件的灵活性
  • 插槽分为
  • 匿名插槽
  • 具名插槽
  • 作用域插槽

10.1 匿名插槽的使用

<slot></slot> 占位符使用

  • 1 在组件内找到需要插槽的地方,使用占位

  • 2:在使用组件是,将要自定的标签传入组件中

子组件

<template>
 <div>
 <h1>李白</h1>
 <!-- slot占位符 -->
<slot></slot>
 </div>
</template>         
<script>
export default{
name:'Panel',
        data () {
                return {
                        
                }
        },
        created () {
        },
        computed:{
        },
        methods:{
        }
}
</script>
<style lang='less'  scoped>
 
</style>

父组件

<template>
 <div>
<Panel>
<h3>窗前明月光</h3>
<h3>疑是地上</h3>
<h3>举头望明月</h3>
<h3>低头思故乡</h3>
  
  </Panel><hr>
<Panel>
<h3>日照香炉生紫烟</h3>
<h3>遥看瀑布挂前川</h3>
<h3>飞流直下三千尺</h3>
<h3>疑是银河落九天</h3>
  </Panel><hr>
<Panel>
<h3>故人西辞黄鹤楼</h3>
<h3>烟花三月下扬州</h3>
<h3>孤帆远影碧空尽</h3>
<h3>唯见长江天际流</h3>
</Panel>
 </div>
</template>
           
<script>
import Panel from './components/Panel.vue'
export default{
    name:'',
    components:{
 Panel
    },
    data () {
        return {
           
        }
    },
 
    }

</script>
<style lang='less'  scoped>
 
</style>

111.png

10.3 默认内容

  • 什么是默认内容
  • 就是使用我组件的人,不给slot传标签,就是把slot标签里面的内容当做默认内容 父组件
<template>
 <div>
<Panel>
<h3>窗前明月光</h3>
<h3>疑是地上</h3>
<h3>举头望明月</h3>
<h3>低头思故乡</h3>
  
  </Panel><hr>
<Panel>
<h3>日照香炉生紫烟</h3>
<h3>遥看瀑布挂前川</h3>
<h3>飞流直下三千尺</h3>
<h3>疑是银河落九天</h3>
  </Panel><hr>
<Panel>
<h3>故人西辞黄鹤楼</h3>
<h3>烟花三月下扬州</h3>
<h3>孤帆远影碧空尽</h3>
<h3>唯见长江天际流</h3>
</Panel>
<Panel></Panel>
 </div>
</template>
           
<script>
import Panel from './components/Panel.vue'
export default{
    name:'',
    components:{
 Panel
    },
    data () {
        return {
           
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
     
      }
    }

</script>
<style lang='less'  scoped>
 
</style>

子组件

<template>
 <div>
 <h1>李白</h1>
 <!-- slot占位符 -->
<slot>
    <h1>默认内容</h1>
</slot>
 </div>
</template>         
<script>
export default{
name:'Panel',
        data () {
                return {
                        
                }
        },
        created () {
        },
        computed:{
        },
        methods:{
        }
}
</script>
<style lang='less'  scoped>
 
</style>

444.png

10.2:具名插槽的使用

具名插槽的基本使用

1:在组件内给slot设置name属性

2:在使用组件时,通过template包裹不同区域的代码

3:在template标签上使用v-slot名称,来指定name

4:v-slot:可以简写#

父组件

<template>
 <div>
<Panel>

  <template v-slot:title>
   <h1>静夜思---李白</h1>
  </template>
  <template #content>
    <h3>窗前明月光</h3>
    <h3>疑是地上</h3>
    <h3>举头望明月</h3>
     <h3>低头思故乡</h3>
  </template>
</Panel><hr>
<Panel>
  <template v-slot:title>
    <h2>李白---望庐山瀑布</h2>
  </template>
  <template #content>
  <h3>日照香炉生紫烟</h3>
   <h3>遥看瀑布挂前川</h3>
   <h3>飞流直下三千尺</h3>
   <h3>疑是银河落九天</h3>
  </template>
  </Panel><hr>
<Panel>
  <template v-slot:title>
    <h2>  李白---黄鹤楼送孟浩然之广陵</h2>
  </template>
<template #content>
<h3>故人西辞黄鹤楼</h3>
<h3>烟花三月下扬州</h3>
<h3>孤帆远影碧空尽</h3>
<h3>唯见长江天际流</h3></template>
</Panel>
<Panel></Panel>
 </div>
</template>
           
<script>
import Panel from './components/Panel.vue'
export default{
    name:'',
    components:{
 Panel
    },
    data () {
        return {
           
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
     
      }
    }

</script>
<style lang='less'  scoped>
 
</style>

子组件

<template>
 <div>
 <slot name='title'></slot>
 <!-- slot占位符 -->
<slot name="content">
    <h1>默认内容</h1>
</slot>
 </div>
</template>         
<script>
export default{
name:'Panel',
        data () {
                return {
                        
                }
        },
        created () {
        },
        computed:{
        },
        methods:{
        }
}
</script>
<style lang='less'  scoped>
 
</style>

111.png

10.3:作用域插槽的使用

  • 1:在子组件上,在slot动态绑定一个数据
  • 2:在使用组件时,template标签 v-slot='自定义变量'一般都默认变量为(scope)接收传递过来的数据
  • 3:自定义的变量就是一个对象,会自动收集传递过来的数据,然后使用就可以了 父组件
<template>
 <div>
  <Panel>
  <template v-slot='scope'>
    <h1>{{scope.row.name}}</h1>
    <h1>{{scope.row.title}}</h1>
    <h3>日照香炉生紫烟</h3>
   <h3>遥看瀑布挂前川</h3>
   <h3>飞流直下三千尺</h3>
   <h3>疑是银河落九天</h3></template>
  </Panel>
 <Panel>
<template v-slot='scope'>
  <p>{{scope.row.name}}</p></template>
</Panel>
 </div>
</template>
           
<script>
import Panel from './components/Panel.vue'
export default{
    name:'',
    components:{
      Panel
    },
    data () {
        return {
        
        }
    },
    created () {
    },
    computed:{
    },
    methods:{
    }
}
</script>
<style lang='less'  scoped>
 
</style>

子组件

<template>
 <div>
    

  <slot :row='obj'></slot>
 </div>
</template>         
<script>
export default{
name:'Panel',
        data () {
                return {
                    obj:{
                        name:'李白',
                        title:'望庐山瀑布'
                    }    
                }
        },
        created () {
        },
        computed:{
        },
        methods:{
        }
}
</script>
<style lang='less'  scoped>
 
</style>

11 自定义指令的使用

全局注册:

Vue.directive("自定义指令名",{
inserted(el){
//可以对el标签的扩展额外功能

}
})

局部注册

directives:{
"指令名":{
inserted(el){
//el进行操作
}
}
}

11 $refs获取原生DOM

1:目标标签--添加id/ref
<h1 ref='myH1' id="h">ref/id获取原生dom</h1>
2:恰当时机,通过id/通过fef属性获取目标标签
mounted(){
方法1:通过原生的方式来获取dom
document。getElementById("h")
方法2:通过ref属性来获取dom
this.$refs.myH1
}

12 $nextTick处理异步

为什么要用nextTick,因为有时候我们修改数据后并没有立即去更新dom,因为dom的更新是异步的,他的流程:数据修改=>VUe监听到数据变化=>创建一个异步任务(更新DOM)=>继续执行同步代码=>当DOM更新结束后执行updated钩子函数

$this.nextTick(()=>{
//内容代码
})

v-model的本质

1:给元素绑定一个value属性 2:给元素绑定一个input事件 v-model,是实现双向数据绑定的指令,本质是一个语法题,他一共做了两件事, 1:给元素绑定了一个value属性,单向的数据流 2:给元素绑定一个input事件,数据修改时反向同步给父组件。

12 路由 学习

  • 12.1 什么是路由:路由就是路径和组件的映射关系
  • 12.2 为什么要使用路由:因为Vue组件是单页面应用(SPA):所有的功能在一个html上实现,而前端的路由作用就是实现业务场景的切换,说白了路由就是用来组件切换
  • 路由的好处就是:整体不刷新,用户体验更好
  • 数据传递容易,开发效率高。
  • 缺点:首屏加载会比较慢一点,不利于seo

12.3 vue-router

  • 概念:本质就是一个三方包
  • 组件分类:页面组件(在项目中我们把页面组件放在src/wiews文件夹),复用组件(src/components文件夹中)

12.3.1vue-router的使用

  • 1:下载vue-router模块到当前工程 npm i vue-router@3.5.3
  • 2:在main.js引入VueRouter函数 import VueRouter from 'vue-router'
  • 3:添加到Vue.use()身上,注册全局的router——link和router——view组件 Vue.use(VueRouter)
  • 4:创建路由规则数组 :路径和组件名对应的关系
import Vue from 'vue'
import App from './App.vue'
//第一步下包npm i vue-router@3.5.3指定版本,门前最新的router是vue3的
//第二步 导入路由
import VueRouter from 'vue-router'
//第三步 使用路由插件
Vue.use(VueRouter)
// 第四步创建路由规则
import Find from './views/Find.vue'
import My from './views/My.vue'
import Part from './views/Part.vue'

const routes=[
  {
    path:'/find',
    component:Find
  },
  {
    path:'/my',
    component:My
  },
  {
    path:'/part',
    component: Part
  }
]
// 第五步 创建路由对象 -传入规则
const router=new VueRouter({
  routes
})
//第六步关联到vue实例
new Vue({
  render: h => h(App),
  router
}).$mount('#app')
//第七步占位符占位
 <router-view></router-view>
  • 5:用规则生成路由对象 const router = new VueRouter({ routes })

  • 6:把路由对象注入到new Vue 实例中 const router = new VueRouter({ router })

  • 7:components换成router-view,用router-view作为挂载点,切换不同的路由页面

<router-view></router-view>
  • 这里需要注意的是router和routes

111.png

声明式导航--跳转

  • router提供了一个全局组件 router-link
  • router-link实质上最终会渲染成a链接 to属性等价于提供 href属性(to无需#)
  • router-link提供了声明式导航高亮的功能(自带类名)
  • 总结: 链接导航, 用router-link配合to, 实现点击切换路由 111.png

声明式导航--传参在router-link上的to属性传值, 语法格式如下

-第一种: /path?参数名=值

<!-- 第一种查询字符串传参 -->
 <router-link to="/part?name=小明&age=18&sex=男">查询字符串传参</router-link>
 -----------------------------
 <!-- 对应的组件页面接收$route.query -->
 
 <h1>我是part页面</h1>
<h1>{{$route.query.name}}</h1>
<h1>{{$route.query.age}}</h1>
<h1>{{$route.query.sex}}</h1>
  • 第二种: /path/值 – 需要路由对象提前配置 path: “/path/参数名”
动态路劲传参
第一步路径上/传参
<router-link to="/my/小红/28/女">动态路径传参</router-link>
第二步路由规则上配置
{
    path:'/my/:name/:age/:sex',
    component:My
  },
------------------------- <!-- 对应的组件页面接收$route.params -->
<h1>我是my页面</h1>
<h1>{{$route.params.name}}</h1>
<h1>{{$route.params.age}}</h1>
<h1>{{$route.params.sex}}</h1>

编程式导航

就是利用js代码来跳转和传参,因为声明式导航需要route-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> -->
      <!-- 编程式导航跳转--根据路径跳转 -->
     <span @click="goto('/find')">发现音乐页面</span>
     <span  @click="goto('/my')">我的音乐页面</span>
     <span @click="goto('/part')">我的朋友页面</span>
    </div>
    <div class="top">
         <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {
    methods:{
        goto(path){
            // this.$route 路由参数对象,用于获取传递过来的参数
            // this.$router 路由对象,进行路由跳转

             this.$router.push({
                path
             })
            // 简写--可以直接传字符串
            //this.$router.push('/find')

           // this.$$router.push(path)

        }
    }
};
</script>

第二种根据name来跳转

const routes=[
  {
    path:'/find',
    component:Find,
    name:'Find'
  },
  {
    path:'/my',
    component:My,
    name:'My'
  },
  
  {
    path:'/part',
    component: Part,
    name:'Part'
  },
============================在路由配置name属性
<template>
  <div>
    <div class="footer_wrap">

      <!--声明式导航 -->
      <!-- <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/part">朋友</router-link> -->
      <!-- 编程式导航跳转--根据路径跳转 -->
     <span @click="goto('Find')">发现音乐页面</span>
     <span  @click="goto('My')">我的音乐页面</span>
     <span @click="goto('Part')">我的朋友页面</span>
    </div>
    <div class="top">
         <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {
    methods:{
        goto(name){
            // this.$route 路由参数对象,用于获取传递过来的参数
            // this.$router 路由对象,进行路由跳转
        this.$router.push({
                name
            })

        }
    }
};
</script>

声明式导航传参两种方式无需任何配置

  • 注意点1:如果用的是path路径跳转,则不能params来传,因为path会忽略params
  • 所以如果用的是path来跳转的,则只能用query来传参
  • 注意点2:如果比较隐私的数据用query建议不用query传,因为query在路径上 组合:
    path+query传参

name+params 页面刷新后数据会丢失,但是不用担心丢失问题,因为传过去我们已经用了

<template>
  <div>
    <div class="footer_wrap">

      <!--声明式导航 -->
      <!-- <router-link to="/find">发现音乐</router-link>
      <router-link to="/my">我的音乐</router-link>
      <router-link to="/part">朋友</router-link> -->
      <!-- 编程式导航跳转--根据路径跳转 -->
     <span @click="goto">发现音乐页面</span>
     <span  @click="gotoQuery">我的音乐页面</span>
     <span  @click="gotoParams">我的朋友页面</span>
    </div>
    <div class="top">
         <router-view></router-view>
    </div>
  </div>
</template>

<script>

/* 注意点:如果用的是path路径跳转,则不能params来传,因为path会忽略params
所以如果用的是path来跳转的,则只能用query来传参。*/

/* 总结:组合:path+query传参
              name+params 页面刷新后数据会丢失
*/
export default {
    methods:{
       gotoQuery(){
          this.$router.push({
              path:'/my',
              query:{
                 name:'假如生活欺骗了你'
              }
             
          })
    },
========================query传query接  
<h1>我是my页面</h1>
<h1>{{$route.query.name}}</h1>
  
=====================================    
    gotoParams(){
           this.$router.push({
               name:'Part',
               params:{
                   name:'不要悲伤,不要心急'
               },
               query:{
                   age:18
               }

              
           })
    },
    goto(){

    }
}
}
</script>
======================params传params接
<h1>我是part页面</h1>

<h1>{{$route.query.age}}</h1>
<h1>{{$route.params.name}}</h1>

重定向和模式

什么是路由重定向(redirect)

就是匹配path后强制跳转path的路径

const routes = [
  {
    path: "/", // 默认hash值路径
    redirect: "/find" // 重定向到/find
    // 浏览器url中#后的路径被改变成/find-重新匹配数组规则
  }
]

404页面(*)

  • 请求一个不存在页面,我们可以显示404页面
  • 可以新建一个组件做404页面,我们为给用户更好的体验,写样式,然后*号配置路由即可

import NotFound from '@/views/NotFound'

const routes = [
  // ...省略了其他配置
  // 404在最后(规则是从前往后逐个比较path)
  {
    path: "*",
    component: NotFound
  }
]

路由模式设置(*)

在实例化路由对象时,传入mode选项和值修改

const router = new VueRouter({
  routes,
  mode: "history" // 打包上线后需要后台支持, 模式是hash
})

嵌套和守卫

路由嵌children

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

14.Vue路由-声明式导航-类名区别

目标

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

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

111.png

15.Vue路由-全局前置守卫

什么是路由守卫(就是让路由有权限判断)

    1. 路由在真正跳转前, 会执行一次beforeEach函数, next调用则跳转, 也可以强制修改要跳转的路由
    1. 在路由对象上使用固定方法beforeEach 3:beforeEach他有三个参数
  • 参数1 to: 要跳转到的路由 (路由对象信息)   目标

  • 参数2 from: 从哪里跳转的路由 (路由对象信息) 来源

  • 参数3 next,是否放行 (true,false)

  • 注意一点:导航守卫一旦注册,必须使用next,否则路由系统就会瘫痪。

// 目标: 路由守卫
// 场景: 当你要对路由权限判断时
// 语法: router.beforeEach((to, from, next)=>{//路由跳转"之前"先执行这里, 决定是否跳转})
// 参数1 to: 要跳转到的路由 (路由对象信息)    目标
// 参数2 from: 从哪里跳转的路由 (路由对象信息)  来源
// 参数3 next: 函数体 - next()才会让路由正常的跳转切换, next(false)在原地停留, next("强制修改到另一个路由路径上") 决定是否放行
// 注意: 如果不调用next, 页面留在原地
​
// 例子: 判断用户是否登录, 是否决定去"我的音乐"/my
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路径跳转

16.Vue路由-组件缓存

组件缓存的好处:不会频繁创建和销毁组件,页面更快的呈现

1.  <keep-alive></keep-alive>, 裹切换的挂载点即可
2:用法
 <keep-alive>
  <router-view></router-view>
 </keep-alive>

2:

18.Vue路由-组件缓存-新钩子函数

  • 1:上面介绍到了组件缓存,但是如果我只想缓存某个组件或者说我不想缓存某个组件怎么做
  • 2:keep-alive的高级用法他有两个属性来解决上面的问题
  • 第一步:给组件取个名字
<script>
    export default {
        name: '组件名'
    }
</script>

  • 第二步:包含缓存和不包含两种include/exclude
  • include 只包含写入到include这个组件,一定要组件名,多个用逗号隔开
<keep-alive include="Find">
    <router-view></router-view>
</keep-alive>

include 不包含写入到exclude这个组件,一定要组件名,多个用逗号隔开

<keep-alive exclude="My,Part">
    <router-view></router-view>
</keep-alive>

19 组件路由钩子

  • 前面学了8个路由钩子,现在扩展两个
  • 使用keep-alive后, 新增了2个钩子函数
  • 组件不执行销毁/初始化创建的方法了, 如何知道组件被失去激活/激活呢?
  1. activated --- 组件被激活状态
  2. deactivated --- 组件被失去激活状态
activated () {
    console.log("发现音乐-组件激活");
},
deactivated () {
	console.log("发现音乐-组件失去激活");
}