webpack 是核心,webpack-cli是给我们提供了相关的命令,能够基于webpack相关命令进行编译和打包。
webpack webpack-cli是用来在开发时将代码编译打包的,打包出的代码部署在服务器上,在生产环境下发布。所以在生产环境下是不需要编译打包的,因此要安装在开发环境下。
一、安装
// 生成package.json
npm init -y
// 安装在开发环境下
npm i webpack webpack-cli --save-dev
二、零配置使用
webpack默认会把当前项目src目录下的文件进行打包编译(零配置入口默认是index.js),编译到dist文件目录下(webpack编译代码的过程中支持Commonjs规范和ES6Module规范),未来部署到服务器上。
运行webpack
方法1:
npx webpack
方法2:
在package.json中配置可执行命令
{
"scripts": {
"serve":"webpack"
},
}
==> npm run serve
三、自定义webpack打包配置项
可创建两个js文件:webpack.config.js / webpackfile.js
文件名一定不能改变
- 编写自定义的webpack配置项,以后webpack打包编译的时候是按照自己配置的内容进行打包编译处理的
- 这个文件放在根目录下
- 文件名:webpack.config.js 或 webpackfile.js (自己能识别)
- webpack本身基于Node开发,所以配置项的模块处理规则参考CommonJs规范
const path = require('path')
module.exports = {
// 设置编译模式, development(只合并,不压缩)/production(合并压缩,默认)
mode:'production',
// 设置编译的入口文件
entry:'./src/main.js',
// 设置编译出口文件
output:{
// 编译后的文件名[hash]编译时会随机在名字中生成唯一的哈希值,来保证每次编译出的文件是不一样的(防止浏览器缓存问题)
filename:'bundle.[hash].min.js',
// filename:'bundle.min.js',
// 输出目录(绝对路径)
path:path.resolve(__dirname,'build')
}
}
四、plugin插件
1. 常用插件安装
此处安装了3个常用插件
npm i html-webpack-plugin clean-webpack-plugin webpack-dev-server --save-dev
2. 常用插件介绍
1)html-webpack-plugin
解决问题: 每次更改代码,重新编译后都需要手动去更改指定html页面中导入的js信息。此插件可以自动处理html的编译和导入文件。
注意:
a)将入口html页面(index.html)可以放在public目录下
b)index.html页面不再写引入js的代码
{
// 在webpack中使用插件
plugins:[
// 配置指定的html页面模版(后期编译时会把编译好的资源文件自动导入到我们的页面模版中)
new HtmlWebpackPlugin({
// 模版路径
template:'./public/index.html',
// 编译后生成的文件名
filename:'index.html',
// 是否把编译的资源文件导入到页面中,设置hash值(清除强缓存,和output中hash值一样,保留一个hash即可)
hash:true,
// 把模版中的html代码也进行压缩编译(配置规则)项目常用
minify:{
// 删除标签之间的空格
collapseWhitespace:true,
// 去除注释
removeComments:true,
// 去除div属性的双引号,div="xxx"
removeAttributeQuotes:true,
// 去除空属性 <div class="aa" id=""></div> id整体会被删除
removeEmptyAttributes:true
}
})
]
}
2). clean-webpack-plugin
解决问题:每次打包都把之前打包的内容清空
plugins:[
// 每次打包都把之前打包的清空
new CleanWebpackPlugin()
]
3). webpack-dev-server
解决问题: 帮我们构建一个后台,这个后台的web服务可以做到:
- 自动监听代码改变,改变自动编译
- 自动打开浏览器渲染页面
- 重新编译后自动刷新浏览器,看到最新效果 【除非配置项更改了需要自己重新执行,否则都是自动化处理】
module.exports = {
// 配资dev-server
devServer:{
// 端口
port:3000,
// 开启gzip压缩
compress:true,
// 指定资源访问路径
contentBase:path.resolve(__dirname,"build"),
// 自动打开浏览器
open:true,
// 开启热更新
hot:true,
// proxy跨域代理
proxy:{
'/':'http://127.0.0.1:8888'
}
},
}
配资dev-server,编译后的结果放在计算机内存中,并不会像之前的webpack命令一样,把编译后的东西放到build下,dev-server仅仅是在开发模式下,随时编译并且预览的,项目要部署的时候,还是需要基于webpack编译打包的
"scripts": {
"serve": "webpack-dev-server",
"build": "webpack"
},
所以在真实项目中有两种执行命令:
- 开发下用serve,随时编译到内存,随时看效果,随时刷新
- 开发完毕,执行build,把内容编译到build文件夹下,并上传至服务器
启用
{
"scripts": {
"serve": "webpack-dev-server"
},
}
==> npm run serve
异常点:
webpack-dev-server启动如果报错:
Error: Cannot find module ‘webpack-cli/bin/config-yargs‘
原因:
由于webpack-cli版本4,删除了webpack-cli/bin/config-yargs文件,bin目录下没有config-yargs.js
解决方案
提示下载webpack-cli 3版本的依赖
卸载webpack-cli
npm uninstall webpack-cli
安装3版本webpack-cli
npm install webpack-cli@3.3 --save-dev
完美解决!
3. 模版导入资源文件
在模版中可以自己单独导入一些CSS/JS/图片等资源文件
- 自己导入的文件不会受webpack编译的影响(不会和其他模块的文件编译在一起),所以有时候会单独导入一些公共资源文件
- 有些资源类库不支持CommonJs规范和ES6Module规范,这样的资源文件只能在模版中手动导入,因为webpack也无法处理。
五、配置多入口的打包编译
module.exports = {
// 多入口
entry:{
index:'./src/main.js',
login:'./src/login.js'
},
output:{
// [name]多入口配置的属性名 index/login
filename:'[name].[hash].min.js',
},
}
配置多页面模版
const htmlPlugins = ['index','login'].map(item => {
new HtmlWebpackPlugin({
template:`./public/${item}.html`,
filename:`${item}.html`,
minify:{
collapseWhitespace:true,
removeComments:true,
removeAttributeQuotes:true,
removeEmptyAttributes:true
}
})
})
plugins:[
...htmlPlugins
]
打包后会发现每个html文件都会导入全部的入口js文件,这不是我们想要的,就可以配置chunks,chunks用于多入口的时候。
const htmlPlugins = ['index','login'].map(item => {
new HtmlWebpackPlugin({
template:`./public/${item}.html`,
filename:`${item}.html`,
chunks:[item], // ***
minify:{
collapseWhitespace:true,
removeComments:true,
removeAttributeQuotes:true,
removeEmptyAttributes:true
}
})
})
访问时默认进index页面
独立打包js文件
多个页面公共的部分我们可以独立打包出来。
以jquery为例,如果不想将jq合并在其他js中,想将jquery也单独打包编译成js文件,可以这么写:
entry:{
index:'./src/main.js',
login:'./src/login.js',
jquery:'jquery' // ***
},
如果当前index和login页面都想引入jquery,可以这么配置
// 配置多页面模版
const htmlPlugins = ['index','login'].map(item => {
return new HtmlWebpackPlugin({
template:`./public/${item}.html`,
filename:`${item}.html`,
chunks:[item,'jquery'], // ***
minify:{
collapseWhitespace:true,
removeComments:true,
removeAttributeQuotes:true,
removeEmptyAttributes:true
}
})
})
实际项目中,我们需要先加载jquery,所以可以在chunks中调整顺序。
chunks:['jquery', item],
非独立打包
不想独立打包,可以在对应的js文件,以CommonJs规范,或者ES6Module规范导入进来即可。
例如,在src/main.js文件导入jquery
const $ require('jquery')
六、CSS资源处理
1. 加载器loader--样式处理
1)安装 less 为例
npm install css-loader style-loader less less-loader autoprefixer postcss-loader --save-dev
-
css-loader: css加载器,处理@import/URL()语法
-
style-loader:样式加载器,把处理好的css插入到页面中(内嵌式)
-
autoprefixer postcss-loader:自动加前缀,处理兼容
postcss-loader搭配autoprefixer一起使用时,需要额外再配置一些信息。在根目录创建postcss.config.js文件,使用它的语法包 postcss.config.js 配置module.exports = { plugins:[ require('autoprefixer') ] }设置浏览器兼容 (package.json)
github.com/browserslis… 中介绍了设置浏览器兼容的写法。
{ "browserslist":[ "> 1%", // 包含了99%的浏览器 "last 2 versions" // 兼容浏览器最近的两个版本 ] } -
less-loader:把less编译为css
安装sass:sass-loader node-sass
2)配置
module与plugins同级
// 配置weboack加载器 loader
module:{
// 设置规则和处理方式 默认执行顺序:从右到左,从下到上
rules:[
{
// 匹配哪些文件基于处理
test:/\.(css|less)$/i,
use:[
"style-loader",
"css-loader",
"postcss-loader",
// "less-loader"
// 不同写法,options可以添加额外的配置
{
loader:"less-loader",
options:{}
}
]
}
]
}
2. 抽离CSS :mini-css-extract-plugin 插件
将css内容抽离出来,形成单独文件(未压缩),用link形式导入进来,可以使用这个插件。
// 安装
npm install mini-css-extract-plugin --save-dev
// 导入 (webpack-config.js)
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 使用
plugins:[
new MiniCssExtractPlugin({
filename:'[name].[hash].min.css'
})
]
loader内容此时,要注意:不能再写style-loader了,要使用
MiniCssExtractPlugin自带的loading。这个loader能够将css分离出来,分离到我们指定的目录下(此时为filename的地址)。
module:{
// 设置规则和处理方式 默认执行顺序:从右到左,从下到上
rules:[
{
// 匹配哪些文件基于处理
test:/\.(css|less)$/i,
use:[
// "style-loader", // *** 删除style-loader
MiniCssExtractPlugin.loader // ***
]
}
]
}
运行程序可以看到,css使用link引入
七、 设置优化项--压缩CSS/JS
1. 安装
npm install optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin terser-webpack-plugin --save-dev
2. 插件介绍
1) CSS压缩插件:optimize-css-assets-webpack-plugin
2) JS压缩插件(谷歌):uglifyjs-webpack-plugin
3) JS压缩插件 (常用):terser-webpack-plugin
使用
// 导入
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
// 配置webpack优化项
optimization:{
// 设置压缩方式
minimizer:[
// 压缩css
new OptimizeCssAssetsWebpackPlugin(),
// 压缩Js
new UglifyjsWebpackPlugin({
cache:true,// 是否使用缓存
parallel:true, // 是否是并发编译
sourceMap:true, //启动源码映射(方便调试)
}),
new TerserWebpackPlugin()
]
}
}
Js压缩插件使用一个即可。
八、图片处理
项目中会用到图片的地方:
- css设置背景图
- js动态创建图片
- html中直接写图片
1. 安装
npm install file-loader url-loader html-withimg-loader --save-dev
2. 加载器介绍
1)file-loader:编译图片的加载器
处理css,js中的图片,和字体图标的处理
module:{
// 设置规则和处理方式 默认执行顺序:从右到左,从下到上
rules:[
// 图片的处理
{
test: /\.(png|jpe?g|gif|ico|bmp)$/i,
use: ['file-loader'], // ***
// 指定处理哪些目录下的
include:path.resolve(__dirname,'src'),
// 忽略哪些目录下的不处理
exclude: /node_modules/
},
// 字体图标的处理 编译图片的加载器
{
test: /\.(svg|eot|ttf|woff|woff2)$/i,
use: ["file-loader"] // ***
},
]
},
2)html-withimg-loader:处理html页面中的图片
html
<img src="../src/static/image/icon.png" alt="">
webpack.config.js
module:{
// 设置规则和处理方式 默认执行顺序:从右到左,从下到上
rules:[
// 编译html页面中的图片,把其安照另外两种图片的处理机制处理
{
test: /\.html$/,
use: ['html-withimg-loader']
}
]
},
3) url-loader:base64转换
url-loader在编译的时候,会把符合条件的图片进行base64,对于不符合条件的还是继续用file-loader处理。包括limit条件的。
module:{
rules:[
{
test: /\.(png|jpe?g|gif|ico|bmp)$/i,
// use: ['file-loader'],
use :[{
loader:'url-loader',
options:{
// 不超过200kb的用base64处理
limit: 200 * 1024,
// 在打包编译时,把图片都放在统一的images文件夹下
// 指定名字
name: 'images/[name],[hash].[ext]',
esModule:false
}
}]
}
]
}
3. 问题记录
问题:js动态创建图片,页面找不到图片地址问题.
// main.js
let image = new Image()
image.src = "./static/image/dog.jpg"
document.body.appendChild(image)
分析:
- 如果地址是一个外网的绝对地址直接使用即可(编译后地址还是外网地址)
- 如果需要设置的是相对地址,则需要基于require把图片导入进来再使用,否则编译后地址找不到。
因此,可以这么写
let A = require("./static/image/dog.jpg")
let image = new Image()
image.src = A
document.body.appendChild(image)
九、JS处理
基于babel实现ES6的转换
1. 安装
npm install babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-transform-runtime --save-dev
2. 加载器介绍
1)babel-loader:babel加载器
2)@babel/core:语法包
3)@babel/preset-env:语法包,将ES6转为ES5
3. 相关插件介绍
加载器可以将ES6转为ES5语法,但是有些ES6/ES7及以上无法直接转为ES5语法,因此需要引入一些插件。
1)@babel/plugin-proposal-decorators:类的装饰器
2) @babel/plugin-proposal-class-properties
这个插件可以转换,ES7中class的新语法。
// 举例:
class A{
// ES6
constructor(){
this.x = 10
}
// ES7中新增的语法,相当于this.x = 10
x=10
// ES7
static m = 10
}
ES7中我们可以在类里设置属性(基本类型)
3) @babel/plugin-transform-runtime
4. 配置
// JS处理
{
test:/\.js$/i,
use:[{
loader:'babel-loader',
options:{
presets:[
// 将ES6转为ES5
"@babel/preset-env"
],
// 基于插件处理ES6/ES7中class的特殊语法
plugins:[
// 类的装饰器
["@babel/plugin-proposal-decorators",{
"legacy":true
}],
// 类中设置属性的
["@babel/plugin-proposal-class-properties",{
"loose":true
}],
"@babel/plugin-transform-runtime"
]
}
}],
include: path.resolve(__dirname, 'src'), // 指定处理哪些目录下的
exclude: /node_modules/ // 忽略哪些目录下的不处理
}
持续更新...