1. webpack介绍
webpack 是一个JavaScript 应用程序的静态模块打包工具
1.1 安装
npm install webpack webpack-cli --save-dev
1.2 入口(entry)
- 入口起点(entry point): 表示 webpack 应该使用哪个模块,用来作为构建其内部依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
- 默认是
./src/index.js
,但可以通过在webpack.config.js
中配置entry
属性,来指定一个(或多个)不同的入口起点。
1.2.1 创建 webpack.config.js
module.exports = {
entry: './src/index.js', // 入口配置,可以是对象
};
1.2.2 创建两个源代码文件
- src\style.css
body {
margin: 0;
padding: 0;
}
- src\index.js
import './style.css';
const str = "Hello Webpack!!!";
console.log(str);
1.3 输出(output)
output
表示 webpack 在哪里输出它所打包构建的 bundle,以及如何命名这些文件。- 主要输出文件的默认值是
./dist/main.js
,其他生成文件默认放置在./dist
文件夹中。
1.3.1 webpack.config.js
+ const path = require('path');
module.exports = {
entry: './src/index.js',
+ output: {
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'main.js'
+ }
};
1.4 模式(mode)
-
webpack 4.x 版本引入了 mode 的概念: 区分开发环境和生产环境。
-
development:开发时使用,构建结果用于本地开发调试,不进行代码压缩,打印 debug 信息,包含 sourcemap 文件。
-
production:生产时使用,打包构建后的结果是直接应用于线上的,即代码都是压缩后,运行时不打印 debug 信息,静态文件不包括 sourcemap。
-
mode 指定
production
时,默认会启用各种性能优化的功能,包括构建结果优化以及 webpack 运行性能优化。 -
mode 指定
development
时,则会开启 debug 工具,运行时打印详细的错误信息,以及更加快速的增量编译构建。
1.4.1 webpack.config.js
const path = require('path');
module.exports = {
+ mode: 'development', // production/development
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
}
1.4.2 环境差异
-
其默认值为
production
-
开发环境
- 需要生成 sourcemap 文件;
- 需要打印 debug 信息;
- 需要 live reload 或者 hot reload 的功能;
- 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。
-
生产环境
- 可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件;
- 需要压缩 HTML/CSS/JS 代码;
- 需要压缩图片;
- 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin。
1.4.3 区分环境
--mode
:用来设置模块内的process.env.NODE_ENV。
--env
:用来设置 webpack 配置文件的函数参数。cross-env
:用来设置 node 环境的process.env.NODE_ENV
。webpack.DefinePlugin
:用来设置模块内的全局变量。
1.4.4 环境配置
- 方式一
- webpack的 mode 默认为
production
webpack serve
的 mode 默认为development
- 可以在模块内通过
process.env.NODE_ENV
获取当前的环境变量,无法在 webpack 配置文件中获取此变量
package.json
"scripts": {
"build": "webpack",
"start": "webpack serve"
},
index.js
console.log(process.env.NODE_ENV); // development | production
webpack.config.js
console.log(process.env.NODE_ENV); // undefined
- 方式二
与方式一作用一样
package.json
"scripts": {
"build": "webpack --mode=production",
"start": "webpack --mode=development serve"
},
- 方式三
- 无法在模块内通过
process.env.NODE_ENV
访问- 可以通过
webpack 配置文件中
中通过函数获取当前环境变量
package.json
"scripts": {
"dev": "webpack serve --env=development",
"build": "webpack --env=production",
}
index.js
console.log(process.env.NODE_ENV); // undefined
webpack.config.js
console.log(process.env.NODE_ENV);// undefined
module.exports = (env, argv) => {
console.log('env',env); // development | production
};
- 方式四
webpack.config.js 中配置,与方式二的作用一样
module.exports = {
mode: 'development'
}
- 方式五: webpack.DefinePlugin
- 设置全局变量(不是
window
),所有模块都能读取到该变量的值。- 可以在任意模块内通过
process.env.NODE_ENV
获取当前的环境变量。- 无法在 node 环境(webpack 配置文件中)下获取当前的环境变量。
webpack.config.js 中配置 plugins
const webpack = require('webpack');
plugins:[
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) // 必须使用JSON.stringify 指定
})
]
index.js
console.log(NODE_ENV); // production 其他等等变量
webpack.config.js
console.log(process.env.NODE_ENV); // undefined
console.log(NODE_ENV); // error
- 方式六:cross-env
- 只能设置 node 环境下的变量 NODE_ENV
package.json
"scripts": {
"dev": "cross-env NODE_ENV=development webpack"
}
webpack.config.js
console.log(process.env.NODE_ENV); // development
1.5 loader
- webpack 只能解析
JavaScript
和JSON
资源文件。 - loader 让
webpack
能够去处理其他类型的资源文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中。
1.5.1 webpack.config.js
const path = require('path');
module.exports = {
mode: 'development', // production/development
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/, // 指定解析的资源类型
+ use: ['style-loader','css-loader'] // webpack 为从后往前使用对应的 loader
+ }
+ ]
+ }
};
1.6 插件(plugin)
- loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化 、资源管理、注入环境变量等。
1.6.1 src\index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webpack</title>
</head>
<body></body>
</html>
1.6.2 webapck.config.js
- 安装 html-webpack-plugin
npm install html-webpack-plugin --save-dev
webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve("dist"),
filename: "main.js",
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
+ plugins: [
+ new HtmlWebpackPlugin({
+ template: "./src/index.html",
+ }),
+ ],
};
2. 开发服务器 webpack-dev-server
2.1 安装
npm install webpack-dev-server --save-dev
2.2 webpack.config.js
类别 | 配置名称 | 描述 |
---|---|---|
output | path | 指定输出到硬盘上的目录 |
output | publicPath | 表示的是打包生成的 index.html 文件里面引用资源的前缀 |
devServer | publicPath | 表示的是打包生成的静态文件所在的位置(若是devServer里面的publicPath没有设置,则会认为是output里面设置的publicPath的值) |
devServer | static | 用于配置提供额外静态文件内容的目录(默认就是 output 中指定 path 路径) |
webpack.config.js
+ const CopyWebpackPlugin = require("copy-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve("dist"),
filename: "main.js",
},
+ devServer: {
+ static: path.resolve(__dirname, 'public'), // 指定开发时的资源静态目录,如果里面还有其他资源在打包到生产环境时需要用到 copy-webpack-plugin 插件包资源拷贝到 output 中指定的打包到生产环境 path 路径中。可以配置为数组,表示多个静态目录。static: []
+ port: 8080,
+ open: true
+ },
plugins: [
+ new CopyWebpackPlugin({
+ patterns: [
+ { from: "other", to: "public" },
+ { from: "public", to: "." },
+ ],
+ }),
],
}
2.3 package.json
"scripts": {
"build": "webpack",
+ "start": "webpack serve"
}
3. 设置变量别名
设置的路径别名,方便在模块中导入其他模块。
3.1 webpack.config.js
const path = require('path');
module.exports = {
+ resolve: {
+ alias: {
+ "@": path.resolve("src"),
+ "assets": path.resolve("src/assets"),
+ },
+ },
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
}
4. 支持CSS
- css-loader 解析处理@import和url()
- style-loader 可以把 CSS 插入 DOM 中,style 标签中。
4.1 安装
npm install style-loader css-loader --save-dev
4.2 webpack.config.js
module.exports = {
// other config ...
module: {
rules: [
+ {
+ test: /\.css$/,
+ use: ["style-loader", "css-loader"], // 从后往前执行对应的loader
+ },
],
}
};
PS:
{
test: /\.css$/,
use: [
"style-loader",
// "css-loader",
{
// 需要一些参数设置,就这样配置一个对象,css-loader
loader: "css-loader",
options: {
url: false, // true: url引入的路径会被处理成 hash:background-image: url("images/a.jpg");
import: false, // true: @import('./index.css')
modules: false, // true: 模块化 css, 类名会被修改,需要 import styles from './index.css'
sourceMap: true,
importLoaders: true,
esModule: true, // true: {default: xxx} 给 style-loader 的数据结构
},
},
],
}
4.3 CSS 资源文件的应用
- src/reset.css
body {
background: red;
margin: 0;
padding: 0;
}
- src/index.css
@import "./reset.css";
h1 {
color: #ccc;
font-family: monospace;
}
- src/index.js
import "./index.css";
5. 支持 sass
5.1 安装
npm install sass sass-loader --save-dev
5.2 webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
+ {
+ test: /\.scss$/,
+ use: ["style-loader", "css-loader", "sass-loader"],
+ },
]
},
};
6. CSS兼容性
-
为了浏览器的兼容性,加入 -webkit,-ms,-o,-moz 这些前缀
- Trident内核:代表为IE浏览器, 前缀为 -ms
- Gecko内核:代表为Firefox, 前缀为 -moz
- Presto内核:代表为Opera, 前缀为 -o
- Webkit内核:产要代表为Chrome和Safari、Edge, 前缀为 -webkit
-
伪元素
::placeholder
可以选择一个表单元素的占位文本,它允许开发者和设计师自定义占位文本的样式。 -
查询css的兼容性: caniuse.com/
-
postcss-loader 可以使用 PostCSS 处理 CSS。
-
postcss-preset-env 把现代的CSS转换成大多数浏览器能理解的。
-
postcss-preset-env
已经包含了autoprefixer
和browsers
选项。
6.1 安装
npm install postcss-loader postcss-preset-env --save-dev
6.2 创建 postcss.config.js
const postcssPresetEnv = require("postcss-preset-env");
module.exports = {
plugins: [
postcssPresetEnv({
browsers: "last 5 version",
}),
],
};
6.3 webpack.config.js
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader", "postcss-loader"],
},
+ {
+ test: /\.scss$/,
+ use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
+ },
]
},
};
6.3 package.json 或 .browserslistrc
- browserslist
- browserslist-example
- .browserslistrc
{
+ "browserslist": {
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ],
+ "production": [
+ ">0.2%"
+ ]
+ }
}
7. 资源模块
- 资源模块是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader
raw-loader
=>asset/source
导出资源的源代码(字符串)file-loader
=>asset/resource
发送一个单独的文件并导出 URLurl-loader
=>asset/inline
导出一个资源的 data URI- asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用
url-loader
,并且配置资源体积限制实现 - Rule.type
- asset-modules
7.1 webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
console.log("process.env.NODE_ENV", process.env.NODE_ENV); // development | production
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve("dist"),
filename: "main.js",
},
module: {
rules: [
+ {
+ test: /\.png$/,
+ type: "asset/resource", // 相当与 file-loader,处理成一个文件输出到对应的目录中
+ },
+ {
+ test: /\.ico$/,
+ type: "asset/inline", // 相当于 url-loader,可以把文件内容处理成base64内联到html文件中
+ },
+ {
+ test: /\.png$/,
+ type: "asset/resource", // 相当与 raw-loader,输出字符串,对内容不做任何转换
+ },
+ {
+ test: /\.jpg$/,
+ type: "asset",
+ parser: {
+ dataUrlCondition: { // 内联条件,如果文件体积大于 maxSize 处理成文件,小于 maxSize 就处理成 base64
+ maxSize: 4 * 1024, // 4kb
+ },
+ },
+ },
],
}
};
PS:
{
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset', // 根据图片大小是 url 路径还是 base64 方式引入
generator: {
filename: 'images/[hash:8][ext][query]' // 局部指定输出位置
},
parser: {
dataUrlCondition: {
maxSize: 500 * 1024 // 限制于 500kb
}
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource', // 字体图标直接使用 url 路径引入
},
// html 中的资源处理, 比如 image
{
test: /\.html/i,
loader: 'html-loader'
},
}
src/index.js
+ import png from './assets/a1.png';
+ import ico from './assets/a2.ico';
+ import jpg from './assets/a3.jpg';
+ import txt from './assets/a4.txt';
+ console.log(png, ico, jpg, txt);
8. JS 兼容性处理
Babel 是一个编译 JavaScript 的平台, 可以把 ES6/ES7, React 的 JSX转义为 ES5
- @babel/preset-env
Babel 默认只转换新的最新ES语法,比如箭头函数
8.1 安装依赖
- babel-loader 使用 Babel 和 webpack 转译 JavaScript 文件。
- @babel/@babel/core Babel编译的核心包。
- @babel/preset-env 转换新的最新ES语 。
- @babel/@babel/preset-react React插件的Babel预设。
- @babel/plugin-proposal-decorators 把类和对象装饰器编译成ES5。
- @babel/plugin-proposal-class-properties 转换静态类属性以及使用属性初始值化语法声明的属性。
npm install babel-loader @babel/core @babel/preset-env --save-dev
npm install @babel/preset-react --save-dev
npm install @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-proposal-private-property-in-object @babel/plugin-proposal-private-methods --save-dev
8.3 webpack.config.js
module: {
rules: [
+ {
+ test: /\.jsx?$/,
+ // exclude: /node_modules/,
+ include: path.resolve("src"),
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: ["@babel/preset-env", '@babel/preset-react'],
+ plugins: [
+ ["@babel/plugin-proposal-decorators", { legacy: true }],
+ ["@babel/plugin-proposal-private-property-in-object", { "loose": true }],
+ ["@babel/plugin-proposal-private-methods", { "loose": true }],
+ ["@babel/plugin-proposal-class-properties", { loose: true }],
+ ],
+ },
+ },
+ },
{ test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] },
{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'postcss-loader','sass-loader'] },
{
test: /\.(jpg|png|gif|bmp|svg)$/,
type:'asset/resource',
generator:{
filename:'images/[hash][ext]'
}
}
]
},
- 或者配置 .babelrc
.babelrc
{
"presets": [
"@babel/preset-env"
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
[
"@babel/plugin-proposal-private-property-in-object",
{ "loose": true }
],
["@babel/plugin-proposal-private-methods", { "loose": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
这样 webpack 就配置简单一些
webapck.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
console.log("process.env.NODE_ENV", process.env.NODE_ENV); // development | production
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve("dist"),
filename: "main.js",
},
module: {
rules: [
+ {
+ test: /\.jsx?$/,
+ // exclude: /node_modules/,
+ include: path.resolve("src"),
+ loader: "babel-loader",
+ }
],
},
};
8.4 src/index.js
function readonly(target, key, descriptor) {
descriptor.writable = false;
}
class Person {
@readonly PI = 3.14;
}
let p1 = new Person();
p1.PI = 3.15;
console.log(p1);
8.5 配置编辑器识别一些特殊的写法,或提示
jsconfig
- 创建 jsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true
}
}
9. ESLint 语法(包括 jsx 语法)检查与矫正
eslint 一般会配合 husky 与 lint-staged 一起使用。
9.1 安装
babel-eslint 被 @babel/eslint-parser 替换
eslint-loader 被 eslint-webpack-plugin 替换
npm install eslint eslint-webpack-plugin @babel/eslint-parser --save-dev
9.2 webpack.config.js
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
+ const ESLintWebpackPlugin = require("eslint-webpack-plugin");
module.exports = {
mode: "development",
entry: "./src/index.js",
output: {
path: path.resolve("dist"),
filename: "main.js",
},
module: {
rules: [
{
test: /\.jsx?$/,
include: path.resolve("src"),
loader: "babel-loader",
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
+ new ESLintWebpackPlugin(),
],
};
9.3 .eslintrc.js
module.exports = {
root: true,
parser: "@babel/eslint-parser",
//指定解析器选项
parserOptions: {
sourceType: "module",
ecmaVersion: 2015,
},
//指定脚本的运行环境
env: {
browser: true,
},
// 启用的规则及其各自的错误级别
rules: {
indent: "off", // 缩进风格
quotes: "off", // 引号类型
"no-var": "error", // 禁止使用 var
"generator-star-spacing": "off", // allow async-await
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
},
};
10. prettier 代码风格检测
10.1 安装
npm install eslint-plugin-prettier prettier --save-dev
10.2 .eslintrc.js
module.exports = {
root: true,
parser: "@babel/eslint-parser",
//指定解析器选项
parserOptions: {
sourceType: "module",
ecmaVersion: 2015,
},
//指定脚本的运行环境
env: {
browser: true,
},
// 启用的规则及其各自的错误级别
// "off"或者0 // 关闭规则
// "warn"或者1 // 在打开的规则作为警告(不影响退出代码)
// "error"或者2 // 把规则作为一个错误(退出代码触发时为1)
+ plugins: ["prettier"],
rules: {
+ "prettier/prettier": "error", // 遇到 prettier 检测就报错
indent: "off", // 缩进风格
quotes: "off", // 引号类
"no-var": "error", // 禁止使用 var
"generator-star-spacing": "off", // allow async-await
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
},
};
10.3 prettier 与 eslint 规则冲突解决
- 安装 eslint-config-prettier
通过使用 eslint-config-prettier 配置,能够关闭一些不必要的或者是与 prettier 冲突的 lint 选项。这样我们就不会看到一些 error 同时出现两次。使用的时候需要确保,这个配置在 extends 的最后一项。
npm install eslint-config-prettier --save-dev
10.4 .eslintrc.js
{
+ extends: [
+ 'standard', //使用standard做代码规范
+ "prettier",
+ ],
}
简写
{
+ "extends": ["plugin:prettier/recommended"]
}
10.5 prettierrc.js
module.exports = {
printWidth: 80, // 一行的字符数,如果超过会进行换行,默认为80
tabWidth: 2, // 一个tab代表几个空格数,默认为80
useTabs: false, // 是否使用tab进行缩进,默认为false,表示用空格进行缩减
singleQuote: false, // 字符串是否使用单引号,默认为false,使用双引号
semi: true, // 行位是否使用分号,默认为true
trailingComma: "none", // 是否使用尾逗号,有三个可选值"<none|es5|all>"
bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
parser: "babylon", // 代码的解析引擎,默认为babylon,与babel相同。
};