webpack4.X 小白从0开始一步学到位

来自三刷老肖的学习视频,依个人理解有增删

webpack介绍

1.webpack是什么
  • 打包工具(模块)
  • 前端必不可少的工具
2. webpack的作用
  • 打包(把多个文件打包成一个js文件,减少服务器压力、带宽)
  • 转化(比如less/sass/ts)需要loader
  • 优化(SPA越来越盛行,前端项目复杂度高,webpack可以对项目进行优化)
3. webpack构成
  • 入口 (entry)
  • 出口 (output)
  • 转化器 (loader)
  • 插件 (plugins)
  • 开发服务器 (devServer)
  • 模式 (mode)
4. 安装webpack
1
2
3
4
5
npm install webpack-cli -g
// or
yarn add webpack-cli -g
// 验证webpack环境已经OK ?
webpack -v
5. 开发环境和生产环境
  • 开发环境(development): 就是你平时编写代码的环境
  • 生产环境(production): 项目开发完毕,部署上线
6. npm基本命令
  • 生成package.json:
    • npm init
    • npm init -y
  • 下载线上环境包依赖
    • npm install [包名,包名,包名…] –save
    • npm i [包名,包名,包名…] -S
  • 下载开发环境包依赖
    • npm install [包名,包名,包名…] –save–dev
    • npm i [包名,包名,包名…] -D

webpack4.x初探

1. 跑一跑webpack

  当前项目下,新建src文件,src文件下新建index.js
  index.js文件编写测试代码【console.log(‘Hello World’);】
  将src下index.js打包为dist下bundle.js,执行如下:

1
webpack src/index.js --output dist/bundle.js
2. webpack.config.js配置文件

  当前项目下执行npm init -y
  当前项目下新增webpack.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
const path = require('path');
module.exports = {
entry: {
app: './src/index.js'
},
output:{
filename: 'bundle.js',
path: path.resolve(__dirname,'/dist')
},
module: {},
plugins: [],
devServer:{},
};

3. 使用自定义webpack配置文件名 例:myself.config.js
1
webpack --config myself.config.js
4. package.json配置运行命令

  package.json文件scripts下配置命令
  配置webpack开发环境(dev)/线上环境(build)命令如下

1
2
3
4
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production",
},

  执行开发环境打包:npm run dev
  执行线上环境打包:npm run build
  mode:设置打包环境

多入口、多出口、html插件使用

1. 多入口打包
1
2
3
4
5
6
7
module.exports = {
entry: ['./src/index.js','./src/index2.js'],
output:{
filename: 'bundle.js',
path: path.resolve(__dirname,'dist')
},
};
2. 多出口
1
2
3
4
5
6
7
8
9
10
module.exports = {
entry: {
app: './src/index',
main: './src/index2.js'
},
output:{
filename: '[name].bundle.js',
path: path.resolve(__dirname,'dist')
}
};
3. html-webpack-plugin

  作用:生成页面
  依赖webpack webpack-cli
  安装插件:npm i html-webpack-plugin -D
  使用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...,
plugins:[
new HtmlWebpackPlugin({
template: './src/index.html',
title: '可配置标题',
hash: true,
minify:{
collapseWhitespace: true
},
})
]
};

1
2
3
4
5
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title%></title>
</head>
<!--htmlWebpackPlugin 为插件定义名称,与js中自定义的插件名无关 -->

参数解析

  • title:HTML文档标题
  • filename: 文件名,默认为index.html,可设置
  • template: 版本地址,默认查询 src/index.ejs,若未查到,将自动生成模板
  • templateParameters: 允许重新模板参数
  • inject: 注入,允许对head、body等添加脚本
  • favicon: 设置网页顶部图标
  • meta: 在head中注入mate标签
  • base: 设置公共地址,如 https://example.com/path/page.html
  • minify: 是否压缩
  • hash: 是否生成哈希值,若为true将为所以注入文件加入哈希值
  • cache: 是否启用缓存,仅在文件有改动后才更新文件
  • showErrors: 是否报错
  • chunks: 是否仅允许添加指定块
  • chunksSortMode: 设置块加载排序方式
  • excludeChunks: 不允许加入的块
  • xhtml: 是否遵照xhtml加入link标签

HTML插件优化、服务器、热更新

1. 生成多页面
1
2
3
4
5
6
7
8
9
10
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
new HtmlWebpackPlugin({
filename: 'index2.html',
template: './src/index2.html',
}),
...
]

  配置多个页面时,此处一定要配置filename
  mpa项目可用node读取文件,来生成多个模板
  默认js都会打入模板内

2. 指定文件打入指定模板

  将app文件打入index.html,将main文件打入index2.html,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = {
entry: {
app: './src/index',
main: './src/index2.js'
},
plugins: [
new HtmlWebpackPlugin({
chunks:['app'],
template: './src/index.html',
}),
new HtmlWebpackPlugin({
chunks:['main'],
filename: 'index2.html',
template: './src/index2.html',
})
],
};

3. clean-webpack-plugin

  作用:清除文件/文件夹
  使用方式:

1
2
3
4
5
6
7
8
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
module.exports = {
...,
plugins: [
new CleanWebpackPlugin(),
],
devServer:{},
};

4. devServer

  下载: npm install webpack-dev-server
  package.json中配置启动项

1
2
3
4
5
"scripts": {
"dev": "webpack --mode development",
"build": "webpack --mode production",
"start:dev": "webpack-dev-server"
},

  配置一个基本的devServer

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
...,
devServer:{
// 设置服务器访问的基本目录
contentBase: path.resolve(__dirname,'dist'),
// 服务IP地址,本地可设置 localhost / 127.0.0.1
host: 'localhost',
// 设置端口
port: '8090',
},
};

  项目中我们发现dist文件为空,因为devServer将contentBase下的文件放到了活动内存中
  devServer支持自动刷新

5. 热更新

  作用:不重新加载整个文档,保留之前的操作,仅对有变动的部分更改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const webpack = require('webpack');

module.exports = {
...,
devServer:{
// 设置服务器访问的基本目录
contentBase: path.resolve(__dirname,'dist'),
// 服务IP地址,本地可设置 localhost / 127.0.0.1
host: 'localhost',
// 设置端口
port: '8090',
// 是否开启热更新
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
};

6, 实现跨域

  利用devServer实现跨域
  将所有/api开头的请求,代理至本机3000端口,配置如下

1
2
3
4
5
6
7
8
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: {'^/api' : ''}
}
}
}

  pathRewrite:重写

loader作用,处理css文件及压缩方式

1. loader介绍

  作用:加载器、转化器
  比如:less/scss转成css;ES7/8/9、JSX转成js
  loader从后向前加载

2. css loader

  我们编写了一个app.css文件,并在js文件中引用了它

1
import "./app.css";

  此时工具将会报编译错误,提示我们缺少loader,下面我是使用loader将其转化
  下载css相关的两个最核心loader:style-loader与css-loader
  loader基础配置如下:

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader:['style-loader','css-loader'],
exclude: /node_modules/
}
]
},
}

关于loader的写法

  • loader: [‘loader1’,’loader2’,…]
  • use: [‘loader1’,’loader2’,…]
  • use: [{loader: ‘loader1’},{ loader: ‘loader2’ },…]

css-loader先于style-loader解析
css-loader用来解析文件,style-loader将注入到模板内(在模板的head标签内注入style标签,并将样式放入style标签内)

3. 文件压缩

  4.x版本之前,压缩插件使用uglifyjs-webpack-plugin
  4.x版本之后,仅需将模式改为线上模式,将自动生成压缩文件

处理图片,分离css

1. 图片处理

  当我们在css中引用一张路径正确的图片时,编译工具会报错(当时直接引入css的时候也是这样)
  图片转化我们经常会用到两个loader: url-loader和file-loader
  url-loader: 支持js引入文件,并将文件输出到output的文件夹
  file-loader与url-loader相似,但比url-loader功能还要强大
  url-loader使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module: {
rules: [
{
test: /\.(png|jpg|gif|jpeg)$/i,
use: {
loader: 'url-loader',
options: {
limit: 50*1000,
outputPath: 'image',
}
},
exclude: /node_modules/
}
]
},

  背景图小于50kb时,图片会生成base64位

2. 图片分离

  图片分离插件 extract-text-webpack-plugin 4.0版后已废弃
  4.0版后请使用mini-css-extract-plugin
  使用如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
loader:[{
loader: MiniCssExtractPlugin.loader,
options:{
publicPath: ''
}
},'css-loader'],
exclude: /node_modules/
},{
test: /\.(png|jpg|gif|jpeg)$/i,
use: {
loader: 'url-loader',
options: {
outputPath: 'image',
limit: 50*1000
}
},
exclude: /node_modules/
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css'
}),

],
}

less/sass处理,添加前缀,消除冗余css

1. 处理less

  下载转化less的loader: npm install less less-loader -D
  配置如下:

1
2
3
4
5
6
7
8
9
module: {
rules: [
{
test: /\.less$/,
use: ['style-loader','css-loader','less-loader'],
exclude: /node_modules/,
}
]
},

2. 处理sass

  下载转化sass的loader: npm install node-sass sass-loader -D
  配置如下:

1
2
3
4
5
6
7
8
9
module: {
rules: [
{
test: /\.s(a|c)ss/,
use: ['style-loader','css-loader','sass-loader'],
exclude: /node_modules/
}
]
},

3. 自动添加css前缀

  CSS预处理器: postCss
  下载预处理器loader:npm install postcss-loader autoprefixer -D
  项目根目录下新增postcss配置文件: postcss.config.js,配置如下

1
2
3
4
5
module.exports = {
plugins: [
require('autoprefixer')
]
}

  webpack配置如下

1
2
3
4
5
6
7
8
9
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader','css-loader','postcss-loader'],
exclude: /node_modules/
}
]
},

  目前遇到项目中不显示前缀,但demo中就可以,还在排查原因

4. 消除冗余CSS代码

  下载依赖:npm install purifycss-webpack purify-css -D
  paths参数用来设置需要分析的路径地址
  配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
   
const path = require('path');
const glob = require('glob');
const PurifyCssPlugin = require('purifycss-webpack');

module.exports = {
plugins: [
new PurifyCssPlugin({
paths: glob.sync(path.join(__dirname, 'dist/*.html'))
})
]
}

SourceMap、babel、react环境配置

1. SourceMap

  webpack 4.x版本默认在development环境下自动开启调试模式
  webpack 3.采用sourceMap

1
2
3
4
module: {
...,
devTool: 'source-map'
},
2. babel

作用:

  • babel用来编译js
  • 能够轻松使用ESnext转化
  • 支持jsx等语法

  babel核心模块: babel-loader @babel/core @babel/preset-env
  配置如下:

1
2
3
4
5
6
7
8
9
10
11
module: {
rules: [
test: /\.jsx?$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}]
]
}
3. 支持JSX语法

  下载插件:npm install react react-dom @babel/preset-react -D
  项目根目录下新建babel配置文件.babelrc.json

1
2
3
4
5
6
7
8
{
"presets": ["@babel/preset-react"],
"env": {
"development": {
"presets": [["@babel/preset-react", { "development": true }]]
}
}
};

JSON配置、模块化设置、静态资源、插件

1. 使用模块化

  以提取rules为例
  新建rules模块文件webpack.rule.js
  复制模块规则并通过module.exports导出
  webpack配置文件引入如下

1
2
3
4
5
6
const ruleConfig = require('./webpack.rule');
module.exports = {
module: {
rules: ruleConfig
},
}

  在webpack中使用JSON

  • webpack 3.X之前使用json-loader
  • webpack 3.X之后默认可自动识别
2. 静态资源输出

  使用插件copy-webpack-plugin
  配置如下

1
2
3
4
5
6
7
8
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
plugins: [
new CopyWebpackPlugin([
{from : path.resolve(__dirname,'src/assets'),to: './assets'},
])
]
}

  注:默认打入dist文件

优雅使用第三方库、提取JS文件、优化

1. 使用第三方库

   ① 通过npm引入
   ② 通过webpack内置插件ProvidePlugin,向全局暴露,使用方法如下

1
2
3
4
5
6
7
8
const webpack =  require('webpack');
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
})
]
}

  通过ProvidePlugin引入与通过import引入的区别:

  • 通过import引入后,无论是否使用,都会被打包,这样会产生大量冗余js
  • ProvidePlugin只有在使用后才会打包
2. 提取JS文件

  在webpack 3.x及之前使用CommonsChunkPlugin
  webpack 4.x使用SplitChunksPlugin,使用如下

1
2
3
4
5
6
7
8
const webpack = require('webpack');
module.exports = {
optimization: {
splitChunks: {

}
}
}

  最后,这套课程老肖录制于18年初,截至到现在已经有一些插件、loader或写法发生了变化,我已对其做了少量调整,即便如此,对于新手而言这一定是一个很棒的课。
  这套课使自己快速入了webpack的门,每看一遍都有新收获,后面我将再写一篇由浅入深更全面、更深入的教程课,但老肖这套,唯有纸笔记之算自己才觉得可以算完结,好在我做到了。