• Next.js项目初始化(附gitHub地址)


    Next.js项目初始化

    1.脚手架搭建

    npx create-next-app@latest

    生成目录:
    image.png
    我生成的package.json:

    {
      "name": "nest-initial-demo",
      "version": "0.1.0",
      "private": true,
      "scripts": {
        "dev": "next dev",
        "build": "next build",
        "start": "next start",
        "lint": "next lint"
      },
      "dependencies": {
        "@types/node": "20.6.0",
        "@types/react": "18.2.21",
        "@types/react-dom": "18.2.7",
        "eslint": "8.49.0",
        "eslint-config-next": "13.4.19",
        "next": "13.4.19",
        "react": "18.2.0",
        "react-dom": "18.2.0",
        "typescript": "5.2.2"
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.样式依赖安装

    • npm i normalize.css --save
    • npm i sass --save
    • npm i classnames --save

    引入全局

    import "normalize.css";
    import "@/styles/globals.scss";
    import type { AppProps } from "next/app";
    
    export default function App({ Component, pageProps }: AppProps) {
      return <Component {...pageProps} />;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    tsconfig.json@引入配置
    image.png

    3.head配置:

    // 这个四个属性是必须的
    import { Html, Head, Main, NextScript } from "next/document";
    export default function Document() {
      return (
        <Html lang="en">
          <Head>
            <meta name="description" content="react next demo" />
            <link rel="icon" href="/favicon.ico" />
          </Head>
          <body className="hy-body">
            <Main />
            <NextScript />
          </body>
        </Html>
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.reduxjs/toolkit集成

    npm i next-redux-wrapper --save

    • 可以避免在访问服务器端渲染页面时store的重置
    • 该库可以将服务器端redux存的数据,同步一份到客户端上
    • 该库提供了HYDRATE调度操作
      • ➢ 当用户访问动态路由或后端渲染的页面时,会执行Hydration来保持两端数据状态一致
      • ➢ 比如:每次当用户打开使用了getStaticProps或getServerSideProps函数生成的页面时,HYDRATE将执行调度操作。

    npm i @reduxjs/toolkit react-redux --save

    image.png

    numberDemo

    import { createSlice } from "@reduxjs/toolkit";
    import { HYDRATE } from "next-redux-wrapper";
    
    const numberSlice = createSlice({
      name: "numberDemo",
      initialState: {
        counter: 100,
      },
      reducers: {
        // 默认参数就有类型提示了
        increment(state, action) {
          state.counter = action.payload + state.counter;
        },
      },
      extraReducers: (builder) => {
        builder.addCase(HYDRATE, (state, action: any) => {
          return {
            ...state, // 当前模块的state
            ...action.payload.home, // payload:rootSate
          };
        });
      },
    });
    export const { increment } = numberSlice.actions;
    export default numberSlice.reducer;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    extraReducer:添加更多额外reducer处理other action

    app集成
    import "normalize.css";
    import "@/styles/globals.scss";
    import type { AppProps } from "next/app";
    import Layout from "@/components/C-user/layout";
    import warpper from "../store";
    import { Provider } from "react-redux";
    
    export default function App({ Component, ...rest }: AppProps) {
      const { store, props } = warpper.useWrappedStore(rest);
      return (
        
    ); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    使用
    import { useDispatch, useSelector } from "react-redux";
    import { IAppDispatch, IAppState } from "../store";
    import { increment } from "@/store/C-demo/numberDemo";
    
    export default function Home() {
      const { numberDemo } = useSelector((rootState: IAppState) => {
        return {
          numberDemo: rootState.numberDemo.counter,
        };
      });
      const dispatch: IAppDispatch = useDispatch();
      function addNumber() {
        dispatch(increment(2));
      }
      return (
        
    { minHeight: "400px", background: "#DADAE5" }}> number:{numberDemo}
    ); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    requestDemo

    import { getHomeInfoData } from "@/service/user/index";
    import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
    import { HYDRATE } from "next-redux-wrapper";
    export interface IHomeInfo {
      banners?: any[];
      categorys?: any[];
      recommends?: any[];
      digitalData?: any;
    }
    interface IInitialState {
      requestDemoInfo: IHomeInfo;
    }
    const requestSlice = createSlice({
      name: "requestDemo",
      initialState: {
        requestDemoInfo: {},
      } as IInitialState,
      reducers: {
        // 默认参数就有类型提示了
        changeNavbarAction(state, action) {
          state.requestDemoInfo = action.payload;
        },
      },
      extraReducers: (builder) => {
        builder.addCase(HYDRATE, (state, action: any) => {
          return {
            ...state,
            ...action.payload.requestDemo, // hydration home模块数据
          };
        });
      },
    });
    // 异步的action
    export const fetchHomeInfoAction = createAsyncThunk(
      "fetchHomeInfoAction",
      async (payload: number, { dispatch }) => {
        // console.log("payload=>", payload);
        const res = await getHomeInfoData();
        dispatch(requestSlice.actions.changeNavbarAction(res.data));
      }
    );
    
    export const { changeNavbarAction } = requestSlice.actions;
    export default requestSlice.reducer;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    index使用

    import { useDispatch, useSelector } from "react-redux";
    import { IAppDispatch, IAppState, wrapper } from "@/store/index";
    import { increment } from "@/store/C-demo/numberDemo";
    import { GetServerSideProps } from "next";
    import { getHomeInfoData } from "@/service/user";
    import { useCallback, useEffect, useState } from "react";
    import { fetchHomeInfoAction } from "@/store/C-demo/requestDemo";
    interface listRoot {
      id: number;
      productId: number;
      picId: number;
      backendPicId: number;
      addTime: number;
      position: number;
      type: number;
      url: string;
      bannerExtJson: any;
      isSetTime: number;
      beginTime: number;
      endTime: any;
      picStr: string;
      backendPicStr: string;
    }
    
    export default function Home() {
      const { numberDemo } = useSelector((rootState: IAppState) => {
        return {
          numberDemo: rootState.numberDemo.counter,
        };
      });
      const { requestDemo } = useSelector((rootState: IAppState) => {
        return {
          requestDemo: rootState.requestDemo.requestDemoInfo,
        };
      });
      const dispatch: IAppDispatch = useDispatch();
      function addNumber() {
        dispatch(increment(2));
      }
      const [bannersList, setBannersList] = useState<listRoot[]>([]);
      const getUserCb = useCallback(() => getHomeInfoData(), [numberDemo]);
      useEffect(() => {
        getUserCb()
          .then((res) => {
            console.log(res);
            if (res.data.banners) setBannersList(res.data.banners);
          })
          .catch((err) => {
            console.log(err);
          });
      }, [getUserCb]);
      return (
        <div style={{ minHeight: "400px", background: "#DADAE5" }}>
          <button onClick={addNumber}>add2</button>
          number:{numberDemo}
          <div>
            直接的List:
            <ul>
              {bannersList.map((item) => {
                return <li key={item.id}>{item.picStr}</li>;
              })}
            </ul>
            redux的List:
            {requestDemo?.banners && (
              <ul>
                {requestDemo?.banners.map((item) => {
                  return <li key={item.id}>{item.picStr}</li>;
                })}
              </ul>
            )}
          </div>
        </div>
      );
    }
    
    export const getServerSideProps: GetServerSideProps =
      wrapper.getServerSideProps(function (store) {
        return async (context) => {
          await store.dispatch(fetchHomeInfoAction(1));
          return {
            props: {},
          };
        };
      });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    集成网络请求request封装

    npm install axios

    src建立service文件:
    image.png

    4.集成ant-design

    npm install antd --save
    npm install @types/antd --save-dev

    _app全局导入:
    import “antd/dist/reset.css”;
    页面调用:
    import { Button } from “antd”;

    Github地址

    https://github.com/wzz778/nest-initial-demo

  • 相关阅读:
    NV040D语音芯片:智能破壁机,悦享美好生活
    如何使用 Fail2ban 防止对 Linux 的暴力攻击?
    Allegro导入导出设计数据操作指导
    进销存仓库管理系统:规范数据、流程与管理
    v-model和v-bind的区别?
    Ubuntu中USB端口与外设绑定,ROS读取IMU模块数据
    RabbitMQ------死信队列(消息超时、达到最大长度、消费拒绝)(六)
    5. 二叉树定义、【满二叉树、完全二叉树、二叉排序树、平衡二叉树】、二叉树的性质、二叉树的存储结构
    Java.Integer.bitCount(int)源码解析
    vue页面动态生成二维码,扫描二维码跳转到微信小程序,同时传递参数
  • 原文地址:https://blog.csdn.net/Azbtt/article/details/132998974