ASCII码 ASCII码

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

发布于:2021-12-16 12:19:17  栏目:技术文档

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
  • 父子孙组件通信 - 插槽及传值

    父子孙组件通信

  • 路由测试

    路由测试

  • 状态管理和异步更新

    状态管理

使用组合 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>
相关推荐
阅读 +