Ajax,跨域,nrm

时间:2023-03-09 08:12:53
Ajax,跨域,nrm

一、ajax 原理 和 使用

ajax,即在不重新加载整个网页的情况下,对网页的某部分进行更新。

下面演示ajax 的实现原理

配置:

cd ajax

参考:http://www.expressjs.com.cn/starter/generator.html

express --view=ejs myapp
cd myapp
npm install

1、完整的ajax流程:

(1)、 创建XMLHttpRequest对象

var xhr = new XMLHttpRequest();

(2)、准备发送

 xhr.open('get', './01check.js?username='+uname+'&password='+pw,true);

Ajax,跨域,nrm

(3)、执行发送动作

 xhr.send(null);

Ajax,跨域,nrm

(4)、监听response 回调函数

onreadystatechange 事件:每当 readyState 改变时,就会触发 onreadystatechange 事件。

Ajax,跨域,nrm

2、代码逻辑

index.js 路由

......
router.get('/api/one',(req,res,next)=>{
res.json({
status:200,
result:'this is one.....'
})
}); /* GET home page. */
router.get('/one', function(req, res, next) {
res.render('index1', { title: 'Express' });
})
...........

index.ejs:

<body>
<button id="send">发送1个请求</button>
<script>
var btn = document.getElementById('send');
btn.addEventListener('click',function () {
// 使用原生的ajax 技术,发送一个网络请求
// 1.创建XMLHttpRequest对象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
} // 2.准备发送
/*
参数1: 请求方式 (get/post)
参数2: 请求url
参数3: 是否异步
*/
xhr.open('get','http://localhost:3000/api/one',true); // 3. 发送
xhr.send(); // 4. 监听服务器的响应
// 一旦服务器响应回来之后, 就会执行这个函数
xhr.onreadystatechange = function () {
console.log(xhr.readyState);
if(xhr.readyState === 4){ // 代表服务器已经给了响应, 不代表响应成功
if(xhr.status === 200){
console.log(xhr.response);
}
}
} });
</script>
</body> // 结果:
/*
2
3
4
{"status":200,"result":"this is one....."}
*/


index.js 路由:

router.get('/api/two',(req,res,next)=>{
console.log(req.query);
res.json({
status:200,
result:'this is two.....'
})
}); router.get('/two', function(req, res, next) {
res.render('index2', { title: 'Express' });
});

index2.ejs

<body>
<input id="account" type="text" name="account">
<input id="pwd" type="text" name="pwd">
<button id="send">发送一个请求</button>
<script>
window.onload=function () { var btn = document.getElementById('send');
btn.onclick = function () {
// 使用原生的ajax 技术,发送一个网络请求
// 1.创建XMLHttpRequest对象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
} // 从页面获取需要传递的数据
var userName = document.getElementById('account').value;
var pwd = document.getElementById('pwd').value; // 2.准备发送
/*
参数1: 请求方式 (get/post)
参数2: 请求url
参数3: 是否异步
*/
// 后面跟一个随机数值,保证每次发送ajax请求,都是真的发然后从响应中获取最新数据,而不是从缓存中取得
xhr.open('get', 'http://localhost:3000/api/two?account=' + account + '&pwd=' + pwd + '&random=' + getRandomStr(), true); // 3. 发送
xhr.send(); // 4. 监听服务器的响应
// 一旦服务器响应回来之后, 就会执行这个函数
xhr.onreadystatechange = function () {
// 5. 处理响应的数据 (对方说话)
console.log(xhr.readyState);
if(xhr.readyState === 4){ // 代表服务器已经给了响应, 不代表响应成功
if(xhr.status === 200){
console.log(xhr.response);
}
}
}
}
} function getRandomStr() {
return Math.random() + (new Date().getTime())
}
</script> </body>

前端打印:

2
3
4
{"status":200,"result":"this is two....."}

服务端打印:

{ account: '[object HTMLInputElement]',
pwd: '123456',
random: '1547356096169.2708' }
GET /api/two?account=[object%20HTMLInputElement]&pwd=123456&random=1547356096169.2708 200 8.824 ms - 42


封装ajax:

AjaxTool.js:

(function (window) {
function AjaxTool() {} AjaxTool.ajaxRequest = function (url, paramObj, timeout, successCallBack, errorCallBack) {
// 1.创建XMLHttpRequest对象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
} // 2.准备发送
/*
参数1: 请求方式 (get/post)
参数2: 请求url
参数3: 是否异步
*/
// 后面跟一个随机数值,保证每次发送ajax请求,都是真的发然后从响应中获取最新数据,而不是从缓存中取得
var params_str = getStrWithObj(paramObj);
var uri_str = url + '?' + params_str;
// 转码
var codeURI = encodeURI(uri_str)
xhr.open('get', codeURI, true); // 3. 发送
xhr.send(); // 4. 监听服务器的响应
xhr.onreadystatechange = function () {
// 5. 处理响应的数据
console.log(xhr.readyState);
if(xhr.readyState === 4){ // 代表服务器已经给了响应, 不代表响应成功
if(xhr.status === 200){
successCallBack(xhr);
}else {
errorCallBack(); }
}
} // 5. 控制请求的时间
var timer;
if(timeout > 0){
timer = setTimeout(function () {
// 取消ajax 请求
xhr.abort();
},timeout);
} }
/*
* 把对象转换成字符串
* */
function getStrWithObj(paramsObj) {
var arr = [];
for(var k in paramsObj){
var str = k + '=' + paramsObj[k];
arr.push(str);
}
arr.push('random='+ getRandomStr());
return arr.join('&');
} /*
返回一个随机数
* */
function getRandomStr() {
return Math.random() + (new Date().getTime())
} window.AjaxTool = AjaxTool;
})(window);

index3.ejs:

<body>
<input id="account" type="text" name="account">
<input id="pwd" type="text" name="pwd">
<button id="send">发送一个请求</button>
<script>
window.onload=function () { var btn = document.getElementById('send');
btn.onclick = function () {
// 使用原生的ajax 技术,发送一个网络请求
// 1.创建XMLHttpRequest对象
var xhr;
if(window.XMLHttpRequest){
xhr = new XMLHttpRequest();
}else {
xhr = new ActiveXObject('Microsoft.XMLHTTP');
} // 从页面获取需要传递的数据
var userName = document.getElementById('account').value;
var pwd = document.getElementById('pwd').value; // 2.准备发送
/*
参数1: 请求方式 (get/post)
参数2: 请求url
参数3: 是否异步
*/
// 后面跟一个随机数值,保证每次发送ajax请求,都是真的发然后从响应中获取最新数据,而不是从缓存中取得
xhr.open('get', 'http://localhost:3000/api/two?account=' + account + '&pwd=' + pwd + '&random=' + getRandomStr(), true); // 3. 发送
xhr.send(); // 4. 监听服务器的响应
// 一旦服务器响应回来之后, 就会执行这个函数
xhr.onreadystatechange = function () {
// 5. 处理响应的数据 (对方说话)
console.log(xhr.readyState);
if(xhr.readyState === 4){ // 代表服务器已经给了响应, 不代表响应成功
if(xhr.status === 200){
console.log(xhr.response);
}
}
}
}
} function getRandomStr() {
return Math.random() + (new Date().getTime())
}
</script> </body>

3、简单版原生 post方式

<body>
<input id="account" type="text" name="account">
<input id="pwd" type="text" name="pwd">
<button id="send">发送一个请求</button>
<script>
window.onload=function () {
var btn = document.getElementById('send');
btn.onclick = function () {
// 1. 获取数据
var account = document.getElementById('account').value;
var pwd = document.getElementById('pwd').value; // 第一步
var xhr = new XMLHttpRequest();
// 第二步
xhr.open('post','http://localhost:3000/api/four',true)
// 第三步
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('account='+account + '&pwd=' + pwd); // 第四步
xhr.onreadystatechange = function () {
console.log(xhr.readyState);
console.log(xhr.status);
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.responseText);
}else {
console.log('请求失败');
}
} }
} </script> </body>

4、ajax 完整封装

(function(window){
function AjaxTool(){}
AjaxTool.ajaxRequest = function(params,successCallBack,errorCallBack){
// 0. 获取参数
var requestType = params['requestType'] || 'get';
var url = params['url'];
var paramsObj = params['paramsObj'];
var timeout = params['timeout'];
console.log(params);
// 1.创建XMLHttpRequest对象 (找到一个电话)
var xhr;
if(window.XMLHttpRequest){
xhr =new XMLHttpRequest();
}else{
xhr = new ActiveXObject('Microsoft.XMLHTTP');
} // 2.判断请求方式
if(requestType.toLowerCase() === 'get'){
var codeURI = encodeURI(url + '?' + getStrWithObject(paramsObj));
xhr.open('get',codeURI,true);
xhr.send();
}else if(requestType.toLowerCase() === 'post'){
// 请求体
var codeParam = encodeURI(getStrWithObject(paramsObj));
xhr.open('post',url,true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
console.log(codeParam);
xhr.send(codeParam); } // 3. 监听服务器响应
xhr.onreadystatechange = function(){
// 3.1 处理响应数据
if(xhr.readyState === 4){
// 4 代表服务器给响应了,并不代表响应成功
if(xhr.status === 200){
successCallBack(xhr);
// 清除定时器
clearTimeout(timer);
}else{
errorCallBack();
}
}
} // 4. 控制响应时间
var timer;
if(timeout > 0){
timer = setTimeout(function(){
// 取消ajax请求
xhr.abort();
},timeout);
} } /*
返回一个随机数
*/
function getRandomStr(){
return Math.random() + (new Date().getTime());
} /*
把对象转成字符串
*/
function getStrWithObject(paramsObj){
var arr = [];
for(var key in paramsObj){
var str = key + '=' + paramsObj[key];
arr.push(str)
}
arr.push('random=' + getRandomStr())
return arr.join('&');
}
window.AjaxTool = AjaxTool;
})(window)

index5.ejs:

<body>
<input id="account" type="text" name="account">
<input id="pwd" type="text" name="pwd">
<button id="send">发送一个请求</button>
<script src="../js/AjaxTool.js"></script>
<script>
window.onload=function () {
var btn = document.getElementById('send');
btn.onclick = function () {
// 1. 获取数据
var account = document.getElementById('account').value;
var pwd = document.getElementById('pwd').value; // 2. 处理数据
var paramsObj = {
'account':account,
'pwd':pwd
} var params = {
'requestType':'post',
'url':'http://localhost:3000/api/five',
'paramsObj':paramsObj,
'timeout':2000
} AjaxTool.ajaxRequest(params,function (xhr) {
console.log('成功' + xhr.responseText);
},function () {
console.log('失败');
}); }
} </script> </body>

index.js 服务端路由文件:

router.get('/five',(req,res,next)=>{
res.render('index5')
}); router.post('/api/five',(req,res,next)=>{
console.log(req.body);
res.json({
status:200,
result:'this is five...method:post..'
})
});

访问:http://localhost:3000/five

输入admin 123456 ,然后提交

前端打印:成功{"status":200,"result":"this is five...method:post.."}

服务端打印:{ account: 'admin', pwd: '123123', random: '1547522571613.208' }

5、用 jquery 进行 ajax 请求

三方封装的是最完善的,我们用三方封装好的即可,但是对ajax原生的执行的大致逻辑知道一些比较好。

index6.ejs:

<body>
<form action="http://localhost:3000/api/six" method="post">
<input id="account" type="text" name="account">
<input id="pwd" type="text" name="pwd">
<input id="send" type="submit" value="请求">
</form>
<script src="/js/jquery.js"></script>
<script>
$('form').on('submit', function () {
console.log($(this).serialize()); //account=admin&pwd=123456
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
data: $(this).serialize(),
success: function (data) {
console.log(data); //{status: 200, result: "this is six...."}
}
}); // 阻止默认事件
return false;
});
</script>

服务端:

router.post('/api/six',(req,res,next)=>{
console.log(req.body);
res.json({
status:200,
result:'this is six....'
})
});

二、跨域

1、什么是跨域

同源策略:

2、解决方案:

jsonp
document.domain+iframe :只有主域名相同的情况下方可使用此方法
location.hash + iframe
window.name + iframe
window.postMessage
flash等第三方插件

3、跨域演示

express-generator 快速创建一个应用的骨架server,然后在服务端index.js 路由中

router.get('/', function(req, res, next) {
// res.render('index', { title: 'Express' });
console.log(`收到客户端的请求: ${req.url}`);
res.end('end');
});

在本地新创建一个页面,不要通过和 server 端口一致

<body>
<button id="send">点我</button>
<script src="js/AjaxTool.js"></script>
<script>
window.onload = function () {
var btn = document.getElementById('send');
btn.onclick = function () {
var paramObj = {
'name':'zhangsan',
'pwd':'123'
};
var params = {
'requestType' : 'get',
'url' : 'http://localhost:3000/',
'paramObj' : paramObj,
timeout : 2000
}; // 发起请求
AjaxTool.ajaxRequest(params,function (xhr) {
console.log('success...'+ xhr.responseText);
},function () {
console.log('failed.....');
})
}
}
</script> </body>

结果:

服务端还是收到请求了,打印:

收到客户端的请求: /?name=zhangsan&pwd=123&random=1547690599507.3115

GET /?name=zhangsan&pwd=123&random=1547690599507.3115 - - ms - -

客户端打印如下图:

Ajax,跨域,nrm

4、jsonp 解决跨域问题

  • jsonp跨域必须前后端联合支持

  • jsonp只支持get不支持post

(1) 演示1

路由:

router.get('/',function (req,res,next) {
console.log(`收到客户端的请求: ${req.url}`);
var data = JSON.stringify({
status_code:200,
result:{
name:'张三',
age:19,
friend:['李四','王五','赵六']
}
});
res.end('getData('+data+')');
});

前端页面:

<body>
<script>
function getData(data) {
console.log(data);
}
</script>
<script src="http://localhost:3000/"></script>
</body>

通过 script 引入,来跨域请求,这个是可行的,同样的方式,还有 css标签的引入

Ajax,跨域,nrm

(2) 封装

路由:

router.get('/',function (req,res,next) {
console.log(`收到客户端的请求: ${req.url}`);
var data = JSON.stringify({
status_code:200,
result:{
name:'张三',
age:19,
friend:['李四','王五','赵六']
}
});
console.log(`${req.query.callback}(${data})`);
res.end(`${req.query.callback}(${data})`);
});

前端:

<body>
<script> jsonp({
url: 'http://localhost:3000/',
data: '',
success: function (data) {
console.log(11111);
console.log(data);
}
});
jsonp({
url: 'http://localhost:3000/',
data: '',
success: function (data) {
console.log(22222); console.log(data);
}
}); /*
* 1、让函数名称不一致
* 2、调用完成后删除
* */
function jsonp(option) {
// 1. 将用户通过对象的命名空间传递进来的函数 挂载到全局
var callbackName = 'hello_' + Math.random().toString().substr(2);
window[callbackName] = function (data) {
option.success(data);
// 删除 script 标签 ,多次调用后 script 太多了,在这里删除掉
document.body.removeChild(script);
}; // 1. 处理url的拼接
option.url = option.url + '?callback=' + callbackName;
// 2. 创建script标签
var script = document.createElement('script');
script.src = option.url;
document.body.appendChild(script); /*说明: 拼接完url后,url 会带上函数的名称,服务端根据url 取得函数名称,然后返回的时候
res.end(`${req.query.callback}(${data})`);
* */
} </script>
</body>

前端代码注释:

加断点可以查看执行流程,首先 jsonp({})执行,接着到下面的 function jsonp({}),在下面的 function jsonp() 里面, window[callbackName] = function (data) {} ,在这里只是声明了函数,并且是全局的,但是并没有调用,里面的东西是在后来服务端 响应后调用的,并接着调用 了 里面的 option.success(data) 的函数,因为这个时候,服务端返回的时候把参数data 也传回来了 res.end(${req.query.callback}(${data})); 。所以接着执行了上面的 jsonp({}) 里面的 success:function(){data} 的函数。

执行结果:

Ajax,跨域,nrm

三、nrm

什么是nrm:

install:

$ npm install -g nrm

查看有哪些源:

$ nrm ls

* npm -----  https://registry.npmjs.org/
cnpm ---- http://r.cnpmjs.org/
taobao -- https://registry.npm.taobao.org/
nj ------ https://registry.nodejitsu.com/
rednpm -- http://registry.mirror.cqupt.edu.cn
skimdb -- https://skimdb.npmjs.com/registry

选择镜像源:

$ nrm use cnpm  //switch registry to cnpm

    Registry has been set to: http://r.cnpmjs.org/

注意:每次安装包的时候,依然是使用 npm 命令来安装。