Vue2 从入门到精通

本教程系统讲解了 Vue2 的核心知识,包括 Vue 基础、Vue 脚手架、Vue Router 和 Vuex。通过丰富的代码示例和详细讲解,帮助前端开发者快速掌握 Vue2 开发技能。


文章目录

    • [第一章:Vue 基础](#第一章:Vue 基础)
    • [第二章:Vue 脚手架](#第二章:Vue 脚手架)
    • [第三章:Vue Router](#第三章:Vue Router)
      • [3.1 基本使用](#3.1 基本使用)
      • [3.2 嵌套路由](#3.2 嵌套路由)
      • [3.3 路由的 query 参数](#3.3 路由的 query 参数)
      • [3.4 命名路由](#3.4 命名路由)
      • [3.5 路由的 params 参数](#3.5 路由的 params 参数)
      • [3.6 路由的 props 配置](#3.6 路由的 props 配置)
      • [3.7 router-link 的 replace 属性](#3.7 router-link 的 replace 属性)
      • [3.8 编程式路由导航](#3.8 编程式路由导航)
      • [3.9 缓存路由组件](#3.9 缓存路由组件)
      • [3.10 两个新的生命周期钩子](#3.10 两个新的生命周期钩子)
      • [3.11 全局路由守卫](#3.11 全局路由守卫)
      • [3.12 独享路由守卫](#3.12 独享路由守卫)
      • [3.13 组件内路由守卫](#3.13 组件内路由守卫)
      • [3.14 history 模式与 hash 模式](#3.14 history 模式与 hash 模式)
        • 对比
        • [配置 history 模式](#配置 history 模式)
    • 第四章:Vuex
      • [4.1 Vuex 简介](#4.1 Vuex 简介)
        • 概念
        • [Vuex 工作原理](#Vuex 工作原理)
        • [什么时候使用 Vuex](#什么时候使用 Vuex)
      • [4.2 基本使用](#4.2 基本使用)
        • [安装 vuex](#安装 vuex)
        • [创建 store](#创建 store)
        • [组件中使用 Vuex](#组件中使用 Vuex)
        • [Vuex 工作流程](#Vuex 工作流程)
      • [4.3 getters 的使用](#4.3 getters 的使用)
        • [定义 getters](#定义 getters)
        • [组件中使用 getters](#组件中使用 getters)
      • [4.4 mapState 与 mapGetters](#4.4 mapState 与 mapGetters)
      • [4.5 mapMutations 与 mapActions](#4.5 mapMutations 与 mapActions)
      • [4.6 多组件共享数据](#4.6 多组件共享数据)
      • [4.7 模块化编码](#4.7 模块化编码)
    • 总结

第一章:Vue 基础

1.1 初识 Vue

概念

Vue 是一套用于构建用户界面的渐进式 JavaScript 框架。

初识 Vue 要点
  1. 想让 Vue 工作,就必须创建一个 Vue 实例,且要传入一个配置对象
  2. root 容器里的代码依然符合 HTML 规范,只不过混入了一些特殊的 Vue 语法
  3. root 容器里的代码被称为【Vue 模板】
  4. Vue 实例和容器是一一对应的
  5. 真实开发中只有一个 Vue 实例,并且会配合着组件一起使用
  6. {``{xxx}} 中的 xxx 要写 JS 表达式,且 xxx 可以自动读取到 data 中的所有属性
  7. 一旦 data 中的数据发生改变,那么页面中用到该数据的地方也会自动更新
JS 表达式与 JS 代码的区别
javascript 复制代码
// JS 表达式:会产生一个值,可以放在任何一个需要值的地方
a                     // 变量
a + b                 // 运算
demo(1)               // 函数调用
x === y ? 'a' : 'b'   // 三元表达式

// JS 代码(语句)
if(){}                // 条件语句
for(){}               // 循环语句
示例代码
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>初识Vue</title>
    <!-- 引入Vue -->
    <script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
    <!-- 准备好一个容器 -->
    <div id="demo">
        <h1>Hello,{{name.toUpperCase()}},{{address}}</h1>
    </div>

    <script type="text/javascript">
        Vue.config.productionTip = false // 阻止 vue 在启动时生成生产提示

        // 创建Vue实例
        new Vue({
            el:'#demo', // el用于指定当前Vue实例为哪个容器服务,值通常为css选择器字符串
            data:{ // data中用于存储数据,数据供el所指定的容器去使用
                name:'前端开发教程',
                address:'北京'
            }
        })
    </script>
</body>
</html>

1.2 Vue 模板语法

Vue 模板语法有两大类:

1. 插值语法
  • 功能:用于解析标签体内容
  • 写法{``{xxx}},xxx 是 JS 表达式,且可以直接读取到 data 中的所有属性
2. 指令语法
  • 功能:用于解析标签(包括:标签属性、标签体内容、绑定事件等)
  • 举例v-bind:href="xxx" 或简写为 :href="xxx"
  • 备注 :Vue 中有很多的指令,且形式都是 v-???
示例代码
html 复制代码
<div id="root">
    <h1>插值语法</h1>
    <h3>你好,{{name}}</h3>
    <hr>
    <h1>指令语法</h1>
    <a v-bind:href="school.url.toUpperCase()">点我去学习1</a>
    <a :href="school.url">点我去学习2</a>
</div>

<script>
    new Vue({
        el:'#root',
        data:{
            name:'jack',
            school:{
                name:'前端开发教程',
                url:'在线学习平台',
            }
        }
    })
</script>

1.3 数据绑定

Vue 中有两种数据绑定的方式:

绑定方式 说明 特点
单向绑定 (v-bind) 数据只能从 data 流向页面 :value="name"
双向绑定 (v-model) 数据不仅能从 data 流向页面,还可以从页面流向 data v-model="name"

备注

  1. 双向绑定一般都应用在表单类元素上(如:input、select 等)
  2. v-model:value 可以简写为 v-model,因为 v-model 默认收集的就是 value 值
示例代码
html 复制代码
<div id="root">
    <!-- 简写 -->
    单向数据绑定:<input type="text" :value="name"><br>
    双向数据绑定:<input type="text" v-model="name"><br>
</div>

<script>
    new Vue({
        el:'#root',
        data:{
            name:'前端开发教程'
        }
    })
</script>

1.4 el 与 data 的两种写法

el 的两种写法
  1. new Vue 时配置 el 属性
  2. 先创建 Vue 实例,随后再通过 vm.$mount('#root') 指定 el 的值
data 的两种写法
  1. 对象式:data: { name: 'xxx' }
  2. 函数式:data() { return { name: 'xxx' } }

重要原则:由 Vue 管理的函数,一定不要写箭头函数,一旦写了箭头函数,this 就不再是 Vue 实例了。

示例代码
javascript 复制代码
// el 的两种写法
const v = new Vue({
    // el:'#root', // 第一种写法
    data:{
        name:'前端开发教程'
    }
})
v.$mount('#root') // 第二种写法

// data 的两种写法
new Vue({
    el:'#root',
    // data的第一种写法:对象式
    /* data:{
        name:'前端开发教程'
    } */

    // data的第二种写法:函数式
    data(){
        console.log('@@@',this) // 此处的this是Vue实例对象
        return{
            name:'前端开发教程'
        }
    }
})

1.5 MVVM 模型

缩写 全称 说明
M Model data 中的数据
V View 模板代码
VM ViewModel Vue 实例

观察发现

  1. data 中所有的属性,最后都出现在了 vm 身上
  2. vm 身上所有的属性及 Vue 原型上所有属性,在 Vue 模板中都可以直接使用

1.6 数据代理

什么是数据代理

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

Vue 中的数据代理
  1. 通过 vm 对象来代理 data 对象中属性的操作(读/写)
  2. Vue 中数据代理的好处:更加方便的操作 data 中的数据
  3. 基本原理
    • 通过 Object.defineProperty() 把 data 对象中所有属性添加到 vm 上
    • 为每一个添加到 vm 上的属性,都指定一个 getter/setter
    • 在 getter/setter 内部去操作(读/写)data 中对应的属性
Object.defineProperty 回顾
javascript 复制代码
let number = 18
let person = {
    name:'张三',
    sex:'男',
}

Object.defineProperty(person,'age',{
    // 当有人读取person的age属性时,get函数(getter)就会被调用
    get(){
        console.log('有人读取age属性了')
        return number
    },
    // 当有人修改person的age属性时,set函数(setter)就会被调用
    set(value){
        console.log('有人修改了age属性,且值是',value)
        number = value
    }
})

1.7 事件处理

事件的基本使用
  1. 使用 v-on:xxx@xxx 绑定事件,其中 xxx 是事件名
  2. 事件的回调需要配置在 methods 对象中,最终会在 vm 上
  3. methods 中配置的函数,不要用箭头函数!否则 this 就不是 vm 了
  4. methods 中配置的函数,都是被 Vue 所管理的函数,this 的指向是 vm 或组件实例对象
  5. @click="demo"@click="demo($event)" 效果一致,但后者可以传参
示例代码
html 复制代码
<div id="root">
    <button @click="showInfo1">点我提示信息1(不传参)</button>
    <button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>

<script>
    new Vue({
        el:'#root',
        data:{},
        methods:{
            showInfo1(event){
                alert('同学你好!')
            },
            showInfo2(event,number){
                console.log(event,number)
                alert('同学你好!!')
            }
        }
    })
</script>
事件修饰符

Vue 中的事件修饰符:

修饰符 说明
.prevent 阻止默认事件(常用)
.stop 阻止事件冒泡(常用)
.once 事件只触发一次(常用)
.capture 使用事件的捕获模式
.self 只有 event.target 是当前操作的元素时才触发事件
.passive 事件的默认行为立即执行,无需等待事件回调执行完毕

修饰符可以连续写@click.prevent.stop="showInfo"

键盘事件

Vue 中常用的按键别名:

别名 对应键
.enter 回车
.delete 删除/退格
.esc 退出
.space 空格
.tab 换行(需配合 keydown)
.up/.down/.left/.right 方向键

未提供别名的按键 :可以使用按键原始的 key 值,但要转为 kebab-case(如 @keyup.page-down

系统修饰键:ctrl、alt、shift、meta

  • 配合 keyup:按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
  • 配合 keydown:正常触发事件

自定义按键别名Vue.config.keyCodes.huiche = 13


1.8 计算属性

计算属性介绍
  1. 定义:要用的属性不存在,要通过已有属性计算得来
  2. 原理 :底层借助了 Object.defineProperty 方法提供的 getter 和 setter
  3. get 函数什么时候执行
    • 初次读取时会执行一次
    • 当依赖的数据发生改变时会被再次调用
  4. 优势:与 methods 实现相比,内部有缓存机制(复用),效率更高,调试方便
  5. 备注
    • 计算属性最终会出现在 vm 上,直接读取使用即可
    • 如果计算属性要被修改,必须写 set 函数去响应修改
示例代码
html 复制代码
<div id="root">
    姓:<input type="text" v-model="firstName"> <br><br>
    名:<input type="text" v-model="lastName"> <br><br>
    全名:<span>{{fullName}}</span> <br><br>
</div>

<script>
    const vm = new Vue({
        el:'#root',
        data:{
            firstName:'张',
            lastName:'三',
        },
        computed:{
            fullName:{
                // get有什么作用?当有人读取fullName时,get就会被调用
                // get什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时
                get(){
                    console.log('get被调用了')
                    return this.firstName + '-' + this.lastName
                },
                // set什么时候调用? 当fullName被修改时
                set(value){
                    console.log('set',value)
                    const arr = value.split('-')
                    this.firstName = arr[0]
                    this.lastName = arr[1]
                }
            }
        }
    })
</script>
计算属性简写

当只有 getter 而没有 setter 时,可以简写:

javascript 复制代码
computed:{
    // 简写形式(只有getter时使用)
    fullName(){
        console.log('get被调用了')
        return this.firstName + '-' + this.lastName
    }
}

1.9 监视属性

watch 监视属性
  1. 当被监视的属性变化时,回调函数自动调用,进行相关操作
  2. 监视的属性必须存在,才能进行监视
  3. 监视的两种写法:
    • new Vue 时传入 watch 配置
    • 通过 vm.$watch 监视
示例代码
javascript 复制代码
const vm = new Vue({
    el:'#root',
    data:{
        isHot:true,
    },
    computed:{
        info(){
            return this.isHot ? '炎热' : '凉爽'
        }
    },
    methods:{
        changeWeather(){
            this.isHot = !this.isHot
        }
    },
    watch:{
        isHot:{
            immediate:true, // 初始化时让handler调用一下
            handler(newValue,oldValue){
                console.log('isHot被修改了',newValue,oldValue)
            }
        }
    }
})

// 第二种写法
vm.$watch('isHot',{
    immediate:true,
    handler(newValue,oldValue){
        console.log('isHot被修改了',newValue,oldValue)
    }
})
深度监视
  1. Vue 中的 watch 默认不监测对象内部值的改变(一层)
  2. 配置 deep: true 可以监测对象内部值改变(多层)
javascript 复制代码
watch:{
    // 监视多级结构中所有属性的变化
    numbers:{
        deep:true,
        handler(){
            console.log('numbers改变了')
        }
    }
}
computed 和 watch 的区别
  1. computed 能完成的功能,watch 都可以完成
  2. watch 能完成的功能,computed 不一定能完成,例如:watch 可以进行异步操作

两个重要的小原则

  1. 所被 Vue 管理的函数,最好写成普通函数,这样 this 的指向才是 vm 或组件实例对象
  2. 所有不被 Vue 所管理的函数(定时器的回调函数、ajax 的回调函数等、Promise 的回调函数),最好写成箭头函数,这样 this 的指向才是 vm 或组件实例对象

1.10 绑定样式

class 样式
写法 说明 适用场景
:class="xxx" 字符串写法 类名不确定,要动态获取
:class="[xxx, yyy]" 数组写法 样式个数不确定,名字也不确定
:class="{xxx: true}" 对象写法 样式个数确定,名字也确定,但不确定用不用
style 样式
html 复制代码
<!-- 对象写法 -->
<div :style="{fontSize: xxx}"></div>

<!-- 数组写法 -->
<div :style="[styleObj1, styleObj2]"></div>

1.11 条件渲染

v-if
html 复制代码
<!-- 写法 -->
<div v-if="condition"></div>
<div v-else-if="condition2"></div>
<div v-else></div>
  • 适用于:切换频率较低的场景
  • 特点:不展示的 DOM 元素直接被移除
  • 注意:v-if 可以和 v-else-if、v-else 一起使用,但要求结构不能被"打断"
v-show
html 复制代码
<div v-show="condition"></div>
  • 适用于:切换频率较高的场景
  • 特点:不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉
备注

使用 v-if 的时,元素可能无法获取到,而使用 v-show 一定可以获取到。


1.12 列表渲染

v-for 指令
html 复制代码
<!-- 语法 -->
v-for="(item, index) in xxx" :key="yyy"

可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)

key 的原理
  1. 虚拟 DOM 中 key 的作用

    • key 是虚拟 DOM 对象的标识
    • 当数据发生变化时,Vue 会根据【新数据】生成【新的虚拟 DOM】
    • 随后进行【新虚拟 DOM】与【旧虚拟 DOM】的差异比较
  2. 对比规则

    • 旧虚拟 DOM 中找到了与新虚拟 DOM 相同的 key:
      • 若虚拟 DOM 中内容没变,直接使用之前的真实 DOM
      • 若虚拟 DOM 中内容变了,则生成新的真实 DOM,随后替换掉页面中之前的真实 DOM
    • 旧虚拟 DOM 中未找到与新虚拟 DOM 相同的 key:创建新的真实 DOM,随后渲染到页面
  3. 用 index 作为 key 可能会引发的问题

    • 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实 DOM 更新
    • 如果结构中还包含输入类的 DOM,会产生错误 DOM 更新
  4. 开发中如何选择 key

    • 最好使用每条数据的唯一标识作为 key(id、手机号、身份证号等)
    • 如果不存在对数据的逆序操作,仅用于渲染列表,使用 index 作为 key 是没有问题的
示例代码
html 复制代码
<div id="root">
    <!-- 遍历数组 -->
    <ul>
        <li v-for="(p,index) of persons" :key="index">
            {{p.name}}-{{p.age}}
        </li>
    </ul>

    <!-- 遍历对象 -->
    <ul>
        <li v-for="(value,k) of car" :key="k">
            {{k}}-{{value}}
        </li>
    </ul>
</div>

<script>
    new Vue({
        el:'#root',
        data:{
            persons:[
                {id:'001',name:'张三',age:18},
                {id:'002',name:'李四',age:19},
                {id:'003',name:'王五',age:20}
            ],
            car:{
                name:'奥迪A8',
                price:'70万',
                color:'黑色'
            }
        }
    })
</script>
Vue 监测数据的原理

对象监测

  • Vue 会监视 data 中所有层次的数据
  • 通过 setter 实现监视,且要在 new Vue 时就传入要监测的数据
  • 对象中后追加的属性,Vue 默认不做响应式处理
  • 如需给后添加的属性做响应式,请使用 Vue.set(target, key, value)vm.$set(target, key, value)

数组监测

  • 通过包裹数组更新元素的方法实现
  • 在 Vue 修改数组中的某个元素一定要用如下方法:
    • 使用 API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
    • Vue.set() 或 vm.$set()

特别注意Vue.set()vm.$set() 不能给 vm 或 vm 的根数据对象添加属性!


1.13 收集表单数据

input 类型 v-model 收集的值
<input type="text"/> value 值
<input type="radio"/> value 值
<input type="checkbox"/> 没有配置 value 属性:checked(布尔值) 配置了 value 属性:value 组成的数组

v-model 的三个修饰符

  • .lazy:失去焦点再收集数据
  • .number:输入字符串转为有效的数字
  • .trim:输入首尾空格过滤

1.14 过滤器

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

语法

  1. 注册过滤器:Vue.filter(name, callback)new Vue({ filters:{} })
  2. 使用过滤器:{``{ xxx | 过滤器名}}v-bind:属性 = "xxx | 过滤器名"

备注

  1. 过滤器也可以接收额外参数、多个过滤器也可以串联
  2. 并没有改变原本的数据,是产生新的对应的数据
示例代码
html 复制代码
<div id="root">
    <h3>现在是:{{ time | timeFormater }}</h3>
    <h3>现在是:{{ time | timeFormater('YYYY_MM_DD') | mySlice }}</h3>
</div>

<script>
    // 全局过滤器
    Vue.filter('mySlice',function(value){
        return value.slice(0,4)
    })
    
    new Vue({
        el:'#root',
        data:{
            time:1621561377603,
        },
        // 局部过滤器
        filters:{
            timeFormater(value, str='YYYY年MM月DD日 HH:mm:ss'){
                return dayjs(value).format(str)
            }
        }
    })
</script>

1.15 内置指令

指令 说明
v-bind 单向绑定解析表达式,可简写为 :xxx
v-model 双向数据绑定
v-for 遍历数组/对象/字符串
v-on 绑定事件监听,可简写为 @
v-if 条件渲染(动态控制节点是否存存在)
v-else 条件渲染(动态控制节点是否存存在)
v-show 条件渲染(动态控制节点是否展示)
v-text 指令
  1. 作用:向其所在的节点中渲染文本内容
  2. 与插值语法的区别:v-text 会替换掉节点中的内容,{``{xx}} 则不会
v-html 指令
  1. 作用:向指定节点中渲染包含 html 结构的内容
  2. 与插值语法的区别:
    • v-html 会替换掉节点中所有的内容,{``{xx}} 则不会
    • v-html 可以识别 html 结构

严重注意:v-html 有安全性问题!在网站上动态渲染任意 HTML 是非常危险的,容易导致 XSS 攻击。一定要在可信的内容上使用 v-html,永不要用在用户提交的内容上!

v-cloak 指令
  1. 本质是一个特殊属性,Vue 实例创建完毕并接管容器后,会删掉 v-cloak 属性
  2. 使用 css 配合 v-cloak 可以解决网速慢时页面展示出 {``{xxx}} 的问题
html 复制代码
<style>
    [v-cloak]{
        display:none;
    }
</style>
<h2 v-cloak>{{name}}</h2>
v-once 指令
  1. v-once 所在节点在初次动态渲染后,就视为静态内容了
  2. 以后数据的改变不会引起 v-once 所在结构的更新,可以用于优化性能
v-pre 指令
  1. 跳过其所在节点的编译过程
  2. 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译

1.16 自定义指令

定义语法

局部指令

javascript 复制代码
new Vue({
    directives:{指令名:配置对象}   或   directives{指令名:回调函数}
})

全局指令

javascript 复制代码
Vue.directive(指令名,配置对象) 或   Vue.directive(指令名,回调函数)
配置对象中常用的三个回调
回调 说明
bind 指令与元素成功绑定时调用
inserted 指令所在元素被插入页面时调用
update 指令所在模板被重新解析时调用
备注
  1. 指令定义时不加 v-,但使用时要加 v-
  2. 指令名如果是多个单词,要使用 kebab-case 命名方式,不要用 camelCase 命名
示例代码
html 复制代码
<div id="root">
    <h2>当前的n值是:<span v-text="n"></span> </h2>
    <h2>放大10倍后的n值是:<span v-big="n"></span> </h2>
    <button @click="n++">点我n+1</button>
    <hr>
    <input type="text" v-fbind:value="n">
</div>

<script>
    new Vue({
        el:'#root',
        data:{
            n:1
        },
        directives:{
            big(element, binding){
                console.log('big',this) // 注意此处的this是window
                element.innerText = binding.value * 10
            },
            fbind:{
                bind(element, binding){
                    element.value = binding.value
                },
                inserted(element, binding){
                    element.focus()
                },
                update(element, binding){
                    element.value = binding.value
                }
            }
        }
    })
</script>

1.17 生命周期

生命周期简介
  1. 又名:生命周期回调函数、生命周期函数、生命周期钩子
  2. 是什么:Vue 在关键时刻帮我们调用的一些特殊名称的函数
  3. 生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的
  4. 生命周期函数中的 this 指向是 vm 或组件实例对象
生命周期流程图

下图展示了 Vue 实例从创建到销毁的完整生命周期过程,包括各个生命周期钩子函数的调用时机:

复制代码
创建阶段:
  beforeCreate() - 数据代理和响应式创建之前
  created() - 数据代理和响应式创建完成
  beforeMount() - 模板解析之前
  mounted() - 模板解析完成(挂载完毕)

更新阶段:
  beforeUpdate() - 数据更新之前
  updated() - 数据更新完成

销毁阶段:
  beforeDestroy() - 销毁之前
  destroyed() - 销毁完成
常用的生命周期钩子
钩子 说明
mounted 发送 ajax 请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】
beforeDestroy 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】
关于销毁 Vue 实例
  1. 销毁后借助 Vue 开发者工具看不到任何信息
  2. 销毁后自定义事件会失效,但原生 DOM 事件依然有效
  3. 一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了

1.18 组件基础

Vue 中使用组件的三大步骤
  1. 定义组件 (创建组件):使用 Vue.extend(options) 创建
  2. 注册组件
    • 局部注册:new Vue({ components:{组件名:组件} })
    • 全局注册:Vue.component('组件名',组件)
  3. 使用组件 :写组件标签 <school></school>
定义组件的注意点
  1. el 不要写,为什么?------ 最终所有的组件都要经过一个 vm 的管理,由 vm 中的 el 决定服务哪个容器
  2. data 必须写成函数,为什么?------ 避免组件被复用时,数据存在引用关系
  3. 使用 template 可以配置组件结构
几个注意点

关于组件名

  • 一个单词组成:schoolSchool
  • 多个单词组成:my-schoolMySchool(需 Vue 脚手架支持)
  • 备注:组件名尽可能回避 HTML 中已有的元素名称
  • 可以使用 name 配置项指定组件在开发者工具中呈现的名字

关于组件标签

  • <school></school><school/>(不使用脚手架时,后者会导致后续组件不能渲染)

一个简写方式Vue.extend(options) 可简写为 options

组件的嵌套
javascript 复制代码
// 定义student组件
const student = Vue.extend({
    name:'student',
    template:`<div><h2>学生姓名:{{name}}</h2></div>`,
    data(){
        return { name:'张三', age:18 }
    }
})

// 定义school组件
const school = Vue.extend({
    name:'school',
    template:`<div><h2>学校名称:{{name}}</h2><student/></div>`,
    data(){
        return { name:'前端开发教程', address:'北京' }
    },
    components:{ student }
})
VueComponent
  1. school 组件本质是一个名为 VueComponent 的构造函数,且不是程序员定义的,是 Vue.extend 生成的
  2. 我们只需要写 <school/><school></school>,Vue 解析时会帮我们创建 school 组件的实例对象
  3. 特别注意 :每次调用 Vue.extend,返回的都是一个全新的 VueComponent
  4. VueComponent 的实例对象,以后简称 vc(组件实例对象)
  5. Vue 的实例对象,以后简称 vm
一个重要的内置关系
javascript 复制代码
VueComponent.prototype.__proto__ === Vue.prototype

为什么要有这个关系:让组件实例对象(vc)可以访问到 Vue 原型上的属性、方法。


第二章:Vue 脚手架

2.1 Vue 脚手架结构分析

项目结构
复制代码
vue_test/
├── public/
│   ├── index.html
│   └── favicon.ico
├── src/
│   ├── assets/
│   ├── components/
│   │   ├── School.vue
│   │   └── Student.vue
│   ├── App.vue
│   └── main.js
├── babel.config.js
├── package.json
└── vue.config.js
main.js 分析
javascript 复制代码
// 该文件是整个项目的入口文件
import Vue from 'vue'
import App from './App.vue'

// 关闭vue的生产提示
Vue.config.productionTip = false

// 创建Vue实例对象---vm
new Vue({
    el:'#app',
    // render函数完成了这个功能:将App组件放入容器中
    render: h => h(App),
})
关于不同版本的 Vue
版本 说明
vue.js 完整版 Vue,包含:核心功能 + 模板解析器
vue.runtime.xxx.js 运行版 Vue,只包含:核心功能;没有模板解析器

因为 vue.runtime.xxx.js 没有模板解析器,所以不能使用 template 配置项,需要使用 render 函数接收到的 createElement 函数去指定具体内容。


2.2 ref 属性

  1. 被用来给元素或子组件注册引用信息(替代 id)
  2. 应用在 html 标签上获取的是真实 DOM 元素,应用在组件标签上获取的是组件实例对象
  3. 使用方式:
    • 打标识:<h1 ref="xxx">...</h1><School ref="xxx"/>
    • 获取:this.$refs.xxx
html 复制代码
<template>
    <div>
        <h1 v-text="msg" ref="title"></h1>
        <button ref="btn" @click="showDOM">点我输出上方的DOM元素</button>
        <School ref="sch"/>
    </div>
</template>

<script>
    export default {
        name:'App',
        data() {
            return { msg:'欢迎学习Vue!' }
        },
        methods: {
            showDOM(){
                console.log(this.$refs.title) // 真实DOM元素
                console.log(this.$refs.btn)  // 真实DOM元素
                console.log(this.$refs.sch) // School组件的实例对象
            }
        },
    }
</script>

2.3 props 配置

功能:让组件接收外部传过来的数据

  1. 传递数据:<Student name="李四" sex="女" :age="18"/>
  2. 接收数据:
javascript 复制代码
export default {
    name:'Student',
    // 简单声明接收
    props:['name','age','sex'],
    
    // 接收的同时对数据进行类型限制
    props:{
        name:String,
        age:Number,
        sex:String
    },
    
    // 接收的同时对数据进行类型限制+默认值的指定+必要性的限制
    props:{
        name:{
            type:String,    // name的类型是字符串
            required:true,  // name是必要的
        },
        age:{
            type:Number,
            default:99     // 默认值
        },
        sex:{
            type:String,
            required:true
        }
    }
}

注意:props 是只读的,Vue 底层会监测你对 props 的修改,如果进行了修改,就会发出警告。若业务需求确实需要修改,那么请复制 props 的内容到 data 中,然后去修改 data 中的数据。


2.4 mixin 混入

功能:可以把多个组件共用的配置提取成一个混入对象

混入的使用方式

第一步:定义混入

javascript 复制代码
export const hunhe = {
    methods: {
        showName(){
            alert(this.name)
        }
    },
    mounted() {
        console.log('你好啊!')
    },
}

第二步:使用混入

javascript 复制代码
// 局部混入
export default {
    name:'School',
    data() {
        return { name:'前端开发教程', address:'北京', x:666 }
    },
    mixins:[hunhe, hunhe2],
}

// 全局混入
Vue.mixin(xxx)

混入的合并规则

  • 数据对象在内部进行递归合并,并在发生冲突时以组件数据优先
  • 同名钩子函数将合并为一个数组,都会被调用,且混入对象的钩子先调用
  • 值为对象的选项(如 methods、components 等)将合并为同一个对象,键名冲突时取组件对象的键值对

2.5 插件

功能:用于增强 Vue

本质:包含 install 方法的一个对象,install 的第一个参数是 Vue,第二个以后的参数是插件使用者传递的数据

定义插件

javascript 复制代码
export default {
    install(Vue, x, y, z){
        console.log(x, y, z)
        
        // 全局过滤器
        Vue.filter('mySlice',function(value){
            return value.slice(0, 4)
        })
        
        // 定义全局指令
        Vue.directive('fbind', {
            bind(element, binding){
                element.value = binding.value
            },
            inserted(element, binding){
                element.focus()
            },
            update(element, binding){
                element.value = binding.value
            }
        })
        
        // 定义混入
        Vue.mixin({
            data() {
                return { x:100, y:200 }
            },
        })
        
        // 给Vue原型上添加一个方法
        Vue.prototype.hello = () => { alert('你好啊') }
    }
}

使用插件

javascript 复制代码
import Vue from 'vue'
import App from './App.vue'
import plugins from './plugins'

Vue.use(plugins)

new Vue({
    el:'#app',
    render: h => h(App),
})

2.6 scoped 样式

作用:让样式在局部生效,防止冲突

写法<style scoped>

html 复制代码
<template>
    <div class="title">你好啊</div>
</template>

<script>
    export default { name:'App' }
</script>

<style scoped>
    .title{
        color: red;
    }
</style>

备注

  • scoped 不影响使用 class 或 id 选择器
  • scoped 会在元素的标签上添加一个 data-v-xxxxx 属性
  • 如果组件中还有引入的其他组件,后代选择器会作用于子组件的根元素

2.7 TodoList 案例

TodoList 是一个经典的任务管理案例,展示了 Vue 开发中的核心概念。

案例结构
复制代码
TodoList/
├── App.vue          # 应用根组件
└── components/
    ├── MyHeader.vue # 添加任务头部
    ├── MyList.vue   # 任务列表
    ├── MyItem.vue   # 单个任务项
    └── MyFooter.vue # 任务统计底部
App.vue(数据管理)
html 复制代码
<template>
    <div class="todo-container">
        <div class="todo-wrap">
            <MyHeader :addTodo="addTodo"/>
            <MyList :todos="todos" :checkTodo="checkTodo" :deleteTodo="deleteTodo"/>
            <MyFooter :todos="todos" :checkAllTodo="checkAllTodo" :clearAllTodo="clearAllTodo"/>
        </div>
    </div>
</template>

<script>
    import MyHeader from './components/MyHeader'
    import MyList from './components/MyList'
    import MyFooter from './components/MyFooter.vue'

    export default {
        name:'App',
        components:{MyHeader, MyList, MyFooter},
        data() {
            return {
                todos:[
                    {id:'001', title:'抽烟', done:true},
                    {id:'002', title:'喝酒', done:false},
                    {id:'003', title:'开车', done:true}
                ]
            }
        },
        methods: {
            // 添加一个todo
            addTodo(todoObj){
                this.todos.unshift(todoObj)
            },
            // 勾选or取消勾选一个todo
            checkTodo(id){
                this.todos.forEach((todo) => {
                    if(todo.id === id) todo.done = !todo.done
                })
            },
            // 删除一个todo
            deleteTodo(id){
                this.todos = this.todos.filter(todo => todo.id !== id)
            },
            // 全选or取消全选
            checkAllTodo(done){
                this.todos.forEach((todo) => {
                    todo.done = done
                })
            },
            // 清除所有已经完成的todo
            clearAllTodo(){
                this.todos = this.todos.filter((todo) => {
                    return !todo.done
                })
            }
        }
    }
</script>
MyHeader.vue
html 复制代码
<template>
    <div class="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" 
               v-model="title" @keyup.enter="add"/>
    </div>
</template>

<script>
    import {nanoid} from 'nanoid'
    export default {
        name:'MyHeader',
        props:['addTodo'],
        data() {
            return { title:'' }
        },
        methods: {
            add(){
                if(!this.title.trim()) return alert('输入不能为空')
                const todoObj = {id:nanoid(), title:this.title, done:false}
                this.addTodo(todoObj)
                this.title = ''
            }
        },
    }
</script>
MyFooter.vue
html 复制代码
<template>
    <div class="todo-footer" v-show="total">
        <input type="checkbox" v-model="isAll"/>
        <span>
            <span>已完成{{doneTotal}}</span> / 全部{{total}}
        </span>
        <button class="btn btn-danger" @click="clearAll">清除已完成任务</button>
    </div>
</template>

<script>
    export default {
        name:'MyFooter',
        props:['todos', 'checkAllTodo', 'clearAllTodo'],
        computed: {
            total(){
                return this.todos.length
            },
            doneTotal(){
                return this.todos.reduce((pre, todo) => pre + (todo.done ? 1 : 0), 0)
            },
            isAll:{
                get(){
                    return this.doneTotal === this.total && this.total > 0
                },
                set(value){
                    this.checkAllTodo(value)
                }
            }
        },
    }
</script>

2.8 浏览器本地存储

localStorage
  1. 数据永久存储,除非手动删除
  2. 存储容量约为 5MB
  3. 以键值对的形式存储
javascript 复制代码
// 保存数据
localStorage.setItem('msg', 'hello!!!')
localStorage.setItem('person', JSON.stringify(p))

// 读取数据
localStorage.getItem('msg')
JSON.parse(localStorage.getItem('person'))

// 删除数据
localStorage.removeItem('msg')

// 清空数据
localStorage.clear()
sessionStorage
  1. 数据存储在会话级别,关闭浏览器或标签页后数据消失
  2. 其他特性与 localStorage 相同

2.9 组件自定义事件

一种组件间通信的方式,适用于:子组件 ===> 父组件

绑定自定义事件

方式一:通过父组件给子组件绑定自定义事件

html 复制代码
<!-- 第一种写法:使用@或v-on -->
<Student @studentEvent="getStudentName" @demo="m1"/>

<!-- 第二种写法:使用ref -->
<Student ref="student" @click.native="show"/>

方式二:通过代码绑定

javascript 复制代码
mounted() {
    this.$refs.student.$on('studentEvent', this.getStudentName)
    // this.$refs.student.$once('studentEvent', this.getStudentName) // 一次性
}
触发自定义事件
html 复制代码
<template>
    <div class="student">
        <button @click="sendStudentName">把学生名给父组件</button>
    </div>
</template>

<script>
    export default {
        name:'Student',
        data() {
            return { name:'张三', sex:'男' }
        },
        methods: {
            sendStudentName(){
                // 触发 Student 实例上绑定的 studentEvent 事件
                this.$emit('前端开发教程', this.name, 1, 2, 3)
            }
        },
    }
</script>
解绑自定义事件
javascript 复制代码
methods: {
    unbind(){
        this.$off('studentEvent')  // 解绑一个自定义事件
        // this.$off(['前端开发教程', 'demo'])  // 解绑多个
        // this.$off()  // 解绑所有自定义事件
    }
}

2.10 全局事件总线

一种组件间通信的方式,适用于任意组件间通信

安装全局事件总线
javascript 复制代码
new Vue({
    el:'#app',
    render: h => h(App),
    beforeCreate() {
        Vue.prototype.$bus = this // 安装全局事件总线
    },
})
使用事件总线

接收数据:A 组件想接收数据,则在 A 组件中给 $bus 绑定自定义事件,事件的回调留在 A 组件自身

javascript 复制代码
mounted() {
    this.$bus.$on('hello', (data) => {
        console.log('我是School组件,收到了数据', data)
    })
},
beforeDestroy() {
    this.$bus.$off('hello')
},

提供数据

javascript 复制代码
methods: {
    sendStudentName(){
        this.$bus.$emit('hello', this.name)
    }
}

2.11 消息订阅与发布

一种组件间通信的方式,适用于任意组件间通信(与全局事件总线类似)

使用步骤
  1. 安装 pubsub:npm i pubsub-js
  2. 引入:import pubsub from 'pubsub-js'
  3. 订阅消息:
javascript 复制代码
mounted() {
    this.pubId = pubsub.subscribe('hello', (msgName, data) => {
        console.log('收到消息了', msgName, data)
    })
},
beforeDestroy() {
    pubsub.unsubscribe(this.pubId)
},
  1. 发布消息:
javascript 复制代码
methods: {
    sendStudentName(){
        pubsub.publish('hello', 666)
    }
}

2.12 nextTick

  1. this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调
  3. 什么时候用:当改变数据后,要基于更新后的新 DOM 进行某些操作时,要在 nextTick 所指定的回调函数中执行
javascript 复制代码
methods: {
    showInput(){
        this.isEdit = true
        this.$nextTick(function(){
            this.$refs.inputTitle.focus()
        })
    }
}

2.13 过渡与动画

Vue 动画理解
  1. 操作元素的 CSS 类名
  2. 元素显示/隐藏时触发
使用 <transition> 组件
html 复制代码
<template>
    <div>
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition name="hello" appear>
            <h1 v-show="isShow">你好啊!</h1>
        </transition>
    </div>
</template>

<style scoped>
    h1{
        background-color: orange;
    }

    /* 入场动画 */
    .hello-enter-active{
        animation: fade 0.5s linear;
    }

    /* 离场动画 */
    .hello-leave-active{
        animation: fade 0.5s linear reverse;
    }

    @keyframes fade {
        from{
            transform: translateX(-100%);
        }
        to{
            transform: translateX(0px);
        }
    }
</style>
过渡的类名
进入 离开 说明
v-enter v-leave 元素进入的起始状态
v-enter-active v-leave-active 元素进入的动画状态
v-enter-to v-leave-to 元素进入的终止状态

如果使用 <transition name="hello">,则类名变为 hello-enter 等。


第三章:Vue Router

3.1 基本使用

路由概念
  1. 路由:就是一组 key-value 的对应关系
  2. 多个路由需经过路由器的管理
基本使用步骤

第一步:安装 vue-router

bash 复制代码
npm i vue-router

第二步:引入并应用插件

javascript 复制代码
import VueRouter from 'vue-router'
Vue.use(VueRouter)

第三步:创建路由器

javascript 复制代码
// router/index.js
import VueRouter from 'vue-router'
import About from '../components/About'
import Home from '../components/Home'

export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home
        }
    ]
})

第四步:在 Vue 实例中配置 router

javascript 复制代码
new Vue({
    el:'#app',
    render: h => h(App),
    router:router
})

第五步:编写路由链接

html 复制代码
<router-link class="list-group-item" active-class="active" to="/about">About</router-link>
<router-link class="list-group-item" active-class="active" to="/home">Home</router-link>

<!-- 指定组件的呈现位置 -->
<router-view></router-view>

3.2 嵌套路由

多级路由 :在父路由的 children 配置中添加子路由

javascript 复制代码
export default new VueRouter({
    routes:[
        {
            path:'/about',
            component:About
        },
        {
            path:'/home',
            component:Home,
            children:[
                {
                    path:'news',    // 不要加斜杠
                    component:News,
                },
                {
                    path:'message',
                    component:Message,
                }
            ]
        }
    ]
})

使用:在子路由组件中继续使用 <router-link><router-view>


3.3 路由的 query 参数

传递参数

html 复制代码
<router-link :to="{
    path:'/home/message/detail',
    query:{
        id:m.id,
        title:m.title
    }
}">
    {{m.title}}
</router-link>

接收参数

html 复制代码
<li>消息编号:{{$route.query.id}}</li>
<li>消息标题:{{$route.query.title}}</li>

3.4 命名路由

作用:可以简化路由的跳转

javascript 复制代码
routes:[
    {
        name:'guanyu',     // 给路由命名
        path:'/about',
        component:About
    }
]

使用

html 复制代码
<!-- 简化前 -->
<router-link to="/about">关于</router-link>

<!-- 简化后 -->
<router-link :to="{name:'guanyu'}">关于</router-link>

<!-- 传递query参数 -->
<router-link :to="{
    name:'xiangqing',
    query:{
        id:m.id,
        title:m.title
    }
}">
    {{m.title}}
</router-link>

3.5 路由的 params 参数

配置路由

javascript 复制代码
routes:[
    {
        path:'/home',
        component:Home,
        children:[
            {
                name:'xiangqing',
                path:'detail/:id/:title',  // 使用占位符声明接收 params 参数
                component:Detail,
            }
        ]
    }
]

传递参数

html 复制代码
<!-- 字符串写法 -->
<router-link :to="`/home/message/detail/${m.id}/${m.title}`">
    {{m.title}}
</router-link>

<!-- 对象写法 -->
<router-link :to="{
    name:'xiangqing',
    params:{
        id:m.id,
        title:m.title
    }
}">
    {{m.title}}
</router-link>

接收参数

html 复制代码
<li>消息编号:{{$route.params.id}}</li>
<li>消息标题:{{$route.params.title}}</li>

3.6 路由的 props 配置

作用:让路由组件更方便的收到参数

javascript 复制代码
routes:[
    {
        name:'xiangqing',
        path:'detail',
        component:Detail,
        
        // props的第一种写法,值为对象
        // 该对象中的所有key-value都会以props的形式传给Detail组件
        // props:{a:1, b:'hello'}
        
        // props的第二种写法,值为布尔值
        // 若布尔值为真,就会把该路由组件收到的所有params参数,以props的形式传给Detail组件
        // props:true
        
        // props的第三种写法,值为函数
        props($route){
            return {
                id:$route.query.id,
                title:$route.query.title,
                a:1,
                b:'hello'
            }
        }
    }
]

接收参数的组件:

javascript 复制代码
export default {
    name:'Detail',
    props:['id', 'title'],
}

  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为 push(追加历史记录)和 replace(替换当前记录)
  3. 默认是 push 模式
  4. 启用 replace 模式:<router-link replace to="/about">关于</router-link>

3.8 编程式路由导航

作用 :不借助 <router-link> 实现路由跳转,让路由跳转更加灵活

javascript 复制代码
methods: {
    pushShow(m){
        this.$router.push({
            name:'xiangqing',
            query:{
                id:m.id,
                title:m.title
            }
        })
    },
    replaceShow(m){
        this.$router.replace({
            name:'xiangqing',
            query:{
                id:m.id,
                title:m.title
            }
        })
    },
    back(){
        this.$router.back()      // 后退
    },
    forward(){
        this.$router.forward()  // 前进
    }
}

3.9 缓存路由组件

作用:让不展示的路由组件保持挂载,不被销毁

html 复制代码
<!-- 缓存一个路由组件 -->
<keep-alive include="News">
    <router-view></router-view>
</keep-alive>

<!-- 缓存多个路由组件 -->
<keep-alive :include="['News', 'Message']">
    <router-view></router-view>
</keep-alive>

3.10 两个新的生命周期钩子

钩子 说明
activated 路由组件被激活时触发
deactivated 路由组件失活时触发
javascript 复制代码
export default {
    name:'News',
    activated(){
        console.log('News组件被激活了')
    },
    deactivated(){
        console.log('News组件失活了')
    }
}

3.11 全局路由守卫

全局前置路由守卫

初始化时被调用、每次路由切换之前被调用

javascript 复制代码
router.beforeEach((to, from, next) => {
    console.log('前置路由守卫', to, from)
    if(to.meta.isAuth){ // 判断是否需要鉴权
        if(localStorage.getItem('school') === '前端开发教程'){
            next()
        }else{
            alert('学校名不对,无权限查看!')
        }
    }else{
        next()
    }
})
全局后置路由守卫

初始化时被调用、每次路由切换之后被调用

javascript 复制代码
router.afterEach((to, from) => {
    console.log('后置路由守卫', to, from)
    document.title = to.meta.title || '在线教育平台'
})

3.12 独享路由守卫

只有一个路由配置,不需要放行

javascript 复制代码
{
    name:'xinwen',
    path:'news',
    component:News,
    meta:{ isAuth:true, title:'新闻' },
    beforeEnter: (to, from, next) => {
        console.log('前置路由守卫', to, from)
        if(to.meta.isAuth){
            if(localStorage.getItem('school') === '前端开发教程'){
                next()
            }else{
                alert('学校名不对,无权限查看!')
            }
        }else{
            next()
        }
    }
}

3.13 组件内路由守卫

javascript 复制代码
export default {
    name:'About',
    // 路由进入之前调用
    beforeRouteEnter(to, from, next){
        // ...
        next()
    },
    // 路由离开之前调用
    beforeRouteLeave(to, from, next){
        // ...
        next()
    }
}

3.14 history 模式与 hash 模式

对比
模式 URL 格式 特点
hash #/about 兼容性好,地址栏带有 # 号
history /about 地址栏干净,但需要后端配置支持
配置 history 模式
javascript 复制代码
const router = new VueRouter({
    mode:'history',
    routes:[...]
})

注意:使用 history 模式部署到服务器时,需要后端配置支持,否则刷新页面会出现 404 错误。


第四章:Vuex

4.1 Vuex 简介

概念

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

Vuex 工作原理

Vuex 采用集中式状态管理架构,通过单向数据流实现组件间的高效通信。其核心组成包括:

什么时候使用 Vuex
  1. 多个组件依赖于同一状态
  2. 来自不同组件的行为需要变更同一状态

4.2 基本使用

安装 vuex
bash 复制代码
npm i vuex@3
创建 store
javascript 复制代码
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

// 准备 actions------用于响应组件中的动作
const actions = {
    jiaOdd(context, value){
        if(context.state.sum % 2){
            context.commit('JIA', value)
        }
    },
    jiaWait(context, value){
        setTimeout(() => {
            context.commit('JIA', value)
        }, 500)
    }
}

// 准备 mutations------用于操作数据(state)
const mutations = {
    JIA(state, value){
        state.sum += value
    },
    JIAN(state, value){
        state.sum -= value
    }
}

// 准备 state------用于存储数据
const state = {
    sum: 0
}

// 创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
})
组件中使用 Vuex
html 复制代码
<template>
    <div>
        <h1>当前求和为:{{$store.state.sum}}</h1>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
        <button @click="incrementOdd">当前求和为奇数再加</button>
        <button @click="incrementWait">等一等再加</button>
    </div>
</template>

<script>
    export default {
        name:'Count',
        data() {
            return { n: 1 }
        },
        methods: {
            increment(){
                this.$store.commit('JIA', this.n)
            },
            decrement(){
                this.$store.commit('JIAN', this.n)
            },
            incrementOdd(){
                this.$store.dispatch('jiaOdd', this.n)
            },
            incrementWait(){
                this.$store.dispatch('jiaWait', this.n)
            },
        },
    }
</script>
Vuex 工作流程
复制代码
组件 ------(dispatch)→ actions ------(commit)→ mutations ------(mutate)→ state ------(render)→ 组件
                ↑                                              |
                └───────────────── getters ────────────────────┘

4.3 getters 的使用

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

定义 getters
javascript 复制代码
const getters = {
    bigSum(state){
        return state.sum * 10
    }
}

export default new Vuex.Store({
    // ...
    getters
})
组件中使用 getters
html 复制代码
<h1>当前求和放大10倍为:{{$store.getters.bigSum}}</h1>

4.4 mapState 与 mapGetters

作用:帮助我们映射 state 和 getters 中的数据为当前组件的计算属性

javascript 复制代码
import { mapState, mapGetters } from 'vuex'

export default {
    computed:{
        // 借助mapState生成计算属性,从state中读取数据(对象写法)
        ...mapState({
            sum:'sum',
            school:'school',
            subject:'subject'
        }),
        
        // 借助mapState生成计算属性,从state中读取数据(数组写法)
        ...mapState(['sum', 'school', 'subject']),
        
        // 借助mapGetters生成计算属性,从getters中读取数据(数组写法)
        ...mapGetters(['bigSum'])
    }
}

4.5 mapMutations 与 mapActions

作用 :帮助我们生成与 mutations 和 actions 对话的方法,即:包含 $store.commit(xxx)$store.dispatch(xxx) 的函数

javascript 复制代码
import { mapMutations, mapActions } from 'vuex'

export default {
    methods:{
        // 借助mapMutations生成对应的方法,方法中会调用commit去联系mutations
        ...mapMutations({
            increment:'JIA',
            decrement:'JIAN'
        }),
        
        // 借助mapActions生成对应的方法,方法中会调用dispatch去联系actions
        ...mapActions({
            incrementOdd:'jiaOdd',
            incrementWait:'jiaWait'
        })
    }
}

注意:mapMutations 和 mapActions 使用时,需要在模板中绑定事件并传递参数。


4.6 多组件共享数据

将共享数据定义在 Vuex 的 state 中,各组件直接读取即可。


4.7 模块化编码

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

开启命名空间
javascript 复制代码
// store/count.js
export default {
    namespaced: true,
    actions: {
        jiaOdd(context, value){ ... },
        jiaWait(context, value){ ... }
    },
    mutations: {
        JIA(state, value){ ... },
        JIAN(state, value){ ... }
    },
    state: {
        sum: 0,
        school: '前端开发教程',
        subject: '前端'
    },
    getters: {
        bigSum(state){ ... }
    },
}
javascript 复制代码
// store/person.js
export default {
    namespaced: true,
    actions: {
        addPersonWang(context, value){ ... },
        addPersonServer(context){ ... }
    },
    mutations: {
        ADD_PERSON(state, value){ ... }
    },
    state: {
        personList: [
            {id:'001', name:'张三'}
        ]
    },
    getters: {
        firstPersonName(state){ ... }
    },
}
合并模块
javascript 复制代码
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import countOptions from './count'
import personOptions from './person'

Vue.use(Vuex)

export default new Vuex.Store({
    modules: {
        countAbout: countOptions,
        personAbout: personOptions
    }
})
组件中使用模块化数据
html 复制代码
<h1>当前求和为:{{$store.state.countAbout.sum}}</h1>
<h1>当前求和放大10倍为:{{$store.getters['countAbout/bigSum']}}</h1>
<button @click="$store.commit('countAbout/JIA', n)">+</button>

<!-- 使用 mapState -->
...mapState('countAbout', ['sum', 'school', 'subject']),

<!-- 使用 mapGetters -->
...mapGetters('countAbout', ['bigSum']),

<!-- 使用 mapMutations -->
...mapMutations('countAbout', { increment:'JIA', decrement:'JIAN' }),

<!-- 使用 mapActions -->
...mapActions('countAbout', { incrementOdd:'jiaOdd', incrementWait:'jiaWait' }),

总结

本教程系统介绍了 Vue2 的核心知识,包括:

模块 主要内容
Vue 基础 模板语法、数据绑定、计算属性、监视属性、生命周期、组件等
Vue 脚手架 ref、props、mixin、插件、scoped、组件通信、过渡动画等
Vue Router 基本使用、嵌套路由、参数传递、路由守卫、编程式导航等
Vuex state、mutations、actions、getters、模块化等
相关推荐
INF_MAX4 小时前
一些不错的页面设计(持续记录)
前端
乔江seven4 小时前
LlamaIndex 实现ReAct Agent
前端·python·react.js
私人珍藏库4 小时前
Edge v146.0.3856.97 内置篡改猴脚本
前端·edge
Thomas21434 小时前
chrome cdp 三种方案对比
前端·chrome
别叫我->学废了->lol在线等4 小时前
claudecode的agent定义
前端·chrome·github
用户806138166594 小时前
SDK(Software Development Kit,软件开发工具包)
前端
张元清4 小时前
在 React 中构建沉浸式 Web 应用:全屏、屏幕常亮与系统通知
前端·javascript·面试
王霸天4 小时前
💥大屏卡成 PPT?这 3 个性能优化招数亲测有效
前端·vue.js·数据可视化
ahhdfjfdf4 小时前
微信H5 页面定位权限处理
前端·javascript