一、Webpack 是什么
Webpack是一种前端资源构建工具,一个静态模块打包器(module bundler)
在Wbpack看来,前端的所有资源文件( js / json / css / img / less / ... )都会作为模块处理。
他将根据模块的依赖关系进行静态分析,打包生成对应的静态资源(bundle)
二、为什么要使用webpack?
当我们在文件里使用less,sass等预处理语言,或者使用es6等新最新语法,很多浏览器不能识别不能解析,所以需要一些工具去把这些文件进行编译浏览器能识别的语法,过去需要一个一个的工具,很麻烦,所以后来出现了webpack把一系列的工具整合成为一个工具
三、webpack是怎么打包的?
从项目的入口文件开始,再根据入口文件的依赖关系对引入的资源(less/es6.. 称之为模块)依次进行打包形成chunk文件代码块,通过这个代码块依次对引入的资源进行分类编译,这个编译的过程叫做打包,打包之后输出的文件为bundle
四、webpack的五个核心概念
4.1、Entry
入口(Entry)指示 Webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图
4.2、Output
输出(Output)指示 Webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
4.3、Loader
Loader 让 Webpack 能够去处理那些非 javaScript 文件(webpack 自身只能理解JavaScript)相当于翻译官的角色,将css,img等非js资源翻译能webpack能识别的资源
loader 和 plugins 的区别:
loader:一般做一些比较傻瓜式的操作
plugin:大部分都是插件来做,做一些比较复杂的事情
4.4、Plugins
插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
4.5、Mode
模式(Mode)指示 Webpack 使用响应模式的配置
五、webpack初体验
5.1、webpack的全局安装
// 基于nodejs平台运行的,模块化采用commonjs
npm i webpack webpack-cli -g
//同时下载webapck 和 webpack-cli 两个模块到全局
//也可以分开下载
npm i webpack -g
npm i webpack webpack-cli -g
//mac用户要加 sudo 字符启用管理员模式
// webpack 和 webpack-cli的关系 就如同 Vue 和 Vue-cli 的关系
// Vue 是一套框架, Vue-cli 是一套基于Vue.js的快速开发的完整系统
// webpack 是打包工具 webpack-cli就是提供了能够简化webpack的配置的工具
5.2、webpack的本地安装
npm init // 初始化文件夹生成package.json文件
npm i webpack webpack-cli -g
5.3、webpack指令
//运行指令:
//开发环境:
webpack ./src/index.js -o ./build --mode=development
//把 ./src/index.js 文件打包输出到 ./build 文件夹下的main.js --mode=development 模式为开发模式
//运行环境:
webpack ./src/index.js -o ./build --mode=production
//把 ./src/index.js 下的文件打包输出到 ./build 文件夹的main.js --mode=development 模式为运行模式
//结论:
//1、webpack能处理js/json资源,不能处理css/img等其他资源
//2、生产环境和开发环境将ES6模块化编译成浏览器能识别的问题
//3、生产环境比开发环境多一个压缩js代码
5.4、处理css等非 js/json 资源方法 (新增 webpack.config.js 放在最外层)
/*
webpack.config.js webpack的配置文件
作用:指示 webpack 干那些活(当你运行 webpack命令 时,会加载里面的配置)
所有构件工具都是基于 nodejs 平台运行的! 模块化默认采用commonjs
*/
// resolve用来拼接绝对路径的方法
const {resolve} = require('path')
// webpack配置
module.exports={
// 入口文件
entry: './src/index.js',
// 输出设置
output: {
//输出文件名称
filename:'built.js',
// 输出路径
//__dirname 这是nodejs的变量,代表当前文件的目录绝对路径
path:resolve(__dirname,'build')
},
// loader的配置
module:{
rules:[
//详细loader配置
//不同文件必须配置不同loader处理
{
// 匹配哪些文件
test:/\.css$/,
// 使用哪些loader,使用到的loader,需要npm下载之后才能打包成功
use:[
//use数组中loader执行顺序:从右到左,从下到上 依次执行
// 创建style标签,将js中的样式资源插入进行,添加到head中生效
'style-loader',
// 将css文件变成commonjs模块加载到js中,里面内容是样式字符串
'css-loader'
]
},
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
// 将less文件编译成css文件
'less-loader'
]
}
]
},
// 模式
mode:'development'
}
六、webpack自动化打包
// 开发服务器 deserver:用来自动化(自动编译,自动打开浏览器,自动刷新浏览器)
// 只会在内存中编译打包,不会有任何输出, 关闭之后需要通过webpack打包一次才能达到最新编译内容
// 所需依赖 webpack-dev-server 需要下载
// 启动devServe指令:webpack serve
devServer:{
//代表我要运行的目录,一定是构建后的目录
contentBase: resolve(__dirname,'build'),
// 启动gzip压缩
compress:true,
// 设置端口号
port:3000,
// 自动打开浏览器
open:true
}
七、webpack生产环境配置
7.1、生产环境和开发环境的区别
开发环境:
顾名思义就是可以让代码运行起来调试的的环境,在这个环境下,需要webpack去做一些编译,将资源编译成浏览器可以识别的资源,而且我们可以加入自动化的工具来让我们更加容易的调试
生产环境:
首先css文件经过打包生成的样式资源是整合在js中的,这样会使js体积变的很大,加载比较缓慢,而且需要加载完js之后才能去加载css样式,这时会产生闪屏现象,所以我们在生产环境打包时要将css样式抽离出来
代码要进行压缩处理
样式代码和js代码在浏览器端是有兼容性的,所以要做兼容处理
等等...
因为生产环境的要求比较高,而且编译时间会很久,所以这些操作需要在生产环境下做
7.2、生产环境的配置
7.2.1、将打包编译后的文件进行文件分类 loader中 outputPath 字段
// 输出设置 将js文件放入js文件夹内
output: {
//输出文件名称
filename:'js/built.js',
// 输出路径
//__dirname 这是nodejs的变量,代表当前文件的目录绝对路径
path:resolve(__dirname,'build'),
// publicPath: './'
},
//outputPath 输出目录
module:{
rules:[
// 打包其他资源都用file-loader
// exclude 需要排除的资源
{
exclude:/\.(html|js|css)$/,
loader:'file-loader',
options:{
name:'[hash:10].[ext]'
outputPath:'other'
}
},
{
// 匹配图片资源
test:/\.(jpg|png|gif)$/,
loader: 'url-loader',
options:{
limit: 8*1024,
esModule:false,
name:'[hash:10].[ext]',
outputPath:'imgs'
}
},
]
}
7.2.2、抽离css资源
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports={
entry:'./src/index.js',
output:{
filename:'js/built.js',
path:resolve(__dirname,'build')
},
module:{
rules:[
{
test:/\.css$/,
use:[
// style-loader: 创建style样式,讲样式放进去
// 用这个loader取代 style-loader , 作用:提取js中的css为单独文件
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test:/\.less$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
}),
// mini-css-extract-plugin 这个插件可以将css单独抽离出来需要先下载
new MiniCssExtractPlugin({
// 对文件进行重命名
filename:'css/built.css'
})
],
mode:'development',
7.2.3、css兼容性
const { resolve } = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 设置环境变量
process.env.NODE_ENV = 'development'
module.exports={
entry:'./src/index.js',
output:{
filename:'js/built.js',
path:resolve(__dirname,'build')
},
module:{
rules:[
{
test:/\.css$/,
use:[
// style-loader: 创建style样式,讲样式放进去
// 用这个loader取代 style-loader , 作用:提取js中的css为单独文件
MiniCssExtractPlugin.loader,
'css-loader',
// css兼容性处理:postcss方法:需要先下载 postcss-loader postcss-preset-env
// 要放在css-loader之下 less-loader之上的位置
// 帮 postcss 找到 package.json 中 browserslist 里面的配置,通过配置加载指定的css兼容性样式
// 默认生效的是生产环境,跟下边mode设置的环境没有关系,我们需要手动更能该nodejs环境变量
/* browserslist 要 写在package.json 中
"browserslist": {
开发环境
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
],
生产环境
"production": [
">0.2%",
"not dead",
"not op_mini all"
]
}
*/
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"postcss-preset-env",
{
// 其他选项
},
],
],
},
},
}
]
},
{
test:/\.less$/,
use:[
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'./src/index.html'
}),
// mini-css-extract-plugin 这个插件可以将css单独抽离出来需要先下载
new MiniCssExtractPlugin({
// 对文件进行重命名
filename:'css/built.css'
})
],
mode:'development',
}
7.2.4、压缩css
//webpack.config.js
// 压缩css的插件
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')
plugins:[
new OptimizeCssAssetsPlugin()
]
7.2.5、js兼容性处理
module:[
rules:[
// js兼容性处理:babel-loader (需要下载 babel-loader , @babel/core 和 @babel/preset-env )
// 1、基本js兼容性处理 --》@babel/preset-env
// 问题:只能转换基本语法,如promise不能转换
// 2、全部js兼容处理 --》 @babel/polyfill 下载之后直接在js顶部 import 引入即可(不采用)
// 问题:我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了
// 3、需要做兼容性处理的按需加载:core-js (需要下载)
{
test:/\.js$/,
loader:'babel-loader',
// 排除第三方库
exclude:/node_modules/,
options:{
// 预设:指示babel做怎么样的兼容性处理
presets:[
[
'@babel/preset-env',
{
// 按需加载
useBuiltIns:'usage',
// 指定core-js版本
corejs: {
version: 3
},
// 指定兼容性做到哪个版本浏览器
targets:{
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}
]
]
}
}
]
]
7.2.6、js / html 压缩
module.exports={
plugins:[
// 压缩打包后生成的html文件
new HtmlWebpackPlugin({
template:'./src/index.html',
minify:{
// 移除空格
collapseWhitespace: true,
// 移除注释
removeComments:true
}
})
],
// 将模式改为生产环境会自动压缩js
mode:'production',
}
7.2.7、开启HMR功能
// HMR:hot modulereplacement 热模块替换 / 模块热替换
// 作用: 一个模块发生变化,只会重新打包这一个模块(而不是打包所有模块)
// 极大提升构建速度
// css文件:可以使用 HMR 功能, 因为style-loader内部实现了 HMR 功能,
// js文件:默认不能使用 HMR 功能 --》 需要修改js代码,添加支持 HMR 功能的代码
// 只能处理非入口文件的其他js文件
// html文件:默认不能使用 HMR 功能,同时会导致问题:html不能热更新了,
// 解决:修改 entry 入口,讲 html 文件引入, 入口html文件是不需要做热更新的
// webpack.config.js
module.exports={
devServer:{
contentBase:resolve(__dirname,'build'),
compress:true,
port:3000,
open:true,
// 开启 HMR 功能
// 当修改了webpack配置,新配置要想生效必须重启webpack服务
hot: true
}
}
// index.js
import sub from './util'
if(module.hot){
module.hot.accept('./util.js',function(){
// 方法会监听 util.js 的文件变化,一旦发生变化,其他默认不会重新打包构建
// 会执行后边的回调函数1
sub()
})
}
7.2.8、优化开发环境调试功能 开启 source-map
// webpack.config.js
module.exports={
devtool: 'source-map'
}
7.2.9、优化loader 处理性能
// webpack.config.js
module.exports={
module={
rules:[
// 只匹配一次,匹配成功后,之后的loader将不会再做匹配
// 注意:不能有两个配置处理同一种类型的文件
oneOf:[
{
test:/\.css$/,
use:[
'style-loader',
'css-loader'
]
}
]
]
}
}
7.2.10、缓存处理
module.exports={
module:{
rules:[
{
test:/\.js$/,
loader:'babel-loader',
// 排除第三方库
exclude:/node_modules/,
options:{
// 预设:指示babel做怎么样的兼容性处理
presets:[
...
],
// 开启 babel 缓存
// 第二次构建时,会读取之前的缓存
cacheDirectory: true
}
}
]
}
}