使用vite搭建Xueduo-UI的技术栈及思考

277 阅读2分钟

项目设计灵感

本项目的灵感来自于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' /> 

相当于传modelValueprops而非自定义的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>