nuxt3 ts vue3 vue3服务端渲染,从构建到部署,兼容pc 移动端

nuxt3 ts vue3 vue3服务端渲染,从构建到部署,兼容pc 移动端

首页枪战射击Project T3更新时间:2024-05-07

简介:该教程兼容pc 移动端,兼容原理:同时封装了pc框架element-plus、和移动端框架vant,根据不同客户端类型渲染不同的组件,同一个组件需要根据客户端类型同时封装pc和移动端,如果觉得开发麻烦,可忽略兼容部分教程,根据需要分别构建pc、和移动端

nuxt3:https://www.nuxt.com.cn/docs/guide/directory-structure/pages

vue3:https://cn.vuejs.org/

nuxt.config:https://www.nuxt.com.cn/docs/api/configuration/nuxt-config

elementPlus:https://element-plus.gitee.io/zh-CN/

vant:https://vant-contrib.gitee.io/vant/#/zh-CN

axios:https://www.axios-http.cn/docs/api_intro

一、构建项目

访问https://codeload.github.com/nuxt/starter/tar.gz/refs/heads/v3,下载压缩报解压到项目下,或者运行npx nuxi@latest init vue_common,需把typescript更新为4.3.5版本

yarn install 安装依赖 yarn dev -o 启动

二、路由,新建pages文件夹

  1. 普通路由 ,每个pages文件下的vue都是一个路由

<template> 2131 </template>

<template> <NuxtPage/> </template>

  1. 嵌套路由

<template> demo父级页面 <NuxtPage/> </template> <script lang="ts" setup> definePageMeta({ title: 'My home page' }) </script>

//demo>test>index.vue <template> demo测试页 </template> <script lang="ts" setup> const route = useRoute() console.log(route.meta.title) </script> 浏览器访问:http://localhost:3000/demo/test

//demo>test1>index.vue <template> demo测试页1 </template> 浏览器访问:http://localhost:3000/demo/test1

  1. 动态路由,创建:pages>dynami[group]>[id].vue

<template> 动态路由1:{{ $route.params.group }} - {{ $route.params.id }} </template> 浏览器访问:http://localhost:3000/dynamicGroup/123

三、根目录下创建静态资源文件 assets

  1. 创建assets>font字体文件,可下载阿里字体图标库 ,解压后文件放入font文件下

<i class="iconfont icon-usename"></i>

  1. 创建css文件

yarn add sass

/** * 涉及全局px需要在此,定义样式,并在globals引用,用来兼容pc和移动,不用兼容可以直接写在 globals,也可以全局引入@import "globalsIndex" */ body{ font-size: $font_size }

/** * 涉及px单位的,需要做pc和移动端的兼容;不需要兼容,可去掉此代码; */ .pc{ @import "globalsIndex"; } .mobile{ @import "globalsIndex"; }

$font_size: 14px;//基础字体大小

@import "./variable"; @import "./globals"; @import "./normalize"; @import "../font/iconfont.css";

export default defineNuxtConfig({ ... css: [ "@/assets/css/iframe.scss" ], vite: { css: { preprocessorOptions: { scss: { additionalData: '@import "assets/css/variable.scss";', } } } }, ... })

  1. 创建image文件

export default defineNuxtConfig({ ... alias: { "@img": "~assets/image/", }, ... })

<img src="@img/idCard.png"/>

四、状态管理器 pinia https://pinia.web3doc.top/introduction.html

  1. 安装依赖

yarn add @pinia/nuxt --dev

  1. 修改nuxt.config.ts

export default defineNuxtConfig({ ... modules: ["@pinia/nuxt"], //配置Nuxt3的扩展的库 ... })

  1. 创建第一个store:useMobile

export default defineNuxtConfig({ ... alias: {"@store":'store'}, ... })

import { defineStore } from "pinia"; interface IsMobileinterface { isMobile: boolean; } const useMobile = defineStore("mobile", { state: ():IsMobileInterface => { return { isMobile: false, }; } }); export default useMobile

//app.vue <template> <NuxtPage/> </template> <script lang="ts" setup> import {onMounted} from "vue"; import {storeToRefs} from "pinia"; import useMobile from "@store/useMobile"; const mobileStore = useMobile(); let {isMobile} = storeToRefs(mobileStore); onMounted(()=>{ //判断是哪个客户端(pc,mobile),主要用来兼容样式 if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) { //增加全局class,用于设置全局样式 document.getElementsByTagName('html')[0].className = 'mobile'; isMobile.value = true; }else{ //增加全局class,用于设置全局样式 document.getElementsByTagName('html')[0].className = 'pc'; isMobile.value = false; } }); </script>

yarn add @pinia-plugin-persistedstate/nuxt --dev

export default defineNuxtConfig({ ... modules: [ //配置Nuxt3的扩展的库 "@pinia/nuxt", '@pinia-plugin-persistedstate/nuxt', ], ... })

//localStorage import { defineStore } from "pinia"; interface IsMobileInterface { isMobile: boolean; } const useMobile = defineStore("mobile", { state: ():IsMobileInterface => { return { isMobile: false, }; }, persist: { storage: persistedState.localStorage, } }); export default useMobile //sessionStorage import { defineStore } from "pinia"; interface IsMobileInterface { isMobile: boolean; } const useMobile = defineStore("mobile", { state: ():IsMobileInterface => { return { isMobile: false, }; }, persist: { storage: persistedState.sessionStorage, } }); export default useMobile //cookie import { defineStore } from "pinia"; interface IsMobileInterface { isMobile: boolean; } const useMobile = defineStore("mobile", { state: ():IsMobileInterface => { return { isMobile: false, }; }, persist: { storage: persistedState.cookiesWithOptions({ sameSite: false, }), } }); export default useMobile export default defineNuxtConfig({ ... modules: [ "@pinia/nuxt", "@pinia-plugin-persistedstate/nuxt" ], piniaPersistedstate: { cookieOptions: { sameSite: "strict", }, storage: "localStorage"//默认储存方式 }, ... })

五、集成postcss

  1. 安装依赖

yarn add postcss-px-to-viewport-8-plugin --dev

  1. 修改nuxt.config.ts

export default defineNuxtConfig({ ... postcss: { plugins: { "postcss-px-to-viewport-8-plugin": { viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度 viewportHeight: 912, // 视窗的高度,对应的是我们设计稿的高度 unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数(很多时候无法整除) viewportUnit: 'vw', // 指定需要转换成的视窗单位,建议使用vw propList: ['*'], selectorBlackList: [/^.pc/], minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值 mediaQuery: false, // 允许在媒体查询中转换`px`, exclude: [/pc[\s\S]*.vue/,/element-plus/,/elementTheme.scss/], //设置忽略文件,用正则做目录名匹配 } } } ... })

参数

说明

类型

默认值

unitToConvert

需要转换的单位,默认为 px

string

px

viewportWidth

设计稿的视口宽度,如传入函数,函数的参数为当前处理的文件路径,函数返回

undefind

跳过转换

number | Function

320

unitPrecision

单位转换后保留的精度

number

5

propList

能转化为 vw 的属性列表

string[]

['*']

viewportUnit

希望使用的视口单位

string

vw

fontViewportUnit

字体使用的视口单位

string

vw

selectorBlackList

需要忽略的 CSS 选择器,不会转为视口单位,使用原有的 px 等单位

string[]

[]

minPixelValue

设置最小的转换数值,如果为 1 的话,只有大于 1 的值会被转换

number

1

mediaQuery

媒体查询里的单位是否需要转换单位

boolean

false

replace

是否直接更换属性值,而不添加备用属性

boolean

true

landscape

是否添加根据

landscapeWidth

生成的媒体查询条件

@media (orientation: landscape)

boolean

false

landscapeUnit

横屏时使用的单位

string

vw

landscapeWidth

横屏时使用的视口宽度,,如传入函数,函数的参数为当前处理的文件路径,函数返回

undefind

跳过转换

number

568

exclude

忽略某些文件夹下的文件或特定文件,例如 node_modules 下的文件,如果值是一个正则表达式,那么匹配这个正则的文件会被忽略,如果传入的值是一个数组,那么数组里的值必须为正则

Regexp

undefined

include

需要转换的文件,例如只转换 'src/mobile' 下的文件 (

include: /\/src\/mobile\//

),如果值是一个正则表达式,将包含匹配的文件,否则将排除该文件, 如果传入的值是一个数组,那么数组里的值必须为正则

Regexp

undefined

六、配置页面标题、icon、meta等

  1. 修改nuxt.config.ts

export default defineNuxtConfig({ ... app: { head: { title: "项目标题", meta: [ {name: "description", content: "项目的重点信息描述--" }, {name: "keywords", content: "项目关键词" }, {name:"applicable-device", content:"pc,mobile"}, // 移动pc适配 ], link: [{ rel:"icon", type: "image/x-icon", href:"/new_favicon.ico" }], } } ... })

七、集成ui框架

  1. 移动端vant,设计尺寸375

yarn add vant yarn add @vant/nuxt --dev

export default defineNuxtConfig({ modules: [ "@vant/nuxt" ], vant: { /** Options */ } })

<van-button type="primary" @click="showToast('toast')">button</van-button>

:root:root { --van-primary-color: red; }

//iframe.scss @import "./vanTheme"; @import "./globals"; @import "./normalize"; @import "../font/iconfont.css";

  1. 集成element plus

yarn add element-plus yarn add @element-plus/nuxt --dev

export default defineNuxtConfig({ ... modules: [ "@element-plus/nuxt" ], elementPlus: { /** Options */ } ... })

<el-button type="primary">button</el-button>

:root:root{ --el-color-primary: green; }

//iframe.scss @import "./elementTheme"; @import "./globals"; @import "./normalize"; @import "../font/iconfont.css";

  1. 编写兼容pc、移动端组件

<template> <el-button class="button"> <slot/> </el-button> </template> <style lang="scss" scoped> @import "pc"; </style>

//pc.scss @import "index";

<template> <van-button class="button"> <slot/> </van-button> </template> <style lang="scss" scoped> @import "mobile"; </style>

//mobile.scss @import "index";

<template> <ClientOnly> <PcButton v-if="!mobileStore.isMobile"> <slot/> </PcButton> <MobileButton v-else> <slot/> </MobileButton> </ClientOnly> </template> <script lang="ts" setup> const mobileStore = useMobile(); </script>

.button{ font-size: 14px; height: 32px; padding: 4px 15px; border-radius: 6px; }

export default defineNuxtConfig({ ... components: [{ path: '~/components/', pathPrefix: false, //只根据名称使用组件 }], ... })

<Button>button</Button>

八、构建布局页面Layouts

  1. 创建layouts 文件夹

布局放在layouts目录中,使用时将通过异步导入自动加载。布局是通过添加到的app.vue,或者手动指定它作为的一个prop,或者在pages页改变它

  1. 创建layouts>default.vue

<template> 一些在所有页面共享的默认布局 <slot /> </template>

//app.vue <template> <NuxtLayout> <NuxtPage/> </NuxtLayout> </template>

  1. 创建layouts>custom.vue

<template> <slot name="header"/> 自定义布局 <slot /> </template>

//app.vue <template> <NuxtLayout name="custom"> <NuxtPage/> </NuxtLayout> </template>

//index.vue <template> <Button>button</Button> </template> <script lang="ts" setup> definePageMeta({ layout: "custom", }); </script>

  1. 动态改变布局

//index.vue <template> <Button @click="enableCustomLayout">button</Button> </template> <script lang="ts" setup> function enableCustomLayout () { setPageLayout('custom') } definePageMeta({ layout: false, }); </script>

  1. 在每页的基础上重写布局

//index.vue <template> <NuxtLayout name="custom"> <template #header>重写布局</template> 当前页面 </NuxtLayout> </template> <script lang="ts" setup> definePageMeta({ layout: false, }); </script>

九、服务端目录service

  1. server 路由,创建server目录,创建.ts文件,并非.tsx

server/api中的文件在它们的路由中会自动以/api作为前缀。 对于添加没有/api前缀的服务器路由,可以将它们放到 server/routes目录中,每个文件都应该导出一个用defineEventHandler()定义的默认函数,处理程序可以直接返回JSON数据,一个Promise或使用event.node.res.end()发送响应

  1. 创建server>routes>创建routes.ts

export default defineEventHandler(() => 'service demo') 浏览器:http://localhost:3000/routes 页面:await useFetch('/routes')

  1. 创建server>api>demo.ts

export default defineEventHandler(() => 'service api demo') 浏览器:http://localhost:3000/api/demo 页面:await useFetch('/api/demo')

  1. 指定请求方式可以用.get, .post, .put, .delete作为后缀,新建:server>routes>routes.post.ts、server>api>demo.post.ts

//routes>routes.post.ts export default defineEventHandler(() => 'service router post") //api>demo.post.ts export default defineEventHandler(() => 'service api post") 页面:await useFetch('/routes',{ method: 'post'}) 页面:await useFetch('/api/demo',{ method: 'post'})

  1. 路由参数,新建:server>routes>par>[name].post.ts、server>api>[name].post.ts,不要在routes根目录下创建带参的server

//routes>par>[name].post.ts import {H3Event,H3EventContext} from "h3"; export default defineEventHandler((event:H3Event) => { let params = event.context.params as H3EventContext; return `service router post ${params.name}` }); //api>[name].post.ts import {H3Event,H3EventContext} from "h3"; export default defineEventHandler((event:H3Event) => { let params = event.context.params as H3EventContext; return `service api post ${params.name}` }); 页面:await useFetch('par/routerName',{ method: 'post'}); 页面:await useFetch('/routerName',{ method: 'post'});

  1. 嵌套路由,新建:server>api>slug>[...slug].ts

import { createRouter, defineEventHandler, useBase } from "h3"; const router = createRouter() router.get('/slugGet', defineEventHandler(() => 'slugGet')) router.post('/slugPost', defineEventHandler(() => 'slugPos')) export default useBase('/api/slug', router.handler); 浏览器:http://localhost:3000/api/slug/slugGet 页面:await useFetch('/api/slug/slugGet'); 页面:await useFetch('/api/slug/slugPost',{ method: 'post'});

  1. 处理body、query、error、设置状态码、runtimeConfig、cookie,以api>[name].post.ts为例子

import {H3Event, H3EventContext} from "h3"; export default defineEventHandler(async (event:H3Event) => { const body = await readBody(event); const query = getQuery(event); let params = event.context.params as H3EventContext; const config = useRuntimeConfig(); const cookies = parseCookies(event) const id = parseInt(params.id) as number; // if (!Number.isInteger(id)) { // throw createError({ // statusCode: 400, // statusMessage: "错误处理", // }) // } // setResponseStatus(200, '21321') return { text:`service router post ${params.name}`, params, body, query, config, cookies } }); 页面:await useFetch('api/apiName',{ method: 'post', body: { test: 'body' }, query:{ test: 'query' } });

  1. 创建server/middleware,中间件

中间件处理程序将在每个请求上运行,再运行任何其他服务器路由,以添加或检查标头、记录请求或扩展事件的请求对象,相当于api拦截器

export default defineEventHandler((event) => { console.log('New request: ' event.node.req.url) })

  1. Nitro:https://nitro.unjs.io/config,服务配置,配置server 存储示例

//nuxt.config.ts export default defineNuxtConfig( ... nitro: { storage: { redis: { driver: "redis", port: 3000, host: "localhost", username: "", password: "", db: 0, tls: {} } } } ... });

export default defineEventHandler(async (event) => { const body = await readBody(event) let params = event.context.params as H3EventContext; await useStorage().setItem('redis:Storage', body) return `service router post ${params.name}` })

export default defineEventHandler(async (event) => { const data = await useStorage().getItem('redis:Storage') return data }); 页面: const { data: resDataSuccess } = await useFetch('/api/apiName', { method: 'post', body: { text: 'storage' } }) const { data: resData } = await useFetch('/api/demo')

  1. 集成axios

yarn add axios 页面:import axios from "axios"; await axios.post("http://localhost:3000/api/apiName",{ test: 'axios' })

  1. 封装axios,创建composables>request>useAxiosRequest.ts

/** * @description axios公共请求封装 * */ import axios, {AxiosResponse, InternalAxiosRequestConfig} from "axios"; /** * @description 定义相关接口或者枚举 * */ //请求枚举 export enum MethodEnum { Get='GET', Post='POST' } //返回结果 export interface ResponseResultInterface<Body> { Header:{}, Body: Body } //请求参数 export interface RequestInterface<params> { url:string, params?:params, method?:MethodEnum } /** * 封装axios * */ // 添加请求拦截器 axios.interceptors.request.use( (config:InternalAxiosRequestConfig)=>{ return config; }, (error)=>{ return Promise.reject(error); }); // 添加响应拦截器 axios.interceptors.response.use( (response:AxiosResponse) => { return response; }, (error) => { return Promise.reject(error); }); /** * @method useAxiosRequest axios请求封装 * @param requestPar { RequestInterface } 请求url * @return Promise * */ const useAxiosRequest= async <params,responseData>(requestPar:RequestInterface<params>):Promise<responseData> => { const requestResult:AxiosResponse = await axios({ method: requestPar.method || MethodEnum.Post, url: requestPar.url, data: requestPar.params }); return requestResult.data as responseData; }; export default useAxiosRequest;

  1. 封装useFetch,创建composables>request>useRequest.ts

/** * @description useFetch请求封装 * */ import {FetchContext} from "ofetch"; import {_AsyncData} from "#app/composables/asyncData"; /** * @description 定义相关接口或者枚举 * */ //请求枚举 export enum MethodEnum { Get='GET', Post='POST' } //返回结果 export interface ResponseResultInterface<Body> { Header:{}, Body: Body } export interface RequestInterface<params>{ url:string, method?: MethodEnum, params?: params } /** * @method useRequest useFetch请求封装 * @param requestPar {RequestInterface} 请求参数 * */ const useRequest = async <params,responseData>(requestPar:RequestInterface<params>):Promise<responseData>=>{ const requestResult:_AsyncData<any, any> = await useFetch(requestPar.url,{ method:requestPar.method || MethodEnum.Post, params:requestPar.params || {}, //请求拦截 onRequest(requestOption:FetchContext):void { // options.headers = options.headers || {} }, //错误请求拦截 onRequestError(RequestErrorOption:FetchContext):void { // throw createError({ // statusCode: 500, // statusMessage: reqUrl, // message: '自己后端接口的报错信息', // }) }, //响应拦截 onResponse(ResponseOption:FetchContext):void { }, //响应错误拦截 onResponseError(responseErrorOption:FetchContext):void { // throw createError({ // statusCode: 500, // statusMessage: reqUrl, // message: '自己后端接口的报错信息', // }) } }); return requestResult.data.value as responseData; } export default useRequest;

  1. 根据自己喜好选择axios、useFetch,axios属于客户端发送请求受跨域影响,useFetch属于第一次渲染页面为服务端请求无跨域影响、再次返回页面为客户端请求受跨域影响。如果需要同时使用两种,建议把共同的枚举统一放到同一个文件

十、插件plugins

  1. 创建plugins目录,创建.ts文件,并非.tsx

自动plugins目录中的文件,并在创建Vue应用程序时加载它们。可以在文件名中使用.server或.client后缀来只在服务器端或客户端加载插件。

  1. 创建plugins>demo.vue

export default defineNuxtPlugin(nuxtApp => { return { provide: { demo: (msg: string) => `demo ${msg}` } } }); 页面:<template> <div>插件:{{ $demo('插件') }}</div> </template> <script lang="ts" setup> const { $demo } = useNuxtApp() </script>

  1. 控制插件注册顺序,在文件名上加前加上数字

export default defineNuxtPlugin(nuxtApp => { return { provide: { demo1: (msg: string) => `demo1 ${msg}` } } });

export default defineNuxtPlugin(nuxtAp => { return { provide: { demo2: (msg: string) => `${nuxtApp.$demo1('demo2')} ${msg}` } } });

  1. 创建监听hook插件,plugins>watchHook.ts

export default defineNuxtPlugin((nuxtApp) => { // 客户端 & 服务端 nuxtApp.hook("app:created", (vueApp) => { console.log("app:created"); }); // 服务端 nuxtApp.hook("app:beforeMount", (vueApp) => { console.log("app:beforeMount"); }); // 客户端 & 服务端 nuxtApp.hook("vue:setup", () => { console.log("vue:setup"); }); // 服务端 nuxtApp.hook("app:rendered", (renderContext) => { console.log("app:rendered"); }); // 客户端 nuxtApp.hook("app:mounted", (vueApp) => { console.log("app:mounted"); }); });

  1. 创建全局vue自定义指令,plugins>directive.ts

import {DirectiveBinding, VNode} from "@vue/runtime-core"; export default defineNuxtPlugin(nuxtApp => { nuxtApp.vueApp.directive('focus', { // 在绑定元素的 attribute 或事件监听器被应用之前调用 created:(el)=>{}, // 在绑定元素的父组件挂载之前调用 beforeMount:(el)=>{}, // 在包含组件的 VNode 更新之前调用 beforeUpdate:(el)=>{}, // 在包含组件的 VNode 及其子组件的 VNode 更新之后调用 updated:(el)=>{}, // 在绑定元素的父组件卸载之前调用 beforeUnmount:(el)=>{}, // 卸载绑定元素的父组件时调用 unmounted:(el)=>{}, // 绑定元素的父组件被挂载时调用 mounted:(el)=>{ el.focus() }, //渲染时向元素添加属性 getSSRProps:(binding:DirectiveBinding, vnode:VNode) =>{ return { id:'test' } } }) }); 页面:<input v-focus/>

十一、创建公共hooks

  1. 创建composables目录

Nuxt 3使用composables/目录,将hooks自动导入vue程序

  1. 把之前创建的store文件放进去

  1. 修改nuxt.config.ts,去除@store配置

export default defineNuxtConfig({ ... imports: { dirs: "composables/**" ] }, ... }); 页面使用:const mobileStore = useMobile();//相比之前不需要在import导入

十二、其他

  1. 创建utils目录:存放一些公共函数、或者枚举,会自动导入vue程序;
  2. nuxt api
  3. nuxt常用命令:https://nuxt.com/docs/api/commands/add
  1. 多环境配置

#开发环境配置 VITE_HOST=http://127.0.0.1:3000/ #本地请求ip 端口 VITE_PROXY=http://127.0.0.1 #需要代理的ip VITE_PROXY_3002=3002 #需要代理的端口

import{loadEnv}from"vite"; /** *@description定义env接口*/ interfaceENV_CONFIG{ host:string host1:string } const envScript=(process.envasany).npm_lifecycle_script.split(''); const envName=envScript[envScript.length-1];//通过启动命令区分环境 const envData=loadEnv(envName,'env') as ENV_CONFIG|unknown; export default defineNuxtConfig({ ... runtimeConfig: { public: envData // 把env放入这个里面,通过useRuntimeConfig获取 }, vite: { envDir: '~/env', // 指定env文件夹 }, ... }); 页面:const envConfig = useRuntimeConfig(); package.json配置:"dev": "nuxt dev --mode dev",

import{getRequestHeaders, getRequestURL, H3Event, HTTPMethod} from "h3"; const {public: { VITE_PROXY, VITE_PROXY_3002 }} =useRuntimeConfig(); interfaceApiInterface{ [propName:string]:string } //把已有的ip和端口。组成请求的url constapiUrlOption:ApiInterface ={ 3002:`${VITE_PROXY}:${VITE_PROXY_3002}` } //请求路由配置,getArticles是后端提供的api constapiRouter:ApiInterface ={ "/getArticles":`${apiUrlOption["3002"]}/getArticles` }; export defaultdefineEventHandler(async (event:H3Event):Promise<any> => { const urlInfo:URL = getRequestURL(event); const requestUrl:string =apiRouter[urlInfo.pathname]; if(requestUrl){ const method:HTTPMethod =getMethod(event); // const query:QueryObject =getQuery(event); const headers =getRequestHeaders(event); const options: any = { responseType: 'json', } options.headers = headers; options.method = method; if (method != 'GET') { options.body = awaitreadBody(event); } return await $fetch(requestUrl,options); } }); 页面:const envConfig:RuntimeConfig = useRuntimeConfig(); await useFetch(`${VITE_HOST}getArticles`)

十三、项目部署

  1. 安装nginx:https://nginx.org/en/download.html,下载合适的版本,解压到任意位置

server { listen 9001; server_name localhost; # server_name btyhub.site, www.btyhub.site; # ssl两个文件,放在 nginx的conf目录中 # ssl_certificate btyhub.site_bundle.pem; # ssl_certificate_key btyhub.site.key; # ssl_session_cache shared:SSL:1m; # ssl_session_timeout 5m; # ssl_ciphers HIGH:!aNULL:!MD5; # ssl_prefer_server_ciphers on; # 代理到Next的服务,默认3000端口,也可以在start的时候指定 location / { proxy_pass http://127.0.0.1:7000/; } }

  1. 安装 pm2,node进程管理工具:npm install -g pm2
  2. 安装npm install cross-env -g
  3. 把打包后得.output上传服务器
  4. package.json

{ "name": "nuxt-app", "private": true, "scripts": { "start": "cross-env PORT=7000 HOST=127.0.0.1 node .output/server/index.mjs" } }

  1. 安装child_process,运行npm install child_process -g 或 yarn add child_process --dev,创建start.js

let exec = require("child_process").exec; exec("yarn start", {windowsHide: true});

  1. 运行pm2 start start.js --name projectName
查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved