# Webpack 文件打包器
官网:https://webpack.docschina.org/guides/getting-started/#basic-setup
修改了配置就要重启
- 1. 创建一个项目 (根目录创建 index.html, src 文件夹及里边的 index.js)
- 2. 把项目变成 node 项目
npm init -y - 3. 安装 webpack
npm install webpack webpack-cli --save-dev - 4. 安装 lodash
npm i lodash - 5. 将 index.html 移动到根目录 dist 文件夹下
- 5. 在命令行执行
npx webpack: 将 src 下的 index.js 编译打包到根目录 dist 文件夹 下的 main.js - 6. 只要修改了 src 文件夹下的 index.js 就执行
npx webpack - 7. 在根目录新建一个文件
webpack.config.js, 去文档的指南->起步->使用一个配置文件中复制 - 8. 借助 npm 的脚本字段 简化我们的打包命令 package.json 中 scripts 字段中的 build 命令
"build": "webpack --config webpack.config.js"
- 9. 管理资源
默认 webpack 可以自动将 js 模块 编译并打包 src 文件夹 下 css 文件夹中的 index.css
但其他的资源 比如 css img less sass…… 并不能直接编译打包- 安装
npm install --save-dev css-loader和npm install --save-dev style-loader - 在 webpack.config.js 中添加模块
module: { rules: [ { test: /\.css$/i, // 匹配资源类型 use: ["style-loader", "css-loader"], // 使用哪些 loader 编译,loader 的编译是有顺序的,写在后面的优先执行 }, ], },
- 安装
# 示例:
-
dist 文件夹下 index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <script src="./main.js"></script> </body> </html> -
src 文件夹下 css 文件夹下 index.css
body { background-color: #ccc; } -
src 文件夹下 index.js
// import _ from "lodash"; const join = require("lodash/join"); // 这里是按需引入 import "./css/index.css"; // 要加.css const arr = ["hello", "world", "cxh"]; const str = join(arr, " "); console.log(str); -
src 文件夹下 webpack.confing.js
const path = require("path"); module.exports = { entry: "./src/index.js", // 编译文件的入口文件 output: { filename: "main.js", // 文件名 path: path.resolve(__dirname, "dist"), // 文件路径 }, // 编译打包之后的出口设置 // module 各种模块编译规则 // 哪些模块需要使用哪些 loader 去编译 module: { rules: [ { test: /\.css$/i, // 匹配资源类型 use: ["style-loader", "css-loader"], // 使用哪些 loader 编译,loader 的编译是有顺序的,写在后面的优先执行 }, ], }, }; -
package.json
{ "name": "webpack-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.config.js" // 看这里 }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^5.2.6", "style-loader": "^2.0.0", "webpack": "^5.37.1", "webpack-cli": "^4.7.0" }, "dependencies": { "lodash": "^4.17.21" } }
# 模块:管理资源
file-loader 加载 images 资源
在中文文档中 LOADER 中查找
v5 版本已废弃:请考虑向 asset modules 迁移。
file-loader 将一个文件中的 import/require () 解析为 url,并且将文件发送到输出文件夹。
-
命令行
npm install file-loader --save-dev -
在 webpack.config.js 中添加代码
module.exports = { module: { rules: [ { test: /\.(png|jpe?g|gif)$/i, use: [ { loader: "file-loader", }, ], }, ], }, }; -
案例
但是 file-loader 已经被 webpack5 废弃了,所以使用了
asset/resource
使用npm uni file-loader卸载 file-loader-
webpack.config.js
const path = require("path"); module.exports = { entry: "./src/index.js", // 编译文件的入口文件 output: { filename: "main.js", // 文件名 path: path.resolve(__dirname, "dist"), // 文件路径 }, // 编译打包之后的出口设置 // module 各种模块编译规则 // 哪些模块需要使用哪些 loader 去编译 module: { rules: [ { test: /\.css$/i, // 匹配资源类型 use: ["style-loader", "css-loader"], }, /* { test: /\.(png|jpe?g|gif)$/i, use: [{ loader: "file-loader" }], }, */ { test: /\.(png|svg|jpg|jpeg|gif)$/i, // type 是 webpack5 新增的资源模块类型 // 用来替代 webpack4 之前使用的一些 loader,比如 file-loader 等 // 这里就不用下载 file-loader 了 type: "asset/resource", }, ], }, }; -
src 文件夹下 index.js
import pic from "./images/dog.png"; const img = document.createElement("img"); img.setAttribute("src", pic); document.querySelector("#app").appendChild(img); -
index.css
body { background-image: url("../images/dog.png"); }
-
# 插件:HtmlWebpackPlugin
下载 npm install --save-dev html-webpack-plugin
该插件的作用是根据模板 自动创建 html 并自动引入编译之后的所有文件
-
案例
-
webpack.config.js
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); // 看这里 module.exports = { entry: "./src/index.js", // 编译文件的入口文件 output: { filename: "main.js", // 文件名 path: path.resolve(__dirname, "dist"), // 文件路径 }, // 编译打包之后的出口设置 // module 各种模块编译规则 // 哪些模块需要使用哪些 loader 去编译 module: { rules: [ { test: /\.css$/i, // 匹配资源类型 use: ["style-loader", "css-loader"], }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, // type 是 webpack5 新增的资源模块类型 // 用来替代 webpack4 之前使用的一些 loader,比如 file-loader 等 // 这里就不用下载 file-loader 了 type: "asset/resource", }, ], }, // 插件 看这里 plugins: [ // HtmlWebpackPlugin // 该插件的作用是根据模板 自动创建html 并自动引入编译之后的所有文件 // new HtmlWebpackPlugin // 会在dist创建一个新的index.html new HtmlWebpackPlugin({ title: "管理输出", // 因为 html 内我们想要设置一些必要的内容,所以使用该插件提供的 template 模板功能 template: "./public/index.html", // 默认的html模板路径,以这个文件为模板 }), ], };
-
# 自动清理 /dist 文件夹
在 output 中加一个 clean:true 会自动清理 /dist 文件夹,清理完之后把最新的留下
clean:true 是 webpack5 的 配置
# 开发模式 & SourceMap & 开发工具 webpack-dev-server
-
案例
-
webpack.config.js
开发模式会将错误和警告具体的显示出来
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { mode: "development", //环境模式:development开发模式 // 开发模式下的 source-map devtool: "eval-source-map", // 使用一个开发工具,将编译后的项目(./dist)启动到服务器上 devServer: { contentBase: "./dist", hot: true, // 启用热模块替换,局部更新无需刷新整个页面 port: 9000, //端口号 // compress: true, //是否压缩 clientLogLevel: "none", //客户端log打印等级,console.log()中默认输出的东西 historyApiFallback: true, //将该服务器下的所有网址都全部指向根目录下的 index.html // 在 history 模式下一般用historyApiFallback这个配置,否则可能会出现404页面 }, entry: "./src/index.js", // 编译文件的入口文件 output: { filename: "main.js", // 文件名 path: path.resolve(__dirname, "dist"), // 文件路径 clean: true, // clean 配置是 webpack5 的配置 // 会自动清理,清理完之后把最新的留下 }, // 编译打包之后的出口设置 }; -
package.json
webpack-dev-server
在 scripts 字段下创建了 serve,创建服务器并启动
npm run serve启动{ "name": "webpack-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.config.js", "serve": "webpack serve --open --config webpack.config.js" // 如果是 webpack.config.js 则直接写 webpack serve --open }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "css-loader": "^5.2.6", "html-webpack-plugin": "^5.3.1", "style-loader": "^2.0.0", "webpack": "^5.37.1", "webpack-cli": "^4.7.0", "webpack-dev-server": "^3.11.2" }, "dependencies": { "lodash": "^4.17.21" } }
-
# 按 Vue 做
npm i vue
-
main.js
// 入口文件 import Vue from "vue"; Vue.config.productionTip = false; // 关闭浏览器的生产信息提示 import "./assets/global.css"; // import App from "./App"; import App from "@/App"; // 别名方式 import "./funComponents/title"; import "@babel/polyfill"; // process.env.NODE_ENV 是自带的一个环境变量,环境变量的值取决于你的环境的 mode 的设置, // 如果设置了就是对应的值,如果没有设置的话,需要在启动命令加上 --node-env producation // 我们可以根据这个值知道项目处于什么环境下, // 我们以前使用这个值对 vue-cli 的 publicPath 配置进行了修改 // console.log(process.env.NODE_ENV); //development // 需要安装 vue-loader 否则解析不了 // 1.npm install -D vue-loader vue-template-compiler // 2. 将https://vue-loader.vuejs.org/guide/#manual-setup中webpack Configuration的配置复制粘贴 new Vue({ // render 只能 return vnode(vue节点,creatElment创建的和组件) // render: (h) => h("h1", "hello world"), render: (h) => h(App), }).$mount("#app"); // 入口(编译谁) entry // 出口(编译到哪) output // 模块解析规则 module // 插件 plugins // 解析 resolve // 优化 optimization // 环境模式 mode // 环境下的资源映射工具(源码和编译之后的代码) devtool // 开发时启动的服务器 devserver -
package.json
{ "name": "webpack-demo", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack --config webpack.prod.js", "serve": "webpack serve --open --config webpack.dev.js --env goal=local" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@babel/core": "^7.14.3", "@babel/plugin-transform-runtime": "^7.14.3", "@babel/preset-env": "^7.14.4", "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", "autoprefixer": "^10.2.6", "babel-loader": "^8.2.2", "css-loader": "^5.2.6", "css-minimizer-webpack-plugin": "^3.0.0", "html-webpack-plugin": "^5.3.1", "less": "^4.1.1", "less-loader": "^9.0.0", "mini-css-extract-plugin": "^1.6.0", "postcss": "^8.3.0", "postcss-loader": "^5.3.0", "postcss-preset-env": "^6.7.0", "style-loader": "^2.0.0", "vue-loader": "^15.9.7", "vue-template-compiler": "^2.6.12", "webpack": "^5.37.1", "webpack-cli": "^4.7.0", "webpack-dev-server": "^3.11.2", "webpack-merge": "^5.7.3" }, "dependencies": { "@babel/polyfill": "^7.12.1", "@babel/runtime": "^7.14.0", "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", "axios": "^0.21.1", "lodash": "^4.17.21", "vue": "^2.6.12" }, "browserslist": ["> 1%", "last 2 versions", "ie >=10"] } -
webpack.dev.js
// const path = require("path"); // const HtmlWebpackPlugin = require("html-webpack-plugin"); // const { VueLoaderPlugin } = require("vue-loader"); // 因为将公共的配置放到了 webpack.common.js 中,所以需要导入公共配置合并到自己的配置中 const { merge } = require("webpack-merge"); const common = require("./webpack.common.js"); const path = require("path"); module.exports = (env) => { // 在 packages.json 的 scripts 字段的 serve 中 --env 后边就是设置的环境变量 // 获取设置的环境变量 goal,输出在控制台 {WEBPACK_SERVE: true, goal:"local"} console.log(env); return merge(common, { output: { filename: "js/main.js", // 文件名 path: path.resolve(__dirname, "dist"), // 文件路径 clean: true, // clean 配置是 webpack5 的配置 // 会自动清理,清理完之后把最新的留下 publicPath: "/", }, // 编译打包之后的出口设置 mode: "development", //环境模式:development开发模式 // 开发模式下的 source-map devtool: "eval-source-map", // 使用一个开发工具,将编译后的项目(./dist)启动到服务器上 devServer: { contentBase: "./dist", hot: true, // 启用热模块替换,局部更新无需刷新整个页面 port: 9000, //端口号 // compress: true, //是否压缩 clientLogLevel: "none", //客户端打印等级,console.log()中默认输出的东西 historyApiFallback: true, //将该服务器下的所有网址都全部指向根目录下的 index.html }, // @babel/polyfill帮助我们加载一些环境中没有的东西 // entry: ["@babel/polyfill", "./src/main.js"], // 编译文件的入口文件 // output: { // filename: "main.js", // 文件名 // path: path.resolve(__dirname, "dist"), // 文件路径 // clean: true, // clean 配置是 webpack5 的配置 // // 会自动清理,清理完之后把最新的留下 // }, // 编译打包之后的出口设置 // module 各种模块编译规则 // 哪些模块需要使用哪些 loader 去编译 module: { rules: [ { test: /\.css$/i, // 匹配资源类型 use: ["style-loader", "css-loader", "postcss-loader"], }, /* { test: /\.(png|jpe?g|gif)$/i, use: [{ loader: "file-loader" }], }, */ // { // test: /\.(png|svg|jpg|jpeg|gif)$/i, // // type 是 webpack5 新增的资源模块类型 // // 用来替代 webpack4 之前使用的一些 loader,比如 file-loader 等 // // 这里就不用下载 file-loader 了 // type: "asset/resource", // }, // vue-loader 加载器 // 可以导入 .vue 后缀的模块 // { // test: /\.vue$/, // loader: "vue-loader", // }, // /* 安装 npm i --save @babel/runtime // npm i --save-dev @babel/plugin-transform-runtime // npm i --save @babel/polyfill // */ // { // test: /\.m?js$/, // exclude: /node_modules/, // use: { // loader: "babel-loader", // // 这个是 babel 的配置,可以使用 .babelrc 或者使用 babel.config.json 进行默认配置,也可以使用下面的 option 配置 // // 如果想用其他的文件当作配置文件需要设置(暂时不处理) // // options: { // // presets: ["@babel/preset-env"], // // }, // // babel 的配置 // // 1. preset 预设 一般选择 @babel/preset-env 编译大部分 js // // 2. targets 浏览器设置,需要到 package.json 中的 browerslist 中设置,不配置该属性的话 ie 无法访问 // // 3. plugins babel的插件。比如:babel-runtime 解析 generator语法 // // 还有一个插件解析 vue 的 jsx 语法 @vue/babel-plugin-transform-vue-jsx // // 4. 当 ie 打开的时候找不到 promise,因为 ie 本身不支持 promise,需要使用 @babel/polyfill 包做兼容 // // 针对 vue 项目 babel 的设置 // // npm install @vue/babel-plugin-transform-vue-jsx --save-dev // // npm install @vue/babel-helper-vue-jsx-merge-props --save // // babel.config.json中的 "plugins": 需要添加["@vue/babel-plugin-transform-vue-jsx"] // }, // }, { test: /\.less$/i, use: [ // compiles Less to CSS "style-loader", "css-loader", "postcss-loader", "less-loader", ], }, ], }, // // 模块解析的配置 // resolve: { // // require 和 import 导入模块的时候可以使用别名方式导入 // // 将 src 目录添加了一个别名 @ // // 以后在项目内就可以使用 @ 替代 src 了 // // webpack 配置-解析-resolve.alias // alias: { // "@": [path.resolve(__dirname, "src/")], // }, // // require 和 import 导入模块的时候省略后缀名 // // webpack 配置-解析-resolve.extensions // extensions: [".js", ".vue"], // }, // // 插件 // plugins: [ // // HtmlWebpackPlugin // // 该插件的作用是根据模板 自动创建html 并自动引入编译之后的所有文件 // // new HtmlWebpackPlugin // 会在dist创建一个新的index.html // new HtmlWebpackPlugin({ // title: "管理输出", // // 因为 html 内我们想要设置一些必要的内容,所以使用该插件提供的 template 模板功能 // template: "./public/index.html", // }), // // VueLoaderPlugin // // vue-loader 加载器插件,插件默认要使用 vue-template-compiler 包,编译 vue 模板语法 // new VueLoaderPlugin(), // ], }); }; -
webpack.common.js
// 公共配置文件 const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { VueLoaderPlugin } = require("vue-loader"); module.exports = { // @babel/polyfill帮助我们加载一些环境中没有的东西 entry: ["@babel/polyfill", "./src/main.js"], // 编译文件的入口文件 // output: { // filename: "js/main.js", // 文件名 // path: path.resolve(__dirname, "dist"), // 文件路径 // clean: true, // clean 配置是 webpack5 的配置 // // 会自动清理,清理完之后把最新的留下 // publicPath: "/", // }, // 编译打包之后的出口设置 // module 各种模块编译规则 // 哪些模块需要使用哪些 loader 去编译 module: { rules: [ // { // test: /\.css$/i, // 匹配资源类型 // use: ["style-loader", "css-loader", "postcss-loader"], // }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, // type 是 webpack5 新增的资源模块类型 // 用来替代 webpack4 之前使用的一些 loader,比如 file-loader 等 // 这里就不用下载 file-loader 了 type: "asset/resource", }, // vue-loader 加载器 // 可以导入 .vue 后缀的模块 { test: /\.vue$/, loader: "vue-loader", }, /* 安装 npm i --save @babel/runtime npm i --save-dev @babel/plugin-transform-runtime npm i --save @babel/polyfill */ { test: /\.m?js$/, exclude: /node_modules/, use: { loader: "babel-loader", }, }, // { // test: /\.less$/i, // use: [ // // compiles Less to CSS // "style-loader", // "css-loader", // "postcss-loader", // "less-loader", // ], // }, ], }, // 模块解析的配置 resolve: { // require 和 import 导入模块的时候可以使用别名方式导入 // 将 src 目录添加了一个别名 @ // 以后在项目内就可以使用 @ 替代 src 了 // webpack 配置-解析-resolve.alias alias: { "@": [path.resolve(__dirname, "src/")], }, // require 和 import 导入模块的时候省略后缀名 // webpack 配置-解析-resolve.extensions extensions: [".js", ".vue"], }, // 插件 plugins: [ // HtmlWebpackPlugin // 该插件的作用是根据模板 自动创建html 并自动引入编译之后的所有文件 // new HtmlWebpackPlugin // 会在dist创建一个新的index.html new HtmlWebpackPlugin({ title: "管理输出", // 因为 html 内我们想要设置一些必要的内容,所以使用该插件提供的 template 模板功能 template: "./public/index.html", }), // VueLoaderPlugin // vue-loader 加载器插件,插件默认要使用 vue-template-compiler 包,编译 vue 模板语法 new VueLoaderPlugin(), ], }; -
webpack.prod.js
const { merge } = require("webpack-merge"); const common = require("./webpack.common.js"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const path = require("path"); // 生产环境的单独配置 module.exports = merge(common, { output: { filename: "js/main.js", // 文件名 path: path.resolve(__dirname, "dist"), // 文件路径 clean: true, // clean 配置是 webpack5 的配置 // 会自动清理,清理完之后把最新的留下 // 相对于服务器的路径,用户根据需求更改 // publicPath: "/dist", }, // 编译打包之后的出口设置 mode: "production", devtool: "source-map", module: { rules: [ { test: /\.css$/i, // 匹配资源类型 // MiniCssExtractPlugin.loader 压缩 css 插件,可以实现 css 分离 use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"], }, { test: /\.less$/i, use: [ // compiles Less to CSS MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "less-loader", ], }, ], }, plugins: [ // 压缩 css 插件 new MiniCssExtractPlugin({ // 文件名 [name] 默认的 入口文件名 filename: "css/[name].css", chunkFilename: "[id].css", }), ], // webpack 的优化配置 optimization: { // minimizer 压缩配置 minimizer: [ // 启用默认压缩配置 "...", // 启用 css 压缩配置 new CssMinimizerPlugin(), ], }, }); -
babel.config.json
{ "presets": ["@babel/preset-env"], "plugins": [ "@babel/plugin-transform-runtime", "@vue/babel-plugin-transform-vue-jsx" ] } -
postcss.config.js
// postcss 的配置文件 module.exports = { plugins: [ [ "postcss-preset-env", // postcss-preset-env 是一个 postcss 预设,也就是里面内置了一些处理 css 方案 // 比如自动添加浏览器厂商前缀 autoprefixer ], ], }; -
App.vue
<template> <div class="app"> <h1>毛茸茸的 Webpack</h1> <!-- <Count :count.sync="count" /> --> <Count v-model="count" /> <div class="titles"> <title :level="1">我是h1</title> <title :level="1">我是另一个h1</title> </div> </div> </template> <script> import Count from "./components/Count.vue"; export default { name: "App", components: { Count }, data() { return { count: 10, }; }, }; </script> -
title.js
// 标题组件 import Vue from "vue"; // 全局 js 组件 Vue.component("Title", { render(h) { // return h("h" + this.level, this.$slots.default); // jsx 语法 webpack 默认是解析不了的 // npm install --save-dev @babel/preset-env 安装 babel/present-env const tag = "h" + this.level; return <tag>{this.$slots.default}</tag>; }, props: { level: { type: Number, }, }, }); -
count.vue
<template> <div> <!-- <input type="text" :value="value" /> --> <input type="text" v-model.number="count" /> <button @click="handleClick">按钮</button> </div> </template> <script> import axios from "axios"; export default { name: "Count", props: { // count: { // type: Number, // }, value: { type: Number, }, }, methods: { async handleClick() { // this.$emit("update:count", 100); const res = await axios.get( "http://jsonplaceholder.typicode.com/posts" ); console.log(res.data); this.$emit("input", 100); }, }, computed: { count: { get() { return this.value; }, set(newValue) { this.$emit("input", newValue); }, }, }, }; </script>