Vue框架

后面会持续更新...(第八次更新内容:路由)

目录

一、Vue简介

1、概述

2、Vue的开发模式

二、Vue入门

1、初识Vue框架

1.1、使用Vue输出“hello world”

1.2、热启动插件安装

1.3、vue devtools工具安装

2、Vue插值

3、不常用的指令

3.1、v-text 

3.2、v-html

3.3、v-cloak

3.4、v-once

4、常用的指令(重点)

4.1、v-bind

4.2、v-on   

    4.2.1、基本使用

    4.2.2、事件修饰符

    4.2.3、enter按键事件

4.3、v-model

    4.3.1、双向数据绑定

    4.3.2、基本使用

    4.3.3、修饰符

4.4、v-for

    4.4.1、数组循环

    4.4.2、对象循环

4.5、v-if  v-show

    4.5.1、基本使用

    4.5.2、理解重绘和回流(重要)

 三、Vue常用属性

3.1、自定义指令 directive(不常用)

3.1.1、全局指令

3.1.2、局部指令

3.2、计算属性  computed

3.3、监听器 watch

3.3.1、监听字符串

3.3.2、监听对象

3.4、过滤器  filter

3.5、分析Vue渲染页面的执行过程

3.6、生命周期(重要)

四、网络请求

4.1、fetch(了解即可)

4.2、axios

五、脚手架

5.1介绍

5.2、安装脚手架

5.3、创建项目

5.4、目录结构介绍

六、Vue的组件

6.1、什么是组件

6.2、组件的使用

6.3、tabs切换案例

6.4、从tabs切换案例体会生命周期

七、组件通信

7.1、父子通信

7.1.1、父传子

7.1.2、子传父

7.2、bus通信或事件总线通信(兄弟间通信)

7.3、ref通信

7.4、跨层级通信

7.5、插槽 

7.5.1、匿名函数

7.5.2、具名插槽

7.5.3、作用域插槽

7.6、$nextTick

八、动态组件

九、路由

9.1、路由的概念

9.2、前端路由的两种模式

9.3、Vue Router

9.4、声明式导航

9.4.1、不携带参数跳转

9.4.2、携带参数跳转

9.5、编程式导航 

9.5.1、编程式导航bug

9.5.2、编程式导航跳转到about页面

9.6、嵌套路由

9.7、动态路由

9.8、重定向和404路由

9.8.1、重定向

9.8.2、404路由

9.9、路由守卫  路由钩子

9.9.1、全局前置守卫

9.9.2、路由独享守卫

9.9.3、组件内守卫


一、Vue简介

官网:Vue.js (vuejs.org)

1、概述

Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

声明式渲染和组件系统是Vue的核心库所包含内容。

2、Vue的开发模式

M-V-VM

M(model):普通的javascript数据对象(其实就是一个对象,对象里放了数据)。

V(view):html内容,也就是前端展示页面。

VM(ViewModel):用于双向绑定数据与页面,也就是Vue的实例。

也就是说,在这种模式下,页面数据发生改变,相应的前端渲染页面也会发生改变。

二、Vue入门


1、初识Vue框架

我们还是在H5页面,进行操作,

1.1、使用Vue输出“hello world”

第一步:先有一个div容器,并且一个项目里有且只有一个div,我们后续所有的工作都要放在着一个div中。

第二步:引入vue框架

第三步:书写js代码

如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <!-- 2.引入vue框架js -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

    <title>Document</title>
</head>
<body>
    <!-- 1、div容器 -->
    <div id="app">
       <!-- 插值 -->
       {{ msg }}
    </div>
    
    <!-- 3、书写js代码 -->
    <script>
       //创建Vue实例化对象
       new Vue({
          el:'#app',//绑定节点
          data:{//定义变量
            msg:'hello world'
          }
       });
    </script>

</body>
</html>

1.2、热启动插件安装

第一步:在根目录下,初始化文件 npm init -y

第二步:全局安装 cnpm i -g browser-sync 

第三步:package.json 添加自定义命令  "start": "browser-sync start -s -f **/* --directory --watch"

第四步:启动,http://localhost:3000 访问即可,或者,在对应文件目录下,输入命令npm run start

即可进入如下页面:

1.3、vue devtools工具安装

我这里提供一个精简版的安装

第一步:

 第二步 

第三步:选择该文件,这个文件资源已上传,有需要可以下载 

2、Vue插值

插值表达式的写法支持使用:变量·、部分表达式、链接且、链接或、函数

{{ }}括起来的区域,就是一个JS语法区域,但是只可以写部分的JS语法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

    <title>Document</title>
</head>
<body>
    <div id="app">
       <!-- 变量 -->
        {{ msg }}<br>

        <!-- 部分表达式 -->
        {{ b ? '王昭没有君啊' : '王昭君' }}<br>
        {{ 1+1}}<br>

        <!-- 链接且 -->
        {{ b && '王昭没有君啊' }}<br>

        <!-- 链接或 -->
        {{ b || '王昭没有君啊' }}<br>

        <!-- 函数 -->
        {{ getData() }}
    </div>
 
    <script>
       new Vue({
          el:'#app',
          data:{
            msg:'hello Vue',
            b:true
          },
          methods:{
            getData(){
               console.log('王昭君');
               return '王昭没有君啊'
            }
          }
       });
    </script>

</body>
</html>

 3、不常用的指令

3.1、v-text 

相当于原生JS中的innerText,和插值差不多,没有闪屏。

3.2、v-html

相当于原生JS中的innerHTML,会解析标签。

在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用 v-html永不用在用户提交的内容上。

3.3、v-cloak

解决闪烁问题。

这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

3.4、v-once

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

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>

    <title>Document</title>
    <style>
       [v-cloak]{
           display: none;
       }
    </style>
    
</head>
<body>
    <div id="app">
       <!-- v-text  -->
       <p v-text="msg"></p>

       <!-- v-html  -->
       <p v-html="mag"></p>

       <!-- v-cloak  -->
       <p v-cloak>{{ msg }}</p>

       <!--  v-once -->
       <p v-once>{{ msg }}</p>

    </div>

    <script>

       var vb = new Vue({
          el:'#app',
          data:{
            msg:'hello Vue',
            mag:'<i> 王昭没有君啊 <i>'
          }
       });
       
    </script>

</body>
</html>

4、常用的指令(重点)

4.1、v-bind

作用:动态设置标签属性

关键代码,如下所示:

<body>
    <div id="app">

       <!-- 普通设置属性 -->
       <p class="my-color">王昭没有君啊</p>

       <!-- 动态属性  在html内置的属性值中使用变量 -->
       <p v-bind:class="myColor">王昭没有君啊</p>
       <!-- 简写的动态属性 -->
       <p :class="myColor">王昭没有君啊</p>

       <!-- 动态属性 后面是对象的形式,对象值控制对象的属性 -->
       <p :class="{'name':myName}">王昭没有君啊</p>
       <!-- 动态属性 后面括号可以放多个变量 -->
       <p :class="[msg,myColor]">王昭没有君啊</p>
       
       <p style="font-size:26px">王昭没有君啊</p>
       <p :style="myStyle">王昭没有君啊</p>
       <p :style="{fontSize:'12px',color:'blue'}">王昭没有君啊</p>

    </div>

    <script>
        new Vue({
           el:'#app',
           data:{
             myColor:'my-blue',
             myName:true,     //true设置属性值name,false无属性值
             msg:'hello-Vue',
             myStyle:{
                 fontSize:'26px',
                 color:'pink'
             }
           }
        });
    </script>
 
</body>


 

4.2、v-on   

作用:绑定事件

4.2.1、基本使用

常规写法:

<button v-on:click="change()">点击</button>

简写:

<button @click="change()">点击</button>

事件对象event 

点击事件不传参

<button @click="clickevent1">不传参</button>

  点击事件传参

<button @click="clickevent2(123,$event)">传参</button>

案例:

html省略,关键代码:

<body>
    <div id="app">
        <p>{{ msg }}</p>
        <!-- 调用自己定义的函数,不允许使用内置函数 -->
        <button @click="change()">点击</button>
        <!-- 不传参事件函数调用,可以直接写函数名,不用加() -->
        <button @click="clickevent1">不传参</button>
        <!-- 传参事件函数调用,事件对象必须作为参数进行传递,事件对象的名称必须是$event -->
        <button @click="clickevent2(123,$event)">传参</button>
    </div>
 
    <script>
         new Vue({
            el:'#app',
            data:{
                msg:'hello',
                
            },
            methods:{
                change(){
                    this.msg = 'hello world';
                },
                clickevent1(){
                    //vue的事件对象就是原生的事件对象
                    console.log('事件对象',event);
                },
                clickevent2(val,$event){
                    console.log(val);
                    //vue的事件对象就是原生的事件对象
                    console.log('事件对象',event);
                }

            }
        })
       
    </script>
 </body>

 4.2.2、事件修饰符

.self  .stop  .once  .prevent

@.click.self  触发事件源时才会触发,可用于阻止事件冒泡
@click.stop 阻止事件冒泡
@click.once 事件只会执行一次
@click.prevent  阻止默认行为

案例:

html省略,关键代码:

<body>
    <div id="app">
        <div @click.self="clickparent()" style="width: 200px;height: 200px;background:pink">
            <div @click.stop="clickchlid()" style="width: 100px;height: 100px;background:red"></div>
        </div>
        <button @click.once="clickfun()">点击</button>
        <a @click.prevent="clicka()" href="http://baidu.com">百度一下</a>
    </div>
 
    <script>
         new Vue({
            el:'#app',
            data:{
               msg:'hello',
               
            },
            methods:{
               change(){
                   this.msg = 'hello world';
               },
               clickparent(){
                   console.log('父盒子');
               },
               clickchlid(){
                   console.log('子盒子');
               },
               clickfun(){
                   console.log('演示once');
               },
               clicka(){
                   console.log('点击a标签');
               }

            }
        })
       
    </script>
 </body>

4.2.3、enter按键事件

.enter  回车键的时候调用

<input type="text" @keyup.enter="enterFun">

案例:

html省略,关键代码:

<body>
   <div id="app">
       <!-- 只有在 `key` 是回车键的时候才能调用 -->
       <input type="text" @keyup.enter="enterFun">
   </div>

   <script>
       new Vue({
          el:'#app',
          methods:{
            enterFun(){
               console.log('回车键');
            }
          }
       })
       
   </script>
</body>

4.3、v-model

作用:双向数据绑定,用于表单(input、textarea、select、checkbox、radio)

4.3.1、双向数据绑定

4.3.2、基本使用

input、textarea、select ,v-model绑定的是value

checkbox、radio,v-model绑定的是checked属性

<input type="text" v-model="msg">

案例:

html省略,关键代码:

<body>
    <div id="app">
        <!-- input -->
        <p> {{msg}} </p>
        <input type="text" v-model="msg">
        <!-- textarea -->
        <p> {{text}} </p>
        <textarea v-model="text"></textarea>
        <!-- select -->
        <p> {{selectvalue}} </p>
        <select v-model="selectvalue">
            <option value="北京">北京</option>
            <option value="上海">上海</option>
            <option value="广州">广州</option>
            <option value="深圳">深圳</option>
        </select>
        <!-- checkbox -->
        <p> {{checkvalue}} </p>
        王者荣耀<input type="checkbox" v-model="checkvalue" value="王者荣耀">
        英雄联盟<input type="checkbox" v-model="checkvalue" value="英雄联盟">
        和平精英<input type="checkbox" v-model="checkvalue" value="和平精英">
        原神<input type="checkbox" v-model="checkvalue" value="原神">
        <!-- radio -->
        <p> {{radiovalue}} </p>
        <label for="北方">北方</label>
        <input id="北方" type="radio" v-model="radiovalue" value="北方">
        <label for="南方">南方</label>
        <input id="南方" type="radio" v-model="radiovalue" value="南方">
    </div>

    <script>
        new Vue({
            el:'#app',
            data:{
                msg:'hello',
                text:'文本内容',
                selectvalue:'北京',
                checkvalue:[],
                radiovalue:' '
            }
        })
    </script>
</body>


 4.3.3、修饰符

.lazy  .number  .trim

<input type="text" v-model.lazy="msg"> 懒加载 把oninput事件变成onblur事件
<input type="text" v-model.number="msg"> 把当前变量转换成number类型,v-model绑定的变量是自动转换字符串的
<input type="text" v-model.trim="msg"> 去除前后的空白

案例:

html省略,关键代码:

<body>
    <div id="app">
        <p> {{msg}} </p>
        <input type="text" v-model.lazy="msg">
        <p> {{msg2}} </p>
        <input type="text" v-model.number="msg2">
        <p> {{msg3}} </p>
        <input type="text" v-model.trim="msg3">
    </div>

    <script>
       var vm = new Vue({
            el:'#app',
            data:{
                msg:'hello',
                msg2:123,
                msg3:''
            }
        })
    </script>
</body>

  

 

4.4、v-for

作用:循环遍历

注意:v-for循环要加key,提高性能,key取唯一值,有id取id,没有可以用index下标。

4.4.1、数组循环

<li v-for="(item,index) in arr" :key="index"></li>
<li v-for="item in arr" :key="item.id"></li>

4.4.2、对象循环

<li v-for="(val,key,index) in obj" :key="index"></li>

案例:

html省略,关键代码:

<body>
    <div id="app">
        <ul>
            <li v-for="(item,index) in arr">
                下标:{{index}}, 元素:{{item}}
            </li>
        </ul>

        <ul>
            <li v-for="item in arr1">
                id:{{item.id}},姓名:{{item.name}},年龄:{{item.age}}
            </li>
        </ul>

        <ul>
            <li v-for="(val,key,index) in obj">
                下标:{{index}},属性:{{key}},属性值:{{val}}
            </li>
        </ul>
    </div>

    <script>
        var vm = new Vue({
            el: '#app',
            data: {
                arr: [1, 2, 3, 4, 5],
                arr1: [
                    { id: 1, name: '王昭', age: '25' },
                    { id: 2, name: '王昭君', age: '28' },
                    { id: 3, name: '王昭没有君啊', age: '23' }
                ],
                obj:{
                    name:'王昭没有君啊',
                    age:23,
                    address:'北京'
                }
            }
        })
    </script>
</body>

 4.5、v-if  v-show

作用:根据表达式的布尔值(true/false)进行判断是否渲染显示该元素。

4.5.1、基本使用

v-if  v-else-if  v-else,v-if可以单独使用,也可以配合v-else一起使用,也可以配合v-else-if和v-else一起使用。

v-show控制元素是不是要显示,元素是肯定要渲染的,false是通过行内样式进行隐藏的,但是节点是存在的。

<p v-if="num === 0">北京</p>
<p v-show="num === 0">上海</p> 
true渲染,false不渲染

案例:

html省略,关键代码:

<body>
    <div id="app">
        <!-- v-if -->
        <p v-if="num === 0">北京</p>
        <p v-else-if="num === 1">上海</p>
        <p v-else-if="num === 2">广州</p>
        <p v-else>深圳</p>
        <!-- v-show -->
        <p v-show="num1 === 0">北京</p>
        <p v-show="num1 === 1">上海</p>
        <p v-show="num1 === 2">广州</p>
        <p v-show="num1 === 3">深圳</p>
    </div>

    <script>
        var vm = new Vue({
            el: '#app',
            data: {
               num:2,
               num1:3,
            }
        })
    </script>
</body>

4.5.2、理解重绘和回流(重要)

如果说我们不涉及到节点的删除和渲染的话,使用v-if。

如果说频繁切换,我们就要用v-show。

针对上面代码,如果我让num=1,num1=2,v-if会先销毁<p>广州</p>,然后重新生成<p>上海</p>,然后重新渲染生成页面,此时v-if会造成页面的重绘和回流;v-show会给<p>深圳</p>添加样式:<p style="display:none">深圳</p>,给<p style="display:none">广州</p>取消样式:<p>广州</p>,v-show,只会造成页面的重绘。

从字面上理解,重绘,重新绘画,画到一半颜色发现不对,然后重新上色。

一个页面加载到完成,会同时加载html,css,js,比如说我们增加或者删除DOM节点,此时html发生变化,就如同绘画一样,画到一半,我发现我不想要画这个房子了,我想画个鸟,此时我就要先把房子擦掉,然后重新绘画,这就会极大的浪费性能,页面加载也是这样,加载到一半发现html发生变化,然后从头再加载html,css,js,重新渲染生成页面。

页面优化也是考核一个开发者能力的关键之一,所以在平时的工作中,我们尽量减少回流。所以我们就有必要要理解重绘和回流。

修改一个元素的宽高,增删DOM节点,页面布局发生变化,html结构发生变化,那么肯定就会先销毁之前的html,然后重新构建新的html,新的html构建完成,随之对页面进行再次渲染,这个过程就是回流。

给一个元素修改颜色,这样的改动并不会影响页面布局的,html结构也不会发生变化,但是颜色变了,就需要重新渲染页面,这就是重绘

回流的代价会远大于重绘,会极大的浪费性能,回流必然会造成重绘,但重绘不一定会造成回流。

 三、Vue常用属性


3.1、自定义指令 directive(不常用)

我们开发者自己定义的指令。

自定义指令分为 全局指令(项目的任何地方都能使用)局部指令(在当前组件能用)

自定义指令常用的钩子函数:

inserted:被绑定元素插入父节点时调用(最常用的,重点记这个;以下的几个几乎不怎么用,若是以后需要用到,只用翻回这一块笔记,看一看即可)

bind:在指令第一次绑定到元素时调用。

update:数据更新时调用。

componentUpdated:指定元素及子节点更新完成后会触发。

unbind:取消绑定后触发。

3.1.1、全局指令

没有参数:

Vue.directive('指令名',{
    钩子函数名: (el)=>{

    }
})

传参:

Vue.directive('指令名',{
   钩子函数名: (el,val)=>{

   }
})

案例:

html省略,代码如下:

<body>

    <div id="app">
        <p v-color> 王昭没有君啊 </p>
        <p v-scolor="'blue'"> 王昭没有君啊 </p>
    </div>
    <script>
        //没有参数
        Vue.directive('color',{ //v-color
            inserted: (el)=>{
                el.style.color = 'pink';
            }
        })
        //传参
        Vue.directive('scolor',{ //v-scolor
            inserted: (el,val)=>{
                el.style.color = val.value;
            }
        })
        var vm = new Vue({
            el:'#app',
            data:{

            }
        })
      
    </script>
    
</body>

3.1.2、局部指令

directives:{
   指令名: {
     钩子函数名:(el,val)=>{

     }
   }
}

案例:

html省略,代码如下:

<body>

    <div id="app">
        <p v-color> 王昭没有君啊 </p>
        <p v-bcolor="'blue'"> 王昭没有君啊 </p>
        
    </div>
    <script>
        var vm = new Vue({
            el:'#app',
            data:{

            },
            directives:{
                "color":{
                    inserted:(el)=>{
                        el.style.color = 'pink';
                    }
                },
                "bcolor":{
                    inserted:(el,val)=>{
                        el.style.color = val.value;
                    }
                }
            }
        })
      
    </script>
    
</body>

3.2、计算属性  computed

计算属性定义在Vue对象中,通关关键词computed属性对象中定义一个个函数,并返回一个值,使用计算属性时和data里的数据使用方式一致,甚至我们可以把其当作是一个变量使用。

核心点:

1、computed首次会执行,我们做二次封装,派生数据缓存,依赖的变量不发生变化,当前函数会缓存,第二次调用时,不会再执行,直接返回结果值。

2、data里面的变量发生改变,当前页面会刷新,也就是,变量发生改变当前函数会重新计算。

3、计算属性必须要有return。

案例:

html省略,代码如下:

<body>
  
    <div id="app">
        <p>{{num}}</p>
        <!-- 多次调用,只要data里的num不发生变化,它会直接使用computed首次执行的返回值 -->
        <p>{{computedNum}}</p><!-- 首次会执行当前函数 -->
        <p>{{computedNum}}</p><!-- 第二次调用,直接使用computed首次执行的返回值 -->
        <!-- 多次调用methods中的方法时,每次都会重新调用 -->
        <p>{{methodsNum()}}</p>
        <p>{{methodsNum()}}</p>
    </div>

    <script>
        new Vue({
            el:'#app',
            data:{
                num:10,//num值发生变化,当前页面会刷新
            },
            computed:{
                computedNum(){//我们再次封装某个数据 派生数据
                    console.log('computed执行了一次');
                    return this.num *2;
                }
            },
            methods:{
                methodsNum(){//普通函数,每次都会执行
                    console.log('methods执行了一次');
                    return this.num *3;
                }
            }
        })
    </script>
    
</body>

 3.3、监听器 watch

作用:某个数据变量发生改变,我们监听他的改变的同步事件。

3.3.1、监听字符串

语法:

watch:{
     msg:{
        'handler':'函数名称', 
        'immediate':true //自动执行一次watch,没有改变之后的值
     }
}

案例:

<body>
    
    <div id="app">
        <p>{{msg}}</p>
        <button @click="msg='hello world'">改变</button>
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{
                msg:'hello'
            },
            watch:{
                //监听msg是否发生改变
                msg:{
                    //固定写法
                    'handler':'changemsg',//函数名称
                    'immediate':true //自动执行一次watch,没有改变之后的值
                }
            },
            methods:{
                changemsg(newval,oldval){
                    console.log(newval);//改变之后的值
                    console.log(oldval);//改变之前的值
                }
            }
        })
    </script>
    
</body>

 3.3.2、监听对象

语法:

watch:{
     obj:{
        'handler':'函数名称',
        'immediate':true, //自动执行一次watch,没有改变之后的值
        'deep':true //深度监听,不加这行代码就是浅监听
     } 
}

案例:

当需要监听一个对象的改变时,普通的watch方法无法监听到对象内部属性的改变,此时就需要deep属性对对象进行深度监听。

<body>
    
    <div id="app">
        <p>{{obj.name}}</p>
        <p>{{obj.abc.age}}</p>
        <button @click="obj.name='王昭君',obj.abc.age=25">改变</button>
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{
                msg:'hello',
                obj:{
                    name:'王昭没有君啊',
                    abc:{
                        age:23,
                    }
                }
            },
            watch:{
                //监听obj是否发生变化
                obj:{
                    //固定写法
                    'handler':'changeobj',//函数名称
                    'immediate':true, //自动执行一次watch,没有改变之后的值
                    'deep':true
                }
            },
            methods:{
                changeobj(newval,oldval){
                    console.log(newval);//改变之后的值
                    console.log(oldval);//改变之前的值
                }
            }
        })
    </script>
    
</body>

 3.4、过滤器  filter

作用:对某个数据进行过滤,格式化数据,比如大小写切换,中英文切换,日期格式转化为指定的格式。

过滤器可以定义为全局过滤器和局部过滤器。

过滤器的本质就是一个方法,使用过滤器实际上就相当于方法调用。

这玩意在vue3中已经废弃了,在vue3中解决办法是通过methods来替代。

语法:

全局过滤器

Vue.filter('过滤器名称',(val)=>{
   return val进行处理
})

局部过滤器

filters:{
   过滤器名:(val)=>{
       return val进行处理
   }
}

案例1(大小写切换):

<body>
    
    <div id="app">
        <!-- 过滤器使用 -->
        <p>{{msg | toUpper}}</p>
        <p>{{msg2 | toLower}}</p>
    </div>
    <script>
        //全局过滤器
        Vue.filter('toLower',(val)=>{//大写转化为小写
            return val.toLowerCase();
        });
        new Vue({
            el:'#app',
            data:{
                msg:'hello',
                msg2:'HELLO WORLD'
            },
            //局部过滤器
            filters:{
                toUpper:(val)=>{//小写切换为大写
                    return val.toUpperCase();
                }
            }
        })
    </script>
    
</body>

 案例2(日期格式转化为指定的格式):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <!-- 引入vue框架 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <!-- 引入moment 时间格式插件 -->
    <script src="https://momentjs.bootcss.com/downloads/moment.min.js"></script>
    <title>Document</title>
</head>
<body>
    <div id="app">
        {{msg}}<br>
        {{ msg | dataCase }}
    </div>
    <script>
        new Vue({
            el:'#app',
            data:{
                msg:new Date()
            },
            filters:{
                'dataCase':(val)=>{
                    return moment(val).format('YYYY-MM-DD');
                }
            }
        })
    </script>
    
</body>
</html>

3.5、分析Vue渲染页面的执行过程

<body>
    <div id="app">
       <p>{{message}}</p>
    </div>

    <script>
        new Vue({
          el:'#app',
          data:{
            message:'hello'
          }
        })
    </script>
</body>

以上述代码为例,我们来分析Vue渲染页面的执行过程:

1、打开页面,正常显示你所以书写的文件。

  • 此时还没有Vue介入到你的页面内。
  • 你本身写的是什么,就渲染什么,页面上可以看到 =>   {{message}}。


2、创建一个Vue实例。

  • 此时还没有渲染页面,只是刚刚把实例创建出来。

3、按照你的配置项去捕获内容。

  • 按照el配置的选择器,去获取一个页面节点。
  • 只能拿到<div id="app"> ... </div>

4、按照自己的虚拟DOM进行节点的准备。

  • 读取el元素内的所有内容,为了查看一下将来应该渲染成一个什么样的内容。
  • 拿到了将来应该渲染成这个样子 =>  <p>{{msg}}</p>

5、生成虚拟DOM

  • 按照本身应该有的结构计算
  • 设计虚拟DOM的时候,会把你书写Vue语法需要渲染的内容进行解析
DOMS = {
   tag: 'p',
   inner: 'hello',
   children: []
}

6、把渲染好的虚拟DOM响应在页面上

  • 按照虚拟DOM准备好的结构
  • 生成一段真实的DOM结构
  • 渲染在页面上
  • 也没上看到的才是被Vue渲染好的内容  =>  hello

上述代码渲染页面是从 {{msg}} 到 hello ,实际上我们能看到的只有第一步和第六步,剩下的都是在内存中进行的,所以,在我们快速刷新页面时,可以看到如下闪烁问题。



 解决闪烁问题,需要用到一个指令,v-cloak。

这个指令的作用:就是当前节点全部渲染完毕以后,给这个节点设置一个 display: block;

为了解决闪烁问题,需要在一开始的时候,手动用CSS样式给这个标签设置一个display: none;要注意的是选择器的权重,因为Vue在最后设置的时候,使用的是属性选择器,那么我们也需要使用属性选择器来设置。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <!-- 引入vue框架 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <title>Document</title>
    <style>
       [v-cloak]{
          display: none;
       }
    </style>
</head>
<body>
    <div id="app" v-cloak>
       <p>{{message}}</p>
    </div>

    <script>
        new Vue({
          el:'#app',
          data:{
            message:'hello'
          }
        })
    </script>
</body>
</html>

3.6、生命周期(重要)

Vue的生命周期:从vue实例产生开始到vue实例被销毁这段时间所经历的过程。

Vue 的生命周期钩子函数:在一个 Vue 完整阶段出现的各个时期我们可以注册的函数。

所有的生命周期函数都是和 data 平级的(也属于Vue实例的配置项之一)

八个生命周期钩子(CMUD)

1、beforeCreate

  • 创建Vue实例之前
  • 执行完毕这个钩子函数,就会生成实例

关键代码:

<body>
    <div id="app" v-cloak>
       <p>{{msg}}</p>
    </div>

    <script>
        new Vue({
          el:'#app',
          data:{
            msg:'hello world'
          },
          beforeCreate(){
              console.group('创建实例之前');
              //查看 Vue 内准备好的数据
              console.log('数据:',this.$data);
              // 查看 Vue 捕获到的页面上的内容
              console.log('结构:',this.$el);
              console.groupEnd('');
          }
        })
    </script>
</body>

 2、created

  • 创建Vue实例之后
  • 执行完毕这个钩子函数,才会捕获页面上的内容,准备渲染页面

关键代码:

created(){
     console.group('创建实例之后');
     //查看 Vue 内准备好的数据
     console.log('数据:',this.$data);
     // 查看 Vue 捕获到的页面上的内容
     console.log('结构:',this.$el);
     console.groupEnd();        
}

 3、beforeMount

  • 挂载数据之前
  • 拿到页面结构,准备渲染到页面上之前
  • 执行完毕这个钩子函数,就会把准备好的数据渲染到页面上

关键代码:

beforeMount(){
     console.group('挂载数据之前');
     //查看 Vue 内准备好的数据
     console.log('数据:',this.$data);
     // 查看 Vue 捕获到的页面上的内容
     console.log('结构:',this.$el);
     console.groupEnd();
}

 4、mounted

  • 挂载数据之后
  • 把data中的数据已经渲染到该渲染的标签上了

关键代码:

mounted(){
    console.group('挂载数据之后');
    //查看 Vue 内准备好的数据
    console.log('数据:',this.$data);
    // 查看 Vue 捕获到的页面上的内容
    console.log('结构:',this.$el);
    console.groupEnd();
}

 5、beforeUpdate

  • 更新数据之前
  • 当该实例的内容被更新的时候,会在更新之前执行这个钩子函数

现在div中添加

<div id="app" v-cloak>
    <p>{{msg}}</p>
    <input type="text" v-model="msg">
</div>

关键代码:

beforeUpdate(){
     console.group('更新数据之前');
     //查看 Vue 内准备好的数据
     console.log('数据:',this.$data);
     // 查看 Vue 捕获到的页面上的内容
     console.log('结构:',this.$el.innerHTML);
     console.groupEnd();
}

 6、updated

  • 更新数据之后
  • 当实例内容被更新后,渲染完毕页面会执行这个钩子函数

关键如下:

updated(){
    console.group('更新数据之后');
    //查看 Vue 内准备好的数据
    console.log('数据:',this.$data);
    // 查看 Vue 捕获到的页面上的内容
    console.log('结构:',this.$el.innerHTML);
    console.groupEnd(); 
} 

 7、beforeDestroy

  • 销毁实例之前
  • 当你准备销毁当前实例了
  • 执行完毕这个钩子函数,才会销毁当前实例

8、destroyed

  • 销毁实例之后
  • 当你销毁完毕这个实例了,那么就会执行一遍这个构子函数,然后整个Vue生命周期结束
  • 再也没有Vue实例了

最后的两个钩子在项目里很少用,包括前面几个钩子,项目里也只用一两个,在特殊的时期做一些特殊的事。

这两个钩子我们一般是用于操作BOM,因为Vue是负责DOM渲染,所有和BOM相关的,Vue管不住,比如说,页面开了个定时器,打开定时器,当Vue实例销毁了,但是定时器还是跑着,因为定时器不属于Vue实例的东西,是BOM中的东西,所以为了性能的节约,我们会把定时器关掉。

以下就是完整的生命周期钩子代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <!-- 引入vue框架 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <div id="app" v-cloak>
       <p>{{msg}}</p>
       <input type="text" v-model="msg">
    </div>

    <script>
        //查看 Vue 内准备好的数据
              // 查看 Vue 捕获到的页面上的内容
        new Vue({
          el:'#app',
          data:{
            msg:'hello world'
          },
          beforeCreate(){
              console.group('创建实例之前');
              //查看 Vue 内准备好的数据
              console.log('数据:',this.$data);
              // 查看 Vue 捕获到的页面上的内容
              console.log('结构:',this.$el);
              console.groupEnd('');
          },
          created(){
              console.group('创建实例之后');
              //查看 Vue 内准备好的数据
              console.log('数据:',this.$data);
              // 查看 Vue 捕获到的页面上的内容
              console.log('结构:',this.$el);
              console.groupEnd();
          },
          beforeMount(){
              console.group('挂载数据之前');
              //查看 Vue 内准备好的数据
              console.log('数据:',this.$data);
              // 查看 Vue 捕获到的页面上的内容
              console.log('结构:',this.$el);
              console.groupEnd();
          },
          mounted(){
            console.group('挂载数据之后');
              //查看 Vue 内准备好的数据
              console.log('数据:',this.$data);
              // 查看 Vue 捕获到的页面上的内容
              console.log('结构:',this.$el);
              console.groupEnd();
          },
          beforeUpdate(){
            console.group('更新数据之前');
              //查看 Vue 内准备好的数据
              console.log('数据:',this.$data);
              // 查看 Vue 捕获到的页面上的内容
              console.log('结构:',this.$el.innerHTML);
              console.groupEnd();
          },
          updated(){
            console.group('更新数据之后');
              //查看 Vue 内准备好的数据
              console.log('数据:',this.$data);
              // 查看 Vue 捕获到的页面上的内容
              console.log('结构:',this.$el.innerHTML);
              console.groupEnd(); 
          },
          beforeDestroy(){},
          destroyed(){} 
        })
    </script>
</body>
</html>

在使用钩子函数的原则:

  • 当多个钩子可以做同一个事情的时候,越早越好
  • 当需要使用多个钩子函数的时候,越少越好

发送Ajax请求在mounted还是在created里面,为什么?

  • created代码会执行多次,mounted只执行一次。

销毁自定义事件,计时器,自定义dom事件在那个生命周期中写?

  • beforeDestroy 这个生命周期

深度面试题

  • vue渲染过程:

1、vue通过object.defineproperty把data数据变成set get监听形式。

2、vue通过render函数,传入数据,输出vdom。

3、vue通过调用patch函数,输入vdom,直接渲染挂载到dom上。

  • vue更新过程:

1、vue触发set函数,触发更新流程。

2、vue通过调用render函数,传入数据,重新输出新的vdom。

3、vue通过pach函数,输入旧的vdom,新的vdom进行diff比较(同行比较,少了就创建,多了就删除),vdom挂载渲染到容器上。

  • 从url输入到浏览器显示页面,缓存的理解。

1、判断是否名字强缓存,是:直接加载本地资源,否:下一步。

2、判断协商缓存,需要单独发请求跟后台协商是否可以使用本地资源,是:加载本地,否:下一步。

3、当前资源从后台服务器拉取。

强缓存不过服务器,协商缓存需要过服务器,协商缓存返回的状态码是304.两类缓存机制可以同时存在,强缓存的优先级高于协商缓存。当执行强缓存时,如若缓存命中,则直接使用缓存数据库中的数据,不再进行缓存协商。

四、网络请求


4.1、fetch(了解即可)

语法1:

//默认是get形式发送请求
fetch(url).then(res => res.json()).then(res => {
   console.log(res)
})

语法2:

fetch(url,{
    method:'POST',
    body:"username=zhangsan&age=11",// 发送的json数据
    headers:new Headers({
        'Content-Type':'application/x-www-form-urlencoded'// 参数是表单的编码格式
    })
})

案例:

<body>
    <div id="app">
        <p>{{JSON.stringify(data)}}</p>
    </div>

    <script>
        new Vue({
            el:'#app',
            data:{
                data:''
            },
            mounted(){
                this.getData();
            },
            methods:{
                /* 
                语法1:
                getData(){
                    fetch('https://api.i-lynn.cn/college').then(res => res.json()).then(res =>{
                        console.log(res);
                        this.data = res.data;
                    })
                } 
                */
               //语法2
               getData(){
                   fetch('https://api.i-lynn.cn/college',{
                       method:'POST',
                       body:"username=zhangsan&age=12",
                       headers:new Headers({
                        'Content-Type': 'application/x-www-form-urlencoded'
                       })
                   }).then(res => res.json()).then(res =>{
                       console.log(res);
                       this.data = res.data;
                   })
               }
                
            }
        })
    </script>
</body>

4.2、axios

语法1:

axios.get(url?参数).then(res=>{})

语法2:

axios.post(url,"参数").then(res=>{})

语法3:

axios({
   method,  //get---params  post---data
   url,
   timeout,
   headers,
   data
}).then(res=>{})

案例:

<body>
    <div id="app">
        <p>{{data}}</p>
    </div>

    <script>
        new Vue({
            el:'#app',
            data:{
                data:''
            },
            mounted(){
                this.getData();
            },
            methods:{
                /*
                //语法1:
                getData(){
                    axios.get('https://api.i-lynn.cn/college?name=zhangsan&age=12').then(res=>{
                        console.log(res);
                        this.data = res.data.data;
                    })
                } */

                /*
                //语法2: 
                getData(){
                    axios.post('https://api.i-lynn.cn/college',"name=zhangsan&age=12").then(res=>{
                        console.log(res);
                        this.data = res.data.data;
                    })
                } */
                //语法3:
                getData(){
                    axios({
                        methods:'post',//类型 get post
                        url:'https://api.i-lynn.cn/college',
                        timeout:1000,//超过没响应报超时错误
                        Headers:{
                            //表单提交
                            // 'Content-Type': 'application/x-www-form-urlencoded'
                            //json提交  默认
                            //  'Content-Type': 'application/json'
                        },
                        //data:"name=zhangsan&age=12",//表单
                        data:{//普通json提交
                            name:'zhangsan',
                            age:12
                        }   
                    }).then(res=>{
                        console.log(res);
                        this.data = res.data.data;
                    })
                }

            }
        })
    </script>
</body>

五、脚手架


5.1介绍

名字:@vue/cli

可以理解为自动帮我们构建项目的工具, vue的自动化构建工具是node.js帮我们做的。

运行环境:基于node运行的

  • 想要使用脚手架
  • 电脑上需要有node环境

5.2、安装脚手架

打开cmd命令行输入以下指令:

  • 全局安装:npm i -g @vue/cli
  • 安装成功后,查看版本内容:vue -V
  • 卸载脚手架:npm uninstall -g @vue/cli

5.3、创建项目

  • 在你需要搭建项目的位置打开命令行
  • 输入指令:vue create 项目名称
  • 就会进入选择环节,选择的时候,两个default分别代表vue2.0的语法和vue3.0的语法,并且都是只有基础内容,只包含vue基础语法,vue.js,bable

1、预设选择

2、预设功能选择,根据自身项目需要选择(按上下键选择,空格确认,回车键下一步)

3、选择Vue版本

4、是否独立配置

5、是否保存本次操作预设

6、项目创建成功

7、输入指令,运行项目:npm run serve 

5.4、目录结构介绍

  • public:不需要去改动现有的文件,里面存放的是浏览器访问的入口文件(index.html)   
  • src:后期很多操作都在这个目录中完成
  • main.js:项目/程序入口文件(该文件中JavaScript代码都会被执行)
  • App.vue:根组件(万物之根)           
  • components:存放自定义的功能组件
  • assets:静态资源目录(图片、视频、音频等就是静态资源),这里面的静态资源浏览器是无法直接访问的,而是给组件通过模块化的方式导入进组件使用的。                                                                                                                                                      

六、Vue的组件


在Vue实例内其实只有一个根节点,我们就需要把所有内容都书写在根节点内,此时内容比较冗余,不利于后期维护,不利于复用。那么框架提供了一种新的开发模式,叫做组件化开发,组件化把某个页面进行拆分,最后再做合并。

6.1、什么是组件

组件就是一段完整的结构,一个可以在Vue语法中使用的元件。

组件其实就是一段包含html、css、js的完整结构,就像一个独立出来的Vue实例;一个自定义”标签“ 。

.vue文件结尾都是组件,组件有template、script、style三部分。

template模板,不止可以写标签,还可以写vue指令。

组件命名以大驼峰形式命名,如MyName(常用)等价于my-name(不建议这样命名)。

组件分为全局组件和局部组件。

6.2、组件的使用

全局组件注册形式如下:

Vue.component('Child',{
   template:`
      组件模板内容
   `,
   data(){
      return{
      
      }
   }
})

components()的第一个参数是组件名,第二个参数是一个对象形式的选项,里面存放一个组件的声明信息。

data必须是函数,同时这个函数要求必须返回一个对象。为什么?

因为Vue实例内的this指向当前实例,而组件内的this指向当前组件,为了组件被复用的时候都可以生成一个独立的数据拷贝,可以互相隔离不受影响,所以就需要使用函数返回对象的形式书写data

局部组件:只能在当前注册它的vue实例中使用,通过某个vue实例或组件的实例选项components注册。

new Vue({
   el:'#app',
   data:{

   },
   components:{
       child:{
          template:`
              组件模板内容
          `,
          data(){
             return{
                name:'~~~'
             }
          }
       }
   }
})

然而,这些都是html页面上的组件注册,我们从根本上还是要基于组件化开发,接下来我们要中重点看的还是脚手架里面的组件到底是怎么使用的。

组件使用步骤:

  1. 定义组件
  2. 引入并注册组件
  3. 使用组件

在src里创建文件夹login,在login里定义组件Child.vue、Parent.vue,然后在这两个

组件里,给大家演示组建的使用。

Child.vue组件的代码如下:

 Parent.vue组件的代码如下:

 main.js入口文件的代码如下:

在cmd里输入指令运行组件:

打开http://localhost:8080,查看运行结果:

6.3、tabs切换案例

在src文件夹里创建 tabsDemo文件夹,并在tabsDemo文件夹创建4个组件ChildA.vue,ChildB.vue,ChildC.vue,Tabs.vue,实现如下功能。

体会:v-if 控制节点创建和销毁,v-show通过css方式控制元素显示或隐藏,接口是渲染的。

实现代码如下:

 ChildA.vue组件的代码:

<template>
  <div>
      <p>我是A的内容</p>
  </div>
</template>

<script>
export default {
    
</script>

<style>

</style>

 ChildB.vue组件的代码:

<template>
  <div>
      <p>我是B的内容</p>
  </div>
</template>

<script>
export default {
   
}
</script>

<style>

</style>

 ChildC.vue组件的代码:

<template>
  <div>
      <p>我是C的内容</p>
  </div>
</template>

<script>
export default {
    
}
</script>

<style>

</style>

 Tabs.vue组件的代码:

<template>
  <div>
      <button @click="change(0)">A</button>
      <button @click="change(1)">B</button>
      <button @click="change(2)">C</button>

      <!-- 3、使用组件 -->
      <!-- 内容  v-if-->
      <!-- if控制元素标签 删除或者创建 -->
      <ChildA v-if="num==0"/>
      <ChildB v-else-if="num==1"/>
      <ChildC v-else/>
      
  </div>
</template>

<script>
//2、引入并注册组件
//引入
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'
import ChildC from './ChildC.vue'
export default {
    components:{
        //注册
        ChildA,
        ChildB,
        ChildC
    },
    data(){
        return{
            num:0
        }
    },
    methods:{
        change(val){
            this.num =val
        }
    }
}
</script>

<style>

</style>

main.js入口文件代码:

import Vue from 'vue'
//入口文件里引入要运行的组件
import Tabs from './tabsDemo/Tabs.vue'
Vue.config.productionTip = false

new Vue({
  
  // el:"#app",
  render: h => h(Tabs),//要运行的组件名称
}).$mount('#app')

6.4、从tabs切换案例体会生命周期

1、当然代码需要一些变动:

ChildA.vue组件的代码:

<template>
  <div>
      <p>我是A的内容</p>
  </div>
</template>

<script>
export default {
    beforeCreate(){
        console.log('创建A之前');
    },
    created(){
        console.log('创建A之后');
    },
    beforeMount(){
        console.log('挂载A之前');
    },
    mounted(){
        console.log('挂载A之后');
    },
    beforeUpdate(){
        console.log('更新A之前');
    },
    updated(){
        console.log('更新A之后');
    },
    beforeDestroy(){
        console.log('销毁A之前');
    },
    destroyed(){
        console.log('销毁A之后');
    }
}
</script>

<style>

</style>

ChildB.vue组件的代码:

<template>
  <div>
      <p>我是B的内容</p>
  </div>
</template>

<script>
export default {
    beforeCreate(){
        console.log('创建B之前');
    },
    created(){
        console.log('创建B之后');
    },
    beforeMount(){
        console.log('挂载B之前');
    },
    mounted(){
        console.log('挂载B之后');
    },
    beforeUpdate(){
        console.log('更新B之前');
    },
    updated(){
        console.log('更新B之后');
    },
    beforeDestroy(){
        console.log('销毁B之前');
    },
    destroyed(){
        console.log('销毁B之后');
    }
}
</script>

<style>

</style>

ChildC.vue组件的代码:

<template>
  <div>
      <p>我是C的内容</p>
  </div>
</template>

<script>
export default {
    beforeCreate(){
        console.log('创建C之前');
    },
    created(){
        console.log('创建C之后');
    },
    beforeMount(){
        console.log('挂载C之前');
    },
    mounted(){
        console.log('挂载C之后');
    },
    beforeUpdate(){
        console.log('更新C之前');
    },
    updated(){
        console.log('更新C之后');
    },
    beforeDestroy(){
        console.log('销毁C之前');
    },
    destroyed(){
        console.log('销毁C之后');
    }
}
</script>

<style>

</style>

Tabs.vue组件的代码不变,我们看结果:

总结:

生命周期的过程:

创建 -- beforeCreate A  =>  created A  =>  beforeMount A  => mounted A

销毁A创建B --  beforeCreate B  =>  created B  =>  beforeMount B  =>  Mounted B  =>   beforeDestroy A  =>  destroyed A  =>  mounted B

2、我们再来把代码变动一下,体会一下缓冲

只需要再上面代码的基础上,改动Tabs.vue组件的代码:

<template>
  <div>
      <button @click="change(0)">A</button>
      <button @click="change(1)">B</button>
      <button @click="change(2)">C</button>

      <!-- 3、使用组件 -->
      <!-- keep-alive 缓存组件,提高性能 -->
      <!-- exclude排除  include包含 
        exclude跟组件名称,不缓存该组件,其他都缓存
        include跟组件名称,指定某个组件进行缓存
      -->
      <-- 默认情况下,缓冲标签内所有的组件 -->
      <keep-alive>
         <ChildA v-if="num==0"/>
         <ChildB v-else-if="num==1"/>
         <ChildA v-else/>
      </keep-alive> 
      
  </div>
</template>

<script>
//2、引入并注册组件
//引入
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'
import ChildC from './ChildC.vue'
export default {
    components:{
        //注册
        ChildA,
        ChildB,
        ChildC
    },
    data(){
        return{
            num:0
        }
    },
    methods:{
        change(val){
            this.num =val
        }
    }
}
</script>

<style>

</style>

 总结:

keep-alive缓存组件,组件创建之后保存到内存中,不会销毁,提高性能之一。

就如我上面一样,创建过一次后,后面再点击AB会从直接从内存中直接调用,不会像之前一样,会销毁A创建B。

七、组件通信


7.1、父子通信

7.1.1、父传子

父组件通过动态属性绑定一个值,子组件通过props数组来接收参数变量。
在src文件里创建LifeDemo文件夹,文件夹里创建两个组件Child.vue、Parent.vue

Child.vue组件的代码如下:

<template>
  <div>
      <p>子组件</p>
      <p>{{num}}</p>
  </div>
</template>

<script>
export default {
    //子组件通过props数组来接收参数变量
    props:['num']
}
</script>

<style>

</style>

Parent.vue组件的代码如下:

<template>
  <div>
      <p>父组件</p>
      <!-- 父组件通过动态属性绑定一个值 -->
      <Child :num="num"/>
      <button @click="num++">+1</button>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
    components:{
        Child
    },
    data(){
        return{
            num:0
        }
    }
}
</script>

<style>

</style>

main.js入口文件代码:

import Vue from 'vue'
//入口文件里引入要运行的组件
import Parent from './01LifeDemo/Parent.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(Parent),//要运行的组件名称
}).$mount('#app')

 7.1.2、子传父

子组件调用事件,this.$emit触发父组件的自定义事件并传参。

在src文件里创建MsgDemo文件夹,文件夹里创建两个组件Child.vue、Parent.vue

Parent.vue组件的代码如下:

<template>
  <div>
      <p>父组件</p>
      <Child :arr="arr" @parentaddfun="parentfun"/>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
    components:{
        Child
    },
    data(){
        return{
            arr:['王昭没有君啊','王昭君','王昭']
        }
    },
    methods:{
        parentfun(val){
            this.arr.push(val);
        }
    }  
}
</script>

<style>

</style>

Child.vue组件的代码如下:

<template>
  <div>
      <p>子组件</p>
      <ul>
          <li v-for="(item,index) in arr" :key="index">{{item}}</li>
      </ul>
      <button @click="addfun">添加</button>
  </div>
</template>

<script>
export default {
    props:['arr'],
    methods:{
        addfun(){
            //$emit()有2个参数
            //第一个参数为自定义的事件名称
            //第二个参数为需要传递的数据
            this.$emit('parentaddfun','王昭君啊');
        }
    }
}
</script>

<style>

</style>

main.js入口文件代码:

import Vue from 'vue'
//入口文件里引入要运行的组件
import Parent from './02MsgDemo/Parent.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(Parent),//要运行的组件名称
}).$mount('#app')

 7.2、bus通信或事件总线通信(兄弟间通信)

使用方法:

  1. 创建一个全局的事件中心( 一般使用new Vue() )
  2. 写自定义事件(A向B通信,要把自定义事件写在B上 Bus.$on  Bus.$off)
  3. A组件触发bus事件并且传参。( Bus.$emit() )

在src文件夹中创建BusDemo文件夹,文件中定义组件Bus.js、ChildA.vue、ChildB.vue、Parent.vue

Bus.js代码如下:

import Vue from 'vue'
//使用Vue实例来做事件中心的载体
export default new Vue()

ChildA.vue代码如下:

<template>
  <div>
      <p>子组件A</p>
      <button @click="addfun">点击</button>
  </div>
</template>

<script>
//引入事件中心载体
import Bus from './Bus'
export default {
   methods:{
       addfun(){
           Bus.$emit('addfun','案例演示事件总线通信');
       }
   }
}
</script>

<style>

</style>

ChildB.vue代码如下:

<template>
  <div>
      <p>子组件B</p>
  </div>
</template>

<script>
//引入事件中心载体
import Bus from './Bus'
export default {
    mounted(){//挂载上
        //绑定自定义事件
        Bus.$on('addfun',this.addfn);
    },
    //定时器 自定义事件 自定义dom事件 要写销毁
    beforeDestroy(){
        //移除自定义事件
        Bus.$off('addfun',this.addfn);
    },
    methods:{
        addfn(val){
            console.log('我被触发了',val);
        }
    }
}
</script>

<style>

</style>

Parent.vue代码如下:

<template>
  <div>
      <p>父组件</p>
      <ChildA />
      <ChildB />
  </div>
</template>

<script>
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'
export default {
    components:{
        ChildA,
        ChildB
    }
}
</script>

<style>

</style>

 main.js入口文件代码:

import Vue from 'vue'
//入口文件里引入要运行的组件
import Parent from './03BusDemo/Parent.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(Parent),//要运行的组件名称
}).$mount('#app')

 7.3、ref通信

父组件调用子组件中的函数,利用ref通信(功能相当强大,不仅可以获取组件的实例,还可以获取标签节点)

步骤:ref绑定值,通过this.$refs.mychild来获取组件实例或者节点

在src文件夹中创建RefDemo文件夹,文件中定义组件Child.vue、Parent.vue

Child.vue代码如下:

<template>
  <div>
      <p>子组件</p>
      <p>{{ count }}</p>
  </div>
</template>

<script>
export default {
  data(){
      return{
          count:0
      }
  },
  methods:{
      changeCount(){
          this.count++;
          return this.count;
      }
  }
}
</script>

<style>

</style>

Parent.vue代码如下:

<template>
  <div>
      <p>父组件</p>
      <Child ref="mychild"/>
      <p ref="myp">王昭没有君啊</p>
      <button @click="changefun">触发子组件</button>
  </div>
</template>

<script>
import Child from './Child'
export default {
    components:{
        Child
    },
    methods:{
        changefun(){
            console.log(this.$refs.mychild.changeCount());//组件的实例
            console.log(this.$refs.myp);//p标签
        }
    }
}
</script>

<style>

</style>

 main.js入口文件代码:

import Vue from 'vue'
//入口文件里引入要运行的组件
import Parent from './04RefDemo/Parent.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(Parent),//要运行的组件名称
}).$mount('#app')


7.4、跨层级通信

作用:用于父组件向孙组件传递数据。

步骤:父组件直接provide(){return{msg:'王昭没有君啊'}},孙组件通过inject:['msg']注入值。

注意:provide inject单向通信,和props一样,子组件不能修改父组件的数据变量。

在src文件夹中创建ProvideDemo文件夹,文件夹中创建组件ComA、ComB、ComC

ComA代码如下:

<template>
  <div>
      <p>我是父组件</p>
      <button @click="obj.color='red'">修改按钮</button>
      <ComB/>
  </div>
</template>

<script>
//特征:provide  inject成对出现,必须得是直系亲属,单项
//provide inject传递对象:实际上传递得指针,只要父组件对象改变,所有后代对象同时改变
import ComB from './ComB.vue'
export default {
    components:{
        ComB
    },
    data(){
        return{
            msg:'provide和inject数据传递',
            obj:{color:'yellow'}
        }
    },
    provide(){//provide在这里注入数据 传递数据
        return{
            MyMsg:this.msg,
            MyName:'王昭没有君啊',
            MyObj:this.obj
        }
    }
}
</script>

<style>

</style>

ComB代码如下:

<template>
  <div>
      <p>我是子组件</p>
      <ComC/>
  </div>
</template>

<script>
import ComC from './ComC.vue'
export default {
    components:{
        ComC
    }
}
</script>

<style>

</style>

ComC代码如下:

<template>
  <div>
      <p>我是孙组件</p>
      <p>获取父组件的数据:{{MyMsg}}</p>
      <p>获取父组件的数据:{{MyName}}</p>
      <p>获取父组件的数据:{{JSON.stringify(MyObj)}}</p>
  </div>
</template>

<script>
export default {
    inject:['MyMsg','MyName','MyObj'] //获取父组件传递的数据
}
</script>

<style>

</style>

 main.js入口文件代码: 

import Vue from 'vue'
//入口文件里引入要运行的组件
import ComA from './05provideDemo/ComA.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(ComA),//要运行的组件名称
}).$mount('#app')

7.5、插槽 

 组件的最大特性就是复用,而好插槽能大大提高组件的可重用能力。插槽无非就是在子组件中挖个坑,坑里面放什么由父组件决定。

插槽分为匿名插槽、具名插槽、作用域插槽。

7.5.1、匿名函数

作用:父组件给子组件传递html片段。

规则:父传递数据,子渲染父数据,父不传递数据,子渲染默认。

在src文件夹中创建SlotDemo文件夹,文件夹中定义组件Child.vue、Parent.vue

Child.vue代码如下:

<template>
  <div>
     <p>插槽子组件</p> 
     <slot>这里是默认标签</slot>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

Parent.vue代码如下:

<template>
  <div>
      <p>插槽父组件</p>
      <Child>
          <ul>
              <li>编号</li>
              <li>姓名</li>
          </ul>
      </Child>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
    components:{
        Child
    }
}
</script>

main.js入口文件代码: 

import Vue from 'vue'
//入口文件里引入要运行的组件
import Parent from './07slotDemo/Parent.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(Parent),//要运行的组件名称
}).$mount('#app')

 7.5.2、具名插槽

作用:多个插槽的场景。

关键词:子组件具名 name="myData"    父组件 v-slot:myData来指定插槽。

在src文件夹中创建SlotDemo文件夹,文件夹中定义组件Child.vue、Parent.vue

Child.vue代码如下:

<template>
  <div>
      <p>具名插槽子组件</p>
      <slot name="right">右</slot>
      <slot name="left">左</slot>
      <slot name="center">中</slot>
      <slot>下</slot>
      
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

Parent.vue代码如下:

<template>
  <div>
      <Child>
          <template v-slot:left>
             <p>文本内容左菜单</p>
          </template>
          <template v-slot:center>
             <p>文本内容中菜单</p>
          </template>
          <template v-slot:right>
             <p>文本内容右菜单</p>
          </template>
          <p>下插槽内容</p>
      </Child>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
    components:{
        Child
    }
}
</script>

<style>

</style>

main.js入口文件代码:

import Vue from 'vue'
//入口文件里引入要运行的组件
import Parent from './07slotDemo/Parent.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(Parent),//要运行的组件名称
}).$mount('#app')

7.5.3、作用域插槽

作用:父组件对子组件进行再次加工

关键词:子组件动态属性绑定值 :data="url" 父组件v-slot="mydata"   mydata:{ data:'http://baidu.com'}

在src文件夹中创建SlotDemo文件夹,文件夹中定义组件Child.vue、Parent.vue

Parent.vue代码如下:

<template>
  <div>
      <p>作用域插槽父组件</p>
      <Child>
          <template v-slot="myData">
             <a :href="myData.data">百度一下</a>
          </template>
      </Child>
  </div>
</template>

<script>
import Child from './Child.vue'
export default {
    components:{
        Child
    }
}
</script>

<style>

</style>

 Child.vue代码如下:

<template>
  <div>
      <p>作用域插槽子组件</p>
      <slot :data="url"></slot>
      
  </div>
</template>

<script>
export default {
  data(){
    return{
      url:'http://baidu.com'
    }
  }
}
</script>

<style>

</style>

main.js入口文件代码:

import Vue from 'vue'
//入口文件里引入要运行的组件
import Parent from './07slotDemo/Parent.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(Parent),//要运行的组件名称
}).$mount('#app')

7.6、$nextTick

vue是异步渲染的框架,react也是,data改变之后,dom不会立即渲染,$nextTick会在dom渲染之后被触发,作用:以获取最新的dom节点。

在src文件夹中创建nextTickDemo文件夹,文件夹中定义组件List.vue

List.vue代码如下:

<template>
  <div>
      <p>nextTick案例</p>
      <ul ref="mylist" v-if="b">
          <li v-for="item in arr" :key="item">
              {{item}}
          </li>
      </ul>
  </div>
</template>

<script>
export default {
    data(){
        return{
            arr:['a','b','c','d'],
            b:false
        }
    },
    mounted(){
        this.arr.push('e');
        this.b = true;
        console.log(this.$refs.mylist);
        this.$nextTick(()=>{
            console.log(this.$refs.mylist);
        })
    }
}
</script>

<style>

</style>

main.js入口文件代码如下:

import Vue from 'vue'
//入口文件里引入要运行的组件
import List from './08nextTickDemo/List.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(List),//要运行的组件名称
}).$mount('#app')

 八、动态组件


通过使用保留得<component>元素,动态的绑定到它的is特性,

动态组件的语法

<template>
  <div>
      <p>父组件</p>
      <!-- 静态组件写法 -->
      <ComA />
      <!-- 动态组件 component是vue的内置标签 :is组件渲染的挂载点 后面是跟着组件名称 -->
      <component :is="data" />
  </div>
</template>

<script>
import ComA from './ComA.vue'
export default {
    components:{
        ComA,
        ComB
    },
    data(){
        return{
            data:'ComA'
        }
    },
}
</script>

案例:登录页面和注册面板切换

在src文件夹中创建component文件夹,文件夹中创建组件ComA、ComB、ComC

ComA代码如下:

<template>
  <div>
      <p>用户组件</p>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

ComB代码如下:

<template>
  <div>
      <p>密码组件</p>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

ComC代码如下:

<template>
  <div>
      <button @click="changefun(0)">登录页面</button>
      <button @click="changefun(1)">注册页面</button>
      <!-- template 在vue叫空标签 -->
      <template v-for="(item,index) in comArr">
          <component :is="item" :key="index"/>
      </template>
  </div>
</template>

<script>
import ComA from './ComA.vue'
import ComB from './ComB.vue'
export default {
    components:{
        ComA,
        ComB
    },
    data(){
        return{
            comArr:['ComA','ComB']
        }
    },
    methods:{
        changefun(val){
            if(val === 0){
                this.comArr = ['ComA','ComB'];
            }else if(val === 1){
                this.comArr = ['ComA','ComB','ComB'];
            }
        }
    }
}
</script>

<style>

</style>

 main.js入口文件代码: 

import Vue from 'vue'
//入口文件里引入要运行的组件
import ComC from './06component/ComC.vue'

Vue.config.productionTip = false

new Vue({
  // el:"#app",
  render: h => h(ComC),//要运行的组件名称
}).$mount('#app')

九、路由


9.1、路由的概念

路由的本质就是一种对应关系,根据不同的URL请求,返回对应不同的资源。那么url地址和真实的资源之间就有一种对应的关系,就是路由。

9.2、前端路由的两种模式

前端路由模式有两种实现方式:hash方式、history方式。

hash路由模式是这样的:http://xxx.com/#abc。 有带#号,hash值为 #/abc,它不会向服务器发出请求,因此也就不会刷新页面。

histroy路由模式,如http://localhost:8080/xx。浏览器地址没有#,  它也一样不会刷新页面的。但是url地址会改变。但它在服务器没有配置的情况下,不能手动刷新,否则返回404页面。

9.3、Vue Router

利用脚手架创建一个带有Router的vue项目。

关键步骤,若有遗忘,可以翻到上面阅读怎样利用脚手架创建项目。

 路由的组成:

在src文件夹中自动生成一个router文件夹,文件夹里有一个index.js文件,并设定路由模式,代码如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'

//vue注册router插件
Vue.use(VueRouter)
//路由配置数组
const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView//同步路由 项目启动九加载了
  },
  {
    path: '/about',
    name: 'about',
    //异步路由 访问about路由才加载当前的页面组件
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]

//使用路由
const router = new VueRouter({
  routes,
  //设定路由模式
  //mode:'hash'
  mode:'history' //常用
})

export default router

第二步:在main.js入口文件注册路由:

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  router,//调用 路由注册 关联
  render: h => h(App)
}).$mount('#app')

第三步:在src文件夹找到APP.vue中指定路由渲染位置

<template>
  <div id="app">
    <!-- 指定路由渲染位置 -->
    <!-- 占位符 -->
    <router-view/>
  </div>
</template>

<style>

</style>

9.4、声明式导航

9.4.1、不携带参数跳转

在src文件夹中的views文件中定义组件Home.vue、About.vue

Home.vue代码如下:

<template>
  <div class="home">
      home页面 <br/>
      <!-- 声明式导航 不携带参数跳转-->
      <!-- router-link替代a标签 -->
      <router-link to="/about">跳转到about页面</router-link>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

About.vue代码如下:

<template>
  <div class="about">
      about页面 <br/>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

index.js代码如下:

import Vue from 'vue'
import VueRouter from 'vue-router'
//导入
import Home from '../views/Home.vue'

//vue注册router插件
Vue.use(VueRouter)
//路由配置数组
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    //异步路由
    component: () => import('../views/About.vue')
  }
]

//使用路由
const router = new VueRouter({
  routes,
  //mode:'hash'
  mode:'history' //常用
})

export default router

 9.4.2、携带参数跳转

Home.vue代码如下:

<template>
  <div class="home">
      home页面 <br/>
      <!-- 声明式导航 携带参数跳转-->
      <!-- query传参 -->
      <router-link to="/about?name=zhangsan">跳转到about页面并传参</router-link>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

About.vue、index.js代码不变

9.5、编程式导航 

9.5.1、编程式导航bug

编程式导航在跳转到与当前地址一致的URL时会报错,但这个报错不影响功能:

 如下代码跳转当前页面,会出现这种情况

<template>
  <div class="home">
      home页面 <br/>
      <!-- 编程式明式导航 -->
      <button @click="clickfun">跳转到本页面</button>
  </div>
</template>

<script>
export default {
  methods:{
    clickfun(){
      //跳转当前页面
      this.$router.push('/');
    }
  }
}
</script>

<style>

</style>

解决方法:

可以在路由入口文件index.js中添加如下代码解决该问题:

// 该段代码不需要记,理解即可
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
    return originalPush.call(this, location).catch((err) => err);
};

9.5.2、编程式导航跳转到about页面

以下四种方法可以根据条件任选一种。

Home.vue代码如下:

<template>
  <div class="home">
      home页面 <br/>
      <!-- 编程式明式导航 -->
      <button @click="clickfun">跳转到about页面</button>
  </div>
</template>

<script>
export default {
  methods:{
    clickfun(){
      //1、跳转
      this.$router.push('/about');

      //2、携带参数跳转
      this.$router.push('/about?name=zhangsan');

      //3、query传参 用的少
      this.$router.push({
        path:'/about',
        query:{
          name:'lisi'
        }
      });

      //4、post传参
      this.$router.push({
        name:'About', //post传参 必须用name跳转
        params:{
          name:'wangzwu'
        }
      })
      //post传参问题:1、隐藏url参数 2、接收this.$route.params 3、刷新丢失参数

    }
  }
}
</script>

<style>

</style>

9.5.3、返回上一个页面

 Home.vue代码不变,About.代码如下:

<template>
  <div class="about">
      about页面 <br/>
      <button @click="backfun">返回</button>
  </div>
</template>

<script>
export default {
  methods:{
    backfun(){
      //this.$router.go(-1); //-1回退一级  0 刷新 1 前进一级
      this.$router.back(); //返回
    }
  }
}
</script>

<style>

</style>

9.6、嵌套路由

在router文件中定义三个组件News.vue、My.vue、User.vue

效果如下:

News.vue代码如下:

<template>
  <div>
      news新闻页面<br/>
      <p>news头部内容</p>
      <!-- 二级路由占位符 -->
      <router-view></router-view>
      <p>news底部内容</p>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

My.vue代码如下:

<template>
  <div>
      <p>新闻页面下的二级页面 My页面</p>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

User.vue代码如下:

<template>
  <div>
      <p>新闻页面下的二级页面 User页面</p>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

index.js代码如下:

import Vue from 'vue'
import VueRouter from 'vue-router'
//导入
import Home from '../views/Home.vue'
import News from '../views/News.vue'
import My from '../views/My.vue'
import User from '../views/User.vue'

//vue注册router插件
Vue.use(VueRouter)
//路由配置数组
const routes = [
  {
    path:'/news',
    name:'News',
    component:News,
    children:[//二级路由
      {
        path:'my',//注意:二级路由开始 前面千万别加 /
        name:'My',
        component:My
      },
      {
        path:'user',
        name:'User',
        component:User
      }
    ]
  },
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    //异步路由
    component: () => import('../views/About.vue')
  }
]

//使用路由
const router = new VueRouter({
  routes,
  //mode:'hash'
  mode:'history' //常用
})


export default router

9.7、动态路由

动态路由就是路由规则中有部分规则是动态变化的,不是固定的值,需要去匹配取出数据。

动态路由 需要路由的支持

1、路由对象 path:'/news/:id?'

2、<router-link to="/news/456">跳转</router-link>

3、接收参数 this.$route.parasms

注意:动态路由传参没写问号必须传参,加了问号可传参可不传。

在news的基础上进行变动:

news.vue代码如下:

<template>
  <div>
      news新闻页面<br/>
      <p>news头部内容</p>
      <!-- 二级路由占位符 -->
      <router-view></router-view>
      <p>news底部内容</p>

      <!-- 动态路由 -->
      <router-link to="/news/456">跳转</router-link>
  </div>
</template>

<script>
export default {
  mounted(){
    //接收参数
    console.log(this.$route.params);
  }
}
</script>

<style>

</style>

index.js代码如下:

import Vue from 'vue'
import VueRouter from 'vue-router'
//导入
import Home from '../views/Home.vue'
import News from '../views/News.vue'
import My from '../views/My.vue'
import User from '../views/User.vue'


//vue注册router插件
Vue.use(VueRouter)
//路由配置数组
const routes = [
  {
    path:'/news/:id?',// 动态路由支持 ?有没有id都可以
    name:'News',
    component:News,
    children:[//二级路由
      {
        path:'my',//注意:二级路由开始 前面千万别加 /
        name:'My',
        component:My
      },
      {
        path:'user',//注意:二级路由开始 前面千万别加 /
        name:'User',
        component:User
      }
    ]
  },
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    //异步路由
    component: () => import('../views/About.vue')
  }
]



//使用路由
const router = new VueRouter({
  routes,
  //mode:'hash'
  mode:'history' //常用
})


export default router

9.8、重定向和404路由

9.8.1、重定向

用户在访问地址A的时候,强制用户跳转到地址C,从而展示特定的组件页面。

redirect指定一个路由地址。

9.8.2、404路由

用于处理路由匹配不上的时候页面的展示,不做404路由,则页面显示白板页面。

在src文件夹中的views文件夹创建NotFind.vue

NotFind.vue代码如下:

<template>
  <div>
      <p>404,页面飞走了</p>
  </div>
</template>

<script>
export default {

}
</script>

<style>

</style>

index.js代码如下:

import Vue from 'vue'
import VueRouter from 'vue-router'
//导入
import Home from '../views/Home.vue'
import News from '../views/News.vue'
import My from '../views/My.vue'
import User from '../views/User.vue'
import Notfind from '../views/NotFind.vue'

//vue注册router插件
Vue.use(VueRouter)
//路由配置数组
const routes = [
  {
    path:'/',
    redirect:'/news' //重定向
  },
  {
    path:'/news/:id?',//动态路由支持 ?有没有id都可以
    name:'News',
    component:News,
    children:[//二级路由
      {
        path:'my',//注意:二级路由开始 前面千万别加 /
        name:'My',
        component:My
      },
      {
        path:'user',//注意:二级路由开始 前面千万别加 /
        name:'User',
        component:User
      }
    ]
  },
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    //异步路由
    component: () => import('../views/About.vue')
  },
  {//404放在最末尾,提示用户页面不存在用的
    path:'*',
    component:Notfind
  }
]

//使用路由
const router = new VueRouter({
  routes,
  //mode:'hash'
  mode:'history' //常用
})


export default router

9.9、路由守卫  路由钩子

9.9.1、全局前置守卫

beforeEach((to,from,next){})

作用:登录权限

9.9.2、路由独享守卫

beforeEnter:(to,from,next)=>{}

9.9.3、组件内守卫

beforeRouteEnter(to,from,next){}  路由进入前调用
beforeRouteUpdate(to,from,next){}  动态路由切换调用
beforeRouteLeave(to,from,next){}  路由离开之前调用

十、Vuex全局状态管理

 

10.1、介绍

  • 如果遇到组件间嵌套层次较多,比较复杂的话,多个组件之间共有一个数据,使用组件传值处理起来的话,就比较麻烦。
  • vuex是vue配套的数据管理工具,我们可以将组件共享数据保存到vuex中,方便整个程序中的任何组件都可以获取和修改vuex中保存的公共数据。

10.2、Vuex是什么

  • Vuex是实现组件全局状态(数据)管理的一种机制,可方便的实现组件之间数据的共享。
  • 本质:用来组件间共享数据的vuex里的数据是全局的,所有组件都可拿到,数据存到vuex,取也从vuex里取。

10.3、使用Vuex统一管理状态的好处

  1. 能够在vuex中集中管理共享的数据,易于开发和后期维护。
  2. 能够高效的实现组件之间的数据共享,提高开发效率。
  3. 存储在vuex中的数据都是响应式的,能够实时保持数据与页面的同步。

10.4、什么样的数据适合存储到Vuex中

一般情况下,只有组件共享的数据,才有必要存储到vue中;对于组件中的私有数据,依旧存储到组件自身的data中即可。

10.5、利用脚手架创建一个带有Vuex项目

关键步骤,若有遗忘,可以翻到上面阅读怎样利用脚手架创建项目

 

10.6、使用vuex及案例


 

 

案例:

利用vuex完成如下功能

 index.js代码如下:

//导入vue组件
import Vue from 'vue'
//导入vuex组件
import Vuex from 'vuex'
//注册vuex组件
Vue.use(Vuex)

//导出实例对象
export default new Vuex.Store({
  state: {//vue放数据的地方
    count:100,
    str:'王昭没有君啊'
  },
  getters: {//也是获取部分数据,派生数据 缓存 依赖性 几乎等同于computed
    getcount(state){
      return '派生数据:' + state.count;
    },
    getstr(state){
      return '派生数据:' + state.str;
    }
  },
  mutations: {//同步数据修改state数据,唯一修改数据的2地方
    creaseCount(state){// 不传参 第一个参数永远是state
      state.count++;
    },
    reduceCount(state,num){// 传参,第一个参数永远是state
      state.count -= num;
    }
  },
  actions: {//异步修改mutations 间接修改state
    increaseAsny({commit}){
      commit('creaseCount');
    },
    reduceAsny({commit},num){
      commit('reduceCount',num);
    }
  }
})

App.vue代码如下:

<template>
  <div id="app">
    <h2>state获取数据的方法</h2>

    <p>方法1:</p>
    <p>{{this.$store.state.count}}</p>

    <p>方法2:</p>
    <p>{{ count }}</p>
    <p>{{ str }}</p>

    <h2>getters获取数据的方法</h2>

    <p>方法1:</p>
    <p>{{this.$store.getters.getcount}}</p>

    <p>方法2:</p>
    <p>{{ getcount }}</p>
    <p>{{ getstr }}</p>

    <h2>mutations修改数据的方法</h2>

    <p>方法1:</p>
    <button @click="crease">+1</button>
    <button @click="reduce">-1</button><br/>

    <p>方法2:</p>
    <button @click="creaseCount">+1</button>
    <button @click="reduceCount(1)">-1</button><br/>

    <h2>actions修改数据的方法</h2>

    <p>方法1:</p>
    <button @click="increaseAsny">+1</button>
    <button @click="reduceSnyc">-1</button><br/>

    <p>方法2:</p>
    <button @click="increaseAsny">+1</button>
    <button @click="reduceAsny(1)">-1</button><br/>

  </div>
</template>

<script>
/* 
  state取数据
  方法1:this.$store.state.count
  方法2:mapState

  getters取数据
  方法1:this.$store.getters.count
  方法2:mapGetters
*/

//方法2:从vuex中按需导入
import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'

export default {
  computed:{
    //方法2
    //将该组件需要的vuex中的全局数据映射到该组件计算属性中
    ...mapState(['count','str']),//数组 是可以取多个state数据
    ...mapGetters(['getcount','getstr'])
  },
  methods:{
    //mutations修改数据方法1:
    crease(){
      this.$store.commit('creaseCount');//不传参
    },
    reduce(){
      this.$store.commit('reduceCount','1');//传参
    },
    //mutations修改数据方法2:
    //将vuex中creaseCount和reduceCount方法映射到组件的methods 中
    ...mapMutations(['creaseCount','reduceCount']),

    //actions修改数据方法1:
    increaseAsny(){
      this.$store.dispatch('increaseAsny');
    },
    reduceSnyc(){
      this.$store.dispatch('reduceAsny','1');
    },
    //actions修改数据方法2:
    ...mapActions(['increaseAsny','reduceAsny'])

  }
}
</script>

<style>

</style>

10.7、 核心概念及案例

  • state是一个全局的状态存储,数据会存储在其中,vue组件可以直接访问其中的值,但是只可以读,不可以进行写操作。
  • getters有些时候我们需要对获取的数据进行加工,获取数据的一部分或者某些属性,而不是直接获取state中的数据,这时候可以通过getters定义函数,返回对应的数据。
  • mutations是vuex中唯一一个可以修改数据的地方,mutations可以定义事件函数,在vue组件中可以通过commit发射事件,调用函数,需要注意的是,mutations中的操作必须是同步的,不可以存在异步操作的情况。
  • actions和mutations比较类似,不同的是actions中不直接修改state,而是通过commit调用mutations修改数据,而且actions中可以存在异步处理逻辑。

  • 25
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王昭没有君啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值