项目初始化阶段,怎么选模板和配置更省心?
做前端项目时,要是用 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 看到,解决方法是,在页面组件的 asyncData
或 fetch
里,把用户数据存在组件自己的 data
里,而不是全局 Vuex;或者在每次请求后,重置全局状态里的用户特定数据。
性能优化从哪些细节入手?
Nuxt 项目性能差,多半是 SSR 耗时高、客户端 hydratation 慢、资源体积大这几个原因,先看SSR 性能优化:
服务端渲染时,每个请求都要执行组件的 asyncData
和 fetch
,如果接口响应慢,整个渲染时间就会拉长,可以给高频接口加缓存(比如用 Redis 缓存接口返回),或者把一些非关键数据放到客户端请求,避免在服务端做复杂计算(比如大量循环、DOM 操作模拟),这些操作既耗性能,又没意义(因为服务端没有真实 DOM)。
然后是客户端 hydratation 优化,Hydratation 是服务端渲染后,客户端重新渲染并绑定事件的过程,如果页面 DOM 结构复杂,hydratation 时间会很长,可以通过减少不必要的组件嵌套、合理使用 v-if
和 v-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前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。