Vue组件通信-插槽-路由-导航守卫-状态管理-组合API
创建 project-demo 项目
npm install -g @vue/cli
vue --version
vue create project-demo
创建父子孙组件
views/Parent.vue
<template>
<div>
<h1>Parent</h1>
<p>Parent - Demo</p>
<p>
<button @click="parentNum++">ParentNum++</button>
= {{ parentNum }}
</p>
<child-com :parentNum="parentNum"></child-com>
</div>
</template>
<script>
// 自定义组件
// 1. 导入组件
import ChildCom from '../components/ChildCom.vue'
export default {
name: 'Parent',
data() {
return {
parentNum: 10,
}
},
// 祖传孙 provide 提供(响应式)
provide() {
// 传递一个引用
return {
cParentNum: this.cParentNum,
}
},
methods: {
cParentNum() {
return this.parentNum
}
},
// 2. 注册组件
components: {
ChildCom,
}
}
</script>
<style lang="scss" scoped>
</style>
components/ChildCom.vue
<template>
<div>
<h1>Child Com</h1>
<p>ChildCom - Parent - Demo</p>
<p>ChildCom props receive father: parentNum = {{ parentNum }}</p>
<p>
<button @click="addChildNum()">childNum++</button>
= {{ childNum }}
</p>
<!-- 父接收子,通过子声明的自定义事件 -->
<grand-son :childNum="childNum" @grandSonChangeFather="grandSonChangeMe"></grand-son>
<!-- 默认插槽 -->
<!-- <slot-com>Slot value</slot-com> -->
<!-- 具名插槽,使用模板 -->
<!-- <slot-com><template v-slot:testSlot>TestSlot value</template></slot-com> -->
<!-- 具名插槽,接收子传参 -->
<slot-com>
<template v-slot:testSlot="{test1, test2}">TestSlot value, params: {{ test1 }} {{ test2 }}</template>
</slot-com>
</div>
</template>
<script>
import GrandSon from './children/GrandSon.vue'
import SlotCom from './children/SlotCom.vue'
export default {
name: 'ChildCom',
data() {
return {
childNum: 100,
}
},
props: ['parentNum'],
// props: {
// parentNum: {
// type: Number,
// default: 0
// }
// },
components: {
GrandSon, SlotCom
},
methods: {
// 子变量自增
addChildNum() {
this.childNum++
},
// 子改变父的自定义事件
grandSonChangeMe(n) {
this.childNum += n
}
}
}
</script>
<style lang="scss" scoped>
</style>
components/children/GrandSon.vue
<template>
<div>
<h1>Grand son</h1>
<p>GrandSon - ChildCom - Parent - Demo</p>
<p>GrandSon props receive father: childNum = {{ childNum }}</p>
<p>GrandSon inject receive grandfather: parentNum = {{ parentNum }}</p>
<!-- 子传父,通过自定义方法 -->
<p>
<button @click="emitGrandSonChangeFather(10)">changeFather childNum + 10</button>
= {{ childNum }}
</p>
</div>
</template>
<script>
export default {
name: 'GrandSon',
// 父传子 props 接收
props: ['childNum'],
// props: {
// childNum: {
// type: Number,
// default: 0,
// }
// },
// 祖传孙 inject 注入
inject: ['cParentNum'],
// inject: {
// parentNum: {
// type: Number,
// default: 0
// }
// },
computed: {
parentNum() {
return this.cParentNum()
}
},
methods: {
emitGrandSonChangeFather(n) {
this.$emit('grandSonChangeFather', n)
}
}
}
</script>
<style lang="scss" scoped>
</style>
创建插槽测试组件
components/children/SlotCom.vue
<template>
<div>
<h1>Slot Com</h1>
<!-- 默认插槽 -->
<!-- <slot>Slot default</slot> -->
<!-- 具名插槽 -->
<!-- <slot name="testSlot">TestSlot default</slot> -->
<!-- 具名插槽,向父传数据 -->
<slot name="testSlot" test1="Hello" test2="vue">TestSlot default</slot>
</div>
</template>
<script>
export default {
name: 'SlotCom',
}
</script>
<style lang="scss" scoped>
</style>
创建路由测试组件
views/Router.vue
<template>
<div>
<h1>Router</h1>
<p>
<router-link :to="router">Router</router-link>
</p>
<p>
<router-link :to="query">query</router-link>
</p>
<p>query: a = {{ paramA }}, b = {{ paramB }}</p>
<p>
<button @click="changeQuery(10, 20)">query</button>
</p>
<p>change a = 10, b = 20</p>
</div>
</template>
<script>
export default {
name: 'Router',
data() {
return {
router: {
name: 'Router',
},
query: {
path: '/router',
query: {
a: 1,
b: 2,
}
}
}
},
computed: {
paramA() {
return this.$route.query.a
},
paramB() {
return this.$route.query.b
}
},
methods: {
changeQuery(a, b) {
this.$router.push({
path: '/router',
query: {
a,
b,
}
})
}
}
}
</script>
<style lang="scss" scoped>
</style>
创建状态管理测试组件
views/State.vue
<template>
<div>
<h1>State management</h1>
<p>store num: {{ num }}</p>
<p>commit mutations change state
<button @click="commitNum(10)">state.num + 10</button>
</p>
<p>dispatch actions commit change mutations change state
<button @click="dispatchNum(100)">state.num + 100</button>
</p>
<p>getters property: {{ sum }}</p>
</div>
</template>
<script>
export default {
name: 'State',
computed: {
num() {
return this.$store.state.num
},
sum() {
return this.$store.getters.sum
}
},
methods: {
commitNum(n) {
this.$store.commit('num', n)
},
dispatchNum(n) {
this.$store.dispatch('num', n)
}
}
}
</script>
<style lang="scss" scoped>
</style>
注册路由和添加导航
router/index.js
import {createRouter, createWebHistory} from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: {
title: 'Home - Demo'
}
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
meta: {
title: 'About - Demo'
}
},
// 父组件
{
path: '/parent',
name: 'Parent',
component: () => import('../views/Parent.vue'),
meta: {
title: 'Parent - Demo'
},
// 子组件
children: [
{
path: 'child-com',
name: 'ChildCom',
component: () => import('../components/ChildCom.vue'),
meta: {
title: 'ChildCom - Parent - Demo'
},
// 孙组件
children: [
{
path: 'grand-son',
name: 'GrandSon',
component: () => import('../components/children/GrandSon.vue'),
meta: {
title: 'GrandSon - ChildCom - Parent - Demo'
}
},
{
path: 'slot-com',
name: 'SlotCom',
component: () => import('../components/children/SlotCom.vue'),
meta: {
title: 'SlotCom - ChildCom - Parent - Demo'
}
},
]
}
]
},
// 路由测试
{
path: '/router',
name: 'Router',
component: () => import('../views/Router.vue'),
meta: {
title: 'Router - Parent - Demo'
}
},
// 状态管理测试
{
path: '/state',
name: 'State',
component: () => import('../views/State.vue'),
meta: {
title: 'State - Parent - Demo'
}
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
// 前置导航守卫
router.beforeEach((to, from) => {
document.title = to.meta.title
})
export default router
添加导航 App.vue
<template>
<div id="nav">
<router-link to="/">Home</router-link>
|
<router-link to="/about">About</router-link>
|
<router-link :to="{name: 'Parent'}">Parent</router-link>
|
<router-link :to="{name: 'Router'}">Router</router-link>
|
<router-link :to="{name: 'State'}">State</router-link>
</div>
<router-view/>
</template>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
a {
font-weight: bold;
color: #2c3e50;
&.router-link-exact-active {
color: #42b983;
}
}
}
</style>
使用状态管理
store/index.js
import {createStore} from 'vuex'
export default createStore({
state: {
num: 100,
},
mutations: {
num(state, n) {
state.num += n
}
},
actions: {
// 解构上下文 context
num({state, commit, getters}, n) {
commit('num', n)
}
},
modules: {},
// 计算属性
getters: {
sum(state, getter) {
let sum = 0;
for (let i = 1; i <= state.num; i++) sum += i
return getter.str + sum;
},
str(state, getter) {
return `1 + 2 + ... + ${state.num} = `;
},
}
})
运行测试
npm run serve
父子孙组件通信 - 插槽及传值
路由测试
状态管理和异步更新
使用组合 api
改写父子孙组件
- views/Parent.vue
<template>
<div>
<h1>Parent</h1>
<p>Parent - Demo</p>
<p>
<button @click="parentNum++">ParentNum++</button>
= {{ parentNum }}
</p>
<child-com :parentNum="parentNum"></child-com>
</div>
</template>
<script>
// 自定义组件
// 1. 导入组件
import ChildCom from '../components/ChildCom.vue'
import {ref, reactive, toRefs, provide} from 'vue'
export default {
name: 'Parent',
// data() {
// return {
// parentNum: 10,
// }
// },
// // 祖传孙 provide 提供(响应式)
// provide() {
// // 传递一个引用
// return {
// cParentNum: this.cParentNum,
// }
// },
// methods: {
// cParentNum() {
// return this.parentNum
// }
// },
// 2. 注册组件
components: {
ChildCom,
},
// 组合 api
setup() {
// 响应式绑定
const parentNum = ref(10)
// 祖传孙 provide 提供(响应式)
provide('parentNum', parentNum)
// 使用的数据或方法返回
return {
parentNum,
}
}
}
</script>
<style lang="scss" scoped>
</style>
- components/ChildCom.vue
<template>
<div>
<h1>Child Com</h1>
<p>ChildCom - Parent - Demo</p>
<p>ChildCom props receive father: parentNum = {{ parentNum }}</p>
<p>
<button @click="addChildNum()">childNum++</button>
= {{ childNum }}
</p>
<!-- 父接收子,通过子声明的自定义事件 -->
<grand-son :childNum="childNum" @grandSonChangeFather="grandSonChangeMe"></grand-son>
<!-- 默认插槽 -->
<!-- <slot-com>Slot value</slot-com> -->
<!-- 具名插槽,使用模板 -->
<!-- <slot-com><template v-slot:testSlot>TestSlot value</template></slot-com> -->
<!-- 具名插槽,接收子传参 -->
<slot-com>
<template v-slot:testSlot="{test1, test2}">TestSlot value, params: {{ test1 }} {{ test2 }}</template>
</slot-com>
</div>
</template>
<script>
import GrandSon from './children/GrandSon.vue'
import SlotCom from './children/SlotCom.vue'
import {ref, reactive, toRefs} from 'vue'
export default {
name: 'ChildCom',
// data() {
// return {
// childNum: 100,
// }
// },
props: ['parentNum'],
// props: {
// parentNum: {
// type: Number,
// default: 0
// }
// },
components: {
GrandSon, SlotCom
},
// methods: {
// // 子改变父的自定义事件
// grandSonChangeMe(n) {
// this.childNum += n;
// }
// }
// 组合 api
setup() {
const data = reactive({
childNum: 100,
})
// 子变量自增
const addChildNum = () => data.childNum++
// 子改变父的自定义事件
const grandSonChangeMe = n => data.childNum += n
// 使用的数据或方法返回
return {
...toRefs(data), addChildNum, grandSonChangeMe
}
}
}
</script>
<style lang="scss" scoped>
</style>
- components/children/GrandSon.vue
<template>
<div>
<h1>Grand son</h1>
<p>GrandSon - ChildCom - Parent - Demo</p>
<p>GrandSon props receive father: childNum = {{ childNum }}</p>
<p>GrandSon inject receive grandfather: parentNum = {{ parentNum }}</p>
<!-- 子传父,通过自定义方法 -->
<p>
<button @click="emitGrandSonChangeFather(10)">changeFather childNum + 10</button>
= {{ childNum }}
</p>
</div>
</template>
<script>
import {ref, reactive, toRefs, inject} from 'vue'
export default {
name: 'GrandSon',
// 父传子 props 接收
props: ['childNum'],
// props: {
// childNum: {
// type: Number,
// default: 0,
// }
// },
// 祖传孙 inject 注入
// inject: ['cParentNum'],
// inject: {
// parentNum: {
// type: Number,
// default: 0
// }
// },
// computed: {
// parentNum() {
// return this.cParentNum()
// }
// },
// 组合 api
setup(props, {emit}) {
let parentNum = inject('parentNum')
const emitGrandSonChangeFather = n => emit('grandSonChangeFather', n)
return {
parentNum,
emitGrandSonChangeFather
}
}
}
</script>
<style lang="scss" scoped>
</style>
改写路由
- views/Router.vue
<template>
<div>
<h1>Router</h1>
<p>
<router-link :to="router">Router</router-link>
</p>
<p>
<router-link :to="query">query</router-link>
</p>
<p>query: a = {{ paramA }}, b = {{ paramB }}</p>
<p>
<button @click="changeQuery(10, 20)">query</button>
</p>
<p>change a = 10, b = 20</p>
</div>
</template>
<script>
import {useRoute, useRouter} from 'vue-router'
import {ref, reactive, toRefs, computed} from 'vue'
export default {
name: 'Router',
// data() {
// return {
// router: {
// name: 'Router',
// },
// query: {
// path: '/router',
// query: {
// a: 1,
// b: 2,
// }
// }
// }
// },
// computed: {
// paramA() {
// return this.$route.query.a
// },
// paramB() {
// return this.$route.query.b
// }
// },
// methods: {
// changeQuery(a, b) {
// this.$router.push({
// path: '/router',
// query: {
// a,
// b,
// }
// })
// }
// }
setup() {
const route = useRoute()
const router = useRouter()
const data = reactive({
router: {
name: 'Router',
},
query: {
path: '/router',
query: {
a: 1,
b: 2,
}
},
})
const paramA = computed(() => route.query.a),
paramB = computed(() => route.query.b),
changeQuery = (a, b) => router.push({
path: '/router',
query: {
a,
b,
}
})
return {
...toRefs(data),
paramA,
paramB,
changeQuery,
}
}
}
</script>
<style lang="scss" scoped>
</style>
改写状态管理
- views/State.vue
<template>
<div>
<h1>State management</h1>
<p>store num: {{ num }}</p>
<p>commit mutations change state
<button @click="commitNum(10)">state.num + 10</button>
</p>
<p>dispatch actions commit change mutations change state
<button @click="dispatchNum(100)">state.num + 100</button>
</p>
<p>getters property: {{ sum }}</p>
</div>
</template>
<script>
// 状态管理
import {useStore} from 'vuex'
import {computed} from 'vue'
export default {
name: 'State',
// computed: {
// num() {
// return this.$store.state.num
// },
// sum() {
// return this.$store.getters.sum
// }
// },
// methods: {
// commitNum(n) {
// this.$store.commit('num', n)
// },
// dispatchNum(n) {
// this.$store.dispatch('num', n)
// }
// }
setup() {
const store = useStore()
const num = computed(() => store.state.num),
sum = computed(() => store.getters.sum),
commitNum = n => store.commit('num', n),
dispatchNum = n => store.dispatch('num', n)
return {
num,
sum,
commitNum,
dispatchNum,
}
}
}
</script>
<style lang="scss" scoped>
</style>