1. HTML中img标签的图片资源处理
读取html中所有img的路径,帮我们把路径作统一的进行转换,处理并且把对应的资源打包
把html中所有的img对应的路径打包
- 安装
npm intall -S html-withimg-loader
- 在
webpack.config.js
文件中添加loader
{
test: /\.(htm|html)$/i,
loader: 'html-withimg-loader'
}
使用时, 只需要在html中正常引用图片即可, webpack会找到对应的资源进行打包, 并修改html中引用路径
copy-webpack-plugin
的作用是将源代码中的一些资源文件拷贝到指定位置
html-withimg-loader
的作用是把html中所有的img对应的路径打包
2. 多页应用的打包
多页应用:
咱们当前的vue项目中都是一个html页面,里面可以引入一个main.js, main.js作为html的唯一入口,可以引入各种各样的js, css, less, sass等等, 包括各种各样的第三方模块等,都可以引入, 而index.html只需要引入这一个main.js就足够了.这就是webpack帮我们实现的最终的目的,这样做的好处就是打包完成之后只会有一个html, 也就是我们俗称的单页面应用程序(SPA, single page application)
- 在
webpack.config.js
中修改入口和出口配置
// 1. 修改为多入口
entry: {
main: './src/main.js',
other: './src/other.js'
},
output: {
path: path.join(__dirname, './dist/'),
// filename: 'bundle.js',
// 2. 多入口无法对应一个固定的出口, 所以修改filename为[name]变量
filename: '[name].js',
publicPath: '/'
},
plugins: [
// 3. 如果用了html插件,需要手动配置多入口对应的html文件,将指定其对应的输出文件
new HtmlWebpackPlugin({
template: './index.html',
filename: 'index.html',
chunks: ['main']
}),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'other.html',
// chunks: ['other', 'main']
chunks: ['other']
})
]
- 修改入口为对象, 支持多个js入口, 同时修改 output 输出文件名为
'[name].js'
表示各自以入口文件名作为输出文件名, 但是html-webpack-plugin
不支持此功能, 所以需要再拷贝一份插件, 用于生成两个html页面,事项多页应用.
3. 第三方库的两种引入方式
比如说我们希望把一个jQuery导入到项目中,并挂载到全局作用域中,也就是说,我希望它放到window身上,而事实上,我们使用webpack导入进来的其实并没有挂载到全局作用域中,它实际上是挂载到了当前这个模块的闭包作用域,因为webpack的原理其实就是把每一个模块作为一个闭包来进行打包. 所以你在任何一个模块导入的jQuery他其实都是在局部变量中,并没有在全局window下面. 所以这个第三方的库,如果我们想作为全局引入,需要借助一个loader.
可以通过expose-loader
进行全局变量注入, 同时也可以使用内置插件 webpack.ProvidePlugin
对每个模块的闭包空间, 注入一个变量, 自动加载模块, 而不必到处import
或require
- expose-loader 将库引入到全局作用域
找到node_modules下面的jquery, 注入到全局
- 安装
expose-loader
npm i -D expose-loader
- 配置loader
module: {
rules: [{
test: require.resolve('jquery'),
use: {
loader: 'expose-loader',
options: '$'
}
}]
}
tips: require.resolve
用来获取模块的绝对路径. 所以这里的loader只会作用于 jquery 模块. 并且只在 bundle 中使用到它时, 才进行处理.
- webpack.ProvidePlugin 将库自动加载到每个模块
-
引入 webpack
const webpack = require('webpack')
-
创建插件对象
要自动加载jquery
, 我们可以将两个变量都指向对应的node模块new webpack.ProvidePlugin({ $:'jquery', jQuery: 'jquery' })
4. Development / Production 不同配置文件打包
项目开发时一般需要使用两套配置文件, 用于开发阶段打包 ( 不压缩代码, 不优化代码, 增加效率 ) 和上线阶段打包 ( 压缩代码, 优化代码, 打包后直接上线使用 )
抽取三个配置文件:
- webpack.base.js
- webpack.prod.js
- webpack.dev.js
步骤如下:
- 将开发环境和生产环境共用的配置放入base中, 不同的配置各自放入 prod 或 dev 文件中 ( 例如: mode )
- 然后再 dev 和 prod 中使用
webpack-merge
把自己的配置与 base 的配置进行合并后导出
npm i -D webpack-merge
- 将 package.json 中的脚本参数进行修改, 通过
--config
手动指定特定的配置文件
5. 定义环境变量
除了区分不同的配置文件进行打包, 还需要在开发时知道当前的环境时开发阶段或上线阶段, 所以可以借助内置插件 DefinePlugin
来定义环境变量. 最终可以实现开发阶段与上线阶段的 api 地址自动切换
- 引入 webpack
const webpack = require('webpack')
- 创建插件对象, 并定义环境变量
new webpack.DefinePlugin({
// 这里的字符串 false会被解析成布尔类型的false
// 如果想输入字符串, 需要套两层引号'"false"'
// 这里相当于会把引号内的内容作为js代码解析
IS_DEV: 'false'
})
3.在src打包的代码环境下可以直接使用
6. 使用 devServer 解决跨域问题
在使用webpack开发的过程中, 我们势必会用到 webpack-dev-server, 一旦用到 webpack-dev-server, 就一定会涉及到跨域.
webpack-dev-server内部其实就是一个服务器, 比如说我们在localhost:3000端口启用了这个服务, 客户端去访问 localhost:3000端口的时候会得到服务器给的响应, 也就是我们看到的html和我们打包好的 bundle.js
客户端拿到这个 html 和 js 之后, 仍然需要通过 ajax 向数据接口服务器获取网络数据, 由于是浏览器中使用 ajax, 就会有一个同源策略的限制. 这就意味着, 我们发送请求到webpack-dev-server 这个localhost:3000端口的位置, 得到了响应, js想发起新的 ajax 请求, 就必须只能发送到 localhost:3000端口, 不能发别的地方, 而且协议, 域名, 端口号都必须相同. 只要有一个地方不相同, 就会被浏览器给阻止掉. 这就是非常经典的跨域问题.
目前解决跨域主要的方案有:
- jsonp (淘汰)
- cors ( cross-origin-resource-share ) 跨域资源共享
原理: 在数据接口服务器加一个响应头信息, 告诉客户端, 我信任你 - http proxy
此处介绍的使用 devServer 解决跨域, 其实原理就是 http proxy
将所有 ajax 请求发送给 devServer 服务器, 再由 devServer 服务器做一次转发, 发送给数据接口服务器
由于 ajax 请求时发送给 devServer 服务器的, 所以不存在跨域, 而 devServer 由于是用 node 平台发送的 http 请求, 自然也不涉及到跨域问题, 可以完美解决!
服务器代码 (返回一段字符串即可) :
const express = require('express')
const app = express()
// const cors = require('cors')
// app.use(cors())
app.get('/api/getUserInfo', (req, res) => {
res.send({
name: '安吉拉',
age: 13
})
});
app.listen(9999, () => {
console.log('http://localhost:9999!');
});
前端需要配置 deServer 的 proxy 功能, 在 webpack.dev.js
中进行配置:
devServer: {
open: true,
hot: true,
compress: true,
port: 3000,
// contentBase: './src'
proxy: {
'/api': 'http://localhost:9999'
}
},
意为前端请求/api
的url时, webpack-dev-server 会将请求转发给 http://localhost:9999/api
处, 此时如果请求地址为http://localhost:9999/api/getUserInfo
, 只需要直接写 /api/getUserInfo
即可, 代码如下:
axios.get('/api/getUserInfo').then(result => console.log(result))
如果我们请求的接口是/getUserInfo
, 我们是不是需要在proxy中再加一条配置呢?
事实上我们有更优秀的处理方式,仔细瞧好了哈
服务端代码:
app.get('/getUserInfo', (req, res) => {
res.send({
name: '安吉拉',
age: 13
})
});
webpack.dev.js 配置:
devServer: {
open: true,
hot: true,
compress: true,
port: 3000,
// contentBase: './src'
proxy: {
'/api': {
target: 'http://localhost:9999',
// 表示转发请求时不携带 /api, /api 被重写成''
pathRewrite: {
'^/api': ''
}
}
}
},
请求:
axios.get('/api/getUserInfo').then(result => console.log(result))
7. HMR的使用
需要对某个模块进行热更新 ( 不刷新页面的局部更新 ) 时, 可以通过 module.hot.accept
方法进行文件监视
只要模块内容发生变化, 就会触发回调函数, 从而可以重新读取模块内容, 做对应的操作
if (module.hot) {
module.hot.accept('./hotmodule.js', function() {
console.log('hotmodule.js更新了');
let str = require('./hotmodule.js')
console.log(str)
})
}