同源策略
同源策略是浏览器的一个安全功能,不同源的网页脚本在没有明确授权的情况下,不能读写对方资源。
同源是指"协议+域名+端口"三者相同。
什么是跨域
使用AJAX技术(XMLHttpRequest 对象),从一个网页去请求另一个网页资源时,违反浏览器同源策略限制,引起的安全问题,称为跨域。
跨域错误
前后端分离项目中:
跨域解决思路
1.不使用 AJAX技术(XMLHttpRequest)请求
JSONP技术
script标签img标签访问不同资源时不会引起跨域问题
JSONP技术实现
1.[前端部分]JSONP技术利用同源策略这一“漏洞”,secript标签天生可以跨域特点解决跨域问题
代码动态创建script标签,将请求url地址作为script标签src属性
<!-- <script src="http://10.7.162.67:3000/"></script> -->
<script>
sendProductList('http://10.7.162.67:3000')
function sendProductList(url){
let scriptEle = document.createElement('script') // <script>
scriptEle.setAttribute('src',url)
document.body.appendChild(scriptEle)
}
function callback(res){
let resObj = JSON.parse(res)
console.log(res)
console.log(resObj)
}
</script>
2.[后端部分]JSONP需要服务端支持
把真正的数据封装到一个函数中一起返回 callback({code:1,info:'helloworld'})
// 商品列表
let productList = [
{
number: 1001,
name: 'javascript高级编程',
url: 'https://img2.baidu.com/it/u=1527170010,3082273564&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
price: 88.98,
num: 0,
state: false,
},
{
number: 1002,
name: 'css高级编程',
url: 'https://img2.baidu.com/it/u=1209311252,2459534360&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
price: 58.58,
num: 0,
state: false,
},
{
number: 1003,
name: 'html高级编程',
url: 'https://img0.baidu.com/it/u=2950970616,2356748823&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=496',
price: 48.58,
num: 0,
state: false,
},
]
let http = require('http') // 引入内置http模块
// 创建一个web服务
let server = http.createServer(function (request, response) {
// response.setHeader('Access-Control-Allow-Origin', '*')// 跨域问题
response.writeHead(200, { 'content-type': 'text/html;charset=utf-8' })// 解决乱码
productList = JSON.stringify(productList) // 将数组转字符串
response.write(`callback(${productList})`) // 封装 向响应对象写入数据helloworld
response.end() // 结束写入,发送数据给请求客户端
})
// 启动web服务
server.listen(3000, () => console.log('web服务启动成功,监听3000端口...'))
缺点:只支持get请求
2.授权跨域资源共享
跨域资源共享CORS
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用sharing的限制。
3.代理服务器技术
回调函数
一个函数作为另一个函数参数,在另一个函数中调用执行,这个函数称为回调函数
function fun(cb) { //cb = function () {console.log('这是回调函数')}
console.log('fun')
cb()
}
fun(function () {
console.log('这是回调函数')
})
作用
1.将函数中的数据作为参数传给回调函数
function fun(cb) { cb = function () {console.log('这是回调函数')}
let num = 100 //函数中数据
cb(num)
}
fun(function (data) { // data =100
console.log('执行回调函数',data)
})
2.处理异步任务的结果
同步操作:一个任务执行完成后再执行下一个任务
异步操作:一个任务开始后,不等主程序执行完,继续向下执行
异步任务:定时器
function test1() {
console.log('烧开水')
setTimeout(function () {
console.log('买茶叶')
}, 1000)
console.log('主程序继续执行')
} //烧开水 主程序继续执行 买茶叶
异步任务:AJAX
let data // 接收响应结果变量
let xhr = new XMLHttpRequest()
xhr.open('get','http://10.7.162.150:8089/api/shop/list')
xhr.send() // 启动异步
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status === 200){
data = xhr.responseText
console.log('data 1111 ',data) //data 数据
}
}
}
console.log('data ',data) //data undefined
示例-封装jsonp
//<script src="http://10.7.162.150:8089/api/jsonp/list"></script>
class MyJsonp {
constructor(url) {
this.scriptEle = document.createElement('script') // <script>
this.scriptEle.setAttribute('src', url)
document.body.appendChild(this.scriptEle)
}
getResult(cb){//cb=function(data){}
window.callback = function(result){
// console.log('data >> ',data)
cb(result)
}
}
}
const myJsonp = new MyJsonp('http://10.7.162.150:8089/api/jsonp/list')
myJsonp.getResult(function(data){
console.log('data>>11',data)
})
AJAX封装
<script src="./myajax.js"></script>
<script>
ajax({
method: 'post',
url: 'http://10.7.162.67:8888/test/fourth',
data: {
name: 'rose',
age: 108,
},
success: function (data) {
console.log('data >> ', data)
},
})
</script>
myajax.js
function ajax(options) {
// 1. 创建XMLHttpRequest
let xhr = new XMLHttpRequest()
let param = formateParam(options.data) // name=jack&age=18
let method = options.method.toUpperCase()
// 2. 建立连接
if (method == 'GET') {
xhr.open(options.method, options.url + '?' + param)
// 3. 发送请求
xhr.send()
}else if(method == 'POST'){
xhr.open(options.method,options.url)
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
xhr.send(param)
}
// 4. 接收响应数据
xhr.onreadystatechange = function () {
// 4.1 是否响应完成
if (xhr.readyState === 4) {
// 4.2 是否成功响应
if (xhr.status === 200) {
let data = xhr.responseText // 响应内容
data = JSON.parse(data)
options.success(data)
} else {
alert('网络出错 ' + xhr.status)
}
}
}
}
/**
* 格式化参数
* {name:'jack',age:18} => name=jack&age=18
* 遍历对象,属性转换成名称=值形式,存储到数组, 再将数组元素用&符号拼接join('&)
* ['name=jack','age=18'] ->
*/
function formateParam(obj) {
let arr = []
for (const key in obj) {
let item = `${key}=${obj[key]}` // name=jack age=18
arr.push(item) // ['name=jack','age=18;]
}
return arr.join('&') // name=jack&age=18
}
前后端分离商品详情放大镜案例
<!-- index.html 商品列表 -->
<div class="container">
<!-- 动态渲染 -->
</div>
<script src="./js/myajax.js"></script>
<script src="./js/index.js"></script>
/** index.js
* 获取商品列表数据
*/
function getProductList() {
ajax({
method: 'get',
url: 'http://10.7.162.150:8089/api/shop/list',
success: function (res) {
showProductList(res.resultInfo.list)
},
})
}
/**
* 动态渲染商品列表
*/
function showProductList(productList) {
let list = productList.map(item => {
return `<div class="product-item" onclick="onDetail(${item.id})">
<img src="${item.picture}" alt="pic1">
<p>${item.product}</p>
<p>¥${item.price} ${item.putaway}人已买</p>
</div>`
})
const rootEle = document.querySelector('.container')
rootEle.innerHTML = list.join('')
}
/**
* 跳转详情页
* @param {*} id
*/
function onDetail(id) {
// 跳转详情页
location.href = './detail.html?id=' + id
}
getProductList()
<!-- detail.html-->
<div class="glass-wrapper">
<!-- 动态渲染 -->
</div>
<script src="./js/myajax.js"></script>
<script src="./js/glass.js"></script>
<script src="./js/detail.js"></script>
/** detail.js
* 获取商品id
* @returns
*/
function getProductId() {
let str = location.search // ?id=2
let id = str.split('=')[1]
return id
}
/**
* 获取商品详情
*/
function getProductDetail() {
let id = getProductId()
ajax({
method: 'get',
url: 'http://10.7.162.150:8089/api/shop/find',
data: {
id,
},
success: function (res) {
showProductDetail(res.resultInfo)
},
})
}
/**
* 动态渲染详情
*/
function showProductDetail(product) {
let str = `<div class="container" id="glass1">
<div class="left-wraper">
<div class="m-left">
<img src="${product.picture}" alt="show1" />
<!-- 遮罩层 -->
<div class="mask"></div>
</div>
<ul>
<li class="active"><img src="${product.list[0]}" alt="small1" /></li>
<li><img src="${product.list[1]}" alt="small2" /></li>
<li><img src="${product.list[2]}" alt="small3" /></li>
<li><img src="${product.list[3]}" alt="small4" /></li>
</ul>
</div>
<!-- 放大镜 -->
<div class="right-wraper">
<img src="${product.picture}" alt="big1" />
</div>
</div>`
const detailWraper = document.querySelector('.glass-wrapper')
detailWraper.innerHTML = str
// 数据动态渲染完成后,再操作放大镜节点
let glass = new Glass('#glass1')
glass.setScale()
glass.onGlass()
glass.onTab(product)
}
getProductDetail()
/** glass.js
* 放大镜类
*/
class Glass {
constructor(id) {
this.rootEle = document.querySelector(id)
this.mask = this.rootEle.querySelector('.mask') //mask遮罩层
this.showBox = this.rootEle.querySelector('.m-left') //showbox显示盒子
this.glassBox = this.rootEle.querySelector('.right-wraper') // 放大镜glassBox
this.bgPic = this.rootEle.querySelector('.right-wraper>img') //背景图bgpic
this.ulLis = this.rootEle.querySelectorAll('.left-wraper ul>li')
this.showBoxPic = this.showBox.querySelector('img')
}
/**
* 计算放大镜图片的比例
* 目的: 遮罩层区域遮罩区域大小 与 放大镜放大区域大小相同
* 遮罩层mask 放大镜
* -------------- = -----------------
* 显示盒子showBox 背景图 ?
* 背景图 = 放大镜* showBox / mask
*/
setScale() {
// 遮罩层mask
let maskW = parseInt(window.getComputedStyle(this.mask).width)
let maskH = parseInt(window.getComputedStyle(this.mask).height)
// 显示盒子showBox
let showBoxW = parseInt(window.getComputedStyle(this.showBox).width)
let showBoxH = parseInt(window.getComputedStyle(this.showBox).height)
// 放大镜glassBox
let glassBoxW = parseInt(window.getComputedStyle(this.glassBox).width)
let glassBoxH = parseInt(window.getComputedStyle(this.glassBox).height)
// 背景图
let bgPicW = (glassBoxW * showBoxW) / maskW
let bgPicH = (glassBoxH * showBoxH) / maskH
this.bgPic.style.width = bgPicW + 'px'
this.bgPic.style.height = bgPicH + 'px'
}
/**
* 遮罩层移光标移动
* offsetX offsetY 相对自身
* clientX clientY 浏览器
* pageX pageY 页面
*
* window.getComputedStyle(ele).width
* offsetWidth
* clientWidth
*/
onGlass() {
let _this = this
// 移入显示
this.showBox.addEventListener('mouseover',()=>{
this.mask.style.display = 'block'
this.glassBox.style.display = 'block'
})
// 移出隐藏
this.showBox.addEventListener('mouseout',()=>{
this.mask.style.display = 'none'
this.glassBox.style.display = 'none'
})
//showBox鼠标移动事件
this.showBox.addEventListener('mousemove', function (e) {
e = e || window.event
let x = e.offsetX - _this.mask.clientWidth / 2
let y = e.offsetY - _this.mask.clientHeight / 2
// 边界检查
if (x < 0) {
x = 0
}
if (x > _this.showBox.clientWidth - _this.mask.clientWidth) {
x = _this.showBox.clientWidth - _this.mask.clientWidth
}
if (y < 0) {
y = 0
}
if (y > _this.showBox.clientHeight - _this.mask.clientHeight) {
y = _this.showBox.clientHeight - _this.mask.clientHeight
}
// 移动遮罩层
_this.mask.style.left = x + 'px'
_this.mask.style.top = y + 'px'
// 移动背景图片
/*
遮罩层 遮罩层移动距离
------ = -----------------
放大镜 背景图移动距离?
背景图移动距离= 遮罩层移动距离*放大镜/遮罩层
*/
// 遮罩层mask
let maskW = parseInt(window.getComputedStyle(_this.mask).width)
let maskH = parseInt(window.getComputedStyle(_this.mask).height)
// 放大镜glassBox
let glassBoxW = parseInt(window.getComputedStyle(_this.glassBox).width)
let glassBoxH = parseInt(window.getComputedStyle(_this.glassBox).height)
let moveX = x * glassBoxW / maskW
let moveY = y * glassBoxH / maskH
_this.bgPic.style.left = -moveX + 'px'
_this.bgPic.style.top = -moveY + 'px'
})
}
/**
* 切换图片
*/
onTab(product){
let _this = this
for(let i = 0; i < this.ulLis.length; i++){
this.ulLis[i].addEventListener('mouseover',function(){
// 清除所有选中效果
_this.onClear()
// 当前选设置选中效果
this.className = 'active'
// 显示盒子图片
_this.showBoxPic.setAttribute('src',`${product.list[i]}`)
// 背景图片切换
_this.bgPic.setAttribute('src',`${product.list[i]}`)
})
}
}
onClear(){
for(let i = 0; i < this.ulLis.length; i++){
this.ulLis[i].className = ''
}
}
}
//ajax.js
function ajax(options) {
// 1. 创建XMLHttpRequest
let xhr = new XMLHttpRequest()
let param = formateParam(options.data) // name=jack&age=18
let method = options.method.toUpperCase()
// 2. 建立连接
if (method == 'GET') {
xhr.open(options.method, options.url + '?' + param)
// 3. 发送请求
xhr.send()
}else if(method == 'POST'){
xhr.open(options.method,options.url)
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
xhr.send(param)
}
// 4. 接收响应数据
xhr.onreadystatechange = function () {
// 4.1 是否响应完成
if (xhr.readyState === 4) {
// 4.2 是否成功响应
if (xhr.status === 200) {
let data = xhr.responseText // 响应内容
data = JSON.parse(data)
options.success(data)
} else {
alert('网络出错 ' + xhr.status)
}
}
}
}
/**
* 格式化参数
* {name:'jack',age:18} => name=jack&age=18
* 遍历对象,属性转换成名称=值形式,存储到数组, 再将数组元素用&符号拼接join('&)
* ['name=jack','age=18'] ->
*/
function formateParam(obj) {
let arr = []
for (const key in obj) {
let item = `${key}=${obj[key]}` // name=jack age=18
arr.push(item) // ['name=jack','age=18;]
}
return arr.join('&') // name=jack&age=18
}
/*index.css*/
*{padding: 0;margin: 0;}
.container{
width: 1200px;
margin: 100px auto;
display: flex;
flex-wrap: wrap;
}
.container .product-item{
width: 260px;
height: 440px;
background-color: rgb(242,246,244);
margin-right: 35px;
cursor: pointer;
}
.container .product-item img{
width: 100%;
}
/*glass.css*/
*{padding: 0;margin: 0;}
ul,li{list-style: none;}
.glass-wrapper .container{
width: 1000px;
margin: 100px auto;
display: flex;
}
.glass-wrapper .container .m-left{
position: relative;
width: 350px;
height: 350px;
}
.glass-wrapper .container .m-left img{
width: 100%;
height: 100%;
}
.glass-wrapper .container .m-left ol{
position: relative;
width: 100%;
height: 100%;
}
.glass-wrapper .container .m-left ol li{
position: absolute;
top: 0;
left: 0;
display: none;
}
.glass-wrapper .container .m-left ol .on{
display: block;
}
.glass-wrapper .container .m-left .mask{
width: 200px;
height: 200px;
background-color: rgba(219, 219, 109, 0.6);
position: absolute;
top: 0;
left: 0;
pointer-events: none;
display: none;
}
.glass-wrapper .container ul{
display: flex;
justify-content:space-around;
}
.glass-wrapper .container ul li img{
width: 100px;
height: 100px;
}
.glass-wrapper .container ul .active{
border: 1px solid red;
}
.glass-wrapper .container .right-wraper{
position: relative;
width: 400px;
height: 400px;
/* border: 2px solid gray; */
overflow: hidden;
margin-left: 20px;
display: none;
}
.glass-wrapper .container .right-wraper img{
position: absolute;
left: 0;
top: 0;
}