webpack 五个核心概念
1. Entry
(资料图片)
入口(Entry)指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。
2. Output
输出(Output)指示 webpack 打包后的资源 bundles 输出到哪里去,以及如何命名。
3. Loader
webpack 只能理解 JavaScript 和 JSON 文件,这是 webpack 开箱可用的自带能力。loader 让webpack 能够去处理其他类型的文件,并将它们转换为有效 模块,以供应用程序使用,以及被添加到依赖图中。
4. Plugins
插件(Plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
5. Mode
模式(Mode)指示 webpack 使用相应模式的配置。
下面就来给大家详细介绍一下webpack这五个核心概念。
1、入口(Entry)
entry 对象是用于 webpack 查找启动并构建 bundle。entry 是应用程序的起点入口,从这个起点开始,应用程序启动执行。如果传递一个数组,那么数组的每一项都会执行。入口起点(entry point) 指示 webpack 应该使用哪个模块,来作为构建其内部依赖图(dependency graph) 的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
简单规则:每个 HTML 页面都有一个入口起点。单页应用(SPA):一个入口起点,多页应用(MPA):多个入口起点。
默认值是 ./src/index.js
,但你可以通过在 webpack configuration 中配置 entry
属性,来指定一个(或多个)不同的入口起点。例如:
//单入口--字符串module.exports = { entry: "./path/to/my/entry/file.js",};//多入口--数组module.exports = { entry: ["./src/index.js", "./src/add.js"]};//多入口--对象module.exports = { entry: { home: "./home.js", about: "./about.js", contact: "./contact.js" }};
entry的值类型:
字符串:单入口,打包形成一个chunk,最终只会输出一个bundle文件,chunk 的名称默认是 main
数组:多入口,所有的入口文件最终也只会形成一个chunk,最终输出一个 bundle 文件,chunk 的名称默认为 main。一般只用在 HMR 功能中让html热更新生效
对象:多入口,有多少个 key 就会形成多少个chunk,也就输出多少个 bundle 文件,每个键(key)会是 chunk 的名称。在对象类型中,每个key的值还可以是一个数组,不仅仅是一个字符串
2、输出(output)
output
指示 webpack 如何去输出、以及在哪里输出你的bundle、asset 和其他你所打包或使用 webpack 载入的任何内容。输出的 bundle 的默认值是 ./dist/main.js
,其他生成文件默认放置在 ./dist
文件夹中。
你可以通过在配置中指定一个 output
字段,来配置这些处理过程:
//webpack.config.jsconst path = require("path");module.exports = { entry: "./path/to/my/entry/file.js", output: { path: path.resolve(__dirname, "dist"), filename: "my-first-webpack.bundle.js", },};
我们可以通过 output.filename
和 output.path
属性,来告诉 webpack bundle 的名称,以及 bundle 生成到哪里。
2.1、output.filename(文件名和目录)
此选项决定了每个输出 bundle 的目录和名称。这些 bundle 将写入到 output.path
选项指定的目录下。
对于单个入口
起点,filename 会是一个静态名称。然而,当通过多个入口起点(entry point)、代码拆分(code splitting)或各种插件(plugin)创建多个 bundle,应该使用其他方法来让每个 bundle 都有一个唯一的名称。
//单入口时:module.exports = { //... output: { filename: "js/bundle.js" }};//多入口--使用入口名称:module.exports = { //... output: { filename: "[name].bundle.js" }};//多入口--使用每次构建过程中,唯一的 hash 生成module.exports = { //... output: { filename: "[name].[hash].bundle.js" }};...
2.2、output.path(文件目录)
output.path 指定所有输出文件的目录,即将来所有资源输出的公共目录。path 必须是绝对路径。
module.exports = { //... output: { path: path.resolve(__dirname, "dist/assets") }};
2.3、output.publicPath(引用资源的路径前缀)
publicPath 指定的是 html 文件中的所有资源引入的公共路径前缀。它并不会对生成文件的路径造成影响,而是在 html 文件引入各种资源时,将 publicPath 作为前缀加到引入资源的路径前面。
实例:
在 vue-cli 生成的 webpack 配置中,生产环境下 publicPath 的值默认是 "/",即当前目录的根目录。
打包过后,我们打开 html 文件,可以看到 html 文件中引入的资源路径为:
可以看到,都在路径前面加了 / 符号。当我们打开浏览器访问生成的 html 文件时,会发现报错,资源访问不到,报404,此时资源的访问类似如下:
在服务器上可能会是如下,但访问一样可能会有问题。
我们可以将 publicPath 修改为相对路径,或者直接把它注释掉也行。
2.3.1、path和publicPath的区别
path 指定的是打包后文件在硬盘中的存储位置,是webpack所有文件的输出的路径,必须是绝对路径。比如:输出的js、图片,HtmlWebpackPlugin生成的html文件等,都会存放在以path为基础的目录下。
publicPath 并不会对生成文件的路径造成影响,主要是对你的页面里面引入的资源的路径做对应的补全。2.4、output.chunkFilename(非入口chunk的名称)
output.chunkFilename 决定了非入口(non-entry) chunk 文件的名称。也就是除了入口文件生成的chunk外,其他文件生成的chunk文件命名。
module.exports = { //... output: { chunkFilename: "js/[name]_chunk.js" //非入口chunk的名称 }};
3、loader
webpack 本身只能打包 JavaScript 和 JSON 文件(webpack3+和webpack2+
内置可处理JSON文件,但webpack1+并不支持,
需要引入json-loader
),这是 webpack 开箱可用的自带能力。webpack 本身不支持打包其他类型文件,比如 css、vue 等,但我们可以通过各种 loader 来让 webpack 去处理这些类型的文件。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL,loader 甚至允许你直接在 JavaScript 模块中 import
CSS文件!
通过使用不同的loader
,webpack
有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换 scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件。对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。
在 webpack 的配置中,loader 有两个属性:
test
属性,识别出哪些文件会被转换。
use
属性,定义出在进行转换时,应该使用哪个 loader。
include/exclude(可选):
手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)
query(可选)
:为loaders提供额外的设置选项
//示例:webpack.config.jsconst path = require("path");module.exports = { output: { filename: "my-first-webpack.bundle.js", }, module: { rules: [ { test: /\.txt$/, loader: "raw-loader" }, { test: /\.css$/, use: ["style-loader", "css-loader"] } //使用多个loader的话应该用 use ], },};
以上配置中,对一个单独的 module 对象定义了 rules
属性,里面包含两个必须属性:test
和 use
。这相当于告诉 webpack 编译器在碰到 require()
/import
语句中被解析为 ".txt" 的路径时,在对它打包之前,先使用raw-loader
转换一下。
使用多个loader的话应该用 use,use 数组中的 loader 执行顺序:从右到左,依次执行。比如上面的 css 文件,首先 css-loader 会将 css 文件编译成 JS 加载到 JS文件中,然后再由 style-loader 创建 style 标签,将 JS 中的样式资源插入到 head 标签中。
3.1、CSS-loader
webpack提供两个工具处理样式表,css-loader
和 style-loader
,二者处理的任务不同。css-loader
使你能够使用类似import
的方法来引入 css 文件,style-loader
将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中,由此就可以在JS文件中引入css文件了。
//安装npm install --save-dev style-loader css-loader //css-loader版本太高编译可能会出错,建议降低版本比如 css-loader@1.0.1 可用
//使用module.exports = { ... module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /\.css$/, //对同一个文件引入多个loader的方法。loader的作用顺序是后面的loader先开始作用 use: [ { loader: "style-loader" }, { loader: "css-loader" } ] } ] }};
假设有一个 main.css 文件:
body { backgroud: green;}
为了让webpack能找到”main.css“文件,我们把它导入”main.js “中,如下:
//main.jsimport React from "react";import {render} from "react-dom";import Greeter from "./Greeter";import "./main.css";//使用require导入css文件 render(, document.getElementById("root"));
通常情况下,css会和js打包到同一个文件中,并不会打包为一个单独的css文件。不过通过合适的配置webpack也可以把css打包为单独的文件的。
4、插件(plugin)
loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务,包括:打包优化、压缩、资源管理、注入环境变量等。插件目的在于解决 loader 无法实现的其他事。
要使用某个插件,我们需要通过npm
安装它,然后在 plugins 属性下添加该插件的一个实例。由于插件可以携带参数/选项,你必须在 webpack 配置中,向 plugins
属性传入 new
实例。多数插件可以通过选项自定义,你也可以在一个配置文件中因为不同目的而多次使用同一个插件。
//webpack.config.jsconst HtmlWebpackPlugin = require("html-webpack-plugin"); // 通过 npm 安装const webpack = require("webpack"); // 用于访问内置插件module.exports = { module: { rules: [{ test: /\.txt$/, use: "raw-loader" }], }, plugins: [new HtmlWebpackPlugin({ template: "./src/index.html" })],};
在上面的示例中,html-webpack-plugin
为应用程序生成一个 HTML 文件,并自动注入所有生成的 bundle。
4.1、BannerPlugin插件(添加版权说明)
下面我们添加了一个给打包后代码添加版权声明的插件。该插件是webpack中的内置插件不用安装。
const webpack = require("webpack");module.exports = {... module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true } }, { loader: "postcss-loader" } ] } ] }, plugins: [ new webpack.BannerPlugin("wenxuehai版权所有,翻版必究") ],};
4.2、Hot Module Replacement 插件(热加载)
Hot Module Replacement
(HMR)是webpack里很有用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。热加载和webpack-dev-server不同,热替换在应用运行时,无需刷新页面,便能查看代码更新后的效果 ,就跟直接在浏览器上修改dom样式一样,而webpack-dev-server是要刷新页面的。
(1)在webpack配置文件中添加HMR插件;
(2)在Webpack Dev Server中添加“hot”参数;
4.2.1、react实现热加载
React模块可以使用Babel实现功能热加载。Babel有一个叫做react-transform-hrm
的插件,可以在不对React模块进行额外的配置的前提下让HMR正常工作;
安装react-transform-hmr
npm install --save-dev babel-plugin-react-transform react-transform-hmr
const webpack = require("webpack");module.exports = { entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件 output: { path: __dirname + "/public", filename: "bundle.js" }, devtool: "eval-source-map", devServer: { contentBase: "./public",//本地服务器所加载的页面所在的目录 historyApiFallback: true,//不跳转 inline: true, hot: true }, module: { rules: [ { test: /(\.jsx|\.js)$/, use: { loader: "babel-loader" }, exclude: /node_modules/ }, { test: /\.css$/, use: [ { loader: "style-loader" }, { loader: "css-loader", options: { modules: true } }, { loader: "postcss-loader" } ] } ] }, plugins: [ new webpack.BannerPlugin("版权所有,翻版必究"), new webpack.HotModuleReplacementPlugin() //热加载插件 ],};
配置Babel
// .babelrc{ "presets": ["react", "env"], "env": { "development": { "plugins": [["react-transform", { "transforms": [{ "transform": "react-transform-hmr", "imports": ["react"], "locals": ["module"] }] }]] } }}
//Greeter,jsimport React, { Component} from "react"import styles from "./main.css" class Greeter extends Component { render() { return ( < div>aaaf