nuxt3官网搭建,适配pc和移动端支持一键切换中英文,使用pinia和持久化插件
、1使用命令行创建项目脚手架
npx nuxi@latest init may-app // my-app你的项目名称
一般情况下都会创建失败,这里有一个项目的模版,可以自行下载使用
Nuxt基础配置模板地址:https://github.com/Seven7v/Nuxt3-vue3-project
2、如何适配pc端和移动端?
- 文件middleware/auth.global.ts // 加上global后缀,默认全局中间件,进入所有路由都会通过这里
- 在中间件文件中判断是什么设备,跳转至对应的路由
export default defineNuxtRouteMiddleware((to, from) => {
if (process.server){ // 在服务器端处理路由
const nuxtApp = useNuxtApp()
} else { // 在客户端处理路由
// 是否是移动端设备
const isMobile = /(Android|webOS|iPhone|iPod|tablet|BlackBerry|Mobile)/i.test(navigator.userAgent)
// 是否是手机端路由
const isRouterMobile = /^\/m\//.test(to.fullPath);
// 移动端并且 不是/m开头路由
if(isMobile && !isRouterMobile){
return navigateTo(`/m${to.fullPath}`)
}
// 不是移动端 是/m开头路由
if( !isMobile && isRouterMobile){
return navigateTo(`${to.fullPath.replace('/m','')}`)
}
}
})
- 适配移动端,使用插件
postcss-px-to-viewport
在nuxt.config.ts文件中设置css如下
css: {
postcss: {
plugins: [
postcsspxtoviewport({
unitToConvert: 'px', // 要转化的单位
viewportWidth: 750, // UI设计稿的宽度
unitPrecision: 6, // 转换后的精度,即小数点位数
propList: ['*'], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
viewportUnit: 'vw', // 指定需要转换成的视窗单位,默认vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: ['el-'], // 指定不转换为视窗单位的类名,例如van-(vantUI组件),
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
exclude: [/node_modules/], // 设置忽略文件,用正则做目录名匹配,最好不要排除node_modules 文件,排除后在项目中会发现字体不能跟随页面放大
landscape: false // 是否处理横屏情况
})
]
}
}
3、如何实现一键切换中英文?
- 安装
@intlify/unplugin-vue-i18n
和vue-i18n
两个依赖,如果安装不上,请使用强制命令 --force
npm i @intlify/unplugin-vue-i18n vue-i18n
- 配置 nuxt.config.ts 文件
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'url'
import VueI18nVitePlugin from '@intlify/unplugin-vue-i18n/vite'
export default defineNuxtConfig({
build: {
transpile: [/vue-i18n/]
},
vite: {
resolve: {
alias: {
'vue-i18n': 'vue-i18n/vue-i18n.runtime.esm-bundler.js'
}
},
plugins: [
VueI18nVitePlugin({
include: [
resolve(dirname(fileURLToPath(import.meta.url)), './locales/*.json')
]
})
]
}
})
- plugins中将i18n在vue中使用,配置好后 在plugins目录下创建i18n.ts文件。 因为需要在plugins中将i18n挂到vue上
// 目录结构
|- plugins
|-- i18n.ts
|- i18n
|-- zh.ts
|-- en.ts
zh.ts en.ts 配置国际化匹配的内容
// zh.ts
export default {
home: '主页',
}
//en.ts
export default {
home: 'HomePage',
}
plugins目录下创建i18n.ts
import { createI18n } from 'vue-i18n'
import en from '../i18n/en'
import zh from '../i18n/zh'
export default defineNuxtPlugin(({ vueApp }) => {
// 这里设置了默认启动时从cookie获取语言配置。至于为什么是cookie ,是因为nuxt 的首屏服务端加载原因
const language = useCookie('lang').value || 'zh'
const i18n = createI18n({
fallbackLocale: 'zh',
locale: language,
messages: {
zh,
en
},
})
vueApp.use(i18n)
})
- 在components文件夹中创建Lang.vue组件
<template>
<div>
<select v-model="$i18n.locale" @change="handleChange">
<option value="zh">中文</option>
<option value="en">English</option>
</select>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
const { locale } = useI18n()
const handleChange = () => {
const cookieLang = useCookie('lang')
cookieLang.value = locale.value
}
</script>
<style lang="less" scoped>
select {
outline: none
}
</style>
- 在页面中直接使用 :
$t('home')
<template>
<div> {{ $t('home') }}</div>
</template>
4. 如何使用pinia和持久化插件
- 按装pinia和pinia-plugin-persistedstate
npm install pinia @pinia/nuxt
npm i pinia-plugin-persistedstate
- nuxt工程代码中注册插件(/plugis/pinia.ts文件中)
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.$pinia.use(piniaPluginPersistedstate)
})
- 配置nuxt.config.ts中的modules
modules: [ '@pinia/nuxt' ],
- 定义一个store(/store/counter.ts文件中)
import { defineStore } from 'pinia'
interface CounterState {
times: number
name: string
}
export const useCounter = defineStore('counter', {
state: (): CounterState => ({
times: 5,
name: 'jjww'
}),
actions: {
increment() {
this.times++
},
clearName(){
this.name = ''
}
},
persist: process.client && {
storage: localStorage,
paths: ['times','name']
}
})
// 注意:persist定义要做判断,因为localStorage是客户端参数,所以需要加process.client
- 在页面只中使用pinia的store
<script setup lang="ts">
import { useCounter } from '~/store/counter'
const counter = useCounter()
</script>
<template>
<div style="padding-top: 200px;padding-left: 50px;">
<div style="margin-bottom: 30px;">
<pre>{{ counter.$state }}</pre>
<p>
times: {{ counter.times }}
<br>
name: {{ counter.name }}
</p>
<input v-model="counter.name" type="text">
<br>
<input v-model="counter.times" type="number">
</div>
<button @click="counter.increment()">
+1 number click
</button>
<br>
<button @click="counter.clearName()">clearName</button>
</div>
</template>
5. nuxt中间件的使用方法
- 单页面的路由中间件的写法
<!-- 对单页面的路由守卫 -->
<script setup lang="ts">
definePageMeta({
middleware: (to, from,next) => {
if (!localStorage.getItem('token')) {
return navigateTo('/login') //一定要写return
}
}
})
</script>
- 命名中间件:如果多个页面需要配置守卫,可以将单页的内容 抽出
// 1 新建文件
|- middleware
|-- auth.ts // 中间件名称自定义
// 2 auth.ts中写入
export default defineNuxtRouteMiddleware((to, from) => {
if (!localStorage.getItem('token')) {
return navigateTo('/login') //一定要写return
}
})
// 3 页面如果需要用到中间件的地方只需要配置,就可以实现导航守卫功能
<script setup lang="ts">
definePageMeta({
middleware: 'auth'
})
</script>
- 全局中间件(注意命名方式,要加上global)
// 1 新建文件
|- middleware
|-- auth.global.ts // 加上global后缀,默认全局中间件,进入所有路由都会通过这里
// 2 auth.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
const limitList = ['/article', '/home', '/product'] // 配置适配 路由
if (limitList.includes(to.fullPath)) {
if (!localStorage.getItem('token')) { // !!!!这里有个大坑
return navigateTo('/login') //一定要写return
}
}
})
这样写如果从不做验证的页面进如,后跳转到
/home
时完全没有问题的,但是如果,路由地址中直接写**/home**回车,页面会加载不出来,提示500报错,没有localStorage。此时/home时首页,首页渲染时服务端返回,所以没有localStorage,可以将token
放到cookie
中解决 也可以使用proess.server
来判断,此代码是否是在服务端
运行的,如果时true,可以做对应处理。打印后,在启动项目的终端会打印出true 将token储存在cookie中,使用useCookie
useCookie可以实现如下操作
if (process.server) {
// 从服务端的cookie中获取token
} else {
// js 使用从浏览器的cookie中获取token
}
从而可以写成,这样从/home
直接进入的话就不会报错了
export default defineNuxtRouteMiddleware((to, from) => {
const limitList = ['/article', '/home', '/product']
if (limitList.includes(to.fullPath)) {
const token = useCookie('token')
if (!token.value) {
return navigateTo('/login') //一定要写return
}
}
})
页面重定向
现在进入页面 直接加载会显示404,这时可以进行重定向
|- middleware
|-- redirect.global.ts // 中间件名称自定义
export default defineNuxtRouteMiddleware((to, from) => {
if (to.fullPath === '/') {
return navigateTo('/home')
}
})
6. Nuxt3 反向代理配置
由于Nuxt的首屏服务端渲染,以及只有浏览器才受同源限制 的问题,在面对跨域请求时,需要对客户端和服务端都进行代理
最优处理方法 ,在nuxt.config.ts
中进行如下配置
export default defineNuxtConfig({
devtools: { enabled: true },
// 反向代理
nitro: {
// 用于客户端代理
devProxy: {
'/api': {
target: 'https://xxx.com/api', // 这里是接口地址
changeOrigin: true,
prependPath: true
}
},
// 该配置用于服务端请求转发
routeRules: {
'/api/**': {
proxy: 'https://xxx.com/api/**'
}
}
}
})
不配置routeRules 的话
请求数据时就可以写成,如果不配置routeRules,会使页面首屏加载时,请求错误。
const { data } = await useLazyFetch(
'/api/xxxxx/xxx/xx',
{
// baseURL: process.server ? 'https://i.maoyan.com/api' : '', 如果不配置routeRules,兼容服务端与客户端请求可以这样写
}
)
7. 项目打包部署
- 使用
npm run build
命名进行打包,会在项目的根目录下产生一个.output
文件夹 - 把
.output
文件夹复制到D盘下,切换至D盘d:
(自己的任意盘下) - 使用命名
node .output/server/index.mjs
即可启动项目 - 在浏览器中输入
http://localhost:3000/
打开项目
注意:关掉命令窗口,服务就停止了,可以使用pm2
按键盘快捷键"win + R",输入“cmd”,再按快捷键“ctrl+shift+enter”,出来的cmd窗口就已经是管理员身份了。
安装npm i pm2 -g
用管理员身份安装
在刚才的D盘下的output文件夹下新建 ecosystem.config.js
文件
// ecosystem.config.js
// 运行生产环境
module.exports = {
apps: [
{
name: '晋百慧',
script: './server/index.mjs',
env:{
NODE_ENV:'production',
PORT:'8080'
}
}
]
}
在打开的命令行窗口中切换到D盘(d:),切换至.output文件夹(cd .output)
运行项目命令: `pm2 start`
停止项目命令: `pm2 stop`
清除项目命令: `pm2 deleta`
也可以运行某个项目,使用id: `pm2 start 1`