从零开始的web前端学习-Vue

这篇文章仅仅是分享 Vue 的部分内容,并没有完整地对其进行学习总结,想要完整地学习 Vue,请移至:https://cn.vuejs.org/guide/essentials/template-syntax.html

1初始化

npm create vite@latest(npm 安装 vite,通过 vite 对 vue 项目进行构建)

在通过 vite 构建好 vue 项目文件夹后,输入命令行指令对 vue 项目进行初始化

也可以直接通过 npm create vue@latest 命令构建,vue 默认使用 vite 进行管理

当然,如果是简单的项目,vue 也支持 cdn 方式引入

2响应式数据与插值表达式

<!-- Basic.vue -->
<script>
    export default {
        data() {
            return {
                name: "Jack",
            }
        },
        methods: {
            output() {
                return `this student's name is Jack `
            }
        }
    }
</script>

<template>
    <p>{{ name }}</p>

    <p>{{ 1 + 2 + 3 + 4 }}</p>
    <p>{{ 1 > 2 ? "right" : "wrong" }}</p>
    
    <p>{{ output() }}</p>
</template>

vue 内置 data 方法可以通过 return 返回数据,methods 对象包含着需要定义的方法,插值表达式 {{ }},可以将 data 方法的返回值进行渲染,也可以内部进行一些逻辑的运算并对结果进行呈现

3计算属性

计算属性与 methods 对象类似,都是方法的集合,但是计算属性具有缓存性,在响应式变量没有改变时,不会重复计算,而是输出缓存结果

<!-- Computed.vue -->
<script>
    export default {
        data() {
            return {
                number: 2,
            }
        },
        computed: {
            compare() {
                console.log('compare function run')
                return this.number > 4 ? `${this.number} is over 4` : `${this.number} is not over 4`
            }
        }
    }
</script>

<template>
    <p>{{ number }}</p>

    <p>{{ compare }}</p>
    <p>{{ compare }}</p>
</template>

注意,上述确实会生成两个具有相同内容的 p 标签,但是控制台输出仅有一次,即没有重复计算,并且,计算属性内部虽然写成函数形式,但是外部调用是不能加括号的

4侦听器

侦听器的主要功能是在响应式变量更新后,渲染页面的同时执行一些其它操作

<!-- Listener.vue -->
<script>
    export default {
        data() {
            return {
                isTrue: false
            }
        },
        methods: {
            change() {
                this.isTrue = true
            }
        },
        watch: {
            isTrue(newValue, oldValue) {
                console.log(`(new, old): (${newValue}, ${oldValue})`)
            }
        }
    }
</script>

<template>
    <p>{{ change() }}</p>
</template>

5指令

指令 说明
v-text 渲染文本,无法对标签进行渲染
v-html 可渲染标签
v-for 循环渲染
v-if 选择性渲染,不渲染时会被移除
v-show 选择性隐藏
v-bind 属性绑定响应式变量
v-on 事件绑定
v-model 绑定表单元素,实现双向数据绑定
<!-- Directives.vue -->
<script>
    export default {
        data() {
            return {
                text: "<span>text</span>",
                html: "<span>html</span>",
                name: "Jack",
                status: "pending",
                tasks: ["task one", "task two", "task three"],
                link: "https://cn.vuejs.org/guide/essentials/template-syntax.html",
                newTask: "",
                person: {
                    age: 18,
                    sex: "male",
                    isStudent: true
                }
            }
        },
        methods: {
            toggleStatus() {
                if (this.status === "active") {
                    this.status = "pending"
                } else if (this.status === "pending") {
                    this.status = "inactive"
                } else {
                    this.status = "active"
                }
            },
            addTask() {
                if (this.newTask.trim() !== "") {
                    this.tasks.push(this.newTask)
                    this.newTask = ""
                }
            },
            removeTask(index) {
                this.tasks.splice(index, 1)
            }
        }
    }
</script>

<template>
    <p v-text="text"></p>
    <p v-html="html"></p>

    <br>

    <p v-if="status === 'active'">{{ name }} is active</p>
    <p v-else-if="status === 'pending'">{{ name }} is pending</p>
    <p v-else>{{ name }} is inactive</p>

    <br>

    <p v-show="false">hidden content</p>

    <form @submit.prevent="addTask">
        <input type="text" name="newTask" id="newTask" placeholder="add task" v-model="newTask">
        <button type="submit">add task</button>
    </form>

    <br>

    <h2>cycle</h2>
    <p v-for="i in 5">{{ i }}</p>

    <br>

    <h2>list of tasks</h2>
    <ul>
        <li v-for="(task, index) in tasks" :key="index"><span>{{ task }}&nbsp;</span><button @click="removeTask(index)">x</button></li>
    </ul>

    <br>

    <h2>object cycle</h2>
    <p v-for="(item, key, index) in person">item: {{ item }}, key: {{ key }}, index: {{ index }}</p>

    <br>

    <a v-bind:href="link">go to vue documnet</a>
    <!-- <a :href="link">go to vue documnet</a>  -->

    <br>

    <button v-on:click="toggleStatus">change status</button>
    <!-- <button @click="toggleStatus">change status</button> -->
</template>

在 addTask 函数中,我们利用 JavaScript 中的 trim 函数实现空格的清除,实际上我们也可以在 v-model 后加入修饰符,如:v-model.trim,即可实现同样效果

6组件通信

  1. 父传子
    vue 父传子组件通信与 react 类似,同样通过 props 机制进行数据传输
<!-- Comunication.vue -->
<script>
    export default {
        props: {
            name: String,
            age: {
                type: [Number, String],
                default: 18,
                // required: true, 
            },
            isStudent: Boolean
        }
    }
</script>

<template>
    <p>{{ name }} is {{ age }} years old, isStudent: {{ isStudent }}</p>
</template>

type 代表该数据的类型(可为多类型),default 为该数据默认值,required 代表该数据父组件必须传递

<!-- App.vue -->
<script>
  export default {
    components: {
      Communication
    }
  }

  import Communication from './components/Communication.vue';
</script>

<template>
  <h1>Vue Demo</h1>
  <Communication name="Jack" v-bind:is-student="parentIsStudent"/>
</template>
  1. 子传父
    vue 子传父组件通信则需要通过自定义事件进行实现
<!-- Comunication.vue -->
<script>
    export default {
        data() {
            return {
                childCount: 0
            }
        },
        emits: ["childCountAdd"],
        methods: {
            addCount() {
                this.childCount++
                this.$emit("childCountAdd", this.childCount)
            }
        }
    }
</script>

<template>
    <button v-on:click="addCount">add</button>
</template>

注意,在 vue3 中需要先在 emits 中声明自定义函数

<!-- App.vue -->
<script>
  export default {
    data() {
      return {
        childData: 0
      }
    },
    methods: {
      getChildCount(childCount) {
        this.childData = childCount
      }
    },
    components: {
      Communication
    }
  }

  import Communication from './components/Communication.vue';
</script>

<template>
  <h1>Vue Demo</h1>
  <Communication @childCountAdd="getChildCount"/>
  <p>{{ childData }}</p>
</template>

7组件插槽

组件插槽,简单来说,就是标签内容,通过双标签实现差异化,不仅如此,插槽也可以实现父组件“调用”子组件变量

<!-- Slot.vue -->
<script>
    export default { 
        data() {
            return {
                count: 0
            }
        }
    }
</script>

<template>
    <br>
    <slot>default content</slot>
    <br>
    <slot name="footer">default footer</slot>
    <br>
    <slot name="dataOne" v-bind:count="count">data content one</slot>
    <br>
    <slot name="dataTwo" v-bind:count="count">data content two</slot>
    <br>
</template>
<!-- App.vue -->
<script>
  export default {
    data() {
      return {  }
    },
    methods: {  },
    components: {
      Slot
    }
  }

  import Slot from './components/Slot.vue';
</script>

<template>
  <h1>Vue Demo</h1>
  <Slot></Slot>
  <Slot>content one</Slot>
  <Slot>content two<template v-slot:footer>input footer two</template></Slot>
  <Slot>content three<template #dataOne="dataObj">{{ dataObj.count }}</template></Slot>
  <Slot>content three<template #dataTwo="{ count }">{{ count }}</template></Slot>
</template>

注意,父组件“调用”子组件变量,是因为变量本质是定义在子组件上,然后插槽的实质也是在子组件内部调用,然后该调用的返回值是一个对象,可以通过对象属性访问或者解构赋值的方式访问变量

8Vue Router

安装 vue-router 包:npm install vue-router@4,vite 项目构建默认已安装 vue-router 插件

在 src 文件夹下创建 views 文件夹,该文件夹主要存储页面文件

<!-- Home.vue -->
<script>
    export default {  }
</script>

<template>
    <h1>Home Page</h1>
    <p>This is home page.</p>
    <a href="./#/About">goto</a>
</template>
<!-- About.vue -->
<script>
    export default {  }
</script>

<template>
    <h1>About Page</h1>
    <p>This is about page.</p>
    <a href="./#/">goto</a>
</template>

在 src 文件夹下创建 router 文件夹,并创建 index.js 文件

// src/router/index.js
import { createRouter, createWebHashHistory } from 'vue-router';
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';

const routes = [
  { path: '/', name: 'Home', component: Home },
  { path: '/about', name: 'About', component: About }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

注意:

属性 说明
path 实际 url 地址
name 逻辑 url 地址,映射实际地址
component 对应 url 地址显示组件

对于一些初始不用渲染的组件,我们可以使用懒加载进行优化,仅在使用时加载,如 About 组件,可以进行以下的懒加载优化

const About = () => import('@/views/About.vue')

以上,我们的简易 router 搭建完毕,然后我们需要引入 router

// src/main.js
import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)
app.use(router)
app.mount('#app')

接着我们在 App.vue 中添加 <router-view></router-view> 标签即可显示页面内容,也可以加入类似 <router-link to="/about">About</router-link> 的 nav 链接组实现各页面切换

动态路由:简单来讲就是为 url 添加查询参数实现不同的页面内容

<!-- Number.vue -->
<script>
    export default {
        props : ['number']
    }
</script>

<template>
    <p>Number: {{ number }}</p>
</template>

{ path: '/number/:number', name: 'Number', component: Number, props: true }(/router/index.js 中 routes 对象添加路径信息)

<!-- App.vue -->
<script>
    export default {  }
</script>

<template>
  <nav>
    <router-link to="/number/2">Number2</router-link> |
    <router-link :to="{ name: 'Number', params: { number: 4 } }">Number4</router-link>
  </nav>
  <router-view></router-view>
</template>

注意,我们对于路径的设置存在两种方式,已在动态路由示例中演示

嵌套路由:简单来讲,即为原 url 路径添加子路径,与动态路由类似,但是嵌套路由是固定的不同路由,而动态路由则是根据某一查询参数变化而动态变化的路由

<!-- ProductComment.vue -->
<script>
    export default {  }
</script>

<template>
    <h1>Comment Page</h1>
    <p>This page provides comments of some products.</p>
</template>
<!-- Product.vue -->
<script>
    export default {  }
</script>

<template>
    <h1>Product Page</h1>
    <p>This is product page.</p>
    <nav>
        <router-link :to="{ name: 'product-comment'}">comment</router-link>
    </nav>
    <router-view></router-view>
</template>

{ path: '/product', name: 'Product', component: Product, children: [{ path: 'comment', name: 'product-comment', component: ProductComment }] }(/router/index.js 中 routes 对象添加路径信息)

<router-link to="/product">Product</router-link>(App.vue 中添加链接)

编程式导航:在某些情况下,实现自动跳转到某一路径

<!-- BackHome.vue -->
<script>
    export default {
        data() {
            return {
                timer: null
            }
        },
        created() {
            this.timer = setTimeout(() => {
                this.$router.push({ name: 'Home' })
            }, 3000)
        },
        beforeDestroy() {
            if (this.timer) {
                clearTimeout(this.timer)
            }
        }
    }
</script>

<template>
    <h1>Back Home Page</h1>
    <p>It will go home after 3 seconds.</p>
</template>

this.$router 能够指向 router 中设定的路由,上述代码实现 3 秒后跳转 home 页面

路由传参:各个 url 路径间传递数据

<!-- Deliver.vue -->
<script>
export default {
    data() {
        return {
            timer: null
        }
    },
    created() {
        this.timer = setTimeout(() => {
            this.$router.push({ name: 'product-comment', query: { comment: 'the product is good!' } })
        }, 3000)
    },
    beforeDestroy() {
        if (this.timer) {
            clearTimeout(this.timer)
        }
    }
}
</script>

<template>
    <p>It will send a good commnet after 3 seconds in console.</p>
</template>
<!-- Product.vue -->
<script>
export default {
    created() {
        console.log(this.$route.query)
    }
}
</script>

<template>
    <h1>Product Page</h1>
    <p>This is product page.</p>
    <nav>
        <router-link :to="{ name: 'product-comment' }">comment</router-link> |
        <router-link :to="{ name: 'back-home' }">back</router-link> |
        <router-link :to="{ name: 'deliver-comment' }">deliver</router-link>
    </nav>
    <router-view></router-view>
</template>

注意此处的 this.$route 与之前的 this.$router 不同,之前的 router 是对路由进行一些操作,而 route 则是对路由数据进行一些操作

上述代码在由 Deliver 页面跳转至 ProductComment 页面后,会在控制台输出一条评论信息

导航守卫:对所有导航做统一设置

// src/router/index.js
import { createRouter, createWebHashHistory } from 'vue-router';
import Home from '@/views/Home.vue';

const About = () => import('@/views/About.vue')
const Number = () => import('@/views/Number.vue')
const Product = () => import('@/views/Product.vue')
const ProductComment = () => import('@/views/product/ProductComment.vue')
const BackHome = () => import('@/views/product/BackHome.vue')
const Deliver = () => import('@/views/product/Deliver.vue')

const routes = [
  { path: '/', name: 'Home', component: Home },
  { path: '/about', name: 'About', component: About },
  { path: '/number/:number', name: 'Number', component: Number, props: true },
  { path: '/product', name: 'Product', component: Product, children: [{ path: 'comment', name: 'product-comment', component: ProductComment }, { path: 'back', name: 'back-home', component: BackHome }, { path: 'deliver', name: 'deliver-comment', component: Deliver}] }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

router.beforeEach((to, from, next) => {
  console.log('router run')
  next()
})

export default router

其中,router.beforeEach 是导航守卫函数之一,to 代表着前往路由,from 代表出发路由,next 代表切换路由函数,若不执行该函数,则页面不会相应更新

9Vuex

Vuex 是组件状态管理工具,安装 vuex 包:npm install vuex,vite 项目构建默认已安装 vuex 插件

在 src 文件夹下创建 store 文件夹,并创建 index.js 文件

// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
    state() {},
    getters: {},
    mutations: {},
    actions: {},
    modules: {}
})

export default store

然后,需要导入 store

// src/main.js
import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')

state:简单来讲,是全局数据存储,所有组件皆可使用 state 中的数据

// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
    state() {
        return {
            isLogin: true
        }
    }
})

export default store
<!-- TestStore.vue -->
<script>
export default {
    data() {
        return {
            isLogin: this.$store.state.isLogin
        }
    }
}
</script>

<template>
    {{ isLogin }}
</template>

注意,通过 this.$store.state 访问 state 中的数据

实际上,可以直接通过赋值的方式修改 state 中的数据,但是这会破坏全局数据的一致性,所以并不建议这么做

通过 data 访问全局数据会更加规范,但是直接访问也是可行的

mutations:提供修改 state 中数据的途径

// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
    state() {
        return {
            count: 0
        }
    },
    mutations: {
        addCount(state, num) {
            state.count += num
        }
    }
})

export default store
<!-- TestStore.vue -->
<script>
export default {
    methods: {
        add() {
            this.$store.commit('addCount', 1)
        }
    }
}
</script>

<template>
    {{ this.$store.state.count }}
    <button v-on:click="add">add</button>
</template>

注意访问 mutations 方法的方式是 this.$store.commit 请求,而非 this.$store.mutations 属性,当然也可以直接使用 mutations 方法,但是通过 methods 方法的方式使用 mutations 方法会更加规范

actions:mutations 是同步操作,对于异步操作,只会记录最终结果,而 actions 可以理解为异步的 mutations,但是注意,修改依旧依赖于 mutations 方法,actions 仅仅是添加异步操作

// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
    state() {
        return {
            count: 0,
            timer: null
        }
    },
    mutations: {
        addCount(state, num) {
            state.count += num
        },
        setDelayTimer(state, timer) {
            state.timer = timer
        },
        clearDelayTimer(state) {
            state.timer = null
        }
    },
    actions: {
        delayMinus(store, num) {
            if (store.state.timer) {
                clearTimeout(store.state.timer)
            }
            
            const timer = setTimeout(() => {
                store.commit('addCount', -num)
                store.commit('clearDelayTimer')
            }, 3000)
            
            store.commit('setDelayTimer', timer)
        }
    }
})

export default store
<!-- TestStore.vue -->
<script>
export default {
    methods: {
        delayMinus() {
            this.$store.dispatch('delayMinus', 1)
        }
    }
}
</script>

<template>
    {{ this.$store.state.count }}
    <button @click="delayMinus">delay minus</button>
</template>

上述代码实现延迟 3 秒减一操作,若不进行删除定时器操作,效果类似,但是定时器冗余,可能会降低执行效率

注意,优先同步操作,后异步操作

getters:可以理解为全局的计算属性

// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
    state() {
        return {
            count: 0
        }
    },
    getters: {
        compare(state) {
            console.log('compare run')
            return state.count > -1
        }
    }
    modules() {}
})

export default store
<!-- TestStore.vue -->
<script>
export default {
    methods: {
        compare() {
            console.log(this.$store.getters.compare)
        }
    }
}
</script>

<template>
    <button @click="compare">compare</button>
</template>

modules:划分模块,各模块间不互相依赖,或者可以理解为封装,将针对某一部分用户的变量、方法等进行封装

// src/store/index.js
import { createStore } from "vuex";

const store = createStore({
    modules: {
        student: {
            state() {
                return {
                    id: '001'
                }
            },
            getters: {},
            mutations: {},
            actions: {}
        }
    }
})

export default store

对此的访问也需要稍微改变: this.$store.state.student.id

当然,你也可以真正将 modules 中的对象封装为单独的 JavaScript 文件

// src/store/module/worker.js
const worker = {
    state() {
        return {
            job: 'cleaner'
        }
    },
    getters: {},
    mutations: {},
    actions: {}
}

export default worker
// src/store/index.js
import { createStore } from "vuex";
import worker from "./module/worker";

const store = createStore({
    modules: {
        student: {
            state() {
                return {
                    id: '001'
                }
            },
            getters: {},
            mutations: {},
            actions: {}
        },
        worker: worker
    }
})

export default store

10vue3

vue3 是当前 vue 的默认版本,与 vue2 存在一些差别,但是大部分内容是相同的

vue 现在存在选项式与组合式两种代码风格,vue2 语法多为选项式,而 vue3 则提供一种新的写法,即组合式 API

选项式 API 会导致某一功能的代码较为分散,组合式 API 则能够将同一功能的实现集中起来,会更加类似于 JavaScript

在组合式 API 中,直接通过 const 或者 let 声明的变量都不是响应式的,对其进行修改并不能生效,需要通过相应的函数创建响应式数据

响应式数据-reactive:reactive 函数能够创建响应式数据,但是仅能创建对象、数组、映射、元组等对象类型数据

<!-- Reactive.vue -->
<script>
import { reactive } from 'vue';

export default {
    setup() {
        const numArray = reactive([1, 2, 3])

        function pushNumber() {
            numArray.push(4)
        }

        return {
            numArray,
            pushNumber
        }
    }
}
</script>

<template>
    <p>{{ numArray }}</p>
    <button @click="pushNumber">push</button>
</template>

在代码中,也能够看出组合式 API 的一种实现方式,即 setup 函数

reactive 函数创建的变量的各个维度都是响应式的,无论维度是深层次还是浅层次,如果不希望如此,可以使用 sallowReactive 函数,该函数仅仅会对数据的浅层次进行响应式处理

响应式数据-ref:ref 函数的使用范围会更加广泛,它对基本类型以及对象类型的数据都适用,但是 ref 创建的变量不能直接修改,而是应该对其 value 属性进行修改

<!-- Ref.vue -->
<script setup>
import { ref } from 'vue'

const name = ref("Jack")
const status = ref("active")
const tasks = ref(["task one", "task two", "task three"])

const toggleStatus = () => {
    if (status.value === "active") {
        status.value = "pending"
    } else if (status.value === "pending") {
        status.value = "inactive"
    } else {
        status.value = "active"
    }
}
</script>

<template>
    <p v-if="status === 'active'">{{ name }} is active</p>
    <p v-else-if="status === 'pending'">{{ name }} is pending</p>
    <p v-else>{{ name }} is inactive</p>

    <h2>list of tasks</h2>
    <ul>
        <li v-for="task in tasks" :key="task">{{ task }}</li>
    </ul>

    <button @click="toggleStatus">change status</button>
</template>

上述代码还给出组合式 API 实现的另一种方式,即 setup 属性,该方式并不需要 return 返回逻辑

只读数据-readonly:readonly 函数创建只读数据,该数据仅读不能进行写操作,所以是非响应式的

<!-- ReadOnly.vue -->
<script setup>
import { readonly } from 'vue';

const readData = readonly({
    name: 'Jack',
    age: 18,
    isStudent: true
})
</script>

<template>
    <p>{{ readData.name }} is {{ readData.age }} years old.</p>
    <p>Is he a student? <br> {{ readData.isStudent }}</p>
</template>

如果希望数据浅层次只读,数据深层次可写,则可以使用类似的 shallowReadonly 函数,该函数对于数据深层次是可写的,但是数据仍然是非响应式的,需要自己手动刷新查看变动

计算属性-computed:与选项是 API 的计算属性类似

<!-- ComputedFun.vue -->
<script setup>
import { computed, ref } from 'vue';

const content = ref('test')
const getLen = computed(() => {
    console.log('getLen run')
    return content.value.length
})
</script>

<template>
    {{ getLen }}
    {{ getLen }}
</template>

侦听器-watch:和组合式 API 类似,但是会存在一些特殊情况

<!-- Watch.vue -->
<script setup>
import { reactive, ref, watch } from 'vue';

const count = ref(0)
watch(count, (newValue, oldValue) => {
    console.log(oldValue, newValue)
})

function addCount() {
    count.value++
}

const student = reactive({
    name: 'Jack',
    age: 18,
    friends: ['Tom', 'Aily']
})
watch(() => student.age, (newValue, oldValue) => {
    console.log(oldValue, newValue)
})

function beOlder() {
    student.age++
}
</script>

<template>
    <p>{{ count }}</p>
    <button @click="addCount">add</button>
    <p>{{ student }}</p>
    <button @click="beOlder">old</button>
</template>

侦听器能够直接监听 reactive 和 ref 对象,但是对于对象类型的某一具体属性的监听,需要通过函数的形式实现

watchEffect:类似于 react 框架中的 useEffect,但是 watchEffect 会更加智能,会自动监听使用变量的值是否发生变化

<!-- WatchEffect.vue -->
<script setup>
import { ref, watchEffect } from 'vue';

const count = ref(0)
const judge = ref(true)

watchEffect(() => {
    console.log(`count: ${count.value}, judge: ${judge.value}`)
})

function addCount() {
    count.value++
}

function toggleJudge() {
    judge.value = !judge.value
}
</script>

<template>
    <p>{{ count }}</p>
    <button @click="addCount">add</button>

    <p>{{ judge }}</p>
    <button @click="toggleJudge">toggle</button>
</template>

11vue3状态管理-pinia

在项目构建时,如果没有勾选 pinia,需要通过 npm install pinia 安装并导入

// src/main.js
import './assets/main.css'

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)

const pinia = createPinia()
app.use(pinia)

app.mount('#app')

然后,在 src/store 文件夹下创建 counter.js 文件

如果在构建时已经勾选 pinia,那么 counter.js 文件内容如下

// src/store/counter.js
import { ref, computed } from 'vue';
import { defineStore } from 'pinia';

export const useCounterStore = defineStore('counter', () => {
    const count = ref(0)
    const doubleCount = computed(() => count.value * 2)
    
    function increment() {
        count.value++
    }

    return {
        count,
        doubleCount,
        increment
    }
})

实际上,在 pinia 中进行状态管理,并没有特别之处,使用的基本语法与 vue3 中的语法几乎相同,ref 会被类比为 vuex 中的 state,computed 会被类比为 getters,pinia 没有 mutations 的概念,而 function 会被类比为 actions + mutations,最后在 pinia 中的模块概念实际就是该 JavaScript 文件,如 counter.js 文件,而 defineStore 就创建了 'counter' 这个模块

<!-- TestPinia.vue -->
<script setup>
import { useCounterStore } from '@/store/counter';
const counterStore = useCounterStore()
</script>

<template>
    <p>{{ counterStore.count }}</p>
    <p>{{ counterStore.doubleCount }}</p>
    <button @click="counterStore.increment">add</button>
</template>
posted @ 2025-08-08 20:26  /O_o\  阅读(30)  评论(0)    收藏  举报