「 Vue 」在 Vue 中使用 Vue Router 和 Vuex

152 阅读3分钟

这是我参与11月更文挑战的12天,活动详情查看:2021最后一次更文挑战

Vue Router

创建一个 demo 项目。

? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Router, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback in production) No
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

路由是指根据 url 的不同展示不同的效果。

main.js 中引入了 router

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

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

router/index.js

import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '../views/Home.vue'

const routes = [
  {
    path: '/',  // 路径
    name: 'Home', // 路由名
    component: Home // 组件
  },
  {
    path: '/about',
    name: 'About',
    // 异步加载路由 访问其他页面时不加载当前组件,只有访问当前组件时才加载
    // 通过异步加载路由可以加快首页加载速度,但访问当前组件时会加载当前的组件
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]

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

export default router

该文件中引入了 HomeAbout 组件:

// views/Home.vue
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
  </div>
</template>

<script>
export default {
  name: "Home",
};
</script>
// views/About.vue
<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>

App.vue

<template>
  <div id="nav">
    <!-- router-link 是跳转路由的标签 -->
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link>
  </div>
  <!-- router-view 用来展示路由的组件 -->
  <router-view />
</template>

<style>...</style>

<router-link> 自定义组件使得 Vue Router 可以在不重新加载页面的情况下更改 url ,处理 url 的生成以及编码。

<router-view> 将显示与 url 对应的组件。

Vuex

创建一个 demo 项目。

? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Router, Vuex, Linter
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback in production) No
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

mian.js

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

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

store/index.js

import { createStore } from 'vuex'

export default createStore({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

Vuex 是一个数据管理框架,当项目变得复杂时,我们需要在页面之间传递数据,如果使用原来组件传递数据的方法,可维护性较差。Vuex 创建了一个全局唯一的仓库,用来存放全局的数据。

定义全局数据

// store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    name: 'Jack'
  },
  mutations: {
  },
  actions: {
  },
  modules: {
  }
})

在首页中通过 this.$store.state 使用数据:

// views/Home.vue
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <h1>{{ myName }}</h1>
  </div>
</template>

<script>
export default {
  name: "Home",
  computed: {
    myName() {
      return this.$store.state.name;
    },
  },
};
</script>

修改全局数据

修改全局数据有一定的流程。在代码中如下:

// views/Home.vue
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <h1 @click="handleClick">{{ myName }}</h1>
  </div>
</template>

<script>
export default {
  name: "Home",
  computed: {
    myName() {
      return this.$store.state.name;
    },
  },
  methods: {
    handleClick() {
      // 第一步,Vuex 需要派发一个名为 change(名称自定)的 action
      this.$store.dispatch("change");
    },
  },
};
</script>
// store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    name: 'Jack'
  },
  mutations: {
    // 第四步,对应的 mutation 被执行
    change() {
      // 第五步,在 mutation 中修改数据
      this.state.name = 'Joe'
    }
  },
  actions: {
    // 第二步, store 感知到你触发了一个叫做 change 的 action,执行 change
    change() {
      // 第三步,commit 一个名为 change 的改变,触发一个 mutation
      this.commit("change")
    }
  },
  modules: {
  }
})

mutation 里建议只写同步代码,不写异步代码。可以在 action 里写异步代码。

如果只进行同步操作,可以跳过 action

// views/Home.vue
<script>
export default {
  name: "Home",
  computed: {
    myName() {
      return this.$store.state.name;
    },
  },
  methods: {
    handleClick() {
      // 跳过第一步和第二步
      this.$store.commit("change");
    },
  },
};
</script>

dispatchcommit 方法可以为事件传参:

// views/Home.vue
methods: {
  handleClick() {
    this.$store.commit("change", "Hello World");
  },
},
// store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    name: 'Jack'
  },
  mutations: {
    // 第一个参数是 state,可以代替 this.state
    change(state, str) {
      state.name = str
    }
  },
  actions: {
    // 第一个参数是 store,可以代替 this
    change(store, str) {
      store.commit("change", str)
    }
  },
  modules: {
  }
})

在组合式 API 中使用 Vuex

About 页面点击相应位置, 2s 后修改全局数据 name

// views/Home.vue
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
    <h1>{{ name }}</h1>
  </div>
</template>

<script>
// 从 vuex 中引入 useStore
import { useStore } from "vuex";
import { toRefs } from "vue";

export default {
  name: "Home",
  setup() {
    const store = useStore();
    const { name } = toRefs(store.state);
    return {
      name,
    };
  },
};
</script>
// views/About.vue
<template>
  <div class="about">
    <h1 @click="handleClick">Click here to change the name after 2s.</h1>
    <h1>{{ name }}</h1>
  </div>
</template>

<script>
import { useStore } from "vuex";
import { toRefs } from "vue";

export default {
  name: "Home",
  setup() {
    const store = useStore();
    const { name } = toRefs(store.state);
    const handleClick = () => {
      // 调用 dispatch
      store.dispatch("getData");
    };
    return {
      name,
      handleClick,
    };
  },
};
</script>
// store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    name: 'Jack'
  },
  mutations: {
    changeName(state, str) {
      state.name = str
    }
  },
  actions: {
    getData(store) {
      setTimeout(() => {
        // 调用 commit
        store.commit('changeName', 'Hello World')
      }, 2000);
    }
  },
  modules: {
  }
})

发送 ajax 请求

使用 axios 发送 ajax 请求,运行 npm install axios --save安装 axios。

store/index.js 中发送请求:

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

export default createStore({
  state: {
    name: 'Jack'
  },
  mutations: {
    changeName(state, str) {
      state.name = str
    }
  },
  actions: {
    getData(store) {
      axios.get("...")
        .then((response) => {
          const msg = response.data.message;
          store.commit('changeName', msg)
        });
    }
  },
  modules: {
  }
})