目标
能说出常见的web开发模式及其优劣
对自己在还原项目时觉得值得注意的地方进行记录
写在前面
写在前面:本文案例为跑在node服务器上的全栈项目,对项目中的注意点以及个人理解进行记录(此类项目功能大同小异)。可能存在许多个人记忆性注意点,但也有共性点,希望大家都能有所收获。
1. 开发模式
1.1 web开发模式的变化
-
最早,
混合开发
,由后端主导,前端写的代码,会交给后端,后端将自己写的代码与前端代码混合在一起,拼接成模板字符串,也称为后端渲染。这样的话,前端代码和后端代码融合在一个文件中,前端代码会被破坏,分工也不明确,代码多,很难排错,花费时间长,效率不高; -
在这样的情况下,出现一种mvc开发模式,
前后端分离
,前端代码不会被破坏,而是在视图环节参与进去,后端把接口写好,我们可以直接通过接口调数据,进行业务逻辑,是通过模板引擎渲染界面,也称为前端渲染。显示什么页面,也是路由决定,这样的话,分工明确,实现前后端分离,在这个阶段,我们还是用的后端服务器,路由,传参,收参还需要借助后端服务器完成。 -
Node的崛起的推动了全栈时代的到来,也是当代市场上最新的思维方法,
前后端完全分离
。前端通过建立自己的web服务器,解决一系列的路由 传参 收参问题,实现前后端完全分离,不再受限后端主导。
1.2 开发模式图解
- 混合开发
问题:
- 前后端开发人员对互相的代码都不是特别熟悉,混合开发两者在处理互相的代码时非常困难。
- 在开发的过程中难免会出现代码互相覆盖,导致工作量倍增。
- 前后端分离
好处:职责、分工明确,独立开发,互不影响。
2. 注意点
2.1 Ajax的使用及注意项
- 阻止表单默认提交
ajax
大家都不陌生,jquery封装的ajax
功能也是非常的强悍,实战中大多会使用jquery中的ajax
。对于前后端分离项目ajax
也是起着至关重要的作用。对于项目中的表单几乎都会用ajax
来提交,对于ajax提交
过程中,如果存在表单的默认提交行为,会导致页面默认提交
、数据刷新
等问题。
对于此类问题只需阻止表单默认提交行为即可。阻止默认行为的方法有:1.事件对象法e.preventDefault()
(普通浏览器),e.returnValue = false
(ie678),2.简单粗暴发:return false
(无兼容性问题),项目中常用 return false。
- RESTful 风格的接口注意点
此类接口目前几乎已经普及,好处不再阐述。对于值得注意的地方一般为:后端提供的接口文档一般格式/users/:id
,但我们在发送请求时切记不能带上:
,一般写法url: '/users/' + id
。关于RESTful
风格的API详情查看:RESTful风格的接口介绍。
- 接口一般可复用
对于不同的功能,可能存在数据的交集,此时用过的接口也会派上用场,不是所有的接口只能用一次。
对于ajax请求成功后的success()
函数内嵌套ajax请求也非常常见,只需用.
给第一次请求对象追加第二次请求对象即可。
2.2 表单提交(普通值+二进制文件)
- 表单值的提交
- 对于
动态渲染的表单
,绑定提交事件时要用到事件委托
(先获取其父元素,再获取它); - 给每个表单项添加
name属性
,且name属性与接口文档的参数名称保持一致
; - 在事件处理函数中
获取到用户在表单中输入的内容
; - 调用接口,将获取到的内容通过接口
发送给服务器端
,最后处理响应
const formData = $(this).serialize();
console.log(formData);
serialize()
方法可以得到表单中内容,并格式化成参数字符串
注意:对于参数字符串中汉字的base64
格式服务器会对其进行解析,返回我们需要的数据。
- 二进制文件的上传(图片为例)
对于文件的上传(<input type="file">
),监听文件上传控件的onchange
事件,file控件的this
指向选中文件的信息,将此信息作为属性值给FormData
实例对象,提交给服务器处理(node第三方模块formidable
),服务器返回客户端需要的数据(此处返回图片路径),此时可将图片路径的值给表单中的隐藏域作为参数
,随表单其他参数一起提交
。
$('#modifyBox').on('change', '#avatar', function() {
// 二进制数据上传需要FormData对象解析
var formData = new FormData();
formData.append('avatar', this.files[0]);
$.ajax({
url: '/upload',
type: 'post',
data: formData,
// 告诉ajax方法不要解析请求参数
processData: false,
// 告诉ajax方法不要设置请求参数类型
contentType: false,
success(response) {
// 实现预览功能
$('#preview').attr('src', response[0].avatar);
// 隐藏域向服务器传递图片路径
$('#hiddenAvatar').val(response[0].avatar)
},
error() {
console.log('图片上传失败')
}
})
})
注意:FormData对象在表单提交中也是非常有用,对于FormData的进一步了解可参考下:一文让你搞懂FormData。
- 表单值得注意的元素
下拉选择框select
<select name="" id="">
<option value="0">0</option>
<option value="1">1</option>
</select>
注意:表单提交的name写在select
身上,value写在option
身上,提交时,哪个value
在可视化区域,提交哪个value。
状态选择radio
<label><input type="radio" name="role" value="admin ">超级管理员</label>
<label><input type="radio" name="role" value="normal">普通用户</label>
注意:此时两个元素的name
要保持一致,value
存在差别,提交时,谁被选中提交谁。
文本域textarea
<textarea id="content" class="form-control input-lg" cols="30" rows="10" placeholder="内容" name="content"></textarea>
注意:文本域无value
值,它的内容直接写在双标签内。
date标签在赋值时注意格式
<input type="date" value="2020-12-01">
2.3 批量操作
此知识点就是实现复选框的全选及反选
,用jquery更加简便一些
// 全选按钮,实现全选功能
$(".checkall").change(function() {
// 所有子复选框随全选变化
$(".j-checkbox, .checkall").prop("checked", $(this).prop("checked"));
});
// 子复选框的变化影响总复选框
$(".j-checkbox").change(function() {
// 如何子复选框的选中数 = 商品总数,总复选框就选中,否则不选
if ($(".j-checkbox:checked").length === $(".cart-item").length) {
$(".checkall").prop("checked", true);
} else {
$(".checkall").prop("checked", false);
}
});
2.4 分页功能
对于分页功能,知识点:服务器返回的数据+模板引擎
,处理分页功能时,注意,上一页、下一页按钮的显示情况。
<script type="text/html" id="pageTpl">
{{if page > 1}}
<li><a href="javascript:;" onclick="getPage({{page - 1}})">上一页</a></li>
{{/if}} {{each display}}
<li>
<a href="javascript:;" onclick="getPage({{$value}})">{{$value}}</a>
</li>
{{/each}} {{if page
< pages}} <li><a href="javascript:;" onclick="getPage({{page + 1}})">下一页</a></li>
{{/if}}
</script>
注意:此处的getpage()
为请求第几页数据函数,模板引擎的知识运用较多,了解模板引擎:让交互变得优雅。
2.5 地址栏中获取参数方法的封装
对于一些依靠页面跳转,且地址栏中携带操作参数
(携带id、数据表的主键等)的任务,我们需要获取到地址栏中的参数
来进行后续的操作,因为此类任务比较常见,所以此处对其进行封装。
/ 注意此处的name与路由中要传递的参数保持一致
function getUtlParams(name) {
// 可能有多个参数,去除第一个"?",以"&"符为分界分割成数组
const paramsAry = location.search.substr(1).split('&');
// 循环数组,进行第二次分割,以"="为界限
for (let i = 0; i < paramsAry.length; i++) {
// 把每个数据从"="分割成数组
const tmp = paramsAry[i].split('=');
// 如果传入的参数名就是数组第一个值,返回数组第二个值(参数值)
if (tmp[0] == name) {
return tmp[1]
}
}
}
注意:此方法一般用于修改数据,且支持页面跳转(例如:https://editor.csdn.net/md?articleId=106910227(csdn编辑页)),拿到参数值,查询到数据,然后页面呈现。当修改页与编辑页同一路由时,就可根据是否携带参数来进行相应页面的渲染。
2.6 逻辑依靠数据
对于一些动画的渲染
(轮播图为例),如果其基本元素为ajax
动态渲染,此时我们在发送ajax请求渲染模板时就要把动画的逻辑写在success()
函数中,防止渲染后动画失效(因为逻辑找不到元素)
$.ajax({
type: 'get',
url: '/slides',
success(response) {
const html = template('slideTpl', {
data: response
});
$('#slideBox').html(html);
// 模板加载完毕才可调用轮播图逻辑代码,所以,逻辑在此处调用
var swiper = Swipe(document.querySelector('.swipe'), {
auto: 3000,
transitionEnd: function(index) {
// index++;
$('.cursor span').eq(index).addClass('active').siblings('.active').removeClass('active');
}
});
// 上/下一张
$('.swipe .arrow').on('click', function() {
var _this = $(this);
if (_this.is('.prev')) {
swiper.prev();
} else if (_this.is('.next')) {
swiper.next();
}
})
}
})
注意:意在表示逻辑在success()
中,逻辑部分可忽略。
2.7 公共部分代码抽离(iframe、load()、公共模板使用)
对于一些页面的重复部分(公共部分)我们当然要将他们集中处理
,尽量避免代码的冗余度过高、难以维护的情况。
- iframe元素
iframe可谓功能强大,它可以解决资源的跨域请求,当然本地页面请求当然不在话下。实现步骤:1.公共代码抽离,写在独立的文件内,2.在抽离的部分写入iframe
标签,src属性
写公共部分代码路径。
注意:对于请求到的资源会在iframe
中,我们要为iframe
重新写样式(不推荐)。
- jquery的load()方法
jquery的load方法类似于ajax
请求,请求公共部分代码,然后渲染。实现步骤:1.公共代码抽离,写在独立的文件内,2.在抽离的部分写入任意块级元素,3.用jquery获取此元素
,用load() 方法
请求公共代码并填充到元素内。
<script src="./js/jquery.min.js"></script>
$('#header').load('./common/header.html')
注意:此方法仅限于同一服务器(不可跨域且服务器运行正常)。对于公共部分抽离方法较多,建议用load()
(毕竟简便且项目要跑在服务器上),此外还可用gulp
对公共部分进行抽离,此处不赘述,了解gulp可查看:一文学会gulp
- 公共模板引擎的渲染
重复的页面往往不只静态页面,对于重复的动态页面,我们可在模板引擎上做手脚
$.ajax({
type: 'get',
url: '/posts/random',
success(response) {
// 用模板字符串对数据进行拼接
const randomTpl = `
{{each data}}
<li>
<a href="detail.html?id={{$value._id}}">
<p class="title">{{$value.title}}</p>
<p class="reading">阅读({{$value.meta.views}})</p>
<div class="pic">
<img src="{{$value.thumbnail}}" alt="">
</div>
</a>
</li>
{{/each}}
`;
// 定义要渲染的模板及数据
const html = template.render(randomTpl, {
data: response
});
// 把拼接好的模板填充到渲染位置
$('#randomBox').html(html);
}
})
注意:哪里需要填充,就在哪里填充。
2.8 项目中其他注意点
- 对于项目中的操作一定要确认操作者的
状态
(确认用户登录状态下操作,不然后续难维护); - 对于请求资源时,要明白资源路径,相对于
服务器
的位置; - 对于请求失败的数据,不防打印出请求警告,看是否是参数不符合
验证规则
; - 对于
MongoDB
数据库要了解数据的id
是自动生成的,且id获取时要写作_id
; - 对于请求到的数据,
以真实数据为准
,接口文档作为参考。对于复杂的数据,不防单独细致的看清结构
,再进行操作; - 对于一些
动态渲染的元素
,我们在操作时,可能会存在获取不到
的情况,此时我们就要用到事件委托
(获取其父级元素,再获取它)。
写在最后
写在最后:对于项目中碰到的细节可能有遗漏的地方,对于已写出的注意点也可能有不准确的地方,希望大家能够给予意见,坐等斧正,随时更新。最后希望大家都能够不负韶华,用代码养活自己。 Come on