开发日志
初始化项目
项目环境
- nodeJs环境
- 服务端npm包
- express
- mongoose
- cors
- inflection 命名转换
- multer 写入上传文件
- bcrypt hash加密
- jsonwebtoken json转token串
- http-assert http错误响应
- require-all
- 前端npm包
- vue CLI
- element-ui
- axios
- vue-router
- vue2-editor 富文本编辑
- dayjs
- 服务端npm包
- MongoDB数据库
项目根目录
- server 服务端项目
- web web客户端项目
- admin 后台界面项目
初始化操作
安装mongoDB
Vue CLI 创建前端项目
vue create web
vue create admin
server初始化
-
初始化 npm
npm init -y
-
安装 nodemon
npm install -g nodemon
Nodemon会持续监视您的目录或文件系统,当发现任何更改时,它会自动重新启动节点应用程序服务器。取代node命令修改源文件后需要重启server
-
package.json添加node命令
"scripts": { "serve": "nodemon index.js", }
admin初始化
配置vue-router
ElementUI使用
-
安装npm包
npm install element-ui --save
-
plugins/element.js安装插件
import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI);
-
main.js
import './plugins/element.js'
-
使用ElementUI的后台管理组件
- 创建views/Main.vue作为后台管理主页
- router/index.js配置Main.vue路由
admin与serve开发
Main.vue开发
- 左侧每个el-menu-item切换至各自子路由,router-view在el-main内显示
- 路由功能
- el-menu添加router属性,el-menu-item的index属性值设为路由path
- router.js下为Main.vue添加子路由配置
- 开发子路由视图
- axios请求
- 安装axios并配置network/request.js
- 项目中使用request函数
- main.js中在Vue.prototype上全局绑定request方法
- network独立各个views组件的请求方法,本项目使用该方式
- serve接口
- 安装express,mongoose,cors包
- 配置express,如post参数处理
- routes/admin配置’/admin/api’路径下的路由处理
- plugins/db.js配置MongoDB数据库连接
- models/新建mongoose模型,routes/admin声明对模型的处理
- admin的views请求数据
- network/声明各种request方法的请求参数
- views/视图组件引入请求方法并调用方法发送增删改查请求
子分类功能
- 父级分类是固定的,Categoryd/Edit.vue是编辑子分类
- 数据库设计:
- 父子分类记录都以Category模型存储在数据库
- 子分类的记录多一个parent属性关联父分类的id
- 操作:在category的schema上声明一个parent字段,值为ObjectId类型
- 新建子分类
- 查询所有父分类作为可选项
- 提交时,子分类的parent参数即为数据库存储时parent的关联id
- 编辑子分类
- 由于mongoDB的记录与双向绑定的model内部相互统一
- 查询子分类
- List.vue中每个子分类需显示其所属父分类
- mongoDB支持populate(‘parent’)返回集合关联id的记录,即父分类记录
CURD通用接口
-
简单的重复的增删改查操作可以合并为一个CURD通用接口
-
原理:请求路径名,如’/categories’,crud操作模型名,如’Category’。按照规则命名
-
因而通用接口只需把路径名转换为对应命名规则的操作模型名,对其crud即可
-
实现步骤:
-
通用接口监听的路由路径为’/admin/api/reset/:resource’,resource参数用于匹配实际请求路径
-
核心:
- app.use插入中间件处理,通过第三方inflection模块将resource参数转为符合命名规则的modelName,即操作模型名称
- 引入操作模型对象,将其暂存至req.Model属性
-
已监听router子路由的操作处理会在req.Model上对正确的模型进行CRUD
-
拓展部分
get请求处理判断modelName为’Category’时,增加populate处理
-
图片上传
- 使用ElementUI的upload组件,设置图片上传至服务器的地址
- 当用户选择文件后,ElementUI内部自动向声明的地址上传binary格式图片
- server端接口接收post的binar数据,使用multer中间件保存至/uploads目录,将图片信息对象返回前端
- 前端声明图片上传完成执行afterUpload处理,接收图片信息,设置src显示图片
- 此时服务端应添加静态资源托管,自动响应图片的请求
编辑Hero
-
先定义Hero的Schema,声明部分属性与另一Schema。ObjectId关联
-
edit视图需请求与Hero关联模型的所有记录作为选项
-
编辑完成后,选中选项的id即关联Schema的值
-
技能编辑
- UI组件默认绑定model.skills数据
- 第一次编辑时,skills为空,通过添加按钮push空对象至skills,添加一个待编辑技能
- 技能删除:每个技能编辑组件下的添加删除按钮,执行对skills删除当前索引项
编辑Article
- 富文本编辑
- 本项目使用第三方npm包’vue2-editor’
- 安装至admin目录后在ArticleEdit使用标签
- 在视图引入vue2-editor,组件’vue-editor’上双向绑定v-model
- 文章图片上传
- 富文本编辑器的图片是通过base64编码(粘贴的图片则引用src地址)直接保存在数据库
- 通过vue2-editor的API可以定义在图片编辑时自动上传至指定url地址,将图片转为src链接保存
管理员管理
password密码
- serve端保存管理员密码时,使用bcrypt工具Hash加密
- 操作:定义AdminUser的schema模型在时在password字段添加set方法处理
- serve仅允许admin编辑新密码覆盖旧密码,不允许直接向serve查询密码;
- 操作:定义AdminUser的schema模型在时在password字段添加select: false不可查询
Login.vue开发
serve接收登录请求,执行登录校验
- 查找用户名
- 对表单的用户名参数在AdminUser模型中查找对应数据记录
- 查找结果不存在则返回错误码与错误message至admin,否则执行下一步
- 密码校验
- 使用bcrypt工具对比表单的password参数和数据记录的password
- 由于设置了数据记录的password默认不可直接查询,此处查找数据记录时需添加select(‘+password’)
- 检验布尔值为false,返回错误码与message至admin,否则执行下一步
- 生成token并返回
- 验证通过,使用jsonwebtoken工具将自定义的json数据转为token串返回至admin
- 前端admin将返回的token存储至sessionStorage,并跳转路由至admin’/’根目录 - 错误统一处理 + serve对于前端无效访问返回错误代码422,响应内容为{message: ‘错误信息’} + admin的axios.interceptors拦截器监听服务端返回非200代码时,将error.response.data.message作为响应数据返回
服务端校验token
- admin添加axios.interceptors.request拦截器,发送任何请求前将token串添加至请求头
- 为serve通用CRUD接口与uoload接口添加检验token中间件处理,检验未通过使用assert方法返回错误信息
- 添加app.use(err,req,res,next)错误处理中间件,向admin响应message数据对象
- admin响应拦截器判断错误status,为401时跳转至login界面
前端admin路由限制
- 目前仅在请求后端admin/api接口时校验token,前端依旧可以直接访问admin项目的文件
- 实际开发应当在admin后端添加校验token处理,此处采用路由导航beforeEach限制没有token访问时跳转登录
- 上传图片请求的token
- upload图片采用ElementUI默认的formData请求,需在请求头添加token值至后端
- 通过maxin混合,在具有upload功能视图引入mixinsUpload对象,添加upload请求头授权参数method
- upload组件添加:headers=”getAuthHeader()”
移动端web开发
项目准备
- 样式采用sass编写,安装sass,sass-loder –save-dev
- 引入reset.scss用于初始化页面css
- 声明网站主要的color,font-size等值作为变量,使用sass遍历生成对应快捷样式类
- 生成文本对齐,flex布局,padding、margin边距等可复用的辅助类
首页开发
-
vue add router引入并配置router
-
topbar组件开发
-
navbar组件开发
-
home.vue开发
- swipper轮播组件
- npm install swiper
- 根据API文档引入依赖模块与配置options
- swipper组件为轮播容器,swipperSlide为轮播项组件
- 在home引入swipper、swipperSlide组件,swipperSlide插槽内插入轮播内容
- 为初始化页面多个swiper区别开,每个swiper组件实例绑定swiper属性值作为初始化时指定类名的元素
- 图标列表导航
- 图标使用sprite图定位,(spritecow)[http://www.spritecow.com/]网站工具快速划分每个sprite的css大小位置
- 声明sprite类名为同一sprite图icon的公共类,设置同一背景图与大小,在各个icon添加独立类名,如sprite-icon用于添加该icon的背景位置
- 先保证px单位的正确css样式,再将css根据比例转rem单位实现图标自适配
- 一些独立的icon背景图使用background-size: cover或百分比适配
- home-card组件
- home页内容区由3个卡片组成,封装card组件,包括card-header栏和card-body
- home页面下card的card-body内容包括一个nav和swiper,封装为home-card组件于childCompos/homeCard,使用card-body的插槽插入nav和swiper
- home.vue使用home-card组件,传入title、icon、categories等props至组件,实现自定义card的头部,card的nav与轮播数据
- 其中轮播的swiperSlide内容通过home-card的插槽在home页插入单个slide内容
- 后台新闻数据录入
- 提取官网数据:chrome支持\((".class")语法获取元素,如\)(“.class”).map(v => v.innerHTML);返回所有指定DOM元素的数据
- server后台添加一次录入初始数据的接口,该接口路由执行插入初始数据处理
- router/web/index.js引入Category、Article模型,取得所有article分类为“新闻分类”的子类目数组,官网新闻标题数据定义为title数组
- 将自类目数组,title数组对应关系通过map方法组成新闻标题数组,insertMany一次插入至Article模型中
- 新闻分类数据接口
- populate+schema.virtual虚拟字段实现,但是无法控制仅查询5个
- aggregate+pipe管道查询
- swiper与card-nav相互响应
- 初始化swiper时绑定slideChange事件,轮播项改变后设置$refs.nav的activeIndex改变
- 监听nav的handleClick事件,nav切换时设置swiper切换至对应索引轮播项
- 后台英雄数据
- 官网数组nav-list,hero-list两层数组循环生成web端所需英雄数据格式
- aggreate对英雄分类下职业分类与heroes联表查询所有英雄数据以正确格式返回
- swipper轮播组件
-
article文章详情开发
- 配置router
- server添加’/article/:id’接口,返回指定id的文章数据
- 查询当前article数据的下两条记录作为相关文章数据
-
hero详情页开发
- 配置router
- server添加’/hero/:id’接口,返回指定id的英雄数
- 对测试的英雄记录补全hero页面所需的基本数据
- 对于缺乏的字段,在Hero模型中添加字段,更新admin端使其支持编辑新字段
vue add 和 npm install 区别
es6中asnyc-await和promise都可用于异步,区别是?
- promise可以判断错误/成功预定义不同处理
- async-await会阻塞函数内部,函数外部依旧正常执行
注意
vue常见问题
-
一般情况下视图对其data存在依赖,当依赖一个数据对象的未初始化属性时,会报错
依赖数据对象的属性,应当在数据对象声明默认值;或者显式赋值
-
在对v-for循环的item执行操作,相对于写在methods添加接受item参数的处理方法 也可以直接在v-for内直接编写处理逻辑
-
当后端传递的数据直接赋予data的某个数据对象的属性时,视图不会及时响应
对于响应对象的未声明的属性,视图是不会对属性值进行响应的,此时需要显式赋值
-
数据绑定细节:list查询所有数据表时,将关联id的数据记录也返回;当查询单个记录时 ,只查询关联的id,通过查询其所有数据记录集自动匹配到值
-
添加Vue插件,调用全局vue的方法应通过vue.prototype上访问/调用
-
不要一昧使用:model=”变量名”数据绑定,对于固定的数据,直接title=”新闻”绑定,只有在绑定值为变量或后端数据时使用v-bind绑定
-
router-view中同一组件的不同路由之间跳转,默认路由不更新,需绑定:key=”$route.path”声明path为更新键
-
子组件修改父组件传递的props报错,解决:data中新建一个属性将props值赋值后单独保存
目前写死的值有
- ItemEdit.vue的uploadUrl图片上传地址
- server端upload.js接口,file.url即返回图片的src值
admin前端没有对服务端操作失败导致进行处理
关于服务端错误
- 一种是代码执行错误,仅在服务端报错
- 另一种是响应请求错误,会返回前端
- 错误对象包含错误name和错误message,我们可以根据错误name的值判断,向前端响应自定义的错误数据
Sass语法
- $colors: (….)声明数组或map
- @each $key,$val in $list 遍历
- map-get($map,”$var”) 获取list/map值
- map-get获取颜色名为颜色关键字时加引号
css技巧
- 网站各个主体部分间使用margin-top分隔,主体与之主体内子元素使用padding与margin-bottom结合布局
- space-between布局时,如果最后一行未占满会以分离方式布局, 解决:父元素不设置space-between布局产生空隙,改为子元素添加margin-right产生空隙
- 行布局要求行左右贴紧块的文字时,但元素块有左右padding,或固定宽,使用space-between无法解决,可为父元素左右添加负margin解决;
- fixed定位元素100%宽会溢出,父元素添加overflow-x: hidden解决
- 不同行内大小元素对齐:float+line-height
js技巧
- 数组的map方法可以跳过数组的某些项返回数组
vue cli4配置
- 关于模块打包的浏览器支持配置在package.json下最后browserslist数组
- CLI4天生支持postcss,只需新建postcss.config.js配置autoprefixer即可
- 不同loader包版本配置时使用的方法可能不同,如sass loader配置全局变量,v8版本使用prependData,最新版使用additionalData
- label
mongoose数据库
- 在db.js连接数据库时,引入所有model模型文件,之后可通过mongoose,model(‘模型名’)快速引用模型
- 对查询完成的数据记录对象添加自定义字段前,需对数据对象调用lean方法转为json
swiper踩坑
-
准备swiper:安装swiper,引入附加功能模块
-
配置参数,如autoplay、loop、请求后台数据作为内容添加observe,
-
mounted周期new Swiper初始化轮播
-
注意区分一个页面上多个swiper,即使配置一样,定义不同类名产生不同轮播实例
关于vue UI库
element ui pc端库,可做简单的pc页面与后台
vurtify 移动pc都支持,可自由组合的组件
iview pc后台专用
vant 移动端库,适用商城业务
vux 移动端库,类似微信界面UI,年轻的新框架,也有一些待完善的坑
quasar 支持spa,pwa,electron等各种形态,各端的web应用