# webpack-demo
**Repository Path**: lukasavage/webpack-demo
## Basic Information
- **Project Name**: webpack-demo
- **Description**: 一个用于webpack深度学习的项目
- **Primary Language**: JavaScript
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-01-03
- **Last Updated**: 2022-12-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 一、webpack基础知识介绍
## 一、webpack介绍
> 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个 依赖图(dependency graph),此依赖图会映射项目所需的每个模块,并生成一个或多个 bundle
### 1.1 起步与安装
> 首先需要安装2个包,其中webpack是核心包,webpack-cli是命令行工具包,是基于webpack的
```bash
npm install webpack webpack-cli --save-dev
```
拓展:
①、将src下的js文件解析打包到build目录中(一般正式开发中都会创建webpack.config文件,所以用不上)
webpack ./src/js/index.js -o ./build/js/bulit.js --mode=development //开发环境
webpack ./src/js/index.js -o ./build/js/bulit.js --mode=production //生产依赖
### 1.2 核心模块概述
#### 1.2.1 入口(entry)
> 指示 webpack 以哪个文件为入口开始打包
- 入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部 依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的
- 默认值是 `./src/index.js`,但你可以通过在 `webpack configuration` 中配置 `entry` 属性,来指定一个(或多个)不同的入口起点.
如果想要设置多入口,则可以这样写↓
```json
entry: {
index: './public/assets/js/index', //打包入口项
list: './public/assets/js/list',
search: './public/assets/js/search',
detail: './public/assets/js/detail',
jquery: './public/assets/vendors/jquery/jquery.min.js',
},
```
- 最终写法
```js
const path = require('path');
module.exports = {
entry: './src/index.js',
+ output: {
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'main.js'
+ }
};
```
#### 1.2.2 输出 (output)
- `output` 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件
- 主要输出文件的默认值是 `./dist/main.js`,其他生成文件默认放置在 `./dist` 文件夹中。
- 写法示例
注意:path传的是绝对路径,即要配合核心模块path来搭配使用
```js
const path = require('path');
module.exports = {
entry: './src/index.js',
+ output: {
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'main.js'
+ }
};
```
#### 1.2.3 loader
- webpack 只能理解 `JavaScript` 和 `JSON` 文件
- loader 让 `webpack` 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中
- 写法示例
```js
const path = require('path');
module.exports = {
mode: 'development',
devtool:false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
+ module: {
+ rules: [
+ { test: /\.css$/, use: ['style-loader','css-loader']}
+ ]
+ }
};
```
#### 1.2.4 插件(plugin)
- loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量
- 写法示例
```js
const path = require('path');
// htmlWebpackPlugin: 创建html文件,同时引入打包后的js文件
+const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
devtool:false,
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
module: {
rules: [
{ test: /\.css$/, use: ['style-loader','css-loader']}
]
},
+ plugins: [
+ new HtmlWebpackPlugin({template: './src/index.html'})
+ ]
};
```
★★★拓展★★★:
```js
loader模块和plugins模块有什么区别?
1)从功能角度区分:
·loader模块只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译。仅仅是为了打包
·plugins模块主要是为了帮助loader模块完成不了的任务
2)从运行角度分析
·loader运行在打包文件之前(loader为在模块加载时的预处理文件)
·plugins在整个编译周期都起作用。
```
#### 1.2.5 模式(mode)
- 开发模式(development)
- 需要生成 sourcemap 文件
- 需要打印 debug 信息
- 需要 live reload 或者 hot reload 的功能
- 生产模式(默认值,development)
- 可能需要分离 CSS 成单独的文件,以便多个页面共享同一个 CSS 文件
- 需要压缩 HTML/CSS/JS 代码
- 需要压缩图片
那么我们如何设置模式呢?通常我们有三种方法, **--mode** **、** **DefinePlugin**、**cross-env**三种方式,其中前两种无法在webpack配置文件(webpack.config.js)获取环境变量。以下是三种方法的具体描述
- `--mode`用来设置模块内的`process.env.NODE_ENV`
```js
"scripts": {
"build": "webpack --mode=production",
"dev": "webpack --mode=development serve"
},
```
- `cross-env`用来设置node环境的`process.env.NODE_ENV`
需要先下载cross-env
```bash
npm i corss-env -D
```
```js
"scripts": {
"build": "cross-env NODE_ENV=development webpack"
"dev": "cross-env NODE_ENV=development webpack serve"
}
```
- `DefinePlugin`用来设置模块内的全局变量
```js
plugins:[
new webpack.DefinePlugin({
'process.env.NODE_ENV':JSON.stringify(process.env.NODE_ENV)
})
]
```
## 二、开发服务器(webpack-dev-server)
> 使用`webpack-dev-server`打包的话,默认采用express开启一个服务器,结果文件并不会写入硬盘,只会写到内存里面。打包后,http服务器的静态文件根目录有两个
>
> 1.打包后的dist目录
>
> 2.我们指定的静态文件根目录(public目录)
### 2.1 安装服务器
```bash
npm install webpack-dev-server -D
```
### 2.2 具体使用
```js
devServer: {
port: 8080, // 配置http默认服务的端口号,如果不设置,默认为8080
open: true, // 自动打开浏览器
compress: true, // 启动gzip压缩
hot: true, // 默认开启热模块替换,可写也可不写
// contentBase: path.resolve(__dirname, 'public'), // static:额外的静态文件更目录,通常指没有被webpack加载打包的图片等资源 // (先找index.js有没有加载进来,如果没有则会找public文件,还找不到则报错)(现已废弃,请使用static代替)
static: path.resolve(__dirname, 'public') //contentBase的替代方案,即第二个静态文件跟目录
}
```
### 2.3 关于文件路径的补充
| 类别 | 配置名称 | 描述 |
| :-------- | :--------- | :----------------------------------------------------------- |
| output | path | 指定输出到硬盘上的目录 |
| output | publicPath | 表示的是打包生成的index.html文件里面引用资源的前缀 |
| devServer | publicPath | 表示的是打包生成的静态文件所在的位置(若是devServer里面的publicPath没有设置,则会认为是output里面设置的publicPath的值) |
| devServer | static | 用于配置提供额外静态文件内容的目录 |
devServer的publicPath
- 默认值就是 /,也就是说直接访问端口即可访问其中的资源 `http://localhost:8080`
- 如果将其设置为 /www,那么在访问的时候就需要带上 `http://localhost:8080/www`
- 那么,bundle.js 通过 `http://localhost:8080/bundle.js` 也是无法访问的,**需要将 output.publicPath 也设置为 /www;并且官方也是建议 devServer 的 publicPath 和 output 的 publicPath 一致**
```js
module.exports: {
output: {
publicPath: '/www'
},
devServer: {
publicPath: '/www'
}
}
```
一般也不怎么去配置 devServer 的 publicPath,直接访问 `http://localhost:8080` 即可
## 三、支持CSS
> 由于webpack只能识别`js`或者`json`文件,因此在解析其他文件的时候,我们需要编写loader模块的内容。
要让webpack打包css文件,需要至少2个loader,他们分别是**style-loader**、**css-loader**,作用如下
- [css-loader](https://www.npmjs.com/package/css-loader)用来翻译处理@import和url()
- [style-loader](https://www.npmjs.com/package/style-loader)可以把翻译好的CSS变成style标签插入html的头部DOM中
可选包:如果要让你的webpack支持编译less,则可以选择性的下载`less-loader`
### 3.1 安装
```bash
npm i style-loader css-loader -D
```
`webpack.config.js`编码如下
```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
---(省略部分代码)
+ module: {
+ rules: [
+ { test: /\.css$/, use: ['style-loader','css-loader'] },
+ { test: /\.less$/, use: ['styles-loader', 'css-loader', 'less-loader'] }
+ ]
+ },
---(省略部分代码)
};
```
### 3.2 CSS兼容性处理
- 为了浏览器的兼容性,有时候我们必须加入-webkit,-ms,-o,-moz这些前缀
- Trident内核:主要代表为IE浏览器, 前缀为-ms
- Gecko内核:主要代表为Firefox, 前缀为-moz
- Presto内核:主要代表为Opera, 前缀为-o
- Webkit内核:产要代表为Chrome和Safari, 前缀为-webkit
- 伪元素`::placeholder`可以选择一个表单元素的占位文本,它允许开发者和设计师自定义占位文本的样式。
#### 3.2.1 安装
- https://caniuse.com/
- [postcss-loader](https://github.com/webpack-contrib/postcss-loader)可以使用PostCSS处理CSS兼容性
- [postcss-preset-env](https://github.com/csstools/postcss-preset-env)把现代的CSS转换成大多数浏览器能理解的
- PostCSS Preset Env已经包含了`autoprefixer`和`browsers`选项
```bash
npm i postcss-loader postcss-preset-env -D
```
#### 3.2.2 具体编码
1. 首先我们需要在根目录下创建`postcss.config.js`或`.postcssrc`文件
```js
// postcss.config.js
let postcssPresetEnv = require('postcss-preset-env');
module.exports={
plugins:[postcssPresetEnv({
browsers: 'last 5 version' // 表示最近最新的5个版本
})]
}
```
2. 配置`webpack.config.js`文件
```
module: {
rules: [
+ { test: /\.css$/, use: ['style-loader', 'css-loader','postcss-loader'] },
+ { test: /\.less$/, use: ['style-loader','css-loader','postcss-loader','less-loader'] },
+ { test: /\.scss$/, use: ['style-loader','css-loader','postcss-loader','sass-loader'] }
]
},
```
3. 也可以在`package.json`文件中配置
```json
{
+ "browserslist": {
+ "development": [
+ "last 1 chrome version", // 最近的谷歌浏览器1个版本
+ "last 1 firefox version",
+ "last 1 safari version"
+ ],
+ "production": [
+ ">0.2%" // 市场占用率大于0.2支持
+ ]
+ }
}
```
## 四、静态资源处理
在webpack v4版本,我们处理图片、icon等都需要用到2个loader,它们分别是`file-loader`,`url-loader`,作用如下
- file-loader 读取源图片,拷贝并重命名输出到目标目录下,然后返回新的名称。
- url-loader 可以把静态文件bianchengbase64字符串,进行返回,可以直接内嵌到HTML里面。
但是,在v5版本之后,这两个loader都废弃了(因为麻烦,需要单独安装loader,所以内置了),新版配置如下
- `raw-loader` => `asset/source` 导出资源的源代码,通常配置`.txt`文件,会把txt内容直接返回
- `file-loader` => `asset/resource` 发送一个单独的文件并导出 URL,通常配置`.png`图片, 会把png图片自动拷贝到输出目录中,并返回新路径或者名称
- `url-loader` => `asset/inline` 导出一个资源的 data URI,通常配置`.ico`图标,会把ico文件变成base64字符串并返回给调用者
注意:可以看你心情随意配置
编码如下
```js
module.exports = {
module:{
rules: [
---省略部分代码
+ {
+ test: /\.png$/,
+ type: 'asset/resource' // 会把png图片自动拷贝到输出目录中,并返回新路径或者名称
+ },
+ {
+ test: /\.ico$/,
+ type: 'asset/inline' // 会把ico文件变成base64字符串并返回给调用者
+ },
+ {
+ test: /\.txt$/,
+ type: 'asset/source' // 会把txt内容直接返回
+ },
+ {
+ test: /\.jpg$/,
+ type: 'asset', // 表示可以根据实际情况进行子选择是resource还是inline
+ parser: {
+ dataUrlCondition: {
+ maxSize: 4 * 1024 // 4kb,如果文件大小小于4k,走inline,如果大于4k就走resource
+ }
+ }
+ }
]
},
---省略部分代码
};
```
## 五、JS兼容性处理
> Babel其实是一个编译JavaScript的平台,可以把ES6/ES7,React的JSX转义为ES5
### 5.1 安装依赖
- [babel-loader](https://www.npmjs.com/package/babel-loader)使用Babel和webpack转译JavaScript文件
- [@babel/@babel/core](https://www.npmjs.com/package/@babel/core)Babel编译的核心包
- [babel-preset-env](https://www.babeljs.cn/docs/babel-preset-env)
- [@babel/@babel/preset-react](https://www.npmjs.com/package/@babel/preset-react)React插件的Babel预设
- [@babel/plugin-proposal-decorators](https://babeljs.io/docs/en/babel-plugin-proposal-decorators)把类和对象装饰器编译成ES5
- [@babel/plugin-proposal-class-properties](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties)转换静态类属性以及使用属性初始值化语法声明的属性
```bash
npm i babel-loader @babel/core @babel/preset-env @babel/preset-react -D
npm i @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties @babel/plugin-proposal-private-property-in-object @babel/plugin-proposal-private-methods -D
```
### 5.2 具体编码
```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
--- 省略部分代码
module: {
rules: [
+ {
+ test: /\.jsx?$/,
+ 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 }],
+ ],
+ },
+ },
+ },
--- 省略部分代码
]
}
};
```
## 六、代码校验
### 6.1 安装
- [eslint](https://eslint.org/docs/developer-guide/nodejs-api#cliengine)
- [eslint-loader](https://www.npmjs.com/package/eslint-loader)
- [configuring](https://eslint.org/docs/user-guide/configuring)
- [babel-eslint](https://www.npmjs.com/package/babel-eslint)
- [Rules](https://cloud.tencent.com/developer/chapter/12618)
- [ESlint 语法检测配置说明](https://segmentfault.com/a/1190000008742240)
```bash
npm install eslint eslint-loader babel-eslint --D
```
### 6.2 具体编码
1. 首先我们配置`webpack.config.js`
```js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
--- 省略部分代码
module: {
rules: [
+ {
+ test: /\.jsx?$/,
+ loader: 'eslint-loader',
+ enforce: 'pre', // 指定loader的类型,(类型有pre normal post inline)
+ options: { fix: true }, // 错误自动修复
+ exclude: /node_modules/, // 不检测node_modules里面的文件
+ },
]
}
};
```
2. 新建一个`.eslintrc.js`文件
```js
module.exports = {
root: true, // 代表根配置文件
parser: 'babel-eslint', // 通过babel-eslint去识别
extends: 'airbnb', // 如果这里继承了airbnb,那么下面的rules就不用写了
parserOptions: {
// 解析选项
sourceType: 'module', //
ecmaVersion: 2015
},
env: {
browser: true,
node: true
},
// rules: {
// indent: 'off',
// quotes: 'off',
// 'no-console': 'error'
// }
}
```
## 七、服务器代理
主要是解决开发环境请求跨域的问题。在我们安装并配置了devServer后,还需要配置代理服务器,例如:express服务器端口号是3000,但我们的devServer是8080,我们只需要添加proxy选项配置代理就可以解决问题了。如下代码
```js
devServer: {
port: 8080,
proxy: {
"/api": {
target: 'http://localhost: 3000',
pathRewrite: {
'^/api': ''
}
}
}
}
```
# 二、原理剖析
## 一、预备知识