next.js+react+typescript+antd+antd-mobile+axios+redux+sass react服务端渲染构建项目,从构建到发布,兼容pc+移动端_react+next.js+antd-CSDN博客


本站和网页 https://blog.csdn.net/randy521520/article/details/130066048 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

next.js+react+typescript+antd+antd-mobile+axios+redux+sass react服务端渲染构建项目,从构建到发布,兼容pc+移动端_react+next.js+antd-CSDN博客
next.js+react+typescript+antd+antd-mobile+axios+redux+sass react服务端渲染构建项目,从构建到发布,兼容pc+移动端
局外人LZ
已于 2023-04-19 17:32:28 修改
阅读量2.4k
收藏
点赞数
分类专栏:
前端
文章标签:
javascript
react.js
sass
前端
typescript
于 2023-04-10 20:29:25 首次发布
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/randy521520/article/details/130066048
版权
前端
专栏收录该内容
23 篇文章
1 订阅
订阅专栏
简介:该教程兼容pc+移动端,如只需一端,可忽略兼容部分教程,根据需要运行的客户端构建项目
antd官网:https://ant.design/components/overview-cn/antd-mobile官网:https://mobile.ant.design/zhnext.js: https://www.nextjs.cn/react:https://react.zcopy.site/redux:https://react-redux.js.org/api/hooks#custom-context
一、介绍
Next.js,这是一个 React 的同构应用开发框架。
直观的、 基于页面 的路由系统(并支持 动态路由)预渲染。支持在页面级的 静态生成 (SSG) 和 服务器端渲染 (SSR)自动代码拆分,提升页面加载速度具有经过优化的预取功能的 客户端路由内置 CSS 和 Sass 的支持,并支持任何 CSS-in-JS 库开发环境支持 快速刷新利用 Serverless Functions 及 API 路由 构建 API 功能 完全可扩展
二、构建项目
yarn create next-app “文件名” --typescript
yarn dev
三、调整项目
文件目录 _app.tsximport type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
index.tsximport {NextPage} from "next";
const Home: NextPage = (props) => {
return <div>dsada</div>
};
export default Home
三、静态资源assets
创建assets>css、assets>font、assets>img安装依赖yarn add sass
集成字体图标,下载阿里icon库,解压后把压缩包里的文件复制到assets>font<i class="iconfont icon-usename"></i>
css文件下分别创建globals.scss、iframe.scss、normalize.scss、variable.scss//globals.scss 全局样式文件
body{
font-size: $font_size!important;
//iframe.scss 公共样式导入
@import "./globals";
@import "./normalize";
@import "../font/iconfont.css";
//normalize.scss 同一浏览器样式,下载后放入该文件中
http://nicolasgallagher.com/about-normalize-css/
https://github.com/necolas/normalize.css
//variable.scss 全局变量文件
$primary-color: red;
/**
* 字体大小
*/
$font_size: 14px;//基础字体大小
$sm_font_size: 12px;//小号字体
$bg_font_size: 16px;//大号字体
$xl_font_size: 20px;//超大号字体
/**
* icon 大小
*/
$icon_size: $font_size;//默认字体
$bg_icon_size: $bg_font_size;//大号字体
$sm_icon_size: $sm_font_size;//小号字体
$xl_icon_size: $xl_font_size;//超大号字体
/**
* button 颜色、大小
*/
$btn_primary: #1677ff;
$btn_danger: #ff4d4f;
/**
* h1-h5标签字体大小
*/
$h1_font_size: 38px;//h1字体大小
$h2_font_size: 30px;//h2字体大小
$h3_font_size: 24px;//h3字体大小
$h4_font_size: $xl_font_size;//h4字体大小
$h5_font_size: $bg_font_size;//h5字体大小
配置引入路径tsconfig.json"paths": {
...
"@css/": [
"./src/assets/css/"
],
"@img/": [
"./src/assets/img/"
],
...
引入全局样式,修改_app.tsximport type { AppProps } from "next/app";
import "@css/iframe.scss";
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
引入全局sass变量,next.config.jsconst path = require("path");
/** @type {import('next').NextConfig} */
const nextConfig = {
...
sassOptions:{
includePaths: [path.join(__dirname, "./src/assets/css")],
prependData: "@import 'variable.scss';"
},
...
module.exports = nextConfig
配置antd-mobile主题,https://mobile.ant.design/zh/guide/theming,新建 css>antdMobileTheme.scss:root:root {
--adm-color-primary: #ff4d4f;
配置antd主题
新建pages>antTheme.module.scss
/* antd 主题配置
* 详细配置可参考 https://ant.design/docs/react/customize-theme-cn*/
:export {
colorPrimary: $primary-color;
fontSize: $font_size;
fontSizeHeading1: $h1_font_size;
fontSizeHeading2:$h2_font_size;
fontSizeHeading3:$h3_font_size;
fontSizeHeading4:$h4_font_size;
fontSizeHeading5:$h5_font_size;
fontSizeLG:$bg_font_size;
fontSizeSM:$sm_font_size;
fontSizeXL:$xl_font_size;
fontSizeIcon:$sm_icon_size;
修改_app.tsximport type { AppProps } from "next/app";
import {ConfigProvider} from "antd";
import them from "./antTheme.module.scss";
import "@css/iframe.scss";
export default function App({ Component, pageProps }: AppProps) {
return <ConfigProvider theme={{token: them}}>
<Component {...pageProps}/>
</ConfigProvider>
集成postcss
安装依赖postcss-px-to-viewport-8-pluginyarn add postcss-px-to-viewport-8-plugin --dev
根目录新建postcss.config.js//postcss.config.js
module.exports = {
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.module/,/antTheme.module.scss/,/braft-editor/], //设置忽略文件,用正则做目录名匹配
},
};
四、集成redux
安装依赖yarn add redux react-redux redux-saga
yarn add @types/react-redux @types/redux-saga next-redux-wrapper redux-devtools-extension --dev
创建redux>reducers、redux>sagas文件夹配置引入路径tsconfig.json"paths": {
...
"@reducers/*": [
"./src/redux/store/reducers/*"
],
"@sagas/*": [
"./src/redux/store/sagas/*"
],
"@store/*": [
"./src/redux/store/*"
],
...
创建第一个store,redux>reducers>mobileStore.tsx/**
* @description 该store,判断是否是移动端
* */
/**
* @description 定义相关接口或者枚举
* */
export enum MobileStoreActionEnum {
INIT="mobileStoreInit",
CHANGE="mobileStoreChange"
export type MobileStoreStateType = boolean;
interface MobileStoreActionInterface{
type: MobileStoreActionEnum,
payload:MobileStoreStateType
/**
* @description store逻辑
* */
const mobileInitState:MobileStoreStateType = false;
const mobileStore = (state:MobileStoreStateType =mobileInitState, action: MobileStoreActionInterface):MobileStoreStateType => {
switch (action.type) {
case MobileStoreActionEnum.INIT:
return state
case MobileStoreActionEnum.CHANGE:
return action.payload
default:
return state
export default mobileStore;
创建第一个sagaStore,redux>reducers>demoStore.tsx,异步store/**
* @description 定义相关接口或者枚举
* */
export enum DemoStoreActionEnum{
WATCH='watchDemoStore',
CHANGE='demoStoreChange'
interface DemoStoreStateInterface {
num:number
export interface DemoStoreActionInterface {
type: DemoStoreActionEnum
payload: DemoStoreStateInterface
/**
* @description store逻辑
* */
const initState:DemoStoreStateInterface = {
num: 100
const demoStore = (state:DemoStoreStateInterface = initState, action: DemoStoreActionInterface):DemoStoreStateInterface => {
switch (action.type) {
case DemoStoreActionEnum.CHANGE:
return action.payload
default:
return state
};
export default demoStore;
依次创建redux>sagas>demo.tsx、redux>sagas>mainSaga.tsx
saga的应用场景是复杂异步,如长时事务LLT(long live.transcation)等业务场景。方便测试,可以使用takeEvery打印logger。提供takeLatest/takeEvery/throttle方法,可以便利的实现对事件的仅关注最近事件、关注每一次、事件限频提供cancel/delay方法,可以便利的取消、延迟异步请求提供race(effects),[…effects]方法来支持竞态和并行场景提供channel机制支持外部事import { call, put, takeEvery, takeLatest,take,all,race,throttle,delay,fork,cacel,cancelled} from 'redux-saga/effects';
takeEvery:被调用的任务无法控制何时被调用, 它们将在每次 action 被匹配时一遍又一遍地被调用。并且它们也无法控制何时停止监听。
take:与takeEver相反,与 action 只会监听一次,使用一次就销毁
takeLatest:每次 action 被匹配,当有action正在匹配,会放弃正在匹配的action,执行最新的
call: saga通过 Generator函数实现,在yield函数后执行effect,其中call是用于执行某些异步操作的。
put: 和上面的call一样,中间件提供put 来把action丢到中间件中去dispatch,好处同样是便于测试
all: 同步执行多个任务使需要用到 yield all([call(fetch, '/users'),call(fetch, '/repos')])
race: 和promise中的race一个概念,执行多个任务,受到响应后则继续执行 yield race({posts: call(fetchApi, '/posts'),timeout: call(delay, 1000)})
fork:fork和take不同,take会和call一样阻塞代码的执行,知道结果返回,fork则不会,它会将任务启动并且不阻塞代码的执行,fork会返回一个task,可以用cacel(task)来取消任务
cacel:来取消任务
cancelled:如果当前任务,被cacel取消,则返回true
throttle:节流
//redux>sagas>demo.tsx
import {call, put, takeEvery} from "@redux-saga/core/effects";
import {DemoStoreActionEnum, DemoStoreActionInterface} from "@reducers/demoStore";
// 延时器
const delay = (ms:number) => new Promise(resolve => setTimeout(resolve, ms));
function* asyncDemoSaga(action:DemoStoreActionInterface):Generator {
yield call(delay,2000);
yield put({ type: DemoStoreActionEnum.CHANGE,payload:action.payload})
function* watchDemoSaga():Generator {
yield takeEvery(DemoStoreActionEnum.WATCH, asyncDemoSaga)
export default watchDemoSaga;
//redux>sagas>mainSaga.tsx
import {all} from "redux-saga/effects"
import watchDemoSaga from "@sagas/demo";
// saga中间件 主saga,用于区别是否需要saga来处理异步操作,如果没有异步,则放行
function* mainSaga() {
yield all([
// 监听 saga 中有 userWatchSaga 操作,所以会拦截这个 action
watchDemoSaga(),
])
// 主saga要对外暴露出去
export default mainSaga;
修改_app.tsximport type { AppProps } from "next/app";
import {ConfigProvider} from "antd";
import them from "./antTheme.module.scss";
import "@css/iframe.scss";
import {useEffect} from "react";
import {useDispatch} from "react-redux";
import { MobileStoreActionEnum} from "@reducers/mobileStore";
import wrapper from "@/redux";
import {Dispatch} from "redux";
const App = ({ Component, pageProps }: AppProps) => {
const dispatch:Dispatch = useDispatch();
useEffect(():void => {
//判断是哪个客户端(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)) {
dispatch({
type: MobileStoreActionEnum.CHANGE,
payload: true
});
//增加全局class,用于设置全局样式
document.getElementsByTagName('html')[0].className = 'mobile';
}else{
//增加全局class,用于设置全局样式
document.getElementsByTagName('html')[0].className = 'pc';
},[])
return <ConfigProvider theme={{token: them}}>
<Component {...pageProps}/>
</ConfigProvider>
export default wrapper.withRedux(App)
创建redux>index.tsximport { createWrapper, MakeStore } from "next-redux-wrapper";
import { applyMiddleware, createStore, Store} from "redux";
import createSagaMiddleware, {SagaMiddleware} from "redux-saga";
import { composeWithDevTools } from "redux-devtools-extension/developmentOnly";
import rootReducer from "@store/index";
import mainSaga from "@sagas/mainSaga"; //异步初始化store
const makeStore: MakeStore<Store> = () => {
const sagaMiddleware:SagaMiddleware = createSagaMiddleware()
const store:Store = createStore(rootReducer, composeWithDevTools(applyMiddleware(sagaMiddleware)))
sagaMiddleware.run(mainSaga)
return store
export default createWrapper<Store>(makeStore)
封装pc+移动端兼容性button组件,创建兼容性ui框架,需要把4. antd、antd-mobile二次封装,并合并一些共同的参数,修改成共同的样式;创建非ui框架的组件,只要注意像素单位的兼容就行,如:mobile.module.scss、pc.module.scss,postcss已限制pc.module 样式文件的转换
修改.eslintrc.json{
"extends": "next/core-web-vitals",
"rules": {
"react/display-name": "off"
创建components>antd>button创建pc端button组件button>pc.tsx、button>pc.module.scss//button>pc.tsx
/**
* @description pc端Button组件
* */
/**********第三方插件、组件引用**********/
import React from "react";
import {Button as PcButton, ButtonProps} from "antd";
import {SizeType} from "antd/es/config-provider/SizeContext";
import {ButtonType} from "antd/es/button";
/**********当前目录文件*********/
import styles from "./pc.module.scss";
export interface PcButtonInterface {
type?: ButtonType,
size?: SizeType,
onClick?: ButtonProps['onClick'],
children?: React.ReactNode
const Button = React.memo((props:PcButtonInterface)=>{
return <PcButton className={styles.button}
type={props.type}
size={props.size}
onClick={props.onClick}>
{ props.children }
</PcButton >
});
export default Button;
创建移动端button组件button>mobile.tsx、button>mobile.module.scss//button>mobile.tsx
/**
* @description 移动端Button组件
* */
/**********第三方插件、组件引用**********/
import React from "react";
import {Button as MobileButton, ButtonProps} from "antd-mobile";
/**********当前目录文件*********/
import styles from "./mobile.module.scss";
export interface MobileButtonInterface {
type?: ButtonProps['color'],
size?: ButtonProps['size'],
onClick?:ButtonProps['onClick'],
children?: React.ReactNode;
const Button = React.memo((props:MobileButtonInterface)=>{
return <MobileButton className={styles.button}
color={props.type}
size={props.size}
onClick={props.onClick}>
{ props.children }
</MobileButton>
});
export default Button;
创建兼容pc+移动组件button>index.tsx、button>index.scss//button>index.tsx
/**
* @description 同时兼容pc、移动的Button组件
* */
import React, {useState} from "react";
import PcButton, {PcButtonInterface} from "./pc";
import MobileButton, {MobileButtonInterface} from "./mobile";
import {useSelector, useStore} from "react-redux";
import {Store} from "redux";
interface ClientButtonInterface {
type?: PcButtonInterface['type'] & MobileButtonInterface['type'],
size?: PcButtonInterface['size'] & MobileButtonInterface['size'],
onClick?: PcButtonInterface['onClick'] & MobileButtonInterface['onClick'],
children?: React.ReactNode
const Button = React.memo((props: ClientButtonInterface) => {
const store:Store = useStore();
const storeState = store.getState() as any;
const [mobile,setMobile]= useState(storeState.mobileStore)
useSelector((state:any):void => {
if(mobile!=state?.mobileStore){
setMobile(state?.mobileStore);
});
return <>
{mobile ? <MobileButton {...props}/> : <PcButton {...props}/>}
</>
});
export default Button;
//button>index.scss
.button{
font-size: 14px;
height: 32px;
padding: 4px 15px;
border-radius: 6px;
修改button>mobile.module.scss、button>pc.module.scss@import "./index";
使用import Button from "@/components/antd/button";
<Button type="primary">antd</Button>
持续储存
安装依赖,https://www.npmjs.com/package/redux-persistyarn add redux-persist
修改redux>index.tsximport { createWrapper, MakeStore } from "next-redux-wrapper";
import { applyMiddleware, createStore, Store} from "redux";
import createSagaMiddleware, {SagaMiddleware} from "redux-saga";
import { composeWithDevTools } from "redux-devtools-extension/developmentOnly";
import {persistStore, persistReducer} from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "@store/index";
import mainSaga from "@sagas/mainSaga"; //异步初始化store
//持久化储存配置
const persistConfig = {
key: 'root', //在localStorge中生成key为root的值
storage,
blacklist:['demoSaga'] //设置某个reducer数据不持久化
const makeStore: MakeStore<Store> = () => {
const sagaMiddleware:SagaMiddleware = createSagaMiddleware();
const rootPersistReducer = persistReducer(persistConfig, rootReducer)
const store:Store = createStore(rootPersistReducer, composeWithDevTools(applyMiddleware(sagaMiddleware)))
sagaMiddleware.run(mainSaga);
persistStore(store);
return store
export default createWrapper<Store>(makeStore)
五、页面配置
设置页面标题:_app.tsximport '@/assets/css/globals.scss';
import type { AppProps } from 'next/app';
import Head from 'next/head';
import { ConfigProvider } from 'antd';
import them from '@/pages/app.module.scss';
export default function App({ Component, pageProps }: AppProps) {
return <>
<Head>
<title>页面标题</title>
</Head>
<ConfigProvider theme={{token: them}}>
<Component {...pageProps}/>
</ConfigProvider>
</>
设置页面框架代码:_document.tsx,只会在初始化预渲染,设置的内容只会在build后生效import {Html, Head, Main, NextScript} from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head>
<link rel="icon" href="/favicon.ico"></link>
<meta name="description" content="页面框架"></meta>
</Head>
<body>
<Main/>
<NextScript/>
</body>
</Html>
自定义404页面,pages下新建404.tsx页面export default function Custom_404(){
return <>404页面</>
六、图片引用
方法一:原生img,使用’ '可能会导致LCP变慢和带宽增加,build时会有此警告import idCard from '@img/idCard.png';
<img src={idCard.src}/>
方法二:使用 next/image;简单的图片引用建议用原生img//建议用div包括起来,不单独使用,单独使用会自动生成很多自带的样式;Image会自适应div大小
import idCard from '@img/idCard.png';
import Image from 'next/image';
<div><Image src={idCard} alt=""/></div>
next/image 自带优化的api适用于SSR,SSG中无法使用 ,可以改动 next.config.js 配置解决const nextConfig = {
reactStrictMode: true,
swcMinify: true,
images:{
unoptimized:true
module.exports = nextConfig
安装sharp包(高性能图片处理器),Next.js将自动使用它的图像优化yarn add sharp
七、动态路由
创建pages>demo文件夹创建demo>index.tsx、demo>index.scss、demo>mobile.module.scss、demo>pc.module.scss//demo>index.tsx
import Image from "next/image";
import idCard from "@img/idCard.png";
import useStyle from "@hook/styleHook";
import mobileStyle from "./mobile.module.scss";
import pcStyle from "./pc.module.scss";
const Demo = () => {
const styles = useStyle(pcStyle,mobileStyle);
return <div className={styles.P_demo}>
<Image src={idCard} alt=""/>
</div>;
};
export default Demo
//demo>index.scss
.P_demo{
img{
width: 100px;
height: 100px;
//demo>mobile.module.scss、demo>pc.module.scss
@import "./index";
修改page>index.tsximport {NextRouter, useRouter} from "next/router";
const router:NextRouter = useRouter();
<Button onClick={() => router.push('/demo')}>goDemo</Button>
自定义路由,改动 next.config.js 配置const nextConfig = {
...
//自定义路由,通常不需要自定义路由,适用于SS
exportPathMap: async ()=>{
return {
'/':{
page:'/index'
},
...
module.exports = nextConfig
动态路由
修改demo>index.tsx为demo>[id].tsximport Image from "next/image";
import idCard from "@img/idCard.png";
import useStyle from "@hook/styleHook";
import mobileStyle from "./mobile.module.scss";
import pcStyle from "./pc.module.scss";
import {NextRouter, useRouter} from "next/router";
const Demo = () => {
const styles = useStyle(pcStyle,mobileStyle);
const router:NextRouter = useRouter();
console.log(router.query)
return <div className={styles.P_demo}>
<Image src={idCard} alt=""/>
</div>;
};
export default Demo
修改pages>index.tsx<Button onClick={() => router.push('/demo/1')}>goDemo</Button>
路由嵌套
[id].tsx:paths 里面参数只有一个id ,/demo/1[…routers].tsx :paths 里面可以包含多个路径,以数组形式发挥参数,/demo/1/3[id]/[comment].tsx:paths 里面可以包含多个路径,以json形式返回,/demo/1/3
八、接口api
NEXT.js存在CSR/SSR/SSG 三种请求方式,最多存在两种:1、CSR+SSR;2、CSR+SSG CSR请求:常规前端项目中请求方式,由客户端浏览器端发送请求,拿到数据后再渲染道页面 SSR请求:由服务端发起请求(NEXT.js中的node.js),拿到数据后,组装HTML,再把HTML返回到客户端浏览器 SSG请求:与SSR请求类似,由服务端发起请求(NEXT.js中的node.js),拿到数据后,组装HTML,然后静态化输出。由于是完全静态化输出,当数据变化时,必须重新静态化才能更新页面
安装axiosyarn add axios
封装axios/**
* @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 baseRequest= 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 baseRequest;
配置引入路径,修改 tsconfig.json"paths": {
...
"@common/*": [
"./src/common/*"
],
"@api/*": [
"./src/pages/api/*"
],
...
创建api服务,pages>api>demoApi.tsx// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next";
import {ResponseResultInterface} from "@common/baseRequest";
export interface DemoInterface {
id: number,
name?: string
type ApiDemoType = ResponseResultInterface<DemoInterface>
export default function demoApi(
req: NextApiRequest,
res: NextApiResponse<ApiDemoType>
):void {
let data:ApiDemoType= {
Header:{},
Body:{
id:-1
};
if(req.method == "GET"){
const id:number = Number(req.query.id);
data.Body.id = id;
switch (id) {
case 1:
data.Body.name = "我是API服务1"
break;
res.status(200).json(data)
}else{
res.status(200).json(data)
修改pages>demo>[id].tsximport Image from "next/image";
import {NextRouter, useRouter} from "next/router";
import {GetServerSideProps} from "next";
import {ParsedUrlQuery} from "querystring";
import idCard from "@img/idCard.png";
import useStyle from "@hook/styleHook";
import baseRequest, {MethodEnum, RequestInterface} from "@common/baseRequest";
import {DemoInterface} from "@api/demoApi";
import mobileStyle from "./mobile.module.scss";
import pcStyle from "./pc.module.scss";
const Demo = (props: DemoInterface) => {
const styles = useStyle(pcStyle,mobileStyle);
const router:NextRouter = useRouter();
console.log(router.query)
console.log(props);
return <div className={styles.P_demo}>
<Image src={idCard} alt=""/>
</div>;
};
/**
* getServerSideProps 适用于SSR,不适用于SSG
* getStaticProps SSR 和 SSG 均支持,但仅在网站build时候发起API请求
* getServerSideProps 和 getStaticProps请求都是在服务端进行不涉及跨域
* */
export const getServerSideProps: GetServerSideProps = async (paths) => {
const query:ParsedUrlQuery = paths.query;
const requestOption:RequestInterface<undefined>={
url:`http://localhost:3000/api/demoApi?id=${query.id}`,
method:MethodEnum.Get
const requestResult = await baseRequest<DemoInterface>({
url: requestOption.url,
method:requestOption.method
});
return {
props: requestResult.Body
/**
* SSG 静态生成
* getStaticPaths build 时会生成多个页面
* 只是用于getStaticProps
* */
// export const getStaticPaths: GetStaticPaths<DemoParams> = async () => {
// // const arr: string[] = ['1', '2'];
// // const paths = arr.map((id) => {
// // return {
// // params: { id },
// // }
// // })
// // return {
// // //这里的路由参数提供给getStaticProps使用
// // paths,
// // //不在以上参数路由将返回404
// // fallback: false
// // }
// const id1:DemoParams = {id: '1'};
// const id2:DemoParams = {id: '2'};
// const staticPathOption = {
// //这里的路由参数提供给getStaticProps使用
// path: [{
// params: id1
// }, {
// params: id2
// }],
// //不在以上参数路由将返回404dc
// // fallback: false
// }
// return staticPathOption
// }
export default Demo
九、生成静态网站(SSG)
设置SSG的export命令,修改package.json"scripts": {
"dev": "next dev",
"build": "next build && next export",
"start": "next start",
"lint": "next lint"
},
然后执行yarn build,该命令回显执行next build 再执行 next export,输出目录为根目录下的out目录 设置静态资源的bassePath,如果发布在服务器二级目录需要设置,更改next.config.js 配置;/app为二级目录const nextConfig = {
reactStrictMode: true,
swcMinify: true,
basePath: process.env.NODE_ENV == "development"? '':'/app'
images:{
unoptimized:true
module.exports = nextConfig
设置输出目录export输出目录为app"scripts": {
"dev": "next dev",
"build": "next build && next export -o app",
"start": "next start",
"lint": "next lint"
},
十、以SSR模式运行项目
```
yarn build
yarn start -p 4000 //默认端口3000
```
十一·、以SSR模式运行项目
安装依赖npm install cross-env -g
package.json "scripts": {
"dev": "next dev",
"build": "next build && next export",
"start": "next start",
"lint": "next lint",
"customBuild": "cross-env BASE_PSTH=%npm_config_base% next build && next export -0 %npm_config_base%",
"customBuild": "cross-env BASE_PSTH=$npm_config_base next build && next export -0 $npm_config_base%"//mac
},
运行npm run customBuild --base=/demo --out=demo
十二、多环境开发配置
安装依赖yarn add cross-env --dev
根目录下创建.env.production(生产环境)、.env.development(开发环境)、.env.test(测试环境)、.env.local(默认环境,始终覆盖默认设置)、.env(所有环境)//.env.test
TEST=test //只有服务端可以获取到
NEXT_PUBLIC_HOST=http://127.0.0.1:3000/ //变量暴漏给浏览器端,加NEXT_PUBLIC_
指定环境运行,修改package.json "scripts": {
"dev:test": "cross-env NODE_ENV=test next dev",
},
页面打印:
console.log(process.env.TEST);
console.log(process.env.NEXT_PUBLIC_HOST);
十二、项目部署
安装nginx:https://nginx.org/en/download.html,下载合适的版本,解压到任意位置 nginx 配置
启动cd到nginx目录运行 nginx.exe启动cd到nginx目录运行start nginx,访问http://localhost:80 正常访问nginx运行成功启动cd到nginx目录运行重新加载配置启动cd到nginx目录运行nginx -s stop 停止启动cd到nginx目录运行nginx -s quit 正常关闭启动cd到nginx目录运行nginx -s reopen 重新打开配置nginx.conf
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:3000/;
安装 pm2,node进程管理工具:npm install -g pm2把打包后得.next,next.config.js 上传服务器package.json,运行yarn install{
"name": "react_common",
"version": "0.1.0",
"private": true,
"scripts": {
"start": "next start"
},
"dependencies": {
//项目下package.json 中dependencies
},
"devDependencies": {
//项目下package.json 中devDependencies
"child_process": "^1.0.2"
安装child_process,运行yarn add child_process --dev,创建start.jslet exec = require("child_process").exec;
//yarn start -p 9003 指定端口运行项目
exec("yarn start", {windowsHide: true});
运行pm2 start start.js --name projectName
十三、next.config.js
```
const configs = {
// 编译文件的输出目录
distDir: 'dest',
// 是否给每个路由生成Etag
generateEtags: true,
// 页面内容缓存配置
onDemandEntries: {
// 内容在内存中缓存的时长(ms)
maxInactiveAge: 25 * 1000,
// 同时缓存多少个页面
pagesBufferLength: 2,
},
// 在pages目录下那种后缀的文件会被认为是页面
pageExtensions: ['jsx', 'js'],
// 配置buildId
generateBuildId: async () => {
if (process.env.YOUR_BUILD_ID) {
return process.env.YOUR_BUILD_ID
// 返回null使用默认的unique id
return null
},
// 手动修改webpack config
webpack(config, options) {
return config
},
// 修改webpackDevMiddleware配置
webpackDevMiddleware: config => {
return config
},
// 可以在页面上通过 procsess.env.customKey 获取 value
env: {
customKey: 'value',
},
//CDN 前缀支持
assetPrefix: 'https://cdn.mydomain.com',
//静态优化指标
devIndicators: {
autoPrerender: false
},
//禁止etag生成
generateEtags: false,
//禁止x-powered-by,x-powered-by用于告知网站是用何种语言或框架编写的
poweredByHeader: false
//自定义路由
exportPathMap: async ()=>{
return {
'/':{
page:'/index'
},
images:{
unoptimized:true
},
// 只有在服务端渲染时才会获取的配置
serverRuntimeConfig: {
mySecret: 'secret',
secondSecret: process.env.SECOND_SECRET,
},
// 在服务端渲染和客户端渲染都可获取的配置
publicRuntimeConfig: {
staticFolder: '/static',
},
关注博主即可阅读全文
优惠劵
局外人LZ
关注
关注
点赞
收藏
觉得还不错?
一键收藏
打赏
知道了
评论
next.js+react+typescript+antd+antd-mobile+axios+redux+sass react服务端渲染构建项目,从构建到发布,兼容pc+移动端
Next.js,这是一个 React 的同构应用开发框架。直观的、 基于页面 的路由系统(并支持 动态路由)预渲染。支持在页面级的 静态生成 (SSG) 和 服务器端渲染 (SSR)自动代码拆分,提升页面加载速度具有经过优化的预取功能的 客户端路由内置 CSS 和 Sass 的支持,并支持任何 CSS-in-JS 库开发环境支持 快速刷新利用 Serverless Functions 及 API 路由 构建 API 功能 完全可扩展。
复制链接
扫一扫
专栏目录
nextjs-ts:带有TypeScript和Redux的带有争议的Next JS项目样板
02-05
NextJS样板
一个使用NextJS启动项目的自以为是的样板
产品特点
:clapping_hands_medium-light_skin_tone:下10个
:atom_symbol_selector: React17
:water_wave: 自定义文档和应用
:keyboard_selector: 在严格模式下使用TypeScript进行类型安全
:lipstick: 使用PostCSS +预设环境编写SCSS和将来CSS
:pen: SVG精灵图标
:eyes: 使用ESLint&Stylelint整理代码
:rainbow: 更漂亮的代码风格
:hammer: Jest +进行测试
:index_pointing_up_selector: 赫斯基(Husky)+短绒(Lint)进行代码质量保证
:gear_selector: Editorconfig文件用于一致的缩进
:file_cabinet: Redux与react-redux和redux-thunk
变异分支
git clone -b
基于umi+antd-mobile的移动端项目模板
02-22
基于umi+antd-mobile的移动端项目模板
2 条评论
您还未登录,请先
登录
后发表或查看评论
大前端 - react - 服务端渲染 - Next.js
weixin_38245947的博客
10-05
1431
1.
如何进行各个终端的页面适配(react项目安装插件 postcss-px-to-viewport)
最新发布
qq_52181663的博客
11-17
495
2、在webpack的配置文件中(webpack.config.js),引入这个插件,并进行配置。1、先安装这个插件 这个插件会自动将样式文件中的 px 转换为 vw。(2)、再进行配置 在 getStyleLoaders()中配置。一般我们都是按着UI稿的尺寸来写固定的px 这个习惯。插件是 postcss-px-to-viewport。在上例的配置中,页面的参考宽高为 750 1624。我们可以引用插件来做。(以reactApp为例)这个可以根据设计稿的宽高来,这样方便!
Nextjs13基础及Antd的使用
09-16
774
nextjs全栈开发
React入门及18及Next.js
marsAphrodite的博客
03-27
2371
文章目录一、React是什么?JSXJSX 原来是颗糖二、React181.下载包2.组件to be continue......
一、React是什么?
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库
使用 React 可以将一些简短、独立的代码片段组合成复杂的 UI 界面,这些代码片段被称作组件
JSX
JSX是什么呢?它就是js的语法拓展。
<script type="text/babel">
const ele = (<div cla
next.js中引入sass
人间草木
04-01
5122
第一步:
安装sass
npm install --save @zeit/next-sass node-sass
第二步:
在项目根目录添加next.config.js 文件,用于指示Next加载对用的功能:
const withSass = require('@zeit/next-sass')
module.exports = withSass()
第三步:
在 pages...
Redux-cnode:react + react-router + redux + es6 + antd-mobile + webpack版本的Cnode
02-05
楼主身为一个非计算机专业的大四学生,目前在学校附近的公司实习,利用工作之余做的第一个redux项目,也是刚刚接触redux 1个月,从最开始的看不懂到现在的能稍微写点东西出来了,希望各位大神觉得哪里做的不好,不足...
next-js-with-typescript-redux:next.js + TypeScript + react-redux的应用模板
05-11
带样式脚本的下一个js Redux next.js + TypeScript + react-redux的应用模板 它基于... 设置 yarn install 启动开发服务器 yarn dev
使用React+Redux+React-router构建可扩展的前端应用
02-01
React及其相关技术,来进行实际前端项目的开发。因为主要介绍如何将技术用于实践,所以希望读者已经对相关概念已经有一定的了解。 本文最初来源于笔者在StuQ的一次同名课程直播,现在加以整理成文,希望能对更多的人...
使用Next.js+React.js+Koa+Typescript搭建的博客社区系统源码
10-14
使用Next.js+React.js+Koa+Typescript搭建的博客社区系统源码
TypeScript项目中Axios的封装
有事儿私信。
08-14
464
为了实现统一的网络请求处理和管理,在日常开发中我们常常封装 axios,来实现统一配置、设置请求拦截器和响应拦截器、错误处理等。TypeScript可以给项目提供类型检查和类型推导,axios请求回来的数据也会受其检查。本文将提供axios结合ts写法的封装步骤和代码。
next.js中antd引入以及按需加载
祥哥的说
10-27
6242
babel:
babel是一种js语法编译器,在前端开发过程中,由于浏览器的版本和兼容性问题,很多js的新方法和特性的使用都受到了限制。使用babel可以将代码中js代码编译成兼容绝大多数主流浏览器的代码。
简而言之,就是把不兼容的 JavaScript 代码转为可兼容,可以执行的 JavaScript 代码。
功能:
语法转换
  添加一些兼容性的 polyfill 功能
  源码转换等
(1) presets :是某一类 plugin 的集合,包含了某一类插件的所有功能。
(2) plugin
学习React与Next.js过程中的疑惑
weixin_43850639的博客
04-17
526
next.js提供了快速刷新的功能,我很疑惑:快速刷新什么?Next.js 开发服务器启用了快速刷新。当您对文件进行更改时,Next.js 几乎立即自动在浏览器中应用更改。无需刷新!这将帮助您快速迭代您的应用程序。Hook是React 16.8版本引入的一种特性,它是一些特殊的函数,可以让开发者在函数式组件中使用 React 的一些特性,如状态(state)、生命周期函数(lifecycle)、上下文(context)和副作用(effects)等。
NextJs 请求数据 (fetch axios)
weixin_44284981的博客
09-10
8029
NextJs 请求数据 (fetch axios)
在使用nextJs请求数据之前 首先我们来说一下 nextJs
nextJs是一个轻量级的react服务端渲染应用框架,所以他不用考虑前端经常要涉及到的跨域
nextJs不用写router即路由的配置 只需在pages下创建js文件 即生成路由路径 例如 创建about.js 就可以用过localhost: 3000/about访问的到
请求数据
官方推荐的是fetch 注意 fetch中的路径应是完整的路径
function About({ res
nextjs移动端开发总结
big_wolf0的博客
10-09
2025
移动端rem适配
1.让font-size随页面的宽度改变而改变
这里使用lib-flexible插件
yarn add lib-flexible
由于nextjs是服务端渲染,没有window对象
这里封装了一个方法来解决这一问题
import { useEffect } from 'react'
export default function useRem() {
const setRem = async () => {
await require('lib-flexible')
React + Next.js 搭建项目(配有对比介绍一起食用)
Yuki_yuhan的博客
08-26
1416
Next.js是一个React框架,它允许你使用React框架建立有利于SEO的、极度面向用户的静态网站和网络应用。它具有混合静态和服务器渲染、TypeScript支持、智能捆绑、路由预取等功能,无需额外配置。create-react-app与create-next-app的区别:前者是React官方出的构建React单页面应用的脚手架工具;后者是由Next.js的创建者正式维护,能快速构建新的Next.js应用程序,并设置好一切。App Router与Pages Router 的区别...
React Native 内嵌h5页面(基于vue)的实现方案调研
llz0725的博客
03-02
4845
React Native 是目前流行的跨平台移动应用开发框架之一。通过采用不同的方法进行混合移动应用开发,它不会生成原生 UI 组件,而是基于 React,React Native 是一个用于构建基于 Web 的交互界面的 JavaScript 库,因此会有更丰富的 UI 体验效果,同时也能够很好地调用底层框架的UI使用。 React Native 已经成为一种流行的移动开发技术,它提供了一个使用 JavaScript 构建原生跨平台移动应用的强大框架。
在做原生开发时,一般我们会有一些加载H5网页的需求,
React使用next.js实现服务器端渲染
lulula的博客
09-03
1824
什么是服务端渲染及优势
服务端渲染(Server side rendering),是指通过服务器执行React页面的渲染和生成,并返回给客户端静态的HTML页面。
服务端渲染主要优势:
SEO
首屏渲染友好
速度相对快
使用React编写出来的程序是单页的应用程序,前端请求会都是一个html模板,对于信息分发类或者公众网站来说致命,SEO无法优化,搜索引擎无法找到网站想要分发出去的东西。...
帮忙生成一个next.js + react + ts 的项目
05-31
好的,您可以按照以下步骤来生成一个 next.js + react + ts 的项目:
1. 首先,确保您的电脑已经安装了 Node.js 和 npm 包管理器。
2. 打开终端(Terminal)或命令行界面,进入您想要生成项目的文件夹。
3. 运行以下命令来生成一个 next.js 项目:
```
npx create-next-app my-app --typescript
```
其中,`my-app` 是您的项目名称,`--typescript` 表示使用 TypeScript。
4. 进入项目文件夹并启动开发服务器:
```
cd my-app
npm run dev
```
5. 打开浏览器,访问 `http://localhost:3000` 即可看到项目的欢迎页面。
这样,您就成功生成了一个 next.js + react + ts 的项目。接下来,您可以根据您的需求进行开发和定制。
“相关推荐”对你有帮助么?
非常没帮助
没帮助
一般
有帮助
非常有帮助
提交
局外人LZ
CSDN认证博客专家
CSDN认证企业博客
码龄4年
暂无认证
123
原创
4893
周排名
1万+
总排名
5万+
访问
等级
1441
积分
1523
粉丝
159
获赞
16
评论
283
收藏
私信
关注
热门文章
mysql超全语法大全
2837
前端校验身份证号正则,校验规则省份、年月日,包含15位校验(完整)
2836
python GUI工具之PyQt5模块,pyCharm 配置PyQt5可视化窗口
2819
python爬虫之feapder.AirSpider轻量爬虫案例:豆瓣
2613
next.js+react+typescript+antd+antd-mobile+axios+redux+sass react服务端渲染构建项目,从构建到发布,兼容pc+移动端
2480
分类专栏
前端
23篇
python
91篇
数据库
8篇
服务器与系统
4篇
正则
1篇
小游戏
1篇
最新评论
python内置模块subprocess 模块,创建和管理子进程
局外人LZ:
多谢支持
python内置模块subprocess 模块,创建和管理子进程
KevinDuangDuangDuang:
写得这么好的吗?
cygwin编译redis、nssm配置redis服务
老骚猫:
我命令行redis-server.exe redis.conf 启动不起来 可否提供一下正确的命令呢
cygwin编译redis、nssm配置redis服务
局外人LZ:
redis 7.2.0及以上版本编译好的redis-server是有问题的,通过命令是可以启动
cygwin编译redis、nssm配置redis服务
老骚猫:
redis 7.2.0及以上版本 在cygwin下编译的 目前无法启动 玛法您测试一下看看
您愿意向朋友推荐“博客详情页”吗?
强烈不推荐
不推荐
一般般
推荐
强烈推荐
提交
最新文章
node之vm2库,js沙盒环境
node之express模块,Web 应用程序框架
AST技术的V8引擎与babel库
2024年2篇
2023年115篇
2021年6篇
目录
目录
分类专栏
前端
23篇
python
91篇
数据库
8篇
服务器与系统
4篇
正则
1篇
小游戏
1篇
目录
评论 2
被折叠的 条评论
为什么被折叠?
到【灌水乐园】发言
查看更多评论
添加红包
祝福语
请填写红包祝福语或标题
红包数量
红包个数最小为10个
红包总金额
红包金额最低5元
余额支付
当前余额3.43元
前往充值 >
需支付:10.00元
取消
确定
下一步
知道了
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝
规则
hope_wisdom 发出的红包
打赏作者
局外人LZ
你的鼓励将是我创作的最大动力
¥1
¥2
¥4
¥6
¥10
¥20
扫码支付:¥1
获取中
扫码支付
您的余额不足,请更换扫码支付或充值
打赏作者
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额
抵扣说明:
1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。 2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。
余额充值