让Pinia写起来更简洁
让Pinia写起来更简洁
前言
其实Pinia已经很简洁优雅,但是我们在组织代码结构的时候还可以再少些代码。
本文简洁之处在于通过require.context()
自动化导入各个store模块,最后放在一个store对象中供其他vue文件使用。
注意 本文以Vue2为例,但与Vue3写法上无明显差别
1. 为什么用pinia
- 模块化且按需导入,避免所有的store都被导入
- Pinia 相对于 Vuex 提供了更简单的 API,更少的规范,以及 Composition-API 风格的 API。
- Pinia 没有 Mutations,Actions 支持同步和异步,没有模块的嵌套结构。
2. 安装pinia
- 在Vue2中使用
npm i pinia @vue/composition-api
(安装pinia和组合api) 想了解细节可参考官方安装文档
- 在Vue3中使用
npm i pinia
3. 引入pinia
项目目录
├── src
├── api
├── assets
├── components
├── router
├── store
├── modules
├── user.js
├── login.js
├── temp
├── test.js
├── index.js
├── views
├── App.vue
└── main.js
3.1 具体代码
在store文件夹下的index文件中引入并导出,然后挂载到vue上
/*./src/store/index.js*/
//在index文件中引入pinia,最后导出
import Vue from 'vue'
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
// 导入modules文件夹下的所有js文件,包含子文件夹下的js文件
const modulesFiles = require.context('./modules', true, /\.js$/)
//modulesFiles.keys() 得到文件路径 ['./login.js', './temp/test.js', './user.js']
const store = modulesFiles.keys().reduce((pre, cur) => {
const moduleName = cur.replace(/^\.\/(.*)\.\w+$/, "$1");
pre[moduleName] = modulesFiles(cur).default;
return pre;
}, {});
console.log(store) //打印结果看 #3.2关于代码中的require.context() 的图片
export {storeToRefs} from 'pinia'
export default pinia
export {store}
---------------------------------------------------------------------------------------
/*main.js*/
import pinia from "./store";
new Vue({
router,
pinia,
render: h => h(App)
}).$mount("#app");
3.2 关于代码中的require.context()
//语法:
require.context(directory,useSubdirectories,regExp)
* `directory`:表示检索的目录
* `useSubdirectories`:表示是否检索子文件夹
* `regExp`:匹配文件的正则表达式,一般是文件名
const modulesFiles = require.context('./modules', true, /\.js$/)
作用:返回 modules
目录下的所有后缀为.js
模块的引用,包含子目录
-
modulesFiles.keys() 得到文件路径 ['./login.js', './temp/test.js', './user.js']
-
关于代码中用的正则表达式
/^\.\/(.*)\.\w+$/
表示以 ./
开头,以 .\w+
结尾。 (.*)
是匹配到的文件名.
\w
是指大小写字母数字或下划线。
replace方法中的$1
是指正则表达式中括号内的字符,即第一个子匹配
所以 ./login.js
会替换成 login
- 通过reduce方法导出一个对象
const store = modulesFiles.keys().reduce((pre, cur) => {
const moduleName = cur.replace(/^\.\/(.*)\.\w+$/, "$1");
pre[moduleName] = modulesFiles(cur).default;
return pre;
}, {});
console.log('store',store);
对象的属性名是modules目录下的js文件名,值是js文件中默认导出的 useStore
方法
例如 login.js 中 export default loginStore
,到时候在组件中使用 loginStore 时,可以直接使用store.login()
获取loginStore实例
这是console.log(store)的结果
对于temp子文件夹中的js文件,例如 test.js 。要通过store['temp/test']()获取实例对象
3.3 踩坑提示
vue2中如果出现类似报错,请在vue.config.js中配置webpack
module.exports = {
configureWebpack: config => {
config.module.rules.push({
include: /node_modules/,
test: /\.mjs$/,
type: 'javascript/auto'
})
}
}
4. 使用pinia
4.1 定义一个store
/*user.js*/
------------------------------------------------------------------
import { defineStore } from 'pinia'
//'user' 是应用程序中 store 的唯一 id,Pinia 使用它将 store 连接到 devtools。
const userStore = defineStore('user', {
state: () => { //state:状态库、必须是函数,否则会报错。
return {
store_counter: 0,
store_nameList: ['Eduardo'],
store_obj:{age:14,gender:'男'}
}
},
actions: { //actions: 相当于vue组件中的 methods .可以通过this访问state数据,支持异步,async、await关键字
updateState({ data, type }) {
this[type] = { ...this[type], ...data };
}
}
})
//导出userStore
export default userStore
4.2 关于state的操作
- 引用state
//store 就是sotre文件夹中导出的index
import { store, storeToRefs } from "@/store";
export default {
setup() {
const userStore = store.userStore();
return {
userStore,
...storeToRefs(userStore), //从 Store 中提取所有属性同时保持其响应式
};
},
methods: {},
mounted() {
console.log(this.store_counter)
}
}
修改state
//方式一:直接赋值修改
this.store_counter = 0
//方式二:使用 $patch 批量赋值修改
this.userStore.$patch({
store_counter: this.store_counter + 1,
store_nameList: [...this.store_nameList,'张三']
})
//方式三:$patch的参数改成函数,可以部分修改数组中的元素,而非新建一个数组(对象也是一样)
this.userStore.$patch((state) => {
state.store_counter ++
state.store_nameList.push('张三')
})
//方式三: 触发actions中的方法
this.userStore.updateState({data:{gender:'女'},type:'store_obj'})
//这个方法的意思是更新变量,相当于this.store_obj = {this.store_obj,...{gender:'女'}}
//推荐更新对象里的属性时用这个方法
4.3 关于getters的操作
定义getters
/*user.js*/
------------------------------------------------------------------
import { defineStore } from 'pinia'
const userStore = defineStore('user', {
state: () => {
return {
store_nameList: ['Eduardo'],
}
},
getters: { //getters: 计算属性,用于需要二次加工的属性,与vuex几乎无区别
store_logNameList(state) {
console.log(state.store_nameList)
}
}
})
//导出userStore
export default userStore
组件中使用getters (和使用state一样,直接this.getters名
)
//store 就是sotre文件夹中导出的index
import { store, storeToRefs } from "@/store";
export default {
setup() {
const userStore = store.userStore();
return {
...storeToRefs(userStore), //从 Store 中提取所有属性同时保持其响应式
};
},
methods: {},
mounted() {
console.log(this.store_logNameList)
}
}
5. 使用插件
假设需要在stroe中使用router,就需要使用到pinia的插件机制扩展store的api
定义插件
import { createPinia } from 'pinia'
import { App} from "vue";
import { router } from '@/routers'
const pinia = createPinia()
pinia.use(({ store }) => {
store.$router = router
})
app.use(pinia)
使用插件
import { defineStore } from 'pinia'
import { RouteLocationNormalized } from 'vue-router'
export const useApplicationStore = defineStore('application', {
state: () => (
...
),
actions:{
toHome(){
this.$router.push('/home')
}
}
})
5. 注意
推荐加上store_
前缀,让同事一眼认出变量是来自于pinia。方便别人读懂你的代码
转载自:https://juejin.cn/post/7242596613750538301