前后端交互之Ajax编程02


模板引擎

官方地址:https://aui.github.io/art-template/zh-cn/docs/
将数据和html连接起来,这里是客户端模板引擎,art-template模板引擎;

  1. 下载模板引擎
    服务器端下载方式:npm install art-template --save
    客户端下载方式:保存到D:\web_study\Ajax\02\public\js
  2. 将模板引擎的库文件引入到当前页面<script src="/js/template-web.js"></script>
  3. 准备art-template模板
<script type="text/html" id="tpl">
        <h1>{{username}}{{age}}</h1>
    </script>
  1. 告诉模板引擎哪个数据和哪个模板拼接
<script type="text/javascript">
        //3. 告诉模板引擎哪个数据和哪个模板拼接
        var html = template('tpl', {
            username: 'zhangsan',
            age: 30
        });
        //container是一个div盒子的ID
       document.getElementById('container').innerHTML = html;
    </script>

案例

1. 验证邮箱i地址唯一性

用户输入完成后,鼠标离开焦点的时候,判断用户是否注册过,根据判断结果给出不同的提示信息显示在页面中;
在这里插入图片描述

  1. 获取页面中的元素
// 获取页面中的元素
		var emailInp = document.getElementById('email');
		var info = document.getElementById('info');
  1. 鼠标离开焦点以后,
// 当文本框离开焦点以后
		emailInp.onblur = function () {
			// 获取用户输入的邮箱地址
			var email = this.value;
			// 验证邮箱地址的正则表达式
			var reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$/;
			// 如果用户输入的邮箱地址不符合规则
			if (!reg.test(email)) {
				// 给出用户提示
				info.innerHTML = '请输入符合规则的邮箱地址';
				// 让提示信息显示为错误提示信息的样式
				info.className = 'bg-danger';
				// 阻止程序向下执行
				return;
			}
  1. 使用Ajax向服务器发送请求,首先需要引入ajax.js文件<script src="/js/ajax.js"></script>
// 向服务器端发送请求
			ajax({
				type: 'get',
				url: 'http://localhost:3000/verifyEmailAdress',
				data: {
				//email就是用户输入的邮箱地址
					email: email
				},
				success: function (result) {
					console.log(result);
					info.innerHTML = result.message;
					info.className = 'bg-success';
				},
				error: function (result) {
					console.log(result)
					info.innerHTML = result.message;//result是响应结果
					info.className = 'bg-danger';
				}
			});

在app.js文件中设置邮箱地址验证路由

//设置邮箱地址验证路由
app.get('/verifyEmailAdress', (req, res) => {
    // 接收客户端传递过来的邮箱地址
    const email = req.query.email;
    // 判断邮箱地址注册过的情况
    if (email == 'itheima@itcast.cn') {
        // 设置http状态码并对客户端做出响应
        res.status(400).send({ message: '邮箱地址已经注册过了, 请更换其他邮箱地址' });
    } else {
        // 邮箱地址可用的情况
        // 对客户端做出响应
        res.send({ message: '恭喜, 邮箱地址可用' });
    }
});

2. 搜索框内容自动提示

在这里插入图片描述

  1. 获取搜索框
// 获取搜索框
		var searchInp = document.getElementById('search');
		// 获取提示文字的存放容器
		var listBox = document.getElementById('list-box');
  1. 在用户文本框中输入的时候触发事件
searchInp.oninput = function () {}

获取用户输入的内容var key = this.value;

  1. 向服务器发送请求
// 向服务器端索取和用户输入关键字相关的内容
				ajax({
					type: 'get',
					url: 'http://localhost:3000/searchAutoPrompt',
					data: {
						key: key
					},
					success: function (result) {
						// 使用模板引擎拼接字符串 先引入模板
						var html = template('tpl', {result: result});
						// 将拼接好的字符串显示在页面中
						listBox.innerHTML = html;
						// 显示ul容器
						listBox.style.display = 'block';
					}
				})
			}, 800)
  1. 以上代码中的success方法中的函数:先引入模板引擎,然后写模板
<script src="/js/template-web.js"></script>
    <script type="text/html" id="tpl">
        {{each result}}
        <li class="list-group-item">{{$value}}</li>
        {{/each}}
    </script>

搜索框只要有输入就会立即触发oninput事件,为了使得输入完整再触发这个事件,所以在发送请求之前加入延时;所以把ajax({});代码放到了延时函数timer = setTimeout(function(){},800)里面,在每一次触发oninput事件的时候需要清除上一次开启的定时器;如果搜索框没有内容就隐藏下拉框

if (key.trim().length == 0) {
                // 将提示下拉框隐藏掉
                listBox.style.display = 'none';
                // 阻止程序向下执行
                return;
            }

在app.js中写好搜索框路由

// 输入框文字提示
app.get('/searchAutoPrompt', (req, res) => {
    // 搜索关键字
    const key = req.query.key;
    // 提示文字列表
    const list = [
        '黑马程序员',
        '黑马程序员官网',
        '黑马程序员顺义校区',
        '黑马程序员学院报名系统',
        '传智播客',
        '传智博客前端与移动端开发',
        '传智播客大数据',
        '传智播客python',
        '传智播客java',
        '传智播客c++',
        '传智播客怎么样'
    ];
    // 搜索结果
    let result = list.filter(item => item.includes(key));
    // 将查询结果返回给客户端
    res.send(result);
});

3. 省市三级联动

在这里插入图片描述

先准备获取省份,根据省份id获取城市,根据城市id获取县城路由


// 获取省份
app.get('/province', (req, res) => {
	res.json([{
		id: '001',
		name: '黑龙江省'
	},{
		id: '002',
		name: '四川省'
	},{
		id: '003',
		name: '河北省'
	},{
		id: '004',
		name: '江苏省'
	}]);
});

// 根据省份id获取城市
app.get('/cities', (req, res) => {
	// 获取省份id
	const id = req.query.id;
	// 城市信息
	const cities = {
		'001': [{
			id: '300',
			name: '哈尔滨市'
		}, {
			id: '301',
			name: '齐齐哈尔市'
		}, {
			id: '302',
			name: '牡丹江市'
		}, {
			id: '303',
			name: '佳木斯市'
		}],
		'002': [{
			id: '400',
			name: '成都市'
		}, {
			id: '401',
			name: '绵阳市'
		}, {
			id: '402',
			name: '德阳市'
		}, {
			id: '403',
			name: '攀枝花市'
		}],
		'003': [{
			id: '500',
			name: '石家庄市'
		}, {
			id: '501',
			name: '唐山市'
		}, {
			id: '502',
			name: '秦皇岛市'
		}, {
			id: '503',
			name: '邯郸市'
		}],
		'004': [{
			id: '600',
			name: '常州市'
		}, {
			id: '601',
			name: '徐州市'
		}, {
			id: '602',
			name: '南京市'
		}, {
			id: '603',
			name: '淮安市'
		}]
	}
	// 响应
	res.send(cities[id]);
});

// 根据城市id获取县城
app.get('/areas', (req, res) => {
	// 获取城市id
	const id = req.query.id;
	// 县城信息
	const areas = {
		'300': [{
			id: '20',
			name: '道里区',
		}, {
			id: '21',
			name: '南岗区'
		}, {
			id: '22',
			name: '平房区',
		}, {
			id: '23',
			name: '松北区'
		}],
		'301': [{
			id: '30',
			name: '龙沙区'
		}, {
			id: '31',
			name: '铁锋区'
		}, {
			id: '32',
			name: '富拉尔基区'
		}]
	};
	// 响应
	res.send(areas[id] || []);
});
  1. 获取省份信息
// 获取省市区下拉框元素
       var province = document.getElementById('province');
        var city = document.getElementById('city');
        var area = document.getElementById('area');
ajax({
            type: 'get',
            url: 'http://localhost:3000/province',
            success: function(data) {
                // 将服务器端返回的数据和html进行拼接
                var html = template('provinceTpl', {
                    province: data
                });
                // 将拼接好的html字符串显示在页面中,这里的province是id标签得到的
                province.innerHTML = html;
            }
        });
  1. 省份模板
<script type="text/html" id="provinceTpl">
        <option>请选择省份</option>
        {{each province}}
        <option value="{{$value.id}}">{{$value.name}}</option>
        {{/each}}
    </script>
  1. 为省份下拉框添加事件
    在这里插入图片描述
  2. 根据省份id获取城市信息
ajax({
                type: 'get',
                url: '/cities',
                data: {
                    id: pid
                },
                success: function(data) {
                    var html = template('cityTpl', {
                        city: data
                    });
                    city.innerHTML = html;
                }
            })
        };
  1. 城市模板
    在这里插入图片描述
  2. 县是一样的做法,

FormData

  1. FormData对象的作用
  • 创建普通表单,表单必须有name属性
    在这里插入图片描述
  • 获取按钮,获取表单,为按钮添加点击事件
    在这里插入图片描述
  • 在按钮点击事件里面将普通的html表单转换为表单对象
    在这里插入图片描述
  • 创建ajax对象,并且配置,发送请求,监听xhr的onload事件;在这里插入图片描述
  • 使用formidable模块接收客户端传递过来的FormData参数
    在这里插入图片描述
  1. FormData对象的实例方法;
    在这里插入图片描述
    在这里插入图片描述
    在属性已经存在的情况下,set会覆盖原有的属性,append保留原有属性;
  2. FormData二进制文件上传
    在这里插入图片描述
    实现文件上传路由
// 实现文件上传的路由
app.post('/upload', (req, res) => {
    // 创建formidable表单解析对象
    const form = new formidable.IncomingForm();
    // 设置客户端上传文件的存储路径
    form.uploadDir = path.join(__dirname, 'public', 'uploads');
    // 保留上传文件的后缀名字
    form.keepExtensions = true;
    // 解析客户端传递过来的FormData对象
    form.parse(req, (err, fields, files) => {
        // 将客户端传递过来的文件地址响应到客户端
        res.send({
            path: files.attrName.path.split('public')[1]
        });
    });
});
  1. FormData文件上传进度显示
// 在文件上传的过程中持续触发 bar是进度条元素
			xhr.upload.onprogress = function (ev) {
				// ev.loaded 文件已经上传了多少
				// ev.total  上传文件的总大小
				var result = (ev.loaded / ev.total) * 100 + '%';
				// 设置进度条的宽度
				bar.style.width = result;
				// 将百分比显示在进度条中
				bar.innerHTML = result;
			}
  1. 上传完成之后在页面显示
// 将客户端传递过来的文件地址响应到客户端
        res.send({
            path: files.attrName.path.split('public')[1]
        });

动态创建img标签

if (xhr.status == 200) {
                    // 将服务器端返回的数据显示在控制台中
                    var result = JSON.parse(xhr.responseText);
                    // 动态创建img标签
                    var img = document.createElement('img');
                    // 给图片标签设置src属性
                    img.src = result.path;
                    // 当图片加载完成以后
                    img.onload = function() {
                        // 将图片显示在页面中
                        box.appendChild(img);
                    }
                }

同源政策

Ajax请求限制,Ajax只能向自己的服务器发送请求。

同源: 页面有相同的协议、域名、端口。否则不同源

为什么有同源政策: 保证用户信息安全,防止恶意的网站窃取数据
Ajax不能向非同源服务器发送请求:
非同源请求
在这里插入图片描述

JSONP解决非同源限制问题

  • 使用JSONP解决同源限制问题;不属于Ajax请求,但是可以模拟Ajax请求;
  • 将不同源的服务器端请求地址写在script标签src属性中在这里插入图片描述
    在这里插入图片描述
  • 服务器端响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数
    在这里插入图片描述
    在这里插入图片描述
    以上要发送给客户端的数据是{name:“张三”,age:“20”}
  • 在客户端全局作用域下定义函数 fn
    在这里插入图片描述
  • 在 fn 函数内部对服务器端返回的数据进行处理
    在这里插入图片描述

JSONP代码优化

在这里插入图片描述
在这里插入图片描述
在客户端将script中的代码更改为,客户端的函数名字改变不会影响服务器端;
在这里插入图片描述
对应的s2 app.js文件里面的/better路由需要:
在这里插入图片描述
封装jsonp
在这里插入图片描述
调用jsonp jsonp({url:’http://localhost:3001/better?callback=fn2'});jsonp封装进行优化
在这里插入图片描述
jsonp里面有data需要拼接数据,
在这里插入图片描述
然后放到请求地址中在这里插入图片描述

服务端的代码优化

调用jsonp方法;res.jsonp({ name: 'lisi', age: 20 });就相当于前面的接受了客户端传过来的函数名称,调用这个函数,并且返回给客户端;

CORS跨域资源共享解决非同源问题

CORS跨域资源共享,允许浏览器向跨域服务器发送Ajax请求,克服了Ajax只能同源使用的限制;
点击按钮发送ajax请求,ajax.js请求文件自己写好
在这里插入图片描述
在app.js当中没有写cross路由,也没有设置头就会报下面的错误
在这里插入图片描述
上面的错误表示从3000来的向3001的请求没有被允许;所以需要设置响应头来允许指定的访问来源
在这里插入图片描述
这里需要注意这个拦截所有请求需要放到所有路由的上面*代表允许所有客户端访问;

案例之腾讯天气获取

把jsonp函数单独封装在一个文件中,即可使用;

  1. 向服务端获取天气信息
    在这里插入图片描述
  2. 处理时间格式
    在这里插入图片描述

访问非同源数据服务器端解决方案

同源 政策是浏览器给予Ajax技术的限制
由自己的客户端去访问自己的服务器端:
在这里插入图片描述
在自己的服务器模块中写server路由文件,由自己的服务器去访问另外一个服务器,使用nodejs的第三方模块request 首先需要在s1文件目录下安装 npm install request
在这里插入图片描述

跨域中的cookie复习

在这里插入图片描述

withCredentials属性

在使用Ajax技术发送跨域请求时,默认情况不会在请求中带cookie信息;
withCredentials:指定在涉及到跨域请求时,是否携带cookie信息,默认值是false;
实现跨域登录功能
在这里插入图片描述
输入设置好的用户及密码之后,再点击检测用户登录状态便可以显示处于登录状态;
在这里插入图片描述
在检测路由里面判断req.session.isLogin布尔值;还需要在之前写的拦截所有请求里面写入代码res.header('Access-Control-Allow-Credentials', true);


总结

客户端模板的使用,FormData的使用,以及解决非同源问题的三种方法,一种是jsonp模拟Ajax请求,服务器返回的必须是一个函数的调用;第二种是CORS跨域请求,设置响应头信息;第三种是服务器端解决非同源问题,利用自己服务器去访问另外一个服务器,需要借助nodejs的formidable插件;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值