Мастер Vue — Рефакторинг «Макет»

Vue.js
Мастер Vue — Рефакторинг «Макет»

лицом к макету

Начну с вопроса:

«Как вы думаете, что-то не так с настройками макетов в вашем текущем проекте Vue?»

images (1).jpg

Вы можете ответить:

«Нет проблем. Разве это не просто компонент Layout на внешнем слое?»

Я думаю, что это должно быть что-то вроде этого:

<template>
  <MyLayout>
    <h1>Here is my page content</h1>
  </MyLayout>
</template>

<script>
import MyLayout from '@/layouts/MyLayout.vue'
export default {
  name: 'MyPage',
  components: { MyLayout }
}
</script>

Или вот так, например, проект Bengua, Настройки макета:

  • Если вы использовали vue-element-admin, вы должны быть знакомы с такими настройками маршрутизации (бизнес-компоненты являются подкомпонентами компонентов макета).
const AdminLayout = () => import('@/views/admin/homepage/layout.vue')
const OrgList = () => import('@/views/admin/admOrg/orgList.vue')
const OrgDetail = () => import('@/views/admin/admOrg/orgDetail.vue')

export const admOrgRouters = {
  path: '/orgManage',
  component: AdminLayout,
  redirect: '/orgList',
  name: '组织管理',
  iconClass: 'orgManage',
  children: [
    orgList,
    orgDetail,
  ],
  meta: {
    roles: ['isAdmin', 'ordinaryAdmin'],
    title: '组织管理'
  }
}

Приятно! Если ваш проект похож на этот, вы можете продолжить чтение следующей статьи~ (иначе просто поставить лайк 👍 и закрыть ❎ выход 😄)

Найдите болевые точки

Вроде бы нет ничего плохого в установке Layout во внешнем пакете, но давайте подумаем:

  1. Нам нужно импортировать макет на разные страницы, а «повторное введение» всегда является одной из самых раздражающих вещей для программистов;
  2. Мы должны сделать так, чтобы макет обертывал наш контент, что несколько ограничено;
  3. Это утяжеляет наш код и вынуждает компонент брать на себя ответственность за отрисовку макета (компонент и макет недостаточно разделены);

Хотя это не очень большие моменты, но из-заNuxtJS, поэтому мы решили провестиbreaking change, чтобы изменить эту ситуацию.

Это nuxtjs именно то, что вдохновило его? Короче, а именно:

Вы можете определить [макет] как [свойство] компонента, вместо того, чтобы устанавливать родительский компонент макета для вашего приложения.

Прикрепил:nuxtjs-layouts

Давайте посмотрим, как это вдохновение может быть реализовано в нашем проекте Vue?

Подготовка проекта

Мы по-прежнему используем Vue CLI для быстрой сборки наших проектов:

vue create vue-layouts

Вы можете выбрать Vue2+ или Vue3+, в этой статье будет описано соответственно.

Мы очищаем некоторые ненужные файлы, принесенные инициализацией, такие какHelloWorld.vue, а затем создайте новый файл, чтобы получить этот каталог:

--src
----views
------About.vue
------Contacts.vue
------Home.vue
----App.vue
----main.js
----router.js

Тогда в App.vue код такой:

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link> |
      <router-link to="/contacts">Contacts</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
#nav {
  padding: 30px;
}
#nav a {
  font-weight: bold;
  color: #2c3e50;
}
#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

Код Home.vue выглядит следующим образом:

<template>
  <div>
    <h1>This is a home page</h1>
  </div>
</template>

<script>
export default {
  name: 'Home'
}
</script>

Код About.vue выглядит следующим образом:

<template>
  <div>
    <h1>This is an about page</h1>
  </div>
</template>

<script>
export default {
  name: 'About'
}
</script>

Код Contacts.vue выглядит следующим образом:

<template>
  <div>
    <h1>This is a contacts page</h1>
  </div>
</template>

<script>
export default {
  name: "Contacts"
}
</script>

Код router.js выглядит следующим образом:

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue')
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('@/views/About.vue')
  },
  {
    path: '/contacts',
    name: 'Contacts',
    component: () => import('@/views/Contacts.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

Затем вызовите маршрутизатор в main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  router: router
}).$mount('#app')

Что ж, подготовка завершена. Мы можем получить такой интерфейс:

layouts1.png

Это три простых перехода маршрутизации и коммутация компонентов. Здесь не будет проблем~

Построить макет

Дело пикантное!

мы создаем новыйlayouts/AppLayout.vueкомпоненты.

  • Vue2
<template>
  <component :is="layout">
    <slot />
  </component>
</template>

<script>
const defaultLayout = 'AppLayoutDefault'
export default {
  name: "AppLayout",
  computed: {
    layout() {
      const layout = this.$route.meta.layout || defaultLayout
      return () => import(`@/layouts/${layout}.vue`)
    }
  }
}
</script>

Этот фрагмент кода может показаться простым, но он является частью нашей новой системы компоновки.основной!

В этом шаблоне мы добавилидинамические компонентыи добавьте к нему вычисляемое свойство с именем layout.

При расчете свойства мы видим, что он вернет [] [в соответствии с компонентами макета маршрутизации, соответствующими [Динамическому] и загрузит сборку], чтобы перейти, в противном случае включите макет по умолчанию — defaultLayout.

  • Код в Vue3 выглядит следующим образом:
<template>
  <component :is="layout">
    <slot />
  </component>
</template>

<script>
import AppLayoutDefault from './AppLayoutDefault'
export default {
  name: "AppLayout",
  data: () => ({
    layout: AppLayoutDefault
  }),
  watch: {
    $route: {
      immediate: true,
      async handler(route) {
        try {
          const component = await import(`@/layouts/${route.meta.layout}.vue`)
          this.layout = component?.default || AppLayoutDefault
        } catch (e) {
          this.layout = AppLayoutDefault
        }
      }
    }
  }
}
</script>

Реализация Vue 3 Composition API:

<template>
  <component :is="layout">
    <slot />
  </component>
</template>

<script>
import AppLayoutDefault from './AppLayoutDefault'
import { markRaw, watch } from 'vue'
import { useRoute } from 'vue-router'
export default {
  name: 'AppLayout',
  setup() {
    const layout = markRaw(AppLayoutDefault)
    const route = useRoute()
    watch(
      () => route.meta,
      async meta => {
        try {
          const component = await import(`@/layouts/${meta.layout}.vue`)
          layout.value = component?.default || AppLayoutDefault
        } catch (e) {
          layout.value = AppLayoutDefault
        }
      },
      { immediate: true }
    )
    return { layout }
  }
}
</script>

Несколько макетов

Опираясь на суть предыдущего раздела, давайте посмотрим, как мы можем улучшить нашу систему компоновки~

Помните три основных компонента, которые мы подготовили при инициализации:Home,About,Contacts, к которому мы готовимсяСоздайте три макета. (Конечно, вы можете настроить столько макетов, сколько захотите.)

Перед этим делаем небольшой рефакторинг App.vue:

Создайте новый файл:layouts/AppLayoutLinks.vue, извлеките сюда код App.vue, оставив чистый файл App.vue.

// Новый AppLayoutLinks.vue

<template>
  <div id="nav">
    <router-link to="/">Home</router-link> |
    <router-link to="/about">About</router-link> |
    <router-link to="/contacts">Contacts</router-link>
  </div>
</template>

<script>
export default {
name: "AppLayoutLinks"
}
</script>

<style scoped>
#nav {
  padding: 30px;
}
#nav a {
  font-weight: bold;
  color: #2c3e50;
}
#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

// очищаем App.vue

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}
</style>

Затем создайте три файла макета~

  • Первый метод макета: AppLayoutHome.vue
<template>
  <div>
    <header class="header" />
    <AppLayoutLinks />
    <slot />
  </div>
</template>

<script>
import AppLayoutLinks from '@/layouts/AppLayoutLinks'
export default {
  name: "AppLayoutHome",
  components: {
    AppLayoutLinks
  }
}
</script>

<style scoped>
.header {
  height: 5rem;
  background-color: green;
}
</style>
  • Метод макета 2: AppLayoutAbout.vue
<template>
  <div>
    <header class="header" />
    <AppLayoutLinks />
    <slot />
  </div>
</template>

<script>
import AppLayoutLinks from '@/layouts/AppLayoutLinks'
export default {
  name: "AppLayoutAbout",
  components: {AppLayoutLinks}
}
</script>

<style scoped>
.header {
  height: 5rem;
  background-color: blue;
}
</style>
  • Третий метод макета: AppLayoutContacts
<template>
  <div>
    <header class="header" />
    <AppLayoutLinks />
    <slot />
  </div>
</template>

<script>
import AppLayoutLinks from '@/layouts/AppLayoutLinks'
export default {
  name: "AppLayoutContacts",
  components: {
    AppLayoutLinks
  }
}
</script>

<style scoped>
.header {
  height: 5rem;
  background-color: red;
}
</style>

Здесь демонстрационная раскладка отличается простыми цветовыми различиями, в основном «знающими». Макет по умолчанию не меняет цвет и не будет вдаваться в подробности.

Настроить маршрутизацию

если внимательно посмотретьПостроить макетВы, должно быть, видели суть этогоthis.$route.meta.layoutэтот звонок. Значит нам нужно вернуть маршрут в set, код такой:

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue'),
    meta: {
      layout: 'AppLayoutHome'
    }
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('@/views/About.vue'),
    meta: {
      layout: 'AppLayoutAbout'
    }
  },
  {
    path: '/contacts',
    name: 'Contacts',
    component: () => import('@/views/Contacts.vue'),
    meta: {
      layout: 'AppLayoutContacts'
    }
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

Завершить работу

Все вышеперечисленное настроено, и, наконец, нам нужно связать AppLayout.vue с App.vue:

  <div id="app">
    <AppLayout>
      <router-view />
    </AppLayout>
  </div>

Готово!

Вы можете загрузить проект локально и запустить его:гитхаб-адрес

layouts2.png

Таким образом, мы добилисьновая система компоновки. Вдохновленный коллекцией волос Sassoon, о нет, вдохновленный NuxtJS~ вы чувствуете это?

Подводя итог: наша предыдущая компоновка была обернута компонентами или маршрутизацией, что часто требовало нескольких ссылок. При наличии нескольких способов раскладки будет сложнее, а отдельной системы раскладки для четкого деления нет. Эта статья проходит«Динамические компоненты» + «Свойства прослушивания» + «Конфигурация маршрута» + «Глобальное монтирование»Способ извлечь систему макета, избежать множественных представлений и избежать нечеткой структуры каталогов. Было бы разумно построить этот макет в начале проекта сборки! Если это старый проект, есть различные макеты, вы можете отрефакторить его, когда у вас будет время, и почувствовать это. почему бы нет?

Писать не просто✍, ставьте лайки и поощряйте 👍, обратите внимание на мой паблик [Nuggets Anthony] 😎, искренний выход...