VUEX vue 里可以使用 vuex 管理应用的数据,使用这个包提供的功能可以创建一个 store,这个 store 你可以想象成是应用的一个数据仓库,你在里面可以定义应用的数据还有方法等
安装 vuex package.json 里添加 “vuex”: “^4.0.0-0” ,然后 npm install 一下
创建与使用 store 新建 app.store.ts 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createStore } from 'vuex'; /** * 创建store */ const store = createStore({ state:{ appName:'cxn' } }); /** * 默认导出 */ export default store;
main.ts 中导入
1 2 3 4 5 import appStore from './app/app.store'; /** * 应用store */ app.use(appStore);
定义使用 store 模块 使用 store 的时候可以定义 store 模块,比如在内容里定义内容的 store,在 user 里定义 user 的 store。 app 下新建 post/post.store.ts
1 2 3 4 5 6 7 8 9 import { postCreateStoreModule } from './create/post-create.store'; export const postStoreModule = { namespaced: true, //使用命名空间 modules: { create: postCreateStoreModule, //设置其他的store模块 }, };
在 post 目录下新建 create/post-create.store.ts:
1 2 3 4 5 6 7 export const postCreateStoreModule = { namespaced: true, //使用命名空间 state: { loading: false, }, };
打开 app.store.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createStore } from 'vuex'; import { postStoreModule } from './post/post.store'; /** * 创建store */ const store = createStore({ state: { appName: 'cxn', }, modules: { post: postStoreModule, }, }); /** * 默认导出 */ export default store;
然后在 app.vue 中输出看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template> <h3>{{ name }}</h3> </template> <script> export default { data() { return { name: 'nihao~', }; }, created() { console.log(this.$store.state); }, }; </script> <style> @import './styles/app.css'; </style>
解决 store 模块的类型问题 app.store.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import { createStore } from 'vuex'; import { postStoreModule, PostStoreState } from './post/post.store'; export interface RootState { appName: string; post: PostStoreState; } /** * 创建store */ const store = createStore({ state: { appName: 'cxn', } as RootState, modules: { post: postStoreModule, }, }); /** * 默认导出 */ export default store;
post.store.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { RootState } from '../app.store'; import { Module } from 'vuex'; import { postCreateStoreModule, PostCreateStoreState, } from './create/post-create.store'; export interface PostStoreState { create: PostCreateStoreState; } export const postStoreModule: Module<PostStoreState, RootState> = { namespaced: true, //使用命名空间 modules: { create: postCreateStoreModule, //设置其他的store模块 }, };
post/create/post-create.store.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import { RootState } from '@/app/app.store'; import { Module } from 'vuex'; export interface PostCreateStoreState { loading: boolean; } export const postCreateStoreModule: Module<PostCreateStoreState, RootState> = { namespaced: true, //使用命名空间 state: { loading: false, } as PostCreateStoreState, getters: { loading(state) { return state.loading; }, }, };
应用的路由 安装:在 package.json 里添加 “vue-router”: “^4.0.0-0”,然后 npm install
创建与使用路由 app/app.router.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createRouter, createWebHistory } from 'vue-router'; import appRoutes from './app.routes'; /** * 创建路由器 */ const router = createRouter({ history: createWebHistory(), routes: [...appRoutes], }); /** * 默认导出 */ export default router;
新建 app.routes.ts 在这里可以定义应用整体相关的路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { RouteRecordRaw } from 'vue-router'; import AppHome from './components/app-home.vue'; /** * 定义路由 */ const routes: Array<RouteRecordRaw> = [ { name: 'home', path: '/', component: AppHome, }, ]; /** * 默认导出 */ export default routes;
新建组件 AppHome:
1 2 3 <template> <div>你好啊啊啊</div> </template>
定义内容模块的路由: post/post.routes.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { RouteRecordRaw } from 'vue-router'; import PostIndex from './index/post-index.vue'; /** * 定义路由 */ const routes: Array<RouteRecordRaw> = [ { name: 'postIndex', path: '/posts', component: PostIndex, }, ]; /** * 默认导出 */ export default routes;
新建组件 post/index/PostIndex:
1 2 3 <template> <div>PostIndex</div> </template>
在 app.router.ts 导入;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { createRouter, createWebHistory } from 'vue-router'; import appRoutes from './app.routes'; import postRoutes from '@/app/post/post.routes'; /** * 创建路由器 */ const router = createRouter({ history: createWebHistory(), routes: [...appRoutes, ...postRoutes], }); /** * 默认导出 */ export default router;
axios 请求服务端接口 安装:package.json 中添加 vue “axios”: “0.20.0” ,然后 npm install
创建 app/app.service.ts:
1 2 3 4 5 6 7 //在这里可以定义导出一些应用需要的方法 import axios from 'axios'; export const apiHttpClient = axios.create({ baseURL: 'http://localhost:3000', });
使用的时候:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <template> <div>PostIndex</div> </template> <script> import { apiHttpClient } from '@/app/app.service'; export default { async created() { const response = await apiHttpClient.get('/posts'); console.log(response); }, }; </script>
vue 应用的环境变量配置 有时候你可能希望在开发环境和生产环境上使用不同的配置,比如 axios 里设置了
1 2 3 export const apiHttpClient = axios.create({ baseURL: 'http://localhost:3000', });
这个地址在不同环境下可能是不一样的,我们可以在项目里给不同的环境创建各自的环境变量文件
创建开发环境的配置文件 .env.development:
1 VUE_APP_API_BASE_URL = http://localhost:3000
创建生成环境的配置文件 .env.production:
1 VUE_APP_API_BASE_URL = https://ravent-node-api.ninghao.net
创建项目配置文件 app/app.config.ts:
1 2 3 4 /** * 服务接口基本地址 */ export const API_BASE_URL = process.env.VUE_APP_API_BASE_URL;
在 app.service.ts 中使用:
1 2 3 4 5 6 7 8 //在这里可以定义导出一些应用需要的方法 import axios from 'axios'; import { API_BASE_URL } from './app.config'; export const apiHttpClient = axios.create({ baseURL: API_BASE_URL, });
在 vue 应用里定义与使用组件 如果在项目里使用了 ts,可以使用 vue 提供的 defineComponent 去返回这个组件对象。
新建组件 post/index/components/post-list-item.vue:
1 2 3 4 5 6 7 8 <template> <div>PostListItem</div> </template> <script> import { defineComponent } from 'vue'; export default defineComponent({}); </script>
新建组件 post/index/components/post-list.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 <template> <PostListItem /> </template> <script> import { defineComponent } from 'vue'; import PostListItem from './post-list-item'; export default defineComponent({ components: { PostListItem, }, }); </script>
在 post/post-index.vue 中导入
1 2 3 4 5 6 7 8 9 10 11 12 13 <template> <PostList /> </template> <script> import { defineComponent } from 'vue'; import PostList from './components/post-list'; export default defineComponent({ components: { PostList, }, }); </script>
在 Vue 组件内部请求服务接口获取组件需要的数据 post/index/components/post-list.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template> <PostListItem v-for="post in posts" :item="post" :key="post.id" /> //使用 </template> <script> import { defineComponent } from 'vue'; import PostListItem from './post-list-item'; import { apiHttpClient } from '@/app/app.service'; //引入axios export default defineComponent({ data() { return { posts: [], }; }, async created() { const response = await apiHttpClient.get('/posts'); //获取数据 this.posts = response.data; }, components: { PostListItem, }, }); </script>
post/index/components/post-list-item.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <template> <h3> {{ item.title }} - <small>{{ item.user.name }}</small> </h3> </template> <script> import { defineComponent } from 'vue'; export default defineComponent({ props: { item: Object, }, }); </script>
在 Vue 组件里使用 Store 里的动作与数据 post-index.store.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import { PostItem } from '../post.store'; import { Module } from 'vuex'; import { RootState } from '@/app/app.store'; import { apiHttpClient } from '@/app/app.service'; export interface PostIndexStoreState { loading: boolean; posts: Array<PostItem>; } export const PostIndexStoreModule: Module<PostIndexStoreState, RootState> = { namespaced: true, state: { loading: false, posts: [], } as PostIndexStoreState, getters: { loading(state) { return state.loading; }, posts(state) { return state.posts; }, }, mutations: { setLoading(state, data) { state.loading = data; }, setPosts(state, data) { state.posts = data; }, }, actions: { async getPosts({ commit }) { commit('setLoading', true); try { const response = await apiHttpClient.get('/posts'); commit('setPosts', response.data); commit('setLoading', false); return false; } catch (error) { commit('setloading', false); throw error.response; } }, }, };
post.store.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import { RootState } from '../app.store'; import { Module } from 'vuex'; import { postCreateStoreModule, PostCreateStoreState, } from './create/post-create.store'; import { PostIndexStoreModule } from './index/post-index.store'; export interface PostItem { id: number; title: string; content: string; } export interface PostStoreState { create: PostCreateStoreState; } export const postStoreModule: Module<PostStoreState, RootState> = { namespaced: true, //使用命名空间 modules: { create: postCreateStoreModule, //设置其他的store模块 index: PostIndexStoreModule, }, };
post-list.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <template> <div v-if="loading">加载中...</div> <PostListItem v-for="post in posts" :item="post" :key="post.id" /> </template> <script> import { defineComponent } from 'vue'; import { mapGetters, mapActions } from 'vuex'; import PostListItem from './post-list-item'; export default defineComponent({ async created() { this.getPosts(); }, computed: { ...mapGetters({ loading: 'post/index/loading', posts: 'post/index/posts', }), }, components: { PostListItem, }, methods: { ...mapActions({ getPosts: 'post/index/getPosts', }), }, }); </script>
内容页面:定义 Store 模块 新建 post/show/post.show.store.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import { apiHttpClient } from '@/app/app.service'; import { RootState } from '@/app/app.store'; import { Module } from 'vuex'; export interface Post { id: number; title: string; content: string; } export interface PostShowStoreState { loading: boolean; post: Post; } export const PostShowStoreModule: Module<PostShowStoreState, RootState> = { namespaced: true, state: { loading: false, post: {}, } as PostShowStoreState, getters: { loading(state) { return state.loading; }, post(state) { return Object.keys(state.post).length ? state.post : null; }, }, mutations: { setLoading(state, data) { state.loading = data; }, setPost(state, data) { state.post = data; }, }, actions: { async getPostById({ commit }, postId) { commit('setLoading', true); try { const response = await apiHttpClient.get(`/posts/${postId}`); commit('setLoading', false); commit('setPost', response.data); return response; } catch (error) { commit('setLoading', false); throw error.response; } }, }, };
post.store.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { RootState } from '../app.store'; import { Module } from 'vuex'; import { postCreateStoreModule, PostCreateStoreState, } from './create/post-create.store'; import { PostIndexStoreModule } from './index/post-index.store'; import { PostShowStoreModule } from './show/post.show.store'; export interface PostItem { id: number; title: string; content: string; } export interface PostStoreState { create: PostCreateStoreState; } export const postStoreModule: Module<PostStoreState, RootState> = { namespaced: true, //使用命名空间 modules: { create: postCreateStoreModule, //设置其他的store模块 index: PostIndexStoreModule, show: PostShowStoreModule, }, };
内容页面:定义路由与组件 新建 post-show.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <template> <div v-if="showPost"> <h1>{{ post.title }}</h1> <div> {{ post.content }} - <small>{{ post.user.name }}</small> </div> </div> </template> <script> import { mapGetters, mapActions } from 'vuex'; import { defineComponent } from 'vue'; export default defineComponent({ props: { postId: String, }, created() { this.getPostById(this.postId); }, computed: { ...mapGetters({ loading: 'post/show/loading', post: 'post/show/post', }), showPost() { return !this.loading && this.post; }, }, methods: { ...mapActions({ getPostById: 'post/show/getPostById', }), }, }); </script>
post.routes.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import { RouteRecordRaw } from 'vue-router'; import PostIndex from './index/post-index.vue'; import PostShow from './show/post-show.vue'; /** * 定义路由 */ const routes: Array<RouteRecordRaw> = [ { name: 'postIndex', path: '/posts', component: PostIndex, }, { name: 'postShow', path: '/posts/:postId', component: PostShow, props: true, }, ]; /** * 默认导出 */ export default routes;
post-list-item.vue:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template> <h3> <router-link :to="{ name: 'postShow', params: { postId: item.id } }"> {{ item.title }} </router-link> - <small>{{ item.user.name }}</small> </h3> </template> <script> import { defineComponent } from 'vue'; export default defineComponent({ props: { item: Object, }, }); </script>
Mixin 制作一个设置页面标题的混合 在环境变量文件里新增项目名称 .env.development , .env.production
app.config.ts 使用一下
1 2 3 4 5 6 7 8 9 /** * 应用名字 */ export const APP_NAME = process.env.VUE_APP_APP_NAME; /** * 服务接口基本地址 */ export const API_BASE_URL = process.env.VUE_APP_API_BASE_URL;
新建 app/app.mixin.ts 定义 mixin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import { ComponentOptions, ComponentPublicInstance } from 'vue'; import { APP_NAME } from './app.config'; /** * 设置页面标题 */ const setTitle = (vm: ComponentPublicInstance) => { //获取组件的title选项 const { title } = vm.$options; if (title) { let titleContent; if (typeof title === 'function') { //执行title titleContent = title.call(vm); } else { titleContent = title; } document.title = `${titleContent} - ${APP_NAME}`; } if (vm.$route.path === '/') { document.title = APP_NAME; } }; /** * 标题混合 */ export const titleMixin: ComponentOptions = { created() { setTitle(this); }, updated() { setTitle(this); }, };
在入口文件 main.ts 中使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import { createApp } from 'vue'; import App from './app/app.vue'; import appStore from './app/app.store'; import appRouter from './app/app.router'; import { titleMixin } from './app/app.mixin'; /** * 创建应用 */ const app = createApp(App); /** * 应用store */ app.use(appStore); /** * 应用路由器 */ app.use(appRouter); /** * 标题混合 */ app.mixin(titleMixin); /** * 挂载应用 */ app.mount('#app');
最后在组件里定义页面的标题就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <template> <PostList /> </template> <script> import { defineComponent } from 'vue'; import PostList from './components/post-list'; export default defineComponent({ title() { return '内容列表'; //使用title方法或者属性定义此页面标题 }, components: { PostList, }, }); </script>