{"id":19069565,"url":"https://github.com/srect/nextjs-blog","last_synced_at":"2025-09-07T14:31:19.166Z","repository":{"id":114172787,"uuid":"443293849","full_name":"sRect/nextjs-blog","owner":"sRect","description":"my blog","archived":false,"fork":false,"pushed_at":"2023-09-27T07:50:27.000Z","size":13017,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-08T03:13:43.097Z","etag":null,"topics":["antd-mobile","nextjs"],"latest_commit_sha":null,"homepage":"nextjs-blog-srect.vercel.app","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sRect.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-31T08:15:55.000Z","updated_at":"2024-04-25T10:46:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"eb69e757-a38a-4e22-881c-7f9b0d08da15","html_url":"https://github.com/sRect/nextjs-blog","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sRect/nextjs-blog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2Fnextjs-blog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2Fnextjs-blog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2Fnextjs-blog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2Fnextjs-blog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sRect","download_url":"https://codeload.github.com/sRect/nextjs-blog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2Fnextjs-blog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274047849,"owners_count":25213365,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-07T02:00:09.463Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["antd-mobile","nextjs"],"created_at":"2024-11-09T01:14:47.007Z","updated_at":"2025-09-07T14:31:17.813Z","avatar_url":"https://github.com/sRect.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## 使用 Next.js + Docker 打造一个属于你的私人博客\n\n### 1. Next.js 简介\n\n\u003e [The React Framework for Production] Next.js gives you the best developer experience with all the features you need for production: hybrid static \u0026 server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.\n\n一顿牛皮下来，就是 Next.js 在生产和开发环境下都能带给你最佳体验，开箱体验，无需任何配置\n\n### 2. 为什么选择 Next.js\n\n\u003e 老表，咋回事哦，vue 不能满足了吗，搞这玩意干嘛，[vuepress](https://www.vuepress.cn/) 写所谓的静态博客网站不香？香，还比这简单，但请容许我介绍完这个 Next.js\n\n![](public/images/nextjs-blog/gs.jpg)\n\n#### 2.1 那 Next.js 有哪些优点呢？\n\n- 图片优化\n- 支持国际化\n- 零配置\n- 支持 SSG + SSR\n- 文件系统路由\n- 优点太多...\n\n![](public/images/nextjs-blog/yd.jpg)\n\n#### 2.2 那 Next.js 比这 vue 和 react 造出来的单页面有何不同？\n\n1. vue 和 react 造出来的单页面应用 SEO 不友好，搜索引擎抓不到 html 的内容，内容都在 js 里\n2. vue 和 react 造出来的单页面首屏白屏时间过长，在不对项目 webpack 专门优化的情况下，那个 bundle.js 很大，严重影响体验\n\n**总结**：如果项目对 SEO 要求比较高，建议上 Next 或[Nuxt](https://www.nuxtjs.cn/)\n\n#### 2.3 Next.js 和传统的 php、jsp 有何区别？\n\n\u003e 简单了解\n\n1. 客户端渲染\n\n前后端分离，通过 ajax 进行数据交互，vue 和 react 就是这种模式\n\n2. 服务端模板渲染\n\nphp 和 jsp 是解析模板文件，将数据渲染到文件上，最后将模板文件变为 html，生成 html 返回给浏览器，前后端不用同一套代码\n\n3. 前后端同构渲染\n\n也是服务端生成 html 返回给浏览器，区别在于前后端会共用一部分组件代码逻辑，这部分代码既可以用于服务端，也可以用于客户端，而模板渲染是两套代码\n\n### 3. Next.js 主要 api 快速上手\n\n\u003e **注意**：Node.js 版本 12.22.0 起步\n\n#### 3.1 使用`create-next-app`脚手架创建项目\n\n```bash\nnpx create-next-app@latest\n# or\nyarn create next-app\n```\n\n#### 3.2 项目目录结构\n\n```\n│  .eslintrc.json\n│  .gitignore\n│  next.config.js     # next配置文件\n│  package.json\n│  README.md\n│  yarn.lock\n│\n├─pages               # 页面路由\n│  │  index.js\n│  │  _app.js\n│  │\n│  └─api              # api服务\n│          hello.js\n│\n├─public              # 静态资源\n│      favicon.ico\n│      vercel.svg\n│\n└─styles              # css样式\n        globals.css   # 全局样式\n        Home.module.css\n```\n\n#### 3.3 路由\n\n1. 文件系统路由\n\n- `/pages/index.js` 路径为 `/`\n- `/pages/posts/about.js` 路径为 `/posts/about`\n- `/pages/posts/[id].js` 动态路径为 `/posts/foo` 或者`/posts/bar` 等等\n\n2. Link 组件\n\n\u003e Link 组件会自动执行 prefetch 预加载\n\n```javascript\nimport Link from \"next/link\";\n\nexport default function Home() {\n  return (\n    \u003cLink href=\"/posts/about\"\u003e\n      \u003ca\u003eabout page\u003c/a\u003e\n    \u003c/Link\u003e\n  );\n}\n\n// 或者不用a标签，传参示例\n\u003cLink\n  href={{\n    pathname: \"/about\",\n    query: { name: \"test\" },\n  }}\n  passHref\n\u003e\n  \u003cp\u003eabout page\u003c/p\u003e\n\u003c/Link\u003e;\n```\n\n3. useRouter\n\n```javascript\nimport { useRouter } from \"next/router\";\nimport { useCallback, useEffect } from 'react';\n\nexport default List() {\n  const router = useRouter();\n\n  const gotoDetail = useCallback((data) =\u003e {\n    const { fileName: detailid } = data;\n\n    // https://www.nextjs.cn/docs/api-reference/next/router#with-url-object\n    router.push({\n      pathname: \"/posts/[detailid]\",\n      query: {\n        detailid,\n      },\n    });\n  }, []);\n\n  useEffect(() =\u003e {\n    // Prefetch the dashboard page\n    router.prefetch('/dashboard');\n  }, []);\n\n  return (\n    \u003cdiv\u003e\n      ...\n    \u003c/div\u003e\n  )\n}\n```\n\n4. 动态路由\n\n就是`/pages/posts/[id].js`这样的路由\n\n```javascript\nimport { getAllPostIds, getPostData } from \"@/lib/posts\";\n\nexport async function getStaticPaths() {\n  const allListData = await getAllPostIds();\n  const paths = allListData.map((item) =\u003e {\n    return {\n      params: { id: item.fileName },\n    };\n  });\n\n  return {\n    paths,\n    fallback: false,\n  };\n}\n\nexport async function getStaticProps({ params }) {\n  const postData = await getPostData(params.id);\n\n  return {\n    props: {\n      postData,\n    },\n  };\n}\n\nexport default function List({ postData }) {\n  // ...\n}\n```\n\n#### 3.4 Head 组件\n\n\u003e 用于自定义 head 标签内容\n\n```javascript\nimport Head from \"next/head\";\n\nexport default function Layout({ children }) {\n  return (\n    \u003cdiv\u003e\n      \u003cHead\u003e\n        \u003cmeta charSet=\"UTF-8\" /\u003e\n        \u003cmeta\n          name=\"viewport\"\n          content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\"\n        /\u003e\n        \u003cmeta name=\"keywords\" content=\"Next.js\" /\u003e\n        \u003cmeta name=\"description\" content=\"Next.js\" /\u003e\n        \u003ctitle\u003eNext.js\u003c/title\u003e\n      \u003c/Head\u003e\n      \u003cmain\u003e{children}\u003c/main\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n#### 3.5 Image 组件\n\n\u003e 使用适当的属性，可以大幅优化图像，提升页面渲染\n\n```javascript\nimport Image from \"next/image\";\n\nexport default function MyImg() {\n  return (\n    \u003cImage\n      className={styles.homeBgImg}\n      src={bgImg}\n      layout=\"fill\"\n      objectFit=\"cover\"\n      objectPosition=\"center\"\n      quality={65}\n      priority={true}\n      placeholder=\"blur\"\n      blurDataURL={DEFAULT_BASE64}\n      alt=\"img\"\n    /\u003e\n  );\n}\n```\n\n#### 3.6 Script 组件\n\n```javascript\nimport Script from \"next/script\";\n\nexport default function Home() {\n  return (\n    \u003c\u003e\n      \u003cScript\n        src=\"https://Jquery.js\"\n        onLoad={() =\u003e {\n          $.ajax({\n            // ...\n          });\n        }}\n      /\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n#### 3.7 CSS\n\n1. CSS Modules（已内置）\n2. Sass（已内置）\n3. styled-jsx（已内置）\n4. [styled-components](https://github.com/vercel/next.js/tree/canary/examples/with-styled-components)（需自行配置）\n5. [Tailwind CSS](https://tailwindcss.com/docs/installation/using-postcss)（需自行配置）\n\n#### 3.8 Next.js 的 3 种基本渲染方式\n\n1. Client-side Rendering\n\n\u003e 就是常见的前后端分离\n\n```javascript\nimport useSWR from \"swr\";\n\nconst fetcher = (url) =\u003e fetch(url).then((res) =\u003e res.json());\n\nfunction Profile() {\n  const { data, error } = useSWR(\"/api/user\", fetcher);\n\n  if (error) return \u003cdiv\u003efailed to load\u003c/div\u003e;\n  if (!data) return \u003cdiv\u003eloading...\u003c/div\u003e;\n  return \u003cdiv\u003ehello {data.name}!\u003c/div\u003e;\n}\n```\n\n2. Static Generation (Recommended)\n\n\u003e 一般以展示一些静态固定数据为主，打包的时候就直接生成，比如博客页面、固定营销页面、帮助文档等\n\n```javascript\nimport { getAllPostIds } from \"@/lib/posts\";\n\nexport async function getStaticProps() {\n  const allListData = await getAllPostIds();\n\n  return {\n    props: {\n      allListData,\n    },\n  };\n}\n\nexport default function List({ allListData }) {\n  // ...\n}\n```\n\n3. Server-side Rendering\n   \u003e 以动态数据为主，每次请求的时候都在服务端执行，对服务器压力比较大\n\n```javascript\nexport async function getServerSideProps(context) {\n  return {\n    props: {\n      list: [...]\n    }\n  }\n}\n\nexport default function List({ list }) {\n  // ...\n}\n```\n\n### 4. 部署\n\n#### 4.1 使用 Vercel 快速部署\n\n使用 github 账户注册登录[Vercel](https://vercel.com/)官网，授权访问该仓库，即可快速部署，部署完即可访问。还能看到部署日志。\n\n#### 4.2 部署到自己的服务器\n\n##### 4.2.1 在服务器上进行 docker 镜像制作，然后部署\n\n\u003e 这里服务器以 centos 为例\n\n###### 第 1 步：Dockerfile 文件\n\n1. 使用 Next.js[官方 Dockerfile](https://www.nextjs.cn/docs/deployment#docker-image)\n\n**注意**：如果使用官方 Dockerfile，比如在阿里云上进行部署，会遇到网络问题，下载某些包会很慢，跟你本地访问 github 官网一样，所以要设置国内镜像下载，速度就会变快\n\n```\n# Install dependencies only when needed\nFROM node:alpine AS deps\n# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.\nRUN apk add --no-cache libc6-compat\nWORKDIR /app\nCOPY package.json yarn.lock ./\nRUN yarn install --frozen-lockfile\n\n# Rebuild the source code only when needed\nFROM node:alpine AS builder\nWORKDIR /app\nCOPY . .\nCOPY --from=deps /app/node_modules ./node_modules\nRUN yarn build \u0026\u0026 yarn install --production --ignore-scripts --prefer-offline\n\n# Production image, copy all the files and run next\nFROM node:alpine AS runner\nWORKDIR /app\n\nENV NODE_ENV production\n\nRUN addgroup -g 1001 -S nodejs\nRUN adduser -S nextjs -u 1001\n\n# You only need to copy next.config.js if you are NOT using the default configuration\n# COPY --from=builder /app/next.config.js ./\nCOPY --from=builder /app/public ./public\nCOPY --from=builder --chown=nextjs:nodejs /app/.next ./.next\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY --from=builder /app/package.json ./package.json\n\nUSER nextjs\n\nEXPOSE 3000\n\nENV PORT 3000\n\n# Next.js collects completely anonymous telemetry data about general usage.\n# Learn more here: https://nextjs.org/telemetry\n# Uncomment the following line in case you want to disable telemetry.\n# ENV NEXT_TELEMETRY_DISABLED 1\n\nCMD [\"node_modules/.bin/next\", \"start\"]\n```\n\n2. 使用自己的 Dockerfile\n\n\u003e 这里可以对 Docker 进行多阶段构建，使打包出来的镜像体积变小\n\n```bash\n# 1. 构建基础镜像\nFROM alpine:3.15 AS base\n#纯净版镜像\n\nENV NODE_ENV=production \\\n  APP_PATH=/app\n\nWORKDIR $APP_PATH\n\n# 使用国内镜像，加速下面 apk add下载安装alpine不稳定情况\nRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories\n\n# 使用apk命令安装 nodejs 和 yarn\nRUN apk add --no-cache --update nodejs=16.13.1-r0 yarn=1.22.17-r0\n\n# 2. 基于基础镜像安装项目依赖\nFROM base AS install\n\nCOPY package.json yarn.lock ./\n\nRUN yarn install\n\n# 3. 基于基础镜像进行最终构建\nFROM base\n\n# 拷贝 上面生成的 node_modules 文件夹复制到最终的工作目录下\nCOPY --from=install $APP_PATH/node_modules ./node_modules\n\n# 拷贝当前目录下的所有文件(除了.dockerignore里排除的)，都拷贝到工作目录下\nCOPY . .\n\nRUN yarn build\n\nEXPOSE 3000\n\nCMD [\"yarn\", \"start\"]\n```\n\n###### 第 2 步： 将源代码搞到服务器上\n\n1. 使用`scp`命令手动将本地源代码上传至服务器\n\n```bash\nscp -r local_dir root@121.xxx.xxx.xxx:remote_dir\n```\n\n2. 或者在远程服务器上`wget`下载 github 源码，然后解压\n\n```bash\nwget https://github.com/xxx/main.zip -O main.zip \u0026\u0026 unzip main.zip -d .\n```\n\n3. 使用`xshell`工具上传文件到服务器\n\n上面 3 种方法都可以把本地文件传到服务器对应目录\n\n###### 第 3 步： docker 镜像制作\n\n\u003e 前提是安装好了 docker 并启动\n\n```bash\n# 切换都源码目录执行\ndocker image build -t blog-demo .\n```\n\n接着会看到命令行上正在执行镜像制作过程，顺利的话，就成功了\n\n![build](public/images/nextjs-blog/build.jpg)\n\n```bash\n# 不出意外，可以看到刚才制作的镜像\ndocker image ls\n```\n\n###### 第 4 步： 启动容器\n\n\u003e 前提要在服务器上开好安全组\n\n```\ndocker container run -d -p 80:3000 -it blog-demo\n# -d： 后台运行容器\n# -p: 前面80是本机服务器开放端口，后面3000是容器暴露出来的端口\n# --name：给容器命名\n```\n\n不出意外，容器成功运行。可以在浏览器里进行访问了。\n\n##### 4.2.2 使用`github actions`自动部署\n\n\u003e 上面手动步骤太麻烦了，需要解放双手\n\n这里也可以选择 dockerhub，注册好后，创建仓库，即可推送。阿里云的镜像容器服务，也需要提前开通准备好（命名空间+私人仓库）。\n\n###### 第 1 步：提前准备好\n\n- 容器登录账号+密码\n- 服务器的 HOST + 登录账户 + 密码\n- 阿里云或者 dockerhub 的镜像容器仓库\n\n###### 第 2 步：github 该仓库 Settings-\u003eSecrets 添加秘钥，即上面准备好的这 5 个\n\n\u003e 我这里买的是阿里云的屌丝 1 核 2G 机器，生产环境别这么玩，账号密码可能泄露\n\n![secrets](public/images/nextjs-blog/secrets.jpg)\n\n###### 第 3 步：项目根目录添加`github yml`配置文件\n\n\u003e .github/workflows/deploy.yml\n\n```bash\nname: Docker Image CI\n\non:\n  push: # push 时触发ci\n    branches: [main] # 作用于main分支\n  # pull_request:\n  #   branches: [main]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      # 拉取main分支代码\n      - name: Checkout\n        uses: actions/checkout@v2\n\n      # 制作docker镜像并推送到阿里云容器镜像服务\n      - name: build and push docker image\n        run: |\n          echo ${{ secrets.ALIYUN_DOCKER_PASSWORD }} | docker login registry.cn-hangzhou.aliyuncs.com --username ${{ secrets.ALIYUN_DOCKER_USERNAME }} --password-stdin\n\n          docker image build -t myblog:latest .\n          docker tag myblog registry.cn-hangzhou.aliyuncs.com/test-blog/myblog:latest\n          docker push registry.cn-hangzhou.aliyuncs.com/test-blog/myblog:latest\n          docker logout\n      # 登录远程服务器，拉取镜像，制作并重启容器\n      # https://github.com/marketplace/actions/remote-ssh-commands\n      - name: ssh remote deploy\n        uses: fifsky/ssh-action@master\n        with:\n          command: |\n            cd /\n            echo -e \"1.docker login start==\u003e\"\n            echo ${{ secrets.ALIYUN_DOCKER_PASSWORD }} | docker login registry.cn-hangzhou.aliyuncs.com --username ${{ secrets.ALIYUN_DOCKER_USERNAME }} --password-stdin\n\n            echo -e \"2.docker stop myblog container==\u003e\"\n            docker container stop myblog\n\n            echo -e \"3.docker conatainer rm==\u003e\"\n            docker container rm myblog\n\n            echo -e \"4.docker image rm==\u003e\"\n            docker image rm registry.cn-hangzhou.aliyuncs.com/test-blog/myblog:latest\n\n            echo -e \"5.docker pull==\u003e\"\n            docker pull registry.cn-hangzhou.aliyuncs.com/test-blog/myblog:latest\n\n            echo -e \"6.docker container create and start==\u003e\"\n            docker container run -d -p 80:3000 --name myblog registry.cn-hangzhou.aliyuncs.com/test-blog/myblog:latest\n\n            echo -e \"7.docker logout==\u003e\"\n            docker logout\n          host: ${{ secrets.HOST }}\n          user: ${{ secrets.USER }}\n          pass: ${{ secrets.PASSWORD }}\n\n```\n\n###### 第 4 步：提交代码，自动部署\n\n\u003e 不出意外，在仓库的 Actions 里看到一切 ok\n\n```\ngit add .\ngit commit -m \"chore: add github actions yml\"\ngit push -u origin main\n```\n\n![deploy](public/images/nextjs-blog/deploy.jpg)\n\n[当前博客点击预览](https://nextjs-blog-nine-delta-65.vercel.app/)\n\n![666](public/images/nextjs-blog/666.png)\n\n### 5. 参考资料\n\n1. [Next.js 官方文档](https://nextjs.org/docs/getting-started)\n\n2. [如何优化 node 项目的 docker 镜像](https://juejin.cn/post/6991689670027542564)\n\n3. [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html)\n\n4. [前后端同构和模板渲染的区别是什么呢？](https://www.zhihu.com/question/379598562)\n\n5. [手把手教你用 Github Actions 部署前端项目](https://juejin.cn/post/6950799922178310152)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrect%2Fnextjs-blog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsrect%2Fnextjs-blog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrect%2Fnextjs-blog/lists"}