VUE3学习笔记

117 阅读11分钟

1、将一个模块挂载到全局,从而不用每次在页面中引入。

// 以使用axios为例

// 首先,我们需要安装axios

// 然后,在main.js中,将它设置为全局属性

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import Axios from 'axios'

const app = createApp(App)
app.config.globalProperties.axios = Axios

app.mount('#app')

2、teleport

比如,我们正常写了div中嵌套了一个子元素span,渲染的结果也是span元素包裹在div中。这时候,如果我们希望渲染后,span元素是包裹在body元素中,而不是div中,我们就可以使用teleport

Vue3.x中的组件模板属于该组件,有时候我们想把模板的内容移动到当前组件之外的DOM种,这个时候就可以使用teleport

//span元素将挂载到body上面

<div>
    <teleport to="body">
        <span>子元素内容</span>
    </teleport>
<div>
        

image.png

// to的值可以是一个元素的id
<div>
    <teleport to="#app">
        <span>子元素内容</span>
    </teleport>
<div>

3、Composition API

composition API主要是为了解决代码维护比较复杂,复用性不高而生的。

vue2.x中,我们定义响应式数据,就是写在data中,定义方法就是写在methods中,监听数据变化就用watch,而composition-api为我们提供了另一种书写方式来实现我们的目的。

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

  • setup
    • 我们所有的组合式api的代码都需要写在setup这个方法当中
  • ref
    • 定义响应式数据的方法
    • 主要用于定义String/Number/Boolean/Array数据
  • reactive
    • 定义响应式数据的方法
    • 主要用于定义Object数据
  • watchEffect
  • watch
    • 这两个监听数据变化的方法和vue2中的watch基本一样
  • computed
    • 计算属性同vue2中的一样
  • toRefs
    • 主要是结构响应式对象数据的
  • 生命周期的hooks

(1) setup组件选项中定义响应式数据

import {ref, reactive} from 'vue'

export default {
    setup () {
        // ref定义响应式数据 string/number/boolean/array
        // reactive 定义响应式数据 object
        
        let title = ref('我是一个标题')
        let userInfo = reactive({
            username: '张三',
            age: 20
        })
        // 这里必须return出去,模板中才能获取到响应式数据
        return {
            title,
            userInfo
        }
    }
}

(2) setup组件选项中定义方法

import {ref, reactive} from 'vue'

export default {
    setup () {
        // ref定义响应式数据 string/number/boolean/array
        // reactive 定义响应式数据 object
        let title = ref('我是一个标题')
        let userInfo = reactive({
            username: '张三',
            age: 20
        })
        
        // 获取ref里面定义的数据
        let getTitle = () => {
            // 这里不能直接获取title,获取到的结果是[object,object]
            // 需要获取title.value,才能获取到具体的值
            alert(title.value)
        }
        
        // 获取reactive里面定义的数据
        let getUserName = () => {
            alert(userInfo.username)
        }
        
        // 设置ref里面定义的数据
        let setTitle = () => {
            // 设置同样需要改变title.value的值
            title.value = '我是改变的ref标题'
        }
        
        // 设置reactive里面定义的数据
        let setUserName = () => {
            userInfo.username = '李四'
        }
        // 这里必须return出去,模板中才能获取到响应式数据
        return {
            title,
            userInfo,
            getTitle,
            getUserName,
            setTitle,
            setUserName
        }
    }
}

(3) setup组件选项中使用toRefs解构响应式对象数据

比如,我们在reactive中定义了一个对象userInfo,如果我们想在模板中获取userInfousernameage,就只能通过userInfo.usernameuserInfo.age这种方式去获取。而toRefs可以让我们在模板中直接使用usernameage来获取具体的数据。

<template>
    <input v-model="title" type="text">
    <input v-model="age" type="text">
</template>
import {ref, reactive, toRefs} from 'vue'

export default {
    setup () {
        let article = reactive({
            title: '我是天下第一',
            count: 200
        })
        
        // 获取reactive里面定义的数据
        let getTitle = () => {
            alert(article.title)
        }
        
        // 设置reactive里面定义的数据
        let setTitle = () => {
            article.title = '天下无敌'
        }
        // 这里必须return出去,模板中才能获取到响应式数据
        // 方法一,使用三点运算符把article中的属性与当前对象进行合并,但是这种方法出来的属性不是响应式数据,当视图改变时,数据并没有改变。
        return {
            // article,
            getTitle,
            setTitle,
            ...article
        }
        // 方法二,使用`toRefs`来解构这个响应式对象,然后与当前`return`出的对象进行合并,这时候的属性还是响应式数据。
        return {
            // article,
            getTitle,
            setTitle,
            ...toRefs(article)
        }
    }
}

(4) setup组件选项中使用computed

<template>
    <input v-model="firstName" type="text" placeholder="fistName">
    <input v-model="lastName" type="text" placeholder="lastName">
    
    {{fullName}}
</template>
import {reactive, toRefs, computed} from 'vue'

export default {
    setup () {
        let userinfo = reactive({
            firstName: '',
            lastName: ''
        })
        
        // 定义计算属性方法
        let fullName = computed(() => {
            // 这里不能使用this,直接使用我们定义的响应式数据
            return userinfo.firstName + ' ' + userinfo.lastName
        })
        // 这里必须return出去,模板中才能获取到响应式数据
        return {
            fullName,
            ...toRefs(userinfo)
        }
    },
    computed: {
        // 这是模拟以前的写法,仅用于演示
        // 要注意的是,以前的这种写法是不能获取到组合式API中定义的变量
        fullName () {
            return userinfo.firstName + ' ' + userinfo.lastName
        }
    }
}

(5) setup组件选项中使用readonly

readonly深层的只读代理

比如,我们有一个响应式数据,当我们改变视图时,数据也会相应的发生变化。这时候,如果我们想让这个响应式数据变成非响应式数据,也就是页面发生变化时,数据并不发生变化,我们可以使用readonly

我们可以给readonly传入一个对象(响应式或普通)或者ref,这时会返回一个原始对象的只读代理。一个只读的代理是深层的,也就是对象内部任何嵌套的属性都是只读的。

<template>
    <h3>原始对象</h3>
    <input v-model="obj.username" type="text" placeholder="username">
    
    
    <input v-model="lastName" type="text" placeholder="lastName">
</template>
import {reactive, readonly} from 'vue'

export default {
    setup () {
        // 定义了一个原始对象,它是一个非响应式数据
        // 原始对象我们可以在页面中绑定它,但是没法在视图发生变化时,改变数据
        let obj = {
            username: '',
            age: ''
        }
        
        // 响应式数据
        let userinfo = reactive({
            firstName: '',
            lastName: ''
        })
        
        // 将一个响应式数据改为非响应式数据
        userinfo = readonly(userinfo)
        
        // 这里必须return出去,模板中才能获取到响应式数据
        return {
            obj,
            ...toRefs(userinfo)
        }
    }
}

(6) setup组件选项中使用watchEffectwatch实现监听

watchEffectwatch的区别:

  • watchEffect在第一次加载的时候,无论数据是否改变,都会执行一次。而watch是懒执行的,只会在数据改变时,才执行。
  • watch可以明确要监听哪个数据变化
  • watch可以获取变化前后的数据

使用watchEffect监听数据变化

import {reactive, toRefs, watchEffect} from 'vue'

export default {
    setup () {
        // 响应式数据
        let data = reactive({
            num: 1
        })
        let count = reactive({
            num: 1
        })
        
        // 监听data变化
        // 这里如果改变的是非data数据,比如是count,则不会发生变化
        // watchEffect可以监听到reactive返回对象的某个属性变化,而watch则不行
        watchEffect(() => {
            console.log(`num=${data.num}`)
        })
        
        // 设置一个定时器,每隔1s钟加1
        setInterval(() => {
            data.num++
        }, 1000)
        
        return {
            ...toRefs(data)
        }
    }
}

使用watch监听数据变化

<template>
    <input v-model="num" type="text" placeholder="num">
</template>
import {reactive, toRefs, watch} from 'vue'

export default {
    setup () {
        // 响应式数据
        let data = reactive({
            num: 1
        })
        
        // 监听data变化
        // 第一个参数是我们要监听的数据,这个数据必须是reactive返回的这个对象,而不能是这个对象的某个属性。
        // 第二个参数是回调函数,该函数的前两个参数分别是变化后的值和变化前的值。
        watch(data, (newValue, oldValue) => {
            console.log(`data.num=${data.num}`)
            console.log(newValue, oldValue)
        })
        
        // 设置一个定时器,每隔1s钟加1
        setInterval(() => {
            data.num++
        }, 1000)
        
        return {
            ...toRefs(data)
        }
    }
}

(7) setup组件选项中使用生命周期函数

在组合式API中使用生命周期函数,需要在前面加上on

在组合式API中,没有beforeCreatecreated这两个生命周期函数。因为setup就是在beforeCreate之前执行的。

image.png

import {reactive, watch, onMounted} from 'vue'

export default {
    setup () {  
        // 这里的执行顺序分别是setup、beforecreate、onmounted
        console.log('setup')
        // 响应式数据
        let userinfo = reactive({
            firstName: '',
            lastName: ''
        })
        
        onMounted(() => {
            console.log('onmounted')
        })
        
        // 这里必须return出去,模板中才能获取到响应式数据
        return {
            obj,
            ...toRefs(userinfo)
        }
    },
    beforeCreate () {
        console.log('beforecreate')
    }
}

(8) setup组件选项中使用props

import {reactive, watch, onMounted} from 'vue'

export default {
    props: ['msg'], // 指定接受的props
    setup (props) {  
        // setup的第一个参数,就是props
        console.log(props)
    }
}

(8) setup组件选项中使用provideinject

provideinject主要用于多层嵌套时,父级像子级传递数据时使用。

image.png

在Vue2中,在父组件中通过provide提供的的数据,并不是响应式数据。比如,父组件通过provide提供了一个title的数据,子组件引入title后绑定到模板中。当父组件改变title时,子组件中的title并不会发生改变。

在Vue3中,使用组合式API定义的provide提供的数据则是响应式数据,并且不同于props,子组件中改变数据的操作,也会影响父组件中数据的显示。比如,父组件通过provide提供了一个title的数据,子组件引入title后绑定到模板中。当父组件改变title时,子组件中的title会发生改变。同时,如果子组件中改变了title,父组件中的title也会发生改变。

// 父组件 parent.vue

import {ref, provide} from 'vue'

export default {
    setup () {  
        let title = ref("app根组件里面的title")
        // 这里是key/value的书写方式
        provide('title', title)
        
        return {
            title
        }
    }
}
// 子组件 child.vue

import {ref, inject} from 'vue'

export default {
    setup () {  
        // 引入组件中传入的数据
        let title = inject('title')
        
        return {
            title
        }
    }
}

4、Vue中集成Typescript

这样,我们就可以在项目中使用typescript的语法了。

// 安装vue-cli
npm install -global @vue/cli

// 创建一个项目
vue create my-project-name

// 然后进入项目目录
cd my-project-name

// 在项目中添加typescript
vue add typescript

安装过程选择为否的内容:

  • class-style
  • skip type checking of all declaration files (recommend for apps) ?

(1) 在vue3中,选项式API与typescript一起使用

  • 使用typescript的前提
// 1、要指定`script`标签的语言类型
<script lang="ts">
    // 2、引入defineComponent
    import {defineComponent} from 'vue'
    // 这里需要注意,使用ts的时候,引入组件的名称需要加上后缀
    import Home from './components/Home.vue'
    
    // 3、在暴露组件的时候,调用defineComponent这个方法,当我们在写具体的vue方法时,它会对我们写的内容进行校验。
    export default defineComponent({
        name: 'App',
        data () {
            return {
                title: '文章标题'
            }
        },
        components: {
            Home
        }
    })
</script>

typescript的具体使用方式:

  • 校验某个数据的类型
<script lang="ts">
    // 指定title为一个string类型
    let title:string = '我是一个Home组件' 
    import {defineComponent} from 'vue'
    
    export default defineComponent({
        name: 'App',
        data () {
            return {
                title
            }
        },
        methods: {
            // 这里void表示我们调用这个方法时,没有任何的返回值
            setTitle():void {
                // 这里赋值必须是字符串类型,如果是其他类型,就有校验失败,出现提示
                this.title = '改变后的home组件'
            }
        }
    })
</script>
  • 批量配置数据类型的方法
    • 可以结合接口来实现
<script lang="ts">
    // 1、定义一个接口,在这个接口里面指定所有数据的类型
    // News是接口的名称
    interface News {
        title: string,
        description: string,
        count: number | string, // 指定多个数据类型
        content?: string // 这是一个可选参数,也就是数据对象中可以没有这个数据
    }
    
    // 2、定义一个变量newsData实现我们上面定义的那个接口,也就是newsData里面的数据类型按照接口中的规则来定义。
    // 在newsData中,title/description/count必须要定义,content是可选参数,所以可有可无
    
    // 实现接口写法一
    let newsData:News = {
        title: '文章标题',
        description: '我是文章描述',
        count: 123
    }
    
    // 实现接口写法二
    let newsData = {
        title: '文章标题',
        description: '我是文章描述',
        count: 123
    } as News
    
    import {defineComponent} from 'vue'
    
    export default defineComponent({
        name: 'App',
        data () {
            return newsData
        },
        methods: {
            // 这里表示要取这个方法返回一个字符串类型
            reverseTitle():string {
                return this.title.split('').reverse().join('')
            }
        }
    })
</script>
  • 方法的类型校验
  • 批量配置数据类型的方法
    • 可以结合接口来实现
<script lang="ts">
    interface News {
        title: string,
        description: string,
        count: number | string, 
        content?: string 
    }

    let newsData:News = {
        title: '文章标题',
        description: '我是文章描述',
        count: 123
    }
    
    import {defineComponent} from 'vue'
    
    export default defineComponent({
        name: 'App',
        data () {
            return newsData
        },
        methods: {
            // 这里表示要取这个方法返回一个数字类型
            // 这个方法的参数必须是一个数字类型
            reverseTitle(count:number):number {
                return this.count++
            }
        }
    })
</script>

(2) 在vue3中,组合式API与typescript一起使用

  • reactive定义的数据方法实现类型校验

image.png

<script lang="ts">
    // 1、定义一个接口
    interface User {
        username: string,
        age: number,
        setUsername(username:string): void, // 有一个字符串类型的参数,并且这个函数不返回任何结果
        getUsername(): string // 这个函数返回一个字符串类型的结果
    }
    
    import {defineComponent, reactive, toRefs} from 'vue'
    
    export default defineComponent({
        setup () {
            // 实现接口写法一
            let user:User = reactive({
                username: '张三',
                age: 20,
                setUsername(username) { // 我们也可以在reactive中定义方法
                    // 这里访问数据需要用this
                    this.username = username
                },
                getUsrname() {
                    return this.username
                }
            })
            
            // 实现接口写法二
            // reactive这个方法接口的参数是一个泛型,这个泛型继承了object,所以,我们也可以使用方法二实现接口
            // 这里表示要对传入reactive的参数类型进行约束,规则就是我们接口指定的规则
            // 这种方式并不是所有方法都可以使用的,必须看方法的参数类型,如果方法的参数类型是泛型,才可以使用这种方法实现接口
            
            let user = reactive<User>({
                username: '张三',
                age: 20,
                setUsername(username) { // 我们也可以在reactive中定义方法
                    // 这里访问数据需要用this
                    this.username = username
                },
                getUsrname() {
                    return this.username
                }
            })
            
             // 实现接口写法三
            let user = reactive({
                username: '张三',
                age: 20,
                setUsername(username) { // 我们也可以在reactive中定义方法
                    // 这里访问数据需要用this
                    this.username = username
                },
                getUsrname() {
                    return this.username
                }
            }) as User
            
            return {
                ...toRefs(user)
            }
        }
    })
</script>
  • ref定义的数据方法实现类型校验

image.png

<script lang="ts">
    import {defineComponent, ref} from 'vue'
    
    export default defineComponent({
        setup () {
            // ref这个方法接口的参数是一个泛型,所以,我们也可以使用该方法二实现数据类型校 
            let count = ref<number | string>(20)
            
            // 注意,如果我们用reactive方法一的方式指定类型的时候,会报错
            // let num:string = ref(20)

            return {
                count
            }
        }
    })
</script>
  • 计算属性实现类型校验
<script lang="ts">
    import {defineComponent, ref} from 'vue'
    
    export default defineComponent({
        setup () {
            let count = ref<number | string>('张三')
            // 表示这个计算属性必须返回一个字符串类型的值
            let reverseUsername = computed(():string => {
                return count.split('').reverse().join('')
            })

            return {
                count,
                reverseUsername
            }
        }
    })
</script>

5、路由

路由可以让应用程序根据用户输入的不同地址动态挂载不同的组件。

image.png (1)使用步骤:

  • 1、安装路由模块
npm install vue-router --save
  • 2、准备要挂载的组件
<template>
    <div>user组件</div>
</template>
  • 3、配置路由

新建一个routes.ts文件配置路由


import {createRouter, createWebHashHistory} from 'vue-router'

// 引入组件
import User from './components/User.vue'

// 配置路由表
let routes = [
    {path: '/', component: User}
]

// 配置路由
const router = createRouter({
    history: VueRouter.createWebHashHistory(),
    routes
})

// 暴露router
export default router
  • 4、挂载路由

main.js中挂载路由

// main.js 入口文件
import {create} from 'vue'
import App from './App.vue'
import route from './routes'

let app = createApp(App)

// 挂载路由
app.use(route)
app.mount('#app')
  • 5、渲染组件

在模板中通过router-view渲染组件,也就是指定显示组件的位置

<template>
  <div>
    // 动态切换路由
    <ul>
      <li>
        <router-link to="/">首页</router-link>
      </li>
      <li>
        <router-link to="/user">用户页</router-link>
      </li>
    </ul>
        
    // 指定组件加载位置
    <router-view></router-view>
  </div>
</template>

(2)路由传值

  • 方式一:动态路由传值

1、配置动态路由,在路由表中指定我们要传递的参数

const router = createRouter({
    history: createWebHashHistory(),
    routes: [
        {path: '/newscontent/:id', component: NewsContent}
    ]
})

2、路由跳转

    <div v-for="{item, index} in list" :key="index">
      <router-link :to="`./newscontent/${index}`"></router-link>
    </div>

3、获取路由传参

this.$route.params
  • 方式二:GET传值

1、路由跳转

<router-link to="./newscontent?id=123"></router-link>

2、获取路由参数

this.$route.query

(3)路由编程式导航及传值

  • 动态路由:
this.$router.push({
    path: '/newscontent',
    parrams: {
        id: 123
    }
})
  • GET传值
this.$router.push({
    path: '/newscontent',
    query: {
        id: 123
    }
})

(4)路由HTML5 History模式和hash模式

改为hash模式的配置

默认配置为hash模式

import {createRouter, createWebHashHistory} from 'vue-router'

const router = createRouter({
    history: createWebHashHistory(),
    routes: [//...]
})
http://localhost:8080/#/user

hash模式的路径,默认自带/#,如果想把它去掉,变成下面的模式,则可以使用HTML5 History模式

http://localhost:8080/user

改为HTML5 History模式的设置

import {createRouter, createWebHistory} from 'vue-router'

const router = createRouter({
    history: createWebHistory(),
    routes: [//...]
})

注意:开启html5 history模式后,发布到服务器需要配置伪静态。

(5)命名路由

  • 动态路由

1、配置路由表

const router = new VueRouter({
    routes: [
        {
            path: '/user:userId',
            name: 'user',
            component: User
        }
    ]
})

2、路由跳转

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

1、配置路由表

const router = new VueRouter({
    routes: [
        {
            path: '/user',
            name: 'user',
            component: User
        }
    ]
})

2、路由跳转

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

(6)路由重定向

路由重定向就是,在用户输入/a的路径时,我们会把把它替换为/b的路径,然后去匹配/b

重定向也需要在routes配置中完成

const routes = [{path: '/', redirect: '/home'}]

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

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

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

const routes = [
    {
        path: '/search/:searchText',
        redirect: to => {
            return {path: '/search', query: {q: to.params.searchText}}
        }
    }
]

(7)路由别名

别名就是,当用户访问/a路径时,用户看到的路径不变变化,就是/a,但是,实际上匹配的是路径/b的内容

别名的配置方式和path的配置方式一样,就是一个路径。

const routes = [{path: '/home', component: homepage, alias: '/homeAlias'}]

配置多个别名

const routes = [
    {path: '/home', 
     component: homepage, 
     alias: ['/homeAlias1', '/homeAlias2']}
]

动态路由别名传参

const routes = [{path: '/home', component: homepage, alias: '/homeAlias/:id'}]

(8)路由嵌套

const router = new VueRouter({
    routes: [
        {
            path: '/user',
            name: 'user',
            component: User,
            children: [
                {path:'', redirect: '/user/userlist'},
                {path: 'userlist', component: UserList},
                {path: 'useradd', component: UserAdd}
            ]
        }
    ]
})

6、Vue状态管理模式Vuex

Vuex是一个专门为vue.js应用程序开发的状态管理模式。

主要功能:

  • 实现不同组件之间的状态共享
  • 可以实现组件里面数据的持久化

实现数据缓存的几种方式:

  • localStorage
  • keep-alive
  • vuex

vuex的几个概念

  • state:定义数据
  • Getters:相当于计算属性
  • Mutations:相当于方法
  • Actions:触发mutaions里面的方法,可以写一些异步逻辑
  • Modules:模块

(1)vuex使用步骤

  • 1、安装vuex
npm install vuex --save
  • 2、src目录下面新建一个vuex的文件夹,vuex文件夹里面新建一个store.js
import {createStore} from 'vuex'
const store = createStore({
    state () {
        return {
            count: 1
        }
    },
    getters: {
        doneTodosCount (state) {
            return state.msg.split('').reverse().join('')
        }
    },
    mutations: {
        increment (state) {
            state.count++
        }
    }
})

export default store
  • 3、main.ts中挂载vuex
// main.js 入口文件
import {create} from 'vue'
import App from './App.vue'
import route from './routes'
import store from './vuex/store'

let app = createApp(App)

// 挂载路由
app.use(route)
// 挂载vuex
app.use(store)
app.mount('#app')

(2)将vuex中state数据渲染到页面

  • 方法一:用到的组件里面引入store,然后计算属性里面获取(不推荐)
computed: {
    count () {
        return store.state.count
    }
}
  • 方法二:由于全局配置了Vuex app.use(store),所以可以直接通过下面方法获取store里面的值
computed: {
    count () {
        return this.$store.state.count
    }
}
  • 方法三:通过mapState助手

如果,当前页面需要使用的state数据量很大,我们可以通过mapState批量把state中的数据映射到当前页面

它的用法就是,在当前组件中的计算属性中,通过扩展运算符将mapState返回的对象与当前计算属性中的对象合并。

如果我们当前组件映射的数据名称和state中的数据名称一样,我们可以直接使用一个数组

computed: {
    // 数组中的选项,就是state中数据的名称
    ...mapState([        'count',        'list'    ])
}

如果我们当前组件映射的数据名称和state中的数据名称不一样,我们可以直接使用一个对象

computed: {
    // 传入的这个对象的属性名就是我们自己想要的变量名,后面则是state中映射的数据
    ...mapState({
        myCount: state => state.count,
        myList: state => state.list
    })
}

(3)将vuex中getters渲染到页面

getters就相当于vuex中的计算属性,在页面中的引入方式和state一样。

  • 方法一:(不推荐)
computed: {
    count () {
        return store.getters.doneTodosCount
    }
}
  • 方法二:
computed: {
    count () {
        return this.$store.getters.doneTodosCount
    }
}
  • 方法三:通过mapGetters助手

如果我们当前组件映射的数据名称和mapGetters中的数据名称一样,我们可以直接使用一个数组

computed: {
    // 数组中的选项,就是state中数据的名称
    ...mapGetters([        'doneTodosCount'    ])
}

如果我们当前组件映射的数据名称和mapGetters中的数据名称不一样,我们可以直接使用一个对象

computed: {
    ...mapGetters({
        myDoneCount: 'doneTodosCount'
    })
}

(4)改变vuex中state的数据(Mutations使用)

  • 首先,在store.js中定义一个改变state的方法,这个方法里面有具体的逻辑处理。
import {createStore} from 'vuex'
const store = createStore({
    state () {
        return {
            count: 1
        }
    },
    // 用于保存改变state的所有方法
    mutations: {
        // 方法的第一个参数就是state
        increment (state) {
            state.count++
        }
    }
})

export default store
  • 然后,在页面中触发mutations中某个具体的方法
export default {
    methods: {
        inCount () {
            // 触发mutations中的某个方法
            this.$store.commit('increment')
        }
    }
}

触发mutations中方法时传参

// store.js

import {createStore} from 'vuex'
const store = createStore({
    state () {
        return {
            count: 1
        }
    },
    mutations: {
        setCount (state, num) {
            state.count = num
        }
    }
})

export default store
// home.vue
export default {
    methods: {
        inCount () {
            this.$store.commit('setCount', 123)
        }
    }
}

(5)改变vuex中state的数据(Actions使用)

mutations主要写一些改变state的方法,这些方法可以是一些异步方法,比如,写一个定时器,过了1s钟,让数字加一,这种需求可以实现。但是,当我们想要在mutations某个方法中调用其他mutations的方法时,这时候就不行了,mutations不支持这种操作。

actions则没有这种问题,它可以执行mutations中多个不同的方法。在actions中我们可以写一些异步操作。当然,我们也可以把这些异步处理逻辑写在页面中,然后在页面中触发不同的mutations方法。

  • 首先,在store.js中定义一个改变state的方法,这个方法里面有具体的逻辑处理。
import {createStore} from 'vuex'
const store = createStore({
    state () {
        return {
            count: 1
        }
    },
    // 用于保存改变state的所有方法
    mutations: {
        // 方法的第一个参数就是state
        increment (state, msg) {
            state.count = msg
        }
    },
    actions: {
        inCount (context, msg) {
            // 当这里的异步方法成功或失败时,我们可以执行不同的mutations方法
            setTimeout(() => {
                context.commit('increment', msg) // 执行mutations中方法
            }, 1000)
        }
    }
})

export default store
  • 然后,在页面中触发actions中某个具体的方法
export default {
    methods: {
        inCount () {
            // 触发actions中的某个方法
            this.$store.dispatch('inCount', 123)
        }
    }
}

(5)vuex中Modules使用

当数据量特别大时,所有的数据方法都集中在stote.js中,store对象可能会变得很臃肿。Vuex允许我们将store分割成模块,每个模块都有自己的state/mutations/action/getter,甚至是嵌套子模块,从上到下进行同样方式的分隔。

// moduleA
const moduleA = {
    state: () => ({...}),
    mutations: () => ({...}),
    actions: () => ({...}),
    getters: () => ({...}),
}
export default moduleA
// moduleB
const moduleB = {
    state: () => ({...}),
    mutations: () => ({...}),
    actions: () => ({...}),
    getters: () => ({...}),
}
export default moduleB
// store.js

import moudleA from './moduleA'
import moduleB from './moduleB'

const store = createStore({
    modules: {
        a: moduleA,
        b: moduleB
    }
})

export default store

在页面中使用子模块中的数据

<template>
    // 获取a模块中的count,这里在state后面需要加上子模块的名称
    <div>{{$store.state.a.count}}</div>
</template>

在页面中使用子模块中的方法

export default {
    methods: {
        inCount () {
            // 这里调用时候,就不在需要加上子模块的名称了
            // 需要注意,两个子模块如果有一样的名称,两个方法会同时触发,所以应该避免两个子模块中的方法名一样
            this.$store.commit('increment')
        }
    }
}

(6)vuex结合Composition API

import {computed, defineComponent} from 'vue'
// 1.引入useStore
import {useStore} from 'vuex'

export default defineComponent({
    setup() {
        // 2.获取store对象
        const store = useStore()
        
        return {
            count: computed(() => {
                return store.state.count
            }), // 获取state数据
            num: computed(() => {
                return store.getters.num
            }), // 获取getters
            inCount: () => {
                store.commit('inCount')
            }, // 触发mutations方法
            inActionCount: () => {
                store.dispach('setCount', 123)
            }
        }
    }
})

(6)vue结合TS的项目中使用vuex

使用步骤

  • 1、安装vuex
npm install vuex --save
  • 2、src目录下面新建一个vuex的文件夹,vuex文件夹里面新建一个store.ts
import {ComponentCustomProperties} from 'vue'
import {createStore, Store} from 'vuex'

// 配置vue+ts的项目里面使用vuex
// 这是个固定写法
declare module '@vue/runtime-core' {
    // 声明接口,指定state中数据的类型
    interface State {
        count: number, // 数字类型
        list: string[], // string类型数组
        msg: string
    }
    
    interface ComponentCustomProperties {
        $store: Store<State>
    }
}

const store = createStore({
    state () {
        return {
            count: 1,
            msg: '你好 vue',
            list: ['张总', '王总']
        }
    },
    getters: { // 计算属性
        // 这里需要为state指定any类型,否则会有警告提示,并且它找不到state下面的msg
        doneTodosCount (state:any) {
            return state.msg.split('').reverse().join('')
        }
    },
    mutations: { // 方法
        inCount (state:any) {
            state.count++
        },
        setCount(state:any, num:number) {
            state.count = num
        },
        setMsg (state:any, msg:string) {
            state.msg = msg
        }
    }
})

export default store
  • 3、main.ts中挂载vuex
// main.js 入口文件
import {create} from 'vue'
import App from './App.vue'
import route from './routes'
import store from './vuex/store'

let app = createApp(App)

// 挂载路由
app.use(route)
// 挂载vuex
app.use(store)
app.mount('#app')

组件中使用TS 有时,我们在集成ts以后,在组件里面使用this.$store的时候可能会出现警告信息,这时候我们可以安装一个vue3 snipts的插件,然后在重启vscode就可以了。

  • 选项式API
// 声明语言类型为ts
<script lang="ts">
    import {mapState} from 'vuex'
    export default {
        computed: {
            // 因为这里对state做了映射,它不知道state的数据类型,所以这里需要指定state为any类型,否则会报错
            ...mapState({
                num: (state:any) => state.count
            })
        },
        methods: {
            // 当前函数没有任何返回值
            inCount():void {
                this.$store.commit('inCount')
            }
        }
    }
</script>
  • 组合式API
// 声明语言类型为ts
<script lang="ts">
    import {defineComponent} from 'vue'
    import {useStore} from 'vuex'
    export default defineComponent({
        setup() {
            const store = useStore()
            
            return {
                msg: computed(() => {
                    store.state.msg
                }),
                count: computed(() => {
                    store.state.count
                }),
                setMsg: ():void => {
                    store.commit('setMsg', '组合式API里面改变后的数据')
                }
            }
        }
    })
</script>