web-dev-qa-db-fra.com

Afficher différents composants Vuejs pour les navigateurs mobiles

Je développe un SPA avec Vue 2.0. Les composants développés jusqu'à présent sont destinés aux navigateurs "de bureau", par exemple, j'ai

Main.vue, ProductList.vue, ProductDetail.vue,

Je souhaite un autre ensemble de composants pour les navigateurs mobiles, tels que MainMobile.vue, ProductListMobile.vue, ProductDetailMobile.vue,

Ma question est la suivante: où et comment faire en sorte que mon SPA rende la version mobile des composants lors de l'affichage dans un navigateur mobile?

Veuillez noter que je souhaite explicitement éviter de rendre mes composants réactifs. Je veux en garder deux versions séparées.

Merci, 

7
sean717

J'ai une idée, utilisez un mixin qui détecte le navigateur mobile ou de bureau (exemple pour le code js dans cette answer ) .. puis utilisez v-if, par exemple 

<production-list v-if="!isMobile()"></production-list>
<production-list-mobile v-else></production-list-mobile>

voici donc un exemple sur https://jsfiddle.net/Ldku0xec/

9
Mohd_PH

J'ai une solution simple pour Vue.js

<div v-if="!isMobile()">
  <desktop>
  </desktop>
</div>
<div v-else>
  <mobile>
  </mobile>
</div>

Et des méthodes:

methods: {
 isMobile() {
   if(/Android|webOS|iPhone||iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
     return true
   } else {
     return false
   }
 }
}
5
Zhanserik

J'ai eu le même problème, je l'ai résolu en utilisant un fichier neutre et non en vue (Init.vue) accessible par mobile et par ordinateur, et ce fichier redirige vers le fichier correct.

Supposons que j'ai le Main.vue et le MainMobile.vue. Je vais ajouter un Init.vue qui redirigera. Donc, mon routeur/index.js est que:

import Router from 'vue-router'
import Vue from 'vue'
import Main from '@/components/Main'
import MainMobile from '@/components/MainMobile'
import Init from '@/components/Init'

Vue.use(Router)

export default new Router({
  routes: [
     {
        path: '/',
        name: 'Root',
        component: Init
     },
    {
      path: '/Main',
      name: 'Main',
      component: Main
    },
    {
      path: '/MainMobile',
      name: 'MainMobile',
      component: MainMobile
    },
  ]
})

Dans le fichier Init.vue, la détection de mobile/desktop aura lieu:

<template>
</template>
<script>
    export default {
        name: 'Init',
        methods: {
            isMobile() {
                if( screen.width <= 760 ) {
                    return true;
                }
                else {
                    return false;
                }
            }
        },
        created() {
            if (this.isMobile()) {
                this.$router.Push('/MainMobile');
            }
            else {
                this.$router.Push('/Main');
            }
        }
    }
</script>
<style scoped>
</style>

La fonction isMobile () utilisée est très simple, vous pouvez en changer une autre.

2
Derzu

Un peu tard pour cela, mais, au cas où l'un de vous cherche, je gère la situation comme ceci:

const router = new Router({
     routes: [{
      path: '/main-view
      name: 'mainView',
      component: MainView,
      meta: {
        'hasMobileView': true
      }
     },
    {
     path: '/mobile-view',
      name: 'mobileView',
      component: mobileView,
      meta: {
        'hasDesktopView': true
      }
     },
    }]
})

then on beforeeach function 

router.beforeEach((to, from, next) => {
  const hasMobileView = to.matched.some((route) => route.meta.hasMobileView)
  if (hasMobileView) {
    if (navigator.userAgent.match(/Android/i) ||
      navigator.userAgent.match(/webOS/i) ||
      navigator.userAgent.match(/iPhone/i) ||
      navigator.userAgent.match(/iPad/i) ||
      navigator.userAgent.match(/iPod/i) ||
      navigator.userAgent.match(/BlackBerry/i) ||
      navigator.userAgent.match(/Windows Phone/i)) {
      next('/mobile-view')
    } else {
      next()
    }
  }
})`

1
vishalbasnet23

Je cherchais une solution pour cela et je suis venu ici mais je ne trouvais pas ce dont j'avais besoin:

  1. Les importations asynchrones ne chargent dans le bundle que ce qui était nécessaire en fonction de la fenêtre d'affichage.
  2. Possibilité de servir une mise en page différente si la mise en page a été redimensionnée

J'ai mélangé et assorti quelques choses que j'ai lues en ligne, y compris les réponses ici, alors j'ai pensé que je reviendrais et que je mettrais tous mes apprentissages dans une fonction unique pour ceux qui cherchent:

/**
 * Breakpoint configuration to be in line with element-ui's standards
 * @type {{LABELS: string[], VALUES: number[]}}
 */
const BREAKPOINTS = {
    LABELS: ['xs', 'sm', 'md', 'lg', 'xl'],
    VALUES: [0, 768, 992, 1200, 1920, Infinity]
};


/**
 * @typedef ViewFactory
 * @type function
 * A function which returns a promise which resolves to a view. Used to dynamically fetch a view file on the fly during
 * run time on a need basis
 */


/**
 * A helper to get a responsive route factory which renders different views based on the current view point
 * @param {{xs:[ViewFactory],sm:[ViewFactory],md:[ViewFactory],lg:[ViewFactory]}} map - A map of breakpoint key to a ViewFactory
 * @returns {ViewFactory} - A view factory which invokes and returns an item supplied in the map based on the current viewport size
 */
export default function responsiveRoute(map) {
    return function getResponsiveView() {
        const screenWidth = document.documentElement.clientWidth;

        // Find the matching index for the current screen width
        const matchIndex = BREAKPOINTS.VALUES.findIndex((item, idx) => {
            if (idx === 0) {
                return false;
            }
            return screenWidth >= BREAKPOINTS.VALUES[idx - 1] && screenWidth < BREAKPOINTS.VALUES[idx];
        }) - 1;


        if (map[BREAKPOINTS.LABELS[matchIndex]]) {
            // Perfect match, use it
            return map[BREAKPOINTS.LABELS[matchIndex]]();
        } else {
            // Go down the responsive break points list until a match is found
            let counter = matchIndex;
            while (counter-- > 0) {
                if (map[BREAKPOINTS.LABELS[counter]]) {
                    return map[BREAKPOINTS.LABELS[counter]]();
                }
            }
            return Promise.reject({
                code: 500,
                info: 'No component matched the breakpoint - probably a configuration error'
            });
        }
    };
} 

Usage:

const router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes:[{
      path: '/login',
      name: 'login',
      component: responsiveRoute({
          // route level code-splitting
          // this generates a separate chunk (login-xs.[hash].js) for this route
          // which is lazy-loaded when the route is visited.
          xs: () => import(/* webpackChunkName: "login-xs" */ './views/Login/Login-xs.vue'),
          // sm key is missing, it falls back to xs
          md: () => import(/* webpackChunkName: "login-md" */ './views/Login/Login-md.vue')
          // lg, xl keys are missing falls back to md
      }) 
  }]
}); 

Comment ça marche:

Vue Router prend en charge la définition de la clé component en tant que fonction qui renvoie une promesse de prise en charge des itinéraires asynchrones. Le moyen le plus courant consiste à utiliser la fonction webpack import() qui renvoie une promesse. La fonction qui retourne la promesse n'est invoquée que lorsque la route est sur le point d'être rendue, ce qui permet de charger paresseusement nos composants.

La fonction responsiveRoute accepte une mappe de ces fonctions avec des clés définies pour différents points d'arrêt et renvoie une fonction qui, lorsqu'elle est appelée, vérifie la taille de la fenêtre d'affichage disponible et renvoie appelle la fabrique de promesses correcte et renvoie la promesse renvoyée par celle-ci.

Remarques:

J'aime cette méthode car elle n'exige pas que l'architecture de l'application ou les configurations de route soient d'une certaine manière. Il est assez plug-and-play en utilisant les capacités Vue Router fournies prêtes à l'emploi. Cela ne vous oblige pas non plus à définir une vue pour chaque combinaison point de rupture/itinéraire. Vous pouvez définir un itinéraire comme d'habitude sans cela (chargé paresseux ou non) aux côtés d'autres itinéraires qui l'utilisent sans aucun problème.

Cette méthode n'utilise pas l'agent de détection d'utilisateur, mais utilise plutôt la largeur disponible du document.documentElement. D'autres méthodes que j'ai vues recommandées, telles que window.screen.width, qui donne la taille exacte de l'écran du périphérique indépendamment de la taille de la fenêtre ou un window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth plus robuste. Mélanger et assortir au besoin.

Mes points de rupture sont (nombre et leurs valeurs) sont basés sur les points de rupture élément-ui, tels que je les utilisais pour une conception réactive normale. Ceci peut à nouveau être configuré en fonction des besoins en modifiant les constantes en haut.

1
Chirag Ravindra