简介
代码转换/文件优化/代码分割/模块合并/自动刷新/代码校验/自动发布
和 gulp 主要区别:
webpack 主要站在整个项目的角度,强调处理各种依赖关系,处理各种文件
gulp强调流式管道操作,强调点对点之间的关系
安装
windows
4.x 中命令行由 `webpack-cli` 提供
npm init -y
npm i webpack webpack-cli -D
如果执行 webpack 时报错 webpack: command not found
npm i webpack webpack-cli -g
五个核心
- entry
- output
- loader
- plugins
- mode
入口出口
entry: './src/main.js',
output: {
filename: 'app.js',
path: `${__dirname}/dist`
}
CSS
处理 css
npm i style-loader css-loader -D
打包 css
npm i mini-css-extract-plugin -D
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'development',
entry: './src/main.js',
output: {
filename: 'app.js',
path: `${__dirname}/dist`,
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
],
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html',
}),
new MiniCssExtractPlugin({
filename: 'css/index.css',
// publicPath: ''
}),
],
};
处理 css 兼容
默认生产环境,如果需要开发环境要手动设置 process.env.NODE_ENV = ‘development’
npm i postcss-loader postcss-preset-env -D
webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 打包 css
'css-loader',
{
loader: 'postcss-loader', // 处理 css 兼容
options: {
ident: 'postcss',
plugins: () => [require('postcss-preset-env')],
},
},
],
},
],
}
package.json
"browserslist": {
"development": [
"last 1 chrome version",
"last 1 firefox version"
],
"production": [
">0.1%",
"not dead",
"not op_mini all"
]
}
压缩 css
npm i optimize-css-assets-webpack-plugin -D
const optimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
...
plugins: [
new optimizeCssAssetsWebpackPlugin()
]
处理 stylus
npm i stylus stylus-loader -D
rules: [
{
test: /\.styl(us)*$/,
use: ['style-loader', 'css-loader', 'stylus-loader'],
},
]
JS
使用 eslint 规范 js
npm i eslint eslint-loader eslint-plugin-import eslint-config-airbnb-base -D
eslint-loader
依赖 eslint
airbnb
风格的代码规范包eslint-config-airbnb-base
依赖 eslint-plugin-import
和 eslint
package.json
"eslintConfig": {
"extends": "airbnb-base" // 继承 airbnb-base
}
webpack.config.js
rules: [
{
test: /\.js$/,
loader: 'eslint-loader',
exclude: /node_modules/,
options: {
fix: true, // 自动修复
}
}
]
babel 语法降级
基本 js 兼容问题处理: @babel/preset-env
只能处理基本的 es6 => es5 比如箭头函数 const let
全部 js 兼容处理: @babel/polyfill
npm i @babel/core babel-loader @babel/preset-env -D
rules: [
{
test: /.\js$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: ['@babel/preset-env'] // 预设,告诉 babel 如何处理兼容。这里使用 @babel/preset-env 预设
}
}
]
polyfill
npm i @babel/polyfill -D
babel 基本的不能处理如如 new Promise() 在 IE 11 中会报错,这个 js 会将所有的兼容问题全都纳入进来
main.js
import '@babel/polyfill' // 引入后打包文件会增加 440kb 左右
问题: 体积太大,全部兼容引入
解决:
npm i core-js -D
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage', // 按需加载
corejs: {
version: 3, // 指定 corejs 版本
},
targets: { // 兼容到哪个版本
chrome: '60',
firefox: '50',
ie: '9',
},
},
],
],
},
},
],
处理 html
npm i html-webpack-plugin -D
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
new HtmlWebpackPlugin({
template: '../public/index.html',
})
]
处理图片
- css 中的本地图片依赖
npm i url-loader file-loader -D
webpack.config.js
module.exports = {
module: {
rules: {
test: /\.(jpg|png|gif)$/,
loader: 'url-loader',
options: {
limit: 8 * 1024, // 小于 8KB 转为 base64 减少 HTTP 请求次数,增加 CPU 负担
name: '[hash:10].[ext]', // hash 10 位,原拓展名
}
}
}
}
- html 中的本地图片依赖处理
npm i html-loader -D
负责引入 img 处理 html 的 img,从而能被 url-loader 处理
html
<img src="./vue.png" />
weboack.config.js
rules: {
test: /\.html$/,
loader: 'html-loader',
}
处理 iconfont
main.js
import './assets/icon/iconfont.css'
webpack.config.js
module: {
rules: [
{ test: /\.css$/, use: ['style-loader', 'css-loader'] },
{ test: /\.(eot|svg|ttf|woff|woff2)$/, loader: 'file-loader', options: { name: '[hash:10].[ext]' } },
],
}
devServer
反复通过 webpack 构建很麻烦,需要 devServer 用来自动化打包,打开浏览器,自动更新等开发功能
安装:npm i webpack-dev-server
启动:npx webpack-dev-server
module.exports = {
/**
* 在内存中编译打包,没有任何输出,只做开发调试
* 安装 npm i webpack-dev-server -D
* 启动 npx webpack-dev-server
*/
devServer: {
port: 3000,
open: true,
contentBase: `${__dirname}/dist`, // 选择文件位置 但据说 配置 HtmlWebpackPlugin 后这个配置不起任何作用
compress: true, // gzip 压缩
},
};
性能优化
HMR -> Hot Module Replacement
webpack-dev-server 给予自动更新的能力,但是不论代码里修改哪里,都会全局打包,如果项目大很影响性能。
我们需要 HMR 热模块替换功能优化,来按需热更新
devServer: {
hot: true, // 但工作中不建议开启这个东西
}
样式文件:支持 HMR, 因为 style-loader 支持,但是生产环境我们需要把 css 抽出来,所以用 mini-css-extract-plugin 插件,但好像目前也支持热更新
JS 文件:不支持 HMR
HTML:不支持 HMR,不支持热更新
解决: entry: [ './src/main.js', './public/index.html' ]
Source Map
构建后的代码与源代码之间的映射,可以更好的调试源代码
[inline|hidden|eval]-[nosources]-[cheap-[module]]-source-map
内联:速度快,内联到 js 文件中,不单独生成 map 文件。但会让代码体积变得非常大,生产模式下不可用
外部:单独生成 map 文件,独立与 js
代码 | 方式 | 说明 |
---|---|---|
inline-source-map | 内联 | 只生成一个内联 source-map |
hidden-source-map | 外部 | 只能提示错误原因,不能追踪到源代码对应位置,只隐藏源代码,不隐藏构建后的代码 |
eval-source-map | 内联 | 每一个文件都有对应的 source-map 且在 eval 中 |
source-map | 外部 | |
nosources-source-map | 外部 | 只能提示错误原因,不能追踪到源代码对应位置。全部隐藏 |
cheap-source-map | 外部 | 只精确到错误行,不能准确到行内的具体哪里错误 |
cheap-module-source-map | 外部 | 只精确到错误行,不能准确到行内的具体哪里错误 |
hidden 和 nosources 就是为了隐藏源代码防止代码泄漏的
方式 | 代码 |
---|---|
速度 | eval-cheap > eval > inline > cheap |
调试友好 | source-map > cheap-module-source-map > cheap-source-map |
综合 | eval-source-map -> vue/react 脚手架默认方式 |
生产环境:内联让体积变大不考虑 | source-map / cheap-module-source-map |
module | 会将 loader 等的三房 source-map 带进来 |
webpack 官网说明 https://www.webpackjs.com/configuration/devtool/
三方博客https://www.jianshu.com/p/62dc120d96d0
devtool | 构建速度 | 重新构建速度 | 生产环境 | 品质(quality) |
---|---|---|---|---|
(none) | +++ | +++ | yes | 打包后的代码 |
eval | +++ | +++ | no | 生成后的代码 |
cheap-eval-source-map | + | ++ | no | 转换过的代码(仅限行) |
cheap-module-eval-source-map | o | ++ | no | 原始源代码(仅限行) |
eval-source-map | – | + | n | 原始源代码 |
cheap-source-map | + | o | n | 转换过的代码(仅限行) |
cheap-module-source-map | o | - | no | 原始源代码(仅限行) |
inline-cheap-source-map | + | o | n | 转换过的代码(仅限行) |
inline-cheap-module-source-ma | o | - | no | 原始源代码(仅限行) |
source-map | – | – | yes | 原始源代码 |
inline-source-map | – | – | no | 原始源代码 |
hidden-source-map | – | – | yes | 原始源代码 |
nosources-source-ma | – | – | yes | 无源代码内容 |
+++ 非常快速, ++ 快速, + 比较快, o 中等, - 比较慢, -- 慢
devtool: 'source-map',
oneOf
所有类型的文件都会走一边 loader,命中的处理,不命中的走下一个,这样会浪费性能。
但是不能有两个loader 处理同一个配置,比如eslint 的 js loader和 babel 的 js loader 。这时候就需要把 eslint 的loader 提取到 oneOf 外边
rules: [
oneOf: [
{
test: /\.css$/,
use: []
}
]
]
缓存
- babel 缓存
babel-loader
https://www.webpackjs.com/loaders/babel-loader/
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/,
options: {
preset: [],
...,
cacheDirectory: true, // 开启 babel 缓存
}
}
- 文件资源缓存
hash: webpack 每次打包都会有一个唯一的 hash,利用这个可以做到每次不同打包 hash 不同。但是一个文件改动重新打包所有文件 hash 都变
chunkHase: 如果文件来自于同一个 chunk,则 hash 一致
chunk:所有在 webpack.config.js 中entry 引入的都会生成一个 chunk,
contenthash:根据单个文件生成的 hash 文件不变 hash 不变
output: {
path: `${__dirname}/dist`,
filename: 'js/app[contenthash:10].js'
},
new MiniCssExtractPlugin({
filename: 'css/index[contenthash:10].css'
})
tree shaking
概念:去除无用代码,缩小项目体积
- 必须使用 ES6 模块化
- 开启 production 模式
缺点: 可能会无意中把 css/ @babel/polyfill 等文件删掉
解决:
package.json
"sideEffects": "[*.css]", // 把 css 标记为不进行 tree shaking 的文件类型
code split
代码分割可以将单页应用分割成小分按需加载。比如路由分割 -> 单页通过路由切换组件
使用
- 安装
- 配置
webpack.config.js
文件 - 解析文件 , 获得配置对象
- 根据配置对象进行打包构建
webpack -w
自动监听文件更改,避免修改后重复输入webpack
指令
根目录下新建 src/index.js 作为目标文件
配置 webpack.config.js
终端输入 npx webpack
|| 终端内输入webpack 文件名 出口路径
可以处理单个文件(测试不管用)
webpack.config.js
// 通过 npx webpack直接进行打包
/*
webpack配置文件默认叫 webpack.config.js | webpackfile.js
在 nodemodules/webpack-cli/bin/config/config-yargs.js 中有相关代码,搜索 webpackfile.js
可在 package.json 的脚本中指定配置文件,通过 npm run build 执行
"scripts: {
"build": "webpack --config my_webpack"
}"
也或者在 npm run build -- --config my_webpack.js 指定配置文件
-- 是让命令知道后边跟的是字符串
*/
// commonjs 语法,也就是 nodejs 用的规范
const path = require('path')
module.exports = {
mode: 'development', // development | production
entry: './src/index.js', // 入口文件
output: {
filename: 'bundle.js', // 出口文件名字
// path.resolve 方法可以将任意路径解析为绝对路径
// __dirname 可以写也可以不写,不写已当前目录解析出来个绝对路径
// __dirname:已当前目录解析
path: path.resolve(__dirname, 'dist'), // 打包路径,必须是一个绝对路径
}
}
npx webpack 后,默认找 nodemodule/.bin/webpack.cmd
,如果有
当命令中键入 npx webpack 时,webpack 发现没有通过命令的形式指定出入口,就会去找 webpack.config.js 文件,当找到配置文件后,webpack 会去解析执行这个配置文件。当解析执行完配置文件后,导出配置对象,进行打包处理
包的查找规则
1.找项目根目录中有没有 node_modules 的文件夹
2.在 node_modules 中找包对应的文件夹
3.在 文件夹中找 package.json
4.在package.json中找 main 属性,main 属性指定了这个包在加载时的入口文件
plugins
webpack 默认只支持 js 模块,如果需要处理其他的文件格式,就必须通过 plugins
loader
默认 webpack 除了 js 外不支持其他语言,需要通过 loader 转换成 js 后统一处理
src/index.css
body:{marigin:0 auto}
src/index.js
require('./index.css')
npx webpack ==> 报错不识别 body:{marigin:0 auto} 提示需要对应的 loader
webpack 4.x
注意:直接 import ‘./index.css’ 不会向外暴露任何东西,所以不需要 import xx from ‘xx’ 接收
如果要模块化处理,需要接收。在 node_modules 中安装的包,引入时不用写路径,直接 import xx from '包名'
即可
目录分析
一般 webpack 中,带s的是数组,不带s的是对象
根目录
dist
main.js 打包出口文件
src
index.js 打包入口文件
webpack.config.js 配置文件
.babelrc 配置 babel
webpack.config.js
webpack.config.js 是默认的文件名,可以手动指定配置文件。
命令行
npx webpack --config xx
或者在 package.json 中的 npm 脚本中配置
"scripts": {
"build": "webpack --config xx"
}
这里不需要 "npx webpack","webpack" 会自动去 nodemu 中找 webpack
在或者在原来的 npm scripts 后加参数
"scripts": {
"build": "xxx"
}
npm run build --config myWebpack.js
因为 wepback 是基于 node 的,支持 node 的所有语法和 API
在 webpack 4.x 中,约定大于配置。默认打包入口文件路径为 src/index.js
const path = require('path');
module.exports = {
entry: './src/main.js',
output: {
filename: 'js/main.js', // output filename
path: path.resolve(__dirname, 'dist') // output path mast be an absolute path
},
mode: 'development' || 'production'
}
// 单页面
entry: '/src/index.js',
output: {
filename: 'js/bundle_[hash:6].js',
path: path.resolve(__dirname, 'dist'), //磁盘路径
}
// 多页面
entry: {
key: value, // key为入口文件名,value为入口文件路径
key: value,
},
output: {
filename: 'js/[name]_[hash:6].js',
path: path.resolve(__dirname, 'dist')
}
因为 node 默认打包. js 文件,但是对于图片 html 等文件无法主动处理,所以要配置第三方的 loader ,在 module 中配置。当 webpack 执行中只要遇到不能处理的就会来 module 寻找第三方匹配规则
module: {
rules: [
use两个顺序颠倒报错 exclude排除目录一定要写,否则跑起来报错
{ test: /\.css$/, use: ['style-loader', 'css-loader'] , exclude:/node_modules/ },
{ test: /\.js|jsx$/, use: 'bable-loader' , exclude:/node_modules/ }
]
}
webapck-dev-devServer
webpack 静态服务器
webpack 内置了一个开发的静态服务,内部通过 express 实现,可以把文件打包到内存中
安装:npm i webpack-dev-server -D
npx webpack-dev-server
或者在 package.json 中配一个
"scripts": {
"server": "webpack-dev-server"
}
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/a.js',
output: {
// 输出哈希文件名防止缓存问题
filename: 'build.[hase:9].js',
path: path.resolve(__dirname, 'build')
},
devServer: {
hot:true, // 热重载
port: 3000,
open: true, // 自动浏览器打开项目
progress: true // 打包加入进度条
contentBase: './build',
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify: {
// 移除 html 属性值的双引号
removeAttributeQuotes: true,
// 代码压缩成一行
collapseWhitespace: true,
},
// 添加哈希戳
hash: true,
})
]
}
当 npx webpack 后,发现虽然起个静态服务器,但是 webpack 没有处理 js 与 index.html 之间的依赖关系。在 src/index.html 中 scr 引入的 js 并没有在出口文件中自动生成 index.html
HtmlWebpackPlugin
安装:npm i html-webpack-plugin -D
插件的使用差不多都一样,webpack 本身基本上是用各种插件堆积起来的
src 下的 index.html 叫模版
使用插件
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/a.js',
output: {
// 输出哈希文件名防止缓存问题
filename: 'build.[hase:8].js',
path: path.resolve(__dirname, 'build')
},
devServer: {
port: 3000,
open: true,
progress: true,
contentBase: './build',
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify: {
// 移除 html 属性值的双引号
removeAttributeQuotes: true,
// 代码压缩成一行
collapseWhitespace: true,
},
// 添加哈希戳
hash: true
})
]
}
resolve
文件拓展名省略
resolve: {
extensions: ['.js', '.css', 'vue']
}
.babelrc
- 装包
- 在
module
中加规则 - 配置
.babelrc
JSON 语法,内层双引号
{
"presets": [ 所有语法
],
"plugins": [ 所有插件
]
}
webpack-dev-server
帮我们监听项目代码,实时编译,不需要重新手动单个编译
-
安装
npm i webpack-dev-server -D
- 注意:
webpack-dev-server
依赖于webpack
,并强制要求项目本地安装webpack
和webpack-cli
-
使用:
-
推荐方式:
npm
脚本中"dev": "webpack-dev-server --open --port 3000 --hot --contentBase 目录"
- 或者在
webpack.config.js
中配置devServer
。然后cmd
键入webpack-dev-server
--open iexp --hot --port 3000 --host 127.0.0.1
- 热重载的意思为当代码改变时不需要 webpack 一下,刷新浏览器就能看到变化的结果,如果想自动刷新浏览器,目前未知解决办法。当前浏览器不能自动刷新
- https://blog.csdn.net/hsl0530hsl/article/details/78419693
-
-
webpack-dev-server 默认打包好的 main.js 是托管在根目录的内存中,/main.js 可以访问到,但是在本地中找不到。所以在用script标签引入main.js时写./main.js 而不是 …/dist/main.js
-
常见报错
webpack-dev-server
不是内部或外部命令,也不是可运行的程序
原因 : cmd只能直接执行全局-g安装的 , 无法直接运行项目-D安装的 , 所以无法把它当作脚本命令。
解决方法:写入npm
脚本运行code ENOSELF
解决方法:把package.json
中的"name": "webpack"
值随便改一下- 如果还不行,删
node_modules
重来 - 热重载失效
webpack-dev-server
并不能读取你的webpack.config.js
的配置output
。你在webpack.config.js
里面的配置output
属性是你用webpack
打包时候才起作用的,对webpack-dev-server
并不起作用webpack-dev-server
打包生产的文件不出现在项目目录中,它默认打包的文件名是bundle.js
,在电脑内存中存储,默认地址为项目根目录,你可以在http://localhost:3000/bundle.js
尝试查看
-
注意事项
webpack-dev-server
会自动将打包好的文件并没有放在实际的物理磁盘中 , 而是直接托管到了电脑内存中,所以在根目录看不见对应的文件。可以认为以一种虚拟的形式托管在根目录中- 由于打包后的文件托管在根目录下,引用时scr直接写根目录下对应的
xxx.js
- 原因:磁盘速度没有内存快,不用每次重新请求磁盘文件,减少http请求次数
- 工作中,我们更推荐在npm脚本中直接书写
// 热重载第一步
const webpack = require('webpack');
devServer: {
// 端口号
port: 9000,
// 自动打开
open: true,
// 指定托管的根目录
contentBase:'src',
// 热重载第二步
hot: true,
},
plugins:[
// 热重载第三步
new webpack.HotModuleReplacementPlugin(),
]
html-webpack-plugin
src/index 作为模版,会原封不动的输出到 output 目录,所以在模版内不能引入任何资源,统一由 plugins 自动插入
将页面放在内存中
- 安装
npm i html-webpack-plugin -D
- 这个插件自动将内存中的main.js插入到页面当中,所以不需要手写script src
webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const htmlPlugin = new HtmlWebpackPlugin({
template: path.join(__dirname, './src/index.html'), // path.join(__dirname) 表示当前文件的路径,第二个参数为文件路径
filename: 'index.html' // 在内存生成后的名字
}
module.exports = {
plugins: [htmlPlugin], 注意这里是数组,不是对象
mode: 'development'
}
webpack
之 bable
- 安装
bable
:npm i bable-loader@8.0.4 @bable/core @bable/preset-env -D
路径别名
resolve: {
extensions: ['js', 'jsx', 'json'], 这里是省略后缀
alias: {
'@': path.join(__dirname, './src')
}
}
处理 css 文件
webpack 默认不能模块化处理 css 文件,需要安装 loader
cnpm i style-loader css-loader -D
- 配置 webpack 的 rules
当一个文件通过 import '.css' 时候,webpack 不识别,然后在配置里找第三方找 loader。
当发现有规则的时候,从右往左先调用 css-loader
然后交给 style-loader 二次处理,最后交给 webpack 打包处理
因此 use:['style-loader','css-loader'] 顺序不能错
module:{ 所有第三方模块的配置
rules:[ 第三方匹配规则
打包处理第三方样式表 loader
{text:/\.css$/,use:['style-loader','css-loader']},
]
}
处理 scss 文件
npm i sass-loader node-sass -D
:注意这里都是 sass 不是 scss- 配置 webpack
rules:[
打包处理 scss 的 loader
{text:/\.scss$/, use:['style-loader', 'css-loader', 'sass-loader'] }
]