个人博客项目(重难点)
token 过期怎么办?
后端返回的时候不仅返回 token,而且返回一个 refres_token 字段用于刷新 token。token 存储的时间较短,refres_token 存储的时间较长。如果 token 过期,那么就使用 refresh_token 去访问然后返回新的 token 然后再请求。
错误处理
对一些常见状态码错误做了处理,比如 400(参数错误),401(没有权限),404(找不到),403(禁止访问),412(已存在)等。
路径别名
使用 module-alias
来在 koa 中起别名,在package.json
中设置_moduleAliases
字段,在里面设置别名
接口调用频率限制
使用koa-ratelimit
来限制接口的调用频率,给服务器减轻一些压力
路由自动获取
传统做法每次都要导入路由,很麻烦,所以使用require-directory
来自动加载路由,省去了没必要的导入路由这个操作。
const requireDirectory = require('require-directory');
...
const apiDirectory = `${process.cwd()}/app/api`
reuqireDirectory(module, apiDirectory)
sequelize
连接到数据库
- 连接数据库
- 设置数据库的全局配置。可把 Sequelize 的实例 sequelize 看做 Mysql 中的 Database
建立模型
Model 是由 sequelize.define()方法定义用于映射数据模型和数据表之间的关系的对象模型,其实就是 Mysql 的一张数据表
同步到 Mysql 中
推荐将所有的模型定义在单文件中以实现模块化,并通过 sequelize.import() 方法把模块导入到 index.js 中统一管理。
sequelize.sync()将模型同步到数据库的三种方法和区别?
- sequelize.sync():
- 动态同步
- 修改同名数据表结构,以使用模型
- sequelize.sync({alter: true})
- 强制同步
- 删除同名数据表后同步,谨慎使用,会导致数据丢失
- sequelize.sync({force: true})
同步成功后,把 sequelize.sync 注释掉,因为,我们再次重启应用后不需要再重新同步
服务器部署
配置 nginx,由于没有系统学习过 nginx,所以配置遇到一些问题也要在查询上花费很多时间。使用宝塔遇到数据库的问题。
React UI 组件库
总体思路是创建一个组件之前根据组件的行为确定一部分组件的必备属性和方法,然后写上处理逻辑和基本的 ui,使用 classnames 来组合类名。可以方便地动态确定类名。后面再添加需求完善组件。
使用 React.forwardRef 来做 ref 转发,因为我们是 ui 组件库,复用率很高,很多时候需要使用 ref,但是组件又不能使用 ref,所以需要这个做一个中间层
属性冲突
可能会和原生 dom 节点上的属性冲突,那么需要 Omit 来忽略掉。
Input 组件
主要就是考虑到一些前缀,icon,后缀等等。
AutoComplete 组件
大致和 Input 组件相同,只是多了展示根据搜索展示的内容。使用防抖(一段时间内只执行一次)来避免产生太多次太频繁的请求。
Menu 组件
主要属性:
- defaultIndex: 默认 active 的菜单项的索引值
- mode:’horizontal’ | ‘vertical’ 分为横向和纵向两种模式
- onSelect: 点击菜单项触发的回调函数
- 设置子菜单项的默认打开,只有在纵向模式下生效
通过 context 传递给 Menu.Item 和 Menu.SubMenu 组件一些值,index,onSelect,mode, defaultOpenSubmenus,用于指定 menu 的模式,和是否需要展开 子 menu。
通过 React.children.map 遍历 Menu 中传递的 Item 或者 SubMenu。通过 type 获取子组件类型,是 MenuItem 还是 SubMenu。使用 React.cloneElement 把一些 props(index 下标)添加到子组件上。
最后通过 TypeScript 交叉类型将 MenuItem 和 SubMenu 挂载到 Menu 上,这样就可以只引入 Menu 组件然后通过 Menu.Item 和 Menu.SubMenu 使用了。
Tab 组件
与 Menu 组件类似。
主要属性:
- defaultIndex: 默认处于哪个 tab 下
- tabStyle: ‘underline’ | ‘outline’ 两种模式
- onSelect: 选择的时候处理函数
同样是使用 React.children.map 来遍历子组件使用 React.cloneElement 来把一些 props 添加在子组件上
组后通过 Typescript 联合类型将 TabItem 挂载到 Tab 上,然后就可以只引入 Tab 组件而使用 Tab 和 TabItem 两个组件了。
Transition 组件
主要就是使用 react-transition-group 来包装,设置了四个方向的过渡动画——zoom-in-left、zoom-in-right、zoom-in-top、zoom-in-bottom。
upload 组件
主要属性:
- actions:文件上传地址
- defaultFileList:文件列表
- onPregress:上传过程中的生命周期回调函数
- onSuccess:成功的生命周期回调函数
- onError:出现错误的生命周期函数
- beforeUpload:上传文件前的生命周期函数,用于文件的验证或者转换等等
- onChange:成功或者失败后的回调
- onRemove:移除文件列表
- headers: 额外的请求头
- name: 用户自定义文件名
- data:额外的参数
- withCredentials:是否携带 cookie
- accept:筛选可用的文件类型
- multiple:允许多个文件上传
- drag:是否拖动上传
文件列表包含的一些属性:
- uid
- size
- name
- percentage:上传进度百分比
- raw:原文件
- response:响应
- error:错误
我是使用 input 标签进行文件上传,但是我又不想用 input 标签的样式,于是隐藏了 input 标签的样式,使用 useRef 获取 input 标签的 ref 然后模拟点击上传。
上传文件就是使用 FormData 的 append 添加文件然后进行上传。使用 axios 的 onUploadProgress 监听文件上传进度。使用percentage = Math.round((e.loaded * 100) / e.total) || 0
获取进度。使用 Progress 组件来展示进度条。
拖拽上传其实就是利用 DragEvent。使用 e.dataTransfer.files 获取拖拽过的文件,然后利用父组件传过来的方法进行上传即可。
使用组件库时,怎么引用你的声明文件?
package.json 的 types 字段,因为我打包后给每个文件都生成了 .d.ts 文件,所以 types 这里写的是 dist/index.d.ts 入口文件的 .d.ts 文件。
如何做版本管理?
semver 规范(语义化版本)。semver 有两个概念:固定版本(0.1.2、1.2.3-bata.0)和范围版本(1.2.3-2.3.4、1.x、^0.2、>1.4)。
- 版本格式:
- 主版本号:当做了不兼容的 API 修改
- 此版本号:当做了向下兼容的功能性新增
- 修订号:当做了向下兼容的问题修正
- 先行版本号及版本编译信息可以加到”主版本号.此版本号.修订号”的后面
- 先行版本号可以作为发布正式版之前的版本,格式是在修订版本号后面加上一个连接号,再加上一连串以 (.) 分割的表示符,标识符可以由英文、数字和连接号([0-9A-Za-z-])组成
1.0.0-alpha
1.0.0-alpha.1
1.0.0-0.3.7
1.0.0-x.7.z.92
- 先行版本号可以作为发布正式版之前的版本,格式是在修订版本号后面加上一个连接号,再加上一连串以 (.) 分割的表示符,标识符可以由英文、数字和连接号([0-9A-Za-z-])组成
- alpha:内部测试版,一般不会向外部发布
- beta:测试测试版,这个阶段的版本会一直加入新功能。在 Alpha 版之后推出
- 定义依赖版本号
^
:表示同一主版本号中,不小于指定版本号的版本号——^2.2.1
对应主版本号为 2,不小于2.2.1
的版本号,当该依赖有最新版本时,npm install 会安装新的依赖~
:表示同一主版本号和此版本号中,不小于指定版本号的版本号>
、<
、=
、>=
、<=
、-
:用来指定一个版本号范围,注意使用-
时,必须两边有空格||
:表示或x
、X
、*
:表示通配符;*
对应所有的版本号,3.x
对应所有主版本号为 3 的版本号
当主版本号升级后,次版本号和修订号需要重置为 0,此版本号进行升级后,修订版本要重置为 0
在 npm install 后,会生成一个 package-lock.json 文件用于保存当前安装依赖的各种来源及版本号。
package-lock.json 的变动规则:
- 当在 install dependency 的指定版本时,会自动更新 packge-lock.json 文件中该 dependency 的 version 到 指定的 version。
- 当在 install dependency 的范围版本时,当前的 version 低于 or 等于 package-lock.json 中对应的 dependency 的 version 时,会安装 package-loack.json 中的 version
- 如果高于 package-lock.json 中对应的 dependency 的 version 时,会安装当前范围版本号中最高的版本,会更新 package-lock.json 文件对应的版本号
本地引用组件库做开发展示怎么支持?
npm link。
到需要被 link 的项目(name1)下 执行 npm link
,创建一个软链接到全局;到使用的项目下,执行npm link name1
,就行了。
- “main”: 规定一个包的主要入口文件,服务于 node.js
- “module”: 为了使用 esModule,进而实现 tree shaking
- “types”: 类型
你组件的按需加载?
通过 ES modules 的 tree-shaking 来实现按需引入
ES modules
ES6 module 中通过 import 关键字引入模块,通过 export 关键字导出模块。在编译时输出静态定义的接口。import 命令会被 js 引擎静态分析,优于模块内的其他内容执行。加载某个输出值而不是整个模块。这种加载称为 ”编译时加载“
与之不同的CommonJS
,通过 require
关键字加载的是整个对象(module.exports 属性),从对象上读取方法,这种加载称为 运行时加载。该对象是在脚本加载完成之后生成的,所以不能进行静态分析。
module.exports : module 是一个变量,指向一块内存,exports 是 module 中的一个属性,存储在内存中,然后 exports 属性指向{} 模块。既可以使用(.)语法,也可以使用=赋值。
ESM 模块依赖关系是确定的,和运行时的状态无关,可以进行可靠的静态分析,这也是 tree-shaking 的基础
tree-shaking
Tree-Shaking 依赖于 ES6 import 和 export 静态分析能力,它会尽可能地删除未被使用地函数和变量。由于 JS 的动态语言特性,所以 webpack 和 roll 在打包的时候都会保留类和对象的内部。
Travis CI
持续集成
就是在提交更改时自动构建和运行测试的过程
根目录下创建一个 .travis.yml
配置文件。这个文件必须保存在 github 仓库里,一旦代码仓库有新的 commit,Travis 就会去找这个配置文件,执行里面的命令。
- language:可以指定默认运行环境
- script: 可以执行一些脚本
- env
- cache
- deploy
rollup
使用到的包
@babel/preset-env // 一个预设,可以让你使用最新的JavaScript
@babel/preset-react // react项目依赖的预设
@babel/preset-typescript // 项目中使用 TS 需要用到的预设
@rollup/plugin-babel // rollup和babel之间的集成
@rollup/plugin-commonjs // 使能够使用 cjs 语法
@rollup/plugin-json // 访问json文件
@rollup/plugin-node-resolve // 使用Node解析算法定位模块 比如 可使用 `import {module} from './file'` 替换 `import {module} from './file/index.js`
rollup-plugin-peer-deps-external // 打包的时候用来排除 package.json 内 peerDependencies 字段内的包
@rollup/plugin-replace // 在打包文件时替换文件中的字符串
rollup-plugin-typescript2 // 用于解析ts文件,并生成 x.d.ts 类型文件
rollup-plugin-terser // 包最小化生成,也就是压缩
难点:
“ Unexpected character ‘@’ (Note that you need plugins to import files that are not JavaScript)”
前后端交互如何保证数据安全?
https
请求参数进行签名
加签和验签就是在请求发送方将请求通过加密算法生成一个 sign 值,放到请求参数里;请求接收方收到请求后,也用同样的方式对请求参数进行加密生成 sign 值,只要两个 sign 值相同,说明参数没有被篡改。
比如我们设定一定的规则进行 md5 计算得到 sign 值放在 url 或请求参数中。服务端通过同样的规则计算得到 sign 值