App.vue
<template>
<div>
<h2>我是父组件,下面是全局组件的内容</h2>
<HelloWorld></HelloWorld>
</div>
</template>
<script setup>
</script>
<style scoped></style>
全局组件HelloWorld.vue
<template>
<div>
<h2>我是全局组件</h2>
</div>
</template>
<script setup>
</script>
<style scoped></style>
main.js
// createApp是一个工厂函数
import {
createApp
} from 'vue'
import './style.css'
import App from './App.vue'
let app = createApp(App)
// 注册全局组件
import HelloWorld from './components/HelloWorld.vue'
app.component('HelloWorld', HelloWorld)
app.mount('#app')
// 另一种写法
// createApp(App).mount('#app')
App.vue
<template>
<div>
<button v-has="{ color: '#f60', text: '全局自定义指令' }">{{ btn }}</button>
</div>
</template>
<script setup>
import { ref } from "vue";
let btn = ref('按钮')
</script>
<style scoped></style>
main.js
// createApp是一个工厂函数
import {
createApp
} from 'vue'
import './style.css'
import App from './App.vue'
let app = createApp(App)
// 注册全局组件
import HelloWorld from './components/HelloWorld.vue'
app.component('HelloWorld', HelloWorld)
// 全局自定义指令
// 写法1:
// app.directive('has', {
// beforeMount(el, binding) {
// },
// mounted(el, binding) {
// el.innerHTML = binding.value.text
// el.style.color=binding.value.color
// }
// })
// 写法2:
app.directive('has', function (el, binding) {
el.innerHTML = binding.value.text
el.style.color = binding.value.color
})
app.mount('#app')
// 另一种写法
// createApp(App).mount('#app')
router->index.js
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// import AboutView from '../views/AboutView' 下面用了懒加载方法,所以这里注释掉
// 定义一些路由
// 每个路由都需要映射到一个组件
const routes = [
{
name: 'home',
path: '/',
component: HomeView
},
{
name: 'about',
path: '/about/:name/:age', // '/about'这个前缀代表了不同的模块或功能区域,避免路由名称冲突
component: () => import('../views/AboutView.vue') // 路由懒加载
},
]
// 3. 创建路由实例并传递 `routes` 配置
// 你可以在这里输入更多的配置,但我们在这里
// 暂时保持简单
const router = createRouter({
// 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
history: createWebHashHistory(),
routes, // `routes: routes` 的缩写
})
export default router
main.js
// 导入
import router from './router'
// router是插件,需要use才能用
app.use(router).mount('#app')
App.vue
<template>
<div>
<!-- 写法1: -->
<!-- <router-view></router-view> -->
<!-- 写法2: -->
<RouterView></RouterView>
<!-- 点击按钮后将本路由数据传给AboutView.vue路由,展示在页面上 -->
<button @click="goHome">home</button>
<button @click="goAbout">about</button>
</div>
</template>
<script setup>
import { reactive } from 'vue';
import { useRoute, useRouter } from 'vue-router';
let info = reactive({
name: 'haha',
work: {
job: 'xi',
age: 27
}
})
const router = useRouter() //解构出来
let goHome = () => {
router.push('/')
}
let goAbout = () => {
router.push(
// query传参
// {
// path: '/about',
// query: {
// age: info.work.age,
// name: info.name
// }
// }
// params传参
{
name: 'about',
params: {
name: info.name,
age: info.work.age
}
}
)
}
</script>
<style scoped></style>
AboutView.vue
<template>
<div>
<h2>AboutView</h2>
<!-- <h2>{{ route }}</h2> -->
<!-- <h2>{{ $route }}</h2> -->
<!-- App.vue路由传过来的数据 -->
<!-- <h2>{{ $route.query.age }}</h2> -->
<!-- <h2>{{ $route.query.name }}</h2> -->
<h2>{{ $route.params.name }}</h2>
<h2>{{ $route.params.age }}</h2>
</div>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted } from 'vue'
import { useRoute } from 'vue-router';
const route = useRoute()
console.log(route.params);
</script>
<style scoped lang="less"></style>
HomeView.vue
<template>
<div>
<h2>HomeView</h2>
</div>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted} from 'vue'
</script>
<style scoped>
</style>
<template>
<div>
<button @click="show = !show">电我吧</button>
<transition name="fade">
<p v-show="show" :style="styleobj">看我变身</p>
</transition>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue';
let show = ref(true)
let styleobj = reactive({
fontSize: '30px',
color: 'red'
})
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 2s;
}
/* vue3这里的fade-enter后面加了一个from,比vue2的动画离开效果更生动了 */
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
Pinia 是 Vue 的存储库,它允许您跨组件/页面共享状态。pinia和Vuex的作用是一样的,它也充当的是一个存储数据的作用,存储在pinia的数据允许我们在各个组件中使用
优点:
Vue2和Vue3都支持,这让我们同时使用Vue2和Vue3的小伙伴都能很快上手。
pinia中只有state、getter、action,抛弃了Vuex中的Mutation,Vuex中mutation一直都不太受小伙伴们的待见,pinia直接抛弃它了,这无疑减少了我们工作量。
pinia中action支持同步和异步,Vuex不支持
良好的Typescript支持,毕竟我们Vue3都推荐使用TS来编写,这个时候使用pinia就非常合适了
无需再创建各个模块嵌套了,Vuex中如果数据过多,我们通常分模块来进行管理,稍显麻烦,而pinia中每个store都是的,互相不影响。
体积非常小,只有1KB左右。
pinia支持插件来扩展自身功能。
支持服务端渲染。
pinia的使用
user.js
import { defineStore } from 'pinia'
// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore('users', {
// 其它配置项
})
使用store
使用store很简单,直接引入我们声明的useUsersStore 方法即可
App.vue
<template>
</template>
<script setup>
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
console.log(store);
</script>
<style scoped>
</style>
添加state
前面我们利用defineStore函数创建了一个store,该函数第二个参数是一个options配置项,我们需要存放的数据就放在options对象中的state属性内。
user.js
export const useUsersStore = defineStore("users", {
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
}); 上段代码中我们给配置项添加了state属性,该属性就是用来存储数据的,我们往state中添加了3条数据。需要注意的是,state接收的是一个箭头函数返回的值,它不能直接接收一个对象。
操作state
读取state数据
App.vue
<template>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
</template>
<script setup>
import {ref} from 'vue'
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
console.log(store);
const name = ref(store.name);
const age = ref(store.age);
const sex = ref(store.sex);
</script>
<style scoped>
</style>
简单方法:用解构的方法获取数据
App.vue
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
const { name, age, sex } = store; 多个组件使用state
接下来我们新建一个child.vue组件,在该组件内部也使用state数据
components->child.vue
<template>
<h1>我是child组件</h1>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted } from 'vue'
import { useUsersStore } from "../store/user";
const store = useUsersStore();
const { name, age, sex } = store;
</script>
<style scoped></style> App.vue
<child></child>
import child from './components/child.vue' 这样我们就实现了多个组件同时使用store中的数据
修改state数据
如果我们想要修改store中的数据,可以直接重新赋值即可,我们在App.vue里面添加一个按钮,点击按钮修改store中的某一个数据
App.vue
<button @click="changeName">更改姓名</button>
const changeName = () => {
store.name = "张三";
console.log(store);
}; 我们可以看到store中的name确实被修改了,但是页面上似乎没有变化,这说明我们的使用的name不是响应式的。
很多小伙伴可能会说那可以用监听函数啊,监听store变化,刷新页面...
其实,pinia提供了方法给我们,让我们获得的name等属性变为响应式的,我们重新修改代码。
利用pinia的storeToRefs函数,将sstate中的数据变为了响应式的
App.vue
import { storeToRefs } from 'pinia';
// 响应式
const { name, age, sex } = storeToRefs(store);
child.vue
import { storeToRefs } from 'pinia';
// 响应式
const { name, age, sex } = storeToRefs(store); 重置state
有时候我们修改了state数据,想要将它还原,此时,我们直接调用store的$reset()方法即可,继续使用我们的例子,添加一个重置按钮
<button @click="reset">重置store</button>
// 重置store
const reset = () => {
store.$reset();
}; 批量更改state数据
一次性需要修改很多条数据的话,有更加简便的方法,使用store的$patch方法,修改app.vue代码,添加一个批量更改数据的方法
<button @click="patchStore">批量修改数据</button>
// 批量修改数据
const patchStore = () => {
store.$patch({
name: "张三",
age: 100,
sex: "女",
});
}; 假如我们state中有些字段无需更改,但是按照上段代码的写法,我们必须要将state中的所有字段例举出了。
为了解决该问题,pinia提供的$patch方法还可以接收一个回调函数,它的用法有点像我们的数组循环回调函数了
App.vue
// 修改单个数据
const patchStore = () => {
store.$patch((state) => {
state.name='haha'
state.hasChanged = true
})
}; getters属性
getters是defineStore参数配置项里面的另一个属性,前面我们讲了state属性。getter属性值是一个对象,该对象里面是各种各样的方法。大家可以把getter想象成Vue中的计算属性,它的作用就是返回一个新的结果,既然它和Vue中的计算属性类似,那么它肯定也是会被缓存的,就和computed一样
添加getter
user.js
// 第一个参数是应用程序中 store 的唯一 id
export const useUsersStore = defineStore('users', {
// 其它配置项
state: () => {
return {
name: "小猪课堂",
age: 25,
sex: "男",
};
},
getters: {
getAddAge: (state) => {
return state.age + 100;
},
},
}) 我们在配置项参数中添加了getter属性,该属性对象中定义了一个getAddAge方法,该方法会默认接收一个state参数,也就是state对象,然后该方法返回的是一个新的数据
使用getter
<template>
<p>新年龄:{{ store.getAddAge }}</p>
<button @click="patchStore">批量修改数据</button>
</template>
<script setup lang="ts">
import { useUsersStore } from "../src/store/user";
const store = useUsersStore();
// 批量修改数据
const patchStore = () => {
store.$patch({
name: "张三",
age: 100,
sex: "女",
});
};
</script> 上段代码中我们直接在标签上使用了store.gettAddAge方法,这样可以保证响应式,其实我们state中的name等属性也可以以此种方式直接在标签上使用,也可以保持响应式。
当我们点击批量修改数据按钮时,页面上的新年龄字段也会跟着变化
getter中调用其他getter
有时候我们需要在这一个getter方法中调用其它getter方法,其实很简单,我们可以直接在getter方法中调用this,this指向的便是store实例,所以理所当然的能够调用到其它getter。
getters: {
getAddAge: (state) => {
return state.age + 100;
},
getNameAndAge(): string {
return this.name + this.getAddAge; // 调用其它getter
},
}, 上段代码中我们又定义了一个名为getNameAndAge的getter函数,在函数内部直接使用了this来获取state数据以及调用其它getter函数。
细心的小伙伴可能会发现我们这里没有使用箭头函数的形式,这是因为我们在函数内部使用了this,箭头函数的this指向问题相信大家都知道吧!所以这里我们没有采用箭头函数的形式。
组件中
<p>调用其它getter:{{ store.getNameAndAge }}</p> getter传参
getAddAge: (state) => {
return (num) => state.age + num;
}, 上段代码中我们getter函数getAddAge接收了一个参数num,这种写法其实有点闭包的概念在里面了,相当于我们整体返回了一个新的函数,并且将state传入了新的函数。
组件中
<p>新年龄:{{ store.getAddAge(1100) }}</p> actions属性
前面我们提到的state和getters属性都主要是数据层面的,并没有具体的业务逻辑代码,它们两个就和我们组件代码中的data数据和computed计算属性一样。
那么,如果我们有业务代码的话,最好就是卸载actions属性里面,该属性就和我们组件代码中的methods相似,用来放置一些处理业务逻辑的方法。
actions属性值同样是一个对象,该对象里面也是存储的各种各样的方法,包括同步方法和异步方法。
添加actions
actions: {
saveName(name: string) {
this.name = name;
},
}, 上段代码中我们定义了一个非常简单的actions方法,在实际场景中,该方法可以是任何逻辑,比如发送请求、存储token等等。大家把actions方法当作一个普通的方法即可,特殊之处在于该方法内部的this指向的是当前store。
使用actions
<button @click="saveName">actions修改数据的方法</button>
const saveName = () => {
store.saveName("我是小猪");
}; 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- gamedaodao.net 版权所有 湘ICP备2024080961号-6
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务