webpack跨域问题
本节来学习怎么用webpack解决跨域的问题。
我们先来用express来自己写一个服务端,来模拟跨域。
我们在启动webpack服务本地访问项目时,其实就是使用的express在本地起的服务。
所以webpack本身自己就带了一个express
模拟跨域,写个接口
新建一个server.js
// webpack-dev-2\server.js
// express
const express = require('express')
let app = express()
app.get('/api/user', (req, res) => {
res.json({
name: 'cheny'
})
})
app.listen(3001, () => {
console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/');
})
运行一下,然后再浏览器就能访问到这个接口,http://localhost:3001/api/user
模拟跨域
修改index.js,发送一个请求,然后模拟跨域
// webpack-dev-2\src\index.js
let xhr = new XMLHttpRequest()
// 默认访问 http://localhost:8080/ webpack-dev-server的服务
xhr.open('GET', '/api/user', true) // true表示开启异步
// 把成功后的结果打印出来
xhr.onload = function () {
console.log(xhr.response);
}
// 发送这个Ajax
xhr.send()
然后我是运行一下 npm run dev
因为默认访问的是8080端口,但是我们写的服务端的端口是3001端口,所以就跨域的,显示接口访问不到
解决跨域1 本地webpack-dev-server 配置代理 proxy
因为接口会默认访问的webpack-dev-server的8080端口发送出去,8080访问3001肯定是访问不到的,所以我们可以这么干
- 先将服务发送到本地的webpack-dev-server的express上
- 然后通过本地的express来去访问接口
- 最后再转发给前端
就是配置一个本地的代理。http-proxy
修改配置文件
// webpack-dev-2\webpack.config.js
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html模板,将打包后的js塞进模板文件中
module.exports = {
mode: 'development', // 模式,默认两种模式 production 和 development
devServer: { // 开发服务器的配置
port: 8080, // 默认端口是8080,这里可以改
progress: true, // 打包时候的进度条
contentBase: './build', // 以哪个文件夹作为服务的根目录
open: true, // 服务启动完毕后,直接打开浏览器
compress: true, // 启动gzip压缩
// 配置代理服务器,解决跨域
proxy: {
// 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
'/api': 'http://localhost:3001', // 配置了一个代理
}
},
// 入口
entry: {
home: './src/index.js', // 入口1 home
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
// 配置loader
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
}
}
}
]
},
// 插件
plugins: [
// 生成html模板插件,将打包后的js以外链的形式塞到模板中
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
}),
],
}
配好以后重启服务,npm run dev
,发现就可以访问到了
特殊情况1 重写接口
我们在写接口的时候,有时候接口不会带api开头,比如请求的接口为 http://localhost:3001/user
,但是我们在写的时候习惯性的加上api,这时候该怎么办呢。
其实webpack在发服务的时候可以重写接口地址,把某一块给干掉。
修改server.js
// webpack-dev-2\server.js
// express
const express = require('express')
let app = express()
// http://localhost:3001/user
app.get('/user', (req, res) => {
res.json({
name: 'cheny & xzz'
})
})
app.listen(3001, () => {
console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/');
})
修改配置文件
// webpack-dev-2\webpack.config.js
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html模板,将打包后的js塞进模板文件中
module.exports = {
mode: 'development', // 模式,默认两种模式 production 和 development
devServer: { // 开发服务器的配置
port: 8080, // 默认端口是8080,这里可以改
progress: true, // 打包时候的进度条
contentBase: './build', // 以哪个文件夹作为服务的根目录
open: true, // 服务启动完毕后,直接打开浏览器
compress: true, // 启动gzip压缩
// 配置代理服务器,解决跨域
proxy: { // 重写的方式,把请求代理到express服务器上
// 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
'/api': {
target: 'http://localhost:3001',
pathRewrite: { '/api': '/' }, // 把接口地址的 /api 替换为空
},
}
},
// 入口
entry: {
home: './src/index.js', // 入口1 home
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
// 配置loader
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
}
}
}
]
},
// 插件
plugins: [
// 生成html模板插件,将打包后的js以外链的形式塞到模板中
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
}),
],
}
重启服务,发现成功了,虽然访问的是 /api/user
,但是最后也能准确的访问到后台的接口/user
前端mock一些数据
修改配置文件
// webpack-dev-2\webpack.config.js
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin') // 生成html模板,将打包后的js塞进模板文件中
module.exports = {
mode: 'development', // 模式,默认两种模式 production 和 development
devServer: { // 开发服务器的配置
port: 8080, // 默认端口是8080,这里可以改
progress: true, // 打包时候的进度条
contentBase: './build', // 以哪个文件夹作为服务的根目录
open: true, // 服务启动完毕后,直接打开浏览器
compress: true, // 启动gzip压缩
// (1)配置代理服务器,解决跨域
/* proxy: {
// 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上
'/api': {
target: 'http://localhost:3001',
pathRewrite: { '/api': '/' }, // 把接口地址的 /api 替换为空
},
} */
// (2) 我们前端只想单纯来模拟一些接口数据
before(app) { // webpack-dev-server提供的一个钩子,调用的时候会传一个app,其实就是express的app
app.get('/user', (req, res) => {
res.json({
name: 'cheny & xzz ❤'
})
})
},
},
// 入口
entry: {
home: './src/index.js', // 入口1 home
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
// 配置loader
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
}
}
}
]
},
// 插件
plugins: [
// 生成html模板插件,将打包后的js以外链的形式塞到模板中
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
}),
],
}
修改index.js
// webpack-dev-2\src\index.js
let xhr = new XMLHttpRequest()
// 默认访问 http://localhost:8080/
xhr.open('GET', '/user', true) // true表示开启异步
// 把成功后的结果打印出来
xhr.onload = function () {
console.log(xhr.response);
}
// 发送这个Ajax
xhr.send()
重启服务,访问成功
解决跨域2 在服务端启动webpack,端口用服务端端口
相当于前端代码和服务端代码启动在了一个端口上,这样就没有跨域了。
相当于在服务端启动了一个页面,在这个页面访问服务端的接口。
因为是在服务端express中启动前端页面,需要一个中间件
yarn add webpack-dev-middleware@^3.7.0 -D
修改server.js
// webpack-dev-2\server.js
// express
const express = require('express')
let app = express()
// 在服务端使用webpack启动前端页面
let webpack = require('webpack')
// 使用中间件,引入webpack
let middle = require('webpack-dev-middleware')
/*
1. 先通过webpack拿到配置文件,交给webpack来处理
2. 会产生一个编译后的结果
3. 然后将编译后的结果 仍给中间件就可以了
*/
let config = require('./webpack.config.js') // 1
let compiler = webpack(config) // 2
app.use(middle(compiler)) // 3
// 这样的话,我们运行服务的时候,会连带着把webpack也启动开
// http://localhost:3001/user
app.get('/user', (req, res) => {
res.json({
name: 'cheny & xzz'
})
})
app.listen(3001, () => {
console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/');
})
我们使用node .\server.js
启动这个node服务,会发现连带着也启动了一个webpack
我们试着访问接口,没问题
如果我们直接访问 http://localhost:3001/
,发现也能拿到这个项目了
这就相当于后端和前端启动到了一起。
总结
webpack解决跨域的方式
使用代理,通过webpack自带的express来访问后台接口
写法1 直接代理
jsproxy: { // 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上 '/api': 'http://localhost:3001', }
写法2 重写路径
jsproxy: { // 如果是 /api 开头的服务,就使用devServer去发送请求,发送到 http://localhost:3001 上 '/api': { target: 'http://localhost:3001', pathRewrite: { '/api': '/' }, // 把接口地址的 /api 替换为空 }, }
直接将项目启动到后台上,相当于前台和后台公用一个端口
js// webpack-dev-2\server.js // express const express = require('express') let app = express() // 在服务端使用webpack启动前端页面 let webpack = require('webpack') // 使用中间件,引入webpack let middle = require('webpack-dev-middleware') /* 1. 先通过webpack拿到配置文件,交给webpack来处理 2. 会产生一个编译后的结果 3. 然后将编译后的结果 仍给中间件就可以了 */ let config = require('./webpack.config.js') // 1 let compiler = webpack(config) // 2 app.use(middle(compiler)) // 3 // 这样的话,我们运行服务的时候,会连带着把webpack也启动开 // http://localhost:3001/user app.get('/user', (req, res) => { res.json({ name: 'cheny & xzz' }) }) app.listen(3001, () => { console.log('服务启动成功 在 3001 端口', 'http://localhost:3001/'); })
另外使用webpack-dev-server还可以模拟后台接口数据,因为它自带的有express,可以拿到app,并且有个before钩子,如果访问接口匹配到这个钩子会直接拦截
devServer: { // 开发服务器的配置
before(app) { // webpack-dev-server提供的一个钩子,调用的时候会传一个app,其实就是express的app
app.get('/user', (req, res) => {
res.json({
name: 'cheny & xzz ❤'
})
})
},
},