quasar@v2 ssr改造遇到的问题

1.在ssr改造第一个页面加上preFetch之后发现页面没有数据,代码如下

Index.vue

复制代码
import { useGoodsStore } from 'stores/goods';

computed: {
  ...mapWritableState(useGoodsStore, {
  listData: 'items',
  isListEnd: 'isListEnd',
  max: 'maxPage',
  }),
preFetch({ store, currentRoute, previousRoute, redirect, ssrContext, urlPath, publicPath }) {
    console.log('Index prefetch');
    // fetch data, validate route and optionally redirect to some other route...

    // ssrContext is available only server-side in SSR mode

    // No access to "this" here

    // Return a Promise if you are running an async job
    // Example:
    const myStore = useGoodsStore(store);
    myStore.getItemList(currentRoute.path, currentRoute.query.q);

},
复制代码

stores/googs.js

复制代码
import { defineStore } from 'pinia';
import { axios } from 'boot/axios';

export const useGoodsStore = defineStore('goods', {
  state: () => ({
    items: [],
    maxPage: 0,
    isListEnd: false,
  }),

  getters: {
    doubleCount(state) {
      return state.counter * 2;
    },
  },

  actions: {
    getItemList(path, q, sortIndex) {
      // console.log('$$$$$$' + this.query);
      // this.$q.loading.show({
      //   delay: 400, // ms
      // });
      return axios
        .post(`${global.config.domain}/goods/list`, {
          page: 1,
          path: path,
          query: q,
          sort: sortIndex,
        })
        .then((res) => {
          // console.log(res.data.data);
          console.log('getItemList  return');

          this.items = res.data.data.records;
          // console.log(this.items);
          if (res.data.data.records.length < 20) {
            this.isListEnd = true;
          }
          this.maxPage = Math.ceil(res.data.data.total / res.data.data.size);
          console.log('this.maxPage ' + this.maxPage);
          // this.$q.loading.hide();
        });
    },
  },
});
复制代码

经过调试,问题出在 myStore.getItemList是个异步方法,preFetch执行完了,但数据还没有开始取。 then this data needs to be pre-fetched and resolved before we start the rendering process. 经实测preFetch这个hook不管时server端还是cclient端都会被call到。有异步数据正确做法是在preFetch返回一个Promise。

复制代码
  preFetch({ store, currentRoute, previousRoute, redirect, ssrContext, urlPath, publicPath }) {
    console.log('Index prefetch');
    // fetch data, validate route and optionally redirect to some other route...

    // ssrContext is available only server-side in SSR mode

    // No access to "this" here

    // Return a Promise if you are running an async job
    // Example:
    const myStore = useGoodsStore(store);
    return myStore.getItemList(currentRoute.path, currentRoute.query.q);
  },
注意在actions里
getItemList 也要return axios.post

复制代码

 

2. 改成ssr后Quasar的Screen插件失效,$q.screen.width永远=0,Screen.gt.sm 这些也失效。根据开发人员的回复是因为ssr时无法知道window的size,所以设为0,否则会发生hydration errors,留下空白页。

When rendering on the server-side, the width of the client's window is unknown (no way to know it; no HTTP header for this, nothing). So we must assume it's the lowest possible (otherwise hydration errors!), then on the client side, when it takes over, it can correct it...

Unfortunately, such an assumption cannot be accurate. And there's tons of screen sizes for mobiles. Then you also have tablets. And you can use custom breakpoints. And on desktop, the browser window width can be anything. If the assumption goes wrong, there's gonna be hydrating errors and the page left blank for those users, rendering the website useless.

This is has no technical solution. On the client, the screen plugin needs to match the SSR-server rendered content in order to successfully hydrate. When rendering on the server side there is no possibility to acquire the client's screen dimensions, so a default hardcoded value is being used. When rendering on client side (and coming from SSR) we thus intentionally delay (with as less time as possible, usually on the next tick) the screen dimensions acquisition so that only after Vue has been correctly hydrated can we then set the actual dimensions. 
 
https://github.com/quasarframework/quasar/issues/10922
https://github.com/quasarframework/quasar/issues/5079
 
解决方法:
@Evertvdw I would suggest putting your $q.screen test in a computed and return the proper class. Then use that in your html. There could be an issue with reactivity and inline code like the way you have it.

 don't use the Screen Plugin and use a CSS based solution (media queries)

还发现一个问题,$q.screen.gt.sm的反应失效

复制代码
       <div v-if="$q.screen.gt.sm" class="col-4">
          <router-view name="hot"></router-view>
        </div>

  created() {
    console.log('MainLayout created');
    // const $q = useQuasar();
    console.log('v-if ' + this.$q.screen.gt.sm);
    // this.fabYoutube = fabYoutube;
  },

 mounted() {
    console.log('v-if-' + this.$q.screen.gt.sm);
}
复制代码
MainLayout created
MainLayout.vue?713b:239 v-if false

MainLayout mounted
MainLayout.vue?713b:245 v-if-false

无论是created()还是mounted(),$q.screen.gt.sm都是false,但 v-if="$q.screen.gt.sm" 却是true, 这里不清楚是$q的插件反应式出了啥问题,在github上项目有类似的问题

https://github.com/quasarframework/quasar/issues/9711

这个问题问的是 this.$q.screen.gt.xs的值正确,但class却没有更新。这个问题还是open状态。

复制代码
<template>
        <q-input outlined bg-color="red" label="test" :class="$q.screen.gt.xs ? '' : 'fit'"/>
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  created() {
    console.log(this.$q.screen.gt.xs);
  },
  mounted() {
    console.log(this.$q.screen.gt.xs);
  },
})
</script>
复制代码

 经测试,改成下面这样就可以

 <div v-if="isBigScreen" class="col-4">
   <router-view name="hot"></router-view>
 </div>

 data() {
    isBigScreen: Screen.gt.sm ? true : false,
}

 

 3.关于prefetch hook何时才会调用,经测试,下面的router

复制代码
    path: '/',
    component: () => import('layouts/MainLayout.vue'),
    children: [
      {
        path: '',
        meta: { isGoodsList: true },

        components: {
          default: () => import('pages/Index.vue'),
          // hot: () => import('components/HotList.vue'),
          hot: () => import('components/ResourceSideList.vue'),
        },
        props: {
          default: (route) => ({ sort: route.query.sort }),
          hot: false,
        },
      },
      {
        path: 'list',
        meta: { isGoodsList: true },

        components: {
          default: () => import('pages/Index.vue'),
          // hot: () => import('components/HotList.vue'),
          hot: () => import('components/ResourceSideList.vue'),
        },
        props: {
          default: (route) => ({
            sort: route.query.sort,
            query: route.query.q,
            page: route.query.page,
          }),
          hot: false,
        },
      },
      {
        path: 'list/:page',
        meta: { isGoodsList: true },

        components: {
          default: () => import('pages/Index.vue'),
          // hot: () => import('components/HotList.vue'),
          hot: () => import('components/ResourceSideList.vue'),
        },
        props: {
          default: (route) => ({ page: route.query.q, sort: route.query.sort }),
          hot: false,
        },
      },
 {
        path: ':path',
        meta: { isGoodsList: true },

        components: {
          default: () => import('pages/Index.vue'),
          hot: () => import('components/ResourceSideList.vue'),
        },
        props: {
          default: (route) => ({
            query: route.query.q,
            page: route.query.page,
            sort: route.query.sort,
          }),
          hot: false,
        },
      },
 
复制代码

 从 路径'' route到 路径'list', Index组件在路径''时已生成router到'list' 时prefetch hook不会触发,但route到':path'时却触发了。从':path'route到不同的':path'也会触发,但从':path'route到''或'list'就不会触发了。

Now, let’s see how the hooks are called when the user visits these routes in the order specified below, one after another.

Route being visitedHooks called fromObservations
/ App.vue then LandingPage App.vue hook is called since our app boots up.
/shop/all ShopLayout then ShopAll -
/shop/new ShopNew ShopNew is a child of ShopLayout, and ShopLayout is already rendered, so ShopLayout isn’t called again.
/shop/product/pyjamas ShopProduct -
/shop/product/shoes ShopProduct Quasar notices the same component is already rendered, but the route has been updated and it has route params, so it calls the hook again.
/shop/product/shoes/overview ShopProduct then ShopProductOverview ShopProduct has route params so it is called even though it’s already rendered.
/ LandingPage -

 Quasar notices the same component is already rendered, but the route has been updated and it has route params, so it calls the hook again. 看这句,这个文档只强调了 route params,看来没有route params的路径如果是相同的组件就不会触发prefetch了。

 

posted @   zjhgx  阅读(166)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示