在前端页面开发中,大部分的时间都是在与后端进行数据交互:获取数据、计算并渲染。而页面上又有大量的元素状态须要维护,显示、隐藏、变化。这些均可能让咱们焦头烂额,而后在一周后看不懂本身的代码。javascript
因此项目开发的过程当中须要一个规范来约束代码的走向,让代码能按照统一的、最高效的方式运行(还有让别人阅读)。这里介绍一个前端对接后端接口数据的一个最佳实践。html
先让咱们看看反例,不知道你是否是用过这样的 app:前端
这里的例子说明:若是前端开发中不能把异常描述清楚、涵盖全面,数据状态的糟糕反馈就会直接影响用户体验。java
咱们先从最简单的状况入手,一个页面使用一个接口。这种状况下一般是:后端
这样的状况又分两种,服务器
不管是哪种状况,咱们都只在一个页面里处理一个接口,这是最简单的状况。那么咱们来看一下下面的图片,并把它看成一个开发任务思考一下你会怎么处理。网络
若是你只想到了**「调用接口」「渲染页面」里,那你这篇文章就是为你写的(笑)。其实上面的图只向你展现了两个状态**:「初始状态」、「理想结果状态」,我用了「理想结果」这个词来描述这个状态,是由于这是咱们在一切操做都完美的状况下获得的理想状态。app
而一般在项目里你只会从别人手里获得这两张图,我说的对吗?(产品经理和设计师都默认你了解他们须要的一切)。函数
若是咱们但愿作一个优秀的前端,咱们就须要马上发现这里还缺乏了三张图(三个状态)(有些交互里并不须要这么多状态,这里只讨论最全面的状况)优化
数据获取中状态 无数据状态 数据异常状态
从调用一个接口到渲染页面咱们大体分为一下几部
调用接口 -> 获得数据 -> 处理数据 -> 渲染
接下来咱们来编写一些代码,来对接接口而且管理数据和状态。为了使代码更加聚合,用一个字面量对象 SeaerchInput
来维护状态。而后咱们模拟一个接口的调用。
// 以上面搜索为例
// 建立页面对象
let SearchInput = {}
// 模拟一个接口
function API () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
let result = [{
name: '李三'
}];
resolve(result);
})
})
}
复制代码
接下来咱们在SearchInput
中用data
字段保存数据,用getSearchResult()
方法绑定数据,调用接口并直接绑定数据,那么咱们将获得的「理想结果状态」。
let SearchInput = {
data: null,
getSearchResult() {
API.then(
(res) => {
this.data = res; // 绑定数据
}
)
}
}
SearchInput.getSearchResult(); // 获取数据
function API () {
return new Promise(function (resolve, reject) {
// ...
})
}
复制代码
// html 的语法将使用 angular 指令去表达
<div>
<!-- 渲染结果 -->
<p ng-repeat="result in SearchInput.data"></p>
</div>
复制代码
这样的代码是十分脆弱的,由于咱们已经默认数据会瞬间返回而且没有任何问题。
function API () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
let result = [{
name: '李三'
}];
resolve(result);
}, 3000) // 为接口增长3秒的延时
})
}
复制代码
一旦给API
增长点延时,就会发现页面会在纯白状态下停留好久,由于页面没有任何提示,因此用户根本没法知道发生了什么事情,是等待仍是返回?
为此咱们须要管理从接口发起请求(request)到接收响应(response)这段时间的状态,在SearchInput
中用hasDone
来保存接口的响应状态,null
表明这个接口还在初始化状态,false
表明已经发出请求但未收到响应,true
表明已经收到响应。
let SearchInput = {
data: null,
hasDone: null, // 初始化
getSearchResult() {
this.hasDone = false; // 发起请求时置为 false
API.then(
(res) => {
this.hasDone = true; // 收到响应时置为 true
this.data = res;
}
)
}
}
SearchInput.getSearchResult();
function API () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
// ...
}, 3000) // 为接口增长3秒的延时
})
}
复制代码
<!-- 数据获取中状态 -->
<div ng-if="SearchInput.hasDone === false">
loading
</div>
<div ng-if="SearchInput.hasDone">
<!-- 渲染结果 -->
<p ng-repeat="result in SearchInput.data"></p>
</div>
复制代码
这下好了,若是接口很慢页面也会显示 loading,用户不会为此不知所措了。
尽管如今网络和服务器已经十分稳定,不多会出现异常,可是不管是网络、服务器或代码哪个出现异常而没有考虑,那都会形成用很差的用户体验
function API () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
let error = '服务器异常';
reject(error); // 接口返回了异常
})
})
}
复制代码
如今咱们假设咱们的API
返回了异常,页面又会变为纯白了,没有任何数据显示也没有任何提示。
为此咱们须要一个状态来管理接口返回的状态,在SearchInput
中用hasSuccess
来保存接口的返回状态,null
表明还在初始化状态,false
表明接口返回失败,true
表明接口成功返回数据。(你甚至能够先判断数据的格式、数量等是否知足你的要求,若是不知足要求,即便接口返回了数据,你同样能够将hasSuccess
设置为false
,由于这里的 success 表明了你获得了能够正确使用的数据,而不只仅是获得了数据)
let SearchInput = {
data: null,
hasDone: null,
hasSuccess: null, // 初始化
getSearchResult() {
this.hasDone = false;
API.then(
(res) => {
this.hasDone = true;
this.hasSuccess = true; // 获得数据置为 true
this.data = res;
},
(err) => {
this.hasDone = true; // 此时咱们也要更新 hasDone
this.hasSuccess = false; // 发生异常置为 false
}
)
}
}
SearchInput.getSearchResult();
function API () {
return new Promise(function (resolve, reject) {
// ...
})
}
复制代码
<!-- 数据获取中状态 -->
<div ng-if="SearchInput.hasDone === false">
loading
</div>
<!-- 数据异常状态 -->
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess === false">
数据异常
</div>
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess">
<!-- 渲染结果 -->
<p ng-repeat="result in SearchInput.data"></p>
</div>
复制代码
如今咱们会在hasDone === true
后知道数据是否正常,而且给出了错误的提示。
最后一个状态也是咱们要考虑的,当用户尝试搜索一个词却什么都没返回,又变成了可恶的纯白界面,咱们还须要考虑一下当获取数据时什么都没有的状况。
function API () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
let result = []; // 如今没有任何结果
resolve(result);
})
})
}
复制代码
咱们须要一个状态来管理数据的状态,在SearchInput
中用hasData
来保存数据状态,null
表明还在初始化中,false
表明数据为空,true
表明数据不为空。
let SearchInput = {
data: null,
hasDone: null,
hasSuccess: null,
hasData: null, // 初始化
getSearchResult() {
this.hasDone = false;
API.then(
(res) => {
this.hasDone = true;
this.hasSuccess = true;
this.hasData = res.length > 0; // 有置为 true,没有数据置为 false
this.data = res;
},
(err) => {
this.hasDone = true;
this.hasSuccess = false;
this.hasData = false; // 失败确定没有数据了
}
)
}
}
SearchInput.getSearchResult();
function API () {
return new Promise(function (resolve, reject) {
// ...
})
}
复制代码
<!-- 数据获取中状态 -->
<div ng-if="SearchInput.hasDone === false">
loading
</div>
<!-- 数据异常状态 -->
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess === false">
数据异常
</div>
<!-- 无数据状态 -->
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess && SearchInput.hasData === false">
数据异常
</div>
<div ng-if="SearchInput.hasDone && SearchInput.hasSuccess && SearchInput.hasData">
<!-- 渲染结果 -->
<p ng-repeat="result in SearchInput.data"></p>
</div>
复制代码
如今上面的代码基本上就是你所须要的了,它能够帮你应对各类状况,让页面展现的更加完美。
这一大段代码就是对应一个简单接口五个状态的设计,也是我目前项目中使用的模式,虽然看上去比较繁琐,可是相比后期再不停的补充和修改,一次性考虑全面带来不少好处。
若是一个接口是为了实现分页加载,那么状态的数量又会有所提高,这篇文章再也不阐述。
若是一个页面使用了多个接口,数据和状态之间产生了交叉,为了使状态逻辑清晰应该合理利用字面量对象来聚合代码逻辑。
在多人协做方面,因为你们使用同一套规范,对代码的阅读速度有显著提升。
这里列出的代码以普及为主,不少实现细节方面均可以再去优化,提炼。甚至写一个构造函数也是很方便的选择。
若是喜欢文章 请留下一个赞~ 若是喜欢文章 分享给更多人~
自由转载-非商用-非衍生-保持署名(创意共享3.0许可证) 转载时请保留原文连接 以保证可及时获取对文章的订正和修改