webpack:模块打包机
webpack安装:推荐在项目内本地安装,不推荐使用全局安装。
如果配置文件的名字不是webpack.config.js
的话,在使用npx webpack
进行打包的时候要npx webpack --config 自定义的配置文件名字
webpack-cli的作用:可以使我们在命令行中运行webpack这个指令,如果不安装webpack-cli,则在命令行中运行webpack指令会报错。
webpack配置
webpack可以0配置:不需要写任何配置文件,直接运行npx webpack
打包指令为npx webpack
此时打包的是src目录下的index.js文件,打包生成的是和src文件同级的dist文件下的main.js文件。
注:打包是为了支持js的模块化,如果在index.js中使用了Common.js的语法规范,即通过require的方式引入了一个模块,本来index.js是无法运行在浏览器环境下的,通过打包之后生成的main.js就可以运行在浏览器环境下了。
通过配置文件来进行配置
默认的webpack的配置文件名为webpack.config.js
下面是一个配置文件的基本结构
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports={
//模式:可以选择是生产模式或者是开发模式,在生产模式下,打包的js文件会被压缩。
mode:'development',
//打包文件的入口地址
entry:path.resolve(__dirname,'./index.js'),
//打包文件的出口地址
output:{
path:path.resolve(__dirname,'dist'),//打包文件将来存放的地址,这个地址必须是一个绝对地址。这里会新生成一个dist目录。打包出来的文件就在这个文件夹下面。
filename:'bundle.js'//打包后输出的文件名字。
}
//开发服务器的配置,也就是webpack-dev-server的配置
devServer:{
port:3000//端口号
contentBase:path.resolve(__dirname,'dist')//指定托管到电脑的内存中的路径
open:true//自动打开浏览器
}
//这里放着webpack的所有插件
plugins:[
new HtmlWebpackPlugin({
template:''//模板对象,一般是src目录下的index.html
filename:'index.html'//在内存中生成的文件名字
})
]
//这里放着所有的模块
module:{
rules:[//规则
//css-loader是解析@import这种语法的
//style-loader是把css文件插入到head标签中
//loader的特点是希望用途单一
//loader的顺序默认是从右向左执行。
{test:/\.css$/,use:['style-loader','css-loader']} //处理css文件的loader
]
}
}
打包其他格式的文件
默认情况下,webpack只能打包js文件,在需要打包其他格式的文件的时候,需要安装对应的loader,并在webpack.config.js中的module中去添加相对应的匹配规则。
loader
作用:Loader直译为”加载器“,webpack将一切文件都看成是模块,但是原声带webpack只能解析js文件,如果其他文件也想被打包的话,就会用到相应的loader,所以loader的作用是让webpack拥有加载和解析非JavaScript文件的能力。
file-loader
module.exports = {
module:{
rules:[
{
test:/\.(jpg|png|gif)$/,
use:{
loader:'file-loader',
options:{
name:'[name]_[hash:8].[ext]',//打包的图片的名字是源文件名字_8位hash
outputPath:'images/'//打包输出的图片存放在dist目录下的images文件里。
}
}
}
]
}
}
url-loader
把图片转化为base64的字符串插入到打包的js文件里,减少了依次http请求。缺点是如果打包的文件特别大,生成的base64字符串也会特别大,所以,对于较小的图片,适合使用url-loader,对于较大的图片,还是使用file-loader比较好。
//配置的rules和file-loader及其相似。
module.exports = {
module:{
rules:[
{
test:/\.(jpg|gif|png)$/,
use:{
loader:'url-loader',
options:{
name:'[name]_[hash:8].[ext]',
outputPath:'images/',
limit:5000//这是和file-loader最主要的区别,url-loader在打包图片时,如果图片大小小于配置的limit值,就将图片传化位base64的字符串插入到打包的js文件中,如果大于limit,打包规则就和file-loader一致。所以也可以说url-loader是对file-loader更深层次的包装。
}
}
}
]
}
}
webpack处理样式相关的文件
打包css文件
npm install style-loader css-loader -D
- 添加相应的规则
module.exports = {
module:{
rules:[
{test:/\.css/,use:['style-loader','css-loader']}
]
}
}
//style-loader作用是生成一个<style><style>标签插入在html文件中,css-loader作用是解析@import xxx.cs这种语法。
打包scss文件
npm install style-loader css-loader sass-loader
- 添加相应的规则
module.exports = {
module:{
rules:[
{test:/\.scss/,use:['style-loader','css-loader','sass-loader']}
]
}
}
上述处理样式的文件的方法,都会吧css文件直接以<style>
标签的形式插入到生成的html文件中,而如果我们希望将css文件打包后单曲抽离出来成为一个文件,且通过link标签的形式引入,就需要这样做:
将css文件抽离出来成为一个单独的文件
npm install mini-css-extract-plugin -D
安装相应的插件- 在配置文件中使用这个插件
new MiniCssExtractPlugin({
filename:'css/main.css'//将抽离出来的css文件会存放在打包时产生的bulid文件夹下的css文件夹下的main.css
})
3.将处理css和less文件的loader中的style-loader
替换为MiniCssExtractPlugin.loader
{test:/\.css$/,use:[MiniCssExtractPlugin.loader,'css-loader']},
{test:/\.less$/,use:[MiniCssExtractPlugin.loader,'css-loader','less-loader']}
给css3属性加浏览器前缀
使用的loader是postcss-loader
使用的插件是autoprefixer
具体过程如下:
- 安装相应的loader和插件
- 在根目录下建一个postcss.config.js,里面内容如下:
module.exports={
plugins:[require('autoprefixer')]
}
- 将postcss-loader写在相应的rules里面。(写在处理css文件的css-loader的下面即可)
处理字体图标
- 在iconfont字体图标矢量库中选区自己需要的字体图标,下载代码到本地
- 将下载下来的代码中的字体文件和iconfont.css文件拷贝到项目中。
- 修改webpack.config.js的配置项,新增加一个匹配规则如下:
module.exports = {
rules:[
{test:/\.(svg|ttf|eot|woff)$/,use:{
loader:'file-loader',
options:{
outputPath:'./fonts'
}
}}
]
}
- 在项目的index.css文件里通过
@import 'iconfont.css'
的形式引入字体图标的样式文件。
plugins
作用:Plugin的直译是‘’插件‘’.Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。在webpack运行的生命周期中会广播出来许多事件,plugin可以监听这些事件,在合适的时机通过webpack提供的api改变输出结果。
html-webpack-plugin
作用:在打包结束之后,自动生成一个html文件,并将打包生成的js文件自动引入到这个html文件中。
使用方法:
- 下载安装相应的插件
npm install html-webpack-plugin -D
- 修改配置文件,添加plugins属性
let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html',//打包后自动生成的html文件的模板
filename:'index.html'//自动生成的html文件的名字
})
]
}
clean-webpack-plugin
作用:在每次打包之前都会将上一次打包生成的dist目录删除掉。
使用方法:
- 安装相应的插件
npm install clean-webpack-plugin -D
- 修改配置文件,添加相应的plugin
注意:
每一次打包的结果中的hash值是随着打包源文件变化而变化的。如果要打包的源文件没有发生变化,那么打包结果的hash值也就不会有变化。但是实际上确实每一次都是删除了dist目录下的文件重新生成新的一份打包文件的。
let {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
plugins:[
new CleanWebpackPlugin()//在打包之前删除dist目录中的内容。
]
}
多入口情况处理
// 多入口
let path = require('path')
let HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
mode: 'development',
entry: {
home: './src/index.js',
other: './src/other.js'
},
output: {
filename: "[name].js",//这里的name就是entry里面的两个文件,可以是home,也可以是other
path: path.resolve(__dirname, 'dist2'),
publicPath:cdn地址//这里再打包完之后,会在html文件引入打包的js文件之前都加入这个cdn
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
filename: 'home.html',
chunks: ['home']//表示home.html里面引入的是打包生成的home.js
}),
new HtmlWebpackPlugin({
template: './index.html',
filename: 'other.html',
chunks: ['other', 'home'] // other.html 里面有 other.js & home.js
}),
]
}
souce-map的配置
作用:源码映射,用于在编码出错时,迅速找到源码的出错地址。
在module.exports导出的对象中加入devtool: 'source-map'
// 增加映射文件可以帮我们调试源代码
「source-map」
:源码映射 会标识错误的代码 打包后生成独立的文件 大而全 出错了会标识当前出错的列和行「evl-source-map」
:不会生成单独的文件 但是可以显示行和列「cheap-module-source-map」
:不会产生列,产生单独的映射文件 (生产环境线上推荐这个)「cheap-module-eval-source-map」
:不会产生文件 集成在打包后的文件中 不会产生列 (开发环境推荐使用这个)
watch
监视打包文件,当需要打包的文件发生改变的时候,就自动重新打包
watch: true,
watchOptions: {
poll: 1000, // 每秒检查一次变动
aggregateTimeout: 300, // 当第一个文件更改,会在重新构建前增加延迟(修改文件防抖)
ignored: /node_modules/ // 对于某些系统,监听大量文件系统会导致大量的 CPU 或内存占用。这个选项可以排除一些巨大的文件夹,
},
webpack-dev-server的使用
安装:npm install webpack-dev-server -D
配置时就是在module.exports导出的对象中添加一个devServer属性,具体情况如下所示:
module.exports = {
devServer:{
contentBase:'./dist',//表示服务器起在这个目录下,因为我们打包生成的目录时dist,所以这里和其保持一致
port:3000,//表示服务器时开在3000端口上的
open:true,//自动打开浏览器,
progress:true//在打包编译的过程中会有一个进度条效果
proxy:{//这个属性时用来做跨域的。
'/api':'http://localhost:3000'//当前端的请求是以api开头的,就转到后面的这个地址上。
}
}
}
webpack跨域配置
常见的跨域有三种情况,下面分别介绍在这三种情况下如何配置webpack
设置代理:这种情况下,会起一个后端服务
服务端采用express框架搭建,服务端代码如下:
let express = require('express');
let app = express();
情况1:
app.get('/api/user',(req,res)=>{
res.json({name:'GouKu'});
})
情况2:后端请求并不是以api开头
app.get('/user',(req,res)=>{
res.json({name:'GouKu'});
})
app.listen(3000,()=>{console.log('app运行在3000端口上')});
webpack默认端口是8080,所以可以在配置文件中设置一个代理
module.exports ={
devServer:{
contentBase:'./dist',
open:true,
//情况1:
proxy:{
'/api':'http://localhost:3000'//表示如果前端i请求的路径是以api开头的,都转到3000这个端口上去。
}
//情况2:在这种情况下,虽然前端依然请求的是api/user,但是经过代理之后,给后端发出的请求就是将api删掉了,实际向后端请求的是/user
proxy:{
'/api':{
target:'http://localhost:3000',
reWritePath:{'^/api',''}
}
}
}
}
index.js中是这样写的
let xhr = new XMLHttpRequset();
xhr.open('GET','/api/user',true);//这样每当请求的是以api开头的,就转到3000端口上去
xhr.onload=function(){
console.log(xhr.response);
}
xhr.send();
只是前端去mock数据,并不涉及到后端
module.exports={
devServer:{
contentBase:'./dist',
before:function(app){//启用一个钩子函数
app.get('/api/user',(req,res)=>{
res.json({'name':'GouKu'})
})
}
}
}
有服务端且不使用代理,直接从服务端开启webpack
- 首先下载安装webpack的中间键
npm install webpack-dev-middleware -D
//server.js的内容如下:
let express = require('express');
let webpack = require('webpack');
let app = express();
let middle = require('webpack-dev-middleware');
let config = require('./webpack.config.js');
let compiler = webpack(config);
app.use(middle(compiler));
app.get('/api/user',(req,res)=>{
res.json({'name':"GouKu"});
})
app.listen(3000,()=>{console.log('app运行在3000端口上')})
//这样,在访问localhost:3000/api/user时也可以得到数据。
热模块更新(hot module replacement HMR)
使用热模块更新,可以在不刷新浏览器的情况下,只针对修改的文件做出样式活着是js上的变化。具体使用步骤如下:
- 由于webpack自带着针对HMR的插件,所以,我们不必要再额外下载安装插件,只需要引入webpack即可。
- 在webpack.config.js中的devServer属性中添加上hot:true
- 在webpack.config.js中的plugins属性中实例化这个插件。
const webpack = require('webpack');//setp1
module.exports = {
devServer:{
contentBase:'./dist',
port:3000,
progress:true,
open:true,
hot:true,//step2
}
plugins:[
new webpack.HotModuleReplacementPlugin();//step3
]
}
注意:
如果修改的是css文件,由于css-loader的关系,不需要在做额外的行为就可以针对修改的css样式浏览器做出反应,如果修改的是js文件,则需要在打包的入口文件中添加上以下代码:
if(module.hot){
module.hot.accept(修改的js文件,()=>{要执行的变化(业务代码)})
}
webpack利用babel处理ES6语法
- 安装相应的loader
npm install --save-dev babel-loader @babel/core
- 在rules添加相应的规则
module:{
rules:[
{test:/\.js$/,exclude:/node_modules/,use:'babel-loader'}
]
}
- 在项目根路径下创建一个名字叫.babelrc的文件,里面的内容为
{
"preset":["@babel/preset-env"]
}
注意:上述做法是babel官网的教程,但是这样的做法是不全面的,更全面的做法是,在上述基础上,接着如下操作:
- 安装babel/polyfill
npm install --save @babel/polyfill
- 在打包的入口js文件导入babel/polyfill
import "@babel/polyfill";
这样其实就可以了,但是打包出来的js文件会非常大,所以还需要第6步的优化 - 修改.babelrc文件为:
{
"preset":[["@babel/preset-env",{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},//target里面写的内容是这些版本及以上的的浏览器对ES6语法都做了很好的兼容,所以,不需要再ployfill,就可以减少打包文件的体积。
useBuiltIns: 'usage'//只有用到的ES6语法方案才会被注入。
}]]
}
注意:上述的关于babel在webpack上的配置都是用来处理业务代码的,如果在实际开发中我们需要开发的是js类库这种不需要污染全局变量的化,就不能使用babel/polyfill了,而是要采用以下配置
- 不需要在入口文件引入
import "@babel/polyfill
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
- 修改.babelrc文件如下:
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
- 此时打包的时候会报错,是因为还需要再
npm install --save @babel/runtime-corejs2
这样就不会报错了。