Code前端首页关于Code前端联系我们

项目初始化阶段,怎么选模板和配置更省心?

terry 5小时前 阅读数 5 #Vue

做前端项目时,要是用 Vue2 搭配 Nuxt 开发,既能享受 Vue 的开发体验,又能靠 SSR 解决 SEO 和首屏加载问题,但实际开发里「踩坑」的地方可不少,从项目初始化的配置混乱,到 SEO 优化没达到预期,再到部署时服务器报错,每个环节都可能卡壳,这篇文章就用问答形式,把 Vue2 + Nuxt 开发里最容易栽跟头的环节拆解清楚,教你怎么高效把项目落地~

很多新手刚接触 Nuxt 时,第一步「初始化项目」就容易懵,先解答最基础的 —— 怎么快速搭骨架?
Nuxt 提供了官方脚手架,执行 npx create-nuxt-app 项目名 后,终端会引导你选模板、UI 框架、测试工具这些,要是想快速启动,选「Starter Template」就够;如果需要更完整的目录(比如自带 Vuex 模块、布局模板),选「Full Template」更省心,但别盲目选复杂模板,小项目用 Starter 能减少冗余代码。

选完模板,核心配置文件 nuxt.config.js 得重点盯,举个常见坑:全局样式重复加载,有人把 Element UI、自定义全局 CSS 全丢进 css 数组,结果组件里又重复引入,导致样式冲突,正确做法是:全局通用的样式(reset.css、主题色)放 css 里;组件自己的样式用 <style scoped> 隔离。head 配置动态标题和 meta 时,别直接写死,后期 SEO 优化要动态改,所以先预留好通过异步方法修改的口子。

还有 plugins 配置第三方库(axios、echarts),如果直接 import 后丢进 plugins,服务端渲染时可能报错(因为某些库依赖浏览器环境),这时候要加判断:if (process.client) { import('xxx') },保证只在客户端加载,举个例子,给 axios 加拦截器,得把初始化逻辑包在客户端判断里,不然服务端执行时找不到 window,直接崩掉。

SEO 优化是 Nuxt 强项,实操时要注意什么?

Nuxt 最核心的优势之一是 SSR(服务端渲染),天生对 SEO 友好,但实操细节没做好,搜索引擎还是抓不到内容,先解决「动态设置标题和 meta」的问题 ——
很多页面需要根据接口数据改标题(比如文章详情页,标题是文章名),这时候用 asyncData 或者 fetch 方法,在服务端获取数据后,通过 this.$nuxt.$head 动态设置,举个例子:

async asyncData({ $axios, params }) {
  const article = await $axios.get(`/api/article/${params.id}`)
  return { article }
},
head() {
  return { this.article.title,
    meta: [
      { name: 'description', content: this.article.desc }
    ]
  }
}

但要注意,如果接口请求慢,服务端渲染时数据还没返回,标题会是空的,所以得优化接口性能,或者给关键数据加缓存。

SEO 不止标题和描述,还要加「结构化数据(JSON-LD)」,比如电商网站的商品页,把商品名称、价格、评分等信息用 JSON-LD 格式埋进页面,搜索引擎能更精准解析,做法是在 head 里加 script 标签:

head() {
  return {
    script: [
      {
        type: 'application/ld+json',
        innerHTML: JSON.stringify({
          '@context': 'https://schema.org/',
          '@type': 'Product',
          name: this.article.title,
          description: this.article.desc
        })
      }
    ]
  }
}

还要避开一个雷:路由层级过深,Nuxt 自动生成的路由是根据 pages 目录结构来的,pages/article/_id/comments 这样的多层路由,搜索引擎抓取时容易认为页面权重低,尽量把核心页面放在浅层目录,或者通过路由重定向简化结构。

路由与页面加载逻辑,怎么处理才流畅?

Nuxt 的「自动路由」很方便,但动态路由、页面过渡、懒加载这些细节处理不好,用户体验会打折扣,先看动态路由(比如文章详情页 pages/article/_id.vue):
获取参数得用 this.$route.params.id,但如果在 asyncData 里获取,参数是从上下文对象里拿,asyncData({ params }) { ... },这里容易混淆客户端和服务端的参数获取方式,服务端渲染时,asyncData 的上下文有 params,客户端导航时,this.$route.params 也能拿到,所以两种方式都要兼容测试。

然后是页面过渡动画,Nuxt 支持在 layout 或页面组件里配置 transition,比如想做页面切换时的渐变效果,在 default.vue(全局布局)里加:

export default {
  transition: 'page-fade'
}

然后在 assets/css 里写动画样式:

.page-fade-enter-active, .page-fade-leave-active {
  transition: opacity .3s;
}
.page-fade-enter, .page-fade-leave-to {
  opacity: 0;
}

但要注意,过渡动画别太复杂,不然会拖慢页面切换速度。

还有组件懒加载,减少首屏加载体积,Nuxt 里用 component: () => import('@/components/xxx.vue') 就行,但要注意,懒加载的组件如果在服务端渲染时依赖浏览器 API(比如操作 DOM),得用 <client-only> 包裹,不然服务端会报错,比如引入一个依赖 window 的图表组件:

<client-only>
  <ChartComponent />
</client-only>

路由守卫(middleware),比如需要权限验证的页面,在 middleware 目录写一个 auth.js

export default function ({ store, redirect }) {
  if (!store.state.user.isLogin) {
    return redirect('/login')
  }
}

然后在需要验证的页面组件里配置 middleware: 'auth',这里要注意,middleware 会在服务端和客户端都执行,所以判断用户状态时,要确保服务端能拿到正确的会话信息(比如把 token 存在 cookie 里,服务端能解析)。

状态管理(Vuex)在 Nuxt 里有啥特殊玩法?

Nuxt 对 Vuex 做了封装,新手容易搞混模块导入和状态同步逻辑,首先是模块自动导入:只要在 store 目录下新建 .js 文件(user.js),Nuxt 会自动识别为 Vuex 模块,不用手动在 index.js 里 import,但如果模块里有命名空间,得在文件里显式声明 namespaced: true,不然调用 mutations 时容易冲突。

然后是服务端与客户端状态同步,比如用户登录信息,在服务端渲染时获取,要用到 nuxtServerInit 方法(只能在 store/index.js 里定义):

export const actions = {
  nuxtServerInit({ commit }, { req }) {
    // 从 cookie 里解析用户信息
    const token = req.headers.cookie.match(/token=([^;]+)/)?.[1]
    if (token) {
      commit('user/setToken', token)
    }
  }
}

这里要注意,nuxtServerInit 只在服务端执行,所以能安全地从请求头里拿 cookie,但如果是客户端操作(比如用户点击登录),得用普通的 action/mutation。

还有个坑:状态污染,服务端渲染时,所有用户共享同一个 Vue 实例,所以如果把用户特定数据存在全局状态里,没及时清空,就会串数据,比如用户 A 的信息被用户 B 看到,解决方法是,在页面组件的 asyncDatafetch 里,把用户数据存在组件自己的 data 里,而不是全局 Vuex;或者在每次请求后,重置全局状态里的用户特定数据。

性能优化从哪些细节入手?

Nuxt 项目性能差,多半是 SSR 耗时高、客户端 hydratation 慢、资源体积大这几个原因,先看SSR 性能优化
服务端渲染时,每个请求都要执行组件的 asyncDatafetch,如果接口响应慢,整个渲染时间就会拉长,可以给高频接口加缓存(比如用 Redis 缓存接口返回),或者把一些非关键数据放到客户端请求,避免在服务端做复杂计算(比如大量循环、DOM 操作模拟),这些操作既耗性能,又没意义(因为服务端没有真实 DOM)。

然后是客户端 hydratation 优化,Hydratation 是服务端渲染后,客户端重新渲染并绑定事件的过程,如果页面 DOM 结构复杂,hydratation 时间会很长,可以通过减少不必要的组件嵌套、合理使用 v-ifv-show 来简化 DOM,用 keep-alive 缓存页面组件,避免重复渲染:

<nuxt keep-alive />

但要注意,缓存的页面如果有动态数据,要及时更新,不然会显示旧数据。

再讲静态资源处理,Nuxt 默认用 webpack 打包,所以要优化打包配置:

  • 图片资源用 vue-loader 处理,开启 url-loader 的 limit,小图片转 base64,减少请求;
  • 第三方库(lodash)用按需导入,import { debounce } from 'lodash' 代替全量引入;
  • 开启 CDN 加速,把 node_modules 里的大库(Vue、Element UI)通过 externals 配置,从 CDN 加载,减少打包体积。

打包体积优化,用 webpack-bundle-analyzer 分析包体积,把大的第三方库拆分成单独的 chunk,配置 splitChunks

// nuxt.config.js
build: {
  analyze: true, // 打包后自动打开分析报告
  splitChunks: {
    chunks: 'all',
    minSize: 30000,
    maxSize: 0,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        priority: 10,
        chunks: 'all'
      }
    }
  }
}

这样能把 node_modules 里的库单独打包,缓存更友好。

部署上线和多端兼容,容易踩哪些雷?

部署 Nuxt 项目分两种场景:服务端渲染(SSR)部署静态站点生成(SSG,nuxt generate),先讲 SSR 部署:
Nuxt 作为 SSR 项目,需要 Node 服务运行,很多人直接把打包后的 .nuxt 目录丢到服务器,结果启动报错,因为没安装依赖,正确步骤是:服务器装 Node.js,拉取代码后执行 npm install,然后用 nuxt start 启动,或者用 PM2 管理进程(pm2 start npm --name "项目名" -- run start),还要配置 Nginx 反向代理,把 80 端口转发到 Nuxt 启动的 3000 端口(默认),同时处理静态资源(把 dist 目录下的文件作为静态资源目录)。

如果项目是博客、文档这类静态内容多的,用 nuxt generate 生成静态 HTML 更高效,但要注意,动态路由(_id.vue)需要配置 routes 数组,告诉 Nuxt 要生成哪些页面,在 nuxt.config.js 里加:

export default {
  generate: {
    routes() {
      return axios.get('/api/articles').then(res => {
        return res.data.map(article => `/article/${article.id}`)
      })
    }
  }
}

不然动态页面不会被生成,访问时会 404。

然后是移动端适配,Nuxt 里用 REM 布局的话,要在 plugins 里加适配逻辑,比如根据设备宽度设置根字体大小:

// plugins/rem.js
if (process.client) {
  function setRem() {
    const docEl = document.documentElement
    const width = docEl.getBoundingClientRect().width
    docEl.style.fontSize = (width / 750) * 100 + 'px'
  }
  window.addEventListener('resize', setRem)
  setRem()
}

然后在 nuxt.config.js 里把这个插件引入,注意要在客户端执行。

IE 兼容,Vue2 对 IE 的支持需要 Babel 转译和 polyfill,在 nuxt.config.js 里配置 build.babel

build: {
  babel: {
    presets({ isServer }) {
      return [
        [
          '@nuxt/babel-preset-app',
          {
            targets: isServer ? { node: 'current' } : { ie: 11 }
          }
        ]
      ]
    }
  },
  polyfill: true // 自动引入必要的 polyfill
}

但要注意,有些新 API(fetch)的 polyfill 得手动加,要测试 IE 下的兼容性,避免白屏。

Vue2 + Nuxt 的组合在 2024 年依然有很多适用场景,尤其是对 SEO 要求高、首屏性能敏感的项目,从初始化时选对模板、配置好核心文件,到 SEO 优化时动态处理元信息,再到路由、状态管理、性能优化和部署的细节把控,每一步都得「避坑」又「增效」,把这些环节吃透,项目开发时才能又快又稳,既满足业务需求,又给用户好体验~要是你在开发中遇到其他具体问题,评论区随时交流~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门