😲VueJS 页面动态布局!问题与解决方案!

2025-05-28

😲VueJS 页面动态布局!问题与解决方案!

我目前正在为一位客户开发一个大型的渐进式 Web 应用 (PWA)。前端我们使用 VueJS、Vue Router、VueX 以及其他一些 VueJS 包。

我们最初设计了两种布局。一种是模态布局,其中包含登录或注册表单。因此,该布局中的所有内容都位于页面的垂直和水平中心。第二种布局是典型应用的布局。该布局包含导航菜单、通知、搜索等组件。

我们还使用 VueX 和 Axios 从后端获取数据。我们无需从上到下或从下到上传递 props。如果需要,我们可以使用 store 来建模后端数据和方法。

现在您已经对所使用的技术有了基本的了解,我将讨论 VueJS 中常见的动态布局解决方案的一些问题。

简介

为了代码示例,我创建了 3 个vue cli项目。

它们都在main.js文件中添加了以下代码片段。



Vue.mixin({
  created() {
    console.log('[created] ' + this.$options.name)
  },
});


Enter fullscreen mode Exit fullscreen mode

每次创建组件时,都会返回conols.log()组件名称。这可以方便地查看 VueJS 组件的创建过程。您还可以添加mounted()钩子detroyed()。对于我们的实验来说,created()这已经足够了。

问题 1:路线改变时重新渲染

当你在线搜索动态布局时,你会发现很多解决方案,其中最常见的一种是下面这个。

在您的代码中App.vue,您有以下代码:



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


Enter fullscreen mode Exit fullscreen mode

然后,你告诉每个页面/视图应该采用什么布局。它通常看起来像下面的About.vue组件。



<template>
  <LayoutB>
    <div class="about">
      <h1>This is an about page</h1>
    </div>
  </LayoutB>
</template>

<script>
import LayoutB from "../layouts/LayoutB";
export default {
  name: "About",
  components: {
    LayoutB
  }
};
</script>


Enter fullscreen mode Exit fullscreen mode

这将起作用,并且您不会看到快速机器的任何问题,因为我们在该页面上没有做太多事情。

那么问题是什么呢?为此,我们现在来看看我们巧妙的Vue.Mixin()辅助函数。

看起来应该console.log是这样的:

替代文本

我们可以看到,如果我们加载页面,我们会看到以下创建顺序。

'App (entry point)' -> 'Home (view/page)' -> 'LayoutA (layout)' -> 'Components'

如果我们看看我们现在是如何设置组件的,那么这是正确的。在布局之前加载页面可能会导致问题,但这对性能的影响并不大。

更大的问题如下:
替代文本

我们正在销毁整个布局并重新创建它。这会导致 UI/UX 运行缓慢,并且违背了将所有这些组件分离的初衷。即使我们没有必要这样做,如果我们销毁并重新创建它们,也会导致 UI/UX 运行缓慢。

如果您有一个通知系统,每次更改页面时都会创建监听器,情况会变得更糟。

尽管这个解决方案有点作用,但并不是很令人满意。

问题 2:双重渲染

这可能是我在多个教程和 StackOverflow 答案中找到的最受欢迎的解决方案。

我们将App.vue代码更改为:



<template>
  <div id="app">
    <component :is="layout">
      <router-view :layout.sync="layout" />
    </component>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      layout: "div"
    };
  }
};
</script>


Enter fullscreen mode Exit fullscreen mode

以及About.vue下面的代码



<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>

<script>
import LayoutB from "../layouts/LayoutB";
export default {
  name: "About",
  created() {
    this.$emit("update:layout", LayoutB);
  }
};
</script>


Enter fullscreen mode Exit fullscreen mode

这里最重要的变化是sync功能$emit。我们现在将布局移到了App.vue组件中,视图/页面组件会告诉它App.vue要加载哪个布局。

再次查看浏览器,您就会发现此解决方案有效!现在让我们看看console.log()输出。

替代文本

App (entry point) -> 'Home (view/page)' -> 'LayoutA (layout)' -> 'Components' -> 'Home (view/page) again😱 -> Click on Contact link ->'Contact (view/page)

我们解决了一个问题。现在布局不会在每次路线更改时被破坏并重新创建,但我们也产生了一个新问题!

每次渲染新的布局时,其中的页面/视图都会被创建、销毁,然后再次创建。这可能会导致我们的商店出现问题。

当你的组件中有一个fetch()加载列表的函数时,这个fetch()函数会 ping 服务器两次,而不是一次。现在想象一下,你的后端没有缓存,一个繁重的计算运行了两次!

此外,如果您的商店没有检查您是否获取了重复的数据,您将在该列表中看到两次所有内容。

再次,在我们的示例中,Home在之前呈现LayoutA

这只是您看到的该解决方案可能出现的一个问题。

该方法也不能很好地解决我们的问题。

解决方案:meta在我们的路线中使用对象

我们需要再次改变App.vue



<template>
  <div id="app">
    <component :is="this.$route.meta.layout || 'div'">
      <router-view />
    </component>
  </div>
</template>

<script>
export default {
  name: "App",
};
</script>


Enter fullscreen mode Exit fullscreen mode

我们About.vue现在看起来像这样



<template>
  <div class="about">
    <h1>This is an about page</h1>
  </div>
</template>

<script>
export default {
  name: "About"
};
</script>


Enter fullscreen mode Exit fullscreen mode

因此,页面不知道它渲染在哪个布局中。
但是这些信息现在存储在哪里呢?
在我们的router/index.js文件中!



import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import Contact from '../views/Contact.vue'
import LayoutA from '../layouts/LayoutA.vue'
import LayoutB from '../layouts/LayoutB.vue'
Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
    meta: { layout: LayoutA }
  },
  {
    path: '/about',
    name: 'About',
    component: About,
    meta: { layout: LayoutB }
  },
  {
    path: '/contact',
    name: 'contact',
    component: Contact,
    meta: { layout: LayoutA }
  }
]

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

export default router


Enter fullscreen mode Exit fullscreen mode

这里最重要的一行位于meta: { layout: LayoutA }每个路线定义中。

让我们再看一下我们的console.log()输出。

替代文本

App (entry point) -> LayoutA (layout) -> Components from the Layout -> Home (view/page)

现在看起来不错。我们终于得到了正确的顺序,并且没有重复渲染。
此外,即使布局不需要更改,我们也可以在不破坏和创建布局的情况下更改路线。

实施此解决方案后,我们感觉应用程序更加流畅,使用体验也更好了。即使用肉眼也感觉不到。单是流畅度就已经是一大优势。

另外,不要用不必要的请求来轰炸我们的服务器!我们可以降低 API 端点的一些限制。

这个小修复对于从最终用户到利益相关者到实际开发人员的每个人来说都是一个胜利。

包含代码的 Git Repo

我创建了一个 repo,您​​可以在其中找到两个有问题的项目以及我们采用的解决方案

关联

**如果您喜欢这个内容,请点击心形或独角兽!

如果您想稍后阅读,请单击独角兽下的书签按钮!**

👋打个招呼! Instagram | Twitter | LinkedIn | Medium | Twitch | YouTube

文章来源:https://dev.to/lampewebdev/vuejs-pages-with-dynamic-layouts-problems-and-a-solution-4460
PREV
人类可读的 JavaScript
NEXT
如何更快地编写代码 - VS Code 版