# nuxt-project **Repository Path**: wangzhaoyv/nuxt-project ## Basic Information - **Project Name**: nuxt-project - **Description**: 使用nuxtjs做的服务端渲染还有eggjs做的后台,开发的一个学习项目 - **Primary Language**: JavaScript - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 2 - **Created**: 2022-01-20 - **Last Updated**: 2024-12-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: SSR, eggjs, nuxtjs, 切片上传 ## README # 项目创建与基本配置 ## nuxtjs app 创建 ``` npx create-nuxt-app <项目名> ``` [nuxtjs 中文官网](https://www.nuxtjs.cn/guide/installation) ## eggjs app 创建 ``` mkdir egg-example && cd egg-example npm init egg --type=simple npm i ``` [eggjs 官网](https://eggjs.org/zh-cn/intro/quickstart.html) ## mongodb + 网上查找安装运行 + 将目录下的`mongodb.bat`文件内容`dbpath`后面的路径指向数据库地址 + 双击即可开启数据库 ## 项目主要目录介绍 ### nuxtjs app - components : 项目组件 - pages : 页面 - plugins : 插件 - static : 静态资源 - test : 测试文件 - nuxt.config.js : 主要配置文件 - package.json : 包管理文件 ### eggjs app ``` egg-project ├── package.json ├── app.js (可选) ├── agent.js (可选) ├── app 接口等编写的主要文件夹 | ├── router.js 路由(接口定义位置,与控制器组合为一个完整接口) │ ├── controller 控制器(接口位置) │ | └── home.js │ ├── service (可选) │ | └── user.js │ ├── middleware (可选) │ | └── response_time.js │ ├── schedule (可选) │ | └── my_task.js │ ├── public (可选) │ | └── reset.css │ ├── view (可选) │ | └── home.tpl │ └── extend (可选) │ ├── helper.js (可选) │ ├── request.js (可选) │ ├── response.js (可选) │ ├── context.js (可选) │ ├── application.js (可选) │ └── agent.js (可选) ├── config 配置文件 | ├── plugin.js 插件配置文件 | ├── config.default.js 默认配置文件,返回的对象可以使用`this.app.config`获取 │ ├── config.prod.js | ├── config.test.js (可选) | ├── config.local.js (可选) | └── config.unittest.js (可选) └── test 测试文件 ├── middleware | └── response_time.test.js └── controller └── home.test.js ``` ## 项目配置 ### 代理配置【只需要在 nuxtjs 项目中配置代理即可】 **文件:nuxt.config.js** ```javascript export default { modules: ["@nuxtjs/proxy"], proxy: { "/api/": { target: "http://localhost:7001", screen: false, pathRewrite: { "^/api": "", }, }, }, }; ``` 【1】.如果运行的时候报`@nuxtjs/proxy`不存在? ``` npm install @nuxtjs/proxy ``` 【2】.配置后,访问还是显示未找到页面? > 尝试重启前端项目 【3】.配置后,发现访问显示 404,与【2】情况不一样? > `"^/api": ""`这里是`^/`,如果没有问题,在检查一下配置 ### axios 拦截配置 ```javascript import Vue from "vue"; import axios from "axios"; const $http = axios.create({ baseURL: "/api", }); // 请求拦截,所有请求前会通过这里,这里可以为请求添加参数 $http.interceptors.request.use((config) => { const token = localStorage.getItem("token"); if (token) { config.headers.common["Authorization"] = "Bearer " + token; } return config; }); // 响应拦截,所以请求响应都会走这里,这里可以处理返回的参数 $http.interceptors.response.use((response) => { const { data } = response; return data; }); Vue.prototype.$http = $http; ``` ### ts 为 Vue 拓展属性添加类型注解 根目录下添加 `shims-vue.d.ts` 文件,内容如下 ```javascript // 拓展Vue原型方法 import { AxiosInstance } from "axios"; declare module "vue/types/vue" { interface Vue { $http: AxiosInstance; } } ``` ### 配置别名(alise) 条件: - ts 项目: 配置别名在`tsconfig.json`中 - js 项目: 配置别名在`nuxt.config.js`文件中 **tsconfig.json** ```json { "compilerOptions": { "paths": { "@type/*": ["./type/*"], "@comp/*": ["./components/*"] } } } ``` **nuxt.config.js** ```javascript export default { alias: { // ts type "@type": "./type/*", }, }; ``` ### axios 读取请求进度 ```javascript this.$http.post("/uploadfile", form, { onUploadProgress: (progress: any) => { this.uploadProgress = Number( ((progress.loaded / progress.total) * 100).toFixed(2) ); }, }); ``` ### 取消 post 请求 csrf 拦截【只需要在 eggjs 项目中配置即可】 > 这个拦截是必要的,但是项目在开发前期,没有使用登录系统的时候,这个拦截会让我们无法测试 post 请求,所以我们暂时取消 > **文件:config.default.js** ```javascript return { security: { csrf: { enable: false, }, }, }; ``` ### 配置 mongodb **文件:config.default.js** ```javascript return { mongoose: { client: { url: "mongodb://127.0.0.1:<端口号:默认端口号为27017>/", options: {}, }, }, }; ``` ### eggjs 开启文件上传 ```javascript config.multipart = { mode: "file", whitelist: () => true, }; ``` ### eggjs 中间键编写与使用 **文件创建** 在`app`目录下添加文件夹`middleware`,创建文件`jwt.js`,后期访问这个中间键使用`app.middleware.jwt` **编写** ```javascript module.exports = ({ app }) => { return async function verify(ctx, next) { // 搞事情 await next(); }; }; ``` > 注意点:`next`方法一定要记得`await`,否则相当于这个请求结束了,但是没有返回,前端接口报 404 错误 **使用** ```javascript module.exports = (app) => { const { router, controller } = app; const jwt = app.middleware.jwt({ app }); router.post("/uploadfile", jwt, controller.util.uploadFile); }; ``` # 功能相关 ## 文件读取与上传校验 ### 浏览器读取 ·类文件对象· 并转为16进制 ```typescript function blobToString(blob: Blob) { return new Promise(resolve => { // 创建读取文件的对象 const reader = new FileReader(); // 二进制文件加载完成后 reader.onload = function () { const result = reader.result as string; // 对读取到的内容进行处理 const res = result.split("") .map(v => v.charCodeAt(0)) .map(v => v.toString(16).toUpperCase()) .join(" "); resolve(res); } // 读取二进制文件 reader.readAsBinaryString(blob); }) } ``` ### 图片类型的16进制的不同 #### 判断是否为gif ```typescript async function isGif(blob: Blob) { const res = await blobToString(blob.slice(0, 6)) return (res === "47 49 46 38 39 61") || (res === "47 49 46 38 37 61"); } ``` > gif图片特点是16进制数据前6位为`47 49 46 38 39 61`或者`47 49 46 38 37 61` #### 判断是否为png ```typescript async function isPng(blob: Blob) { const res = await blobToString(blob.slice(0, 8)) console.log(res) return (res == "89 50 4E 47 0D 0A 1A 0A") || (res === "89 50 4E 47 D A 1A A"); } ``` > png图片特点是16进制数据前8位为`89 50 4E 47 0D 0A 1A 0A`, 因为有时候读出来开头`0`省略,所以添加了后面这个 #### 判断是否为jpg ```typescript async function isJpg(blob: Blob) { let len = blob.size; const start = await blobToString(blob.slice(0, 2)) const tail = await blobToString(blob.slice(-2, len)) return (start === "FF D8") && (tail === "FF D9"); } ``` > jpg图片特点是16进制数据前两位为`FF D8`并且后两位为`FF D9` #### 总结 + 其实在二进制文件中还保存着图片的大小信息,所以其实可以通过这个来进行图片的大小判断 + 通过十六进制的判断,可以防止别人上传的文件不符合要求,比如:将pdf文件后缀改成png文件然后上传 + 当然也可以用这个方法判断其他文件上传时候正常 ## 切片上传 [切片上传](https://juejin.cn/post/7055255840088915976/) ## 拖拽功能 | 事件名称 | 触发条件 | | --------- | ------------------------------------------------------------ | | drag | 拖动元素或选择文本时触发此事件 | | dragstart | 当用户开始拖动元素或选择文本时触发此事件 | | dragend | 当拖动操作结束时(释放鼠标按钮或按下退出键),会触发此事件 | | dragenter | 当拖动的元素或选择文本输入有效的放置目标时,会触发此事件 | | dragexit | 当元素不再是拖动操作的选择目标时触发此事件 | | dragover | 当将元素或文本选择拖动到有效放置目标(每几百毫秒)上时,会触发此事件 | | dragleave | 当拖动的元素或文本选择离开有效的放置目标时,会触发此事件 | | drop | 拖拽物品放下时触发 | [MDN Drag](https://developer.mozilla.org/zh-CN/docs/Web/API/Document/drag_event) [MDN dragEvent](https://developer.mozilla.org/zh-CN/docs/Web/API/DragEvent) # 编写技巧 ## 基础 Controller 问题: 我们返回的内容应该保持一致性,这样前端处理时才会更加方便 > 我们可以编写一个公共的 Controller,并且提供一些公共的方法,然后让其他的 Controller 集成这个公共的,保证返回的一致性 **basic.js** ```javascript "use strict"; const Controller = require("egg").Controller; class BasicController extends Controller { success(data) { const { ctx } = this; ctx.body = { code: 0, data, }; } error(code = -1, message, errors) { const { ctx } = this; ctx.body = { code, message, errors, }; } message(message) { const { ctx } = this; ctx.body = { code: 0, message, }; } } module.exports = BasicController; ``` **user.js** ```javascript "use strict"; const BasicController = require("./basic"); const registerRule = { password: { type: "string" }, username: { type: "string" }, email: { type: "email" }, captcha: { type: "string" }, }; class UserController extends BasicController { async register() { const num = parseInt(Math.random() * 10, 10); if (num % 2 === 0) { return this.error("参数校验失败", -1, "其他消息"); } this.success("注册成功"); } } module.exports = UserController; ```