• Vue3基础


    Vue3基础

    1、Vue入门

    1.1、Vue介绍

    Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同是,Vue被设计为可以自底向上逐层应用。Vue核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。目前最新版本是Vue3.x。
    Vue3.x发布于2020年9月19日,在Vue2.x的基础上面进行了一些优化,对TypeScript有更好的支持。Vue3.x的语法和Vue2.x非常相似,如果已经会用Vue2.x,那么Vue3.x会非常简单。

    Vue官网地址:https://cn.vuejs.org/
    Vue3.x github地址:https://github.com/vuejs/vue-next
    Vue3.x文档地址:https://v3.cn.vuejs.org/

    1.2、Vue官方脚手架

    注意:安装脚手架创建项目之前,必须安装Nodejs,推荐安装nodejs-16.X稳定版本
    文档地址:https://v3.cn.vuejs.org/guide/installation.html
    Vue-cli地址:https://cli.vuejs.org/
    Vite地址:https://github.com/vitejs/vite

    1.3、创建项目

    通过Vue-cli脚手架工具可以快速搭建Vue项目,目前Vue官方提供了2个脚手架,Vue-cli和Vite。

    1.3.1、安装vue-cli
    yarn global add @vue/cli
    #OR
    npm install -g @vue/cli
    #OR
    cnpm install -g @vue/cli
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1.3.2、安装cnpm
    npm install -g cnpm --registry=https://registry.npm.taobao.org
    
    • 1
    1.3.3、安装yarn
    npm i -g yarn
    
    • 1
    1.3.4、vue-cli创建项目
    vue create hello-vue
    yarn serve
    
    • 1
    • 2
    1.3.5、vite创建项目
    npm init vite-app 
    cd 
    npm install
    npm run dev
    
    • 1
    • 2
    • 3
    • 4
    1.3.6、yarn创建项目
    yarn create vite-app 
    cd 
    yarn
    yarn dev
    
    • 1
    • 2
    • 3
    • 4

    1.4、目录结构

    在这里插入图片描述

    1.5、开发工具及插件

    1.5.1、VSCode
    1.5.2、Voar

    2、Vue基础

    2.1、绑定数据

    2.1.1、main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    import './index.css'
    
    createApp(App).mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2.1.2、App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2.2、绑定html

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2.3、绑定属性

    2.3.1、业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2.3.2、template模板
    1、绑定属性(v-bind)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2、绑定属性
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    3、自定义属性
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    4、v-bind动态参数
    ...
    
    • 1

    :attributeName将被动的评估为JavaScript表达式,且其评估值将用作参数的最终值。如果组件实例具有一个数据属性attributeName,其值为href,则此绑定将等效于v-bind:href。

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.4、数据循环

    2.4.1、v-for
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.4.2、循环属性
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.4.3、循环对象
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.4.4、循环嵌套
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.5、事件模板语法类和样式

    2.5.1、事件方法入门
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.5.2、v-bind绑定Class

    当v-bind与class一起使用时,Vue提供了特殊的增强功能style。除了字符串外,表达式可以求值为对象或数组。

    v-bind:class绑定字符串
    业务逻辑
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    class绑定多个动态属性
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    
    
    • 1
    • 2
    • 3
    • 4
    Css
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    数组语法
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    Css
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    数组语法结合三目运算
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    Css
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    v-bind:style绑定内联样式
    第一种绑定方式

    业务逻辑

    data(){
    	return {
            activeColor: "red",
            fontSize: 30
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    template模板

    • 1
    第二种绑定方式

    业务逻辑

    data(){
        return {
            styleObject: {
                color: "red",
                fontSize: "13px"
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    template模板

    • 1
    第三种绑定方式 数组方式

    业务逻辑

    data(){
        return {
            baseStyle: {
                color: "Orange",
                fontSize: "13px"
            },
            orangeStyle: {
                width: "100px",
                height: "100px",
                background: "orange"
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    template模板

    • 1
    2.5.3、案例

    循环数据,第一个数据高亮显示

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    Css
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.5.4、事件方法
    方法传值
    业务逻辑
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    方法调用方法
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    2.5.5、事件对象

    有时需要在内联语句处理程序中访问原始DOM事件。可使用特殊$event变量将其传递给方法

    单个参数
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    多个参数
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.5.6、多个事件处理程序

    在事件处理程序中使用逗号分隔多个事件处理程序

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.5.7、事件修饰符

    Vue中阻止冒泡阻止默认行为,可通过事件对象event.preventDefault()或event.stopPropagation()实现,还可以通过事件修饰符实现。
    Vue提供了很多修饰符

    .stop
    .prevent
    .capture
    .self
    .once
    .passiv
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    stopPropagation

    
    
    • 1

    preventDefault

    
    
    • 1

    stopPropagation And preventDefault

    
    
    • 1
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    2.5.8、按键修饰符

    监听键盘事件时,通常需要检查特定的键。Vue允许在监听事件时v-on或@在监听关键事件时添加按键修饰符

    
    
    • 1

    Vue为最常用的键提供别名

    .enter
    .tab
    .delete(同时捕获"删除"和"退格"键)
    .esc
    .space
    .up
    .down
    .left
    .right
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.6、Dom和表单操作

    2.6.1、人员登记系统
    2.6.2、Dom操作
    原生js获取数据
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    ref获取数据
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    2.6.3、双向数据绑定

    MVVM就是常说的双向数据绑定,Vue就是一个MVVM的框架。M表示model,V表示View。在MVVM的框架中model改变会影响视图view,view视图改变反过来影响model。注意:双向数据绑定主要用于表单中

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    2.6.4、input双向数据绑定
    2.6.5、select双向数据绑定
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    2.6.6、radio双向数据绑定
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.6.7、textarea双向数据绑定
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    2.6.8、checkbox双向数据绑定
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.7、JavaScript表达式

    2.7.1、JavaScript表达式
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.8、条件判断

    2.8.1、v-if
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.8.2、v-if v-else
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    template模板

    :v-else元素必须紧跟在带v-if或者v-else-if的元素后面,否则将不会被识别

    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.8.3、v-else-if

    :与v-else相似,v-else-if元素必须紧跟v-if或v-else-if元素

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    2.8.4、在template元素上使用v-if条件渲染分组

    v-if是一个指令,必须添加到一个元素上。若需切换多个元素呢?此时可把一个template元素当作不可见的包裹元素,并在上面使用v-if。最终的渲染结果将不包含template元素。

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.8.5、v-show

    用于根条件展示元素的选项是v-show指令。

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.8.6、v-if和v-show区别

    v-if是dom操作,v-show只是css的显示隐藏,一般来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要非常频繁的切换,则使用v-show较好。

    2.9、计算属性

    2.9.1、计算属性入门

    在模板中表达式非常便利,但实际上只能用于简单操作。
    模板是为了描述视图的结构。在模板中放入太多的逻辑会让模板过重且难以维护。这就是Vue.js将绑定表达式限制为一个表达式。若需要多于一个表达式的逻辑,应当使用计算属性。

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    2.9.2、计算属性实现数据筛选
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.10、watch监听

    Vue.js提供了一个方法watch,它用于观察Vue实例上的数据变动。当一些数据需要根据其它数据变化时,watch很诱人——特别是如果来自AngularJS。通常更好的办法时使用计算属性而不是命令式的watch回调。

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3、Vue集成

    3.1、集成sass和scss

    3.1.1、安装sass-loader node-sass
    npm install -D sass sass-loader node-sass
    
    • 1
    3.1.2、style中配置sass/scss

    :lang可以配置scss,scope表示这里写的css只有当前组件有效

    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    css
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.2、待办事项(案例)

    实现完整toDoList(待办事项)以及类似京东App搜索缓存数据功能。

    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    template模板(v-show)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    template模板(v-if)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    css
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.3、模块化及封装Storage

    3.3.1、Storage
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    css
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    3.3.2、模块化
    storage.js

    :创建src/modules/storage.js

    const storage = {
        set(key,value){
            localStorage.setItem(key,JSON.stringify(value));
        },
        get(key){
            return JSON.parse(localStorage.getItem(key));
        },
        remove(key){
            localStorage.removeItem(key);
        }
    }
    
    //暴露storage
    export default storage;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    业务逻辑
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    template模板
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    css
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    4、组件

    组件可以拓展HTML标签,解决HTML标签构建应用的不足,Vue项目由一个一个的组件组成。

    4.1、Home组件

    定义组件

    创建src/components/Home.vue

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    挂载组件

    在src/App.vue中引入Home组件

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.2、Header组件

    定义组件

    创建src/components/Header.vue

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    使用组件
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4.3、用户组件

    定义组件
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    使用组件
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    5、父子组件

    5.1、父组件给子组件传值

    5.1.1、父组件调用子组件时传值
    Header.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    获取父组件数据

    Header.vue

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    5.1.2、父组件实例传递给子组件

    :home="this"表示将当前组件传递给子组件,子组件就可以获取父组件的数据和执行父组件的方法。

    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    Header.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    5.2、Props验证

    可以为组件的props指定验证要求,如类型验证,若有需求没有被满足,则Vue会在浏览器控制台中警告。在开发一个会被别人用到的组件时尤其有帮助。

    props: {
        //基础的类型检查('null'和'undefined'会通过任何类型验证)
        propA: Number,
        //多个可能的类型
        propB: [String,Number],
        //必填的字符串
        propC: {
            type: String,
            required: true
        },
        //带有默认值的数字
        propD: {
            type: Number,
            default: 100
        },
        //带有默认值的对象
        propE: {
            type: Object,
            //对象或数组默认值必须从一个工厂函数获取
            default: function(){
                return {message: 'hello'}
            }
        },
        //自定义验证函数
        propF: {
            validator: function(value){
                //这个值必须匹配下列字符串中的一个
                return ['success','warning','danger'].indexOf(value) !== -1
            }
        },
        //具有默认值的函数
        propG: {
            type: Function,
            //与对象或数组默认值不同,这不是一个工厂函数--这是一个用作默认值的函数
            default: function(){
                return 'Default function'
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    5.3、单向数据流

    所有的prop都使得其父子prop之间形成了一个单向下行绑定:父级prop的更新会向下流动到子组件中,但是反过来则不行。
    这样会防止从子组件意外变更父级组件的状态,从而导致应用的数据流向难以理解。
    另外,每次父组件发生变更时,子组件中所有的prop都将会刷新为最新的值。这意味着不应该在一个子组件内部改变prop。
    若需这样做,Vue会在浏览器控制台中发出警告。

    5.4、父组件主动获取子组件的数据和执行子组件方法

    5.4.1、调用子组件时定义一个ref
    #ref名称可自定义
    
    
    • 1
    • 2
    5.4.2、父组件主动获取子组件数据
    this.$refs.header.属性
    
    • 1
    5.4.3、父组件主动执行子组将方法
    this.$refs.header.方法
    
    • 1
    5.4.4、案例
    App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    Header.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    5.5、子组件主动获取父组件的数据和执行父组件的方法

    5.5.1、子组件主动获取父组件的数据
    this.$parent.数据
    
    • 1
    5.5.2、子组件主动获取父组件的数据
    this.$parent.方法
    
    • 1
    5.5.3、案例
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    Header.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    5.6、自定义组件事件以及mitt实现非父子组件传值

    5.6.1、组件自定义事件实现子组件给父组件传值

    :Vue官方推荐始终使用kebab-case的事件名。

    子组件Header.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    父组件Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    5.6.2、组件自定义事件对传值进行验证
    子组件Login.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    父组件Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    5.6.3、第三方插件mitt实现非父子组件传值
    https://github.com/developit/mitt
    Vue3.x以后从实例中移除了$on、$off和$once方法,仍然时现有API的一部分,只能实现子组件触发父组件的方法。
    
    • 1
    • 2
    安装mitt模块
    npm install --save mitt
    
    • 1
    新建model/event.js
    // 引入mitt
    import mitt from 'mitt'
    
    // 创建mitt实例
    const event = mitt();
    
    // 暴露mitt
    export default event;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    组件Header.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    组件Login.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    6、自定义组件使用v-model实现双向数据绑定及slot、Prop的Attribute继承、禁用Attribute继承

    6.1、自定义组件使用v-model实现双向数据绑定

    v-model主要用于表单的双向数据绑定,v-model实现自定义组件的双向数据绑定。

    6.1.1、单个v-model数据绑定

    默认情况下,组件上的v-model使用modelValue作为prop和update:modelValue作为事件。可通过向v-model传递参数来修改这些名称

    子组件Input.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    父组件Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    子组件将需要一个foo prop并发出update:foo要同步的事件

    input.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    6.1.2、多个v-model绑定

    利用特定的prop和事件为目标的能力,v-model参数中,可以在单个组件实例上创建多个v-model绑定。
    每个v-model将同步到不同的prop,而不需要在组件中添加额外的选项。

    UserName.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    6.2、自定义组件slots

    Vue实现了一套内容分发的API,设计灵感来自于Web Components规范草案,将元素作为承载分发内容的出口。

    6.2.1、自定义一个按钮组件
    Button.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    6.2.2、调用这个组件

    slot还允许在自定义组件里面传入任意的HTML标签或其它组件

    Button.vue
    
    
    • 1
    Home.vue
        提交
        

    icon 搜索
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    slot还可以绑定父组件的数据

    Home.vue
        提交
        

    icon搜索 {{msg}}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    6.2.3、slots默认值
    
    
    • 1
    
    
    • 1

    6.3、非Prop的Attribute继承

    一个非prop的attribute是指传向一个组件,但该组件并没有相应props或emits定义的attribute。包括class、style和id属性。

    Button.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    Home.vue
    取消
    
    • 1
    6.3.1、当组件返回单个根节点时,非prop attribute将自动添加到根节点的attribute中
    Home.vue
    取消
    
    • 1
    6.3.2、同样的规则适用于事件监听器
    6.3.3、完整示例

    6.4、自定义Attribute继承

    不希望组件的根元素继承attribute,可在组件的选项中设置inheritAttrs:false。禁用attribute继承的情况是需要将attribute应用于根节点之外的其它元素。
    通过将inheritAttes选项设置为false,可以在访问组件的$attris property,该property包括组件props和emits property中未包含的所有属性(如class、style、v-on监听器等)。

    DatePicker.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    6.5、多个根节点上的Attribute继承

    与单个根节点组件不同,具有多个根节点的组件不具有自动attribute回退行为。如果未显式绑定$attrs,将发出运行时警告。

    
    
    • 1
    //这将发出警告
    app.component('custom-layout',{
        template:`
        	
    ` })
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    //没有警告,$attrs被传递到
    元素 app.component('custom-layout',{ template:`
    ` })
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    7、生命周期函数、动态组件keep-alive、this.$nextTick

    7.1、生命周期函数

    在这里插入图片描述

    beforeCreate

    在实例化之后,数据观测(data observer)和event/watcher事件配置之前被调用。

    created

    在实例创建完成后被立即调用。在这一步,实例已完成以下配置:数据观测(data observer)property和方法的运算,watch/event事件回调。然而,挂载阶段还没开始,$el property目前尚不可用。

    beforeMount

    在挂载开始之前被调用:相关的render函数首次被调用。

    mounted

    实例被挂载后调用,这时Vue.createApp({}).mount()被新创建的vm. e l 替 换 了 。 如 果 根 实 例 挂 载 到 了 一 个 文 档 内 的 元 素 上 , 当 m o u n t e d 被 调 用 时 v m . el替换了。如果根实例挂载到了一个文档内的元素上,当mounted被调用时vm. elmountedvm.el也在文档内。
    注意mounted不会保证所有的子组件都一起被挂载。如果希望等到整个视图都渲染完毕,可在mounted内部使用vm.$nextTick:

        mounted(){
            this.$nextTick(function(){
                //仅在渲染整个视图后运行的代码
            })
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    beforeUpdate

    数据更新时调用,发生在虚拟DOM打补丁之前。这里合适在更新之前访问现有的DOM,如手动移除已添加的事件监听器。

    updated

    由于事件更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
    当这个钩子被调用时,组件DOM已经更新,所以可以执行依赖于DOM的操作。在大多数情况下,应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或侦听器取而代之。
    注意:updated不会保证所有的子组件也都一起被重绘。希望等到整个视图重绘完毕,可以在updated里使用vm.$nextTick:

        updated(){
            this.$nextTick(function(){
                //仅在渲染整个视图后运行的代码
            })
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    activated

    被keep-alive缓存的组件停用时调用

    deactivated

    被keep-alive缓存的组件停用时调用

    beforeUnmount

    在卸载组件实例之前调用,在这个阶段,实例仍然是完全正常的

    unmounted

    卸载组件实例后调用,调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦听器都被移除,所有子组件实例被卸载

    LifeCycle.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    7.2、动态组件keep-alive

    当在这些组件之间切换时,有时会想保持这些组件的状态,以避免反复重复渲染导致的性能问题,此时候就用keep-alive。
    在不同路由切换时想保持组件的状态可以使用keep-alive

    
    	
    
    
    • 1
    • 2
    • 3
    keep-alive结合Tab切换
    LifeCycle.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    7.3、this.$nextTick

    Vue可把获取DOM节点的代码放在mounted里面,如果要在渲染完成数据后获取DOM节点就需要用到this.$nextTick。

        mounted(){
            this.$nextTick(function(){
                //仅在渲染整个视图后运行的代码
            })
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    LifeCycle.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133

    8、全局绑定属性、使用Axios和fetchJsonp请求真实api接口数据、函数防抖实现百度搜索

    8.1、使用Axios请求API接口数据

    8.1.1源码

    https://github.com/axios/axios

    8.1.2、安装
    npm install axios --save
    yarn add axios
    
    • 1
    • 2
    8.1.3、引入使用
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    8.1.4、全局绑定Axios
    main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    import './index.css'
    
    import Axios  from "axios";
    
    const app = createApp(App)
    
    app.config.globalProperties.axios = Axios;
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    8.1.5、全局绑定Storage

    新建src/modules/storage.js

    storage.js
    const storage = {
        set(key,value){
            localStorage.setItem(key,JSON.stringify(value));
        },
        get(key){
            return JSON.parse(localStorage.getItem(key));
        },
        remove(key){
            localStorage.removeItem(key);
        }
    }
    //暴露storage
    export default storage;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    import './index.css'
    
    // 全局引入
    import Axios  from "axios";
    import Storage from "./modules/storage"
    
    const app = createApp(App)
    // 全局绑定
    app.config.globalProperties.axios = Axios; //this.Axios
    app.config.globalProperties.Storage= Storage; //this.Storage
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    浏览器

    在这里插入图片描述

    8.2、使用fetch-jsonp请求jsonp接口

    axios不支持jsonp请求,可用jsonp来请求数据可以使用fetch-jsonp这个模块。

    8.2.1、源码

    https://github.com/camsong/fetch-jsonp

    8.2.2、安装
    npm install fetch-jsonp --save
    
    • 1
    8.2.3、引入使用
    main.js全局引入
    import { createApp } from 'vue'
    import App from './App.vue'
    import './index.css'
    
    // 全局引入
    import Axios  from "axios";
    import Storage from "./modules/storage"
    import fetchJsonp from 'fetch-jsonp';
    
    const app = createApp(App)
    // 全局绑定
    app.config.globalProperties.axios = Axios; //this.Axios
    app.config.globalProperties.Storage= Storage; //this.Storage
    app.config.globalProperties.fetchJsonp=fetchJsonp; //this.fetchJsonp
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    Baidu.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    8.2.4、防抖功能
    Baidu.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    9、mixin实现组件功能的复用

    9.1、mixin介绍

    混入(mixin)提供了一种非常灵活的方式,来分发Vue组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被"混入"进入该组件本身的选项。

    baseMixin.js

    新建src/mixin/baseMixin.js

    const baseMixn = {
        data(){
            return{
                apiUrl:"http://www.itying.con",
                msg:"baseMixin",
            }
        },
        methods:{
            success(){
                console.log("成功");
            }
        }
    }
    
    export default baseMixin;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    9.2、mixin的选项合并

    当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行合并。

    9.2.1、数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    9.3、全局配置mixin

    为Vue应用程序全局应用mixin

    baseMinix.js
    const baseMixin = {
        data(){
            return{
                apiUrl:"http://www.itying.con",
                msg:"baseMixin",
            }
        },
        methods:{
            success(){
                console.log("成功");
            }
        }
    }
    
    export default baseMixin;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    import './index.css'
    
    // 全局引入
    import Axios  from "axios";
    import Storage from "./modules/storage"
    import fetchJsonp from 'fetch-jsonp';
    import baseMixin from "./mixin/baseMixin"
    
    const app = createApp(App)
    // 全局绑定
    app.config.globalProperties.axios = Axios; //this.Axios
    app.config.globalProperties.Storage= Storage; //this.Storage
    app.config.globalProperties.fetchJsonp=fetchJsonp; //this.fetchJsonp
    app.mixin(baseMixin);
    
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    10、Teleport、使用Teleport实现一个模态对话框的组件

    10.1、Teleport

    Vue3.x中的组件模板属于该组件,需要把模板的内容移动到当前组件之外的DOM中,这时就可以使用Teleport。
    表示teleport内包含的内容显示到body中

    
    内容
    
    
    • 1
    • 2
    • 3
    
    内容
    
    
    • 1
    • 2
    • 3

    10.2、使用Teleport实现一个模态对话框的组件

    Modal.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    11、Composition API

    11.1、Composition API简介

    Composition API也叫组合式API,是Vue3.x的新特性。
    通过创建Vue组件,可以将接口的可重复部分及其功能提却到可宠用的代码段中。仅此一项就可以使应用程序在可维护性和灵活性方面走的更远。然而,经验已证明,光靠这一点是不够的,尤其是当应用程序变得非常大时——几百个组件。在处理如此大的应用程序时,共享和重用代码变得尤为重要。

    没有Composition API之前Vue相关业务代码需要配置到option的特定区域,中小型项目是没有问题的,但是在大型项目中会导致后期的维护性比较复杂,同时代码可复用性不高。Vue3.x中的composition-api就是为了解决这个问题而生的。

    composition-api提供了以下几个函数

    • setup
    • ref
    • reactive
    • watchEffect
    • watch
    • computed
    • toRefs
    • 生命周期的hooks

    11.2、setup组件选项

    新的setup组件选项在创建组件之前执行,一旦props被解析,并充当合成API的入口。
    提示
    由于在执行setup时尚未创建组件实例,因此在setup选项中没有this。这意味着,除了props之外,将无法访问组件中声明的任何属性——本地状态、计算属性或方法。
    使用setup函数时,将接受两个参数

    11.2.1、Props
    11.2.2、上下文
    11.2.3、setup组件的property
    11.2.4、ref reactive以及setup结合模板
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    11.2.5、使用this

    11.3、toRefs解构响应式对象数据

    把一个响应式对象转换成普通对象,该普通对象的每个property都是一个ref,和响应式对象property一一对应。

    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    11.4、computed计算属性

    Login.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    11.5、readonly"深层"的只读代理

    传入一个对象(响应式或普通)或ref,返回一个原始对象的只读代理。一个只读代理是"深层的",对象内部任何嵌套的属性也都是只读的。

    Login.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    11.6、watch与watchEffect区别

    对比watchEffect,watch允许

    • 懒执行,就是说仅在侦听的源变更时才执行回调;
    • 更明确哪些状态的改变会触发侦听器重新运行;
    • 访问侦听状态变化前后的值;
    11.6.1、watchEffect

    在响应式地跟踪其依赖项时立即运行一个函数,并在更改依赖项时重新运行它。

    Search.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    11.6.2、watch
    Search.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    11.7、组合式api生命周期钩子

    可以通过在生命周期钩子前面加上"on"来访问组件的生命周期钩子。
    下表包含如何在 setup () 内部调用生命周期钩子:

    选项式 APIHook inside setup
    beforeCreateNot needed*
    createdNot needed*
    beforeMountonBeforeMount
    mountedonMounted
    beforeUpdateonBeforeUpdate
    updatedonUpdated
    beforeUnmountonBeforeUnmount
    unmountedonUnmounted
    errorCapturedonErrorCaptured
    renderTrackedonRenderTracked
    renderTriggeredonRenderTriggered
    activatedonActivated
    deactivatedonDeactivated

    11.8、props

    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    Search.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    12、Provider Inject

    当需要将数据从父组件传递到子组件时,使用props。有一些深嵌套的组件,只需要来自深嵌套子组件中父组件的某些内容。在这种情况下,仍需要将props传递到整个组件链中,这可能会很烦人。
    对于这种情况,可以使用provide和inject对父组件可作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深。这个特性有两个部分:父组件有一个provide选项来提供数据,子组件有一个inject选项来开始使用这个数据。
    在这里插入图片描述

    12.1、组合式api写法

    12.1.1、Provide

    在setup()中使用provide时,首先从的那个Vue显示导入provide方法。能够调用provide时来定义每个property。
    provide函数允许通过两个参数定义property

    • property的name(类型)
    • property的value

    使用自定义组件,提供的值可以按如下方式重构,provide、inject实现父子组件传值时,父子组件数据改变会相互影响。

    App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    Location.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    12.1.2、Inject
    App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    Home.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    Location.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    13、集成Typescript

    13.1、项目创建

    https://v3.cn.vuejs.org/guide/typescript-support.html#项目创建

    Vue CLI 可以生成使用 TypeScript 的新项目
    # 1. Install Vue CLI, 如果尚未安装
    npm install --global @vue/cli@next
    # 2. 创建一个新项目, 选择 "Manually select features" 选项
    vue create vue-demo
    # 3. 如果已经有一个不存在TypeScript的 Vue CLI项目,请添加适当的 Vue CLI插件
    vue add typescript
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述

    13.2、集成scss

    npm install -D sass-loader node-sass
    
    • 1

    13.3、添加组件

    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    vue.config.js
    const { defineConfig } = require('@vue/cli-service')
    module.exports = defineConfig({
      transpileDependencies: true,
      lintOnSave: false,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    13.4、Typescript与组合式API使用

    User.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    User.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    14、路由、路由配置

    14.1、Vue3.x中的路由

    路由可以让应用程序根据用户输入的不同地址动态挂载不同的组件。
    https://router.vuejs.org/

    • router-link

      如何使用自定义组件来创建链接,而不是使用常规a标签。router-link这允许 Vue Router 在不重新加载页面的情况下更改 URL,处理 URL 生成及其编码。

    • router-view

      router-view将显示与 url 对应的组件。可以把它放在任何地方以适应布局。

    14.2、路由的基本配置

    14.2.1、安装路由模块
    npm install vue-router@4
    npm install vue-router@next --save
    
    yarn add vue-router@4
    
    • 1
    • 2
    • 3
    • 4
    14.2.2、新建组件
    Home组件、User组件、News组件
    
    • 1
    14.2.3、配置路由

    新建src/routes.ts配置路由

    配置路由routes.ts
    import { createRouter, createWebHashHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import User from "./components/User.vue";
    
    // 配置路由
    const router = createRouter({
        history: createWebHashHistory(),
        routes: [
            { path: "/", component: Home },
            { path: "/news", component: News },
            { path: "/user", component: User },
    
        ]
    })
    
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    挂载路由main.ts
    import { createApp } from 'vue'
    import App from './App.vue'
    // 引入路由
    import route from "./routes"
    
    const app = createApp(App)
    
    // 挂载路由
    app.use(route)
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    渲染组件App.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    访问
    http://localhost:8080/#/
    http://localhost:8080/#/user
    http://localhost:8080/#/news
    
    • 1
    • 2
    • 3
    router-link

    App.vue

    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    14.3、路由、路由配置、动态路由、get传值、路由跳转

    14.3.1、动态路由
    NewsContent.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    路由跳转
    import { createRouter, createWebHashHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import User from "./components/User.vue";
    import NewsContent from "./components/NewsContent.vue";
    
    // 配置路由
    const router = createRouter({
        history: createWebHashHistory(),
        routes: [
            { path: "/", component: Home },
            { path: "/news", component: News },
            { path: "/user", component: User },
            // 配置动态路由
            {path: "/newcontent/:aid", component: NewsContent},
    
        ]
    })
    // 暴露组件
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    获取动态路由的传值

    NewsContent.vue

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    14.3.2、Get传值
    route.ts
    import { createRouter, createWebHashHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import User from "./components/User.vue";
    import NewsContent from "./components/NewsContent.vue";
    
    // 配置路由
    const router = createRouter({
        history: createWebHashHistory(),
        routes: [
            { path: "/", component: Home, alias: "/home" },
            { path: "/news", component: News },
            { path: "/user", component: User },
            // 配置动态路由
            { path: "/newscontent", component: NewsContent },
    
        ]
    })
    // 暴露组件
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    NewContent.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    14.4、路由编程式导航(Js跳转路由)

    NewsContent.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    14.5、路由、路由模式、命名路由、路由重定向、路由别名

    14.5.1、路由HTML5 History模式和hash模式
    hash模式

    src/routes.ts

    import { createRouter, createWebHashHistory } from "vue-router";
    
    // 配置路由
    const router = createRouter({
        // hash模式
        history: createWebHashHistory(),
        routes: [
        	...
        ]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    HTML5 History
    import { createRouter, createWebHistory } from "vue-router";
    
    // 配置路由
    const router = createRouter({
        // HTML History模式
        history: createWebHistory(),
        routes: [
        	...
        ]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    注意:开启HTML5 History模式后,发布到服务器需要配置伪静态
    https://router.vuejs.org/guide/essentials/history-mode.html

    14.6、命名路由

    通过一个名称来标识一个路由显得方便一些,特别是在链接一个路由,或者是执行一些跳转时。可以创建Router实例时,在routes配置中给某个路由设置名称。

    const router = new VueRouter({
        routes: [{
            path: '/user/:userId',
            name: 'user',
            component: User
        }]
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    要链接到一个命名路由,可以给route-link的to属性传递一个对象

    User
    
    • 1

    跟代码调用router.push()是一回事

    this.$router.push({name:'user',params:{userId:123}})
    
    • 1

    这两种方式都会把路由导航到/user/123路径。

    this.$router.push({name:'newscontent',query:{aid:567}})
    
    • 1
    命名路由跳转
    routes.ts
    import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import User from "./components/User.vue";
    import NewsContent from "./components/NewsContent.vue";
    
    // 配置路由
    const router = createRouter({
        // hash模式
        history: createWebHashHistory(),
        // HTML History模式
        // history: createWebHistory(),
        routes: [
            { path: "/", component: Home, alias: "/home" },
            { path: "/news", component: News , name:"news"},
            { path: "/user", component: User },
            // 配置动态路由
            { path: "/newscontent",name:"newscontent", component: NewsContent },
    
        ]
    })
    // 暴露组件
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    App.vue
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    命名路由传值
    UserInfo.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    routes.ts
    import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import User from "./components/User.vue";
    import NewsContent from "./components/NewsContent.vue";
    import UserInfo from "./components/UserInfo.vue";
    
    // 配置路由
    const router = createRouter({
        // hash模式
        history: createWebHashHistory(),
        // HTML History模式
        // history: createWebHistory(),
        routes: [
            { path: "/", component: Home, alias: "/home" },
            { path: "/news", component: News, name: "news" },
            { path: "/user", component: User },
            // 配置动态路由
            { path: "/newscontent", name: "newscontent", component: NewsContent },
            { path: "/userinfo/:id", name: "userinfo", component: UserInfo },
    
        ]
    })
    // 暴露组件
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    User.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    News.vue
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    14.7、路由重定向

    重定向在routes配置中完成,要从重定向/a到/b

    const routes = [{path:'/home',redirect: {name:'homepage'} }]
    
    • 1

    重定向也可以针对命名路由

    const routes = [{path:'/home',redirect:{name:'homepage'}}]
    
    • 1

    甚至使用函数进行动态重定向

    
    
    • 1
    rotues.ts
    import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import User from "./components/User.vue";
    import NewsContent from "./components/NewsContent.vue";
    import UserInfo from "./components/UserInfo.vue";
    
    // 配置路由
    const router = createRouter({
        // hash模式
        history: createWebHashHistory(),
        // HTML History模式
        // history: createWebHistory(),
        routes: [
            {path:'',redirect:"/home"}, //路由重定向
            { path: "/", component: Home, alias: "/home" },
            { path: "/news", component: News, name: "news" },
            { path: "/user", component: User },
            // 配置动态路由
            { path: "/newscontent", name: "newscontent", component: NewsContent },
            { path: "/userinfo/:id", name: "userinfo", component: UserInfo },
    
        ]
    })
    // 暴露组件
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    访问
    http://localhost:8080
    
    • 1

    14.8、路由别名

    重定向是指用户访问时/home,URL将被替换/,然后与匹配/。
    别名/as/home表示用户访问时/home,URL保持不变/home,但将被匹配,就像用户正在访问时一样。
    以上内容可以在路由配置中表示为

    const routes = [{path:'/',component:Homepage,alias:'/home'}]
    
    • 1

    别名可以自由地将UI结构映射到任意URL,而不受配置的嵌套结构约束。使别名以a开头,/以使路径在嵌套路由中使绝对的。甚至可以将两者结合起来,并为数组提供多个别名

    const routes = [
     {
        path: '/users',
        component: UsersLayout,
        children: [
        	//- /users
        	//- /users/list
        	//- /people
        	{path:'',component:UsersList,alias:['/people','list']},
        ],
     },
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如果路径包含参数,请确保将其包含在任何绝对别名中

    rotues.ts
    import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import User from "./components/User.vue";
    import NewsContent from "./components/NewsContent.vue";
    import UserInfo from "./components/UserInfo.vue";
    
    // 配置路由
    const router = createRouter({
        // hash模式
        history: createWebHashHistory(),
        // HTML History模式
        // history: createWebHistory(),
        routes: [
            { path: '', redirect: "/home" }, //路由重定向
            { path: "/", component: Home, alias: "/home" },
            { path: "/news", component: News, name: "news", alias: ["/n", "/c"] },
            { path: "/user", component: User, alias: "/people" },
            // 配置动态路由
            { path: "/newscontent", name: "newscontent", component: NewsContent },
            { path: "/userinfo/:id", name: "userinfo", alias: "/u/:id", component: UserInfo },
    
        ]
    })
    // 暴露组件
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    访问
    http://localhost:8080/#/people
    http://localhost:8080/#/n
    http://localhost:8080/#/c
    http://localhost:8080/#/u/123
    
    • 1
    • 2
    • 3
    • 4

    14.9、嵌套路由

    User.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    UserList.vue

    src/component/User/UserList.vue

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    UserAdd.vue

    src/component/User/UserAdd.vue

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    News.vue
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    NewsList.vue

    src/component/News/NewsList.vue

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    NewsAdd.vue

    src/component/News/NewsAdd.vue

    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    routes.ts
    import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
    
    // 引入组件
    import Home from "./components/Home.vue";
    import News from "./components/News.vue";
    import NewsList from "./components/News/NewsList.vue";
    import NewsAdd from "./components/News/NewsAdd.vue";
    
    import User from "./components/User.vue";
    import UserAdd from "./components/User/UserAdd.vue";
    import UserList from "./components/User/UserList.vue";
    
    // 配置路由
    const router = createRouter({
        // hash模式
        history: createWebHashHistory(),
        // HTML History模式
        // history: createWebHistory(),
        routes: [
            { path: '', redirect: "/home" },
            { path: '/home', component: Home },
            {
                path: '/news', component: News, name: "news",
                children: [
                    { path: '', redirect: "/news/newslist" },
                    { path: 'newslist', component: NewsList },
                    { path: 'newsadd', component: NewsAdd },
                ]
            },
            {
                path: '/user', component: User,
                children: [
                    { path: '', redirect: "/user/userlist" },
                    { path: 'userlist', component: UserList },
                    { path: 'useradd', component: UserAdd }
                ]
            },
    
        ]
    })
    // 暴露组件
    export default router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    App.vue
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    15、状态管理模式Vuex

    15.1、Vuex

    Vuex是一个转为Vue.js应用程序开发的状态管理模式。
    官网:https://vuex.vuejs.org/
    主要功能

    • Vuex可实现Vue中不同组件之间的状态共享(解决了不同组件之间的数据共享)
    • 可实现组件里面数据的持久化

    Vuex的几个核心概念

    • State
    • Getters
    • Mutations
    • Actions
    • Modules

    15.2、Vuex基本使用

    15.2.1、安装依赖
    npm install vuex@next --save
    yarn add vuex@next --save
    
    • 1
    • 2
    15.2.2、store.js

    src目录下新建一个vuex的文件夹,vuex文件夹里面新建一个store.js

    import { createStore } from "vuex";
    
    const store = createStore({
        state(){
            return {
                count:1,
            }
        }
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    15.2.3、main.ts
    import { createApp } from 'vue'
    import App from './App.vue'
    // 引入路由
    import route from "./routes"
    // 引入状态
    import store from "./vuex/store";
    
    const app = createApp(App)
    
    // 挂载路由
    app.use(route)
    // 挂载状态
    app.use(store)
    app.mount('#app')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    15.3、State

    15.3.1、获取State的方法
    1、第一种获取State的方法(不推荐)

    用到组件里面引入的store,然后计算属性里面获取

    computed: {
        count(){
            return store.state.count
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2、第二种获取State的方法

    由于全局配置了Vuex app.use(store),所以直接可以通过下面方法获取store里面的值

    computed:{
        count(){
            return this.$store.state.count
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    3、第三种获取State的方法,通过mapState助手
    computed:{
        ...mapState({
            count:(state)=>state.count,
            list:(state)=>state.title,
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    示例
    store.js
    import { createStore } from "vuex";
    
    const store = createStore({
        // 定义数据
        state() {
            return {
                count: 1,
                title:['马总','李总','刘总'],
            }
        },
        // 定义方法
        mutations: {
            incCount(state) {
                state.count++;
            },
            setCount(state, num) {
                state.count = num;
            }
        },
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    15.4、Getter

    15.4.1、定义Getter
    15.4.2、访问Getter方法
    第一种访问Getter方法

    Getter会暴露为store.getters对象,可以以属性的形式访问这些值

    store.getters.doneTodos
    
    • 1
    第二种访问Getter方法
    computed:{
        doneTodosCount(){
            return this.$store.getters.doneTodoCount
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    第三种访问Getter方法,通过mapGetters辅助函数
    computed:{
        //使用对象展开运算符讲getter混入computed对象中
        ...mapGetters([
            'doneTodoCount',
            'anotherGetter',
        ])
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果想将一个getter属性另取一个名字,使用对象形式

    ...mapGetters([ "reverseMsg","num"])
    ...mapGetters({
        num:"num",
        reverseMsg:"reverseMsg"
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    示例
    store.js
    import { createStore } from "vuex";
    
    const store = createStore({
        // 定义数据
        state() {
            return {
                count: 1,
                title: ['马总', '李总', '刘总'],
                msg: "你好vue",
            }
        },
        // 定义方法
        mutations: {
            incCount(state) {
                state.count++;
            },
            setCount(state, num) {
                state.count = num;
            },
            setMsg(state, msg) {
                state.msg = msg;
            },
        },
        // 计算属性
        getters: {
            reverseMsg(state) {
                return state.msg.split("").reverse().join("");
            },
            num(state) {
                return state.count + 10;
            }
        }
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    15.5、Mutations

    更改Vuex的store中的状态的唯一方法是提交mutation。Vuex中的mutation非常类似于事件:每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)。这个回调函数实际进行状态更改的地方,并且会接受state作为第一个参数。

    15.5.1、定义Mutations触发Mutations里面的方法
    const store = createStore({
        state:{
            count:1
        },
        mutations:{
            increment(state){
                state.count++
            }
        }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    触发mutations里面的方法

    store.commit('increment')
    
    • 1

    15.6、Actions

    Action类似于mutation,不同在于

    • Action提交的是mutation,而不是直接变更状态
    • Action可以包含任意异步操作
    15.6.1、定义Action
    import { createStore } from "vuex";
    
    const store = createStore({
        // 定义数据
        state() {
            return {
                count: 1,
            }
        },
        // 定义方法
        mutations: {
            incCount(state) {
                state.count++;
            },
        },
        //执行mutations里面的方法,异步操作放在actions
        actions:{
            addCount(context){
                // 执行mutations的addCount方法
                context.commit("incCount")
            },
        }
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    另一种写法

        actions:{
            addCount({commit}){
                // 执行mutations的addCount方法
                context.commit("incCount")
            },
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    15.6.2、分发Action(触发Action中的方法)
    store.dispatch("addCount")
    
    • 1

    mutation必须同步执行,Action就不受约束,可以在actions内部执行异步操作

    store.js
    import { createStore } from "vuex";
    
    const store = createStore({
        // 定义数据
        state() {
            return {
                count: 1,
                title: ['马总', '李总', '刘总'],
                msg: "你好vue",
            }
        },
        // 定义方法
        mutations: {
            incCount(state) {
                state.count++;
            },
            setCount(state, num) {
                state.count = num;
            },
            setMsg(state, msg) {
                state.msg = msg;
            },
        },
        // 计算属性
        getters: {
            reverseMsg(state) {
                return state.msg.split("").reverse().join("");
            },
            num(state) {
                return state.count + 10;
            }
        },
        //执行mutations里面的方法,异步操作放在actions
        actions:{
            addCount(context){
                // 执行mutations的addCount方法
                context.commit("incCount")
            },
            // setMsg(context,msg){
            //     setTimeout(() => {
                      // 执行mutations的setMsg方法
            //         context.commit("setMsg",mgs);
            //     }, 1000);
            // }
            
            // 结构解析
            setMsg({commit},msg){
                setTimeout(() => {
                    commit("setMsg",msg);
                }, 1000);
            }
    
        }
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    15.7、Modules

    由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store对象就有可能变得相当臃肿。
    为了解决这个问题,Vuex允许将store分割成模块(module)。每个模块拥有自己的state、mutation、action、getter,甚至是嵌套子模块——从上至下进行同样方式的分割。

    const moduleA={
        state:()=>({...}),
        mutations:{...},
        actions:{...},
        getters:{...}
    }
    const moduleB={
        state:()=>({...}),
        mutations:{...},
        actions:{...},
    }
    const store=createStore({
        modules:{
            a:moduleA,
            b:moduleB
        }
    })
    store.state.a //->moduleA
    store.state.b //->moduleB
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    示例
    userStore.js
    let userStore = {
          // 定义数据
          state() {
            return {
                count: 1,
                title: ['马总', '李总', '刘总'],
                msg: "你好vue",
            }
        },
        // 定义方法
        mutations: {
            incCount(state) {
                state.count++;
            },
            setCount(state, num) {
                state.count = num;
            },
            setMsg(state, msg) {
                state.msg = msg;
            },
        },
        // 计算属性
        getters: {
            reverseMsg(state) {
                return state.msg.split("").reverse().join("");
            },
            num(state) {
                return state.count + 10;
            }
        },
        //执行mutations里面的方法,异步操作放在actions
        actions:{
            addCount(context){
                // 执行mutations的addCount方法
                context.commit("incCount")
            },
            // setMsg(context,msg){
            //     setTimeout(() => {
                      // 执行mutations的setMsg方法
            //         context.commit("setMsg",mgs);
            //     }, 1000);
            // }
            
            // 结构解析
            setMsg({commit},msg){
                setTimeout(() => {
                    commit("setMsg",msg);
                }, 1000);
            }
    
        }
    }
    
    export default userStore
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    newsStore.js
    let newsStore={
          // 定义数据
          state() {
            return {
                list:["民生","娱乐"],
            }
        },
    }
    
    export default newsStore
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    store.js
    import { createStore } from "vuex";
    import newsStore from "./newsStore";
    import userStore from "./userStore";
    
    const store = createStore({
        modules:{
            "user":userStore,
            "news":newsStore,
        }
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    15.8、结合Composition api

    组合式api中没有this.$store,可以使用userStore来替代

    export default {
        setup(){
            const store = useStore()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    15.8.1、组合式api中访问state和getters
    export default {
        setup(){
            const store = useStore()
            return {
                count: computed(()=>store.state.count),
                double:computed(()=>store.getters.double)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    15.8.2、组合式api中访问Mutations and Actions
    export default{
        setup(){
            const store = useStore()
            return {
                addCount:()=>store.commit('increment'),
                asyncIncrement:()=>store.dispatch('asyncIncrement')
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    15.9、Typescript中使用Vuex

    https://vuex.vuejs.org/guide/typescript-support.html

    首先需要在Vue项目中集成typescript

    vue add typescript
    
    • 1

    提示:如果配置完ts后调用this.$store有警告信息,重启vscode,或者安装Vue3的插件后重启vscode重试。

    15.9.1、修改store.js为store.ts
    15.9.2、配置store.ts中的代码

    Vuex与Typescript一起使用时,必须声明自己的模块扩充

    import { ComponentCustomProperties } from "vue";
    import { Store, createStore } from 'vuex';
    
    declare module '@vue/runtime-core' {
        interface State {
            count: number,
            title: string[],
            msg: string,
        }
    
        interface ComponentCustomProperties {
            $store: Store
        }
    }
    
    const store = createStore({
        // 定义数据
        state() {
            return {
                count: 1,
                title: ['马总', '李总', '刘总'],
                msg: "你好vue",
            }
        },
        // 定义方法
        mutations: {
            incCount(state: any) {
                state.count++;
            },
            setCount(state: any, num: number) {
                state.count = num;
            },
            setMsg(state: any, msg: string) {
                state.msg = msg;
            },
        },
        // 计算属性
        getters: {
            reverseMsg(state: any) {
                return state.msg.split("").reverse().join("");
            },
            num(state: any) {
                return state.count + 10;
            }
        },
        //执行mutations里面的方法,异步操作放在actions
        actions: {
            addCount(context) {
                // 执行mutations的addCount方法
                context.commit("incCount")
            },
            // setMsg(context,msg){
            //     setTimeout(() => {
            // 执行mutations的setMsg方法
            //         context.commit("setMsg",mgs);
            //     }, 1000);
            // }
    
            // 结构解析
            setMsg({ commit }, msg: string) {
                setTimeout(() => {
                    commit("setMsg", msg);
                }, 1000);
            }
    
        }
    })
    
    export default store
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    Home.vue
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    16、Serverless

    16.1、Serverless架构介绍

    Serverless又名无服务器,所谓无服务器并非是说不需要依赖和依靠服务器等资源,而是开发者再也不用过多考虑服务器的问题,可以更专注在产品代码上,狭义的Serverless是Fass和Bass组成。

    无人点餐、无人收银系统是一个颠覆传统行业(中西餐厅、奶茶店、酒吧、火锅店、食堂、KTV)的智能点单系统。无人点单,手机支付,免费收银员,免点餐员,免硬件,免布线的一套智能管理系统。

    在这里插入图片描述

    16.1.1、概念

    Serverless是一种软件系统架构的思想和方法,它不是软件框架、类库或者工具。与传统框架的不同之处在于,完全由第三方管理,由事件触发,存在于无状态(Stateless)、暂存(可能只存在于一次调用的过程中)计算容器内。构建无服务应用程序意味着开发者可以专注在产品代码上,而无须管理和操作云端或本地的服务器或运行时(运行时通俗讲就是运行环境,如nodejs环境、java环境、php环境)。Serverless真正做到部署应用无需涉及基础设施的建设,自动构建、部署和启动服务。

    通俗的讲:Serverless是构建和运行软件时不需要关心服务器的一种架构思想。老程序员都用过虚拟机,刚开始学Serverless时可以把它理解为虚拟机的升级版本。

    虚拟主机已经是快被淘汰的上一代产物。云计算涌现出了很多改变传统IT架构和运维方式的新技术,比如虚拟机、容器、微服务,无论这些技术应用在哪些场景,降低成本、提升效率是云服务永恒的主题。Serverless的出现真正的解决了降低成本、提升效率的问题。它真正做到了弹性伸缩、高并发、按需收费、备份容灾、日志监控等。

    16.1.2、传统模式和Serverless模式下项目开发上线流程

    在这里插入图片描述

    16.1.3、Serverless和ServerFul架构的区别
    传统的ServerFul架构模式

    ServerFul架构是n台Server通过网络通信的方式协作一起,可以说ServerFul架构是基于Server和网络通信(分布式计算)的软件实现架构,Server可以是虚拟机、物理机,以及基于硬件实现的云服务器。
    在这里插入图片描述

    Serverless架构模式

    Serverless的核心特点就是实现自动弹性伸缩和按量付费。
    在这里插入图片描述

    Serverless相比ServerFul的特点
    • 资源分配

      在Serverless架构中,不用关心应用应用运行的资源(服务配置、磁盘大小)只提供一份代码就行。

    • 计费方式

      在Serverless架构中,计费方式按实际使用量计费(如函数调用次数、运行时长),不按传统的执行代码所需的资源计费(如固定CPU)。计费粒度也精确到了毫秒级,而不是传统的小时级。个别云厂商推出了每个月的免费额度,如腾讯云提供了每个月40万GBs的资源使用额度和100万次调用次数的免费额度。中小企业的网站访问量不是特别大的话完全可以免费使用。

    • 弹性伸缩

      Serverless架构的弹性伸缩更自动化、更精确,可以快速根据业务并发扩容更多的实例,甚至允许缩容到零实例状态来实现零费用,对用户来说是完全无感知的。而传统架构对服务器(虚拟机)进行扩容,虚拟机的启动速度比较慢,需要几分钟甚至更久。

    16.1.4、Serverless的能力
    • 计算能力

      资源按需分配,无需申请资源;
      Mwm:租户级别强隔离;
      Docker:进程级别隔离;
      Mwm+Docker轻量级资源毫秒级启动;
      实时扩容,阶梯缩容;
      按需收费;

    • 系统运维能力

      1、性能保障

      ​ 整个链路耗时毫秒级内,并支持VPC内网访问

      2、安全保障

      ​ 资源对用户不可见,安全由腾讯云提供专业的保障
      提供进程级和用户级安全隔离
      访问控制管理

      3、自动性扩缩容

      ​ 根据CPU内容网络IO自动扩容底层资源
      根据请求自动扩容函数实例,业务高峰期扩容,满足业务高并发需求,业务低峰期缩容,释放资源,降低成本

      4、自愈能力

      ​ 每一次请求都是一个健康的实例。

      Serverless中云函数被第一次调用会执行冷启动,Serverless中云函数被多次连续调用会执行热启动。

      冷启动是指在服务器中新开辟一块空间提供一个函数实例运行,这个过程有点像把这个函数放到虚拟机里去运行,每次运行前都需要先启动虚拟机加载这个函数,以前冷启动非常耗时,但是目前云厂商已经能做到毫秒级别的冷启动,这个过程不需要关心,但是需要注意的是使用Session时可能会导致Session丢失,所以建议将Session保存到数据库。

      热启动则是说如果一个云函数被持续触发,那就先不释放这个云函数实例,下次请求让然由之前已经创建的云函数实例来运行,就好比打开虚拟机运行完这个函数之后没有关闭虚拟机,而是让它待机,等待下一次重新触发调用运行,这样的好处就是省去了开机的过程。

    • 业务运维能力

    16.1.5、Serverless厂商
    云厂商

    亚马逊 AWS Lambda https://aws.amazon.com/cn/lambda/
    谷歌 Google Cloud Functions https://cloud.goole.com/functions
    微软Microsoft Azure https://www.azure.cn
    阿里云函数计算 https://www.aliyun.com/product/fc
    腾讯云 云函数 SCF(Serverless Cloud Function) https://cloud.tencent.com/product/scf
    华为云 FunctionGraph https://www.huaweicloud.com/product/functiongraph.html

    大家关心的问题

    1、为什么不使用亚马逊、谷歌、微软、IBM的server less
    在国内阿里云、腾讯云使用的更多一些。就像购买域名、服务器一样,首先想到的肯定不是国外的运营商,英语好的开源尝试一下。
    2、阿里云、腾讯云、华为云为什么选择腾讯云
    微信小程序的云开发就是基于腾讯云,选择腾云更方便和小程序对接
    腾讯云在serverless方面相比其它厂商支持更好一些
    腾讯云的技术在线客服非常棒
    腾讯云和serverless合作在腾讯云中集成了serverless Framework可以用喜欢的框架开发serverless应用。可以快速部署老项目。
    价格更便宜
    3、会使用腾讯云的serverless后,其它服务商的serverless也会了吗
    是的

    16.1.6、部署serverless应用

    通过Serverless Framework提供的云函数SCF组件快速创建与部署一个云函数项目。
    Serverless Framework将项目快速部署到腾讯云Serverless平台,部署前,请确认已经注册腾讯云账号并完成实名认证。

    安装serverless
    npm install -g serverless
    serverless -v
    
    • 1
    • 2
    创建项目

    在项目的目录上面运行serverless

    serverless
    
    • 1
    部署

    在serverless.yml文件所在的项目根目录,运行以下指令进行部署

    serverless deploy
    
    • 1
    16.1.7、Serverless组成

    广义的Serverless更多是指一种技术理念:Serverless是构建和运行软件是不需要关心服务器的一种架构思想。
    侠义的Serverless是指现阶段主流的技术实现:侠义的Serverless是Fass和Bass组成。

    在这里插入图片描述

    16.2、项目

    Serverless架构
    Egg.js基础+Egg.js无人点餐后台管理系统以及API接口
    Vue3.x基础+Vue无人点餐无人收银系统
    项目部署到Serverless

     count: computed(()=>store.state.count),
            double:computed(()=>store.getters.double)
        }
    }
    
    • 1
    • 2
    • 3
    • 4

    }

    
    #### 15.8.2、组合式api中访问Mutations and Actions
    
    
    • 1
    • 2
    • 3

    export default{
    setup(){
    const store = useStore()
    return {
    addCount:()=>store.commit(‘increment’),
    asyncIncrement:()=>store.dispatch(‘asyncIncrement’)
    }
    }
    }

    
    ##### Home.vue
    
    
    • 1
    • 2
    • 3
    Home组件
    
    ### 15.9、Typescript中使用Vuex
    
    https://vuex.vuejs.org/guide/typescript-support.html
    
    首先需要在Vue项目中集成typescript
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    vue add typescript

    
    **提示**:如果配置完ts后调用this.$store有警告信息,重启vscode,或者安装Vue3的插件后重启vscode重试。
    
    #### 15.9.1、修改store.js为store.ts
    
    #### 15.9.2、配置store.ts中的代码
    
    Vuex与Typescript一起使用时,必须声明自己的模块扩充
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    import { ComponentCustomProperties } from “vue”;
    import { Store, createStore } from ‘vuex’;

    declare module ‘@vue/runtime-core’ {
    interface State {
    count: number,
    title: string[],
    msg: string,
    }

    interface ComponentCustomProperties {
        $store: Store
    }
    
    • 1
    • 2
    • 3

    }

    const store = createStore({
    // 定义数据
    state() {
    return {
    count: 1,
    title: [‘马总’, ‘李总’, ‘刘总’],
    msg: “你好vue”,
    }
    },
    // 定义方法
    mutations: {
    incCount(state: any) {
    state.count++;
    },
    setCount(state: any, num: number) {
    state.count = num;
    },
    setMsg(state: any, msg: string) {
    state.msg = msg;
    },
    },
    // 计算属性
    getters: {
    reverseMsg(state: any) {
    return state.msg.split(“”).reverse().join(“”);
    },
    num(state: any) {
    return state.count + 10;
    }
    },
    //执行mutations里面的方法,异步操作放在actions
    actions: {
    addCount(context) {
    // 执行mutations的addCount方法
    context.commit(“incCount”)
    },
    // setMsg(context,msg){
    // setTimeout(() => {
    // 执行mutations的setMsg方法
    // context.commit(“setMsg”,mgs);
    // }, 1000);
    // }

        // 结构解析
        setMsg({ commit }, msg: string) {
            setTimeout(() => {
                commit("setMsg", msg);
            }, 1000);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    })

    export default store

    
    ##### Home.vue
    
    
    • 1
    • 2
    • 3
    Home组件
    
    ## 16、Serverless
    
    ### 16.1、Serverless架构介绍
    
    Serverless又名无服务器,所谓无服务器并非是说不需要依赖和依靠服务器等资源,而是开发者再也不用过多考虑服务器的问题,可以更专注在产品代码上,狭义的Serverless是Fass和Bass组成。
    
    无人点餐、无人收银系统是一个颠覆传统行业(中西餐厅、奶茶店、酒吧、火锅店、食堂、KTV)的智能点单系统。无人点单,手机支付,免费收银员,免点餐员,免硬件,免布线的一套智能管理系统。
    
    
    
    #### 16.1.1、概念
    
    Serverless是一种软件系统架构的思想和方法,它不是软件框架、类库或者工具。与传统框架的不同之处在于,完全由第三方管理,由事件触发,存在于无状态(Stateless)、暂存(可能只存在于一次调用的过程中)计算容器内。构建无服务应用程序意味着开发者可以专注在产品代码上,而无须管理和操作云端或本地的服务器或运行时(运行时通俗讲就是运行环境,如nodejs环境、java环境、php环境)。Serverless真正做到部署应用无需涉及基础设施的建设,自动构建、部署和启动服务。
    
    通俗的讲:Serverless是构建和运行软件时不需要关心服务器的一种架构思想。老程序员都用过虚拟机,刚开始学Serverless时可以把它理解为虚拟机的升级版本。
    
    虚拟主机已经是快被淘汰的上一代产物。云计算涌现出了很多改变传统IT架构和运维方式的新技术,比如虚拟机、容器、微服务,无论这些技术应用在哪些场景,降低成本、提升效率是云服务永恒的主题。Serverless的出现真正的解决了降低成本、提升效率的问题。它真正做到了弹性伸缩、高并发、按需收费、备份容灾、日志监控等。
    
    #### 16.1.2、传统模式和Serverless模式下项目开发上线流程
    
    
    #### 16.1.3、Serverless和ServerFul架构的区别
    
    ##### **传统的ServerFul架构模式**
    
    ServerFul架构是n台Server通过网络通信的方式协作一起,可以说ServerFul架构是基于Server和网络通信(分布式计算)的软件实现架构,Server可以是虚拟机、物理机,以及基于硬件实现的云服务器。
    
    [外链图片转存中...(img-KJ29ArV1-1668475744416)]
    
    ##### Serverless架构模式
    
    Serverless的核心特点就是实现自动弹性伸缩和按量付费。
    
    ##### Serverless相比ServerFul的特点
    
    - 资源分配
    
      在Serverless架构中,不用关心应用应用运行的资源(服务配置、磁盘大小)只提供一份代码就行。
    
    - 计费方式
    
      在Serverless架构中,计费方式按实际使用量计费(如函数调用次数、运行时长),不按传统的执行代码所需的资源计费(如固定CPU)。计费粒度也精确到了毫秒级,而不是传统的小时级。个别云厂商推出了每个月的免费额度,如腾讯云提供了每个月40万GBs的资源使用额度和100万次调用次数的免费额度。中小企业的网站访问量不是特别大的话完全可以免费使用。
    
    - 弹性伸缩
    
      Serverless架构的弹性伸缩更自动化、更精确,可以快速根据业务并发扩容更多的实例,甚至允许缩容到零实例状态来实现零费用,对用户来说是完全无感知的。而传统架构对服务器(虚拟机)进行扩容,虚拟机的启动速度比较慢,需要几分钟甚至更久。
    
    #### 16.1.4、Serverless的能力
    
    - 计算能力
    
      资源按需分配,无需申请资源;
      Mwm:租户级别强隔离;
      Docker:进程级别隔离;
      Mwm+Docker轻量级资源毫秒级启动;
      实时扩容,阶梯缩容;
      按需收费;
    
    - 系统运维能力
    
      1、性能保障
    
      ​	整个链路耗时毫秒级内,并支持VPC内网访问
    
      2、安全保障
    
      ​	资源对用户不可见,安全由腾讯云提供专业的保障
      	提供进程级和用户级安全隔离
      	访问控制管理
    
      3、自动性扩缩容
    
      ​	根据CPU内容网络IO自动扩容底层资源
      	根据请求自动扩容函数实例,业务高峰期扩容,满足业务高并发需求,业务低峰期缩容,释放资源,降低成本
    
      4、自愈能力
    
      ​	每一次请求都是一个健康的实例。
    
      Serverless中云函数被第一次调用会执行冷启动,Serverless中云函数被多次连续调用会执行热启动。
    
      **冷启动**是指在服务器中新开辟一块空间提供一个函数实例运行,这个过程有点像把这个函数放到虚拟机里去运行,每次运行前都需要先启动虚拟机加载这个函数,以前冷启动非常耗时,但是目前云厂商已经能做到毫秒级别的冷启动,这个过程不需要关心,但是需要注意的是使用Session时可能会导致Session丢失,所以建议将Session保存到数据库。
    
      **热启动**则是说如果一个云函数被持续触发,那就先不释放这个云函数实例,下次请求让然由之前已经创建的云函数实例来运行,就好比打开虚拟机运行完这个函数之后没有关闭虚拟机,而是让它待机,等待下一次重新触发调用运行,这样的好处就是省去了开机的过程。
    
    - 业务运维能力
    
    #### 16.1.5、Serverless厂商
    
    ##### 云厂商
    
    亚马逊 AWS Lambda https://aws.amazon.com/cn/lambda/
    谷歌 Google Cloud Functions https://cloud.goole.com/functions
    微软Microsoft Azure https://www.azure.cn
    阿里云函数计算 https://www.aliyun.com/product/fc
    腾讯云 云函数 SCF(Serverless Cloud Function) https://cloud.tencent.com/product/scf
    华为云 FunctionGraph https://www.huaweicloud.com/product/functiongraph.html
    
    ##### 大家关心的问题
    
    1、为什么不使用亚马逊、谷歌、微软、IBM的server less
    在国内阿里云、腾讯云使用的更多一些。就像购买域名、服务器一样,首先想到的肯定不是国外的运营商,英语好的开源尝试一下。
    2、阿里云、腾讯云、华为云为什么选择腾讯云
    微信小程序的云开发就是基于腾讯云,选择腾云更方便和小程序对接
    腾讯云在serverless方面相比其它厂商支持更好一些
    腾讯云的技术在线客服非常棒
    腾讯云和serverless合作在腾讯云中集成了serverless Framework可以用喜欢的框架开发serverless应用。可以快速部署老项目。
    价格更便宜
    3、会使用腾讯云的serverless后,其它服务商的serverless也会了吗
    是的
    
    #### 16.1.6、部署serverless应用
    
    通过Serverless Framework提供的云函数SCF组件快速创建与部署一个云函数项目。
    Serverless Framework将项目快速部署到腾讯云Serverless平台,部署前,请确认已经注册腾讯云账号并完成实名认证。
    
    ##### 安装serverless
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    npm install -g serverless
    serverless -v

    
    ##### 创建项目
    
    在项目的目录上面运行serverless
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    serverless

    
    ##### 部署
    
    在serverless.yml文件所在的项目根目录,运行以下指令进行部署
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    serverless deploy

    
    #### 16.1.7、Serverless组成
    
    广义的Serverless更多是指一种技术理念:Serverless是构建和运行软件是不需要关心服务器的一种架构思想。
    侠义的Serverless是指现阶段主流的技术实现:侠义的Serverless是Fass和Bass组成。
    
    
    
    
    ### 16.2、项目
    
    Serverless架构
    Egg.js基础+Egg.js无人点餐后台管理系统以及API接口
    Vue3.x基础+Vue无人点餐无人收银系统
    项目部署到Serverless
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    【C语言好题系列三】
    攻击者利用漏洞发动DDoS攻击,3万台GitLab服务器仍未修补
    Tkinter 转载 (来自C语言中文网)
    数据库实验五:数据库编程
    为什么用葫芦儿派盘取代U盘?
    Spring框架系列(10) - Spring AOP实现原理详解之AOP代理的创建
    两台Linux机器scp不输密码
    docker学习记录(一)
    3数据库系统——软件设计师
    石化技术杂志石化技术杂志社石化技术编辑部2022年第10期目录
  • 原文地址:https://blog.csdn.net/docsz/article/details/127859648