Webpack的使用(搭建Vue+React环境)

1,425 阅读17分钟

认识Webpack

Webpack对于目前的前端开发来说具有代表性的模块化打包工具

像常用的三大框架的脚手架,Vue-clicreate-react-appAngular-cli都是基于Webpack来进行打包的,常见的场景有,支持开发者使用ES6+语法Typescriptless样式预处理实时监听代码变更反馈到应用中对最终的开发产出进行压缩打包等等优化处理

以一个Vue项目来说,需要通过Webpack处理的地方就有

  • 将单文件组件(Single File components)编译
  • 如果使用了则需要将Typescript转移成Javscript语法
  • 如开发过程用了ES6+的语法为了兼容性,需要转化成ES5或者ES3等语法代码
  • lessscss等预处理语法转化成css
  • 将转化后的css进行模块化提取加载
  • 对项目中使用到的资源文件进行处理加载(图片,文字等)
  • 打包HTML相关资源文件

平时开发不需要自己从零开始搭建,主流的脚手架已经很方便快捷了,但是学习下webpack的使用还是有助于理解提升的

开始上手

没有安装Node的先安装

首先需要安装webpackwebpack-cli,他们俩的关系属于互相依赖,如果只安装webpack则无法使用

  1. 建一个空项目
  2. yarn init -y创建package.json
  3. yarn add webpack webpack-cli -D
  4. 执行webpack时,默认会在当前目录下的srcindex.js,这个入口配置可以改,但先按要求建
  5. 执行webpack有几种方式
    • ./node_modules/.bin/webpack找到目标程序执行
    • 执行npx webpack,这里简单说一下npxnpm5.2.0新增的一个工具,这行代码执行后,会去当前目录下的node_modules/.bin/webpack找到他并执行,如果没有则会去全局模块下寻找,还没有就会从源下载并创建一个临时目录,当执行完后就会自行删除
    • package.json编写scripts,通过yarnnpm执行
{
  "name": "wp",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "devDependencies": {
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0"
  },
  "scripts": {
    "dev": "webpack"
  }
}

执行后应该能看到打包成功的信息,其中红框的警告会告诉你,没有指定任何mode,会使用值为production,两种模式的打包会有不同优化和混淆,可供使用的有development,production,none,一般开发用development,打包时指定production

构建成功后且可以看到当前目录下多了dist/main.js,这个就是打包后的产出文件,产出目录和入口一样都是可以通过配置去改变

到目前为止已经成功执行了一个"打包行为"

添加配置文件

新建一个webpack.config.js文件,这个名字webpack认识,如果用其他命名,则可以通过webpack --config xxx.config.jscli的参数进行指定,下面修改下产出目录文件的配置

const path = require('path');

module.exports = {
  mode: "production", // 指定mode为production
  entry: "./src/index.js", // 指定打包文件检索的入口,这个就是默认值,可以修改
  output: {
    // 指定打包产出的目录文件,这里还是指定为当前目录的dist文件夹
    // 但是文件名指定成bundle.js
    "filename": "bundle.js",
    path: path.resolve(__dirname, "./dist")
  },
}

修改下script,这里只是为了演示使用,使用这个名字可以不用加

"scripts": {
  "dev": "webpack --config webpack.config.js"
}

再次执行后yarn dev后就会在看到产出更改为dist/bundle.js文件了

这是webpack官网首页图,给定一个入口文件,延展出一张依赖图,并逐个进行打包(需要下文说的loader)最终产出

了解插件

截止到目前,虽然能打包了,但是没法看到效果,一个web应用最起码需要一个html页面,这里可以利用一个插件html-webpack-plugin进行自动生成并载入bundle

所谓插件就是可以在打包过程再进一步做更多的事情,如优化,注入等操作

  1. 安装依赖yarn html-webpack-plugin
  2. 在项目根目录下新建一个index.html模板,内容如下,模板采用ejs语法,感兴趣的可以了解下,配置了插件后,会将title的模板变量替换成配置中的值
<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
  	<link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

插件配置项与entry等同级,省略部分代码,配置项是个插件数组

const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  entry: "./src/main.js",
  // ...
  plugins: [
    new HtmlWebpackPlugin({
      title: "webpack好难",
      template: "index.html"
    })
  ]
}

此时进行构建还会报错,会提示BASE_URL is not defined,原因是模板无法找到这个变量,可以再利用一个webpack的内置插件DefinePlugin去定义BASE_URL

const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");

module.exports = {
  entry: "./src/main.js",
  // ...
  plugins: [
    new DefinePlugin({
      BASE_URL: '"./"'
    }),
    new HtmlWebpackPlugin({
      title: "webpack好难",
      template: "index.html"
    })
  ]
}

再次执行webpack命令,应该能看到产出目录多了一个index.html,查看其内容,可以看到有个script标签把打包后的入口文件自动引入了,link的值也符合预期

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link rel="icon" href="./favicon.ico" />
    <title>webpack好难</title>
    <script defer="defer" src="bundle.js"></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

此时再介绍另一个插件,每次构建时自动清理dist目录,yarn clean-webpack-plugin -D,安装后使用方式只需要只需要往插件数组添加一个对象实例

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { DefinePlugin } = require("webpack");

module.exports = {
  // ...
  plugins: [
    new DefinePlugin({
      BASE_URL: '"./"'
    }),
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      title: "webpack好难",
      template: "index.html"
    })
  ]
}

后续每次构建都会自动清理产出目录

了解Loader

webpack默认只认识javascriptjson文件,如csspngtypescriptjsxvue等等它都不认识,这时候需要一个Loader,去将这些文件转换成模块,并添加到webpack的依赖关系中,接下来解决样式文件和资源文件导入的问题

src目录下新建一个main.css

.red-text{
  color:red;
}

接着在index.js头部中import "./main.css"

import "./main.css";

function addChild() {
  const ele = document.createElement("div");
  ele.textContent = "函数添加的Div";
  ele.classList.add("red-text");
  document.body.appendChild(ele);
}

addChild();

执行构建后,会看到错误输出告诉你,需要一个合适的loader去处理css文件

处理css常用的就是css-loader,添加依赖yarn add css-loader -D,接着在配置文件中添加module.rules,这是一个数组,数组每一项可以指定某类文件使用某种loader进行转换,并可以给loader传入特定的参数配置

  module: {
    rules: [
      {
        test: /.css$/i,
        use: "css-loader",
        // use: { loader: "css-loader" },
        // use: ["css-loader"],
        // use: [{ loader: "css-loader" }],
      }
    ]
  },

test的值一般为正则表达式,这里的意思是匹配所有.css后缀的文件,use则指定对应的loadercss-loader,这里的写法是一种简写,简写下还列出了其他几种写法,loader可以配置多个,会从下往上执行,按需编写,再次执行构建就能看到css文件也成功被webpack转换加载打包成功

不过此时查看打包后的页面,会发现红色文字样式没有生效,原因在于webpack只是解析的样式文件,但没有将内容插入到页面当中,此时还需要一个loader来处理,yarn add style-loader -D,安装style-loader后,进行相关配置,由于loader的执行是从下往上,所以需要先经过css-loader解析,然后再通过style-loader进行注入,顺序不能错

  module: {
    rules: [
      {
        test: /.css$/i,
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" },
        ],
      }
    ]
  },

此时再次执行构建并查看页面,样式已经生效

在开发中,可能还会使用到lessscss等预处理器,使用方式也一样,以less举例,yarn add less less-loader -D,安装依赖后进行对应的loader配置(不能只装loader,还得装less),注意顺序一定是先执行的loader转化结构可以被下一个loader解析

还有一个postcss-loader可以了解一下

这个loader可以自动的添加样式浏览器前缀,同时再配合一个loader的插件postcss-preset-env,可以将一些现代的css转成大多浏览器认识的css,并自动添加前缀

yarn add postcss-loader postcss-preset-env -D,接着添加配置

{
        test: /.css$/i,
        use: [
          { loader: "style-loader" },
          { loader: "css-loader" },
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: ["postcss-preset-env"]
              }
            }
          }
        ],
      },

资源loader

除了样式文件外,常用的还有imgpng等资源文件,首先在网上随便找一张图片,丢到src/images目录下,接着修改index.js增加一个图片引入,实例我找了一张vue的图片

import "./main.css";
import vueImage from "./images/vue.jpg";

function addChilds() {
  const ele = document.createElement("div");
  ele.textContent = "函数添加的Div";
  ele.classList.add("red-text");
  document.body.appendChild(ele);
}

addChilds();

function addImageEle() {
  const imgEle = new Image();
  imgEle.src = vueImage;
  document.body.appendChild(imgEle);
}

addImageEle();

此时直接构建会报上面未添加css-loader时相同的错误,这里可以使用file-loader进行加载,并会将文件输出到打包目录下,yarn add file-loader -D,接着配置资源文件的rule

      {
        test: /.(png|jpe?g|gif)$/i,
        user: {
          loader: "file-loader"
        }
      }

此时执行构建,没有报错且图片也成功输出到页面中

查看dist目录,可以看到有个jpg文件,这个命名是可以通过配置生成的

下面配置一下资源文件的命名规则,通过use.options添加,name配置可以使用一些placeholders,具体作用可以查文档 www.webpackjs.com/loaders/fil…

      {
        test: /.(png|jpe?g|gif|svg)$/i,
        use: {
          loader: "file-loader",
          options: {
            name: "[name].[hash:8].[ext]",
            outputPath: "img"
          }
        }
      }

构建后可以看到,图片被放在了img目录下,并且文件名也按照原文件名+hash值取8位+文件后缀的形式

再介绍另一个 url-loader

file-loader有所不同,这个loader有将资源转换成内联base64的作用

yarn add url-loader -D,看效果只需要把file-loader改成url-loader,配置可复用,再次构建后,dist目录下已经木有

下面手动的设置这个配置,配置key叫做options.limit,单位是byte,小于这个大小的文件会被解析成base64,反之则与file-loader一样处理

      {
        test: /.(png|jpe?g|gif|svg)$/i,
        use: {
          loader: "url-loader",
          options: {
            name: "[name].[hash:8].[ext]",
            outputPath: "img",
            limit: 3 * 1024 // 3kb以下就会被处理成base64
          }
        }
      }

如果大资源也弄成了base64会影响页面请求,需要看时机情况来配置

css中,也可能会使用background-image属性来引入资源文件,在main.css中添加如下属性,并且作用到div上

.bg{
	background-image: url("./images/vue.jpg")
}

构建后查看页面会发现,虽然没有报错,但是图片压根就没显示

此时需要添加两个配置,一个是options下的esModule:false,将引入方式改为非(import xx from "yyy"),第二个是与use同级的type,值为javscript/auto,可以避免重复处理资源

      {
        test: /.(png|jpe?g|gif|svg)$/i,
        use: {
          // loader: "file-loader",
          loader: "url-loader",
          options: {
            name: "[name].[hash:8].[ext]",
            outputPath: "img",
            limit: 3 * 1024,
            esModule: false
          }
        },
        type: "javascript/auto"
      }

更改后构建就能看到css背景图生效

webpack5的asset module type

webpack5开始,官方文档更推荐的一种使用是asset module,用于替代这些loader

Asset Modules type replaces all of these loaders by adding 4 new module types:

  • asset/resource emits a separate file and exports the URL. Previously achievable by using .file-loader
  • asset/inline exports a data URI of the asset. Previously achievable by using .url-loader
  • asset/source exports the source code of the asset. Previously achievable by using .raw-loader
  • asset automatically chooses between exporting a data URI and emitting a separate file. Previously achievable by using with asset size limit.url-loader
  • asset/resource可以看成file-loader的实现
  • asset/inline可以看成url-loader的实现
  • asset/source可以看成raw-loader的实现,这个loader的作用是将资源文件导入为字符串(源码)
  • asset则是在resourceinline之间自动选择

现在已经不需要再使用file-loaderurl-loader了,改写原本的loader如下

      {
        test: /.(png|jpe?g|gif|svg)$/i,
        type: "asset/resource",
        generator: {
          filename: "img/[name].[hash:8].[ext]"
        }
      }

构建后效果完全相同,其中filename还可以不在这里配置,在output中配置assetModuleFilename作用等价,他们可以共存,generator配置优先

	  output: {
            "filename": "bundle.js",
            path: path.resolve(__dirname, "./dist"),
            assetModuleFilename: "img/[name].[hash:8].[ext]"
          },

实现url-loader按指定大小自动选择模式

首先将type改为asset,再添加一个parse.dataUrlCondition配置内联条件,这里的定义为小于3kb的资源才会被处理成base64

      {
        test: /.(png|jpe?g|gif|svg)$/i,
        type: "asset",
        parser: {
          dataUrlCondition: {
            maxSize: 3 * 1024
          }
        }
      }

CopyWebpackPlugin

在使用vue/cli的项目中,放到public的文件会默认的复制到打包目录当中,实际上可以通过CopyWebpackPlugin来实现

yarn add copy-webpack-plugin -D

const CopyWebpackPlugin = require("copy-webpack-plugin");

plugins: [
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "public",
          globOptions: {
            ignore: ["**/index.html"]
          }
        }
      ]
    })
  ]

这里通过一个配置防止public中也有index.html,忽略复制这个文件

了解Babel

babel对于目前的前端开发来说,已经是核心之一,他可以让高阶语法转换成低版本浏览器可兼容代码,以及解析jsx``tsx等特殊扩展语法。实际上babel就是一个编译器,将a语法的代码转换成b语法的代码,以一个app.jsx文件为例,先解析->词法语法转换(AST树)->生成目标代码

babel实际上可单独使用,无非搭配webpack等构建工具

yarn add @babel/core bebel-loader @babel/preset-env -D

@babel/corebabel-loader的依赖,类似于less-loader依赖less@babel/preset-env是一堆es6+语法插件的集成包,只有提供插件才能让babel认识es6+语法

首先,添加loader,其次配置插件有两种方式

第一种,直接在loaderoptions里配置预设插件

      {
        test: /.js?$/i,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["@babel/preset-env"]
          }
        }
      },

另一种是新建一个babel.config.js

module.exports = {
  presets: ["@babel/preset-env"]
}

两种方式选择一种即可

尝试引入Vue并构建

首先安装vueyarn add vue

在使用vue之前,简单了解下vue的不同版本区别,可以定位到当前目录下node_modules/vue/dist下,可以看到不同的vue版本

  • vue(.runtime).global(.prod).js的版本,是通过在全局对象下暴露一个Vue对象,实际上可以通过<script src="xxx.js"></script>的方式进行直接引入使用
  • vue(.runtime).esm-browser(.prod).js的版本,提供了通过ES模块的导入方式使用,<script type="module"></script>
  • vue(.runtime).esm-bundler.js的版本,提供给webpack之类的构建工具使用
  • vue.cjs(.prod).js的版本,提供给服务端渲染,在node中使用require

以上所有带runtime的版本都是指不包含编译器的版本(仅运行时),而不带runtime的都是完整版(运行时+编译器

在打包的入口文件js中编写包含如下代码,前提是html模板里必须有一个idapp的元素

import { createApp } from "vue";

createApp({
  template: "<div>{{ msg }}</div>",
  data() {
    return {
      msg: "test Msg"
    }
  }
}).mount("#app");

此时执行构建,虽然构建过程没有报错,但是打开页面的控制台能看到如下信息

意思是告诉你,提供的vue组件包含了template模板,在import vue from "vue"这个方式默认引入的实际上是一个只提供运行时的版本,没有包含内置的模板编译功能,此时有几种方案

  1. 第一种是按要求更改为包含模板编译的完整版本(体积会更大)
import { createApp } from "vue/dist/vue.esm-bundler";
  1. 第二种是使用render函数进行渲染(编写麻烦)
import { h } from "vue";
// h函数从vue包中导入

createApp({
  // template: "<div>123{{msg}}</div>",
  render() {
    return h("div", {}, this.msg)
  },
  data() {
    return {
      msg: "test msg"
    }
  }
}).mount("#app");
  1. 使用SFC(Single File Component),也就是后缀名为vue的文件,并通过如webpack的构建工具进行模板编译,满足运行时版本即可执行的条件(主要使用这种方式)

新建一个Main.vue文件

<template>
  <div>vue组件</div>
</template>

<script>
  export default {
    setup() {
      return {}
    }
  }
</script>

<style scoped>

</style>

接着引入该组件并挂载

import { createApp } from "vue";
import VueApp from "./vue-components/Main.vue";
  
createApp(VueApp).mount(document.querySelector("#vue-app"));

既然是不认识的文件,那就必需要给webpack配置对应的loaderyarn add vue-loader

{
  	test: /.vue$/i,
        loader: "vue-loader"
}

此时执行构建,会看到以下报错

主要看第二行报错信息,告诉你需要在webpack的配置文件中配置对应的VueLoaderPlugin,而这个插件通过vue-loader包引入即可

const { VueLoaderPlugin } = require("vue-loader/dist/index")

//...省略
plugins: [
  	new VueLoaderPlugin()
]

构建后打开页面,即可看到vue单文件组件成功编译并挂载到页面

但观察控制台,vue3版本会有一个警告

意思是vue3版本暴露了两个特性标记给开发者进行覆盖,不配置的话有默认值会照常工作,但是建议我们手动配置,点击地址链接能跳转到解决警告的方式github.com/vuejs/core/…

其中__VUE_OPTIONS_API__指是否使用Vue的构造选项

__VUE_PROD_DEVTOOLS__指在发布模式下,是否支持devtools开发者工具

webpack的解决方式只需要在DefinePlugin中定义这两个配置即可,这个插件上面配置BASE_URL时已经使用过了,追加键值对即可

  plugins: [
    new DefinePlugin({
      BASE_URL: '"./"',
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false
    }),
  ]

再次构建警告便消失

到这里,Vue框架的引入搭建便已经完成

接着引入React框架

yarn add react react-domreact需要引入两个库

使用react一般有两种方式

  1. 一种是使用类似vue中的render函数编写(自行在index.html模板添加对应的容器div
import React from "react";
import ReactDom from "react-dom";

ReactDom.render(React.createElement("div", {}, ["react组件"]), 
                document.querySelector("#react-app"));

构建编译后查看页面,react的引入也成功并挂载到页面上,由于我没有删除vue相关代码,所以此时这个项目即能编写react,又能编写vue

不过观察控制台会有一个react18版本的警告,意思是这个版本虽然能按照旧版17的样子跑,但不再支持ReactDom.render的方法,要改用createRoot,只需要做如下改动警告便会消失

import { createRoot } from "react-dom/client";
import React from "react";

createRoot(document.querySelector("#react-app"))
  .render(React.createElement("div", {}, ["react组件"]));
  1. 一种是通过js/jsx扩展文件编写,在js中编写类html的代码,但需要特殊的语法解析转义成第一种代码,浏览器才能运行,babel插件便能完成这项工作(这篇文章不是主讲React,对jsx感兴趣的自行了解)
const App = () => {
  return (<div>react App 组件</div>)
}

createRoot(document.querySelector("#react-app"))
  .render(<App />);

此时构建的话会报错,因为webpack不认识这种jsx语法,yarn add @babel/preset-react -D,添加babel的预设插件解析语法转译(记得在webpack配置中配置jsjsxbabel-loader,上文已配置过,此处仅添加插件)

module.exports = {
  presets: ["@babel/preset-react", "@babel/preset-env"]
}

构建后查看页面,采用jsx的写法也生效了

devServer

在上面演示中,每一次处理都是手动的去构建代码,理想的开发环境自然是自动检测代码更改并且编译及刷新页面展示

webpack默认提供了自动编译功能

通过webpack --watch

或者webpack配置文件添加watch:true启动,但是这种方式不会刷新页面,仅仅编译代码

这时候就需要一个webpack-dev-server工具

yarn add webpack-dev-server -D

可以通过 webpack serve,这个cli的入参有依赖,如果没有安装webpack-dev-server则会报错

或者直接手动执行webpack-dev-server启动服务,执行后会启动一个本地服务,并且编译的产出会保存到电脑内存中,不会有对应的产出

package.json中添加一个script:"serve": "webpack serve",执行yarn serve

成功的话便能看到以上输出,访问对应的地址可以访问项目

并且对组件内容进行修改后,页面会自动刷新

模块热替换

也称为HRM(Hot Module Replacement)

在程序运行时,动态的增删改局部模块,不需要刷新整个页面,好处在于如果全局刷新,状态无法保存(操作了一半,改下代码就全部重置了),而HRM则指更新需要更新的模块

可以在webpack.config中通过devServer.hot属性去配置布尔值,不过这个属性默认是开启的,但是需要做额外的处理,否则更改代码后仍然全局刷新

首先讲上述的React组件抽离出去,并在index.js中引入组件,并告知webpack服务对哪些模块进行HMR,在启动了HRM的前提下,module.hot是一个对象,反之则是undefinedindex.js改写以下代码

import ReactApp from "./react-components/App"
// ...省略其他代码
if (module.hot) {
  module.hot.accept("./react-components/App.js", () => {
    console.log("react组件局部更新");
  })
}

createRoot(document.querySelector("#react-app"))
  .render(<ReactApp />);

再次启动服务,并且对react组件进行修改,可以看到页面不再是全局刷新,而是仅仅更新了React组件部分,观察控制台,可以看到每更改保存一次文件,传入的回调函数便会执行一次

简单了解下原理

在启动webpack-dev-server时,实际上启动了两个服务,一个用于获取静态资源服务,一个Socket长连接服务

  • 像页面请求,css请求,js请求属于静态资源服务在处理
  • HMR则是基于Socket长链接实现,只要监听到文件变更,就会生成两个文件,一个js和一个json,通过webSocket通讯主动告知发送给浏览器,浏览器拿到后经过处理对更新的模块进行局部更新

启动服务修改文件后,通过开发者工具可以看到对应的websocket链接信息

服务端主动发送的两个更新文件,这里实时更新了两次,所以会有两份jsjson文件

devServer的可配置项

  • port:配置端口,默认是8080
  • open:配置服务启动后,是否自动打开浏览器访问,默认是false
  • proxy:主要用于解决跨域问题(浏览器和服务器协议,域名,端口任一不一致的安全策略),可以配置代理中间件的规则,值得一提的是,webpack-dev-server使用的资源服务器是基于express框架的,而其中的代理配置则是采用了http-proxy-middleware,简单看看使用
devServer: {
    open: true,
    port: 8888,
    proxy: {
      "/api": {
        target: "http://192.168.8.58:8080",
          changeOrigin: true,
          pathRewrite: {
          	"^/api": ""
          },
      }
  }
},

这段proxy配置的意思是将所有请求资源地址为/api开头的,都使用代理规则,原本代码里axios.get("/api/hi")有这么一段请求代码,当前的环境下,他最终会请求http://localhost:8888/api/hi,而我真实提供api的接口地址为http://localhost:8080/hi

  • 不配置pathRewrite的情况下必然会返回404,那么就需要通过pathRewrite正则匹配将以/api的字符替换为空字符串,那么最终实际代理服务器的请求地址是http://192.168.8.58:8080/hi
  • changeOrigin:默认是false,设置为true会更改掉请求头中的Host字段,需要注意的是如果在浏览器的Network观察是不会有变化的,可以在服务端将请求头打印查看

changeOriginfalse的情况下,服务端拿到的请求头数据为当前站点地址

changeOrigin设置未true后,服务端拿到的请求头数据为Proxy中设置的target

  • secure:默认为true,不支持代理转发到https服务器上,如果要支持可以设置为false
  • compress:是否开启gzip压缩,默认是true,改为false则不压缩,目前为false的情况下,bundle.js大小为2mb

删掉compress或者设为true,当前大小只有457kb,服务端应该尽可能的压缩来提高传输效率

  • historyApiFallback:默认为false,设置为true后,页面访问出现404时会自动将/index.html的内容返回

Resolve 解析

webpack默认支持3种形式的解析

  1. 绝对路径
  2. 相对路径,当前文件的路径会当做工作目录上下文,如使用import导入了一个"../test/a.js",那么最终会解析出当前目录的上层目录的test文件夹的a.js的绝对路径
  3. 模块路径,指定解析模块时应该搜索的目录,如import Vue from "vue",由于resolve.modules默认值是['node_modules'],所以会去依赖目录中找到对应的文件
  4. 解析到是文件,有扩展名则直接打包,没有则使用webpack的resolve.extensions默认值是[".wasm",".mjs",".js",".json"]尝试进行解析
  5. 如果是文件夹,则先根据webpack配置resolve.mainFiles的默认值['index']进行查找对应文件,接着按第四点规则解析

编写时省略文件后缀名

webpack默认可省略的配置是    extensions: [".wasm", ".mjs", ".js", ".json", ".vue"],,这里添加.vue,后续引入vue单文件组件便不再需要写xxx.vue,直接写xxx即可

    extensions: [".wasm", ".mjs", ".js", ".json", ".vue"]

    // 也可以通过"..."获取默认配置项
    extensions: ["...", ".vue"],

alias选项设置别名

配置后项目中可以使用对应key代替配置的路径,可以减少很多层级目录的写法,如有一个较深层的文件想要引用src/utils下的某个文件,原本要写../../../utils/xx.js,现在只需要写@/utils/xx.js,至于配置的层级,怎么方便怎么来

alias:{
  "@":path.resolve(__dirname,"./src")
}

看到这里其实已经可以对webpack有一定的认识,回头看那些vuereact的脚手架相信就不会再陌生

完:)