nuxt / vue-meta

Manage HTML metadata in Vue.js components with SSR support
https://vue-meta.nuxtjs.org
Other
4.08k stars 247 forks source link

nuxt generate --spa duplicating some items #438

Closed amritk closed 5 years ago

amritk commented 5 years ago

I'm using nuxt generate --spa and I've been trying to figure out if I was doing something wrong or if this was a bug. Some of the meta tags and scripts are being duplicated with data-n-head="1" and data-n-head="2" I have posted a screenshot. Now only the "1's" are appearing when I view the source, the "2's" seem to be added via javascript somewhere? Developer tools: 2019-08-22:20:48:42 Source: 2019-08-22:20:49:37

pimlie commented 5 years ago

The 1 and 2 are indicators to support multiple Vue-apps on a page, its very basic as in the first Vue-app thats created is 1, the second 2 etc. If you see this happening then it means multiple Vue-apps are running on the page and you should make sure that the Nuxt Vue-app is the one thats runs first

See the original issue & pr about supporting multiple vue-apps for more info: #372 and #373

Please keep me updated why this happens on your page, am very interested to understand when/why this happens!

amritk commented 5 years ago

Hey @pimlie could you point me in the right direction on how to begin figuring out where this second app is created? Do you think it could be something to do with Vuetify? I'm pretty new to nuxt and have no idea where a second app would be triggering, or how to even trigger the first app to be honest. Here's my nuxt.config.ts if that helps, its got a bunch of extra stuff I'm testing out right now to get this SPA working on Cordova.

import Sass from 'sass'
import dotenv from 'dotenv'
import clientKeys from '@hockeycommunity/shared/src/libs/client-keys'
import { NuxtConfigurationModule } from '@nuxt/types/config/module'
import { LinkPropertyBase } from 'vue-meta/types/vue-meta'
import vuetifyLoader from './src/plugins/vuetify-loader'

dotenv.config()

// Generate apple favicons
const appleSizes = [ 57, 60, 72, 76, 114, 120, 144, 152, 180 ]
const appleFavicons = appleSizes.map(size => ({
    rel: 'apple-touch-icon',
    href: `/favicons/apple-icon-${size}x${size}.png`,
    sizes: `${size}x${size}`
}))

const modules: NuxtConfigurationModule[] = [
    '@nuxtjs/axios',
    '@nuxtjs/dotenv',
    '@nuxtjs/sentry',
    'nuxt-babel',
    [ 'cookie-universal-nuxt', { alias: 'nuxtCookies' } ]
]
let link: LinkPropertyBase[]

if (process.env.CORDOVA) {
    // Cordova
    link = [
        {
            rel: 'stylesheet',
            href: '/css/material-icons.css'
        },
        {
            rel: 'stylesheet',
            href: '/css/roboto.css'
        }
    ]
}
// Web only
else {
    // Adsense
    modules.push(
        [ '@nuxtjs/google-adsense', {
            id: clientKeys.googleAdSense,
            analyticsUacct: clientKeys.googleAnalytics,
            analyticsDomainName: 'hockeycommunity.com'
        }]
    )
    // Favicons
    link = [
        {
            rel: 'stylesheet',
            href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons&display=auto'
        },
        ...appleFavicons,
        {
            rel: 'icon', type: 'image/png', sizes: '192x192', href: '/favicons/android-icon-192x192.png'
        },
        {
            rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicons/favicon-16x16.png'
        },
        {
            rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicons/favicon-32x32.png'
        },
        {
            rel: 'icon', type: 'image/png', sizes: '96x96', href: '/favicons/favicon-96x96.png'
        },
        {
            rel: 'icon', type: 'image/x-icon', href: '/favicon.ico'
        },
        { rel: 'manifest', href: '/favicons/manifest.json' }
    ]
}

const config = {
    mode: process.env.CORDOVA ? 'spa' : 'universal',
    debug: !(process.env.NODE_ENV === 'production'),
    env: {
        api: process.env.api,
        NODE_ENV: process.env.NODE_ENV,
        os: process.env.CORDOVA
    },

    // Custom Routes
    router: {
        extendRoutes (routes) {
            // Messages
            routes.push({
                name: 'messages-conversation',
                path: '/messages/:username',
                component: '@/pages/messages.vue'
            })

            // Explore
            routes.push({
                name: 'explore-map',
                path: '/explore/map',
                props: {
                    view: 'map'
                },
                component: '@/pages/explore/map-list.vue'
            })
            routes.push({
                name: 'explore-list',
                path: '/explore/list',
                props: {
                    view: 'list'
                },
                component: '@/pages/explore/map-list.vue'
            })
            routes.push({
                name: 'explore-place',
                path: '/explore/:category/:permalink',
                props: true,
                component: '@/pages/explore/place-category-permalink.vue'
            })

            // Team Finder
            routes.push({
                name: 'team-finder-request',
                path: '/team-finder/:kind/:uuid',
                props: true,
                component: '@/pages/team-finder/request-kind-uuid.vue'
            })
            routes.push({
                name: 'team-finder',
                path: '/team-finder/:kind',
                props: true,
                component: '@/pages/team-finder/feeds-index.vue'
            })
        },
        middleware: [ 'redirect' ],
        mode: 'hash'
    },

    // Head and meta tags
    head: {
        title: 'Hockey Community',
        meta: [
            { charset: 'utf-8' },
            { name: 'viewport', content: 'width=device-width, initial-scale=1, minimal-ui' },
            { name: 'apple-mobile-web-app-capable', content: 'yes' },
            { name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
            { name: 'msapplication-TileColor', content: '#ffffff' },
            { name: 'msapplication-TileImage', content: '/favicons/ms-icon-144x144.png' },
            { name: 'theme-color', content: '#ffffff' },
            {
                hid: 'description',
                name: 'description',
                content: 'Join thousands of players worldwide who share the same passion of making hockey happen.' +
                    'Find shinny, teams, goalies, players, hockey gear, leagues and tournaments all ' +
                    'though Hockey Community'
            },
            {
                hid: 'og:type', property: 'og:type', content: 'website'
            },
            {
                hid: 'og:image',
                property: 'og:image',
                content: 'https://s3-us-west-2.amazonaws.com/static.hc/images/logos/circle.png'
            },
            { hid: 'og:image:height', content: '150' },
            { hid: 'og:image:width', content: '150' },
            {
                hid: 'fb:app_id',
                content: '114995941981612'
            }
        ],
        // Set above
        link,

        // Scripts
        script: [
            {
                src: 'https://maps.googleapis.com/maps/api/js?key=x-xx&libraries=places',
                type: 'text/javascript'
            }
        ]
    },

    // Loading bar color
    loading: {
        color: '#fff'
    },

    // Global css
    css: [
        '@/assets/styles/index.sass',
        '@hockeycommunity/shared/src/assets/fonts/icomoon/icomoon.css',
        'iv-viewer/dist/iv-viewer.css'
    ],

    // Change src directory
    srcDir: 'src/',

    // Plugins to load
    plugins: [
        { src: '@/plugins/filters/index' },
        { src: '@/plugins/vuetify' },
        { src: '@/plugins/i18n' },
        { src: '@/plugins/google-analytics' },
        { src: '@/plugins/index' },
        { src: '@/plugins/image-upload', mode: 'client' },
        { src: '@/plugins/infinite-loading', mode: 'client' }
    ],

    // Nuxt.js modules
    modules,

    // Build modules
    buildModules: ['@nuxt/typescript-build'],

    // Sentry error reporting
    sentry: {
        dsn: 'https://x@sentry.io/x',
        disabled: !(process.env.NODE_ENV === 'production')
    },

    // Where to generate cordova
    generate: {
        dir: 'cordova'
    },

    // Max age for static assets
    render: {
        static: {
            maxAge: 1000 * 60 * 60 * 24 * 7
        }
    },

    // Typescript
    typescript: {
        typeCheck: true
    },

    // Server Middleware
    // serverMiddleware: [
    //     './server-middleware/redirect.ts'
    // ],

    // Build Config
    build: {
        // Different build path for Cordova
        publicPath: process.env.CORDOVA ? '/front-end' : '/_nuxt/',

        // Built asset filenames
        filenames: {
            app: ({ isDev }) => isDev ? '[name].js' : '[name]-[hash].js',
            chunk: ({ isDev }) => isDev ? '[name].js' : '[name]-[hash].js'
        },

        // Extend webpack config
        extend: (config, ctx) => {
            config.devtool = ctx.isClient ? 'eval-source-map' : 'inline-source-map'

            if (ctx.isDev && ctx.isClient && config && config.module) {
                config.module.rules.push({
                    enforce: 'pre',
                    test: /\.(ts|vue)$/,
                    loader: 'eslint-loader',
                    exclude: /(node_modules)/
                })
            }
        },

        loaders: {
            sass: {
                implementation: Sass,
                data: '@import "@/assets/styles/_helpers.sass"'
            }
        },

        // Vuetify Loader - To auto load your components
        transpile: [ /^vuetify/, /^@hockeycommunity\/shared/ ],
        plugins: [
            vuetifyLoader
        ],

        // Extract css into files instead of in head
        extractCSS: {
            allChunks: !!process.env.CORDOVA
        }
    }
}

console.log(config.head.script)

export default config
pimlie commented 5 years ago

Thanks for the info. It seems you are not using the official Vuetify module for Nuxt, was there a reason for that? That probably causes Vuetify to load before Nuxt does hence the app id issue. Vuetify 2.0 was the main reason why support for multiple apps was added, so that's most likely the cause. Do you have any overlays in your app on load?

@aldarund @kevinmarrec Pinging you just as an fyi, seems using the nuxt module really is a must when using Vuetify with Nuxt

kevinmarrec commented 5 years ago

There is an issue around Vuetify code that makes SPA projects using vue-meta broken around theming. There's a fix pending here : https://github.com/vuetifyjs/vuetify/pull/8566

See the related Nuxt Vuetify module issues for more info.

amritk commented 5 years ago

@pimlie I didn't use the nuxt module because I could not figure out how to use it with custom vueitfy loader options. Is there a way? I was using it to load my components as well, make's life much easier:

import fs from 'fs'
import recursiveReaddir from 'recursive-readdir'
import VuetifyLoaderPlugin from 'vuetify-loader/lib/plugin'

// Grab all of our local components for auto loading
const localComponents: {
    [key: string]: string
} = {}
recursiveReaddir('src/components', (err, items) => {
    if (err) {
        console.error('There was an error reading your component directory', err)
    }
    else {
        for (const item of items) {
            localComponents[item.replace(/^(.*[\\/])/, '').slice(0, -4)] = item.replace('src', '')
        }
    }
})

// Same for shared
const sharedComponents: string[] = []
fs.readdir('node_modules/@hockeycommunity/shared/src/components', (err, items) => {
    if (err) {
        console.error('There was an error reading shared components', err)
    }
    else {
        for (const item of items) {
            sharedComponents.push(item.slice(0, -4))
        }
    }
})

export default new VuetifyLoaderPlugin({
    match: (_originalTag: string, { kebabTag, camelTag }: { kebabTag: string; camelTag: string }) => {
        const comp = localComponents[kebabTag]
        if (comp) {
            return [camelTag, `import ${camelTag} from '@${comp}'`]
        }
        if (sharedComponents.includes(kebabTag)) {
            return [camelTag, `import ${camelTag} from '@hockeycommunity/shared/src/components/${kebabTag}.vue'`]
        }
    }
})
pimlie commented 5 years ago

@amritk Thanks for the info but that question is more suited to be asked on the vuetify-module repo ;)

kevinmarrec commented 5 years ago

@amritk you're right, you can't for now, it's something that came to my mind yesterday, it's totally worth the feature request on vuetify-module repo. (I already planned to work on it but please open a feature request 😁)

stale[bot] commented 5 years ago

Thanks for your contribution to vue-meta! This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you would like this issue to remain open:

  1. Verify that you can still reproduce the issue in the latest version of vue-meta
  2. Comment the steps to reproduce it Issues that are labeled as pending will not be automatically marked as stale.
amritk commented 5 years ago

I ended up switching to the vuetify module and that seems to have fixed it.