JavaScript入门 跨域及解决方案/回调函数/AJAX封装 Day26

同源策略

同源策略是浏览器的一个安全功能,不同源的网页脚本在没有明确授权的情况下,不能读写对方资源。

同源是指"协议+域名+端口"三者相同

什么是跨域 

使用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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值