前言
什么是 webpack 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器 (module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
webpack 有哪些功能(代码转换 文件优化 代码分割 模块合并 自动刷新 代码校验 自动发布)
首先学习 webpack 需要有简单的 node 基础,打开 node 官方网站进行安装 node, http://nodejs.cn/ 下载最新版 node 包并进行安装。
学习目标:
- webpack 常见配置 webpack 高级配置
- webpack 优化策略
- AST 抽象语法树
- webpack 中的 Tapable
- 掌握 webpack 流程 手写 webpack
- 手写 webpack 中常见的 loader
- 手写 webpack 中常见的 plugin
- 定义好学习目标让我们开启 webpack 的新旅程。(本文学习主要针对 webpack4.0 进行学习讲解)
webpck—> 基础搭建与使用:
安装完毕在终端 快速创建 node 项目 执行命令 npm init -y 生成 packge.json
在当前目录安装本地 webpack
终端执行命令:
npm i webpack webpack-cli -d
i 表示 install ,d 表示当前是开发环境安装完成会产生 node_modules 文件
webpack 可以进行 0 配置 并且 webpack 是打包工具(默认是 js 模块 通过入口进行打包输出打包后 js 结果)。
创建 src 目录 –> 创建 index.js -> 输出:console.log(‘hello webpack’);
npx 语法进行把 index.js 进行打包
终端执行命令:
npx webpack
我们发现当前目录生成了一个 dist 目录并且创建了一个 main.js(如图:)
执行顺序:(默认找 node_modules—>bin 文件 –> webpack 文件)
这里我们明白了安装 webpack 必须安装他的依赖 webpack-cli
webpack 打包默认支持 js 模块化 -> 类似于 common.js
webpack: 两种默两种模式如果没有创建 webpack.config.js 配置文件指定 mode (production/development)生成模式或开发模式,打包运行会直接默认生产模式打包并且进行压缩。
这里说一下 webpack 配置文件的默认名称有两种(webpack.config.js / webpackfile.js 一般情况下我们会选择前一种)
如何手动配置 webpack 呢?其实比较简单
(1)创建 webpack.config.js 配置文件 由于 webpack 是 node.js 的框架所以配置文件中要采用 node 语法来进行编辑。
const path = require("path"); //webpack 内部方法 path 组件
module.exports = {
mode: "development", // 打包模式 development 开发模式
entry: "./src/index.js", // 入口文件指定
output: {
// 出口文件配置 配置出口打包路径
filename: "build.js", // 打包后的文件名称
path: path.resolve(__dirname, "build") //resolve 绝对路径引入
}
};
我们分析一下 build.js 打包出的结果, 默认下是一个匿名函数 并且接收两个参数 接收一个对象,Key : value (key: 是当前模块的路径 value:是一个执行函数)
接收到 modules 里 先定义一个缓存对象 installedModules 先定一个缓存目的是如果我当前模块加载完成没有必要再进行加载
webpack_require 实现了一个 require 方法因为浏览器无法直接执行 node 的 require 方法 (详解如图)
执行__webpack_require__ 发现接收了一个入口模块
终端运行:npx webpack , 发现我们打包当前目录产生了文件夹 build 目录
分析了一下打包文件是不是感觉 webpack 源码没有想象的那么难 继续我们 webpack 的探索之旅。
如何更改 webpack 配置文件名称呢其实很简单重命名 webpack.config.js (webpack.test.js)
执行命令:
npx webpack –config webpack.test.js 发现可以执行 webpack 打包。
这样打包我发现命令很长所以我们利用 packge.json 来配置打包脚本在 scripts–> 添加 build.
"scripts": {
"test": "echo \"Error: no test specified\"&& exit 1",
"build": "webpack --config webpack.config.js"
}
终端运行 npm run build 发现执行打包结果一样.
(2)webpack 其他配 –> 置插件的使用不会生成文件会生成内存中的打包
安装 webpck 内置服务 webpack-dev-server 好处是
终端执行命令: npm i webpack-dev-server -d -save
安装完成可以执行 npx webpack-dev-server 按提示打开 http://localhost:8080/
如何配置开发服务运行目录可以在配置文件中添加在 webpack.config.js 添加 devServer
const path = require("path");
module.exports = {
mode: "development", // 打包模式
entry: "./src/index.js", // 入口文件指定
output: {
// 出口文件配置 配置出口打包路径
filename: "build.js", // 打包的文件名称
path: path.resolve(__dirname, "build") //resolve 绝对路径引入
},
devServer: {
// 开发服务器配置
contentBase: "./build", // 指向打包目录
port: 3000, // 服务端口号
progress: true, // 打包进度
open: true, // 是否打开浏览器
compress: false // 是否压缩
}
};
在 packge.json 中添加 start 启动服务脚本
"scripts": {
"test": "echo \"Error: no test specified\"&& exit 1",
"build": "webpack --config webpack.config.js",
"start":"webpack-dev-server"
}
运行 npm run start 发现没有自动创建 index.html 不能直观看到我们代码在浏览器的执行。
在 src 目录下创建 html 模板 index.html 并安装 html-webpack-plugin 插件
终端运行: npm i -d html-webpack-plugin
在 webpack.config.js 下添加插件配置 plugins
const path = require("path");
const HtmlWebPackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development", // 打包模式
entry: "./src/index.js", // 入口文件指定
output: {
// 出口文件配置 配置出口打包路径
filename: "build[hash:8].js", // 打包的文件名称 filename: "build[hash:8] 添加哈希值
path: path.resolve(__dirname, "build") //resolve 绝对路径引入
},
devServer: {
// 开发服务器配置
contentBase: "./build", // 指向打包目录
port: 3000, // 服务端口号
progress: true, // 打包进度
open: true, // 是否打开浏览器
compress: false // 是否压缩
},
// 插件
plugins: [
// 数组形式 存放所有的 webpack 插件
new HtmlWebPackPlugin({
filename: "index.html", // 生成打包文件名
template: "./src/index.html", // 模板路径
minify: { // 生产模式可以进行配置
removeAttributeQuotes: true, // 删除 html 文件双引号
collapseWhitespace: true // 折叠控行
},
hash:true, // 添加哈希值
})
]
};
终端执行打包测试:npm run build (build 目录下生成了我们想要生成的 index.html 文件)
样式的配置 webpack 配置 css 模块:
配置样式需要一个合适 loader,loader 会将我们的样式文件解析成模块 (module)
终端:npm i -d –save css-loader style-loader
如何使用样式 loader 进行配置呢?我们先在 src 下建 index.css 并给 body 赋予简单样式
在 webpack.config.js 进行简单配置
css-loader 主要解析我们样式中 @import 语法,style-loader 是吧 css 样式插入 head 标签中.
+ module: {
// 添加模块模块是对象
rules: [
// 规则 css-loader 主要解析我们样式中 @import 语法
{
test: /\.css$/,
use: ["style-loader", "css-loader"] // 执行顺序是重右向左执行 - > 重下到上
}
]
}
在 index.js 中引入样式文件 import ‘./index.css’
终端运行:npm run start 样式生效了同样我们也有对应的 less less-loader
终端:npm i -d –save less less-loader
在这里说一下 loader 的另一种写法对象写法可以给 loader 添加一些属性 options
+ module: {
// 添加模块模块是对象
rules: [
// 规则 css-loader 主要解析我们样式中 @import 语法
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {insertAt: "top" // 把标签插入顶部}
},
"css-loader"
] // 执行顺序是重右向左执行 - > 重下到上
},
{
test: /\.less$/,
use: [
{
loader: "style-loader",
options: {insertAt: "top" // 把标签插入顶部}
},
"css-loader",
"less-loader"
] // 执行顺序是重右向左执行 - > 重下到上
}
]
}
css 样式的抽离 安装抽离插件 mini-css-extract-plugin npm i -d –save mini-css-extract-plugin
引入插件并在配置文件中进行配置
const path = require("path");
const HtmlWebPackPlugin = require("html-webpack-plugin");
const MinCssExtractPlugin = require("mini-css-extract-plugin"); // 抽离 css 插件
module.exports = {
mode: "development", // 打包模式
entry: "./src/index.js", // 入口文件指定
output: {
// 出口文件配置 配置出口打包路径
filename: "[name][hash:8].js", // 打包的文件名称 filename: "build[hash:8] 添加哈希值
path: path.resolve(__dirname, "build") //resolve 绝对路径引入
},
devServer: {
// 开发服务器配置
contentBase: "./build", // 指向打包目录
port: 3000, // 服务端口号
progress: true, // 打包进度
open: true, // 是否打开浏览器
compress: false // 是否压缩
},
// 插件
plugins: [
// 数组形式 存放所有的 webpack 插件
new HtmlWebPackPlugin({
filename: "index.html", // 生成打包文件名
template: "./src/index.html", // 模板路径
minify: {
removeAttributeQuotes: true, // 删除 html 文件双引号
collapseWhitespace: true // 折叠控行
},
hash: true // 添加哈希值
}),
new MinCssExtractPlugin({filename: "mian.css"})
],
module: {
// 添加模块模块是对象
rules: [
// 规则 css-loader 主要解析我们样式中 @import 语法
{
test: /\.css$/,
use: [
MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
"css-loader"
] // 执行顺序是重右向左执行 - > 重下到上
},
{
test: /\.less$/,
use: [
MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
"css-loader",
"less-loader"
] // 执行顺序是重右向左执行 - > 重下到上
}
]
}
}
添加样式前缀 postcss-loader autoprefixer
npm i postcss-loader autoprefixer -d
module: {
// 添加模块模块是对象
rules: [
// 规则 css-loader 主要解析我们样式中 @import 语法
{
test: /\.css$/,
use: [
MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
"css-loader",
"postcss-loader"
] // 执行顺序是重右向左执行 - > 重下到上
},
{
test: /\.less$/,
use: [
MinCssExtractPlugin.loader, // 创建 link 标签放入到 main.css 里
"css-loader",
"postcss-loader",
"less-loader"
] // 执行顺序是重右向左执行 - > 重下到上
}
]
}
- 注意 postcss-loader 需要添加一个配置文件否则不会生效更目录创建 postcss.config.js
module.exports = {plugins: [require("autoprefixer")]
};
但是我们发现问题使用 mini-css-extract-plugin 插件导致我们 css 不会被压缩。
npm 官网有给出 To minify the output, use a plugin like optimize-css-assets-webpack-plugin. Setting optimization.minimizer overrides the defaults provided by webpack, so make sure to also specify a JS minimizer: 参考链接
要使用 optimize-css-assets-webpack-plugin 插件接下来我们安装配置一下.
npm i -d optimize-css-assets-webpack-plugin
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); // 压缩 js
// 配置文件中添加优化项
optimization: {minimizer: [new OptimizeCSSAssetsPlugin({})]
}
使用 optimize-css-assets-webpack-plugin 我们发现 js 右不会被压缩 所以要使用 uglifyjs-webpack-plugin –save-dev
$ npm install uglifyjs-webpack-plugin –save-dev
配置产考链接
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); // 压缩 js
// 配置文件中添加优化项
optimization: {minimizer: [new OptimizeCSSAssetsPlugin({}),new UglifyJsPlugin()]
}
语法的转换 babel
在 index.js 里写点 es6 语法箭头函数
let fn = () => {console.log("es6 webpack");
};
fn();
终端执行: npx webpack 查看打包文件
– 我们发现打包出来的仍然是 es6 语法这个时候我们需要一个 loader 进行转换 babel-loader babel @babel/core
(babel/core 是 @babel-loader 的核心组件转化模块 @babel/preset-env) 参考链接
终端运行:npm i -d babel-loader babel @babel/core @babel/preset-env
在 module 添加配置
// 在 rules 下添加配置
{
test: /\.js$/,
use: [
{
loader: "babel-loader",
options: {
// 转化 es5 语法 --presets 预设
presets: ["@babel/preset-env"]
}
}
]
},
终端运行:npm run build
发现可以已经转换 es6 语法 但是仅仅这样不能转换 es6 高阶语法比如一些特殊的类函数
class Test {// new Test() a =1 实例上添加 a 属性 这个语法属于 es7 语法打包时发现并不能解析
a = 1;
}
终端运行:npm run build 发现报错提示安装 @babel/plugin-proposal-class-properties
那我们按照要求按照一下插件 npm i -d @babel/plugin-proposal-class-properties 并进行一次配置
// 在 rules 下添加配置
{
test: /\.js$/,
use: [
{
loader: "babel-loader",
options: {
// 转化 es5 语法 --presets 预设
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-proposal-class-properties"]
}
}
]
},
还有一种写法 装饰器 @Log 打包也是不被解析的 在 js 添加
按照错误提示安装 decorators-legacy 参考链接 安装官方给出配置添加
// 在 rules 下添加配置
{
test: /\.js$/,
use: {
{
loader: "babel-loader",
options: {
// 转化 es5 语法 --presets 预设
presets: ["@babel/preset-env"],
plugins: [ // 这里要注意添加顺序
["@babel/plugin-proposal-decorators", { legacy: true}],
["@babel/plugin-proposal-class-properties", { loose: true}]]
}
}
},
转化完语法接下来看一下 babel 语法的校验 @babel/plugin-transform-runtime @babel/runtime 参考链接
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
// 转化 es5 语法 --presets 预设
presets: ["@babel/preset-env"],
plugins: [["@babel/plugin-proposal-decorators", { legacy: true}],
["@babel/plugin-proposal-class-properties", { loose: true}],
[
"@babel/plugin-transform-runtime",
{
absoluteRuntime: false,
corejs: false,
helpers: true,
regenerator: true,
useESModules: false
}
]
]
}
},
include: path.resolve(__dirname, "src"), // 只找__dirname - >src
exclude: /node_modules/ // 忽略 node_modulse
},
includes 实例方法不被解析 需要一个补丁模块 @babel/polyfill
npm install –save @babel/polyfill
Babel includes a polyfill that includes a custom regenerator runtime and core-js.
使用可以直接在 js 里引入即可。
接下来看一下代码校验 ESLint 代码校验工具参考链接
终端安装 npm i -d eslint eslint-loader
根据项目需求下载对应的 eslintrc.json 下载链接
// 代码校验 eslint
{
test: /\.js$/,
use: {
loader: "eslint-loader",
options: {enforce: "pre" // 强制执行顺序}
}
},
DEMO
- 本文回顾 1、webpack 基本功能 build.js 打包的原理及源码分析 2、webpack 基础配置及常用插件配置安装 3、loader 的使用与配置样式的处理 4、webpack 优化项的简单使用 5、babel 语法转换与使用 babel 语法校验 . 完成以上我相信大家可以掌握并搭建简单 webpack 项目。
- 第二章我们将讲解 webpack 其他组件的配置及 webpack 优化项 webpack 图片处理 多入口应用)