export与import
js的模块机制核心是, 一个文件默认是一个“模块”,如果你不导出任何内容,其他文件就访问不到里面的内容。
这是为了 模块的封装性与安全性。
在模块化系统中(比如 ES6 模块),每个 .js
文件都是一个作用域封闭的模块,里面定义的变量、函数、对象默认是私有的,外部看不到。
例如:
// math.js
const add = (a, b) => a + b;
如果没有export,别的文件就不能用这个 add
。
想要外部能用,就必须显式export:
// math.js
export const add = (a, b) => a + b;
然后在需要的文件中import:
// main.js
import { add } from './math.js';
console.log(add(2, 3)); // 5
你必须通过
export
把内容“显式地公开”,别人才能通过import
使用,这就是模块之间的“通信规则”。
app.vue
在 Vue.js 项目中,App.vue
是项目的根组件,也就是整个应用程序的起点。它的主要作用可以概括为以下几点:
- 充当应用的根组件
- 所有的子组件(页面组件、功能组件等)最终都会被嵌套在
App.vue
中。 App.vue
通常会被挂载到index.html
中<div id="app"></div>
这个节点上
- 布局框架容器
App.vue
通常定义全局性的结构和样式,例如:
- 顶部导航栏
- 侧边栏
- 页面整体布局(header、main、footer)
<router-view />
或<slot />
的占位符,供子组件显示内容
- 引入全局资源
可以在 App.vue
中引入全局 CSS 文件、字体、图标库等:
<style>
@import './assets/global.css';
</style>
- 初始化数据或状态
可以在 App.vue
的 mounted
生命周期钩子中初始化一些全局逻辑,例如:
- 获取用户信息
- 设置主题
- 全局错误监听等
<template>
<div id="app">
<Navbar />
<Sidebar />
<router-view />
</div>
</template>
<script setup>
import Navbar from './components/Navbar.vue'
import Sidebar from './components/Sidebar.vue'
</script>
<style>
/* 全局样式 */
#app {
font-family: 'Arial', sans-serif;
}
</style>
App.vue
是 Vue 应用的“外壳”组件,负责包裹和管理整个项目的页面结构与逻辑入口。
如果你是用 Vite 创建的项目,它的入口通常是 main.js
或 main.ts
,里面会像这样使用 App.vue
:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
项目实例中的app.vue文件
<script setup>
import {RouterView} from 'vue-router'
import {useAppStore} from "@/stores/AppSetting.js";
import zhCn from 'element-plus/es/locale/lang/zh-cn'
const appStore = useAppStore()
appStore.initThemeColor()
const font = reactive({
color: 'rgba(0, 0, 0, .15)',
})
const locale = zhCn
</script>
<template>
<el-config-provider :locale="locale">
<RouterView/>
</el-config-provider>
</template>
<style scoped>
@import './assets/styles/font.css';
</style>
- 根组件入口:
<template>
<el-config-provider :locale="locale">//设置语言为中文
<RouterView/>
</el-config-provider>
</template>
-
<RouterView />
:这里是 Vue Router 的出口,占位符,用于显示当前路由匹配的页面组件,即整个应用页面内容都会被渲染在这里。 当你访问 -
/
,那么<RouterView />
会渲染:Home.vue
-
/about
,那么<RouterView />
会渲染:About.vue
<RouterView />
会“占位”显示当前路由对应的组件,具体显示哪个组件,取决于当前浏览器地址栏的路径,和你在 Vue Router 中配置的routes
一一对应。 -
所有的页面组件都通过
RouterView
嵌套在App.vue
中,体现了其作为“容器”的作用。
- 布局与全局结构
<el-config-provider :locale="locale">
...
</el-config-provider>
使用了 Element Plus 的 <el-config-provider>
来提供全局配置,这里设置了 中文语言包(zhCn
),会影响整个应用 Element UI 的默认文本(比如按钮、表格分页等的默认显示语言)。
- 引入全局资源与状态
import { useAppStore } from "@/stores/AppSetting.js";
const appStore = useAppStore()
appStore.initThemeColor()
- 使用了 Pinia 状态管理 的
useAppStore()
引入应用配置(比如主题颜色、水印开关等)。 initThemeColor()
表示在根组件初始化时,设置一次主题颜色,这是一种“全局初始化”的行为。
- 引入全局样式
<style scoped>
@import './assets/styles/font.css';
</style>
- 引入了一个全局字体样式文件
font.css
。 - 虽然使用了
scoped
,但字体文件的效果(例如字体引入、通用字体类)通常仍然影响整个页面。 - 虽然scoped本身会使效果只作用于当前组件,不能全局使用,但字体相关的规则(如
@font-face
、body { font-family: ... }
、全局类名.my-font
)本质上是:作用在全局作用域上的 CSS 规则,而不是组件内部的局部选择器。
所以,即使加了 scoped
,它们依然会影响整个页面。
layout
通过router.js,可以看到路由配置首页为layout
在 Vue 项目中,layout
是一个页面的框架/壳,比如包含:
- 顶部导航栏
- 侧边菜单栏
- 内容区域(真正渲染页面内容的地方)
这个 layout 通常作为首页 ('/'
) 的根组件,所有其他页面都是它里面的子页面。
/
页面渲染的是Layout.vue
+Home.vue
叠加的结果- Layout 是主框架,Home 是内容区域
layout:index.vue
<template>
结构部分 —— 布局骨架
<template>
<section class="main-layout" :class="collapseObj">
<!-- 侧边栏 -->
<SideBar v-if="['slide','mixin'].includes(appStore.layoutMode)"/>
<!-- 主内容区域 -->
<section class="main-container" :style="layoutStyle">
<!-- 顶部导航栏区域 -->
<section class="main-top" :class="{ 'fixed-header': true }">
<NavBar/>
<TagView/>
</section>
<!-- 页面主体内容(真正的页面组件由 <router-view> 渲染) -->
<AppMain/>
</section>
</section>
</template>
结构分层说明:
位置 | 组件 | 说明 |
---|---|---|
最外层 | .main-layout | 总框架容器,控制是否折叠侧边栏 |
左侧 | <SideBar /> | 侧边导航栏(可折叠,控制菜单) |
中间 | .main-container | 主内容区域(包含顶部和页面内容) |
顶部 | <NavBar /> + <TagView /> | 顶部菜单、标签页导航(类似标签切换) |
下方 | <AppMain /> | 实际页面内容,通过 <router-view /> 动态渲染 |
layout
sideBar
获取父组件传递来的值
const props = defineProps({
mode: {
type: String,
default: 'vertical'
}
})
从Layout父组件中获取mode
useRoute()获取路由对象
const currentRoute = useRoute();
useRoute()
:vue-router提供的组合式api,用来获取当前的路由对象,也就是你现在页面所在的路径参数元信息等。
例如:
假如你当前在 /user/123
页面,路由配置可能是这样:
{
path: '/user/:id',
component: UserPage
}
那么 useRoute()
获取的 currentRoute
就会是一个对象,里面包含:
currentRoute.path // "/user/123"
currentRoute.params.id // "123"
currentRoute.name // 路由的名字(如果有命名)
你可以在组件内部根据当前路径判断菜单选中、权限、加载数据等等。
通过Pinia获取状态
const appStore = useAppStore();
这是通过 Pinia 提供的状态管理函数,用来访问全局共享状态。
useAppStore
是你项目里自定义的 store 函数(通常在 stores/AppSetting.js
中定义),返回的是这个 store 的数据和方法。
Pinia 中的数据是响应式的,全局共享、自动同步。
当你在一个组件中修改 store 的值,其他使用同一个 store 的组件都会自动感知并更新。
举例:
你可能在AppSetting.js里定义了这个store:
import { defineStore } from 'pinia';
export const useAppStore = defineStore('AppSetting', () => {
// state:响应式数据
const layoutMode = ref('slide');
const sidebar = reactive({ opened: true });
// actions:用于操作 state 的方法
function toggleSidebar() {
sidebar.opened = !sidebar.opened;
}
// return 暴露出去
return {
layoutMode,
sidebar,
toggleSidebar
};
});
- 其中"AppSetting"是唯一标识符,必须有,且不能和别的store重复
return
是 用来“暴露”你想让外部组件访问的数据和方法,你 return 了什么,组件中用这个 store 时就能访问到什么。没有 return,就用不到。