day-122-one-hundred-and-twenty-two-20230728-跨域-模块化-webpack初步
跨域
- 为什么要跨域?
- 浏览器为了安全,不能让我们的html文件可以随意引用别的服务器中的文件,只允许我们的html或js文件中,请求我们自己服务器。这个就是浏览器的同源策略。
- 因为我们的网页是一个html文件,这个html是在一个域名里的。而这个html会引用各种文件,如图片、js文件、css文件,这些文件,有时候并不在一个服务器里。
- 所以我们就需要去到其它服务器中查找这些文件。
- 跨域的方案有:
CORS-跨源资源共享
方案JSONP带填充的json
方案
CORS
- CORS 是一种官方的跨域解决方案,通过在服务器端添加一些 HTTP 头信息来告诉浏览器,允许哪些网站可以访问这些资源。
CORS针对get请求
-
fang/f20230728/f20230728/1.get.html 前端要写的代码。
html<!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>get - http://127.0.0.1:5500/</title> </head> <body> 请求体 - http://127.0.0.1:5500/ </body> <script> let xhr = new XMLHttpRequest(); xhr.open("GET", "http://localhost:8080", true); xhr.onload = () => { console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText); //报错: Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. }; xhr.send(); </script> </html>
-
fang/f20230728/f20230728/1.server.js 后端要写的代码。
jsconst http = require('http') http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500') res.end('from: http://localhost:8080') }).listen(8080, () => { console.log(`服务器地址为: http://localhost:8080`); })
CORS针对post请求
-
fang/f20230728/f20230728/2.post.html
html<!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>http://127.0.0.1:5500/</title> </head> <body> 请求体 - http://127.0.0.1:5500/ </body> <script> let xhr = new XMLHttpRequest(); xhr.open("POST", "http://localhost:8080/users", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = () => { console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText); //报错: Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. }; xhr.send(JSON.stringify({ name: "zhufeng1" })); </script> </html>
-
fang/f20230728/f20230728/2.server.js
jsconst http = require('http') http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500') // res.end('from: http://localhost:8080') // Content-Type // res.setHeader('Access-Control-Allow-Headers', 'Content-Type') res.setHeader('Access-Control-Allow-Headers', 'Content-Type');//这个要在预检阶段返回。 if (req.method === 'OPTIONS') { res.statusCode = 200 return res.end() // return res.end('OPTIONS预检成功') } if (req.url === '/') { res.end('home') } else if (req.url === '/users') { switch (req.method) { case 'GET': res.end('users') break case 'POST': res.end('POST: users') break default: res.end('404') break } } }).listen(8080, () => { console.log(`服务器地址为: http://localhost:8080`); })
CORS针对put请求
-
fang/f20230728/f20230728/3.put.html
html<!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>http://127.0.0.1:5500/</title> </head> <body> 请求体 - http://127.0.0.1:5500/ </body> <script> let xhr = new XMLHttpRequest(); xhr.open("PUT", "http://localhost:8080/users", true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onload = () => { console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText); //报错: Access to XMLHttpRequest at 'http://localhost:8080/' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. }; xhr.send(JSON.stringify({ name: "zhufeng1" })); </script> </html>
-
fang/f20230728/f20230728/3.server.js
jsconst http = require('http') http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500') // res.end('from: http://localhost:8080') // Content-Type // res.setHeader('Access-Control-Allow-Headers', 'Content-Type') res.setHeader('Access-Control-Allow-Headers', 'Content-Type');//这个要在预检阶段返回。 res.setHeader('Access-Control-Allow-Methods', 'PUT');//这个要在预检阶段返回。 if (req.method === 'OPTIONS') { res.statusCode = 200 return res.end() // return res.end('OPTIONS预检成功') } if (req.url === '/') { res.end('home') } else if (req.url === '/users') { switch (req.method) { case 'GET': res.end('GET: users') break case 'POST': res.end('POST: users') break case 'PUT': res.end('PUT: users') break default: res.end('404') break } } else { res.statusCode = 400 res.end() } }).listen(8080, () => { console.log(`服务器地址为: http://localhost:8080`); })
CORS针对Cookie
-
fang/f20230728/f20230728/4.visit.html
html<!DOCTYPE html> <head> <title>get - http://127.0.0.1:5500/</title> </head> <body> 请求体 - http://127.0.0.1:5500/ </body> <script> let xhr = new XMLHttpRequest(); xhr.withCredentials='include' xhr.open("GET", "http://127.0.0.1:8080/visit", true); // xhr.open("GET", "http://localhost:8080/visit", true); xhr.onload = () => { console.log(`接收到的返回体: xhr.responseText-->`, xhr.responseText); }; xhr.send(); </script> </html>
-
fang/f20230728/f20230728/4.server.js
jsconst http = require('http') http.createServer((req, res) => { res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500') // res.end('from: http://localhost:8080') // Content-Type // res.setHeader('Access-Control-Allow-Headers', 'Content-Type') res.setHeader('Access-Control-Allow-Headers', 'Content-Type');//这个要在预检阶段返回。 res.setHeader('Access-Control-Allow-Methods', 'PUT');//这个要在预检阶段返回。 res.setHeader('Access-Control-Allow-Credentials', 'true') if (req.method === 'OPTIONS') { res.statusCode = 200 return res.end() // return res.end('OPTIONS预检成功') } if (req.url === '/') { res.end('home') } else if (req.url === '/users') { switch (req.method) { case 'GET': res.end('GET: users') break case 'POST': res.end('POST: users') break case 'PUT': res.end('PUT: users') break default: res.end('404') break } } else if (req.url === '/visit') { const cookies = req.headers.cookie let visit = 0 if (cookies) { let items = cookies.split(/;\s+/) for (let i = 0; i < items.length; i++) { let item = items[i] let [key, value] = item.split('=') if (key === 'visit') { visit = Number(value) break } } } visit++ res.setHeader('Set-Cookie', [`visit=${visit}; SameSite=None; Secure=false`]) res.end(`${visit}`) } else { res.statusCode = 400 res.end() } }).listen(8080, () => { console.log(`服务器地址为: http://localhost:8080`); })
CORS详细说明
- fang/f20230728/3.cors/1.server.js
js
//1.引入node.js内置用来创建 http服务器的模块
const http = require('http');
//2.创建http服务器,指定请求处理函数,以后每当有客户端请求到达服务器的时候就会由此
//请求处理函数来处理请求,并返回响应
//req代表请求对象,如果 想获取请求信息比如请求行 METHOD url 请求头 请求体可以使用此对象
//res代表响应对象,如果想写入响应信息,比如状态码 响应头 响应体使用此对象
http.createServer((req, res) => {
//服务器返回一个响应头,以后如果是来自于5500的访问,则浏览器不会再进行阻止
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
//在预览的时候,需要返回响应头
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
//在预检的时候,需要返回响应头Allow-Methods,它的值就是允许跨域请求的方法
res.setHeader('Access-Control-Allow-Methods', 'PUT');
//允许跨域的时候携带cookie
res.setHeader('Access-Control-Allow-Credentials', 'true')
//如果客户端发送过来的是一个OPTIONS请求的话,说明客户端只是需要确认一下是否允许跨域
//它需要判断是否有Access-Control-Allow-Origin就可以了,不需要响应体
if (req.method === 'OPTIONS') {
res.statusCode = 200;
return res.end();
}
if (req.url === '/') {
res.end('home');
} else if (req.url === '/users') {
switch (req.method) {
case 'GET':
res.end('users');
break;
case 'POST':
res.end('POST /users');
break;
case 'PUT':
res.end('PUT /users');
break;
default:
break;
}
//当客户端访问/visit路径的时候
} else if (req.url === '/visit') {
//取出客户端传递过来的cookie
const cookies = req.headers.cookie;
console.log('cookies', cookies);
//定义一个客户端访问次数的变量
let visit = 0;
if (cookies) {// key1=value1; visit=1;key3=value3
//先用分割符把cookie拆成字符串的数组,然后再循环此字符串数组
let items = cookies.split(/;\s+/);
for (let i = 0; i < items.length; i++) {
//再用=对每个cookie进行分割,会返回一个数组 1项 key 2项 value
let [key, value] = items[i].split('=');
if (key === 'visit') {
visit = Number(value)
break;
}
}
}
//让访问次数加1
visit++;
//服务器通过响应头Set-Cookie向客户端种植cookie visit=2
//SameSite 是否只允许同域或者说同站点访问, 只有把它设置为None才能允许跨域读写cookie
//Secure为true的话表示只允许在https站点生效
res.setHeader('Set-Cookie', [`visit=${visit}; SameSite=None; Secure=false`]);
//返回最新的访问次数
res.end(`${visit}`);
} else {
res.statusCode = 404;
//即使你没有向客户端写入响应体,也需要关闭响应,
//不然客户端会一直在等待服务器的响应,一直在那里转圈圈
res.end();
}
}).listen(8080, () => console.log('8080'));
- fang/f20230728/3.cors/get.html
html
<!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>
</head>
<body>
<script>
//创建Ajax对象的实例
let xhr = new XMLHttpRequest();
//打开连接
xhr.open("GET", "http://localhost:8080", true);
//指定成功的回调, 等客户端读取完响应信息后对执行onload回调函数
xhr.onload = () => {
console.log(xhr.responseText);
};
//发送请求给服务器
xhr.send();
</script>
</body>
</html>
- fang/f20230728/3.cors/post.html
html
<!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>
</head>
<body>
<script>
//创建Ajax对象的实例
let xhr = new XMLHttpRequest();
//打开连接
xhr.open("POST", "http://localhost:8080/users", true);
//如果要想发送请求体,就需要设置Content-Type请求头,指定请求体的格式
//跨域的时候发送一个请求头
xhr.setRequestHeader("Content-Type", "application/json");
//指定成功的回调, 等客户端读取完响应信息后对执行onload回调函数
xhr.onload = () => {
console.log(xhr.responseText);
};
xhr.onerror = (onerror) => {
console.error(onerror);
};
//发送请求给服务器
xhr.send(JSON.stringify({ name: "zhufeng1" }));
</script>
</body>
</html>
- fang/f20230728/3.cors/put.html
html
<!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>
</head>
<body>
<script>
//创建Ajax对象的实例
let xhr = new XMLHttpRequest();
//打开连接
xhr.open("PUT", "http://localhost:8080/users", true);
//如果要想发送请求体,就需要设置Content-Type请求头,指定请求体的格式
//跨域的时候发送一个请求头
xhr.setRequestHeader("Content-Type", "application/json");
//指定成功的回调, 等客户端读取完响应信息后对执行onload回调函数
xhr.onload = () => {
console.log(xhr.responseText);
};
xhr.onerror = (onerror) => {
console.error(onerror);
};
//发送请求给服务器
xhr.send(JSON.stringify({ name: "zhufeng1" }));
</script>
</body>
</html>
- fang/f20230728/3.cors/visit.html
html
<!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>
</head>
<body>
<script>
let xhr = new XMLHttpRequest();
//withCredentials是XMLHttpRequest的一个属性,该属性定义了是否在请求中携带cookie
//默认情况下此值为 false,这意味着不携带cookie,如果true,则携带 cookie
xhr.withCredentials = "include";
xhr.open("GET", "http://127.0.0.1:8080/visit", true);
xhr.onload = () => {
console.log(xhr.responseText);
};
xhr.send();
</script>
</body>
</html>
JSONP
静态js文件-JSONP原理
-
fang/f20230728/f20230728/6.serve-jsonp.js
jsconst http = require('http') http.createServer((req, res) => { res.setHeader('Content-Type', 'application/javascript') let script = ` // 这里是服务器那边动态生成的js代码。 console.log('开始执行服务器返回的动态js代码。') getData({'id':100,name:'fang'}); console.log('结束执行服务器返回的动态js代码。') ` // res.end(`console.log('from服务器8080的打印输出main')`); res.end(script); }).listen(8080, () => { console.log(`服务器地址为: http://localhost:8080`); })
-
fang/f20230728/f20230728/6.jsonp.html
html<!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>jsonp</title> </head> <body> 静态jsonp原理 <script> window.getData = function getData(data) { console.log(`data-->`, data); }; </script> <script src="http://localhost:8080/sugrec.js"></script> </body> </html>
有了一点动态的jsonp
-
fang/f20230728/f20230728/7.jsonp.html
html<!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>jsonp</title> </head> <body> 动态jsonp。 <script> let callbackName = `JQuery_${Date.now()}`; window[callbackName] = function (data) { console.log(`data-->`, data); }; let script = document.createElement("script"); script.src = `http://localhost:8080/sugrec.js?cb=${callbackName}`; document.body.appendChild(script); </script> </body> </html>
-
fang/f20230728/f20230728/7.serve-jsonp.js
jsconst http = require('http') const url = require('url') http.createServer((req, res) => { res.setHeader('Content-Type', 'application/javascript') const { query: { cb } } = url.parse(req.url || '', true) let script = ` // 这里是服务器那边动态生成的js代码。 console.log('开始执行服务器返回的动态js代码。') ${cb}({'id':100,name:'这个是后端构建出来的json数据'}); console.log('结束执行服务器返回的动态js代码。') ` res.end(script); }).listen(8080, () => { console.log(`服务器地址为: http://localhost:8080`); })
动态jsonp
-
fang/f20230728/f20230728/8.jsonp.html
html<!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>jsonp</title> </head> <body> 动态jsonp。这里是自动生成的`Live Server`起起来的,服务器端口为5500; <script> let callbackName = `JQuery_${Date.now()}`; console.log(`动态函数变量名:callbackName-->`, callbackName); window[callbackName] = async (data) => { console.log(`后端返回的json数据:data-->`, data); await new Promise((resolve, reject) => { setTimeout(() => { resolve(""); }, 300000); }); // 执行结束后,移除多余代码。 console.log(`执行结束后,移除多余代码。`); window[callbackName] = null; document.body.removeChild(script); }; let script = document.createElement("script"); script.src = `http://localhost:8080/sugrec.js?cb=${callbackName}`; console.log(`动态生成的脚本标签:script-->`, script); document.body.appendChild(script); </script> </body> </html>
-
fang/f20230728/f20230728/8.serve-jsonp.js
js// 这里是用node起起来的,服务器端口为8080; const http = require('http') const url = require('url') http.createServer((req, res) => { res.setHeader('Content-Type', 'application/javascript') const { query: { cb } } = url.parse(req.url || '', true)//这里是为了让后端拿到前端定义的那个函数的函数名。 const jsonObj = { 'id': 100, name: '这个是后端构建出来的json数据' }//这个就是后端要返回的json数据。 const jsonStr = JSON.stringify(jsonObj) let script = ` // 这里是服务器那边动态生成的js代码。 console.log('开始执行服务器返回的动态js代码。') ${cb}(${jsonStr}); console.log('结束执行服务器返回的动态js代码。') ` res.end(script); }).listen(8080, () => { console.log(`服务器地址为: http://localhost:8080`); })
jsonP详细说明
- fang/f20230728/3.cors/jsonp.html
html
<!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>
</head>
<body>
<script>
//动态创建方法名
let callbackName = `jQuery_${Date.now()}`;
//动态给window上添加一个变量,值是一个函数,并接收一个数据对象作为参数
window[callbackName] = function (data) {
console.log(data);
};
//动态创建script
let script = document.createElement("script");
//让此动态脚本src等于服务器地址
script.src = `http://localhost:8080/sugrec.js?cb=${callbackName}`;
//把此动态创建的脚本对象添加到body上,浏览器如果发现html上多了一个script标签,
//就会立刻向它的script.src地址发出请求,并且取回来一段JS代码,并且立刻执行
document.body.appendChild(script);
</script>
</body>
</html>
- fang/f20230728/3.cors/jsonp.js
js
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
//使用url.parse方法解析url地址,并且把查询 字符串变成一个query对象
//search=?cb=jQuery_130 query={cb:"jQuery_130"}
const {query:{cb}} = url.parse(req.url,true);
//告诉客户端我返回的是一段JS脚本
res.setHeader('Content-Type','application/javascript');
//创建一段JS脚本字符串
let script = `${cb}({"id":100,"name":"zhufeng"})`;
//作为响应体发回给客户端
res.end(script);
}).listen(8080, () => console.log('8080'));
jsonp示例-百度功能初步实现
- fang/f20230728/f20230728/5.baidu.html
html
<!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>jsonp-baidu</title>
</head>
<body>
<input type="text" id="search-word" />
<ul id="suggestions"></ul>
</body>
<script>
let $ = {
ajax(options) {
return new Promise((resolve, reject) => {
const { url, jsonp, data } = options;
let callbackName = `JQuery_${Date.now()}`;
window[callbackName] = function (data) {
// console.log(`前端所希望的后端数据:data-->`, data);
resolve(data);
};
let script = document.createElement("script");
script.src = `https://www.baidu.com/sugrec?prod=pc&wd=${data.wd}&cb=${callbackName}`;
script.onerror = (error) => reject(error);
document.body.appendChild(script);
});
},
};
let searchWord = document.getElementById("search-word");
let suggestions = document.getElementById("suggestions");
searchWord.addEventListener("input", (event) => {
let wd = event.target.value;
let callbackName = `JQuery_${Date.now()}`;
console.log(`wd-->`, wd);
console.log(`改:callbackName-->`, callbackName);
// https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_222
$.ajax({
url: "https://www.baidu.com/sugrec",
jsonp: "cb",
data: {
prod: "pc",
wd,
},
})
.then((result) => {
console.log(`result-->`, result);
let { g } = result;
if (g) {
let html = ``;
for (let i = 0; i < g.length; i++) {
html += `<li>${g[i].q}</li>`;
}
suggestions.innerHTML = html;
}
})
.catch((error) => {
console.log(`error-->`, error);
});
});
</script>
</html>
百度下拉项示例-jsonp封装
- fang/f20230728/f20230728/9.baidu.html
html
<!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>jsonp-baidu</title>
</head>
<body>
<input type="text" id="search-word" />
<ul id="suggestions"></ul>
</body>
<script>
const jsonpFunction = (options) => {
return new Promise((resolve, reject) => {
const { url, jsonp, data } = options;
const callbackName = `JQuery_${Date.now()}`;
window[callbackName] = function (data) {
resolve(data);
delete window[callbackName];
document.body.removeChild(script);
};
let queryString = url?.indexOf("?") === -1 ? "?" : "&";
for (const key in data) {
const value = data[key];
queryString += `${key}=${value}&`;
}
let script = document.createElement("script");
script.src = `${url}${queryString}&${jsonp}=${callbackName}`;
script.onerror = (error) => {
reject(error);
delete window[callbackName];
document.body.removeChild(script);
};
document.body.appendChild(script);
});
};
let searchWord = document.getElementById("search-word");
let suggestions = document.getElementById("suggestions");
searchWord.addEventListener("input", (event) => {
let wd = event.target.value;
let callbackName = `JQuery_${Date.now()}`;
console.log(`wd-->`, wd);
console.log(`改一:callbackName-->`, callbackName);
// https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_222
jsonpFunction({
url: "https://www.baidu.com/sugrec",
jsonp: "cb",
data: {
prod: "pc",
wd,
},
})
.then((result) => {
console.log(`result-->`, result);
let { g } = result;
if (g) {
let html = ``;
for (let i = 0; i < g.length; i++) {
html += `<li>${g[i].q}</li>`;
}
suggestions.innerHTML = html;
}
})
.catch((error) => {
console.log(`error-->`, error);
});
});
</script>
</html>
纯前端的jsonp封装详细说明
html
<!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>
</head>
<body>
<input id="search-word" />
<ul id="suggestions"></ul>
<script>
function jsonp(options) {
return new Promise((resolve, reject) => {
const { url, jsonp, data } = options;
//1.或者创建一个临时的、唯一的方法名
let callbackName = `jQuery_${Date.now()}`;
//给window添加一个全局变量,变量名为方法,值是一个回调函数
window[callbackName] = function (data) {
//一旦此函数执行了,那么此变量则不再需要了,可以删除销毁了
delete window[callbackName];
//此时script脚本也不再需要,可以删除掉
document.body.removeChild(script);
//把data传递给了resolve函数,也就是传递给了成功的回调函数
resolve(data);
};
//动态创建一个类型为script的对象或者说元素
let script = document.createElement("script");
//定义一个查询字符串变量
//如果url地址里已经有问号了,则用&接着拼接其它的参数,如果没有?,那就用?开头
let queryStr = url.indexOf("?") === -1 ? "?" : "&";
for (let key in data) {
// += `prod=pc&`
// += `wd=a&`;
queryStr += `${key}=${data[key]}&`;
}
//url=https://www.baidu.com/sugrec
//queryStr=?prod=pc&wd=a&
//指定script的来源或者说要访问的脚本的地址
script.src = `${url}${queryStr}${jsonp}=${callbackName}`;
//src=https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_130
script.onerror = (error) => reject(error);
//向body的尾部添加一个script对象
document.body.appendChild(script);
});
}
//获取关键字输入框DOM元素
let searchWord = document.getElementById("search-word");
//获取联想词的下拉列表DOM元素
let suggestions = document.getElementById("suggestions");
//给关键字输入框绑定输入事件,当用户在输入框中输入字符串执行回调函数
searchWord.addEventListener("input", (event) => {
//调用https://www.baidu.com/sugrec?prod=pc&wd=a&cb=jQuery_222
//获取事件源的值,也就是关键字输入框的值
let wd = event.target.value;
jsonp({
url: "https://www.baidu.com/sugrec", //你想请求的url地址
jsonp: "cb", //最终希望调用方法名是通过哪个查询参数发送给服务器的cb=jQuery_13000
data: { prod: "pc", wd }, //其它要传递给服务器的数据,它们都会拼接到查询字符串中
})
.then((result) => {
//获取结果中的g属性
let { g } = result;
if (g) {
let html = "";
for (let i = 0; i < g.length; i++) {
html += `<li>${g[i].q}</li>`;
}
suggestions.innerHTML = html;
}
})
.catch((error) => {
console.log(error);
});
});
</script>
</body>
</html>
模块化
commonJs模块规范
- 这个是在nodejs中使用,主要是ES6MOdule没出来之前。不过它只支持以同步的方式导入一个模块。
以对象属性的方式单个导出
-
fang/f20230728/f20230728/commonJs/math1.js 写模块的。
jsconsole.log(`模块内:exports-->`, exports); exports.add = (a, b) => { return a + b } exports.minus = (a, b) => { return a - b }
-
fang/f20230728/f20230728/commonJs/use1.js 使用模块的。
jslet math = require('./math1') console.log(`使用模块:math-->`, math); console.log(`使用模块:math.add(1,2)-->`, math.add(1, 2)); console.log(`使用模块:math.minus(1,2)-->`, math.minus(1, 2));
-
详细说明:
以module.exports的方式全量导出
-
fang/f20230728/f20230728/commonJs/math2.js
jsconsole.log(`模块内2:module-->`, module); console.log(`模块内2:module.exports-->`, module.exports); module.exports = { add(a, b) { return a + b; }, minus(a, b) { return a - b; } }
-
fang/f20230728/f20230728/commonJs/use2.js
jslet math = require('./math2') console.log(`使用模块2:math-->`, math); console.log(`使用模块2:math.add(1,2)-->`, math.add(1, 2)); console.log(`使用模块2:math.minus(1,2)-->`, math.minus(1, 2));
AMD
AMD单个引入
- fang/f20230728/f20230728/AMD/index.html
html
<!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>
</head>
<body>
<script src="https://unpkg.com/requirejs@2.3.6/require.js"></script>
<script>
require(["math"], (math) => {
console.log(`单个引入:math.add(1,2)-->`, math.add(1, 2));
});
</script>
</body>
</html>
- fang/f20230728/f20230728/AMD/math.js
js
define(function () {
//定义此模块的输出;
return {
add(a, b) {
return a + b
}, minus(a, b) {
return a - b
}
}
});
AMD多个引入
- fang/f20230728/f20230728/AMD/index.html
html
<!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>
</head>
<body>
<script src="https://unpkg.com/requirejs@2.3.6/require.js"></script>
<script>
// console.log(`require-->`, require);
require(["math"], (math) => {
console.log(`单个引入:math.add(1,2)-->`, math.add(1, 2));
});
require(["math", "format"], (math, format) => {
console.log(`多个引入:math.add(1, 2)-->`, math.add(1, 2));
console.log(`多个引入:format.print()-->`, format.print());
});
</script>
</body>
</html>
- fang/f20230728/f20230728/AMD/math.js
js
define(function () {
return {
add(a, b) {
return a + b
}, minus(a, b) {
return a - b
}
}
});
- fang/f20230728/f20230728/AMD/format.js
js
define(function () {
//定义此模块的输出
return {
async print() {
console.log('模块内最初执行', Date.now())
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve('')
}, 4000)
})
console.log('模块内最后执行', Date.now())
}
}
});
AMD详细说明
- fang/f20230728/4.module/2.amd/index.html
html
<!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>
</head>
<body>
<script src="https://unpkg.com/requirejs@2.3.6/require.js"></script>
<script>
//内部会以异步的方式加载math.js文件,并获取math.js导出的对象
//异步加载完成后会执行回调函数,把math.js导出的对象传进去
//require方法接收2个参数,一个依赖数组,一个是回调函数
//当所有依赖都加载成功后,就会调用回调函数,回调函数的参数是依赖模块的输出
require(['math','format'],(math,format)=>{
console.log(math.add(1,2))
console.log(format.print())
});
</script>
</body>
</html>
- fang/f20230728/4.module/2.amd/format.js
js
define(function () {
//定义此模块的输出
return {
print() {
console.log(Date.now())
}
}
});
- fang/f20230728/4.module/2.amd/math.js
js
define(function () {
//定义此模块的输出
return {
add(a, b) {
return a + b;
},
minus(a, b) {
return a - b;
}
}
});
ES6Module
-
fang/f20230728/f20230728/ES6Module/math.js
js//单个导出一个变量age export const age = 16; // export //这个是一个关键字,后面只能接变量名或变量声明。 function add(a, b) { return a + b; } function minus(a, b) { return a - b; } const obj1 = { a: 6 } const msg = 'hello'; //批量导出add,minus多个变量,但也只算是单个导出,依旧要解构才能使用。 export { add, obj1, // obj2 : 6,//报错。 // let obj2 = 6,//报错。 // obj2 = 6,//报错。 minus, } // export { }//这个是一个关键字,大括号内部只能写变量名。 // 相当于语法为: export {变量名1,变量名2,... } //一个模块中的默认导出只能有一个 export default '北京' //A module cannot have multiple default exports. //export default '北京'
-
fang/f20230728/f20230728/ES6Module/app.js
jsimport home from './math.js'; import { age, add, minus, obj1 } from './math.js'; // import home,{age,add,minus} from './math.js';// 可以合并成一个。 console.log(`默认导入:home-->`, home); console.log(`单个导出-导出单个变量:age-->`, age); console.log(`单个导出-导出多个变量:add-->`, add); console.log(`单个导出-导出多个变量:minus-->`, minus); console.log(`单个导出-导出多个变量:obj1-->`, obj1); //if(true){ // //An import declaration can only be used at the top level of a module. // import home,{age,add,minus} from './math.js'; //}
-
fang/f20230728/f20230728/ES6Module/imdex.html
html<!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>ES6Module</title> </head> <body> ES6Module <script src="app.js" type="module"></script> </body> </html>
ES6Module详细说明
- fang/f20230728/4.module/3.esmodule/math.js
js
//单个导出一个变量age
export const age = 16;
function add(a,b){
return a+b;
}
function minus(a,b){
return a-b;
}
const msg = 'hello';
//批量导出add,minus
export {
add,
minus
}
//一个模块中的默认导出只能有一个
export default '北京'
//A module cannot have multiple default exports.
//export default '北京'
- fang/f20230728/4.module/3.esmodule/app.js
js
import home,{age,add,minus} from './math.js';
console.log(home)
console.log(age)
console.log(add)
console.log(minus)
//if(true){
//An import declaration can only be used at the top level of a module.
// import home,{age,add,minus} from './math.js';
//}
- fang/f20230728/4.module/3.esmodule/index.html
html
<!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>
</head>
<body>
<script src="app.js" type="module"></script>
</body>
</html>
UMD
-
fang/f20230728/f20230728/UMD/math.js 以UMD规范来简单写一个模块。
js(function (global, factory) { if (typeof define === 'function') { //当前处于AMD运行环境 define(factory); return } if (typeof module === 'object') { //当前处于node中的commonjs模块化环境 module.exports = factory();//导出factory方法执行的结果 return } // 不是AMD,也不是commonjs,那么就用全局变量的方式。 global.math = factory(); return })(this, () => { // 这里就是我们要导出去的模块。 return { add(a, b) { return a + b; }, minus(a, b) { return a - b; } } }); // 浏览器环境 self=window=this 没有global // Node环境 global=this 没有self和window
-
fang/f20230728/f20230728/UMD/global.html 在浏览器以全局变量的方式来导入一个UMD模块。
html<!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> </head> <body> 浏览器中的global全局环境 <script src="math.js"></script> <script> console.log(window.math); </script> </body> </html>
-
fang/f20230728/f20230728/UMD/amd.html 在浏览器以AMD的方式的来导入一个UMD模块。
html<!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> </head> <body> 浏览器中的AMD环境 <script src="https://unpkg.com/requirejs@2.3.6/require.js"></script> <script> //内部会以异步的方式加载math.js文件,并获取math.js导出的对象 //异步加载完成后会执行回调函数,把math.js导出的对象传进去 //require方法接收2个参数,一个依赖数组,一个是回调函数 //当所有依赖都加载成功后,就会调用回调函数,回调函数的参数是依赖模块的输出 require(["math"], (math) => { console.log(math.add(1, 2)); }); </script> </body> </html>
-
fang/f20230728/f20230728/UMD/commonJs.js 在node中以commonJs方式来导入一个UMD模块。
jslet math = require('./math'); console.log(`node中的commonJs环境:math-->`, math); console.log(`node中的commonJs环境:math-->`, math);
UMD详细说明
- fang/f20230728/4.module/4.umd/math.js
js
(function (global, factory) {
if(typeof define === 'function'){//当前处于AMD运行环境
define(factory);
}else if(typeof module === 'object'){//当前处于COMMONJS模块化环境
module.exports = factory();//导出factory方法执行的结果
}else{
global.math = factory();
}
})(this, () => {
return {
add(a, b) { return a + b; },
minus(a, b) { return a - b; }
}
});
// 浏览器环境 self=window=this 没有global
// Node环境 global=this 没有self和window
- fang/f20230728/4.module/4.umd/use.js
js
let math = require('./math');
console.log(math)
- fang/f20230728/4.module/4.umd/amd.html
html
<!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>
</head>
<body>
<script src="https://unpkg.com/requirejs@2.3.6/require.js"></script>
<script>
//内部会以异步的方式加载math.js文件,并获取math.js导出的对象
//异步加载完成后会执行回调函数,把math.js导出的对象传进去
//require方法接收2个参数,一个依赖数组,一个是回调函数
//当所有依赖都加载成功后,就会调用回调函数,回调函数的参数是依赖模块的输出
require(['math'],(math)=>{
console.log(math.add(1,2))
});
</script>
</body>
</html>
- fang/f20230728/4.module/4.umd/global.html
html
<!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>
</head>
<body>
<script src="math.js"></script>
<script>
console.log(window.math)
</script>
</body>
</html>
不同环境下的this
-
浏览器环境 self=window=this 没有global
-
浏览器控制台中
jsconsole.log(this===window) console.log(this===self)
-
-
Node环境 global=this 没有self和window
-
fang/f20230728/4.module/4.umd/this.js 主js文件中
jsconsole.log(this===global) console.log(this===exports)
-
对象的函数写法
js
let $ = {
ajax: function () {
console.log(`ajax-->`, ajax);
},
}
这两种写法等价
js
let $ = {
ajax(){
console.log(`ajax-->`, ajax);
},
}
webpack初步
- webpack是一个强大的模块打包器。
- 它可以把一个Web网站所需的资源,像JS、CSS、图片、图标等等任意的文件都打包在一起。
- 它的作用是可以从一个入口文件的出发,识别出每个文件之间的依赖关系,然后将这些资源全部打包成浏览器可以识别和处理的静态资源,即打包成一堆js文件或一个html文件。
初步创建一个webpack项目
-
生成package.json文件。
jsnpm init -y
-
安装webpack所需的依赖。
jsnpm install webpack webpack-cli --save
-
创建一个js文件。
-
fang/f20230728/5.webpack/src/index.js 得到一个js文件。
jsnpm install webpack webpack-cli --save
-
-
写一个简单的webpack打包脚本
js{ "scripts": { "build": "webpack --mode=development" }, }
-
执行打包命令。
jsnpm run build
一个简单的webpack示例
-
fang/f20230728/5.webpack/src/index.js 一个普通的js文件,默认的入口文件。
jsconsole.log('main')
-
fang/f20230728/5.webpack/package.json 关于整个webpack项目的配置信息。
json{ "name": "5.webpack", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack --mode=development" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "webpack": "^5.88.2", "webpack-cli": "^5.1.4" } }
-
fang/f20230728/5.webpack/dist/main.js 执行打包命令后出现
js
/*
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
* This devtool is neither made for production nor for readable output files.
* It uses "eval()" calls to create a separate source file in the browser devtools.
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
* or disable the default devtool with "devtool: false".
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
*/
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ "./src/index.js":
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/***/ (() => {
eval("console.log('main')\n\n//# sourceURL=webpack://5.webpack/./src/index.js?");
/***/ })
/******/ });
/************************************************************************/
/******/
/******/ // startup
/******/ // Load entry module and return exports
/******/ // This entry module can't be inlined because the eval devtool is used.
/******/ var __webpack_exports__ = {};
/******/ __webpack_modules__["./src/index.js"]();
/******/
/******/ })()
;
切换npm源
-
安装nrm源切换工具
jsnpm i nrm -g
-
测试不同的源的速度
jsnrm test
-
切换成淘宝源
jsnrm use taobao