1. 同源策略
浏览器都有一个同源策略,同源策略是浏览器的一种安全策略,所谓同源是指协议、域名、端口完全相同,只有满足三者一致,才允许两个url地址之间做数据的交互
2. 跨域
当协议、域名(地址)、端口只要有一个不相同时就会跨域
3. 解决跨域
这里使用 gulp 模拟的前端服务,端口号8080,配置代码不重要主要是进行压缩,配置代码不需要看,需要注意的是,前端服务端口号8080,后端服务端口号9527就会进行跨域
注意:其实使用 vscode 就可以开启一个端口为5050的热加载,也就是说可以不使用 gulp 开启一个服务,但是在前端解决跨域需要在配置文件里代理,所以就把配置文件一起发出来了(gulp 配置文件里都有注释)
// src:入口文件
// dest:出口文件
// series:组合任务
// parallel:并行任务
// watch:观察文件变化
const { src, dest, series, parallel, watch } = require("gulp")
// 删除文件
const del = require("del")
// html代码压缩
const htmlmin = require("gulp-htmlmin")
// css处理
// less转css
const less = require("gulp-less")
// sass转css
const sass = require("gulp-sass")(require("sass"))
// css压缩
const csso = require("gulp-csso")
// js处理
// js代码压缩
const uglify = require("gulp-uglify")
// ES6转换
const babel = require("gulp-babel")
// 热更新
const connect = require("gulp-connect")
// 处理 HTML 模板
const fileinclude = require('gulp-file-include');
// 代理
const { createProxyMiddleware } = require('http-proxy-middleware')
// html 文件处理
function html() {
// 入口文件路径
// .pipe 输送给下一个任务
return src("./src/*.html")
// html模板处理
.pipe(fileinclude({
prefix: '@@',
basepath: '@file'
}))
// html 压缩
.pipe(htmlmin({ collapseWhitespace: true }))
// 出口文件路径
.pipe(dest("./dist"))
//每次改变代码自动更新
.pipe(connect.reload())
}
// css 文件处理
function css() {
return src(["./src/css/*.less", "./src/css/*.scss", "./src/css/*.css"])
// less转css
.pipe(less())
// sass转css
.pipe(sass().on("error", sass.logError))
// css压缩
.pipe(csso())
.pipe(dest("./dist/css"))
.pipe(connect.reload())
}
// js 文件处理
function js() {
return src("./src/js/*.js")
// ES6转换
.pipe(babel({
presets: ["@babel/env"]
}))
// 进行压缩
.pipe(uglify())
.pipe((dest("./dist/js")))
.pipe(connect.reload())
}
// img 文件处理
function img() {
return src("./src/img/**")
.pipe(dest("./dist/img"))
.pipe(connect.reload())
}
// 清空dist下的所有文件
function clear() {
return del(["./dist/**"]).then(() => {
console.log("文件已清空");
}).catch(() => {
console.log("文件清空失败");
})
}
// 热加载
function server() {
connect.server({
name: "dev",
// 地址
host: "localhost",
// 端口
port: "8080",
// 观察哪个文件
root: "./dist",
// 热更新
livereload: true
})
}
// 观察文件变化
function watchList() {
watch("./src/*.html", series(html))
watch("./src/css/*.css", series(css))
watch("./src/css/*.less", series(css))
watch("./src/css/*.scss", series(css))
watch("./src/js/*.js", series(js))
watch("./src/img/**", series(img))
}
function init() {
return [html, css, js, img]
}
// 打包
exports.build = parallel(clear, series(...init()))
// server
exports.server = series(clear, series(...init()), parallel(server, watchList))
这里使用 node 模拟的服务器(后端服务),端口号9527
// 使用 express 框架
const express = require("express")
const app = express()
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.get("/login", (request, response) => {
response.send()
})
app.post("/login", (request, response) => {
response.send()
})
app.listen("9527", () => {
console.log("服务已开启: http://127.0.0.1:9527");
})
3.1 前端解决
- 配置前端服务器代理,转发到后端服务器
// gulp 配置文件 // 进行代理 const apiProxy = createProxyMiddleware("/api", { // 在配置文件中,监听的是前端的地址 // 通过代理转发,到后端获取数据,在传给前端 target: 'http://127.0.0.1:9527', // 目标地址 changeOrigin: true, // 需要进行代理 pathRewrite: { // 重写路径 '^/api': '', } }) // 热加载 function server() { connect.server({ name: "dev", // 地址 host: "localhost", // 端口 port: "8080", // 观察哪个文件 root: "./dist", // 热更新 livereload: true, middleware() { return [apiProxy] } }) }
// 前端请求部分,这一点要注意,要请求前端的地址,然后路径中间拼 api // html结构是使用下面 jsonp 的 // 前端地址 const baseUrl = "http://localhost:8080" let username; let password; document.querySelector("button").addEventListener("click", function () { username = document.querySelector("#username").value password = document.querySelector("#password").value fetch(baseUrl + "/api/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }) }).then(res => { return res.json() }).then(data => { console.log("data:", data); }).catch(err => { console.log("err:", err); }) })
3.2 后端解决
- 配置响应头允许跨域
// 后端服务部分 // 服务端解决跨域 cors,这种不太安全 app.all('*', (req, res, next) => { res.header("Access-Control-Allow-Origin", "*"); // 允许任何地址跨域 res.header("Access-Control-Allow-Headers", "*"); // 允许任何请求数据格式通过 next(); });
注意:这个时候要注意,直接访问后端服务的地址就可以了
3.3 前后端解决
- jsonp,利用 script 标签处理
<!-- html部分 --> <label for="username"> 账号: </label> <input type="text" id="username"> <label for="password"> 密码: </label> <input type="password" id="password"> <button>登录</button>
// js部分 // 服务端地址 const baseUrl = "http://127.0.0.1:9527" let username; let password; // 后端传过来数据时,会执行这个函数,可以在这个函数中处理数据 function login(params) { console.log(params); } document.querySelector("button").addEventListener("click", function () { username = document.querySelector("#username").value password = document.querySelector("#password").value // 点击时动态添加 script 标签,首先 script标签 为 GET 请求,而且不受跨域影响 // 所以用 script 标签代替我们发送 get 请求 let script = document.createElement("script") script.src = baseUrl + `/login?username=${username}&password=${password}` document.body.appendChild(script) })
// nodejs 部分 app.get("/login", (request, response) => { // 后端响应一个 login({key:value})的代码,它会执行这个代码就会找前端中的 login 函数 // 这样就可以前后端一起解决跨域了 response.send(`login(${JSON.stringify(request.query)})`) })