# max-webpack **Repository Path**: max-lu/max-webpack ## Basic Information - **Project Name**: max-webpack - **Description**: webpack - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-09 - **Last Updated**: 2022-08-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: webpack ## README # webpack 官网地址:https://webpack.docschina.org/ react:jsx vue:vue webpack是基于nodejs的 #### 安装 + 默认安装版本是5.x。安装4.x需要指定版本。5x发行时间是2020.10.8。推荐使用4x + 全局安装:-g + 局部安装(推荐):-d ```js npm init -y npm install webpack@4.43.0 webpack-cli@3.3.12 -D ``` + webpack-cli:简易客户端,用来以webpack协议链接相应服务 + webpack:打包编译核心 #### 启动 + 4.x:后实行了0配置启动。是个噱头,不适用,项目开发还是需要自己配置,配置门槛降低了 + 3.x:只需要安装webpack(里面包含cli)。配置门槛比较高(高到业界开始吐槽需要一个配置工程师) + npx可以运行局部安装的工具。会首先在当前的node_modules目录下面去找。找到node_modules/.bin目录下的webpack,进行软启动 ```javascript npx webpack // 打印信息查看 webpack.jpg图片 npm run build // 在package.json中加入"build": "webpack" ``` 执行完成以后,可以在dist中看到有一个main.js文件,里面打包后的代码是经过压缩的。是因为没有设置**mode**,默认是production模式,会对代码进行压缩 #### 0配置启动 webpack是有默认配置的;npx webpack 时,webpack看看是否有配置文件,没有则用默认配置,有的话,才会走开发的配置文件配置(webpack.config.js) ```js // 所谓的0配置(默认配置) { entry: "./src/index.js", output: { path: path.resolve(__dirname, "./dist"), filename: "main.js", }, } ``` #### 配置 ##### entry + 打包入口 + 支持 string array object + string和array:单入口(spa) + object:多入口(mpa);注意:多入口对应多出口 + 单入口(spa) ```js // spa单入口 entry: "./src/index.js" // 相对路径就可以 // 等价于 entry: { main: "./src/index.js", } // 所以 output: { filename: "[name].js", // 这里输出的是 main.js } ``` + 多入口(mpa) ```js // mpa多入口:多入口对应多出口 entry: { // index login可以理解为chunkName,chunks的名称 index: "./src/index.js", login: "./src/login.js", }, output: { path: path.resolve(__dirname, "./dist"), filename: "[name].js", // 多入口对应多出口,需要使用到“占位符”,[name]这里的name是entry里对应的key } ``` ##### output + 打包出口 + 一个对象,接受path和filename + path:绝对路径,需要用到path这个node包(path.resolve(__dirname, "./dist")) + filename:输出的文件名称 ```js output: { path: path.resolve(__dirname, "./dist"), filename: "main.js", // filename: "[name].js", // 利用占位符 } ``` +++ ##### mode + 打包模式 + mode模式:https://v4.webpack.docschina.org/concepts/mode/ ``` TerserPlugin:做js压缩 NoEmitOnErrorsPlugin:不要提示报错 ModuleConcatenationPlugin:摇树优化 ``` + js压缩的两大标杆: ``` TerserJS:TerserPlugin:专门针对es6+语法做的改善工作,在es6+的用法压缩上没有什么问题 UglifyJS(webpack.optimize.UglifyJsPlugin):ES6之前,优先想到这个,但是es6的时候有小小的bug ``` + 不同模式下Chunks名称(执行npm run build 后的log里面查看) ``` development: 有效的名称。从执行结果看是 main。方便一目了然的看到信息。不会压缩 production:启动确定性的混淆名称,是0、1、2、3、4 ``` ##### resolveLoader ```javascript resolveLoader: { modules: ["./node_modules", "./myLoaders"] // 指定使用loader的时候,去哪里找 } ``` ##### 占位符 ```javascript "[name]":entry配置里面的key "[hash]":是打包时,生成的hash "[chunkhash]":就是每次打包时,每个chunk 对应的hash值 "[contenthash]": 内容hash "[ext]":匹配到的文件后缀 "[id]": 模块标识符(module identifier).不常用!!!混淆模块的命名格式 "[query]":模块的 query,例如,文件名 ? 后面的字符串,不常用!!! ``` webpack三种指纹策略 ``` "[hash:6]" 表示取hash的前六位 ``` + hash ``` 1.这里的hash是随机生成的hash值,这个值就是我们后面看到的执行 npx webpack 看到的打印信息里面的hash值。 2.策略更新是和内容有关系,内容变更。则hash值变更,图片不受影响 ``` + chunkhash ``` 1.多入口 2.同一个chunks里面的内容有变化,对应的chunkhash会变 ``` + contenthash ``` 1.只有自身内容变化,contenthash才会变化,不受hash、chunkhash的影响。 2.如果自身改变,会影响到对应chunks里面的chunkhash的值 ``` + 最优方式 ``` 图片:hash js:chunkhash css:contenthash ``` +++ ##### plugins 插件(穿插在整个过程中) +++ ##### module & Loader ``` 问:webpack只支持js模块、json模块的编译,如果编译css文件怎么办? 答:webpack有外包机制,把编译非js、非json的模块,外包出去了。也就是交给第三方 ``` + 官方loader部分:https://v4.webpack.docschina.org/loaders/ + 这些被收录的loader,可以在github上看文档,如:https://github.com/webpack-contrib/css-loader + webpack把模块编译外包出去,有一个明确规定:一个外包只允许做一个事情。就是各种loader ###### module配置规则 ```javascript module: { rules: [ // 这里放入一个一个的loader { test: /\.css$/, // 这个是匹配规则,表示这个loader用在什么文件上 use: ["style-lader", "css-loader"] } ] } ``` ###### loader使用 + 如果单纯的使用loader,不进行任何配置,就直接使用字符串 ```javascript module: { rules: [ { test: /\.css$/, use: ["style-lader", "css-loader"] } ] } ``` + 如果需要一些配置,就需要传入对象(比如配置style-loader) ```javascript module: { rules: [ { test: /\.css$/, use: [ { loader: "style-loader", // 通过loader这个key执行loader options: { // 配置(看文档 injectType: "linkTag", } }, "css-loader" ] } ] } ``` ###### css-loader ``` 加载css文件解析import的css文件,最终返回css代码。只是做了编译,至于如何使用css,没有这方面的功能 ``` ###### style-loader ``` 将模块的导出作为样式添加到 DOM 中。使用后可以在html中看到一个标签。就是对打包的css的引用 通过操作dom的方式,在html、header标签内部创建style标签,把css编译好的代码放到style标签中去 ``` ```js // 不指定版本,会报错,因为我们用的webpack是4版本。默认安装最新的是支持webpack5版本的 npm install css-loader@5.2.6 -d npm install style-loader@2.0.0 -d ``` ```js module: { rules: [ // 规则列表 { test: /\.css/, // use: "css-loader", // 单个loader。只做了css编译,但是不会使用css,所以需要style-loader的帮助 use: ["style-loader", "css-loader"], // 多个loader作用于1个模块,是有执行顺序的,自后往前 } ] } ``` ###### less-loader ```js npm install less-loader@7 -d ``` ```js module: { rules: [ { test: /\.less$/, use: ["style-loader", "css-loader", "less-loader"], // 注意这里less-loader后需要一个css-loader,而不是直接style-loader }, ] } ``` 在node_modules中less-loader目录的上面可以看到less目录(虽然我们并没有安装less),因为less是less-loader的强依赖,一般情况下,建议把less也安装上,可以增强我们的感知 ```js npm install less less-loader@7 -d ``` ###### sass-loader ```js npm i sass-loader@2 sass-node sass -d // sass-loader 依赖于sass-node ``` ```javascript module: { rules: [ { test: /\.scss/, use: ["style-loader", "css-loader", "sass-loader"], }, ], } ``` ###### postcss-loader + postcss 处理css能力是NO.1 + 理解是一个工具集,本身不干活,干活都是下面的js插件,都是用来转换样式(编译CSS)。不可以编译less、sass(也有部分插件支持,但是不建议) + 中文文档:https://github.com/postcss/postcss/blob/main/docs/README-cn.md + 所有插件地址:https://github.com/postcss/postcss/blob/main/docs/plugins.md ```javascript npm install postcss postcss-loader@4 ``` ```javascript module: { rules: [ { test: /\.less$/, use: ["style-loader", "css-loader", "postcss-loader", "less-loader"], // 注意这个postcss-loader的位置 } ] } ``` 因为这个postcss-loader是一个工具集,所以还需要其他的配置。推荐创建postcss.config.js文件来配置 ```javascript npm install autoprefixer // 一个特别重要的插件,用来处理兼容性 npm install cssnano // 代码压缩,去掉一些空格、换行 // postcss.config.js module.exports = { plugins: [require("autoprefixer")], } ``` ###### babel + 处理JS能力是NO.1 +++ #### 核心概览 + chunk 代码片段 + chunks === chunkName 片段组 +++ #### 执行log说明 ```js Hash: 19678db35e4d4757d54d Version: webpack 4.43.0 Time: 57ms Built at: 2021/09/09 下午8:34:54 Asset Size Chunks Chunk Names main.js 3.8 KiB main [emitted] main // [emitted]: 表示生成的意思,就是一个标识位 Entrypoint main = main.js // 表示main.js是从那个文件产出的。具体的文件地址从下面找 [./src/index.js] 32 bytes {main} [built] // [built] 表示创建 ``` + Hash:生成唯一的hash值 + version:webpack的版本 + Time:执行时间 + Entrypoint:输出的文件名和对应原文件路径地址 #### main.js阅读 在development的模式下,执行得到的main.js文件没有压缩的情况下,里面各个代码都是什么意思?(代码dist-copy中查看) + 是一个自执行函数 ```js // 只有单个js文件的打包后的结构(dist-copy/main-1.js) (function(modules){ // 接受一个modules模块对象。对象的key是模块路径 // webpackBootstrap webpack的启动函数,这段代码可以直接在浏览器端运行 })({ /** * 这里传入的是modules。是一个对象 * key是模块路径 * value是一个function。方法里面有一个eval语句,语句里面执行的代码就是key路径对应的代码片段,也就是所谓的chunk */ "./src/index.js": function() { eval("webpack编译path模块的代码片段===chunk") } }) ``` ```js // 在单个文件的基础上,又引入了一个a.js文件的情况下(dist-copy/main-1.js) // webpack启动器函数没有变化 // 传入的modules参数,多了一个key/value("./src/a.js") ``` 把"./src/index.js"后面的eval函数拿出来 ```js // 原来的内容 import { str } from "./a"; console.info("hello webpack"); // webpack编译后的chunk ============================================ __webpack_require__.r(__webpack_exports__); var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/a.js"); // 没有用import,而是使用__webpack_require__函数接受到一个路径 console.info(\"hello webpack\"); //# sourceURL=webpack:///./src/index.js ``` 那么问题来了 ````js __webpack_require__和__webpack_exports__函数从哪里来呢? 是从接受的function中来的 "./src/index.js": (function(module, __webpack_exports__, __webpack_require__) { "use strict"; eval(""); }) 在eval中就可以根据参数链向上找,最后就会到webpack启动函数中找到 ```` #### 编译理解 ```js npx webpack 执行打包任务 1.确认入口模块 编译模块(把该模块编译成对象的代码片段) 分析依赖(找到依赖模块,以及路径) 编译依赖模块(编译成代码片段) ...递归方式处理 2.非js 非json模块的时候 这些模块的编译工作交给loader处理,loaer处理后生成的代码片段再交给webpack 3.webpack为什么给人感觉有很多的冗余代码,不纯粹 你的代码结构无论是简单还是复杂,我的启动函数都在那里,你有或者不用。不是你来决定的 ``` ```js bundle:就是webpack打包后产出的文件,bundle文件 module模块:webpack基于nodeJS,万物皆模块 chunk:代码片段 chunks:是同个组,多个模块的代码片段(也有可能这个组只有一个模块) 1个bundle对应1个chunks 1个chunks至少包含1个module ``` +++ #### 问答 ```js 问:npm源、淘宝源安装依赖是否会出现有区别情况 答:有区别;淘宝源同步npm源有一定的时间差 方案:可以创建 .npmrc 文件,直接限制死指向的源 registry= https://registry.npmjs.org/ 就可以指定使用npm源 ``` ```javascript 问:什么是浏览器集合? 答:通过一段描述告知项目兼容浏览器的版本和类型;是一个package.json的配置(一般在最底下) "browserslist": [ "last 2 versions", // 支持所有浏览器最近的两个大版本。IE最新是IE11,所以支持IE11、IE10 "> 1%", // 支持市场占有率大于1%的浏览器版本 "not ie < 11" // 排除ie11以下 ] browserslist作用:声明一段浏览器集合,用到该集合的工具(babel,postcss),会根据集合描述,输出兼容性的代码 ``` #### browserslist 浏览器集合(来源:can i use) ##### 作用 + 声明一段浏览器集合,用到该集合的工具(babel,postcss),会根据集合描述,输出兼容性的代码 ##### 如何使用 + package.json ```javascript // 正常放到json的底部 // 取并集 "browserslist": [ "last 2 versions", // 支持所有浏览器最近的两个大版本。IE最新是IE11,所以支持IE11、IE10 "> 1%", // 支持市场占有率大于1%的浏览器版本 "not ie < 11", // 排除ie11以下 "chrome > 50", // chrome 版本大于50 "ie 6-8", ] ``` + 创建browserslist工具的配置文件:.browserslistrc ``` last 2 versions < 1% ``` ##### 如何检查集合描述 ```javascript npx browserslist "last 2 versions, >1%" // 执行完成后可以看到所有被支持的浏览器 ```