当前位置: 首页 > article >正文

Vue2学习记录

前言

这篇笔记,是根据B站尚硅谷的Vue2网课学习整理的,用来学习的

如果有错误,还请大佬指正

Vue核心

Vue简介

Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架

它基于标准 HTML、CSS 和 JavaScript 构建,

并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面


官网:Vue.js - 渐进式 JavaScript 框架 | Vue.js (vuejs.org)

初识Vue

  • Vue进行工作,需要提供 Vue实例 和 模版
  • 通过 new Vue() 创建出Vue实例对象
  • 在Vue实例对象中,需要绑定模板
    <!--准备容器-->
    <div id="test01">
        Hello {{word}}!!
    </div>
    <script>
        //创建Vue实例
        new Vue({
            el: '#test01',//指定当前实例为那个模版服务
            data: {
                word: 'world'
            }//提供数据,供指定模版使用
        })
    </script>

 MVVM模型

Vue 仿照MVVM来进行设计

分为模型(Model),视图(View)和视图模型(ViewModel)三部分

  • M:模型(Model) -> data中的数据
  • V:视图(View) -> 模版,即 DOM
  • VM:视图模型(ViewModel) -> Vue 实例对象

模版绑定 与 data 写法

模版绑定

方法一:

使用 el 来绑定

语法 : el : ' css选择器 '

        new Vue({
            el: '#test01',
            data: {
                word: 'world'
            }
        })

方法二:

语法:实例对象 . $mount ( ' css选择器 ' )

        const vm = new Vue({
            data: {
                word: 'world!!!'
            }
        })
        vm.$mount('.test01')

data写法

写法一:对象式

语法:data : { 数据对象 }

            data: {
                word: 'world!!!'
            }

写法二:函数式

必须返回一个数据对象

不要写箭头函数

            data: function(){
                return{
                    word:'world!!!'
                }
            }

模版语法

Vue 使用一种基于 HTML 的模板语法,

使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上

简单来说:

        将Vue实例中的数据,呈现到 HTML 上

插值语法

可以分析标签体内容(标签包裹内容)

语法:

        {{JS表达式}}

示例:

    <div id="test01">
        Hello {{word}}
        <!-- {{}} 中的内容会被替换为Vue实例中的数据-->
    </div>
    
    <script>
        new Vue({
            el:'#test01',
            data:{
                word:'world!!!'
            }
        })
    </script>

{{ }} 中的内容会被替换为Vue实例中的数据

指令语法

可以解析标签(标签属性、标签体内容),并可以绑定事件

指令有很多,以 v- 为开头

例:v-bind:  (可简写为 : )

        v-bind : 标签属性 = 'JS表达式'   或   : 标签属性 = 'JS表达式'

    <div id="test02">
        <a v-bind:href="Prohibited">未满18不要打开</a>
        <!-- "" 中的内容会被替换为Vue实例中的数据-->
    </div>

    <script>
        new Vue({
            el: '#test02',
            data: {
                Prohibited: 'https://store.steampowered.com/app/2358720/_/'
            }
        })
    </script>

数据绑定

单向数据绑定

语法:

v-bind : 标签属性 = 'JS表达式'   或简写为   : 标签属性 = 'JS表达式'

数据只能从data流向页面

即 修改data中的数据,同时改变页面数据

双向数据绑定

语法:

v-model:value="JS表达式" 或简写为 v-model="JS表达式"

用于有value的标签

数据不仅能从 data 流向页面,还能从页面流向data

即 修改data中的数据,同时改变页面数据 ; 修改页面中的数据,同时改变data数据

数据代理

通过一个对象,代理对另一个对象中的属性的操作(读/写)

Object . defineProperty()

Object . defineProperty()可以为对象添加属性

语法:

Object . defineProperty ( 对象名 , ' 添加属性 ' , {

        get函数 ,

        set函数

})

get函数需要有返回值,作为添加属性的值

当读取所添加属性时,get函数会被调用

当修改所添加属性时,set函数会被调用

        let num = 18
        let Person = {
            name: 'zhao',
            school: 'sn'
        }
        Object.defineProperty(Person, 'age', {
            //读取age时,调用get函数,返回值为age的值
            get() {
                return num
            },
            //修改age时,调用set函数,修改值作为参数
            set(value) {
                num = value//将参数,也就是修改值,传给num
            }
        })

使用Object . defineProperty(),实现了Person对num的数据代理,

修改Person的属性值,就可以修改num

Vue中的数据代理

在Vue实例对象中,data的数据存放在Vue._data中

为了更方便地操作data中的数据,Vue通过Object . defineProperty(),

对data中数据进行了数据代理

事件处理 

事件基本使用

使用指令 v-on 来进行事件绑定

语法:v-on : 事件 = " 函数名 "

        简写:@事件 = " 函数名 "

        对应函数需要配置到Vue实例里,methods对象中

    <div>
        <button v-on:click="test01">普通按钮01</button>
    </div>
    <script>
        const vm=new Vue({
            el:'div',
            methods:{
                test01(){
                    console.log(11111)                    
                }
            }
        })
    </script>

函数参数

    <div>
        <button v-on:click="test02">普通按钮02</button>
        <button v-on:click="test03(3)">普通按钮03</button>
        <button v-on:click="test04($event,4,44)">普通按钮04</button>
    </div>
    <script>
        const vm=new Vue({
            el:'div',
            methods:{
                test02(e){
                    console.log(e)
                    console.log(2222)                    
                },
                test03(a){
                    console.log(a)
                    console.log(3333)                    
                },
                test04(e,a,b){
                    console.log(e,a,b)
                    console.log(4444)
                }
            }
        })
    </script>

 以上代码为例:

  1. 没有参数时,传入事件对象(test02)
  2. 有参数时,没有事件对象(test03)
  3. 可以用$enevt,来配置事件对象(test04)

注:

  • 不要用箭头函数,因为this将不再指向vm

事件修饰符

用于修饰事件

语法:v-on : 事件 . 事件修饰符 = " 函数名 "

        简写:@事件 . 事件修饰符 = " 函数名 "

事件修饰符作用
prevent阻止事件的默认行为
stop停止事件冒泡
once事件仅触发一次
capture使用事件捕获
selfe.target为当前元素时触发
passive默认行为立即执行,再进行回调函数

示例:

    <div class="out" @click="work">
        out
        <div class="in" @click.stop="work">
            <!--阻止事件冒泡-->
            in
        </div>
    </div>

修饰符可以连写

        <div class="in" @click.stop.prevent="work" >

按键修饰符

Vue提供了一些触发键盘事件时使用的按键修饰符

用于在按下某个按键时,触发事件

语法:v-on : 键盘事件 . 按键别名 = " 函数名 "

        简写:@键盘事件 . 按键别名 = " 函数名 "

按键别名为Vue提供,即 按键码的别名

    <!--在按下回车键时触发事件-->
    <input type="text" @keyup.enter="work">

一些常用键盘别名

  • 回车键 ==》enter
  • 删除键 ==》delete
  • 退出键 ==》esc
  • 空格键 ==》space
  • 换行键 ==》tab(最好与keyup使用)
  • 上键 ==》up
  • 下键 ==》down
  • 左键 ==》left
  • 右键 ==》right

未提供的键盘别名可以用按键Key值,使用短横线命名法

系统修饰键

如ctrl,alt,shift,meta(win键)有特殊用法

配合keyup使用:

        按下系统修饰键,再按下并放开其他键时,触发事件

配合keydown使用:

        按下系统修饰键即触发事件

定制按键别名

语法:

        Vue.config.keyCodes.自定义按键名= 键码

计算属性

概念

属性:Vue实例中,data中的都认定为属性

计算属性:所需要的属性不存在,需要通过已有属性来计算获得

计算属性需要配置到Vue实例里,computed对象中

语法:

        const vm=new Vue({
            el:'.root',
            data:{
                    属性
                },
            computed:{
                计算属性名:{
                    get函数
                }
            }
        })

通过get()函数来进行计算,需要有return返回值,作为计算属性的值

get(调用时机):

  • 在初次读取计算属性时,get()调用
  • 计算属性所依赖属性数据发生变化时,也会调用

备注:

  • 也可以调用set()函数,进行数据改动
  • get()和set()函数的this均指向Vue实例
  • 计算属性会最终会出现在Vue实例上,直接调用即可

案例:

    <div class="root">
        <input type="text" v-model:value="xing"><br>
        <input type="text" v-model:value="ming"><br>
        <p>
            全名:{{quanming}}
        </p>
    </div>
    <script>
        const vm = new Vue({
            el: '.root',
            data: {
                xing: '张',
                ming: '三'
            },
            computed: {
                quanming: {
                    get() {
                        return this.xing + this.ming
                    }
                }
            }
        })
    </script>

简写

在没有写set()函数时,可以使用简写

语法:

            computed: {
                计算属性名() {
                    函数体
                }
            }
            computed: {
                quanming() {
                    return this.xing + this.ming
                }
            }

监视属性

概念与语法

可以设置被监视的属性,当被监视属性发生改变时,会自行调用函数

写法1:

        const vm = new Vue({
            el: 'div',
            watch: {
                监视属性名: {
                    immediate: 布尔值,//默认为false,是否初始化时调用
                    deep: 布尔值,//默认为false,是否开启深度监视
                    handler(newValue, oldValue) {
                        //newValue为新值,oldValue为旧值
                        函数体
                    }
                }
            }
        })

写法2:

        vm.$watch('监视属性名', {
            immediate: 布尔值,//默认为false,是否初始化时调用
            deep: 布尔值,//默认为false,是否开启深度监视
            handler(newValue, oldValue) {
                //newValue为新值,oldValue为旧值
                函数体
            }
        })

在监视属性改变时,会调用handler()函数

深度监视 

配置deep: true时开启深度监视

Vue中watch默认不检测对象内部值的改变

开启深度监视可以检测对象内部值的改变

简写

当deep和immediate均没有配置时,可以简写

简写1:

        const vm = new Vue({
            el: 'div',
            watch: {
                监视属性名(newValue, oldValue): {
                        //newValue为新值,oldValue为旧值
                        函数体
                    }
                }
            })

简写2:

        vm.$watch('监视属性名', function(newValue, oldValue){
                //newValue为新值,oldValue为旧值
                函数体
        })

计算属性与监听属性区别

  • computed相当于watch更加简单
  • computed能完成的,watch均能完成
  • watch能完成的,computed不一定能完成(watch可进行异步操作)

绑定样式

绑定class

1.字符串写法

直接使用v-bind来绑定样式

可以绑定事件,来动态改变类名

        <div class="main" :class="mood">{{text}}</div>
<head>
    <style>
        .main {
            width: 100px;
            height: 100px;
            border: 1px solid black;
        }

        .test01 {
            background-color: aqua;
        }

    </style>
    <script src="../vue.js"></script>
</head>

<body>
    <div id="root">
        <div class="main" :class="mood">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '111',
                mood: 'test01',
            }
        })
    </script>
</body>

2.数组写法

用于绑定多个样式

可以通过动态操作数组来控制类

<head>
    <style>
        .work01 {
            font-size: 20px;
        }

        .work02 {
            color: aqua;
        }

        .work03 {
            font-style: italic;
        }
    </style>
    <script src="../vue.js"></script>
</head>

<body>
    <div id="root">
        <div class="main" :class="classArr">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '111',
                classArr: ['work01', 'work02', 'work03']
            }
        })
    </script>
</body>

3.对象写法

用于绑定多个样式,并动态决定用与不用

控制对象中的布尔值,动态决定类的用与不用

<head>
    <style>
        .work01 {
            font-size: 20px;
        }

        .work02 {
            color: aqua;
        }

        .work03 {
            font-style: italic;
        }
    </style>
    <script src="../vue.js"></script>
</head>

<body>
    <div id="root">
        <div class="main" :class="classObj">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '111',
                classObj: {
                    work01: true,
                    work02: true,
                    work03: true,
                }
            }
        })
    </script>
</body>

绑定style

1.直接绑定

直接使用v-bind来绑定样式

语法:

        : style = " { 属性(驼峰命名法) : xxx } "

        xxx为Vue示例中的data数据

    <div id="root">
        <div class="main" :style="{fontSize:fs}">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '1111',
                fs:'20px'
            }
        })
    </script>

2.对象写法

    <div id="root">
        <div class="main" :style="styleObj">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '1111',
                styleObj: {
                    fontSize: '40px',
                    backgroundColor: 'aqua'
                }
            }
        })
    </script>

3.数组写法

需要在数组中配置对象

    <div id="root">
        <div class="main" :style="styleArr">{{text}}</div>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                text: '1111',
                styleArr: [
                    {
                        fontSize: '40px',
                        backgroundColor: 'aqua'
                    },
                    {
                        color: 'blue'
                    }
                ]
            }
        })
    </script>

条件渲染

v-show

语法:

        v-show = “ 表达式 ”

        表达式值为布尔值

表达式值为false,元素被隐藏,display值为none

v-if

语法:

        v-if = “ 表达式 ”

        后可接v-else-if和v-else

        表达式值为布尔值

表达式值为false时,元素直接被删除,替换为空注释

    <div id="root">
        <div v-show="false">{{test01}}</div>
        <div v-show="true">{{test01}}</div><br>
        <div v-if="false">{{test02}}</div>
        <div v-if="true">{{test02}}</div>
    </div>
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                test01:'111',
                test02:'222'
            }
        })
    </script>

 列表渲染

v-for遍历

遍历对象

语法:

        v-for = " ( value , key) in 对象名 "  或

        v-for = "  value  in 对象名 "  

        value获取属性值,key获取属性名

    <div id="root">
        <li v-for="(value,key) in Person">
            {{value}}----{{key}}
        </li>
    </div>
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                Person:{
                    name:'Tom',
                    age:18,
                    shool:'sdau'
                }
            }
        })
    </script>

遍历数组 

语法:

        v-for = " ( value , item) in 数组名 "  或

        v-for = "  value  in 对象名 "  

        value获取值,item获取索引

    <div id="root">
        <li v-for="(value,item) in Arr">
            {{value}}----{{item}}
        </li>
    </div>
    <script>
        const vm = new Vue({
            el: '#root',
            data: {
                Arr: ['A','B','C']
            }
        })
    </script>

key的使用与作用

key的使用

在 v-for遍历 中要添加一个 key属性 

key值的选择,最好使用每条数据的唯一标识

例如ID,身份证,学号等等

如果不添加 key属性 则key值默认为数组或对象的序列号

    <div id="root">
        <li v-for="(p,item) in Person" :key="p.id">
            {{p.name}}---{{p.age}}
        </li>
    </div>
    <script>
        const vm=new Vue({
            el:'#root',
            data:{
                Person:[
                    {id:'001',name:'wu',age:18},
                    {id:'002',name:'liu',age:20},
                    {id:'003',name:'zhao',age:19},
                ]
            }
        })
    </script>

key的作用和虚拟DOM

虚拟DOM是Vue根据数据生成的

key是虚拟DOM对象的标识,可以理解成虚拟DOM对象的唯一ID

当数据发生改变时,Vue会根据新数据生成新虚拟DOM

新旧DOM会进行对比:

1.若有相同的key值:

  • 若虚拟DOM内容没有变,直接使用真实DOM生成页面
  • 若虚拟DOM内容变化,生成新真实DOM替换之前的

2.若没有相同的key值:

  • 直接建立真实DOM,生成页面 

Vue监视数据原理

基本

Vue会监视data中所有有层次的数据

被监视的对象,会添加get()和set()函数,以实现响应式

( get()和set()函数用自Object . defineProperty())

示例:

            data: {
                Person: {
                    name: 'Tom',
                    age: 18,
                    shool: 'sdau'
                },
                Arr: ['A','B','C']
            }

注:数组中的数据没有进行监视,但数组本身被监视

如上图,Arr数组有get()和set()函数

如下图,Arr[0]以及其他数据没有get()和set()函数

关于对象

对象中的数据通过 get()和set()函数 监视

对象中后追加的属性,Vue不会进行监视,即不会有get()和set()函数

如果需要监视追加的属性,则需要用到 特定API

语法:

    Vue.set(对象名,属性名,属性值)  

    另一种写法

    this.$set(对象名,属性名,属性值)

对象名指被添加属性的对象,注意 ' '

示例:给Person对象添加sex属性,属性值为 男

                addSex() {
                    //Vue.set(this.Person,'sex','男')  另一种写法
                    this.$set(this.Person, 'sex', '男')
                }

用此API添加的属性,拥有get()和set()函数,会被监视

注:

        该API,不能给vm或vm的根数据对象(data等)添加属性

关于数组

 因为Vue中数组没有get()和set()函数

所有Vue通过其他方式,来实现数组数据的响应式

在数组中修改数据,必须使用以下方法:

  1. 使用API:push(),pop(),shift(),unshift(),splice(),sort(),reverse()
  2. 使用Vue.set() 或 vm.$set().
    Vue.set(数组名,序列,修改值)  

    另一种写法

    this.$set(数组名,序列,修改值)

只有使用以上方法数组数据才可以实现响应式

当然,也可以直接修改数组本身,因为数组本身有get()和set()函数

过滤器

定义:
        对要显示的数据进行特定格式化后再显示(用于一些简单的逻辑处理)

注册过滤器(两种方式):

        Vue.filters(过滤器名称,function(){
            函数体
        })//全局过滤器

        或

        new Vue({
            filters:{
                过滤器名称(){
                    函数体
                }
            }
        })//局部过滤器

使用过滤器:

过滤器可以用于插值语法 和 v-bind指令

  • {{ xxx | 过滤器名称 }}
  • v-bind:属性 = " xxx | 过滤器名称 " 

常见内置指令

之前接触过的指令

指令作用简写
v-bind单向绑定解析表达式:xx
v-model双向数据绑定v-model="xx"
v-for遍历数组/对象/字符串
v-on绑定事件监听@
v-if / v-else条件渲染(控制节点是否存在)
v-show条件渲染(控制节点是否展示)

v-text指令

作用:
        向所在节点渲染文本

    <div id="root">
        <p>{{name}}</p>
        <p v-text="name"></p>
        <p v-text="name">1111</p>
    </div>
    <script>
        new Vue({
            el:'#root',
            data:{
                name:'zhao'
            }
        })
    </script>

注:
        v-text会替换掉节点中的内容

v-html指令

作用:
        向指定节点中渲染包含html结构的内容

        可以识别html结构

    <div id="root">
        <div v-html="test"></div>
    </div>
    <script>
        new Vue({
            el:'#root',
            data:{
                test:'<p>123456</p>'
            }
        })
    </script>

注(v-html有安全性问题):

  1.  在网站上动态渲染任意HTML非常危险,容易导致xss攻击
  2. 在可信内容上使用v-html,不用用在用户提交内容上

v-cloak指令

在网速非常慢的时候,可能会展示未经过Vue渲染的页面

因为网速慢,Vue会加载不出来

可以使用v-cloak指令配合CSS来解决

  • v-cloak没有值
  • v-cloak本质是一个属性
  • v-cloak在Vue加载完成后删除
    <style>
        [v-cloak]{
            display: none;
        }
    </style>


    <div id="root">
        <div v-cloak>111</div>
    </div>

v-once指令

  • v-once没有值
  • v-once所在节点只进行初次动态渲染
  • 初次动态渲染完成后,变为静态内容
  • 以后数据变化不会引起所在结构改变

v-pre指令

用于跳过其所在节点的编译过程

        <p v-pre>{{name}}</p>

自定义指令 

函数式

自定义指令可以通过配置到Vue实例里,directives对象中来实现

语法:

        new Vue({
            directives:{
                指令名(element,binding){
                    函数体
                }
            }
        })

其中传入的参数:

element为绑定自定义指令标签的DOM对象

binding为DOM对象的相关属性

绑定自定义指令时要加v-

    <div class="root">
        <p v-test01>1111</p>
    </div>
    <script>
        const vm=new Vue({
            el:'.root',
            directives:{
                test01(element,binding){
                    console.log(element,binding)
                }
            }
        })
    </script>

自定义指令中函数调用时机:

  • 指令与元素成功绑定时函数调用
  • 指令所在模板重新解析时函数调用

注:命名如果有多个单词,使用 - 链接

对象式

对象式自定义指令函数调用时机更加细致,会比函数时多一次调用

语法:

        new Vue({
            directives:{
                指令名:{
                    bind(element,binding){
                        函数体
                    },
                    inserted(element,binding){
                        函数体
                    },
                    update(element,binding){
                        函数体
                    }
                }
            }
        })

传入的参数同函数式

函数调用时机:

  • bind函数:指令与元素绑定成功时调用
  • inserted函数:指令所在元素被插入页面时调用
  • update函数:指令指令所在模板重新解析时调用

全局自定义指令

通过在Vue实例中配置directives对象所设置的为局部指令

全局自定义指令语法:
        Vue.directive(指令名,配置对象)

        Vue.directive(指令名,回调函数)

    <div class="root">
        <div v-test03>33</div>
        <div v-test04>44</div>
    </div>
    <script>
        Vue.directive('test03', {
            bind(element, binding) {
                console.log(element, binding)
            },
            inserted(element, binding) {
                console.log(element, binding)
            },
            update(element, binding) {
                console.log(element, binding)
            }
        })

        Vue.directive('test04',function(element, binding){
            console.log(element, binding)
        })
    </script>

生命周期

概念

生命周期 又名:生命周期回调函数,生命周期函数,生命周期钩子

生命周期本质上就是函数

Vue会在特殊时刻帮我们调用的一些特殊名称的函数

生命周期函数名字不可更改,其配置在Vue实例中,其中this指向Vue示例

示例(以mounted为例):

    <script>
        const vm = new Vue({
            el: '#root',
            mounted() {
                console.log(this)
            },
        })
    </script>

“Vue的一生”

创建(出生)

Vue刚刚创建,仅完成初始化,此时数据监测和数据代理创建还未开始;

初始化完成后,进行数据监测和数据代理创建时,会调用两个生命周期函数(不是Vue创建时)

此时 模板未解析

1.数据监测和数据代理之前,初始化后:

  • 调用beforeCreate函数
  • 此时无法通过vm访问data中的数据,methods中的方法
  • 因为数据监测和数据代理未完成

2.数据监测和数据代理之后,解析模板之前:

  • 调用created函数
  • 此时可以通过vm访问data中的数据,methods中的方法
  • 因为模板未解析,页面不会显示解析内容
挂载(生活)

挂载即把初始的真实DOM元素放到页面

在Vue解析完模板,生成虚拟DOM后,进行虚拟DOM转真实DOM

此时调用两个生命周期函数

1.解析完模板后,挂载完成前:

  • 调用beforeMount函数
  • 页面此时呈现未未经Vue编译的DOM
  • 所有对DOM的操作,最终都不会奏效

2.挂载完成后:

  • 调用mounted函数
  • 对DOM的操作有效,但尽量避免
  • 此时可以进行一些初始化操作,比如开启定时器,发送网络需求等
更新(颠覆三观)

在数据更新时,会调用两个生命周期函数

1.新虚拟DOM生成前:

  • 调用beforeUpdate函数
  • 此时Data数据是新的,但是页面没有进行更新

2.页面更新后:

  • 调用updated函数
  • 此时Data数据和页面都是新的
销毁(逝世)

Vue可以通过Vue.$destroy()来销毁

销毁时会调用两个生命周期函数

1.销毁前(回光返照):

  • 调用beforeDestroy函数
  • 此时Vue的所有配置都还可以使用,即将销毁
  • 一般在此阶段进行关闭定时器,取消订阅的收尾操作

2.销毁后(已寄):

  • 可以调用destroyed函数,但Vue以及没有

注意事项

常用生命周期钩子:

  1. mounted:初始化操作
  2. beforeDestroy:收尾操作

关于销毁Vue:

  • 销毁后自定义事件失效,但原生DOM事件依然有效
  • 一般不会使用beforeDestroy操作Data数据,因为用不到了

Vue组件化编程

组件

实现应用中局部功能代码资源集合

扒一张官网的图(下图):

在一个大盒子中可以分三个中盒子(灰色) ,中盒子又可以分成几个小盒子(深灰色)

这几个盒子中代码和资源的集合可以视为一个组件

作用:组件可以复用编码,简化项目编码

当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用

非单文件组件

特点:一个文件中包含多个组件

Vue使用组件有三步:

  1. 定义组件(创建组件)
  2. 注册组件
  3. 使用组件

定义组件(创建组件)

定义组件需要使用Vue.extend()在全局中进行定义

其中()中传入的内容与 new Vue()需要传入的几乎一模一样

区别:

  • 不写el配置:最终所有组件由vm中的el来决定服务于那个容器
  • data必须写出函数式:避免数据存在引用关系
  • 使用template配置组件结构

示例:

        //定义组件
        const Person = Vue.extend({
            template: `
            <div>
                <p>{{name}}--{{age}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'zhao',
                    age: '18'
                }
            }
        })

注册组件

1.局部注册

在Vue实例中配置components对象

        const vm = new Vue({
            el: '#Root',
            //注册局部组件
            components: {
                Person: Person
                //简写:Person
            }
        })

2.全局注册

语法:

        Vue.component('组件名', 组件)

        //注册全局组件
        Vue.component('School', School)

使用组件

在HTML中编写组件标签即可

    <div id="Root">
        <!--使用组件-->
        <Person></Person>
        <br>
        <School></School>
    </div>

示例代码

<body>
    <div id="Root">
        <!--使用组件-->
        <Person></Person>
        <br>
        <School></School>
    </div>
    <script>
        //定义组件
        const Person = Vue.extend({
            template: `
            <div>
                <p>{{name}}--{{age}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'zhao',
                    age: '18'
                }
            }
        })
        const School = Vue.extend({
            template: `
            <div>
                <p>{{name}}--{{address}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'sdau',
                    address: '泰安'
                }
            }
        })
        //注册全局组件
        Vue.component('School', School)

        const vm = new Vue({
            el: '#Root',
            //注册局部组件
            components: {
                Person: Person
                //简写:Person
            }
        })
    </script>
</body>

注意事项

组件名
  • 组件名如果由多个单词组成,使用kebab-case命名法:my-school
  • 也可以使用CamelCase命名:MySchool,但是需要Vue脚手架
  • 可以使用name配置指定组件在开发者工具中呈现的名字
        const School = Vue.extend({
            name:'xxx',
            template: `
            <div>
                <p>{{name}}--{{address}}</p>
            </div>
            `,
            data() {
                return {
                    name: 'sdau',
                    address: '泰安'
                }
            }
        })

简写

        const School = Vue.extend(options)

简写为:

        const School = options

组件其他事项

组件的嵌套

组件可以注册在组件中,实现组件的嵌套

示例:

<body>
    <div id="Root">
        <App></App>
    </div>
    <script>
        const School = Vue.extend({
            template: `
                <div>{{name}}--{{address}}</div>
            `,
            data() {
                return {
                    name: 'sdau',
                    address: '泰安'
                }
            }
        })
        const Person = Vue.extend({
            template: `
                <div>{{name}}--{{age}}</div>
            `,
            data() {
                return {
                    name: 'zhao',
                    age: 18
                }
            }
        })

        const App = Vue.extend({
            template: `
            <div>
                <Person></Person>
                <School></School>
            </div>
            `,
            components: {
                School,
                Person
            }
        })

        const vm = new Vue({
            el: '#Root',
            components: {
                App
            }
        })
    </script>
</body>

VueComponent

组件的本质是一个名为VueComponent构造函数,由Vue.extend生成

在我们使用组件时,Vue会解析并创建出组件的实例对象

即Vue会帮我们调用 new VueComponent(options)

另外,每次调用Vue.extend,返回值都是一个全新的VueComponent

关于this指向:

  • 组件配置中,this指向均是【VueComponent实例对象】
  • new Vue()配置中,this指向均是【Vue实例对象】

一个内置关系

在Vue中,组件原型对象 的 原型 为Vue示例的 原型对象

即:VueComponent.prototype.__proto__===Vue.prototype

可以让组件实例对象可以访问到Vue原型上的属性和方法

单文件组件

特点:一个文件中只包含1个组件

将非单文件组件中的多个组件进行拆分,写到多个文件中,每个文件就是一个组件

每个组件文件的后缀为 .vue

通过ES6模块化语法进行导出和引入

导出 

组件文件写法:

其中使用 export default { } 来导出组件

<template>
  <div>
    模板
  </div>
</template>

<script>
    //导出
    export default {
        Vue配置项
    };
</script>

<style>
    样式 /*可省略*/
</style>

示例:

<template>
  <div class="test">
    <h2>{{ name }}--{{ address }}</h2>
    <button @click="show">123456</button>
  </div>
</template>

<script>
export default {
  name: "SchoolName",
  data() {
    return {
      name: "sdau",
      address: "山东泰安",
    };
  },
  methods: {
    show() {
      alert("123456");
    },
  },
};
</script>

<style>
.test {
  background-color: aqua;
}
</style>

引入

引入可以使用 import···from··· 来引入

import 组件名 from "组件地址";

示例:

<template>
  <div>
    <PersonName></PersonName>
    <SchoolName></SchoolName>
  </div>
</template>

<script>
import PersonName from "./components/PersonName.vue";
import SchoolName from "./components/SchoolName.vue";
export default {
  name: "App",
  components: {
    PersonName,
    SchoolName,
  },
};
</script>

ref属性

ref属性用来给元素或子组件注册引用信息(id属性的替代者

语法:

  • 标记(示例):<h1 ref="xxx">···<h1> 或 <组件名 ref="xxx">···</组件名>
  • 获取:this.$refs.xxx

关于获取值:

  • 应用在html标签上,获取真实DOM元素
  • 应用在组件标签上,获取组件示例对象
<template>
  <div id="app">
    <img ref="img" alt="Vue logo" src="./assets/logo.png" />
    <HelloWorld ref="hello" msg="Welcome to Your Vue.js App" />
    <button @click="show">按钮</button>
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";

export default {
  name: "App",
  components: {
    HelloWorld,
  },
  methods: {
    show() {
      console.log(this.$refs);//返回对象
      console.log(this.$refs.img);//返回真实DOM
      console.log(this.$refs.hello);//返回组件示例对象
    },
  },
};
</script>

配置项props

配置项props可以让组件接受外部传来的数据

当父组件引入其他组件,父组件所需数据子组件没有时,

可以使用配置项props,通过父组件给子组件数据赋值

是一种组件间的通信方式,将数据从父组件传递给子组件

传递与接收

1.传递方(父组件):

    <子组件名 数据名=" 值 "/>

将传入的值传递给子组件

2.接收方(子组件):

  props:['数据名']

接收到父组件传递来的数据

示例:

  • App.vue(传递方,部分代码):
<template>
  <div id="app">
    <MyPerson name="zhao"/><!--传递-->
  </div>
</template>
  • MyPerson(接收方,部分代码):
<template>
  <div>
    <h1>{{ name }}--{{ age }}</h1>
  </div>
</template>

<script>
export default {
  name: "MyPerson",
  data() {
    return {
      age: 18,
    };
  },
  props:['name']//接收
};
</script>

接收的三种方法

接收数据共有三种方法:

1.只接收

  props:['name']

2.限制类型

  props:{
    name:String//限制name为字符串
  }

3.限制类型与必要性,指定默认值

  props: {
    name: {
      type: String, //限制name为字符串
      required: true, //必要性
      default: "zhao", //默认值
    },
  },

必要性:父组件必须传值

必要性与默认值一般不在一起使用,有矛盾

mixin(混入)

功能:将多个组件共用的配置,进行提取,成为一个混入对象,进行使用

定义混入

混入可以定义在另一个JS文件中

即多个组件都有的配置,提取到一个JS文件中

export const test={
    methods:{
        show(){
            console.log(this.name);
        }
    }
}

通过导出,以便于组件使用

使用混入

局部混入

在引入混入后,可以通过mixins配置项来使用混入

<template>
    <div>
        <h1>
            {{name}}--{{age}}
        </h1>
        <button @click="show">123</button>
    </div>
</template>

<script>
import {test} from './mixin.js'

export default {
    name:'MyPerson',
    data(){
        return{
            name:'zhao',
            age:18
        }
    },
    mixins:[test]
}
</script>

全局混入

可以直接在App组件中引入混入,使用Vue.mixin(xxx)来进行全局混入

import {test} from './components/mixin'
Vue.mixin(test)

插件

功能:用于增强Vue

本质:插件本质上是一个对象,其中含有一个install方法

install的第一个参数是Vue,所有插件可以实现各种功能

定义插件

对象名.install = function(Vue){
  函数体
}

使用插件

Vue.use(对象名)

scoped样式

在Vue组件化编程中,每个组件的样式也会影响到其他的组件,产生冲突

作用:

        可以让样式在局部生效,防止冲突

写法:

        <style scoped>

组件自定义事件

组件自定义事件是一种组件间的通信方式,将数据从子组件传递给父组件

绑定

绑定自定义事件都是在父组件中

方法一

直接在父组件中,给子组件绑定事件

    <MyPerson @gainP="getPersonName" />

如上,为子组件MyPerosn绑定名为gainP的事件

触发事件调用getPersonName回调函数

方法二

在生命周期钩子中绑定,用到ref属性

    <MySchool ref="dome" />

    ......

    mounted() {
      this.$refs.dome.$on("gainS", this.getSchoolName);
    },

如上,为子组件MySchool绑定名为gainS的事件

触发事件调用getSchoolName回调函数

整体代码示例(父组件):

<template>
  <div id="app">
    <p>{{ PersonName }}//{{ SchoolName }}</p>
    <MyPerson @gainP="getPersonName" />
    <MySchool ref="dome" />
  </div>
</template>

<script>
import MyPerson from "./components/MyPerson.vue";
import MySchool from "./components/MySchool.vue";
export default {
  name: "App",
  components: {
    MyPerson,
    MySchool,
  },
  data() {
    return {
      PersonName: "",
      SchoolName: "",
    };
  },
  methods: {
    getPersonName(test) {
      this.PersonName = test;
    },
    getSchoolName(test) {
      this.SchoolName = test;
    },
  },
  mounted() {
    this.$refs.dome.$on("gainS", this.getSchoolName);
  },
};
</script>

触发

触发自定义事件都是在子组件中

通过触发原生事件来触发自定义事件

语法:

      this.$emit("自定义事件名", 传入数据);

示例: 

  <button @click="givePerosnName">触发事件</button>

  ......

  methods: {
    givePerosnName() {
      this.$emit("gainP", this.Name);
    },
  },

如上,触发按钮点击事件,调用givePerosnName函数

在givePerosnName函数中触发gainP自定义事件

并且传入数据this.Name

解绑

解绑事件也是在子组件中

语法:

      this.$off("自定义事件名");

 示例:

  <button @click="relieve">解绑事件</button>

  ......

  methods: {
    relieve() {
      this.$off("gainP");
    },
  },

如上,触发按钮点击事件,调用relieve函数

在relieve函数中解绑gainP自定义事件

其他

  • 组件可以绑定原生DOM事件,但是需要 native 修饰符
  • 通过方法二来绑定事件时,回调函数要配置在methods中,或用箭头函数,这样this指向正常,为父组件。使用普通函数,this指向子组件。

全局事件总线

全局事件总线是一种组件之间的通信方式,可以用于任意组件间通信

其并非Vue所提供的方法,而是一种思路,所以写法不唯一

核心思想是找到一个各个组件均可进行通信的中介,来进行任意组件间的通信

虽然写法不唯一,但只记一种

安装全局事件总线

中介可以创建在Vue实例对象的原型上

通过生命周期钩子在Vue实例对象创建完成前安装

在main.js中

new Vue({
  render: h => h(App),
  beforeCreate() {
    Vue.prototype.$bus = this
  }
}).$mount('#app')

如上:创建名为$bus的中介

使用全局事件总线

接收

接收数据要在接收方组件中,给中介$bus绑定自定义事件

事件的回调配置在接收方组件中

  methods: {
    getPersonName(test) {
      this.PersonName = test;
    },
  },
  mounted() {
    this.$bus.$on('gainP',this.getPersonName)
  },

如上:

为$bus绑定gainP的自定义事件,函数回调在组件methods配置中

另外,最好在beforeDestroy生命周期钩子中,用$off解绑当前组件所用事件

提供

提供数据只需要在提供方触发$bus的自定义事件即可

      this.$bus.$emit("gainP", this.Name);

消息订阅与发布

消息订阅与发布是一种组件之间的通信方式,可以用于任意组件间通信

1.安装

需要安装pubsub

    npm i pubsub-js

2.引入

import pubsub from 'pubsub-js'

3.接收数据

  methods: {
    dome(){
      ......
    }
  },
  mounted(){
    this.pid=pubsub.subscribe('xxx',this.dome)//订阅消息
  }

4.提供数据

    pubsub.publish('xxx',数据)

5.取消订阅

    PubSub.unsubscribe(pid)

插槽

作用:是一种组件间的通信方式,可以让父组件向子组件指定位置插入HTML结构

           是一种将数据从父组件传递给子组件的方式

默认插槽

父组件(传递方):

设置要传给子组件的HTML结构,需要在使用组件时,在组建标签内写入

    <MyTest>
      <div>Hello World</div>
    </MyTest>
    <!--MyTest为组件名-->

子组件(接收方):

在子组件中需要进行定义插槽,确定传入HTML的插入位置

HTML结构会插入在<slot>标签的位置

<template>
  <div>
    <slot>默认内容</slot>
    <!--如果没有传入HTML,则渲染slot标签内的默认内容-->
  </div>
</template>

 具名插槽

当需要插入多个插槽时,可以用具名插槽

子组件(接收方):

子组件定义多个插槽,并配置name

<template>
  <div>
    <slot name=dome1>默认内容</slot>
    <slot name=dome2>默认内容</slot>
  </div>
</template>

父组件(传递方):

给传递的HTML结构配置solt,值为插槽的name值

    <MyDome>
      <div slot='dome1'>Hello World</div>
      <div slot='dome2'>Hello World</div>
    </MyDome>

作用域插槽

当需要使用的data数据不在传递方父组件自身,而在子组件里时

子组件(接收方):

子组件在定义插槽时,将数据传入插槽中

<template>
  <div>
    <slot :word='word'>默认内容</slot>
  </div>
</template>

<script>
export default {
  name: "TheDome",
  data() {
    return {
      word: "Hello World",
    };
  },
};
</script>

 父组件(传递方):

必须使用<template>标签,并在标签内配置scope

    <TheDome>
      <template scope="scopeData"> 
        <div>{{ scopeData.word }}</div>
      </template>
    </TheDome>

如上, 子组件传递的数据,以对象形式,给了scopeData

nextTick

语法:

      this.$nextTick(()=>{
        回调函数
      })

作用:

        在下一次DOM更新后执行回调

        用于改变数据后,基于更新后的新DOM进行操作时

Vue封装的过渡与动画

如图所示:

Vue封装的过渡与动画分为进入(enter)离开(leave)

进入和离开又分为起点过程中(active)终点(to)

类名

  • 元素进入:
  1. v-enter:进入起点
  2. v-enter-active:进入过程
  3. v-leave-to:进入终点
  • 元素离开:
  1. v-leave:离开起点
  2. v-leave-active:离开过程
  3. v-leave-to:离开终点

在Vue中添加过渡与动画,需要给Vue所配置的类添加样式

过渡/动画

过渡的添加,需要六种样式全部配置

.v-enter,
.v-leave-to {
  transform: translateX(-100%);
}
.v-enter-to,
.v-leave {
  transform: translateX(0);
}
.v-enter-active,
.v-leave-active {
  transition: 1s;
}

使用 <transition>标签将要进行过度的元素进行包裹

    <transition>
      <div class="dome" v-show="xxx">111</div>
    </transition>

被包裹的元素会自行执行样式中所写的过渡

动画的添加,只需要配置v-enter-active和v-leave-active即可,其他同过渡

.v-enter-active {
  animation: 1s test01;
}
.v-leave-active {
  animation: 1s test01 reverse;
}
@keyframes test01 {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

多个元素过渡

相同样式:

多个元素,共用一个过渡,需要用 <transition-group>标签包裹

并且每个元素都要指定key值

    <transition-group> 
      <div class="dome" v-show="xxx" key="1">111</div>
      <div class="dome" v-show="xxx" key="2">222</div>
    </transition-group>

不同样式:

多个元素,不共用一个过渡

要为<transition>标签配置name属性

    <transition name='test01'>
      <div class="dome" v-show="xxx">111</div>
    </transition>

在写样式时,类名中的v换成name值

例:

.test01-enter,
.test01-leave-to {
  transform: translateX(-100%);
}

Vue中的Ajax

Vue脚手架配置代理

配置代理可以解决开发环境 Ajax 跨域问题

在vue.config.js中添加配置

  devServer:{
    proxy:'xxx'//xxx为服务器基础路径
  }

优点:

  • 配置简单,请求资源时直接发给前端

缺点:

  • 不能配置多个代理
  • 不能控制请求是否走代理

在vue.config.js中添加配置

devServer:{
  proxy:{
    '/dome':{
      //匹配所有以'/dome'为开头的请求路径
      target:'xxx',//代理目标的基础路径
      changeOrigin:true,
      pathRewrite:{'^/dome':''}
    }
  }
}

changeOrigin值为true时,服务器收到请求头中host为服务器基础路径

changeOrigin值为flase时,服务器收到请求头中host为本地基础路径

默认值为true

Vuex

Vuex简介

概念

        专门在 Vue 中实现集中式状态(数据)管理的一个 Vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通信方式,且适用于任意组件间通信。

        简而言之,当多个组件共享数据时,使用Vuex来管理这些共享数据

原理

如图,在Vuex中,封装有actions,mutations,state三个对象,由store统一管理(图未显示)

其中,actions用于响应组件,mutations用于修改数据,state用于存储数据

当组件调用dispatch时,会经过相当于门卫的actions,进行一些逻辑处理

当然也可以绕过actions,调用commit直接到mutations,进行相应数据的修改

在action中调用commit,会到mutations中进行相应数据的修改

搭建Vuex环境

安装Vuex

搭建Vuex环境首先需要在终端中安装Vuex

    npm i vuex

创建store文件

创建路径:src/store/index.js

文件内容:

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

//准备actions对象————响应组件中用户动作
const actions = {}
//准备mutations对象————修改state中的数据
const mutations = {}
//准备state对象————存储数据
const state = {}

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state
})

传入store配置

在main.js中创建vm时,传入store配置

import Vue from 'vue'
import App from './App.vue'
//引入store
import store from './store' 

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

基本使用

组件

在组件中,通过调用dispatch,来触发action

语法:

      this.$store.dispatch('函数名', 数据);

示例: 

  methods: {
    add() {
      this.$store.dispatch('add', this.number);
      //将会触发actions中的add函数,并把this.number传给actions
    },
  },

也可以通过调用commit,直接到 mutations中

  methods: {
    add() {
      this.$store.commit('add', this.number);
      //将会触发mutations中的add函数,并把this.number传给mutations
    },
  },

actions

在actions中,可以进行一些逻辑处理(如if判断等),然后调用commit

//准备action对象————响应组件中用户动作
const actions = {
    add(context, value) {
        context.commit('ADD', value)
    },
}

在组件中,触发actions的add函数,函数用两个参数

  • context封装有dispatch函数,commit函数,state对象等
  • context中dispatch函数用于调用其他actions中的函数
  • context中commit函数用于触发mutations
  • context中state数据对象,用于进行逻辑处理
  • value则是组件传入的数据

关于commit函数

        context.commit('ADD', value)

将会触发mutations中的ADD函数,并把tvalue传给mutations

mutations

在mutations,进行state的数据处理

//准备mutations对象————修改state中的数据
const mutations = {
    ADD(state, value) {
        state.sum += value
    },
}

其中的ADD函数传入两个参数:state数据对象和actions传入的value

state

state用于存储数据

//准备state对象————存储数据
const state = {
    sum: 0,
}

回到组件

在组件中,使用 $store.state 即可使用Vuex中的数据

    <div>和为:{{ $store.state.sum }}</div>

getters的使用

概念:

        当state中的数据需要经过加工后再使用时,可以使用getters进行加工

        类似于计算属性,可以当作Vuex中的计算属性

使用:

在index.js中追加getters配置

//准备getters对象————加工数据
const getters = {
    multiple(state) {
        return state.sum * 10
    }
}

并暴露出去

//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})

在组件中,使用 $store.getters 即可使用Vuex中加工后的数据

    <div>10倍和为:{{ $store.getters.multiple }}</div>

map方法

mapState方法

用于映射state中的数据,将其映射为组件中的计算属性

如下传统方法,在组件中,一直使用 $store.state获取数据显得十分麻烦

    <div>和为:{{ $store.state.sum }}</div>
    <div>学校:{{ $store.state.school }}</div>
    <div>姓名:{{ $store.state.name }}</div>

可以直接借助mapState生成计算属性

语法(两种写法)

    //对象写法
    mapState({ sum: "sum", school: "school", name: "name" }),

    //或

    //数组写法
    mapState(["sum", "school", "name"]),

内容

  mounted(){
    console.log(mapState({sum:'sum',school:'school',name:'name'}));
  }

其输出为一个对象,且都是函数,故需要放在计算属性中

使用

import { mapState } from "vuex";
  computed: {
    //对象写法
    ...mapState({ sum: "sum", school: "school", name: "name" }),
  },

  computed: {
    //数组写法
    ...mapState(["sum", "school", "name"]),
  },
    <div>和为:{{ sum }}</div>
    <div>学校:{{ school }}</div>
    <div>姓名:{{ name }}</div>

mapGetters方法

用于映射getters中的数据,将其映射为组件中的计算属性

用法与mapState方法无区别

import { mapGetters } from "vuex";
  computed: {
    //对象写法
    ...mapGetters({ multiple: "multiple" }),
  },

  computed: {
    //数组写法
    ...mapGetters(["multiple"]),
  },
    <div>10倍和为:{{ multiple }}</div>

mapActions方法

如下传统方法,使用$store.dispatch与actions对话,显得特**烦人

  methods: {
    add() {
      this.$store.dispatch("add", this.number);
    },
    sub() {
      this.$store.dispatch("sub", this.number);
    },
    addOdd() {
      this.$store.dispatch("addOdd", this.number);
    },
    addWait() {
      this.$store.dispatch("addWait", this.number);
    },
  },

可以靠mapActions方法来解决,省略$store.dispatc,实现组件与actions对话

其语法与mapState无异

内容

  mounted() {
    console.log(mapActions({ add: "add", sub: "sub" }));
  },

 其输出也为一个对象,且都是函数,需要放在methods配置中

使用

注意:需要手动在绑定事件时传参!

import { mapActions } from "vuex";
  methods: {
    //对象写法
    ...mapActions({
      add: "add",
      sub: "sub",
      addOdd: "addOdd",
      addWait: "addWait",
    }),
  },

  methods: {
    //数组写法
    ...mapActions(["add", "sub", "addOdd", "addWait"]),
  },
    <button @click="add(number)">+</button>
    <button @click="sub(number)">-</button>
    <button @click="addOdd(number)">为奇数时加</button>
    <button @click="addWait(number)">一秒钟后加</button>

mapMutations方法

省略$store.commit,实现组件与actions对话

用法与mapActions方法无异

import { mapMutations } from "vuex";
  methods: {
    //对象写法
    ...mapMutations({
      add: "add",
      sub: "sub",
      addOdd: "addOdd",
      addWait: "addWait",
    }),
  },

  methods: {
    //数组写法
    ...mapMutations(["add", "sub", "addOdd", "addWait"]),
  },

模块化和命名空间

让代码更好维护,让多种数据分类更加明确

修改store

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

const Count01 = {
    namespaced: true,//开启命名空间
    actions: {...},
    mutations: {...},
    state: {...},
    getters: {...}
}

const Count02 = {
    namespaced: true,//开启命名空间
    actions: {...},
    mutations: {...},
    state: {...},
    getters: {...}
}


//创建并暴露store
export default new Vuex.Store({
    modules: {
        Count01,
        Count02
    },
})

不使用map方法

<template>
  <div>
    <!--读取state数据-->
    <p>数:{{ $store.state.Count01.number }}</p>
    <br />
    <!--读取getters数据-->
    <p>10倍为:{{ $store.getters['Count01/multiple'] }}</p>
    <br />
    <button @click="add(1)">+1</button><br /><br />
    <button @click="addIf(1)">为奇数+1</button>
  </div>
</template>

<script>
export default {
  name: "TheCount01",
  methods: {
    add(value) {
      //调用commit
      this.$store.commit("Count01/ADD", value);
    },
    addIf(value) {
      //调用dispatch
      this.$store.dispatch("Count01/addIf", value);
    },
  },
};
</script>

使用map方法

<script>
import { mapState, mapGetters, mapActions, mapMutations } from "vuex";
export default {
  name: "TheCount02",
  computed: {
    //读取state数据
    ...mapState("Count02", ["number"]),
    //读取getters数据
    ...mapGetters("Count02", ["multiple"]),
  },
  methods: {
    //调用dispatch
    ...mapActions("Count02", ["addIf"]),
    //调用commit
    ...mapMutations("Count02", ["ADD"]),
  },
};
</script>

路由

路由的理解

路由就是一组 key-value 的对应关系

多个路由由路由器进行管理

Vue中通过vue-router插件来实现路由

基本路由

创建router文件

创建路径:src/router/index.js

示例:

//引入VueRouter
import VueRouter from "vue-router";
//引入组件
import TheTest01 from '../pages/TheTest01'
import TheTest02 from '../pages/TheTest02'

//创建并暴露router实例对象
export default new VueRouter({
    routes: [
        {
            path: '/test01',
            component: TheTest01
        },
        {
            path: '/test02',
            component: TheTest02
        },

    ]
})

创建的router示例对象中,需要配置一个routers数组,数组存储对象

path值为路由中的key值,component值为组件

组件需要进行引入

安装与应用vue-router插件

搭建vue-router环境首先需要在终端中安装vue-router

npm i vue-router

另外,需要在main.js中应用插件

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

//引入VueRouter
import VueRouter from 'vue-router'
//应用VueRouter
Vue.use(VueRouter)
//引入路由器
import router from './router/index'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router: router
}).$mount('#app')

注意在main.js中引入路由器,即router文件

实现组件切换

    <div>
      <div class="a">
        <router-link active-class="active" to="/test01">test01</router-link>
      </div>
      <div class="a">
        <router-link active-class="active" to="/test02">test02</router-link>
      </div>
    </div>
    <div>
      <router-view></router-view>
    </div>
  </div>

使用<router-link>标签,实现切换,必需配置to以确定切换的页面

active-class可以配置类名

<router-view>标签,用于指定切换页面的展示位置

<router-view>标签本质是<a>标签

注意点

  1. 路由组件一般存放在pages文件夹中,一般组件存放在components文件夹中
  2. 切换后,未显示的组件默认被销毁,展示时在进行挂载
  3. 每个组件都有独属于自身的$router属性,存储自己的路由信息
  4. 整个应用只有一个routers,可以通过组件的$router属性获取

多级路由

多级路由就是实现路由的嵌套

在router文件中,使用children配置项

    routes: [
        {
            path: '/dome01',
            component: TheDome01
        },
        {
            path: '/dome02',
            component: TheDome02,
            //通过children配置子级路由
            children:[
                {
                    path:'childdome01',//注意不要写/
                    component:ChildDome01
                },
                {
                    path:'childdome02',//注意不要写/
                    component:ChildDome02
                },
            ]
        },
    ]

在组件(子组件)中实现跳转要写完整路径

    <div>
      <router-link active-class="active" to="/dome02/childdome01"
        >child01</router-link
      >
    </div>
    <div>
      <router-link active-class="active" to="/dome02/childdome02"
        >child02</router-link
      >
    </div>

路由中的query参数

query参数是以路由为媒介,给路由组件传递数据的

传递方

        <router-link
          active-class="active"
          :to="{
            //注意引号!!
            path: '/test02',
            query: {
              words: 'test02',
            },
          }"
          >test02</router-link
        >

在<router-link>标签中,to配置项写成对象形式,里面写有query对象

query对象中的参数既是传递的数据

另外,也可以使用to的字符串写法,无需配置query,同样可以达到目的

        <router-link active-class="active" to="/test01?words=test01"
          >test01</router-link
        >
        <router-link active-class="active" :to="`/test01?words=${test01}`"
          >test01</router-link
        >

接收方(路由组件)

使用$route.query.xxx来接收数据

  <div>
    <p>{{$route.query.words}}</p>
  </div>

虽说是路由中的query参数,但是路由只是一个媒介

传递方组件的数据通过路由传递给路由组件

在此过程中,router文件不需要进行任何修改

命名路由

作用:用于简化路由的跳转

          主要在多级路由中展现其作用

在router文件中,使用name配置项

            children:[
                {
                    path:'childdome01',//注意不要写/
                    component:ChildDome01,
                    name:'child1'//命名
                },
            ]

组件中,在<router-link>标签中,to配置项同添加query时一样,需要写成对象形式

      <router-link active-class="active" :to="{
        name:'child1'
      }"
        >child01</router-link
      >

可以配合query配置传递参数

路由中的params参数

params参数和query参数一样,是以路由为媒介,给路由组件传递数据的

路由器修改

在router文件中,要事先使用占位符声明接收参数

            children:[
                {
                    path:'childdome01/:words',//使用占位符什么接收params参数
                    component:ChildDome01,
                    name:'child1'
                },
            ]

如上,意为会传递一个名为words的参数

传递方

字符串写法:

直接使用 / 进行拼接

      <router-link active-class="active" :to="`/dome02/childdome02/text2`"
        >child02</router-link
      >

对象写法:

配置params,并且不可以用path,而是用name

      <router-link active-class="active" :to="{
        name:'child1',
        params:{
          words:'text1'
        }
      }"
        >child01</router-link
      >

接收方(路由组件)

使用$route.params.xxx来接收数据

  <div class="dome">
    <p>{{$route.params.words}}</p>
  </div>

路由中的props配置

作用:配合query和params参数,让路由组件更方便收到参数

在接收方路由组件中,以$route.params.xxx或$route.query.xxx来接收数据显得特别麻烦

可以在router文件中配置props解决

方法一:

                {
                    path:'childdome01/:words',
                    component:ChildDome01,
                    name:'child1',
                    
                    //值为对象,对象中所有key-value组合通过props传给路由组件
                    props:{name:'test'}
                },

方法二:

                {
                    path:'childdome01/:words',//使用占位符什么接收params参数
                    component:ChildDome01,
                    name:'child1',
                    
                    //值为布尔值,为true时,把路由收到的所有params参数传给路由组件
                    props:true
                },

方法三:

                {
                    path:'childdome01/:words',//使用占位符什么接收params参数
                    component:ChildDome01,
                    name:'child1',
                    
                    //为函数,返回值传给路由组件
                    props(route){
                        return{
                            test:route.query.words
                        }
                    }
                },

replace属性

浏览器的历史记录有两种写入方式:

  • push:追加历史记录
  • replace:覆盖历史记录

追加上的历史记录可以通过点击上图 ← 返回

历史记录被覆盖后就无法返回

路由跳转默认为push,即可以返回

在<router-link>标签中添加replace属性,可以改为replace

      <router-link active-class="active" to="/dome02" replace>dome02</router-link>

编程式路由

作用:无需使用<router-link>标签,即可实现路由的跳转

  methods:{
    show01(){
      this.$router.push({
        path:'/test01'
      })
    },
    show02(){
      this.$router.replace({
        path:'/test02'
      })
    },
  }

通过this.$router.push和this.$router.replace两个API来实现

分别以追加历史记录和覆盖历史记录的方式进行跳转路由

其里面的配置同to的对象写法

另外,如果配置name,则name为路由组件的name

其他API:

  • this.$router.forward() 前进
  • this.$router.back()     后退
  • this.$router.go()         传入一个整数,正值则前进,负值后退,跳转数为传入数

缓存路由组件

路由组件在切走后会被销毁

缓存路由组件可以使路由组件在切走后保持挂载,不被销毁

将<router-view>标签包裹在<keep-alive>标签中即可

      <keep-alive include="test01">
        <router-view></router-view>
      </keep-alive>

<keep-alive>标签中的include属性为保持挂载的路由组件

不配置默认全部保持挂载,有多个时写成对象的形式

路由中特有的生命周期钩子

路由中有两个特有的生命周期钩子

  • activated()        路由组件激活时触发

  • deactivated()    路由组件切走后触发

注意:只有在<keep-alive>标签包裹下有用!

路由守卫

作用:对路由进行权限控制

分类:全局守卫,独享守卫,组件内守卫

全局守卫

全局守卫分为前置守卫和后置守卫

router文件更改

使用全局守卫,需要用到router的API,故不可用以下方式暴露

export default new VueRouter({
    ......
})

 应改为如下代码

const router = new VueRouter({
    ......
})

export default router

meta配置

每个路由可以配置meta,这是一个可以让程序员自由传值的配置,meta是一个对象

使用路由守卫需要判断路由是否需要进行权限控制

可以在meta中添加一个布尔值,用于判断

const router = new VueRouter({
    routes: [
        {
            path: '/Person',
            component: ThePerson,
            meta: { isAuth: true }
        },
    ]
})

全局守卫用法

1.全局前置守卫

初始化时执行,以及每次路由切换前执行

router.beforeEach((to, from, next) => {
    if (to.meta.isAuth) {//判断路由是否需要进行权限控制
        if (localStorage.getItem('test') === 'xxx') {//判断权限,权限控制的具体规则
            next()//放行
        } else {
            console.log('无权限');
        }
    } else {
        next()//放行
    }
})

全局前置守卫使用router.beforeEach(),传值为一个函数

函数传三个参数:to, from, next

to, from为对象,分别存储着 切换到的路由组件信息 和 切换走的路由组件信息

next为一个函数,调用时放行,进行路由的切换

2.全局后置守卫

初始化时执行,以及每次路由切换后执行

router.afterEach((to, from)=>{
    console.log(to, from);
})

一般情况下不用,只需要传递to和from两个参数

因为以及切换完,无需使用next

独享守卫

独享守卫是单独配置在某个路由中的

用于判断某个路由是否需要权限控制

除使用的API外,其他与前置守卫无区别

const router = new VueRouter({
    routes: [
        {
            path: '/Person',
            component: ThePerson,
            meta: { isAuth: true },
            beforeEnter(to, from, next) {
                if (to.meta.isAuth) {//判断路由是否需要进行权限控制
                    if (localStorage.getItem('test') === 'xxx') {
                        //判断权限,权限控制的具体规则
                        next()//放行
                    } else {
                        console.log('无权限');
                    }
                } else {
                    next()//放行
                }
            }
        },
    ]
})

组件内守卫

组件内守卫分为进入守卫和离开守卫

进入守卫,在通过路由进入某个路由组件时调用

离开守卫,在通过路由离开某个路由组件时调用

需要写在路由组件中,用法同全局守卫

    //进入守卫
    beforeRouteEnter(to, from, next){
      console.log(to, from, next);
    },
    //离开守卫
    beforeRouteLeave (to, from, next) {
      console.log(to, from, next);
    }

路由器两种工作模式

对于一个url来说,#后面的内容就是其hash值

hash值不会包含着http请求中,即hash值不会传递给服务器

路由器有两种工作模式:

1.hash模式

  • 地址中永远带着 #
  • 地址可能被第三方App标记为不合法
  • 兼容性高

2.history模式

  • 地址中不带着 #
  • 兼容性比hash模式差
  • 部署后需要后端修404问题

路由默认为hash模式

修改为history模式如下代码:

const router = new VueRouter({
    mode:'history',
    .......
})

http://www.kler.cn/a/412248.html

相关文章:

  • 【Git】常用命令汇总
  • 关于SpringBoot集成Kafka
  • 【滑动窗口】找到字符串中所有字母异位词
  • [STM32]从零开始的STM32 FreeRTOS移植教程
  • 2024.11.26总结
  • VisionPro 机器视觉案例 之 凹点检测
  • 目录 《Qt精通之路》
  • 离线状态下引入Echarts
  • 路由传参、搜索、多选框勾选、新增/编辑表单复用
  • IDEA2023版本配置项目全局编码
  • 数星星 (C++ 树状数组)
  • uni-app运行 安卓模拟器 MuMu模拟器
  • Cesium教程03_加载b3dm高度
  • faiss VS ChromaDB
  • DINO-X:一种用于开放世界目标检测与理解的统一视觉模型
  • Python 爬虫入门教程:从零构建你的第一个网络爬虫
  • 第六届国际科技创新学术交流大会暨信息技术与计算机应用学术会议(ITCA 2024)
  • Qwen2.5系列——大模型测评常用benchmark对应原始论文介绍(一)——通用任务
  • 基于 Qt 和 GStreamer 的环境中构建播放器
  • Linux中的用户与组的常用命令
  • 【经典论文阅读】NeRF(神经辐射场,neural radiance fields)
  • 基于springboot旅游管理系统源码和论文
  • 从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
  • 一文掌握如何用python开发小程序
  • MyBatis框架-动态SQL-XML中的常用标签+特殊字符在XML中的显示
  • VSCode【下载】【安装】【汉化】【配置C++环境(超快)】(Windows环境)