实现跨域的项目实践
1.功能描述:
实现在系统主窗口点击 “Export to excel” link,open一个新的窗口提示用户‘下载即将开始,正在下载’字样, 同时“Export to excel” link 灰显, 当新窗口下载完成后,提示下载成功或失败信息,同时主窗口的 “Export to excel” link 恢复正常,即可再次点击下载。
2.主要技术:
(1)html5的postMessage 实现两个跨域的通信,下载完成的通知
(2)用动态加载 <script> 标签引入require.js , 指定data-main 程序的主模块,引入主域的公共库文件,方法等
3.实现过程:
Main 窗口:
exportReportToExcel:function (e){ … var baseUrl = window.location.host; var protocol = window.location.protocol; var href = protocol + "//" + baseUrl; window.open(href+"/exportReportToExcel.html?"+reportId); //打开新的子窗口 }
Son 窗口:
<!Doctype html> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <link rel="stylesheet" type="text/css" href="/styles.css?x.x.x.css"/> <link type="text/css" rel="stylesheet" href="/exportStyle.css"/> <title>ACTIVE Works Camp & Class Manager</title> <script type="text/javascript"> var reportId = document.location.href.split("?")[1].split('&')[0]; //通过get方式先获取到reportId window.onbeforeunload =function() { //在即将离开当前页面(刷新或关闭)时执行,广发postMessage,内容就是这个reportId window.opener.postMessage(reportId,'*'); }; </script> </head> <body> <header id="globalHeader" class="show-html"> <nav id="topNav"> <img src="/static/images/camps/CampsLogo.png"> </nav> </header> <div class="downloadPrompt"> <p>Your Report is being generated. Please do not close this window.</p><br/> <p>Download will begin automatically as soon as your report has been generated.</p> <br/><br/> <div id="loadMessage" style="color:red;font-size:12px;"></div> </div> <!— 下面这块是实现一个失败时提示一个弹出框的特效 (遮罩,弹出框 ) —> <div id="bg"></div> <div class="promPit" id="promPit"> <div class="prompt"> <div class="title"> Could not download report <span class="closeIcon" onclick="closePrompt()">X</span> </div> <div class="content" id="content">Sorry, there was a problem trying to download your report. Please try again and contact technical support if the problem persists.</div> <div class="okbutton" onclick="closePrompt()">OK</div> </div> </div> <script> window.onload = function(){ closePrompt(); var newtimestamp = Date.parse(new Date()); var timestamp=document.location.href.split("?")[1].split('&')[1]; var d = (newtimestamp - timestamp)/1000; //when the time more than 5s, the href will failture. if(d > 30){ showPrompt('The link have grown stale. Please try again and contact technical support if the problem persists.'); return; }else{ document.getElementById('loadMessage').innerHTML = ""; var oBody = document.getElementsByTagName('body').item(0); var oScript= document.createElement("script"); oScript.type = "text/javascript"; oScript.src="/arch/requirejs/require.js"; oScript.setAttribute('data-main','exportToExcelMain.js')
//这里也关键,引入require.js, 并通过data-main属性,指定网页程序的主模块,这样主模块的一些组件就可以直接require进来使用 oBody.appendChild(oScript); } } function closePrompt(){ document.getElementById('promPit').style.display = "none"; document.getElementById('bg').style.display = "none"; } function showPrompt(str){ if(str){ document.getElementById('content').innerHTML = str; } document.getElementById('promPit').style.display = "block"; document.getElementById('bg').style.display = "block"; } </script> </body> </html>
exportToExcelMain.js 的实现:
"use strict"; require.config({ baseUrl: "./arch", waitSeconds: 180, paths: { 'underscore': 'lib/underscore', 'jquery': 'lib/jquery', 'WaitManager': '../camps/Application/WaitManager' }, shim: { 'jquery': { exports: 'jQuery' }, 'underscore': { deps: ['jquery'], exports: '_' } } }); define([ 'polling', 'jquery', 'underscore', 'WaitManager' ], //这是主域实现下载的通用方法,拿过来用就好了 function (polling, $, _, WaitManager) { var exportReport = function(reportId) { return $.ajax({ type: 'POST', url: '/service/json/aui/report/customerReport/'+reportId, contentType: 'application/json' }); }; var pollForCompleteReport = function(token) { var data = { pollForReportDto: { reportTokenDto: { value: token }, timeout: 1 } }; return $.ajax({ type:'POST', contentType: 'application/json', url:'json/ReportProxyService/pollForReportResult', data:JSON.stringify(data) }); }; var showPrompt = function(){ document.getElementById('promPit').style.display = "block"; document.getElementById('bg').style.display = "block"; }; WaitManager.startWaiting(); var reportId = document.location.href.split("?")[1].split('&')[0]; var fileId; var promise = exportReport(reportId); promise.then(function(response){ if(!response.isError){ var token = response.value; polling.start({ id:token, interval:1000, onPoll: function(onPollResult){ var promiseToken = pollForCompleteReport(token); promiseToken.then(function(pollingResult){ if(!pollingResult.isError){ fileId = pollingResult.fileId; onPollResult(!!fileId); }else{ WaitManager.stopWaiting(); window.opener.postMessage(reportId,'*'); // 关键在这里,下载失败后广发消息给主窗口,返回这个reportId showPrompt(); } }); }, onComplete: _.bind(function(){ WaitManager.stopWaiting(); window.opener.postMessage(reportId,'*'); // 关键在这里,下载成功后广发消息给主窗口,返回这个reportId downloadFinishedReport(fileId); $('#loadMessage').html('Download successful!'); }, this) }); }else { WaitManager.stopWaiting(); showPrompt(); } }); var downloadFinishedReport = function(fileId) { if (fileId.indexOf('FID=') !== -1) { window.location = '/sys/exported?' + fileId; } }; });
主窗口是怎么接收这个消息的?
看这里:
initialize: function(){ function receiveMessage(event){ console.log(event.data); $('a.exportexcel[data-id='+event.data+']').removeAttr('disabled').css('color','#008cd2').addClass('exportfinish'); //改变那条reportId 的按钮状态 } if (typeof window.addEventListener != 'undefined') { window.addEventListener('message', receiveMessage, false); //监听window的message事件,接收消息 } else if (typeof window.attachEvent != 'undefined') { //这里做了一下兼容IE //for ie window.attachEvent('onmessage', receiveMessage); } },