若依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
计算属性,这里要想一下计算属性和普通函数的区别。接下来就是侦听器,这里有两种
watch
和
watchEffect
,这里就要想一下他们的区别。最后就是一些函数,用来完成一些功能。
接下来就来一点一点的看一下这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
就可以看出,这个样式的作用就是控制顶部导航栏是否需要固定。什么意思呢?就是当页面的高度超出视口的高度时,会出现滚动条,向下滚动时,如果顶部导航栏是固定的话,那么导航栏就不会向上滚动不见,说白点就是他固定之后,不管怎么滚动他都不会消失。
这个容器里面包裹了
navbar
和
tags-view
两个组件,
navbar
组件有一个自定义事件
@setLayout
,当事件触发时,调用
setLayout
函数,如下
const settingRef =ref(null);functionsetLayout(){
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中进行统一管理,在调用的时候会更加方便。
有分析不对的地方或者不足的地方,还请各位大佬在评论区指出多谢多谢
版权归原作者 王赤脚 所有, 如有侵权,请联系我们删除。