0


Vue框架

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

一、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会先销毁

广州

,然后重新生成

上海

,然后重新渲染生成页面,此时v-if会造成页面的重绘和回流;v-show会给

深圳

添加样式:

深圳

,给

广州

取消样式:

广州

,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配置的选择器,去获取一个页面节点。
  • 只能拿到
    ...

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

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

    {{msg}}

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中可以存在异步处理逻辑。


本文转载自: https://blog.csdn.net/weixin_58448088/article/details/123428830
版权归原作者 王昭没有君啊 所有, 如有侵权,请联系我们删除。

“Vue框架”的评论:

还没有评论