Nuxt.js布局系统

学习目标

通过本章节的学习,你将能够:

  • 了解Nuxt.js布局系统的基本概念
  • 掌握布局文件的创建和使用方法
  • 学习如何实现嵌套布局
  • 掌握动态布局的实现方法
  • 了解布局组件之间的通信方式

核心知识点讲解

布局系统的基本概念

在Nuxt.js中,布局系统允许你为应用程序创建一致的页面结构。通过布局,你可以:

  • 定义页面的整体结构(如页眉、页脚、侧边栏等)
  • 在多个页面之间共享相同的布局结构
  • 根据不同页面的需求使用不同的布局

布局文件的创建和使用

创建布局文件

布局文件存放在layouts目录中,默认情况下,Nuxt.js会使用layouts/default.vue作为默认布局:

├── layouts/
│   ├── default.vue    # 默认布局
│   └── custom.vue     # 自定义布局

基本布局文件结构

<!-- layouts/default.vue -->
<template>
  <div class="layout">
    <header>
      <h1>网站标题</h1>
      <nav>
        <ul>
          <li><nuxt-link to="/">首页</nuxt-link></li>
          <li><nuxt-link to="/about">关于我们</nuxt-link></li>
        </ul>
      </nav>
    </header>
    
    <main>
      <nuxt />
    </main>
    
    <footer>
      <p>版权信息</p>
    </footer>
  </div>
</template>

<style>
/* 布局样式 */
.layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

header {
  background-color: #f8f9fa;
  padding: 1rem;
  border-bottom: 1px solid #e9ecef;
}

main {
  flex: 1;
  padding: 2rem;
}

footer {
  background-color: #f8f9fa;
  padding: 1rem;
  border-top: 1px solid #e9ecef;
  text-align: center;
}
</style>

使用布局

在页面组件中,通过layout属性指定要使用的布局:

<!-- pages/about.vue -->
<template>
  <div>
    <h2>关于我们</h2>
    <p>公司简介...</p>
  </div>
</template>

<script>
export default {
  layout: 'custom' // 使用custom布局
}
</script>

如果不指定layout属性,页面将使用默认布局(default.vue)。

嵌套布局

Nuxt.js支持嵌套布局,允许你在一个布局中使用另一个布局:

创建嵌套布局

<!-- layouts/parent.vue -->
<template>
  <div class="parent-layout">
    <header>
      <h1>父布局标题</h1>
    </header>
    <div class="content">
      <nuxt />
    </div>
  </div>
</template>

<!-- layouts/child.vue -->
<template>
  <div class="child-layout">
    <aside>
      <h3>侧边栏</h3>
      <ul>
        <li><nuxt-link to="/child/page1">页面1</nuxt-link></li>
        <li><nuxt-link to="/child/page2">页面2</nuxt-link></li>
      </ul>
    </aside>
    <main>
      <nuxt />
    </main>
  </div>
</template>

<script>
export default {
  layout: 'parent' // 子布局使用父布局
}
</script>

使用嵌套布局

<!-- pages/child/page1.vue -->
<template>
  <div>
    <h2>子页面1</h2>
    <p>页面内容...</p>
  </div>
</template>

<script>
export default {
  layout: 'child' // 使用子布局,子布局会自动使用父布局
}
</script>

动态布局

Nuxt.js允许你根据不同条件动态切换布局:

基于路由参数的动态布局

<!-- pages/[id].vue -->
<template>
  <div>
    <h2>动态页面</h2>
    <p>页面ID: {{ $route.params.id }}</p>
  </div>
</template>

<script>
export default {
  layout({ route }) {
    // 根据路由参数选择布局
    return route.params.id === 'admin' ? 'admin' : 'default'
  }
}
</script>

基于用户状态的动态布局

<!-- pages/profile.vue -->
<template>
  <div>
    <h2>用户资料</h2>
    <p>用户信息...</p>
  </div>
</template>

<script>
export default {
  layout({ store }) {
    // 根据用户登录状态选择布局
    return store.state.user.loggedIn ? 'authenticated' : 'guest'
  }
}
</script>

布局组件通信

通过props传递数据

<!-- layouts/default.vue -->
<template>
  <div class="layout">
    <header>
      <h1>{{ pageTitle }}</h1>
    </header>
    <main>
      <nuxt />
    </main>
  </div>
</template>

<script>
export default {
  props: {
    pageTitle: {
      type: String,
      default: '默认标题'
    }
  }
}
</script>

<!-- pages/about.vue -->
<template>
  <div>
    <h2>关于我们</h2>
    <p>公司简介...</p>
  </div>
</template>

<script>
export default {
  layout: 'default',
  layoutProps: {
    pageTitle: '关于我们'
  }
}
</script>

通过事件传递数据

<!-- layouts/default.vue -->
<template>
  <div class="layout">
    <header>
      <button @click="toggleSidebar">切换侧边栏</button>
    </header>
    <div class="content">
      <aside v-if="sidebarOpen">
        侧边栏内容
      </aside>
      <main>
        <nuxt @update:sidebar="sidebarOpen = $event" />
      </main>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      sidebarOpen: false
    }
  },
  methods: {
    toggleSidebar() {
      this.sidebarOpen = !this.sidebarOpen
    }
  }
}
</script>

<!-- pages/home.vue -->
<template>
  <div>
    <h2>首页</h2>
    <button @click="updateSidebar(true)">打开侧边栏</button>
  </div>
</template>

<script>
export default {
  methods: {
    updateSidebar(status) {
      this.$emit('update:sidebar', status)
    }
  }
}
</script>

布局的生命周期钩子

布局组件支持以下生命周期钩子:

  • beforeCreate
  • created
  • beforeMount
  • mounted
  • beforeUpdate
  • updated
  • beforeUnmount
  • unmounted
<!-- layouts/default.vue -->
<template>
  <div class="layout">
    <header>
      <h1>网站标题</h1>
    </header>
    <main>
      <nuxt />
    </main>
  </div>
</template>

<script>
export default {
  created() {
    console.log('布局创建')
  },
  mounted() {
    console.log('布局挂载')
  },
  beforeUnmount() {
    console.log('布局卸载前')
  }
}
</script>

实用案例分析

案例一:管理后台布局

场景:创建一个管理后台,包含顶部导航栏和侧边栏,不同页面共享相同的布局结构。

解决方案

  1. 创建管理后台布局
<!-- layouts/admin.vue -->
<template>
  <div class="admin-layout">
    <header class="admin-header">
      <div class="logo">
        <h1>管理后台</h1>
      </div>
      <div class="user-info">
        <span>{{ user.name }}</span>
        <button @click="logout">退出登录</button>
      </div>
    </header>
    <div class="admin-content">
      <aside class="admin-sidebar">
        <nav>
          <ul>
            <li>
              <nuxt-link to="/admin/dashboard">仪表盘</nuxt-link>
            </li>
            <li>
              <nuxt-link to="/admin/users">用户管理</nuxt-link>
            </li>
            <li>
              <nuxt-link to="/admin/products">产品管理</nuxt-link>
            </li>
            <li>
              <nuxt-link to="/admin/settings">系统设置</nuxt-link>
            </li>
          </ul>
        </nav>
      </aside>
      <main class="admin-main">
        <nuxt />
      </main>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      user: {
        name: '管理员'
      }
    }
  },
  methods: {
    logout() {
      // 退出登录逻辑
      console.log('退出登录')
    }
  }
}
</script>

<style scoped>
.admin-layout {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

.admin-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;
  height: 60px;
  background-color: #333;
  color: #fff;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.admin-content {
  display: flex;
  flex: 1;
  overflow: hidden;
}

.admin-sidebar {
  width: 200px;
  background-color: #f5f5f5;
  border-right: 1px solid #e0e0e0;
  overflow-y: auto;
}

.admin-sidebar nav ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

.admin-sidebar nav ul li {
  border-bottom: 1px solid #e0e0e0;
}

.admin-sidebar nav ul li a {
  display: block;
  padding: 15px 20px;
  color: #333;
  text-decoration: none;
  transition: background-color 0.3s;
}

.admin-sidebar nav ul li a:hover {
  background-color: #e0e0e0;
}

.admin-main {
  flex: 1;
  padding: 20px;
  overflow-y: auto;
  background-color: #fff;
}
</style>
  1. 使用管理后台布局
<!-- pages/admin/dashboard.vue -->
<template>
  <div>
    <h2>仪表盘</h2>
    <div class="stats">
      <div class="stat-card">
        <h3>总用户数</h3>
        <p>1,234</p>
      </div>
      <div class="stat-card">
        <h3>今日订单</h3>
        <p>56</p>
      </div>
      <div class="stat-card">
        <h3>总收入</h3>
        <p>¥12,345</p>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  layout: 'admin',
  asyncData() {
    // 获取仪表盘数据
    return {
      // 数据
    }
  }
}
</script>

<style scoped>
.stats {
  display: flex;
  gap: 20px;
  margin-top: 20px;
}

.stat-card {
  flex: 1;
  padding: 20px;
  background-color: #f5f5f5;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.stat-card h3 {
  margin-top: 0;
  color: #666;
}

.stat-card p {
  font-size: 24px;
  font-weight: bold;
  color: #333;
}
</style>

案例二:响应式布局

场景:创建一个响应式布局,在不同屏幕尺寸下显示不同的布局结构。

解决方案

<!-- layouts/responsive.vue -->
<template>
  <div class="responsive-layout">
    <header class="site-header">
      <div class="logo">
        <h1>响应式网站</h1>
      </div>
      <button 
        class="menu-toggle" 
        @click="toggleMenu"
        v-if="isMobile"
      >
        {{ menuOpen ? '关闭菜单' : '打开菜单' }}
      </button>
      <nav class="main-nav" v-if="!isMobile || menuOpen">
        <ul>
          <li><nuxt-link to="/">首页</nuxt-link></li>
          <li><nuxt-link to="/about">关于我们</nuxt-link></li>
          <li><nuxt-link to="/services">服务</nuxt-link></li>
          <li><nuxt-link to="/contact">联系我们</nuxt-link></li>
        </ul>
      </nav>
    </header>
    <main class="site-content">
      <nuxt />
    </main>
    <footer class="site-footer">
      <p>© 2023 响应式网站</p>
    </footer>
  </div>
</template>

<script>
export default {
  data() {
    return {
      menuOpen: false,
      isMobile: false
    }
  },
  mounted() {
    this.checkScreenSize()
    window.addEventListener('resize', this.checkScreenSize)
  },
  beforeUnmount() {
    window.removeEventListener('resize', this.checkScreenSize)
  },
  methods: {
    checkScreenSize() {
      this.isMobile = window.innerWidth < 768
      if (!this.isMobile) {
        this.menuOpen = false
      }
    },
    toggleMenu() {
      this.menuOpen = !this.menuOpen
    }
  }
}
</script>

<style scoped>
.responsive-layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.site-header {
  background-color: #333;
  color: #fff;
  padding: 0 20px;
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.logo h1 {
  margin: 0;
  font-size: 20px;
}

.menu-toggle {
  background-color: #444;
  color: #fff;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}

.main-nav ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  gap: 20px;
}

.main-nav ul li a {
  color: #fff;
  text-decoration: none;
}

.site-content {
  flex: 1;
  padding: 20px;
}

.site-footer {
  background-color: #333;
  color: #fff;
  padding: 20px;
  text-align: center;
}

/* 移动端样式 */
@media (max-width: 767px) {
  .main-nav {
    position: absolute;
    top: 60px;
    left: 0;
    right: 0;
    background-color: #333;
    padding: 20px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  }
  
  .main-nav ul {
    flex-direction: column;
    gap: 10px;
  }
}
</style>

总结

本章节详细介绍了Nuxt.js的布局系统,包括:

  1. 布局系统的基本概念:布局的作用和优势
  2. 布局文件的创建和使用:如何创建布局文件以及在页面中使用布局
  3. 嵌套布局:如何实现布局的嵌套,创建更复杂的页面结构
  4. 动态布局:如何根据不同条件动态切换布局
  5. 布局组件通信:如何在布局组件和页面组件之间传递数据
  6. 布局的生命周期钩子:布局组件支持的生命周期钩子

通过合理使用布局系统,可以为应用程序创建一致的页面结构,提高代码的可维护性和重用性。在实际项目中,应根据具体需求设计合适的布局结构,并结合响应式设计原则,确保在不同设备上都能提供良好的用户体验。

练习

  1. 创建一个Nuxt.js项目,添加默认布局和至少一个自定义布局
  2. 实现嵌套布局,创建一个包含页眉、侧边栏和页脚的复杂布局结构
  3. 实现动态布局,根据用户角色显示不同的布局
  4. 创建一个响应式布局,在不同屏幕尺寸下显示不同的布局结构
  5. 测试布局组件和页面组件之间的通信

拓展阅读

« 上一篇 Nuxt.js静态资源管理 下一篇 » Nuxt.js插件机制