# nuxt_case **Repository Path**: ifercarly/nuxt_case ## Basic Information - **Project Name**: nuxt_case - **Description**: Nuxt 案例 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-10-10 - **Last Updated**: 2024-10-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: SSR ## README ## 00. 资源准备 仓库地址:https://github.com/gothinkster/realworld 在线示例:https://demo.realworld.io/#/ 接口文档:https://github.com/gothinkster/realworld/tree/master/api 页面模板:https://github.com/gothinkster/realworld-starter-kit/blob/master/FRONTEND_INSTRUCTIONS.md ## 01. 创建项目 ```bash npm init -y npm i nuxt ``` `package.json` ```json { "scripts": { "dev": "nuxt" }, } ``` `pages/index.vue` ```html ``` ## 02. 准备样式 ```html {{ HEAD }} {{ APP }} ``` ## 03. 布局组件 `nuxt.config.js` ```js module.exports = { router: { extendRoutes(routes, resolve) { // 清除 Nuxt 基于 pages 生成的路由表 routes.splice(0) routes.push( ...[ { path: '/', component: resolve(__dirname, 'pages/layout'), children: [ { path: '', name: 'home', component: resolve(__dirname, 'pages/home'), }, ], }, ] ) }, }, } ``` `pages/layout/index.vue` ```vue ``` `pages/home/index.vue` ```html ``` ## 04. 登录注册界面 `pages/login/index.vue` ```html ``` `nuxt.config.js` ```js module.exports = { router: { extendRoutes(routes, resolve) { // 清除 Nuxt 基于 pages 生成的路由表 routes.splice(0) routes.push( ...[ { path: '/', component: resolve(__dirname, 'pages/layout'), children: [ { path: '', name: 'home', component: resolve(__dirname, 'pages/home'), }, { path: '/login', name: 'login', component: resolve(__dirname, 'pages/login'), }, { path: '/register', name: 'register', component: resolve(__dirname, 'pages/login'), }, ], }, ] ) }, }, } ``` ## 05. 其他界面处理 `pages/profile/index.vue` ```html ``` `pages/settings/index.vue` ```html ``` `pages/editor/index.vue` ```html ``` `pages/article/index.vue` ```html ``` `nuxt.config.js` ```js module.exports = { router: { extendRoutes(routes, resolve) { // 清除 Nuxt 基于 pages 生成的路由表 routes.splice(0) routes.push( ...[ { path: '/', component: resolve(__dirname, 'pages/layout'), children: [ { path: '', name: 'home', component: resolve(__dirname, 'pages/home'), }, { path: '/login', name: 'login', component: resolve(__dirname, 'pages/login'), }, { path: '/register', name: 'register', component: resolve(__dirname, 'pages/login'), }, { path: '/profile/:username', name: 'profile', component: resolve(__dirname, 'pages/profile'), }, { path: '/settings', name: 'settings', component: resolve(__dirname, 'pages/settings'), }, { path: '/editor', name: 'editor', component: resolve(__dirname, 'pages/editor'), }, { path: '/article/:slug', name: 'article', component: resolve(__dirname, 'pages/article'), }, ], }, ] ) }, }, } ``` ## 06. 处理顶部导航链接 `pages/layout/index.vue` ```html ``` ## 07. 处理导航链接高亮 `nuxt.config.js` ```js module.exports = { router: { linkActiveClass: 'active', }, } ``` `pages/layout/index.vue` ```html ``` ## 08. 封装请求模块 `utils/request.js` ```js import axios from 'axios' const request = axios.create({ baseURL: 'https://conduit.productionready.io', }) export default request ``` ## 09. 基本登录功能 `pages/login/index.vue` ```html ``` ## 10. 封装请求方法 `pages/login/index.vue` ```html ``` `api/user.js` ```js import request from '@/utils/request' /** * 登录 * @param {Object} data * @returns Promise */ export const login = (data) => { return request({ method: 'POST', url: '/api/users/login', data, }) } /** * 注册 * @param {Object} data * @returns Promise */ export const register = (data) => { return request({ method: 'POST', url: '/api/users', data, }) } ``` ## 11. 表单验证 `pages/login/index.vue` ```html ``` ## 12. 错误处理 `pages/login/index.vue` ```html ``` ## 13. 用户注册 `pages/login/index.vue` ```html ``` ## 14. 将登录状态存储到容器中 [参考链接](https://www.nuxtjs.cn/examples/auth-external-jwt) `store/index.js` ```js export const state = () => ({ // 登录用户的信息 user: null, }) export const mutations = { setUser(state, data) { state.user = data }, } export const actions = {} ``` `pages/login/index.vue` ```html ``` ## 15. 登录状态持久化 `src/store/index.js` ```js const cookieParser = process.server ? require('cookieparser') : undefined export const state = () => ({ // 登录用户的信息 user: null, }) export const mutations = { setUser(state, data) { state.user = data }, } export const actions = { // 服务端渲染期间自动调用 nuxtServerInit({ commit }, { req }) { let user = null if (req.headers.cookie) { // 把请求头中的 cookie 解析成字符串 const parsed = cookieParser.parse(req.headers.cookie) try { // parsed.user => 是一个字符串 user = JSON.parse(parsed.user) } catch (err) {} } commit('setUser', user) }, } ``` `pages/login/index.vue` ```html ``` ## 16. 处理导航栏展示状态 ```html ``` ## 17. 处理页面访问权限 `middleware/authenticated.js`,没有登录不允许访问需要用户信息的界面 ```js // 校验是否登录的中间件 export default function({ store, redirect }) { if (!store.state.user) { return redirect('/login') } } ``` `middleware/notAuthenticated.js`,已经登陆了,再访问登录/注册页,就跳转到首页 ```js export default function({ store, redirect }) { // 已登录就跳转到首页,一般适用于登录组件 if (store.state.user) { return redirect('/') } } ``` ## 18. 展示公共文章列表 `api/article.js` ```js import request from '@/utils/request' /** * 获取公共文章列表 * @param {Object} params * @returns Promise */ export const getArticles = (params) => { return request({ method: 'GET', url: '/api/articles', params, }) } ``` `pages/home/index.vue` ```html ``` ## 19. 分页参数的使用 `pages/home/index.vue` ```html ``` ## 20. 页码处理 `pages/home/index.vue` ```html ``` ## 21. 展示文章标签列表 `api/tag.js` ```js import request from '@/utils/request' /** * 获取文章标签列表 * @returns Promise */ export const getTags = () => { return request({ method: 'GET', url: '/api/tags', }) } ``` `pages/home/index.vue` ```html ``` ## 22. 改成并行请求 `pages/home/index.vue` ```html ``` ## 23. 处理标签链接和数据 `pages/home/index.vue` ```html ``` ## 24. 处理导航栏-展示状态处理 `pages/home/index.vue` ```html ``` ## 25. 处理导航栏-标签高亮及链接 `pages/home/index.vue` ```html ``` ## 26. 处理导航栏-展示用户关注的文章列表 `api/article.js` ```js import request from '@/utils/request' /** * 获取公共文章列表 * @param {Object} params * @returns Promise */ export const getArticles = (params) => { return request({ method: 'GET', url: '/api/articles', params, }) } /** * 获取关注的用户文章列表 * @param {*} params * @returns */ export const getFeedArticles = (params) => { return request({ method: 'GET', url: '/api/articles/feed', headers: { Authorization: `Token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Inh4eEBnbWFpbC5jb20iLCJ1c2VybmFtZSI6Inh4eCIsInBhc3N3b3JkIjoiJDJhJDEwJEtiZ2pleVIvTUZGSWpYNE5ScEh6V2VBODlON0pCdFdkZFN5U0RLYVdXbDc1b0kyVzZxTzJHIiwiYmlvIjpudWxsLCJpbWFnZSI6Imh0dHBzOi8vZ3cuYWxpcGF5b2JqZWN0cy5jb20vbWRuL3Byb2RfcmVzb3UvYWZ0cy9pbWcvQSpPd1pXUTY4elNUTUFBQUFBQUFBQUFBQmtBUlFuQVEiLCJpYXQiOjE2MzMwOTM2NDQsImV4cCI6MTYzODI3NzY0NH0.3dU8KaH9FaTxaof3Fu49_Nkj7SUm55Jc_98obg3pESg`, }, params, }) } ``` `pages/home/index.vue` ```html ``` ## 27. 统一设置用户 Token `plugins/request.js` ```js import axios from 'axios' export const request = axios.create({ baseURL: 'https://conduit.productionready.io', }) // 通过插件机制获取到上下文对象(query、params、req、res、app、store) // 插件导出函数必须作为 default 成员 export default ({ store }) => { request.interceptors.request.use( function(config) { const { user } = store.state if (user && user.token) { config.headers.Authorization = `Token ${user.token}` } return config }, function(error) { return Promise.reject(error) } ) } ``` `nuxt.config.js` ```js module.exports = { // 注册插件 plugins: ['~/plugins/request.js'], } ``` ## 28. 时间格式处理 `plugins/dayjs.js` ```js import Vue from 'vue' import dayjs from 'dayjs' Vue.filter('date', (value, format = 'YYYY-MM-DD HH:mm:ss') => { return dayjs(value).format(format) }) ``` `nuxt.config.js` ```js module.exports = { // 注册插件 plugins: ['~/plugins/request.js', '~/plugins/dayjs.js'], } ``` ## 29. 文章点赞 ```html ``` ## 30. 文章详情-展示基本信息 ## 31. 文章详情-把 Markdown 转为 HTML ## 32. 文章详情-展示文章作者相关信息 `article/components/article-meta.vue` ```html ``` ## 33. 设置页面 meta 优化 seo ```js ``` ## 34. 通过客户端渲染展示评论列表 `article/components/article-comments.vue` ```html ``` ## 35. Nuxt 打包部署 ```bash npm run build npm run start ``` ## 36. 最简单的部署方式 `nuxt.config.js` ```js module.exports = { server: { host: '0.0.0.0', // 也能对外提供访问服务 port: 3000, }, } ``` 压缩 `.nuxt`、`static`、`nuxt.config.js`、`package.json`、`package-lock.json` ```bash ssh root@xx.xx.xx.xx mkdir realworld cd realworld pwd # 查看目录 exit # 退出服务器 scp .\realworld.zip root@xx.xx.xx.xx:目录地址 # 上传 ssh root@xx.xx.xx.xx # 重新连接 cd realworld # 进入,能看到刚刚上传的压缩包 unzip realworld.zip ls ls -a # 查看隐藏目录 npm i npm run start # 公网 IP + 端口号进行访问 ``` ## 37. 使用 PM2 启动 Node 服务 ```bash npm i -g pm2 pm2 start npm -- start # 给 npm 传参 ``` ## 38. 自动化部署 CI/CD 服务 Jenkins Gitlab CI Github Actions Travis CI Circle CI