1. 什么是AJAX
原生生js中有两种通信 ,一个ajax ,还有一个是fetch。
AJAX 并不是编程语言,是一种从网页访问 Web 服务器的技术。AJAX 代表异步 JavaScript 和 XML。
AJAX 使用浏览器内建的 XMLHttpRequest 对象 从 web 服务器请求数据 ,在使用JavaScript 和 HTML DOM来显示或使用数据 。Ajax 允许通过与场景后面的 Web 服务器交换数据来异步更新网页。这意味着可以更新网页的部分,而不需要重新加载整个页面。
2. AJAX如何工作?
- 网页中发生一个事件(页面加载、按钮点击)。
- 由 JavaScript 创建 XMLHttpRequest 对象。
- XMLHttpRequest 对象向 web 服务器发送HttpRequest请求。
- 服务器处理HttpRequest请求。
- 服务器将响应发送回网页。
- 由 JavaScript 读取响应。
- 由 JavaScript 更新页面。
3. XMLHttpRequest 对象详解
XMLHttpRequest 对象 用于与服务器交互数据。通过 XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。
3.1 AJAX使用步骤
所有现代浏览器都支持XMLHttpRequest 对象 。XMLHttpRequest 对象可用于在后台与 Web 服务器交换数据。这意味着可以更新网页的部分内容,而无需重新加载整个页面。
1. 创建 XMLHttpRequest 对象的语法:
var 实例化对象名 = new XMLHttpRequest();
javascript
var xhr = new XMLHttpRequest();
- 定义回调函数 。回调函数是作为参数传递给另一个函数的函数。在这种情况下,回调函数应包含响应准备就绪时要执行的代码。
javascript
xhr.onload = function() {
// 当响应准备就绪时要做什么
}
3. 发送请求。 向服务器发送请求,使用 XMLHttpRequest 对象的 open()
和**send()
**方法。
案例:
javascript
xhr.open("GET", "http://localhost:4000");
xhr.send();
3.2 跨域访问(Access Across Domains)
出于安全原因,现代浏览器一般不允许跨域访问。但是可以使用跨域的,可以把跨域写入响应头中。
跨域访问书写语法:响应对象.writeHead(响应码,{跨域设置})
案例:
javascript
// 引入http
const http = require("http");
// 创建一个服务对象,req:请求对象, res:响应对象
http.createServer(function (req, res) {
// 编写响应头,200时成功码,
res.writeHead(200, {
"Access-Control-Allow-Origin": "*"
})
res.end("a");
}).listen(process.env.PORT);//侦听来访问该端口的请求
"Access-Control-Allow-Origin":"*" :响应标头指定了该响应的资源是否被允许与给定的来源(origin)共享。
"Access-Control-Allow-Methods":"*":允许所有方法跨域
"Access-Control-Allow-Headers":"*":允许所有标头跨域。
3.3 XMLHttpRequest 对象的属性
|------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 属性 | 描述 |
| onload | 定义接收到(加载)请求时 要调用的函数。使用 XMLHttpRequest 对象时,可以定义一个回调函数,以便在请求收到答复时执行。 |
| onreadystatechange | 定义当 readyState 属性发生变化时调用的函数。 |
| readyState | 保存 XMLHttpRequest 的状态。每当readyState 发生变化时就会调用 onreadystatechange 函数。 * 0:创建ajax并实例化对象。 * 1:服务器连接已建立(open打开),并且发送请求头。 * 2:send发送消息体,并且请求对象已收到响应头。 * 3:接收服务端发送的消息,正在接收处理请求。 * 4:接收服务端发送数据完成,并且响应已就绪。 |
| responseText | 是服务器响应属性, 以字符串形式返回响应数据。 |
| responseXML | 是服务器响应属性, 以 XML 数据返回响应数据。 |
| status | 返回请求的状态号 * 200: "OK" * 403: "Forbidden" * 404: "Not Found" |
| statusText | 返回状态文本(比如 "OK" 或 "Not Found") |
3.4 XMLHttpRequest 对象的方法
|----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 方法名 | 描述 |
| new XMLHttpRequest() | 创建新的 XMLHttpRequest 对象。 |
| abort() | 取消当前请求。 |
| getAllResponseHeaders() | 是服务器响应方法 ,从服务器返回所有头部信息。 |
| getResponseHeader() | 是服务器响应方法, 从服务器返回特定的头部信息。 |
| open(method, url, async, user, password) | 设置请求访问服务器的各种参数。 参数: * method:请求类型 GET 或 POST。Method使用GET时,URL可以是本地路径,可以是网络路径http和https。 * url:被访问服务器的url(http://+ip+端口号)。 * async:是否异步 。true(异步)或 false(同步)。如需异步发送请求,open()
方法的 async参数必须设置为 true。
如果是false同步就不属于使用侦听事件了。 * user:可选的用户名。 * password:可选的密码。 |
| send() | 是 向服务器发送请求的方法。 该方法无参数用于 GET 请求。 |
| send(string) | 是向服务器发送请求的方法 。该方法有参数用于 POST 请求。 |
| setRequestHeader(header,value) | 向请求添加 HTTP 头部。将标签/值对添加到要发送的标头。 参数: * header **:**规定头部名称。 * value **:**规定头部值。 |
3.4.1 open()方法中的参数 method请求类型详解
- GET
- POST:主要用于向服务端发送消息,发送的内容通过send发送 ,可以发送文本、二进制流、大二进制流(Blob)、FormData、文档 。可以接收消息,不会调用缓存,POST发送时同域情况下也会携带cookie,跨域时需要主动携带cookie。POST是先发起请求头,然后再发起消息体。
- PUT:目的是表意,向服务器添加数据,和post作用相同。
- DELETE:目的是表意,向服务器提交删除数据,和post作用相同。
- OPTIONS :在使用POST、PUT、DELETE跨域时都会触发。
使用非GET和POST,跨域需要服务器同意,因为Method也需要处理跨域。
3.4.2 请求头和响应头
1. 请求头的注意事项:
- 请求头必须写在open之后,send之前。使用setRequestHeader()方法 设置请求头。
- 请求头写法单词首字母大写,单词直接使用-分割,如果是自定义请求头使用X-开头。
- 例如:setRequestHeader("Content-Type","application/json")这行代码的作用是告诉服务器发送的请求体 中包含的是 JSON 格式的数据,这样服务器才能正确地解析这些数据。
2. 请求标头中的属性重点详解Content-Type:
Content-Type :指定请求体的内容类型,用于告知服务器请求体的数据类型(如表单数据、JSON数据等)。
Content-Type的属性值可以是以下类型:
- text/plain:纯文本类型,不包含任何格式控制字符,通常用于传输普通文本数据。
- text/html:HTML文档类型,用于传输HTML格式的网页内容。
- application/json:JSON数据类型,用于传输结构化的JSON数据。
- application/xml:XML数据类型,用于传输结构化的XML数据。
- multipart/form-data:用于通过HTTP POST方法传输表单数据,支持传输文件和文本数据,常用于文件上传。
- application/x-www-form-urlencoded:用于通过HTTP POST方法传输表单数据,将表单字段以URL编码的形式包含在请求体中。
- image/jpeg、image/png、image/gif:图片类型,用于传输图片文件的内容。
- application/octet-stream:二进制数据流类型,表示不属于其他已知类型的任意二进制数据,例如传输文件时常用的类型。
其它属性如图所示:
3. 响应头的注意事项:
- 获取所有的响应头使用getAllResponseHeaders()方法。
属性如图所示:
4. timeout超时
timeout是一个无符号长整型数 ,代表着一个请求在被自动终止前所消耗的毫秒数。默认值为 0,意味着没有超时。
案列:
javascript
// timeout超时
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", loadHandler);
xhr.addEventListener("timeout", timeoutHandler);
xhr.open("PUT", "http://localhost:4000");
xhr.timeout = 1;//如果1毫秒之后服务器还没有返回数据就算超时
xhr.send();
function loadHandler(e) {
console.log(xhr.response);
}
function timeoutHandler(e) {
// console.log(e);
xhr.abort();//断开xhr
// 断开后重新调用ajax,3次
}
超时过后会得到一个ProgressEvent事件。
解决超时的方法:使用abort();断开请求,断开后重新调用ajax3次。
5. 服务器使用AJAX的案例
5.1 最简单的前端AJAX通信
-
创建server文件夹。
-
在集成终端中执行npm init -y 初始得到package.json文件。
-
编写package.json文件。
javascript
{
"name": "serverzixie",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "cross-env PORT=4000 nodemon"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"nodemon": "^3.1.4",
"cross-env": "^7.0.3"
}
}
4.在集成终端中执行npm i 。
- 编写server文件并在集成终端中使用npm start启动服务。
javascript
// 引入http
const http = require("http");
// 创建一个服务对象,req:请求对象, res:响应对象
http.createServer(function (req, res) {
// 编写响应头,200时成功码,
res.writeHead(200, {
"Access-Control-Allow-Origin": "*"
})
res.end("a");
}).listen(process.env.PORT);//侦听来访问该端口的请求
- 编写前端发送请求的html文件。
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// 创建一个
var xhr = new XMLHttpRequest();
xhr.addEventListener("load", loadHandler);
xhr.open("GET", "http://localhost:4000");
xhr.send();
function loadHandler(e) {
console.log(xhr.response);
}
</script>
</body>
</html>
- 查看效果。
5.2 服务器使用AJAX与数据库通信
在5.1 代码的基础上继续编写代码
-
在server文件夹新创建一个MYSQL.js的文件。
-
重新编写package.json文件。
javascript
{
"name": "serverzixie",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "cross-env PORT=4000 nodemon"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"mysql": "^2.18.1", //自己的mysql是低版本使用mysql 2.18.1
"mysql2": "^3.11.0" ///自己的mysql是高版本使用mysql2 3.11.0
},
"devDependencies": {
"nodemon": "^3.1.4",
"cross-env": "^7.0.3"
}
}
3.在集成终端中执行npm i 。
- 编写MYSQL.js文件。
javascript
// 引入MySQL文件
const mysql = require("mysql");
// 创建一个连接
const connect = mysql.createConnection({
host: "localhost",//IP地址
port: 3306, //端口号
database: "mysql_js",//连接的数据库名
user: "root",//数据库用户名
password: "123456" //数据库密码
})
console.log(connect);
- 在 server.js中引入数据库。并使用 npm start命令开启服务。
javascript
// 引入数据库js文件
require("./MYSQL");
// 引入http
const http = require("http");
// 创建一个服务对象,req:请求对象, res:响应对象
http.createServer(function (req, res) {
// 编写响应头,200时成功码,
res.writeHead(200, {
"Access-Control-Allow-Origin": "*"
})
res.end("a");
}).listen(process.env.PORT);//侦听来访问该端口的请求
没有报红说明连接数据库成功
6. 历史记录的相关知识
历史记录分为两种:hash历史记录 和 history历史记录。
6.1 hash历史记录
hash历史记录 :hash 不会刷新页面,但是会触发后产生历史记录 。历史记录回退时hash也会回退到上一次历史记录的hash值。 如果刷新页面hash历史记录就会没有了,因为刷新页面会重新初始化页面。
- 访问地址**#后面的内容发生改变时就是hash在发生改变**。
- hashchange:是一个hash事件,可以用来侦听hash值的变化。
案例:点击哪个下面的方框里就显示哪个。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
ul {
list-style: none;
display: flex;
}
li {
width: 160px;
height: 30px;
border: 1px solid #000;
}
li>a {
text-decoration: none;
display: block;
width: 100%;
height: 100%;
text-align: center;
line-height: 30px;
color: #000;
border-left: none;
}
li>a>:first-child {
border-left: 1px solid #000;
}
.content {
width: 1400px;
height: 500px;
border: 1px solid #000;
}
</style>
</head>
<body>
<ul>
<li><a href="#vegetable">蔬菜</a></li>
<li><a href="#fruits">水果</a></li>
<li><a href="#meat">肉禽</a></li>
<li><a href="#oil">米油</a></li>
</ul>
<div class="content"></div>
<script>
let list = {
vegetable: ["白菜", "菠菜", "胡萝卜", "油麦菜", "娃娃菜"],
fruits: ["苹果", "香蕉", "西瓜", "桃子", "榴莲"],
meat: ["猪肉", "牛肉", "羊肉", "鸡肉", "鸭肉"],
oil: ["菜籽油", "橄榄油", "花生油", "大米", "小米"]
}
// hash 不会刷新页面,但是会触发后产生历史记录
var content;
init();
function init() {
// 获取content选择器
content = document.querySelector(".content");
// 侦听hash事件
window.addEventListener("hashchange", hashChangeHandler);
//location.hash :获取到本地的hash
// 点击哪个li便签就会获取a标签href属性的值
console.log(location.hash);//例如在页面中点击蔬菜就会得到#vegetable
// 有hash历史记录时的情况
if (location.hash) {
// 调用侦听的函数
hashChangeHandler();
// 没有有hash历史记录时的情况
} else {
location.href += "#vegetable"
}
}
// 侦听的函数
function hashChangeHandler(e) {
// 获取到的hash包括#,使用slice方法获取到#后面的内容
let name = location.hash.slice(1);
// 把点击对应的内容写入到div盒子中
content.innerHTML = list[name];
}
</script>
</body>
</html>
点击
历史记录回退:
6.2 history历史记录
history.back() : 回退
history.forward():前进
history.go(1) :刷新
history.pushState(数据,无用的标识,url(可以添加hash或者search)):参数数据不能是DOM对象,可以是字符串或者对象、数组。当使用pushState后,会向历史栈中添加一个历史数据,在使用popstate事件后,可以在不同历史中拿到这个数据。
history.replaceState(数据,无用的标识,url(可以添加hash或者search)) :替换历史数据。
案例:
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
ul {
list-style: none;
display: flex;
}
li {
width: 160px;
height: 30px;
border: 1px solid #000;
border-left: none;
text-align: center;
line-height: 30px;
}
li:first-child {
border-left: 1px solid #000;
}
.content {
width: 1400px;
height: 500px;
border: 1px solid #000;
}
</style>
</head>
<body>
<ul>
<li id="vegetable">蔬菜</li>
<li id="fruits">水果</li>
<li id="meat">肉禽</li>
<li id="oil">米油</li>
</ul>
<div class="content"></div>
<script>
let list = {
vegetable: ["白菜", "菠菜", "胡萝卜", "油麦菜", "娃娃菜"],
fruits: ["苹果", "香蕉", "西瓜", "桃子", "榴莲"],
meat: ["猪肉", "牛肉", "羊肉", "鸡肉", "鸭肉"],
oil: ["菜籽油", "橄榄油", "花生油", "大米", "小米"],
};
let ul, content;
init();
function init() {
// 获取ul
ul = document.querySelector("ul");
// 获取content选择器
content = document.querySelector(".content");
// 给ul添加侦听事件
ul.addEventListener("click", clickHandler);
// 当历史发生改变时(点击了前进或者回退按钮时触发)
window.addEventListener("popstate", popStateHandler);
if (!history.state) {
history.replaceState("vegetable", "vegetable")
}
popStateHandler();
}
function clickHandler(e) {
// 如果不是li,return结束
if (e.target.nodeName !== "LI") return;
// 向div盒子中写入被点击对象的内容
content.innerHTML = list[e.target.id];
// 向历史栈中添加一个历史数据
history.pushState(e.target.id, e.target.id)
}
function popStateHandler(e) {
// 获取当前的历史数据
// console.log(history.state);
content.innerHTML = list[history.state];
}
</script>
</body>
</html>
7. 编写服务器与客户端通信案例
7.1 注册功能、注册页面切换到登录页面
-
创建一个template的文件夹,里面有list.js、login.js、register.js、index.html文件。
-
再创建一个js文件夹,里面有ajax.js文件。
-
编写ajax.js文件。
javascript
export default class AJAX {
// IP地址
static URL = "169.254.28.173";
// 端口号
static PORT = 4003;
// 使用的协议
static PROTOCOL = "http://";
// post请求
static POST = "post";
// get请求
static GET = "get";
constructor() {
}
// 处理get请求的方法
// 参数 router:对应接口路由,query:访问地址?号后面的内容, headers:请求头
static async get(router, query = {}, headers = {}) {
// 调用将对象转换为字符串的方法
query = AJAX.queryStringify(query);
// 判断路由的第一位是不是/,字符串的方法startWith接收判断字符串的第一个字符是否是某个字符
// 不是的情况,就拼接上/
if (!router.startsWith("/")) router = "/" + router;
// 是的情况,拼接url。如果query没有传参则传一个空对象
let url = AJAX.PROTOCOL + AJAX.URL + ":" + AJAX.PORT + router + (query.trim().length === 0 ? "" : "?" + query);
// 查看一下url的拼接情况
// console.log(url);//测试4
// 调用ajax的方法发送请求
return await AJAX.ajax(url, AJAX.GET, headers);
}
// 处理post请求的方法
// 参数 router:对应接口路由,body:请求传输过来的数据,headers:请求头
static async post(router, body = {}, headers = {}) {
// 判断路由的第一位是不是/,字符串的方法startWith接收判断字符串的第一个字符是否是某个字符
// 不是的情况,就拼接上/
if (!router.startsWith("/")) router = "/" + router;
// 是的情况,拼接url。如果query没有传参则传一个空对象
let url = AJAX.PROTOCOL + AJAX.URL + ":" + AJAX.PORT + router;
// 调用ajax的方法发送请求
return await AJAX.ajax(url, AJAX.GET, headers, body);
}
//上面的get、post方法调用ajax
// 参数 url:请求路由,method:用于判断是上面方法, headers:请求头, body:请求传输过来的数据
static ajax(url, method, headers, body) {
return new Promise(function (resolve, reject) {
// XMLHttpRequest 对象可用于在后台与 Web 服务器交换数据。
var xhr = new XMLHttpRequest();
// 规定请求
xhr.open(method, url);
for (var key in headers) {
// 设置发送的标头
xhr.setRequestHeader(key, headers[key]);
}
// 如果是get请求直接发送,如果是post请求把将 JavaScript 对象转换为字符串。
method === AJAX.GET ? xhr.send() : xhr.send(JSON.stringify(body));
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status < 300 && xhr.status >= 200) {
let headers = AJAX.getHeaders(xhr)
if (headers["content-type"]?.trim() === "text/html;charset=utf-8") {
resolve(xhr.response)
} else {
let data;
try {
data = JSON.parse(xhr.response)
} catch (e) {
data = AJAX.getQuery(xhr.response)
}
resolve({ data, headers });
}
} else if (xhr.readyState === 4) {
reject(xhr.status);
}
}
})
}
// url传参是对象就将对象转换为字符串的方法。
static queryStringify(query) {
// 如果query是字符串直接返回query
if (typeof query === "string") return query;
// 如果query不是字符串先进行遍历
let str = "";
for (var key in query) {
// 判断遍历的每一个元素是否是数组,这里是数组的情况
if (Array.isArray(query[key])) {
str += "&" + query[key].map(function (item) {
// 打印一下查看效果
// console.log(key, item);
// 返回结果
return key + "=" + item;
}).join("&")
// 测试1:打印查看字符串是否拼接成功
// console.log(str);
// query中的元素是一个对象的情况
} else if (typeof query[key] === "object") {
// 将对象解析成Json字符串
str += "&" + key + "=" + JSON.stringify(query[key]);
// 其它情况
} else {
str += "&" + key + "=" + query[key];
}
}
//去掉字符串第一个&符号
str = str.slice(1);
// 测试2、3:打印查看一下字符串
// console.log(str);
return str;
}
// 请求头
static getHeaders(xhr) {
return xhr.getAllResponseHeaders().split(/\n/).reduce((value, item) => {
if (item.trim().length === 0) return value;
let arr = item.trim().replace(/\r/, "").split(":");
try {
value[arr[0]] = JSON.parse(arr[1])
} catch (e) {
value[arr[0]] = arr[1];
}
return value
}, {})
}
static getQuery(str) {
if (!/.*?=.*?/.test(str)) return str;
return str.split("&").reduce((value, item) => {
let arr = item.split("=");
try {
value[arr[0]] = JSON.parse(arr[1])
} catch (e) {
value[arr[0]] = arr[1];
}
return value
}, {})
}
}
- 在index.html文件中编写测试ajax.js的代码。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
import AJAX from "./js/ajax.js";
// AJAX.queryStringify({ a: [1, 2, 3] });//测试1
// AJAX.queryStringify({ a: [1, 2, 3], b: { a: 1, b: 2 } });//测试2
// AJAX.queryStringify({ a: [1, 2, 3], b: { a: 1, b: 2 }, c: 3, d: 4 });//测试3
//测试4 ==> http://169.254.28.173:4003/login?a=1&b=2
// AJAX.get("/login", { a: 1, b: 2 });
//测试4 ==> http://169.254.28.173:4003/login
AJAX.get("/login");
</script>
</body>
</html>
- 在集成终端中下载jquery和bootstrap@3
执行命名:
npm i jquery
npm i bootstrap@3
- 整理项目结构。
- 把node_modules --> jquery --> dist --> jquery.js移动到js文件夹下。
- 把node_modules --> bootstrap--> dist --> js -->bootstrap.js 移动到js文件夹下。
- 把node_modules --> bootstrap--> fonts文件夹 移动到最外层文件夹 下。template
- 在新建一个css文件夹, node_modules --> bootstrap--> css -->bootstrap.css 移动到新建的css文件夹下
- 拉完以后把node_modules文件夹删掉 ,把package-lock.json和package.json也删掉。
项目结构如下:
- 在js文件夹中新创建并编写VerifyForm.js表单验证类
javascript
// 验证表单是否合法的类
export default class VerifyForm {
static setForm(elem) {
// 根据表单验证的结果添加样式
if (VerifyForm.verify(elem.name, elem.value)) {
// 从元素的父元素的选择器集合中移除has-error类选择器
elem.parentElement.classList.remove("has-error")
// 向元素的下一个兄弟元素的选择器集合中删除glyphicon-remove类选择器
elem.nextElementSibling.classList.remove("glyphicon-remove")
// 向元素的父元素的选择器集合中添加has-success类选择器
elem.parentElement.classList.add("has-success")
// 向元素的下一个兄弟元素的选择器集合中添加glyphicon-ok类选择器
elem.nextElementSibling.classList.add("glyphicon-ok")
} else {
// 从元素的父元素的选择器集合中移除has-success类选择器
elem.parentElement.classList.remove("has-success")
// 向元素的下一个兄弟元素的选择器集合中删除glyphicon-ok类选择
elem.nextElementSibling.classList.remove("glyphicon-ok")
// 从元素的父元素的选择器集合中添加has-error类选择器
elem.parentElement.classList.add("has-error")
// 向元素的下一个兄弟元素的选择器集合中添加glyphicon-remove类选择器
elem.nextElementSibling.classList.add("glyphicon-remove")
}
}
// 传入form表单input标签的name属性和value属性验证输入是否合法
static verify(name, value) {
switch (name) {
case "user":
return /^\w{8,16}$/.test(value);
case "password":
return /^(?=\D+\d)(?=.*[a-z])(?=.*[A-Z])[a-zA-Z0-9_$#@!~]{8,16}$/.test(value);
case "name":
return /^[\u4e00-\u9fd5]{2,4}$/.test(value);
case "age":
return /^\d$|^[1-9]\d$|^1[0-2]\d$/.test(value);
case "tel":
return /^1[3-9]\d{9}$/.test(value);
case "email":
return /^\w+\@\w+\.\w+(\.\w+)?$/.test(value);
default:
return true;
}
}
}
8.编写注册页面的模板 register.js
注意!!!
自学一下bootstrap前端框架
网址 :全局 CSS 样式 · Bootstrap v3 中文文档 | Bootstrap 中文网 (bootcss.com)
javascript
// 注册模板
// 引入ajax的类
import AJAX from "../js/ajax.js";
// 引入验证表单的类
import VerifyForm from "../js/VerifyForm.js";
const registerHTML = `
<h3 class="text-center">注册用户</h3>
<div class="row">
<form action="" class="col-xs-8 col-xs-offset-2">
<!-- <div class="form-group has-success has-feedback">
<label class="control-label" for="user">用户名</label>
<input type="text" class="form-control" id="user" />
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
</div> -->
<div class="form-group has-feedback">
<label class="control-label" for="user">用户名:</label>
<input type="text" class="form-control" id="user" name="user" />
<span class="glyphicon form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="password">密码:</label>
<input
type="password"
class="form-control"
id="password"
name="password"
autocomplete
/>
<span class="glyphicon form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="name">姓名:</label>
<input type="text" class="form-control" id="name" name="name" />
<span class="glyphicon form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="name">性别:</label>
<label class="radio-inline">
<input type="radio" name="sex" id="gentleman" value="男" checked/> 男士
</label>
<label class="radio-inline">
<input type="radio" name="sex" id="lady" value="女" /> 女士
</label>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="age">年龄:</label>
<input type="text" class="form-control" id="age" name="age" />
<span class="glyphicon form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="tel">电话:</label>
<input type="text" class="form-control" id="tel" name="tel" />
<span class="glyphicon form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="email">邮箱:</label>
<input type="text" class="form-control" id="email" name="email" />
<span class="glyphicon form-control-feedback"></span>
</div>
<div >
<button type="submit" class="btn btn-primary col-xs-offset-4">注册</button>
<button type="button" class="btn btn-success col-xs-offset-1 loginbn">登录</button>
</div>
</form>
</div>
`
export default function (content) {
// 当点击注册按钮时,将注册模板注入到注册页面中
content.innerHTML = registerHTML;
// 获取表单
var form = content.querySelector("form");
// 侦听登录的点击事件,调用jumpLogin函数
form.querySelector(".loginbn").addEventListener("click", jumpLogin);
// 侦听表单的提交事件
form.addEventListener("submit", submitHandler);
// 侦听表单中的input是否有填写
form.addEventListener("input", inputHandler);
}
function submitHandler(e) {
// 阻止表单的默认提交行为
e.preventDefault();
// 获取表单提交的数据
var fd = new FormData(this);
// 判断表单中的input框是否输入完成
var data = {};
for (var [key, value] of fd) {
if (!VerifyForm.verify(key, value)) {
// 没有输入数据的input框会进行集焦显示提醒
this.querySelector("[name=" + key + "]").focus();
return;
}
// 每一个input验证完毕并且都填写了合法的内容再放入data对象中
data[key] = value;
}
// 调用提交注册表单的函数
registerForm(data)
}
function inputHandler(e) {
// 表单中不是input框和input框中的name属性=sex的不进行表单验证
if (e.target.nodeName !== "INPUT" || e.target.name === "sex") return;
// age的input框只能输入数字
if (e.target.name === "age") {
e.target.value = e.target.value.replace(/\D/g, "");
}
// 调用验证表单的类
VerifyForm.setForm(e.target);
}
// 提交表单的函数
async function registerForm(data) {
// 调用ajax通信,发送请求
let result = await AJAX.post("/register", data);
// 弹出警示框返回表单是否提交成功的信息
alert(result.data.result.msg)
// 如果提交不成功,调用jumpLogin函数,重新提交表单
if (!result.data.err) {
jumpLogin();
}
}
// 侦听登录的点击事件就跳转到登录页面
function jumpLogin() {
var evt = new MouseEvent("click", { bubbles: true });
loginform.firstElementChild.children[1].firstElementChild.dispatchEvent(evt);
}
9.编写登录页面的模板 login.js
javascript
// 登录模板
import VerifyForm from "../js/VerifyForm.js";
import AJAX from "../js/ajax.js";
const loginText = `
<h3 class="text-center">登录</h3>
<div class="row">
<form action="" class="col-xs-8 col-xs-offset-2">
<!-- <div class="form-group has-success has-feedback">
<label class="control-label" for="user">用户名</label>
<input type="text" class="form-control" id="user" />
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
</div> -->
<div class="form-group has-feedback">
<label class="control-label" for="user">用户名:</label>
<input type="text" class="form-control" id="user" name="user" />
<span class="glyphicon form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<label class="control-label" for="password">密码:</label>
<input
type="password"
class="form-control"
id="password"
name="password"
autocomplete
/>
<span class="glyphicon form-control-feedback"></span>
</div>
<div >
<button type="submit" class="btn btn-primary col-xs-offset-4">登录</button>
<button type="button" class="btn btn-success col-xs-offset-1">注册</button>
</div>
</form>
</div>
`
export default function (content) {
// 当点击登录按钮时,将登录模板注入到登录页面中
content.innerHTML = loginText;
// 获取表单
var form = content.querySelector("form");
// 侦听表单的提交事件
form.addEventListener("submit", submitHandler);
// 侦听表单中的input是否有填写
form.addEventListener("input", inputHandler);
}
function submitHandler(e) {
// 阻止表单的默认提交行为
e.preventDefault();
// 获取表单提交的数据
var fd = new FormData(this);
var data = {};
// 判断表单中的input框是否输入完成
for (var [key, value] of fd) {
if (!VerifyForm.verify(key, value)) {
// 没有输入数据的input框会进行集焦显示提醒
this.querySelector("[name=" + key + "]").focus();
return;
}
// 每一个input验证完毕并且都填写了合法的内容再放入data对象中
data[key] = value;
}
// 调用提交登录表单的函数
login(data);
}
function inputHandler(e) {
// 表单中不是input框不进行表单验证
if (e.target.nodeName !== "INPUT") return;
// 调用验证表单的类
VerifyForm.setForm(e.target);
}
async function login(data) {
// 调用ajax通信,发送请求
let result = await AJAX.post("/login", data);
// 弹出警示框返回表单是否提交成功的信息
alert(result.data.result.msg);
// 如果提交不成功的情况
if (!result.data.err) {
// console.log(result.data.result);
// 存储用户名
localStorage.user = result.data.result.user;
// 存储token令牌
localStorage.token = result.data.result.token;
}
}
- 清除index.html 文件的内容,重新编写index.html 文件。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入文件 -->
<link rel="stylesheet" href="./css/bootstrap.css">
<script src="./js/jquery.js"></script>
<script src="./js/bootstrap.js"></script>
</head>
<body>
<div class="container">
<!-- 导航条 -->
<div class="row">
<p class="navbar-text navbar-right">
张三<a href="#" style="margin-left: 10px" class="navbar-link">退出登录</a>
</p>
</div>
<!-- 切换注册和登录页面 -->
<div class="col-xs-6 col-xs-offset-3 row" id="loginform">
<ul class="nav nav-tabs nav-justified">
<li class="active"><a href="javascript:void(0)" id="register-tab">注册</a></li>
<li><a href="javascript:void(0)" id="login-tab">登录</a></li>
</ul>
<div class="content"></div>
</div>
<!-- 表单 -->
<div id="list"></div>
</div>
<script type="module">
import register from "./template/register.js";
import login from "./template/login.js";
// loginform:登录的表单,content:登录表单中的内容,prev:切换
var loginform, content, prev;
init();
function init() {
// 获取登录的表单
loginform = document.querySelector("#loginform");
// 获取登录表单中的内容
content = loginform.querySelector(".content");
// 侦听点击事件
loginform.addEventListener("click", tabClickHandler);
// 抛发事件
var evt = new MouseEvent("click", { bubbles: true });
loginform.querySelector("#register-tab").dispatchEvent(evt);
}
function tabClickHandler(e) {
// 判断是否点击了a标签,这里是没有点击a标签
if (e.target.nodeName != "A") return;
// 点击后切换,prev有值是显示的
if (prev) {
// 将prev对应的页面隐藏到
prev.className = "";
}
// 被点击元素的父元素
prev = e.target.parentElement;
// 给父元素添加类选择器,将另一个页面显示出来
prev.className = "active";
// 根据切换的页面使用对应的页面表单
if (e.target.id === "register-tab") {
register(content);
} else {
login(content);
}
}
</script>
</body>
</html>
7.2 登录页面切换到注册页面、登录功能
-
继续完善login.js 。
-
在js文件夹下新建并编写一个Hash.js的文件,用路由方法来实现登录页面和注册页面的跳转。