0


Vue知识点汇总【持更】

(部分图例引用黑马教程及其他文章来源)

1 vue的两个特性

1.1 数据驱动视图

数据的变化会驱动视图自动更新。

1.2 双向数据绑定

在网页中,form负责采集数据,Ajax负责提交数据。数据源的变化,会被自动渲染到页面上;页面上表单采集的数据发生变化的时候,会被 vue 自动获取到,并更新到数据源中。

2 MVVM工作原理

MVVM 是 vue 实现数据驱动视图和双向数据绑定的核心原理。MVVM 指的是 Model、View 和 ViewModel,它把每个 HTML 页面都拆分成了这三个部分,如图所示:

注:

1、当数据源发生变化时,会被 ViewModel 监听到,VM会根据最新的数据源自动更新页面的结构。

2、当表单元素的值发生变化时,也会被VM监听到,VM会把变化过后最新的数据自动同步到Model数据源中。

3 vue 的指令

3.1 内容渲染指令

内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。常用的内容渲染指令有如下 3 个:

  • {{}}插值表达式:在实际开发中用的最多,只是内容的占位符,不会覆盖原有的内容!

(注意:插值表达式只能用在元素的内容节点中,不能用在元素的属性节点中!)

  • v-text:会覆盖元素内部原有的内容!
  • v-html:不仅会覆盖原来的内容,而且可以把带有标签的字符串,渲染成真正的HTML内容!

3.2 属性绑定指令

** v-bind**:为元素的属性动态绑定属性值,则需要用到 v-bind 属性绑定指令。

  • 在 vue 中,可以使用v-bind:属性为元素的属性动态绑定值。
  • 简写是英文的 " **: **"。
  • 在使用 v-bind 属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,例如:
<!--html-->
<a :href="'https://www.runoob.com/vue2/'+url">点击跳转vue菜鸟教程</a>

<!--script-->
const vm2=new Vue({
     el:'#box2',
     data:{
         url:'vue-tutorial.html'
     }
})

3.3 事件绑定指令

vue提供了v-on事件绑定指令,用于为DOM元素绑定事件监听。

  • 注意:原生DOM对象有onclick、oninput、onkeyup等原生事件,替换为vue的事件绑定形式后,分别为v-on:click、v-on:input、v-on:keyup。
  • 在v-on指令所绑定的事件处理函数,可以接收事件参数对象event。
  • $event是vue提供的特殊变量,用来表示原生的事件参数对象event。
<button @click="add">自增</botton>
<button @click="changeColor">变色</botton>
data(){
    return{
        count:'',
    }
}
methods:{
    add(){
        this.count++;    
    },
    changeColor(e){
            e.target.style.backgroundColor='red';
    }
}

3.4 事件修饰符

3.5 按钮修饰符

3.6 双向数据绑定指令

3.7 条件渲染指令

  • v-if、v-else、v-else-if条件性的渲染某元素,判定为true时渲染,否则不渲染
  • 两者区别:v-if条件不满足不渲染,v-show条件不满足令其display为none
<div v-if="score<60">不及格</div>
<div v-else-if="60<=score&&score<90">中等</div>
<div v-else="score>=90">优秀</div>

<div v-show="true">display:block显示</div>
<div v-show="false">display:none隐藏</div>

3.8 列表渲染指令

注意:不推荐在同一元素上使用 v-if 和 v-for (详情请查看官网)

4 vue 的生命周期和生命周期函数

4.1 生命周期&生命周期函数

生命周期概念:生命周期是指一个组件从创建 > 运行 > 销毁的整个过程,强调的是一个时间段。

生命周期函数概念:是由 vue 框架提供的内置函数,会伴随着组件的生命周期,自动按次序执行。

(注意:生命周期强调的是时间段,生命周期函数强调的是时间点 。)

4.2 组件生命周期函数的分类

4.3 生命周期函数特点

<template>
  <div class="test-container">
    <h3 id="myh3">Test.vue 组件 --- {{ books.length }} 本图书</h3>
    <p id="pppp">message 的值是:{{ message }}</p>
    <button @click="message += '~'">修改 message 的值</button>
  </div>
</template>
 
<script>
export default {
  props: ['info'],
  data() {
    return {
      message: 'hello vue.js',
      books: []
    }
  },
  watch: {
    message(newVal) {
      console.log('监视到了 message 的变化:' + newVal)
    }
  },
  methods: {
  },
  
  beforeCreate() {
  // 创建阶段的第1个生命周期函数。在这个函数无法访问data、prors、methods
  //很少有
  },
  created() {
    // 组件只是在内存中被创建好,但还未被渲染到页面
    // 经常在它里面,调用 methods 中的方法,利用Ajax请求服务器的数据。并且,把请求到的数据,转存到 data 中,供 template 模板渲染的时候使用!
    //可以访问data、prors、methods
    this.initBookList()
  },
  beforeMount() {
      //只是在内存上编译好HTML
      //将在渲染组件时执行的操作
      //很少用
  },
  
  mounted() {
      //在此之前DOM还没被渲染,但此时在mounted()时DOM已经被渲染
      // 如果要操作当前组件的 DOM,最早只能在 mounted 阶段执行
      //组件创建阶段到此结束
  },
  beforeUpdate() {
   //已经根据拿到最新数据,还没完成组件DOM结构的渲染
  },
  // 当数据变化之后,为了能够操作到最新的 DOM 结构,必须把代码写到 updated 生命周期函数中
  updated() {
  //已经根据最新数据,完成组件DOM结构的渲染。可以被执行多次(因为数据会变化多次)
  //组件运行阶段到此结束
  },
  beforeDestroy() {
    this.message = 'aaa'
  },
  destroyed() {
  //组件销毁阶段到此结束
  }
}
</script>

5 keep-alive

5.1 keep-alive的基本使用

当组件第一次被创建,会执行created生命周期函数,也会执行activated生命周期函数。之后组件再被激活,只会触发activated而不会触发created。

使用:

<keep-alive>
    <组件名></组件名>
<keep-alive>

5.2 keep-alive属性

  • include包含的组件(可以为字符串,数组,以及正则表达式,只有名称匹配的组件会被缓存)。
  • exclude排除的组件(可以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存)。
  • max缓存组件的最大值(类型为字符或者数字,可以控制缓存组件的个数)。
// 只缓存组件name为a和b的组件
<keep-alive include="a,b"> 
  <component />
</keep-alive>

// 组件name为c的组件不缓存(可以保留它的状态或避免重新渲染)
<keep-alive exclude="c"> 
  <component />
</keep-alive>

// 如果同时使用include,exclude,那么exclude优先于include, 下面的例子只缓存a组件
<keep-alive include="a,b" exclude="b"> 
  <component />
</keep-alive>

// 如果缓存的组件超过了max设定的值5,那么将删除第一个缓存的组件
<keep-alive exclude="c" max="5"> 
  <component />
</keep-alive>

注意:若组件没有定义自己的name,则默认以注册组件时的名称作为匹配条件。如果定义了name,会以name作为匹配条件。

6 计算属性和侦听器

6.1 侦听器

6.1.1 作用

watch侦听器允许开发者监视数据的变化,从而针对数据的变化做特定的操作。

6.1.2 侦听器的格式

1、方法格式的侦听器(watch:{……}):

  • 缺点1:无法在刚进入页面的时候自动触发。
  • 缺点2:如果侦听的是一个对象,当对象里的属性值发生变化时,不会触发侦听器。

2、对象格式的侦听器 (watch(){……}):

  • 好处1:可以通过immediate选项让侦听器自动触发。

  • 好处2:可以通过deep选项,让侦听器深度监听对象中每个属性的变化。

如果要侦听对象里属性的变化,可以如以下操作:

6.2 计算属性

6.2.1 使用

1、定义计算属性:

new Vue({
    el:"#app",
    data:{ ... },
    methods:{ ... },
    watch:{ ... },
    computed:{
      计算属性名(){
        计算过程
        return 属性值
      }
    }
})

2、在页面上使用计算属性:

<p>{{计算属性名}}</p>

6.2.2 注意

1、computed 和 data同级,计算属性写在computed中;

2、写起来像方法,用起来像属性;

3、计算属性虽然称为属性,但其本质是一个函数;

4、虽然计算属性本质是一个函数,但是在页面中使用计算属性时,不要加();

5、一定要有返回值;

6、可以使用data中的已知值;

7、只要跟计算属性相关的数据发生了变化,计算属性就会重新计算,不相关的属性无论如何变化,都不会导致计算属性变化;

8、计算属性名不能和data中的数据重名(因为要使用data中的数据)。

【页面上使用】{{reversedMessage}}

【data中定义】msg:'New York'

【计算属性】computed:{
    reversedMsg (){
      return this.msg.split('').reverse().join('')
    }
}

6.3 Computed 和 Watch 的区别

1、computed计算属性:
作用:
(1)解决模板中放入过多的逻辑会让模板过重且难以维护的问题。例如两个数据的拼接或字体颜色的判断。

(2)它支持缓存,只有依赖的数据发生了变化,才会重新计算。例如模板中多次用到数据拼接可以用计算属性,只执行一次计算,除非数据发生变化。

(3)不支持异步,如果有异步操作,无法监听数据的变化。

(4)如果属性值是函数,默认使用get方法,函数的返回值就是属性的属性值。还有一个set方法,当数据变化时就会调用set方法。

(5)computed的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data声明过,或者父组件传递过来的props中的数据进行计算的。

2、watch侦听器:
作用:
(1)它不支持缓存,数据变化时,它就会触发相应的操作。

(2)支持异步监听。

(3)接受两个参数,第一个是最新的值,第二个是变化之前的值。

(4)监听data或者props传来的数据,发生变化时会触发相应操作。有两个参数:

    immediate:立即触发回调函数。

    deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep无法监听到数组和对象内部的变化。

3、总结:
(1)computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。

(2)**watch 侦听器 **: 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作。

4、computed与watch的使用场景:
computed:是多对一,多个数据影响一个。当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算。

watch:是一对多,一个数据发生变化,执行相应操作会影响多个数据。当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
————————————————
版权声明:本板块为CSDN博主「前端路啊」的原创文章,原文链接:原文网址

7 获取组件/元素——refs

7.1 ref的概念

ref用来辅助开发者在不依赖于jQuery的情况下,获取DOM元素或组件的引用。

<p ref="mytext">我会被refs获取到</p>
<button @click="refTest">获取mytext,改变其文本</button>
refTest(){
    console.log(this.$refs.mytext);
    this.$refs.mytext.innerHTML='我被获取到啦'
},

7.2 使用ref引用组件实例

【子组件 child.vue】

showTitle(){
  alert('aaa');
}

【父组件 parent.vue】

<child ref="A">流程环节配置</child>
<button @click="B">点我弹出</button>
B(){
    this.$refs.A.showTitle();
},

8 绑定Class

8.1 对象语法:

8.1.1 传给v-bind:class一个对象,以动态切换class

<template>
  <div>
    <!-- class和style -->
    <div v-bind:class="{ 'active': isActive, 'text-danger': hasError }">aa</div>
  </div>
</template>
<script>
export default {
    data() {
        return {
            isActive:true,
            hasError:false 
        }
    },  
}
</script>
<style>
.active{
    color:#e5c
}
.text-danger{
    color: rgb(16, 212, 65);
}
</style>

上面的语法表示 active 这个 class 存在与否将取决于数据isActive为true还是false。

8.1.2 在对象中传入更多字段来动态切换多个 class

此外,v-bind:class 指令也可以与普通的 class attribute 共存。当有如下模板:

<div
  class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>

data: {
  isActive: true,
  hasError: false
}

以上渲染的结果是:

当 isActive 或者 hasError 变化时,class 列表将相应地更新。例如,如果hasError=true,则渲染结果为:

8.1.3 绑定的数据对象不必内联定义在模板里

<div v-bind:class="classObject"></div>

data: {
  classObject: {
    active: true,
    'text-danger': false
  }
}

8.2 数组语法

8.2.1 把一个数组传给 v-bind:class,以应用一个 class 列表

<div v-bind:class="[class1,class2]"></div>
data: {
    class1:'active',
    class2:'box'
}

以上结果渲染为:

8.2.2 根据条件切换列表中的 class

在数组语法中也可以使用对象语法:

<div v-bind:class="[{ active: isActive }, errorClass]"></div>

这样写将始终添加 errorClass,但是只有在 isActive:true 时才添加 activeClass。

8.3 用在组件上

对于带数据绑定 class的组件也同样适用:

<my-component v-bind:class="{ active: isActive }"></my-component>

当 isActive 为 true 时,class="组件原来的样式 active"

8.4 总结

方式一:v-bind:class="{ '类名1': data1, '类名2': data2,…… }"
data(){
    return{
        data1:false/true,
        data2 : false/true
        ……
    }
}

方式二:v-bind:class="对象名"
data(){
    return{
    对象名:{
         "类名1":false/true,
         "类名2":true/true,
          ……
        }
    }
}

方式三:v-bind:class="[class1,class2,class3,……]"
data(){
    return{
        class1:'类名1',
        class2:'类名2',
        ……
    }
}

9 绑定内联样式

9.1 对象语法

**v-bind:style **的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:

<div v-bind:style="{color:textColor,fontSize:textSize+ 'px'}">aa</div>

data() {
    return {
        textColor:'#aa8',
        textSize:30
    }
},

直接绑定到一个样式对象通常更好,这会让模板更清晰:

<div v-bind:style="styleObject"></div>

data: {
  styleObject:{
        color:'#fff',
        fontSize:25+'px',
    }
}

9.2 数组语法

v-bind:style 的数组语法可以将多个样式对象应用到同一个元素上:

<div v-bind:style="[styleObject1,styleObject2]"></div>
data: {
  styleObject1: {
    color: 'red',
    fontSize:13+'px'
  },
  styleObject2: {
    width: 100+'px',
    height:130+'px'
  }
}

10 组件通讯

10.1 父传子用props

  • 第一步:引入子组件。
  • 第二步:在数据源中定义要传入子组件的数据parentMsg。
  • 第三步:在使用child组件时传入parentMsg。<child :自定义属性名="parentMsg"></child>。
  • 第四步:在子组件中,要 props:['自定义属性名']来接收传过来的参数。

【父组件】

<template>
  <div>
    <h2>parent</h2>
    <!--3、传入parentMsg-->
    <child :visible="visible"></child>
  </div>
</template>
<script>
//1、引入子组件
import child from './child.vue'
export default {
    data() {
        return {
            //2、定义要传入子组件的数据parentMsg
            visible:'true'
        }
    },
    components:{
        child
    }
}
</script>

【子组件】

<template>
  <div>
    {{visible}}
  </div>
</template>
<script>
export default {
    name:'child',
    //使用prors对象可以设置配置项,使用prors数组不可以。
    // props:{
    //     parentMsg:{
    //         type:String,
    //         default:'i am child'
    //     }
    // }
    props:['visible']//接收
}
</script>

10.2 子传父用$emit

emit使用方法:this.$emit(‘自定义事件名’,所需要传的值)

  • 第一步:首先在子组件中定义一个事件,并且使用emit发送给父组件,在示例中子组件使用的click事件触发发送自定义事件(sendmsg)。
  • 第二步:在父组件中需要定义方法(getmsg)接受自定义事件(sendmsg):
  • 第三步:在使用子组件时,<child @sendmsg="getmsg"></child>。

【子组件】发送值

<template>
  <div>
    <button @click="childmsg">点我试试</button>
  </div>
</template>
<script>
export default {
    name:'child',
    data() {
        return {
             msg:"This is the first word from child"
        }
    },
    methods:{
        //点击按钮则向父组件传自定义事件sendmsg,childmsg
        childmsg(){
            this.$emit('sendmsg',this.msg)
        }
    }
}
</script>

【父组件】接收值

<template>
  <div>
    <child @sendmsg="getmsg"></child>
  </div>
</template>
<script>
import child from './child.vue'
export default {
    data() {
        return {
        }
    },
    components:{
        child
    },
    methods:{
        getmsg(val){
            console.log(val)
        }
    }
}
</script>

总结:

  • 父传子【父中定义要传的值A,通过:B="A"传给子,子用props:["B"]接收,但props只读,要C:this.B,在子组件中就可以对C进行操作(可以理解为B只是C的初始值)】
  • 子传父【在子中的某个事件 this.$emit('传给父组件的事件E',要传的值G),父用@E="F"接收并使用,在F中可以如下定义:F(val){……},此处会把G作为实参传给val】

10.3 兄弟组件之间的数据共享

  • 第一步:在兄弟组件同目录下创建eventBus.js,然后创建vue实例:
import Vue from 'vue'
export default new Vue()
  • 第二步:在【兄弟组件A】中,引入eventBus.js > 定义数据msg > 编写方法用于发送msg:
import bus from './eventBus.js'
<button @click="sendMsg">
export default{
    data(){
        return{
            msg:'hello'        
        }    
    },
    methods:{
        sendMsg(){
            bus.$emit('share',this.msg);        
        }    
    }
}
  • 第三步:在【兄弟组件B】中,引入eventBus.js>定义数据newMsg>编写方法用于接收msg和赋值给newMsg:
import bus from './eventBus.js'
<button @click="sendMsg">
export default{
    data(){
        return{
            newMsg:[]        
        }    
    },
    created:{
          bus.$on('share',val=>{
              this.newMsg=val;          
          }) 
    }
}

11 插槽的使用

总体思想:父组件指定内容,子组件渲染内容。

11.1 插槽基本用法

【父组件】
<Left>
    <p>这是在 Left 组件声明的p标签</p>
</Left>

【子组件】
<div class="left-box">
    <span>Left 组件</span>
    <!-- 在 Left 组件内声明一个插槽区 -->
    <slot></slot>
</div>

11.2 v-slot: 将内容放在指定插槽

  • vue 官方规定:每一个 slot 插槽都要有一个 name 名称
  • 如果省略了 slot 的name 则有一个默认名称 default
  • 默认情况下,使用组件时提供的内容会被填充到 name 为 default 的插槽内

【将内容放在指定的插槽内】:

  • 使用 v-slot:xxx, 其中 xxx为插槽 name 值,只能放在 标签内
  • 使用 标签包裹
  • 是一个虚拟标签,只起到包裹性质的作用,不会被渲染为实质性的 html 元素
  • v-slot:xxx 可以简写为 #xxx
【父组件】
<Left>
    <template v-slot:mySlot>
        <p>这是在 Left 组件声明的p标签</p>
    </template>
</Left>

【子组件】
<div style="color:#33e;background:#ee2">
    <slot name="mySlot"></slot>
</div>

11.3 插槽后背(默认)内容

  • 当使用组件指定了插槽内容时,优先显示指定的内容
  • 当没有指定内容时,渲染 slot 标签内的默认内容

11.4 具名插槽

当需要将内容置入不同组件时,要用带有name属性的插槽:

<HeaderVue #header>我是来自header的插槽</HeaderVue>
<Main>
    <template v-slot:box1>我是来自child的插槽box1</template>
    <template v-slot:box2>我是来自child的插槽box2</template>
</Main>
  • 注意 v-slot 只能添加在**<template>**上
  • v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header

11.5 作用域插槽

条件:在封装组件时,为预留的 <slot> 提供属性对应的值
格式
子组件:<slot v-bind:username='username' name='box1'></slot>
父组件:<template v-slot:box1='username_prors'>
在封装组件时,为预留的 <slot> 提供属性对应的值,叫做作用域插槽。这些属性对应的值可以在父组件中访问到,默认为空对象。
【子组件中】

<div>
    <slot v-bind:user="user" name="box3"></slot>
    <slot v-bind:msg="hello world" name="box4"></slot>
</div>

data(){
    return:{
        user:{
              firstname:'lan',
              lastname:'chun'
            }    
    }
}

【父组件中】

<子组件名>
    <template v-slot:box3="slotProps1">
        {{slotProps1.user.firstname}}
        {{slotProps1.user.lastname}}
    </template>
    <template v-slot:box4="slotProps2">
        {{slotProps2.msg}}
    </template>
</子组件名>

11.6 独占默认插槽的缩写语法

条件:被提供的内容只有默认插槽时

格式:直接写在组件里

当被提供的内容只有默认插槽时,组件的标签可以被当作插槽的模板来使用,可以把 v-slot 直接用在组件上:

<子组件名 v-slot:default="slotProps">
  {{ slotProps.user.firstName }}
</子组件名>
  • 当被提供的内容只有默认插槽时,可以用 v-slot:default=" ",也可以直接用缩写v-slot=" "。
  • 默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
  • 只要出现多个插槽,请始终为所有的插槽使用完整的基于 的语法,如下:
<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>

  <template v-slot:other="otherSlotProps">
    ...
  </template>

</current-user>

11.7 动态插槽名

动态指令参数也可以用在 v-slot 上,来定义动态的插槽名:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

11.8 带有 slot attribute 的具名插槽

直接把 slot attribute 用在一个普通元素上:

<base-layout>
  <h1 slot="header">Here might be a page title</h1>

  <p>A paragraph for the main content.</p>
  <p>And another one.</p>

  <p slot="footer">Here's some contact info</p>
</base-layout>

12 路由的使用

url地址里,‘#’及以后的部分称为哈希地址,可以在控制台用location.hash打印哈希地址

12.1 前端路由的概念和原理

1)用户点击页面上的路由链接

2)导致url地址的Hash值变化

3)前端路由监听到Hash地址的变化

4)前端路由把当前Hash地址对应的组件渲染到浏览器中

12.2 配置

  • 第一步:安装vue-router包npm install vue-router@3.5.2 -S
  • 第二步:创建路由模块。创建router文件夹,在文件夹下建立index.js
//1、导入Vue和VueRouter的包,定义 (路由) 组件
import Vue from 'vue'
import VueRouter from 'vue-router'
import Prize from '@/components/Prize.vue'
import Home from "@/components/Main.vue";
import parent from '@/components/parent'

//2、调用Vue.use()函数,把VueRouter安装为vue插件
Vue.use(VueRouter);

//3、创建路由的实例对象
const router =new VueRouter({
    routes:[
        {
            path:'/home',
            component:Home
        },
        {
            path:'/prize',
            component:Prize,
        },
        {
            path:'/parent',
            component:parent
        }
    ]
})

//4、向外共享路由的实例
export default router

12.3 把router对象挂载到main.js上

(在new Vue中的router是“router:router”的简写,若import routerVue from 'xxxx',则应写为router:routerVue)

( "el"等价于$mount)

12.4 路由的基本使用

  • 第一步:在index.js中定制路由规则:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Prize from '@/components/Prize.vue'
import Home from "@/components/Main.vue";
import parent from '@/components/parent'

Vue.use(VueRouter);

const router =new VueRouter({
    routes:[
        {
            path:'/',
            redirect:'/home'
        },
        {
            path:'/home',
            component:Home
        },
        {
            path:'/prize',
            component:Prize,
        },
        {
            path:'/parent',
            component:parent
        }
    ]
})

export default router
  • 第二步:在页面中使用router-view:
<!--可以实现路由跳转,如下:-->
<div id="app">
  <h1>Hello App!</h1>
  <p>
    <!-- 使用 router-link 组件来导航. -->
    <!-- 通过传入 `to` 属性指定链接. -->
    <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
    <router-link to="/home">首页</router-link>
    <router-link to="/prize">奖品</router-link>
  </p>
  <!-- 路由出口 -->
  <!-- 路由匹配到的组件将渲染在这里 -->
  <router-view></router-view>
</div>
//也可以在methods中使用,如下:
methods: {
    goBack() {
      window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
    }
}
  • 在进行模块化导入的时候,如果给定的是文件夹,则默认导入这个文件夹下,名字叫做 index.js 的文件
  • 当对应的路由匹配成功,将自动设置 class 属性值 。查看API文档学习更多相关内容。 ​​​​​

12.5 路由重定向

12.6 嵌套路由

需求:在apptest.vue下路由到about,在about组件下路由到tab1、tab2

  • 第一步:路由配置index.js
{
      path: '/about',
      component: About,
      // redirect: '/about/tab1',
      children: [
        // 子路由规则
        // 默认子路由:如果 children 数组中,某个路由规则的 path 值为空字符串,则这条路由规则,叫做“默认子路由”
        { path: '', component: Tab1 },
        { path: 'tab2', component: Tab2 }
      ]
},
  • 第二步:在apptest.vue下路由到about
<router-link to="/about">关于</router-link>
<!-- 作用很单纯:占位符,给要显示的组件预留位置的 -->
<router-view></router-view>
  • 第三步:在about组件下路由到tab1、tab2
<router-link to="/about">关于</router-link>
<router-link to="/about/tab2">关于</router-link>

<router-view></router-view>

12.7 动态路由匹配

需求:有一个组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。

const User = {
  template: '<div>User</div>'
}

const router = new VueRouter({
  routes: [
    // 动态路径参数 以冒号开头
    { path: '/user/:id', component: User }
    //相当于/user?id=xxx
  ]
})

①可以通过const User = {template: '{{ $route.params.动态路径参数 }}'来查看当前用户的动态路径参数。

打印this时是对象

this.$route 是路由的“参数对象”

this.$router 是路由的“导航对象”

②可以为路由规则开启 props 传参,从而方便的拿到动态参数的值

【index.js】

{ path: '/movie/:mid', component: Movie, props: true },

【movie.vue】

props: ['mid'],
  • 在 hash 地址中, / 后面的参数项,叫做“路径参数”

  • 在路由“参数对象”中,需要使用 this.$route.params 来访问路径参数

  • 在 hash 地址中,? 后面的参数项,叫做“查询参数”

  • 在路由“参数对象”中,需要使用 this.$route.query 来访问查询参数

  • 在 this.$route 中,path 只是路径部分;fullPath 是完整的地址

【 例如】:

/movie/2?name=zs&age=20 是 fullPath 的值

/movie/2 是 path 的值

12.8 捕获所有路由或 404 Not found 路由

{
  // 会匹配所有路径
  path: '*'
}
{
  // 会匹配以 `/user-` 开头的任意路径
  path: '/user-*'
}

注:当使用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该放在最后。路由 { path: '*' } 通常用于客户端 404 错误。

12.9 编程式导航跳转

vue-router中的编程式导航API,常用的导航API有:

①this.$router.push('hash地址')

跳转到指定hash地址,并增加一条历史记录

②this.$router.replace('hash地址')

跳转到指定的hash地址,并替换掉当前的历史记录

③this.$router.go(数值n)

跳回第n条历史记录

<template>
  <div class="movie-container">
    <button @click="gotoLk">通过 push 跳转到“洛基”页面</button>
    <button @click="gotoLk2">通过 replace 跳转到“洛基”页面</button>
    
    <button @click="goback">后退</button>
    <!-- 在行内使用编程式导航跳转的时候,this 必须要省略,否则会报错! -->
    <button @click="$router.back()">back 后退</button>
    <button @click="$router.forward()">forward 前进</button>
  </div>
</template>

<script>
export default {
  name: 'Movie',
  methods: {
    gotoLk() {
      // 通过编程式导航 API,导航跳转到指定的页面
      this.$router.push('/movie/1')
    },
    gotoLk2() {
      this.$router.replace('/movie/1')
    },
    goback() {
      // go(-1) 表示后退一层
      // 如果后退的层数超过上限,则原地不动
      this.$router.go(-1)
    }
  }
}
</script>

12.10 路由守卫

路由守卫可以控制路由的访问权限。

12.10.1 全局前置守卫

【index.js】中

// 为 router 实例对象,声明全局前置导航守卫
// 只要发生了路由的跳转,必然会触发 beforeEach 指定的 function 回调函数
router.beforeEach(function(to, from, next) {
  // to 表示将要访问的路由的信息对象
  // from 表示将要离开的路由的信息对象
  // next() 函数表示放行的意思
  // 分析:
  // 1. 要拿到用户将要访问的 hash 地址
  // 2. 判断 hash 地址是否等于 /main。
  // 2.1 如果等于 /main,证明需要登录之后,才能访问成功
  // 2.2 如果不等于 /main,则不需要登录,直接放行  next()
  // 3. 如果访问的地址是 /main。则需要读取 localStorage 中的 token 值
  // 3.1 如果有 token,则放行
  // 3.2 如果没有 token,则强制跳转到 /login 登录页
  if (to.path === '/main') {
    // 要访问后台主页,需要判断是否有 token
    const token = localStorage.getItem('token')
    if (token) {
      next()//如果有token(登录)就放行
    } else {
      // 没有登录,强制跳转到登录页
      next('/login')
    }
  } else {
    next()
  }
})

12.10.2 next 函数的 3 种调用方式

12.10.3 关于path和fullpath

如:http://localhost:8080/index?page=1

fullPath:路由全地址,fullPath为/index?page=1

path:路径,不带参数,path为/index

12.11 路由传参

12.11.1 query传参

 1. 路由跳转并携带query参数,to的字符串写法 messageData是一个变量
 <router-link :to="`/home/news?id=001&message=${messageData}`" ></router-link>
 
 2. 路由跳转并携带query参数,to的对象
 <router-link :to="{
 path:"/home/news",
 query:{
            id:001,
            message:messageData
 }
 }" >
 </router-link>

获取参数:this.$route.query.id 、 this.$route.query.message

12.11.2 params传参

方式一:路由跳转并携带param参数,to的字符串写法 ,首先我们要在路由文件中定义我们要传递的参数

// 1. 路由跳转并携带params参数,to的字符串写法 messageData是一个变量
 <router-link :to="`/home/news/001/${messageData}`" ></router-link> //即{id:001,message:xxx}

跳转时直接斜杠/后面拼接参数

// 1. 路由跳转并携带params参数,to的字符串写法 messageData是一个变量
 <router-link :to="`/home/news/001/${messageData}`" ></router-link> //即{id:001,message:xxx}

方式二:路由跳转并携带params参数,to的对象写法,不需要在路由文件中定义参数

<router-link :to="{
    name:"HomeNews", //使用params传参时,必须使用name属性进行路由跳转,不能使用path配置项跳转
    params:{
        id:001,
        message:messageData
}
 }" ></router-link>

获取参数:this.$route.params.id 、 this.$route.params.message

12.11.3 路由props配置

传参配置: src/router/index.js

 {
    name:'HomeNews'
    path:'news/:id/:message',//二级路由,定义参数,表示第一个参数是id,第二个是message
    component:News,
    // 第一种写法:props值为对象,该对象中所有的key-value最终都会通过props传递给组件news
    // props:{a:1},
    // 第二种写法(只能params):props值为Boolean,为true时把路由收到的`params`参数通过props传递给组件news
    // props:true,
    // 第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传递给组件news
   props:function(route){
   return {
   id:route.query.id,
   message:route.query.message
   }
   },
 },

使用: New.vue

export default{
    prors:['id','message']
}

13 状态管理

13.1 状态管理简介

vuex是专为vue.js应用程序开发的状态管理模式。它采用集中存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex也集成刀vue的官方调试工具devtools extension,提供了诸如零配置的time-travel调试、状态快照导入导出等高级调试功能。

13.2 Vuex 的思想

当我们在页面上点击一个按钮,它会触发(dispatch)一个action, action 随后会执行(commit)一个mutation, mutation 立即会改变state, state 改变以后,我们的页面会state 获取数据,页面发生了变化。

13.3 核心状态管理

通俗理解是存储在store里的都是全局变量,可以通过方法提交更新,其他页面和组件也会同步更新,拿到最新的值。状态管理核心状态管理有5个核心,分别是state、getter、mutation、action以及module

  • state

驱动应用的数据源。state为单一状态树,可以使用一个对象包含全部的应用层级状态,state就是数据源。

  • getter

getter有点类似vue的计算属性computed,当我们需要对数据进行处理,那么我们就需要使用getter,getter会接收state作为第一个参数,而且getter的返回值会根据它的依赖被缓存起来,只有getter中的依赖值(state中的某个需要派生状态的值)发生改变的时候才会被重新计算。

const getters = {
    getUserState (state) {
        let data;
        if(state.userState==0){
            data='无效'
        }else if(state.userState==1){
            data = '1级'
        }else{
            data = '2级'       
        }
        return data;
    }
}

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

页面上使用这个getters:

计算数据状态:{{$store.getters.getUserState}}
  • mutation

更改store中state状态的唯一方法就是提交mutation,类似vue中的methods。每个mutation都有一个字符串类型的事件类型和一个回调函数,我们需要改变state的值就要在回调函数中改变。我们要执行这个回调函数,那么我们需要执行一个相应的调用方法:store.commit。

  • action

响应在 view 上的用户输入导致的状态变化。action可以提交mutation,在action中可以执行store.commit,而且action中可以有任何的异步操作。action处理异步操作,由于mutation都是同步事务,在 mutation 中混合异步调用会导致你的程序很难调试。action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。

 mutations: {
        addAge: (state, payload) => {
            state.informations.forEach(info => {
                info.age += payload;
            })
        }
 },
 actions: {
        addAge: (context, payload) => {
            setTimeout(function () {
                context.commit("addAge", payload);
            }, 2000);
        }
  }

在组件的methods中能够通过【this.$store.dispatch("addAge", 2);】分发action。

(或者使用mapActions辅助函数将组件的 methods 映射为 store.dispatch 调用)

methods: {
    addAge() {
      this.$store.dispatch("addAge", 2);
    }
},

注意:

1、所有 store 中 state 的变更,都放置在 store 自身的 action 中去管理。

2、Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到更新。

3、不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit)mutation。

13.4 在项目中使用

13.4.1 store一般有以下6个文件

13.4.2 vuex中 this.$store.dispatch() 与 this.$store.commit()方法的区别

这两个的区别是存取的方式不同,两个都是传值给vuex的mutation改变state.

  • this.$store.dispatch含有异步操作,可以向后台提交数据
【存储】this.$store.dispatch("action的方法名",value)
【取值】this.$store.getters.action的方法名
  • this.$store.commit同步操作
【存储】this.$store.commit("mutation的方法名",value)
【取值】this.$store.state.mutation的方法名

13.4.3 实例1

在app.vue里先import store from “./store” 并且在new vue实例的时候加上store,这样就可以全局调用了。

【src/store/index.js】引入Vuex文组件:

//第一步:使用import引入vue和vuex
import Vue from 'vue';
import Vuex from 'vuex';

//第二步:把vuex作为组件引入
Vue.use(Vuex);

//第三步:实例化vuex.store对象
//store变量是实例化一个vuex.store
export const store =new Vuex.Store({
//第四步:定义state
    //state专门用于保存共享的状态值
    state:{
        //保存登陆状态
          login:false  
    },

//第五步:编写方法改变state中的值
//专门书写方法,用于更新state中的值
    mutations:{
        doLogin(state){
            state.login=true;        
        },
         doLoginout(state){
             state.login=false;         
         }
    }
});

【 src/components/Header.vue】组件JS部分:

<script>
//使用vuex的mapState需要引入
import {mapState} form "vuex";

export default{
    name:"Header",
    //引入vuex>store>state中的值,必须在计算属性中书写
    computed:{
        //mapState辅助函数,可以快速引入store中的值
        ...mapState(["login"])    
    },
    methods:{
        loginout(){
            //调用store里登出的方法
            this.$store.commit(" doLoginout");        
        }    
    }
}
</script>

【src/components/Login.vue】组件JS部分 :

<script>
export default {
  name: "Login",
  data() {
    return {
    };
  },
  methods: {
    doLogin() {
             ……
            // 路由跳转指定页面
            this.$router.push({ path: "/" });

            //更新 vuex 的 state的值, 必须通过 mutations 提供的方法才可以
            //通过 commit('方法名') 就可以触发mutations 中的指定方法
            this.$store.commit("doLogin");
          } 
        });
     }
  }
};
</script>

使用$router.push转到首页url,并且调用store里的登录方法把共享登录状态变成true。

13.4.4 实例2

【声明】src/store/index.js:

import Vuex from 'vuex';
import Vue from 'vue';
……;
import test from '@/store/modules/test'

Vue.use(Vuex);
export default new Vuex.Store({
  modules: {
    ……,
    test
  },
});

【定义】src/store/models/test.js:

const state={
    username:'lanchun',
    userState:0
}
const mutations={
    SetUserName(state,name){
        state.username=name;
    },
    SetUserState(state,num){
        state.userState+=num;
    }
}

export default{
    state,
    mutations
}

【使用】src/views/page.vue:

<template>
    <div>
      <h1>{{$store.state.test.username}}</h1>
      <h1>{{$store.state.test.userState}}</h1>
      数据状态:
      <a-button @click="addState">状态+1</a-button>
    </div>
</template>

 methods: {
    addState(){
      console.log(this.$store.state.test);
      console.log(this.$store);
      this.$store.commit('SetUserName','张三');
      this.$store.commit('SetUserState',1);
    },
}

本文转载自: https://blog.csdn.net/qq_45605043/article/details/126846076
版权归原作者 离山港 所有, 如有侵权,请联系我们删除。

“Vue知识点汇总【持更】”的评论:

还没有评论