本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
5、jsonp的实现原理
jsonp是跨域的非官方解决方案,只支持get请求
5.1jsonp是如何工作的?
在网页有一些标签天生具有跨域能力,比如:img link iframe script。
JSONP就是利用script标签的跨域能力来发送请求的。
举个例子:比如引入axios的script标签,引入的是一个远程资源
5.2jsonp的使用
1、动态的创建一个script标签
var script=document. createElement("script");
2、设置script的src,设置回调函数
script.src="http://localhost:3000/testAJAX?calback=abc";
ps:使用jsonp返回代码给html文件需要返回完整的html代码,而不是简单的字符串
如
response.send('hello jsonp-server');//会报错
response.send('console.log("hello")');//不会报错
原因是浏览器引擎只能解析HTML的内容,所以服务端发送过来的内容必须是HTML语句
script.onload = function(){
//将body中的script标签删除
document.body.removeChild(script)
}
5.3jsonp封装
//封装jsonp函数
function jsonp(option){
//动态创建javascript标签
var script = document.createElement('script');
var fnName = 'myjsonp'+Math.random().toString().replace('.','');
var params = '';
for(var attr in option.data){
params += '&'+attr + '=' + option.data[attr];
}
//success函数已经不是全局函数了,要想办法把它变成全局函数
//window对象新建一个fn属性,这个属性是一个函数
window[fnName] = option.success; //变量不能用点来引用
//添加src属性
script.src = option.url + '?callback='+fnName + params;
//将script标签追加到页面中
document.body.appendChild(script);
script.onload = function(){
document.body.removeChild(script);
}
}
var btn1 = document.getElementById('btn1')
btn1.onclick = function(){
jsonp({
url:"http://localhost:8000/better",
success:function(data){
console.log(123);
},
data:{
name:'hgc',
age:20
}
})
}
6、解决跨域问题的官方方案CORS
CORS是通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
app.get('/server',(request,response)=>{
//设置响应头允许跨域
response.setHeader('access-control-allow-origin','*');
response.send('hello ajax');
});
CORS的几种响应头:
1、access-control-allow-origin--设置允许跨域
Access-Control-Expose-Headers--让服务器把允许浏览器访问的头放入白名单,例如:
Access-Control-Expose-Headers:X-My-Custom-Header,X-Another-Custom-Header
否则浏览器只能访问一些最基本的响应头
2、Access-Control-Max-Age--指定了preflight请求的结果能够被缓存多久
Access-Control-Max-Age:<delta-seconds>
delta-seconds表示preflight请求的结果在多少秒内有效
3、Accese-control-A1low-Methods--首部字段用于预检请求的响应。其指明了实际请求所允许使用的HTTP方法。
如
Access-Control-Allow-Methods:<method>[,<method>]*//*表示任何请求方法都被允许
\
4、Access-Control-Allow-Headers--允许发送自定义响应头
Access-Control-Allow-Methods:<filename>[,<filename>]*//*表示允许用户发送任意请求头,可以是预定义的也可以是自定义的
7、ajax封装
发送一次请求代码过多,发送多次请求代码重复
解决方案:将代码封装到函数中,发请求时调用函数即可
ajax({
type:'get',
//设置url为jsonp文件夹中的server.js中创建的服务器中的/server路由
url:'http://127.0.0.1:7000/server',
param:{
name:'张三',
age:'20'
},
header:{
'Content-Type':'application/json'//用户想向服务器端传递json格式类型的请求参数
},
success:function(data){
console.log('这里是success函数');
console.log(data);
},
error:function(data,xhr){
console.log('这里是error函数');
console.log(xhr);
}
})
示例:
<body>
<script type="text/javascript">
function ajax(options){
var defaults = {
type:'get',
url:'',
param:'',
header:
{
'Content-Type':'application/x-www-form-urlencoded'
},
success:function(){},
error:function(){},
}
Object.assign(defaults,options);//使用options对象中的属性覆盖default对象中的属性
//创建ajax对象
var xhr = new XMLHttpRequest();
//用来拼接请求参数的变量
var params ='';
for(var attr in defaults.param){
params += attr + '=' + defaults.param[attr] + '&';
}
//将最后的&截取掉再赋值给params
params = params.substr(0,params.length-1);
xhr.type = defaults.type;
//如果是get请求则参数直接放url后面
if(defaults.type == 'get'){
defaults.url =defaults.url + '?' + params;
}
//配置ajax对象
xhr.open(defaults.type,defaults.url);
//发送请求
if(defaults.type == 'post'){
//用户希望向服务器端传递的参数类型
var ContantType = defaults.header['Content-Type'];
//设置请求参数格式的类型
xhr.setRequestHeader('Content-Type',ContantType);
if(ContantType == 'application/json'){
xhr.send(JSON.stringify(defaults.param));
}else{
xhr.send(params);
}
}else{xhr.send()}
//监听xhr对象下面的onload事件
//当xhr对象接收完响应数据后触发
xhr.onload = function(){
//获取响应头中的数据
var ContentType =xhr.getResponseHeader('Content-Type')
// console.log(xhr.getResponseHeader('Content-Type') );
//服务器返回的数据
var responseText = xhr.responseText
if(ContentType.includes('application/json')){
responseText = JSON.parse(responseText)
}
//当http状态码为200时
if(xhr.status == 200)
{
//请求成功调用处理成功情况的函数
defaults.success(responseText,xhr);
}
else{
//请求失败调用处理失败情况的函数
defaults.error(responseText,xhr);
}
}
}
</script>
</body>
需要注意:
get请求的请求参数可以拼接在url后面,但是post请求的请求参数是放在请求体中
请求参数位置问题:
将请求参数传递到ajax函数内部,在函数内部根据请求方式的不同将请求参数放在不同的位置
get:放在请求地址后面
post:放在send方法中
请求参数格式问题 :
格式一:
application/x-www-form-urlencoded
参数名称=参数值&参数名称=参数值
如:name=zhangsan&age=20
格式二:
application/json
如:{name:‘zhangsan',age:20}
小结:
1.传递对象数据类型对于函数的调用者更加友好
2.在函数内部对象数据类型转换为字符串数据类型更方便
8、模板引擎
使用步骤:
1、下载art-template引擎并在html页面中用script标签引入库文件
2、准备art-template模板
3、告诉模板引擎哪个数据和哪个模板进行拼接
4、将拼接好的html字符串添加到页面中
5、通过模板语法告诉模板引擎,数据和html字符串要如何拼接
<!-- 引入库文件 -->
<script src="./template-web.js"></script>
</head>
<body>
<div id="container"></div>
<!-- 准备art-template模板 -->
<script id="tpl" type="text/html">
<h1>{{username}} {{age}}</h1>
</script>
<!-- 告诉模板引擎将哪个数据和哪个模板进行拼接 -->
<!-- 格式:(1)模板id (2)数据(对象类型) -->
<script type="text/javascript">
//template方法的返回值就是拼接好的字符串
var html = template('tpl',{username:'zhangsan',age:30})
document.getElementById('container').innerHTML = html;
</script>
</body>
8.2模板引擎中的语法
art-template支持两种语法:标准语法和原始语法。
标准语法更好写,原始语法具有强大的逻辑处理能力
8.2.1标准语法:
<h1>{{value}}</h1>
//语法内部可以进行简单的运算
<h1>{{a?b:c}}</h1>
<h2>{{a+b}}</h2>
//原文输出
//如果数据中带html标签,模板引擎默认不会解析标签,会将其转义后输出
{{@数据}}
//在模板中可以根据条件来决定显示哪块html代码
{{if 条件}}
...
{{/if}}
{{if v1}}...{{else if v2}}...{{/if}}
//用来循环数据库返回的数据是数组的情况
{{each target}}
{{$index}} {{$value}}//index代表当前循环的索引,value代表当前循环的数据
{{/each}}
例
<!-- 准备art-template模板 -->
<script>
<ul>
{{each users}}
<li>{{$value.name}}</li>
<li>{{$value.age}}</li>
<li>{{$value.sex}}</li>
{{/each}}</ul>
</script>
<script>
const html=template(views,{
users:[{
name:‘张三‘,age:20,sex:‘男
},{
name:‘李四’,age:30,sex:‘男
},{
name:‘玛丽‘,age:15,[sex:'女
}]
});
</script>
8.2.2原始语法
<h1><%=value %></h1>
<h1><%=a?b:c %></h1>
<h2><%=a+b %></h2>
<%-数据 %>
8.3子模板
使用子模板可以将网站公共区块(头部/底部)抽离到单独的文件中
标准语法
{{include '模板'}}
//例:
{{include '/header.art}}
原始语法
<%include('模板')%>
//例:
<%include('/header.art')%>
8.4模板继承
使用模板继承可以将网站html骨架抽离到单独的文件中,其他页面模板可以继承骨架文件
步骤:
1、填充css内容(main.css)
2、填充js内容(index.js)
3、填充页面主体内容(
hello
)标准语法
{{extend './xxx.art'}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
{{block 'head'}}{{/block}}
</head>
<body>
{{block 'content'}}{{/block}}
</body>
</html>
//先继承挖坑的文件
{{extend './layout.art'}}
{{block 'content'}}
...要填充的内容,如:
<p>{{msg}}</p>
{{/block}}
总结:
在骨架模板使用<block 'name'>标签挖坑,
在具体的.art文件继承骨架然后根据业务需求填坑
9、FormData对象
9.1FormData对象的作用
1.模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单对象中的数据拼接成请求参数的格式。
2、异步上传二进制文件
9.2FormData对象的使用
1、准备html表单
2、将html表单转换为FormData对象
FormData对象可以接受一个form元素作为参数,构造函数会将表单中的数据拼接成请求参数所需的格式
3、提交表单对象
xhr.send(formdata);
ps:
formdata对象不能用于get请求
9.3FormData对象的实例方法
formData.get('key');
formData.set('key','value');
//如果设置的表单属性存在就会替换原来的值,如果设置的表单属性不存在就会创建这个属性
formData.delate('key');
formData.append('key','value');
ps:
set和append的区别是,在属性名已存在的情况下,set 会覆盖已有键名的值,append会保留两个属性及其值,但是如果同个属性设置两次值,后端默认只会接收最后一次设置的那个属性值
9.4、FormData二进制文件上传
<input type="file" id = 'file'/>
<script>
var file = document.getElementById('file')
file.onchange - function(){
//创建空表单对象
var formData = new FormData();
//将用户选择的二进制文件追加到表单对象中
formData.append('attrName',this.files[0]);
//配置ajax对象,请求方式必须为post
var xhr = new XMLHttpRequest();
xhr.open('post','http://localhost:3000/upload')
//发送ajax请求
xhr.send(formData);
//监听服务器端的响应
xhr.onload = function(){
if(xhr.status === 200){
//讲服务器端返回的数据显示在控制台中
console.log(xhr.responseText);
}
}
}
</script>
// 实现文件上传的路由
app.post('/upload', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', "*")
res.setHeader("Access-Control-Allow-Headers", "content-type");
// 创建formidable表单解析对象
const form = new formidable.IncomingForm();
// 设置客户端上传文件的存储路径
form.uploadDir = path.join(__dirname,'uploads');
// 保留上传文件的后缀名字
form.keepExtensions = true;
// 解析客户端传递过来的FormData对象
form.parse(req, (err, fields, files) => {
res.send('ok');
});
});
注意路径问题!
向模板中开放外部变量
格式:
template.defaults.imports.dataFormat = dataFormat;
//template.defaults.imports写法是固定的,.dataFormat是要追加的属性是自定义的,等号右边是处理函数的名称
处理函数:
function dataFormat(data){
var year = data.substr(0,4);
var month = data.substr(4,2);
var day = data.substr(6,2);
var hour = data.substr(8,2);
var min = data.substr(10,2);
var sec = data.substr(12,2);
return year+'年'+month+'月'+day+'日'+hour+'时'+min+'分'+'秒';
//必须要return,return什么页面就显示什么
}
<td>{{dataFormat($value.update_time)}}</td>
//调用处理函数是要把需要处理的数据作为参数传进去
withCredentials属性
在使用Ajax技术发送跨域请求时,默认情况下不会在请求中携带cookie信息。
withCredentials:指定在涉及到跨域请求时,是否携带cookie信息,默认值为false Access-Control-Allow-Credentials:true 允许客户端发送请求时携带cookie