项目设计灵感
本项目的灵感来自于Vuero - A complete Vue 3 design system (cssninja.io)。组件的样式均借鉴于此。
项目搭建
全局安装create-vite-app
yarn global add create-vite-app@version
//或者
npm install -g create-vite-app@version
cra demo-name
vue-router的用法
yarn add vue-router@version
npm install vue-router@version
在入口文件
import Index from './components/Index.vue'
import {createWebHashHistory,createRouter} from 'vue-router' //使用hash模式,优点是在切换路由时毋需再次渲染DOM
const hash = createWebHashHistory()
const router = createRouter({
history:hash,
routes:[
{path:'/',
component:Index
}
]
})
createApp(App)
.use(router) //挂载到容器上
之后只需要在展示路由页面的地方写上<router-view/>即可。
使用<router-link to='/'>点击查看主页</router-link>来创建跳转链接。
父组件与子组件通过provide/inject传递数据
在父组件中放入子组件均会用到的变量。这种做法的好处是无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。
<script lang='ts'>
import { ref, provide } from 'vue'
export default {
setup(){
const asideVisible = ref(false)
provide('to_child',asideVisible)
}
}
</script>
在子组件中获取,return出来就可以在组件中使用了。
<script lang='ts'>
import { Ref, inject } from 'vue'
export default {
setup(){
const asideVisible = inject<Ref<boolean>>('to_child')
return { asideVisible }
}
}
</script>
子组件内部数据的传递
通过在组件中使用ref定义数据的初始状态和修改数据的方法来实现,注意读取ref的值时要使用checked.value。
<template>
<button @click="toggle" :class="{checked}"> //如果checked为真,class加上checked
<span></span>
</button>
</template>
<script lang="ts">
import { ref } from 'vue'
export default {
setup(){
const checked = ref(false)
const toggle = ()=>{
checked.value = !checked.value
}
return {checked, toggle}
}
}
</script>
父子组件通信
在父组件中的子组件上添加事件,控制子组件的状态。
<template>
<div>
<Child :value='y' @input='y = $event' /> //v-bind:value = 'y'
</div>
</template>
<script lang='ts'>
import Child from '@/lib/Child.vue'
export default{
components:{ Child },
setup(){
const y = ref(true)
return { y }
}
}
</script>
在子组件中,使用props来获取value。但是不能直接使用props.value来修改,只能通过setup的第二个参数context的方法emit来修改数据。
<button @click="toggle" :class="{checked:value}">
...
...
export default {
props:{
value:Boolean //直接就可以展示在子组件的template中,与setup无关。
},
setup(props,context){
const toggle = ()=>{
context.emit('input',!props.value) //通过context.emit 触发input事件 实现父子组件通信
}
return { toggle }
}
}
Vue3的编程模型
内部数据 vs 父子数据
子组件中使用ref定义数据的初始状态并创建修改数据的方法。这种方法简单,但不能与外部交流。
父子间的数据传递:如上例,父组件在使用的子组件上添加:value = 'y'(初始状态) 以及 @input = 'y = $event'。子组件通过props.value来获得数据的初始值,通过自定义方法,通过设定context.emit触发input事件来修改。父组件通过@input = 'y = $event'来拿到emit触发事件的结果,即props.value = !props.value。将value的值变为!value。从而实现组件通信。
input只是为了好理解,真正的事件名字与value有关,叫做update:value。
父组件上应该这样写:
<Child :value='y' @update:value='y = $event' />
//而这种写法可以写成
<Child v-model:value='y' />
同时子组件也应该把input改为update:value
setup(props,context){
const toggle = ()=>{
context.emit('update:value',!props.value) //通过context.emit 触发update:value事件 实现父子组件通信
}
return { toggle }
}
如果写成
<Child v-model='y' />
相当于传modelValue给props而非自定义的propsName。
插槽
对于一个自定义组件,如下所示
<base-layout>页面展示</base-layout>
它的模板如下:
<div class="container">
</div>
如果需要展示自定义组件中的字符串、html等任何模板代码,只需要在模板中加入<slot>default content </slot>即可。
还是如上例子
如果更复杂,就需要用上具名插槽。
//自定义组件中
<base-layout>
<template v-slot:header> //只能访问模板中的变量, v-slot 参数提供其名称
<h1>Here might be a page title</h1>
</template>
<template v-slot:default> //只能访问模板中的变量
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</template>
<template v-slot:footer> //只能访问模板中的变量
<p>Here's some contact info</p>
</template>
</base-layout>
//模板中
<div class="container">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> // name 默认值为dafault
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
注意插槽中的内容是在模板中的,它只能访问作用域在模板中的变量。
Vue 3 属性绑定
Vue 3会默认将传给自定义组件的事件传给其模板,事件会作用在包括模板的第一个标签(下例中的div)上。
消除attrs继承
在模板中
export default {
inheritAttrs : false
}
实现点击模板的一部分为事件
如点击button而不是div触发事件。
<template>
<div>
<button v-bind='$attrs'> //也可以使用setup中的context.attrs来获取属性
</div>
</template>