【2024年5月】nuxt3 项目模板,让你开发官网得心应手(续)

3,722 阅读3分钟

前言

上个月写了一篇 【2024年4月】nuxt3 项目模板,让你开发官网得心应手,本人看到后,觉得略显单调,再加点辅料吧。

1. plugins 处理第三方脚本引入

nuxt 项目经常需要像 CSR 项目一样,在 html 引入 第三方脚本,比如如下这样代码。但是 nuxt 没有 html,没地方编写,这应当如何?

<!-- Google Tag Manager -->
<script>
  ;(function (w, d, s, l, i) {
    w[l] = w[l] || []
    w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' })
    var f = d.getElementsByTagName(s)[0],
      j = d.createElement(s),
      dl = l != 'dataLayer' ? '&l=' + l : ''
    j.async = true
    j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl
    f.parentNode.insertBefore(j, f)
  })(window, document, 'script', 'dataLayer', 'GTM-123456')
</script>
<!-- End Google Tag Manager -->
<!-- 这段代码要求加到H5页面的所有html页面的head标签中,并且尽可能放在顶部位置 -->

<!-- Google Tag Manager (noscript) -->
<noscript>
  <iframe
    src="https://www.googletagmanager.com/ns.html?id=GTM-123456"
    height="0"
    width="0"
    style="display: none; visibility: hidden"
  ></iframe>
</noscript>
<!-- End Google Tag Manager (noscript) -->
<!-- 这段代码要求加到H5页面的所有html页面的body中 -->

谷歌 对接人员说了,要把上面的代码放到 html 的对应位置,我们要怎么办呢?

可以使用 plugins,直接在 src/plugins 文件夹里面编写 useGtm.client.ts, 其中 client 表示只在 客户端(web浏览器) 生效。

// filename: src/plugins/useGtm.client.ts
export default defineNuxtPlugin(nuxtApp => {
    nuxtApp.hook('app:created', () => {
      // 将原始的<script>代码内容放入这里
      const scriptContent = `
      ;(function (w, d, s, l, i) {
        w[l] = w[l] || []
        w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' })
        var f = d.getElementsByTagName(s)[0],
          j = d.createElement(s),
          dl = l != 'dataLayer' ? '&l=' + l : ''
        j.async = true
        j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl
        f.parentNode.insertBefore(j, f)
      })(window, document, 'script', 'dataLayer', 'GTM-123456')
        `
  
      // 创建并注入<script>标签
      const scriptTag = document.createElement('script')
      scriptTag.textContent = scriptContent
      scriptTag.async = true
      document.head.appendChild(scriptTag)

      // 创建并注入<iframe>标签
      const iframeTag = document.createElement('iframe')
      iframeTag.src = 'https://www.googletagmanager.com/ns.html?id=GTM-123456'
      iframeTag.height = '0'
      iframeTag.width = '0'
      iframeTag.style.display = 'none'
      iframeTag.style.visibility = 'hidden'
      document.body.appendChild(iframeTag)
    })
  })

2. middleware 处理 404

nuxt 项目,当路由不匹配时会去默认的 404,我们老大觉得那个 404 页面不好看,不匹配时直接去首页,这个如何处理。

我在 App.vue layout 等文件处理一直不行,后来在 翻文档 + 百度comate + 阿里通义千问 等的协助下,经过多次测验才找到正解。

现在揭晓答案,直接在 src/middleware 文件夹编写 redirect-404-to-home.global.ts 即可,注意需要 .global,会直接自动加载该中间件。

// filename: src/middleware/redirect-404-to-home.global.ts
export default defineNuxtRouteMiddleware((to, form) => {
  if (to.matched.length === 0) {
    // 404自动重定向到首页
    return navigateTo('/')
  }
})

3. 请求第三方接口

server/api/demo.ts 代码如下

import { BASE_URL } from '../utils/constant'

import axios from 'axios'
// 设置基础URL,例如 'https://api.example.com'
axios.defaults.baseURL = BASE_URL

export default defineEventHandler(async event => {
  const body = await readBody(event)
  console.log('前端传过来的body->')
  console.log(body)

  const {
    username,
    email,
    phone = '',
    message='',
  } = body

  const bodyObj = {
   username,
   email,
   phone,
   message,
   version: 'v2'
  }

  const params = {
    key: value,
    key2: value2,
  }

  console.log(new Date().toLocaleString())

  console.log('/n 1) 整理后发送给第三方的请求路径->')
  console.log(BASE_URL + '/api/xxx')

  console.log('/n 2) 整理后发送给第三方的params->')
  console.log(params)

  console.log('/n 3) 整理后发送给第三方的body->')
  console.log(bodyObj)


  try {
    const { data } = await axios.post('/api/xxx', bodyObj, { params })
    console.log('/n接口返回的数据:', data)
    return data
  } catch (error) {
    console.error('Error fetching data:', error)
    event.node.res.statusCode = 500
    event.node.res.end(error)
  }
})

前端页面部分,直接如下编写即可:

async function onSubmit(event: FormSubmitEvent<Schema>) {
  // Do something with event.data
  console.log(event.data)
  const { username, email, phone, message } = event.data
  state.sending = true

  const { data } = await useFetch('/api/demo', {
    method: 'post',
    body: {
      username,
      email,
      phone,
      message,
    },
  })
  console.log(data.value)
  state.sending = false
}

4. nuxt.config.ts 配置

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
  modules: [
    '@nuxt/ui',
    '@vueuse/nuxt',
    '@nuxt/image',
  ],
  colorMode: {
    preference: 'light',
  },
  image: {
    format: ['webp', 'avif', 'jpg'],
    provider: 'ipx',
    domains: ['https://www.sanyuk.com'], // 允许加载的图片域名
    loader: {
      type: 'http', // 默认的图片加载器
      options: {
        cache: true, // 是否开启缓存
        format: 'webp', // 图片格式,默认为自动选择
        quality: 75, // 图片压缩质量,默认为75
      },
    },
    strategies: {
      local: {
        transform: true, // 是否开启图片转换
        inline: 'base64', // 小于多少字节的图片转为内联base64,默认为20KB
      },
    },
  },
  css: ['animate.css', '~/assets/css/main.css'],
  devServer: {
    port: 3333,
  },
  app: {
    head: {
      title: 'SANY UK',
      charset: 'utf-8',
      viewport: 'width=device-width, initial-scale=1',
      meta: [
        {
          name: 'description',
          content: 'SANY UK',
        },
        {
          name: 'keywords',
          content: 'SANY, SANY UK, SANY GLOBAL',
        },
      ],
    },
  },
})

5. Carausel + NuxtImg 配置

NuxtImg 在上一步已经配置,可以使用,NuxtImg 有很多特性,什么优先级,什么图片 placeholder等,如下图。

image.png

文档传送门在此 image.nuxt.com/

carousel 本不支持循环播放,需要编写如下代码:

onMounted(() => {
  setInterval(() => {
    if (!carouselRef.value) return

    if (carouselRef.value.page === carouselRef.value.pages) {
      return carouselRef.value.select(0)
    }

    carouselRef.value.next()
  }, 8000)
})

完整代码如下:

<template>
  <section>
    <UCarousel
      ref="carouselRef"
      v-slot="{ item }"
      :items="items"
      :ui="{
        item: 'basis-full',
        indicators: {
          active: 'bg-white',
        },
      }"
      class="overflow-hidden"
      arrows
      indicators
    >
      <NuxtImg
        :key="item"
        :src="item"
        width="100%"
        height="auto"
        layout="responsive"
        alt="Carousel Image"
        :priority="true"
      />
    </UCarousel>
  </section>
</template>

<script setup lang="ts">
const items = ['/index/1.jpg', '/index/2.jpg', '/index/3.jpg']

const carouselRef = ref()
onMounted(() => {
  setInterval(() => {
    if (!carouselRef.value) return

    if (carouselRef.value.page === carouselRef.value.pages) {
      return carouselRef.value.select(0)
    }

    carouselRef.value.next()
  }, 8000)
})
</script>

<style lang="scss" scoped></style>

总结

没啥总结,我感觉 nuxt3 又行了,哈哈~