欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue组件通信-插槽-路由-导航守卫-状态管理-组合API

程序员文章站 2022-03-03 23:43:07
...

Vue组件通信-插槽-路由-导航守卫-状态管理-组合API

创建 project-demo 项目

  1. npm install -g @vue/cli
  2. vue --version
  3. vue create project-demo

创建父子孙组件

views/Parent.vue

  1. <template>
  2. <div>
  3. <h1>Parent</h1>
  4. <p>Parent - Demo</p>
  5. <p>
  6. <button @click="parentNum++">ParentNum++</button>
  7. = {{ parentNum }}
  8. </p>
  9. <child-com :parentNum="parentNum"></child-com>
  10. </div>
  11. </template>
  12. <script>
  13. // 自定义组件
  14. // 1. 导入组件
  15. import ChildCom from '../components/ChildCom.vue'
  16. export default {
  17. name: 'Parent',
  18. data() {
  19. return {
  20. parentNum: 10,
  21. }
  22. },
  23. // 祖传孙 provide 提供(响应式)
  24. provide() {
  25. // 传递一个引用
  26. return {
  27. cParentNum: this.cParentNum,
  28. }
  29. },
  30. methods: {
  31. cParentNum() {
  32. return this.parentNum
  33. }
  34. },
  35. // 2. 注册组件
  36. components: {
  37. ChildCom,
  38. }
  39. }
  40. </script>
  41. <style lang="scss" scoped>
  42. </style>

components/ChildCom.vue

  1. <template>
  2. <div>
  3. <h1>Child Com</h1>
  4. <p>ChildCom - Parent - Demo</p>
  5. <p>ChildCom props receive father: parentNum = {{ parentNum }}</p>
  6. <p>
  7. <button @click="addChildNum()">childNum++</button>
  8. = {{ childNum }}
  9. </p>
  10. <!-- 父接收子,通过子声明的自定义事件 -->
  11. <grand-son :childNum="childNum" @grandSonChangeFather="grandSonChangeMe"></grand-son>
  12. <!-- 默认插槽 -->
  13. <!-- <slot-com>Slot value</slot-com> -->
  14. <!-- 具名插槽,使用模板 -->
  15. <!-- <slot-com><template v-slot:testSlot>TestSlot value</template></slot-com> -->
  16. <!-- 具名插槽,接收子传参 -->
  17. <slot-com>
  18. <template v-slot:testSlot="{test1, test2}">TestSlot value, params: {{ test1 }} {{ test2 }}</template>
  19. </slot-com>
  20. </div>
  21. </template>
  22. <script>
  23. import GrandSon from './children/GrandSon.vue'
  24. import SlotCom from './children/SlotCom.vue'
  25. export default {
  26. name: 'ChildCom',
  27. data() {
  28. return {
  29. childNum: 100,
  30. }
  31. },
  32. props: ['parentNum'],
  33. // props: {
  34. // parentNum: {
  35. // type: Number,
  36. // default: 0
  37. // }
  38. // },
  39. components: {
  40. GrandSon, SlotCom
  41. },
  42. methods: {
  43. // 子变量自增
  44. addChildNum() {
  45. this.childNum++
  46. },
  47. // 子改变父的自定义事件
  48. grandSonChangeMe(n) {
  49. this.childNum += n
  50. }
  51. }
  52. }
  53. </script>
  54. <style lang="scss" scoped>
  55. </style>

components/children/GrandSon.vue

  1. <template>
  2. <div>
  3. <h1>Grand son</h1>
  4. <p>GrandSon - ChildCom - Parent - Demo</p>
  5. <p>GrandSon props receive father: childNum = {{ childNum }}</p>
  6. <p>GrandSon inject receive grandfather: parentNum = {{ parentNum }}</p>
  7. <!-- 子传父,通过自定义方法 -->
  8. <p>
  9. <button @click="emitGrandSonChangeFather(10)">changeFather childNum + 10</button>
  10. = {{ childNum }}
  11. </p>
  12. </div>
  13. </template>
  14. <script>
  15. export default {
  16. name: 'GrandSon',
  17. // 父传子 props 接收
  18. props: ['childNum'],
  19. // props: {
  20. // childNum: {
  21. // type: Number,
  22. // default: 0,
  23. // }
  24. // },
  25. // 祖传孙 inject 注入
  26. inject: ['cParentNum'],
  27. // inject: {
  28. // parentNum: {
  29. // type: Number,
  30. // default: 0
  31. // }
  32. // },
  33. computed: {
  34. parentNum() {
  35. return this.cParentNum()
  36. }
  37. },
  38. methods: {
  39. emitGrandSonChangeFather(n) {
  40. this.$emit('grandSonChangeFather', n)
  41. }
  42. }
  43. }
  44. </script>
  45. <style lang="scss" scoped>
  46. </style>

创建插槽测试组件

components/children/SlotCom.vue

  1. <template>
  2. <div>
  3. <h1>Slot Com</h1>
  4. <!-- 默认插槽 -->
  5. <!-- <slot>Slot default</slot> -->
  6. <!-- 具名插槽 -->
  7. <!-- <slot name="testSlot">TestSlot default</slot> -->
  8. <!-- 具名插槽,向父传数据 -->
  9. <slot name="testSlot" test1="Hello" test2="vue">TestSlot default</slot>
  10. </div>
  11. </template>
  12. <script>
  13. export default {
  14. name: 'SlotCom',
  15. }
  16. </script>
  17. <style lang="scss" scoped>
  18. </style>

创建路由测试组件

views/Router.vue

  1. <template>
  2. <div>
  3. <h1>Router</h1>
  4. <p>
  5. <router-link :to="router">Router</router-link>
  6. </p>
  7. <p>
  8. <router-link :to="query">query</router-link>
  9. </p>
  10. <p>query: a = {{ paramA }}, b = {{ paramB }}</p>
  11. <p>
  12. <button @click="changeQuery(10, 20)">query</button>
  13. </p>
  14. <p>change a = 10, b = 20</p>
  15. </div>
  16. </template>
  17. <script>
  18. export default {
  19. name: 'Router',
  20. data() {
  21. return {
  22. router: {
  23. name: 'Router',
  24. },
  25. query: {
  26. path: '/router',
  27. query: {
  28. a: 1,
  29. b: 2,
  30. }
  31. }
  32. }
  33. },
  34. computed: {
  35. paramA() {
  36. return this.$route.query.a
  37. },
  38. paramB() {
  39. return this.$route.query.b
  40. }
  41. },
  42. methods: {
  43. changeQuery(a, b) {
  44. this.$router.push({
  45. path: '/router',
  46. query: {
  47. a,
  48. b,
  49. }
  50. })
  51. }
  52. }
  53. }
  54. </script>
  55. <style lang="scss" scoped>
  56. </style>

创建状态管理测试组件

views/State.vue

  1. <template>
  2. <div>
  3. <h1>State management</h1>
  4. <p>store num: {{ num }}</p>
  5. <p>commit mutations change state
  6. <button @click="commitNum(10)">state.num + 10</button>
  7. </p>
  8. <p>dispatch actions commit change mutations change state
  9. <button @click="dispatchNum(100)">state.num + 100</button>
  10. </p>
  11. <p>getters property: {{ sum }}</p>
  12. </div>
  13. </template>
  14. <script>
  15. export default {
  16. name: 'State',
  17. computed: {
  18. num() {
  19. return this.$store.state.num
  20. },
  21. sum() {
  22. return this.$store.getters.sum
  23. }
  24. },
  25. methods: {
  26. commitNum(n) {
  27. this.$store.commit('num', n)
  28. },
  29. dispatchNum(n) {
  30. this.$store.dispatch('num', n)
  31. }
  32. }
  33. }
  34. </script>
  35. <style lang="scss" scoped>
  36. </style>

注册路由和添加导航

router/index.js

  1. import {createRouter, createWebHistory} from 'vue-router'
  2. import Home from '../views/Home.vue'
  3. const routes = [
  4. {
  5. path: '/',
  6. name: 'Home',
  7. component: Home,
  8. meta: {
  9. title: 'Home - Demo'
  10. }
  11. },
  12. {
  13. path: '/about',
  14. name: 'About',
  15. // route level code-splitting
  16. // this generates a separate chunk (about.[hash].js) for this route
  17. // which is lazy-loaded when the route is visited.
  18. component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
  19. meta: {
  20. title: 'About - Demo'
  21. }
  22. },
  23. // 父组件
  24. {
  25. path: '/parent',
  26. name: 'Parent',
  27. component: () => import('../views/Parent.vue'),
  28. meta: {
  29. title: 'Parent - Demo'
  30. },
  31. // 子组件
  32. children: [
  33. {
  34. path: 'child-com',
  35. name: 'ChildCom',
  36. component: () => import('../components/ChildCom.vue'),
  37. meta: {
  38. title: 'ChildCom - Parent - Demo'
  39. },
  40. // 孙组件
  41. children: [
  42. {
  43. path: 'grand-son',
  44. name: 'GrandSon',
  45. component: () => import('../components/children/GrandSon.vue'),
  46. meta: {
  47. title: 'GrandSon - ChildCom - Parent - Demo'
  48. }
  49. },
  50. {
  51. path: 'slot-com',
  52. name: 'SlotCom',
  53. component: () => import('../components/children/SlotCom.vue'),
  54. meta: {
  55. title: 'SlotCom - ChildCom - Parent - Demo'
  56. }
  57. },
  58. ]
  59. }
  60. ]
  61. },
  62. // 路由测试
  63. {
  64. path: '/router',
  65. name: 'Router',
  66. component: () => import('../views/Router.vue'),
  67. meta: {
  68. title: 'Router - Parent - Demo'
  69. }
  70. },
  71. // 状态管理测试
  72. {
  73. path: '/state',
  74. name: 'State',
  75. component: () => import('../views/State.vue'),
  76. meta: {
  77. title: 'State - Parent - Demo'
  78. }
  79. }
  80. ]
  81. const router = createRouter({
  82. history: createWebHistory(process.env.BASE_URL),
  83. routes
  84. })
  85. // 前置导航守卫
  86. router.beforeEach((to, from) => {
  87. document.title = to.meta.title
  88. })
  89. export default router

添加导航 App.vue

  1. <template>
  2. <div id="nav">
  3. <router-link to="/">Home</router-link>
  4. |
  5. <router-link to="/about">About</router-link>
  6. |
  7. <router-link :to="{name: 'Parent'}">Parent</router-link>
  8. |
  9. <router-link :to="{name: 'Router'}">Router</router-link>
  10. |
  11. <router-link :to="{name: 'State'}">State</router-link>
  12. </div>
  13. <router-view/>
  14. </template>
  15. <style lang="scss">
  16. #app {
  17. font-family: Avenir, Helvetica, Arial, sans-serif;
  18. -webkit-font-smoothing: antialiased;
  19. -moz-osx-font-smoothing: grayscale;
  20. text-align: center;
  21. color: #2c3e50;
  22. }
  23. #nav {
  24. padding: 30px;
  25. a {
  26. font-weight: bold;
  27. color: #2c3e50;
  28. &.router-link-exact-active {
  29. color: #42b983;
  30. }
  31. }
  32. }
  33. </style>

使用状态管理

store/index.js

  1. import {createStore} from 'vuex'
  2. export default createStore({
  3. state: {
  4. num: 100,
  5. },
  6. mutations: {
  7. num(state, n) {
  8. state.num += n
  9. }
  10. },
  11. actions: {
  12. // 解构上下文 context
  13. num({state, commit, getters}, n) {
  14. commit('num', n)
  15. }
  16. },
  17. modules: {},
  18. // 计算属性
  19. getters: {
  20. sum(state, getter) {
  21. let sum = 0;
  22. for (let i = 1; i <= state.num; i++) sum += i
  23. return getter.str + sum;
  24. },
  25. str(state, getter) {
  26. return `1 + 2 + ... + ${state.num} = `;
  27. },
  28. }
  29. })

运行测试

  1. npm run serve
  • 父子孙组件通信 - 插槽及传值

    Vue组件通信-插槽-路由-导航守卫-状态管理-组合API

  • 路由测试

    Vue组件通信-插槽-路由-导航守卫-状态管理-组合API

  • 状态管理和异步更新

    Vue组件通信-插槽-路由-导航守卫-状态管理-组合API

使用组合 api

改写父子孙组件

  • views/Parent.vue
  1. <template>
  2. <div>
  3. <h1>Parent</h1>
  4. <p>Parent - Demo</p>
  5. <p>
  6. <button @click="parentNum++">ParentNum++</button>
  7. = {{ parentNum }}
  8. </p>
  9. <child-com :parentNum="parentNum"></child-com>
  10. </div>
  11. </template>
  12. <script>
  13. // 自定义组件
  14. // 1. 导入组件
  15. import ChildCom from '../components/ChildCom.vue'
  16. import {ref, reactive, toRefs, provide} from 'vue'
  17. export default {
  18. name: 'Parent',
  19. // data() {
  20. // return {
  21. // parentNum: 10,
  22. // }
  23. // },
  24. // // 祖传孙 provide 提供(响应式)
  25. // provide() {
  26. // // 传递一个引用
  27. // return {
  28. // cParentNum: this.cParentNum,
  29. // }
  30. // },
  31. // methods: {
  32. // cParentNum() {
  33. // return this.parentNum
  34. // }
  35. // },
  36. // 2. 注册组件
  37. components: {
  38. ChildCom,
  39. },
  40. // 组合 api
  41. setup() {
  42. // 响应式绑定
  43. const parentNum = ref(10)
  44. // 祖传孙 provide 提供(响应式)
  45. provide('parentNum', parentNum)
  46. // 使用的数据或方法返回
  47. return {
  48. parentNum,
  49. }
  50. }
  51. }
  52. </script>
  53. <style lang="scss" scoped>
  54. </style>
  • components/ChildCom.vue
  1. <template>
  2. <div>
  3. <h1>Child Com</h1>
  4. <p>ChildCom - Parent - Demo</p>
  5. <p>ChildCom props receive father: parentNum = {{ parentNum }}</p>
  6. <p>
  7. <button @click="addChildNum()">childNum++</button>
  8. = {{ childNum }}
  9. </p>
  10. <!-- 父接收子,通过子声明的自定义事件 -->
  11. <grand-son :childNum="childNum" @grandSonChangeFather="grandSonChangeMe"></grand-son>
  12. <!-- 默认插槽 -->
  13. <!-- <slot-com>Slot value</slot-com> -->
  14. <!-- 具名插槽,使用模板 -->
  15. <!-- <slot-com><template v-slot:testSlot>TestSlot value</template></slot-com> -->
  16. <!-- 具名插槽,接收子传参 -->
  17. <slot-com>
  18. <template v-slot:testSlot="{test1, test2}">TestSlot value, params: {{ test1 }} {{ test2 }}</template>
  19. </slot-com>
  20. </div>
  21. </template>
  22. <script>
  23. import GrandSon from './children/GrandSon.vue'
  24. import SlotCom from './children/SlotCom.vue'
  25. import {ref, reactive, toRefs} from 'vue'
  26. export default {
  27. name: 'ChildCom',
  28. // data() {
  29. // return {
  30. // childNum: 100,
  31. // }
  32. // },
  33. props: ['parentNum'],
  34. // props: {
  35. // parentNum: {
  36. // type: Number,
  37. // default: 0
  38. // }
  39. // },
  40. components: {
  41. GrandSon, SlotCom
  42. },
  43. // methods: {
  44. // // 子改变父的自定义事件
  45. // grandSonChangeMe(n) {
  46. // this.childNum += n;
  47. // }
  48. // }
  49. // 组合 api
  50. setup() {
  51. const data = reactive({
  52. childNum: 100,
  53. })
  54. // 子变量自增
  55. const addChildNum = () => data.childNum++
  56. // 子改变父的自定义事件
  57. const grandSonChangeMe = n => data.childNum += n
  58. // 使用的数据或方法返回
  59. return {
  60. ...toRefs(data), addChildNum, grandSonChangeMe
  61. }
  62. }
  63. }
  64. </script>
  65. <style lang="scss" scoped>
  66. </style>
  • components/children/GrandSon.vue
  1. <template>
  2. <div>
  3. <h1>Grand son</h1>
  4. <p>GrandSon - ChildCom - Parent - Demo</p>
  5. <p>GrandSon props receive father: childNum = {{ childNum }}</p>
  6. <p>GrandSon inject receive grandfather: parentNum = {{ parentNum }}</p>
  7. <!-- 子传父,通过自定义方法 -->
  8. <p>
  9. <button @click="emitGrandSonChangeFather(10)">changeFather childNum + 10</button>
  10. = {{ childNum }}
  11. </p>
  12. </div>
  13. </template>
  14. <script>
  15. import {ref, reactive, toRefs, inject} from 'vue'
  16. export default {
  17. name: 'GrandSon',
  18. // 父传子 props 接收
  19. props: ['childNum'],
  20. // props: {
  21. // childNum: {
  22. // type: Number,
  23. // default: 0,
  24. // }
  25. // },
  26. // 祖传孙 inject 注入
  27. // inject: ['cParentNum'],
  28. // inject: {
  29. // parentNum: {
  30. // type: Number,
  31. // default: 0
  32. // }
  33. // },
  34. // computed: {
  35. // parentNum() {
  36. // return this.cParentNum()
  37. // }
  38. // },
  39. // 组合 api
  40. setup(props, {emit}) {
  41. let parentNum = inject('parentNum')
  42. const emitGrandSonChangeFather = n => emit('grandSonChangeFather', n)
  43. return {
  44. parentNum,
  45. emitGrandSonChangeFather
  46. }
  47. }
  48. }
  49. </script>
  50. <style lang="scss" scoped>
  51. </style>

改写路由

  • views/Router.vue
  1. <template>
  2. <div>
  3. <h1>Router</h1>
  4. <p>
  5. <router-link :to="router">Router</router-link>
  6. </p>
  7. <p>
  8. <router-link :to="query">query</router-link>
  9. </p>
  10. <p>query: a = {{ paramA }}, b = {{ paramB }}</p>
  11. <p>
  12. <button @click="changeQuery(10, 20)">query</button>
  13. </p>
  14. <p>change a = 10, b = 20</p>
  15. </div>
  16. </template>
  17. <script>
  18. import {useRoute, useRouter} from 'vue-router'
  19. import {ref, reactive, toRefs, computed} from 'vue'
  20. export default {
  21. name: 'Router',
  22. // data() {
  23. // return {
  24. // router: {
  25. // name: 'Router',
  26. // },
  27. // query: {
  28. // path: '/router',
  29. // query: {
  30. // a: 1,
  31. // b: 2,
  32. // }
  33. // }
  34. // }
  35. // },
  36. // computed: {
  37. // paramA() {
  38. // return this.$route.query.a
  39. // },
  40. // paramB() {
  41. // return this.$route.query.b
  42. // }
  43. // },
  44. // methods: {
  45. // changeQuery(a, b) {
  46. // this.$router.push({
  47. // path: '/router',
  48. // query: {
  49. // a,
  50. // b,
  51. // }
  52. // })
  53. // }
  54. // }
  55. setup() {
  56. const route = useRoute()
  57. const router = useRouter()
  58. const data = reactive({
  59. router: {
  60. name: 'Router',
  61. },
  62. query: {
  63. path: '/router',
  64. query: {
  65. a: 1,
  66. b: 2,
  67. }
  68. },
  69. })
  70. const paramA = computed(() => route.query.a),
  71. paramB = computed(() => route.query.b),
  72. changeQuery = (a, b) => router.push({
  73. path: '/router',
  74. query: {
  75. a,
  76. b,
  77. }
  78. })
  79. return {
  80. ...toRefs(data),
  81. paramA,
  82. paramB,
  83. changeQuery,
  84. }
  85. }
  86. }
  87. </script>
  88. <style lang="scss" scoped>
  89. </style>

改写状态管理

  • views/State.vue
  1. <template>
  2. <div>
  3. <h1>State management</h1>
  4. <p>store num: {{ num }}</p>
  5. <p>commit mutations change state
  6. <button @click="commitNum(10)">state.num + 10</button>
  7. </p>
  8. <p>dispatch actions commit change mutations change state
  9. <button @click="dispatchNum(100)">state.num + 100</button>
  10. </p>
  11. <p>getters property: {{ sum }}</p>
  12. </div>
  13. </template>
  14. <script>
  15. // 状态管理
  16. import {useStore} from 'vuex'
  17. import {computed} from 'vue'
  18. export default {
  19. name: 'State',
  20. // computed: {
  21. // num() {
  22. // return this.$store.state.num
  23. // },
  24. // sum() {
  25. // return this.$store.getters.sum
  26. // }
  27. // },
  28. // methods: {
  29. // commitNum(n) {
  30. // this.$store.commit('num', n)
  31. // },
  32. // dispatchNum(n) {
  33. // this.$store.dispatch('num', n)
  34. // }
  35. // }
  36. setup() {
  37. const store = useStore()
  38. const num = computed(() => store.state.num),
  39. sum = computed(() => store.getters.sum),
  40. commitNum = n => store.commit('num', n),
  41. dispatchNum = n => store.dispatch('num', n)
  42. return {
  43. num,
  44. sum,
  45. commitNum,
  46. dispatchNum,
  47. }
  48. }
  49. }
  50. </script>
  51. <style lang="scss" scoped>
  52. </style>