問答
一、ajax 是什么胳蛮?有什么作用麸粮?
Ajax是Asynchronous JavaScript and XML的縮寫,通過new XMLHttpRequest創(chuàng)建對象酬滤,它是一個具有應(yīng)用程序接口的JavaScript對象签餐,能夠使用超文本傳輸協(xié)議連接1個服務(wù)器,是微軟在1999年IE5.0瀏覽器中率先提出的盯串。通過這個對象向服務(wù)器請求額外的數(shù)據(jù)氯檐,去獲取數(shù)據(jù),而無需卸載整個頁面体捏,帶來良好的用戶體驗冠摄。
二、前后端開發(fā)聯(lián)調(diào)需要注意哪些事情几缭?后端接口完成前如何 mock 數(shù)據(jù)河泳?(npm install -g server-mock)
- 前后端聯(lián)調(diào)
前后端聯(lián)調(diào)是一種真實業(yè)務(wù)數(shù)據(jù)和本地mock數(shù)據(jù)之間來回切換,以達到前后端分離架構(gòu)下的不同開發(fā)速度時數(shù)據(jù)交換的一種方式方法年栓。需要注意的有: - 數(shù)據(jù):需要約定好傳輸?shù)臄?shù)據(jù)以及類型拆挥。
- 接口:確定接口名稱及請求和響應(yīng)的格式,請求的參數(shù)名稱韵洋、響應(yīng)的數(shù)據(jù)格式竿刁。
- 后端接口完成前mock 數(shù)據(jù)
- 可以搭建php本地服務(wù)器,php寫腳本提供臨時數(shù)據(jù)搪缨;
- 也可使用Mock.js食拜,它能攔截ajax請求并根據(jù)請求中的內(nèi)容來隨機生成符合你要求的假數(shù)據(jù),模擬后端環(huán)境讓你完成對頁面和接口的測試副编。
- 也可以安裝server-mock這樣的工具负甸,這樣不需要特地去寫一個后臺的處理頁面文件來訪問數(shù)據(jù)。
三痹届、點擊按鈕呻待,使用 ajax 獲取數(shù)據(jù),如何在數(shù)據(jù)到來之前防止重復(fù)點擊?
- setTimeout + clearTimeout
連續(xù)的點擊會把上一次點擊清除掉队腐,也就是ajax請求會在最后一次點擊后發(fā)出去蚕捉。 - disable 按鈕。
- 給一個flag狀態(tài)柴淘,用戶點擊后變?yōu)閒alse迫淹,在數(shù)據(jù)到來之前不再發(fā)送請求秘通,ajax成功后設(shè)為true。
代碼
一敛熬、封裝一個 ajax 函數(shù)肺稀,能通過如下方式調(diào)用。
function ajax(opts){
// todo ...
}
document.querySelector('#btn').addEventListener('click', function(){
ajax({
url: 'getData.php', //接口地址
type: 'get', // 類型应民, post 或者 get,
data: {
username: 'xiaoming',
password: 'abcd1234'
},
success: function(ret){
console.log(ret); // {status: 0}
},
error: function(){
console.log('出錯了')
}
})
});
function ajax(opts){
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if(req.readyState == 4 && req.status == 200)
{
console.log(req.responseText)
var json = JSON.parse(req.responseText);
opts.success(json);
}
if(req.readyState == 4 && req.status == 404)
{
opts.error();
}
}; //事件監(jiān)聽函數(shù)
var dataStr = '';
for(var key in opts.data)
{
dataStr += key + '=' + opts.data[key] + '&';
}
dataStr = dataStr.substr(0, dataStr.length-1); // var re = /&$/g
if (opts.type.toLowerCase() === 'post')
{
req.open(opts.type, opts.url, true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send(dataStr);
}
if(opts.type.toLowerCase() === 'get')
{
req.open(opts.type, opts.url + '?' + dataStr, true);
req.send();
}
}
document.querySelector('#btn').addEventListener('click', function(){
ajax({
url: 'getData.php', //接口地址
type: 'get', // 類型话原, post 或者 get,
data: {
username: 'xiaoming',
password: 'abcd1234'
},
success: function(ret){
console.log(ret); // {status: 0}
},
error: function(){
console.log('出錯了')
}
})
});
二、實現(xiàn)如下加載更多的功能诲锹。
效果如下: 加載更多
使用server mock工具實現(xiàn)繁仁。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>加載更多</title>
</head>
<style>
*{
margin: 0;
padding: 0;
}
li{
list-style: none;
margin: 8px;
padding: 4px;
border: 1px solid #ccc;
font-size: 25px;
line-height: 50px;
cursor: pointer;
}
li:hover{
background: green;
}
.btn{
cursor: pointer;
padding: 5px;
margin: 30px 0;
font-size: 25px;
line-height: 50px;
color: purple;
border: 1px solid red;
background: #fff;
outline:none;
}
p{
text-align: center; // 按鈕居中
}
</style>
<body>
<ul class="wrap">
<li>內(nèi)容1</li>
<li>內(nèi)容2</li>
</ul>
<p><button class="btn">加載更多</button></p>
<script type="text/javascript">
var wrap = document.querySelector('.wrap');
var btn = document.querySelector('.btn');
var current = 3;
var isLoading = false;
btn.addEventListener('click', function() {
if(isLoading)
{
return;
}
isLoading: true;
btn.innerText = '正在加載,請稍后...';
ajax({
url: '/getMore',
data: {
start: current,
len: 6
},
success: function(data) {
isLoading = false;
btn.innerText = '加載更多';
if(data.status == 0)
{
getMore(data);
current += 6;
}else{
alert('出錯啦')
}
},
error: function() {
onError();
}
});
});
function ajax(opts) {
var req = new XMLHttpRequest();
req.onreadystatechange = function () {
if (req.readyState == 4 && req.status == 200)
{
var result = JSON.parse(req.responseText);
opts.success(result);
}
if (req.readyState == 4 && req.status == 404)
{
opts.error();
}
};
var filter = '';
for(var key in opts.data)
{
filter += key + '=' + opts.data[key] + '&';
}
filter = filter.substr(0, filter.length-1);
req.open('get', opts.url + '?' + filter, true);
req.send();
};
function getMore(data) {
var data = data.data;
for (var i=0; i<data.length; i++)
{
var li = document.createElement('li');
li.innerText = '內(nèi)容' + data[i];
wrap.appendChild(li);
}
}
function onError() {
isLoading: false;
btn.innerText = '加載更多';
alert('系統(tǒng)異常');
}
</script>
</body>
</html>
router.js
app.get('/getMore', function(req, res) {
// var start = req.query.start,
// len = req.query.len;
var data = [];
for(var i=0; i<req.query.len; i++)
{
data.push(req.query.start++);
}
res.send({
status: 0,
data: data,
})
})
請求接收的數(shù)據(jù)
加載更多
三、實現(xiàn)注冊表單驗證功能
效果如下:表單注冊
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注冊驗證</title>
<style>
form{
font-size: 20px;
}
fieldset label {
float:left;
width:120px;
text-align:right;
padding:4px;
margin:1px;
}
fieldset div {
clear:left;
margin:20px 0;
}
input{
height: 35px;
}
.help{
color: #bbb;
}
.submit{
margin-left: 205px;
}
.borderRed{
border: 1px solid red;
}
</style>
</head>
<body>
<h1>注冊</h1>
<form id="register" action="" method="post" >
<fieldset>
<div>
<label>用戶名:</label>
<input class="username" type="text" name="username" placeholder="用戶名(hunger被注冊過)">
<span class="help usernameWarning">只能是字母归园、數(shù)字改备、下劃線,3-10個字符</span>
</div>
<div>
<label>密碼:</label>
<input class="password" type="password" name="pwd">
<span class="help pwdWarning">大寫字母蔓倍、小寫、數(shù)字盐捷、下劃線最少兩種偶翅,6-15個字符</span>
</div>
<div>
<label>再輸一次:</label>
<input class="passwordAgain" type="password" name="pwdAgain" placeholder="再輸入一次密碼">
<span class="help pwdAgainWarning"></span>
</div>
<input class="submit" type="submit" value="注冊"><br/>
</fieldset>
</form>
<script>
var register = document.querySelector('#register');
var password = null;
var submit = document.querySelector('.submit');
var warning = {
userExist: "用戶名已經(jīng)存在",
usernameformatError: "輸入的用戶名格式不正確,請重新填寫碉渡!",
usernameAvailable: "用戶名可用",
passwordFormatError: "輸入的密碼格式不正確聚谁,請重新設(shè)置!",
passwordAvailable: "密碼可用",
passwordDifferent: "兩次輸入的密碼不一致滞诺!",
};
register.addEventListener('focusout', function(e){
// var className = e.target.className;
var value = e.target.value;
var name = e.target.name;
var className = e.target.className;
switch(name){
case 'username':
checkUser(e, value, className);
break;
case 'pwd':
checkPassword(e, value, className);
break;
case 'pwdAgain':
checkPasswordAgain(e, value, className, password);
break;
}
})
submit.addEventListener('click', function(e){
e.preventDefault();
console.log(document.getElementsByClassName('borderRed'));
if(document.getElementsByClassName('borderRed').length == 0)
{
enroll();
}
})
// 驗證用戶名是否輸入合法
function checkUser(e, value, className) {
console.log(e);
var reg = /^\w{3,10}$/g;
if(value == "hunger")
{
document.querySelector('.usernameWarning').innerText = warning.userExist;
e.target.className = addClass(className, 'borderRed');
return false;
}
if(!reg.test(value))
{
document.querySelector('.usernameWarning').innerText = warning.usernameformatError;
console.log(document.querySelector('.usernameWarning'));
console.log(document.getElementsByClassName('username')[0]);
e.target.className = addClass(className, 'borderRed');
console.log(className);
return false;
}
if(hasClass(className, 'borderRed'))
{
e.target.className = removeClass(className, 'borderRed');
}
document.querySelector('.usernameWarning').innerText = warning.usernameAvailable;
return true;
}
// 驗證密碼是否合法 (/\w{,6}|\w{20,}/g).test(value)
function checkPassword(e, value, className) {
password = value;
if((value.length < 6) || (value.length > 15) || ((/[^\w]/g).test(value)) || ((/^[a-z]+$|^[A-Z]+$|^[0-9]+$|^[_]+$/).test(value)))
{
document.querySelector('.pwdWarning').innerText = warning.passwordFormatError;
e.target.className = addClass(className, 'borderRed');
return false;
}
if(hasClass(className, 'borderRed'))
{
e.target.className = removeClass(className, 'borderRed');
}
document.querySelector('.pwdWarning').innerText = warning.passwordAvailable;
return true;
}
// 兩次密碼是否輸入一致
function checkPasswordAgain(e, value, className, password) {
if(value != password)
{
console.log(document.querySelector('.passwordAgain'));
document.querySelector('.pwdAgainWarning').innerText = warning.passwordDifferent;
e.target.className = addClass(className, 'borderRed');
return false;
}
if(hasClass(className, 'borderRed'))
{
e.target.className = removeClass(className, 'borderRed');
}
document.querySelector('.pwdAgainWarning').innerText = '';
return true;
}
// 發(fā)送數(shù)據(jù)
function enroll() {
ajax({
url: '/register',
type: 'post',
data: {
username: document.querySelector('.username').value,
password: document.querySelector('.password').value,
},
success: function(list) {
alert(list.msg);
},
error: function() {
alert("出錯啦");
}
})
}
function ajax(opts) {
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if(req.readyState == 4 && req.status == 200)
{
var json = JSON.parse(req.responseText);
opts.success(json);
}
if(req.readyState == 4 && req.status == 404)
{
opts.error();
}
}
var dataStr = '';
for (var key in opts.data)
{
dataStr += key + '=' + opts.data[key] + '&';
}
dataStr = dataStr.substr(0, dataStr.length-1);
req.open(opts.type, opts.url, true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send(dataStr);
}
function hasClass(className, cls) {
var reg = new RegExp('(\\s|^)' + cls + '(\\b|$)', 'g');
return reg.test(className)
}
function addClass(className, cls) {
if (!hasClass(className, cls)) {
return className += ' ' + cls;
}
return className; // 不要忘記return
}
function removeClass(className, cls) {
if (hasClass(className, cls)) {
var reg = new RegExp('(\\s|^)' + cls + '(\\b|$)', 'g');
return className = className.replace(reg, '').replace(/\s{2,}/g, ' ');
}
return className; // 不要忘記return
}
</script>
</body>
</html>
router.js
// 注冊
app.post('/register', function(req, res) {
// var start = req.query.start,
// len = req.query.len;
res.send({
status: 0,
msg: "恭喜你形导,注冊成功!",
})
})
請求接受的數(shù)據(jù)
注冊