【若依】若依Vue3前端拆解分析--布局分析--index篇

若依Vue3前端拆解分析

布局分析 – Layout

这个是Layout的目录结构
layout
├─ index.vue  // 主文件
└─ components  // 组件目录
   ├─ AppMain.vue
   ├─ index.js
   ├─ Navbar.vue  // 顶部导航
   ├─ TagsView  // 标签组件
   │  ├─ index.vue
   │  └─ ScrollPane.vue
   ├─ Sidebar  // 侧边栏组件
   │  ├─ index.vue
   │  ├─ Link.vue
   │  ├─ Logo.vue
   │  └─ SidebarItem.vue
   ├─ Settings  // 右侧布局设置抽屉
   │  └─ index.vue
   ├─ InnerLink
   │  └─ index.vue
   └─ IframeToggle
      └─ index.vue

首先来看index主文件。

<template>
  <div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
    <div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
    <!-- 侧边栏 -->
    <sidebar v-if="!sidebar.hide" class="sidebar-container" />
    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
      <div :class="{ 'fixed-header': fixedHeader }">
        <!-- 顶部导航 -->
        <navbar @setLayout="setLayout" />
        <!-- 标签页 -->
        <tags-view v-if="needTagsView" />
      </div>
      <app-main />
      <settings ref="settingRef" />
    </div>
  </div>
</template>

<script setup>
import { useWindowSize } from '@vueuse/core'
import Sidebar from './components/Sidebar/index.vue'
import { AppMain, Navbar, Settings, TagsView } from './components'
import defaultSettings from '@/settings'

import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'

const settingsStore = useSettingsStore()
const theme = computed(() => settingsStore.theme);
const sideTheme = computed(() => settingsStore.sideTheme);
const sidebar = computed(() => useAppStore().sidebar);
const device = computed(() => useAppStore().device);
const needTagsView = computed(() => settingsStore.tagsView);
const fixedHeader = computed(() => settingsStore.fixedHeader);

const classObj = computed(() => ({
  hideSidebar: !sidebar.value.opened,
  openSidebar: sidebar.value.opened,
  withoutAnimation: sidebar.value.withoutAnimation,
  mobile: device.value === 'mobile'
}))

const { width, height } = useWindowSize();
const WIDTH = 992; // refer to Bootstrap's responsive design

watch(() => device.value, () => {
  if (device.value === 'mobile' && sidebar.value.opened) {
    useAppStore().closeSideBar({ withoutAnimation: false })
  }
})

watchEffect(() => {
  if (width.value - 1 < WIDTH) {
    useAppStore().toggleDevice('mobile')
    useAppStore().closeSideBar({ withoutAnimation: true })
  } else {
    useAppStore().toggleDevice('desktop')
  }
})

function handleClickOutside() {
  useAppStore().closeSideBar({ withoutAnimation: false })
}

const settingRef = ref(null);
function setLayout() {
  settingRef.value.openSetting();
}
</script>
注意:css部分没有粘贴到此处

可以看到在template部分,基本上是把定义的组件进行使用,这个地方可以理解为剪纸画,这个index就是一个画板,这些组件就像裁剪好的各种形状,在这个画布的对应位置粘贴上对应的形状,就组成了我们想要的作品。当然这个位置可以通过css来自己定义。

然后是script部分,这个部分先来看一下有什么东西,首先是导入,这里主要导入的是组件还有pinia状态管理工具。然后是一些computed计算属性,这里要想一下计算属性和普通函数的区别。接下来就是侦听器,这里有两种watchwatchEffect,**这里就要想一下他们的区别。**最后就是一些函数,用来完成一些功能。

接下来就来一点一点的看一下这index到底做了什么?

首先是template部分,最外层是一个大的div盒子,包裹了所有的组件,把这个div盒子单独拎出来看一下

<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
    组件内容
</div>

注意这个:class想一下这是vue当中的哪一个指令的简写:class="classObj"这个就是动态设置这个div的样式,它的值是classObj,这个东西是在script当中定义的一个计算属性,代码如下

const classObj = computed(() => ({
  hideSidebar: !sidebar.value.opened,
  openSidebar: sidebar.value.opened,
  withoutAnimation: sidebar.value.withoutAnimation,
  mobile: device.value === 'mobile'
}))

可以看到classObj返回的是一个对象,这里要想一下当:class后面的值为数组和为对象的时候有什么区别。那么这个对象当中的四对键值对是干嘛的?见名知意hideSidebar侧边栏隐藏,openSidebar侧边栏开启,withoutAnimation控制侧边栏开启关闭动画,mobile当前页面显示的设备,是移动设备还是桌面设备,注意移动设备和桌面设备在布局样式上有一些差异。

后面就是一个简单的class="app-wrapper"这个样式就是在当前的文件中进行定义的css样式,最后就是一个:style="{ '--current-color': theme }这里就是设置了一下主题颜色,设置了一个css变量。这样最外层的div盒子就看完了。

接下来是一个div自结束标签,如下

<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />

它没有包裹任何东西,那么它的作用是什么呢?来分析,首先看到v-if这里想一下v-if和v-show的区别v-if后面的值为true它就显示,如果为false他就不显示。他其实就是移动端打开侧边时显示一个透明的黑色遮罩层。他有一个点击事件@click="handleClickOutside"这个点击事件就是点击遮罩层的时候,关闭侧边栏。

来解读一下它,就是当设备为移动端并且侧边栏打开的时候,就显示这个遮罩层,定义了他的样式,比如背景为黑色,有点透明度,给他一个点击事件,点击的时候关闭侧边栏。

后面就是自定义的组件,sidebar侧边栏组件

<sidebar v-if="!sidebar.hide" class="sidebar-container" />

首先是v-if="!sidebar.hide",注意这里的sidebar不是组件,是定义的计算属性,作用是用于控制侧边栏的显示和隐藏,计算属性sidebar使用通过状态管理pinia获取的。class="sidebar-container"它是用来定义侧边栏的样式,注意这个sidebar-container样式类名不在当前的index文件当中,而是在src/assets/style/sidebar.scss文件中,这个文件在同目录下的index.scss文件中进行了导入,而index.scss又在main.js中进行了导入。

接着往下又是一个div盒子,这个盒子就包含了顶部导航栏、主体显示区域、侧边布局设置抽屉。

    <div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
      <div :class="{ 'fixed-header': fixedHeader }">
        <!-- 顶部导航 -->
        <navbar @setLayout="setLayout" />
        <!-- 标签页 -->
        <tags-view v-if="needTagsView" />
      </div>
      <app-main />
      <settings ref="settingRef" />
    </div>

最外层的div同样是使用:class进行动态样式设置,hasTagsView是控制.app-main这个容器的最小高度,一般是顶部导航栏高度加上标签栏的高度,可以在AppMain.vue找到这个类。sidebarHide就是控制侧边栏隐藏的时候,当前容器的宽度。

再往下又是一个div容器还是使用:class动态的设置样式,这个容器放的是顶部导航栏以及标签栏,通过这个类名fixed-header就可以看出,这个样式的作用就是控制顶部导航栏是否需要固定。什么意思呢?就是当页面的高度超出视口的高度时,会出现滚动条,向下滚动时,如果顶部导航栏是固定的话,那么导航栏就不会向上滚动不见,说白点就是他固定之后,不管怎么滚动他都不会消失。

这个容器里面包裹了navbartags-view两个组件,navbar组件有一个自定义事件@setLayout,当事件触发时,调用setLayout函数,如下

const settingRef = ref(null);
function setLayout() {  
  settingRef.value.openSetting();
}

它的作用就是,当navbar内部触发了这个事件的时候,调用setLayout函数,这个函数的功能就是打开右侧设置布局的抽屉。

tags-view组件它只有一个v-if用来控制它是否显示隐藏。

再往下就是app-main组件,他就是路由的出口,也就是后面增加一下路由,每一个路由都有对应的页面,选择对应的路由显示对应的页面,就是通过app-main来显示,后面会详细说这个组件。

最后就是settings组件,这个组件就是右侧的布局设置组件,他有一个ref属性,如下

<settings ref="settingRef" />

有了这个ref属性,就可以通过settingRef来调用这个组件当中的实例。就比如说上面的setLayout,就是通过这个ref属性,调用了settings组件当中的openSetting()方法。

以上就是index这个文件的基本组成,可以发现这个文件中计算属性都是从状态管理工具pinia中获取的,大部分都是与布局样式相关的,这是因为,若依这个前端它可以让用户进行页面布局的自定义,就是在不改代码的情况下,用户可以自定义更改颜色,布局等,那么就可以把这些值放在pinia中进行统一管理,在调用的时候会更加方便。

有分析不对的地方或者不足的地方,还请各位大佬在评论区指出多谢多谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值