# 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': '' } } } } ``` # 二、原理剖析 ## 一、预备知识