webpack新手指南

145 阅读5分钟

为什么了解webpack

如今,cli工具(create-react-app,vue-cli)提供很多的默认的配置, 但是作为一名初学者,了解它是如工作还是很有必要的。 本文将介绍webpack可以做什么,以及如何去配置来满足我们的日常开发需求。

什么是webpack

作为一名前端开发者,大家对模块(module)可能并不陌生

webpack是一个模块绑定器,对webpack来说模块是:

  • ES2015 import 语句
  • CommonJS require() 语句
  • AMD definerequire 语句
  • css/sass/less 文件中的 @import 语句。
  • stylesheet url(...) 或者 HTML <img src=...> 文件中的图片链接。 webpack 的最终目标是将所有这些模块类型统一起来,从而将所有内容导入JavaScript代码,并最生成可以运行的代码。

entry(入口)

webpack入口点是收集前端项目所有依赖的起点,是一个javascript文件。 webpack默认入口是src/index.js ,当然也可以有多个入口点。

output(输出)

生成javascript和静态文件的地方

即使可以存在多个 entry 起点,但只能指定一个 output 配置。

loader

loader用于对模块源代码进行转换,可以将文件从不同的语言转换成JavaScript 或将内联图像转换为 data URL,甚至允许你直接在 JavaScript 模块中 import CSS文件!

plugin

插件是第三方扩展,可以更改webpack的工作方式。例如,有一些用于提取HTML,CSS或设置环境变量的插件

mode

webpack提供两种环境development(开发环境)、production(生产环境),区别是生产环境 生成一些优化后的代。

module.exports = {
  mode: 'production'
};

如果没有设置,webpack 会将 mode 的默认值设置为 production

Code splitting

为了避免生成较大的包,我们可以使用代码拆分延迟加载

被拆分的一段代码称为chunk

webpack入门

创建一个新文件夹,进入文件夹,初始化项目

mkdir webpack
cd webpack
npm init -y

安装 webpack,webpack-cli和 webpack-dev-server

yarn add --dev webpack webpack-cli webpack-dev-server

运行webpack, package.json 配置如下命令即可:

  "scripts": {
    "dev": "webpack --mode development"
  },

第一步

运行 yarn dev

运行后看到如下报错

ERROR in Entry module not found: Error: Can't resolve './src' in 'E:\webpack'

因为webpack没有找到默认入口点 src/index.js,所以创建该文件夹

创建后再次运行yarn dev报错消失,并且生成dist目录

dist
└── main.js

这是我们的第一个webpack包,也称为output。

配置webpack

对于复杂的项目可以配置webpack.config.js

创建webpack.config.js文件 文件中使用 Common JS导出方式

module.exports = {
	....
}

在webpack.config.js中,我们可以通过添加或修改来改变webpack的行为方式

修改入口的路径

const path = require('path')
module.exports = {
    entry: {
        index: path.resolve(__dirname, "source", "index.js")
    }
}

如上:webpack将在source/index.js中查找要加载的文件。更改输出的路径

const path = require('path')
module.exports = {
    output: {
        path: path.resolve(__dirname, "build")
    }
}

如上:webpack将会把最终生成的包放在build目录中而不是默认dist目录

打包HTML(HtmlWebpackPlugin)

安装

yarn add --dev html-webpack-plugin

配置

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

module.exports = {
    plugins: [
        new HtmlWebpackPlugin({
            template: path.resolve(__dirname, "src", "index.html")
        })
    ]
};

如上: webpack,从 src/index.html 加载 HTML 模板。

src目录下创建index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
</body>
</html>

webpack development server

安装

yarn add --dev webpack-dev-server

webpack-dev-server 可以让开发更方便,不需要改动了文件就去手动刷新文件。

打开package.json并添加一个 “start” 命令:来配置 webpack-dev-server

"scripts": {
  "dev": "webpack --mode development",
  "start": "webpack-dev-server --mode development --open",
},

运行yarn start 浏览器默认打开,控制台中可以看到script标签,引入main.js

使用 webpack loader

loader 用于对模块的源代码进行转换,可以将文件从不同的语言(如 TypeScript)转换为 JavaScript。使你在 import 或"加载"模块时预处理文件。 loader 配置介绍:

module.exports = {
  module: {
    rules: [
      {
        test: /\.filename$/,
        use: ["loader-b", "loader-a"]
      }
    ]
  },
  //
};

以module 关键字开始。在module内,rules内配置每个加载程序组或单个加载程序。

对于我们想要作为模块处理的每个文件,我们用test和use配置一个对象

test 告诉 webpack “将此文件名视为一个模块”。use 定义将哪些 loaders 应用于些打包的文件。

打包css

安装loader

  • css-loader:解析 css 代码中的 url、@import语法像import和require一样去处理css里面引入的模块
  • style-loader:帮我们直接将css-loader解析后的内容挂载到html页面当中
yarn add --dev css-loader style-loader

webpack.config.js配置

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

创建style.css文件,src/style.css

h1{
    color: #f60;
}

src/index.html创建h1标签

重新run start效果如下:

Webpack Loader 顺序

Webpack Loader的执行顺序是从右到左执行

打包sass

安装loader

  • sass-loader:加载 SASS / SCSS 文件并将其编译为 CSS
  • css-loader:解析 css 代码中的 url、@import语法像import和require一样去处理css里面引入的模块
  • style-loader:帮我们直接将css-loader解析后的内容挂载到html页面当中
yarn add --dev css-loader style-loader sass-loader sass

webpack.config.js中配置

 {
        test: /\.scss$/,
        use: ["style-loader", "css-loader", "sass-loader"]
 }

创建style.scss文件

$color:#f60;
body{
  background: $color;
  color: white;
}

index.js中引入style.scss

重新```yarn start````效果如下

打包JavaScrip(babel loader)

webpack 使用babel loader转换JavaScript代码

babel是一个JavaScript编译器和“编译器”。可以将现代JS(es6, es7...)转换为可以在(几乎)任何浏览器中运行的兼容代码。

  • babel-core :把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理
  • babel-preset-env:将现代 JS 编译为ES5
  • **babel-loader **:用于 webpack 安装
yarn add --dev @babel/core babel-loader @babel/preset-env

新建babel.config.json配置babel

{
  "presets": [
    "@babel/preset-env"
  ]
}

配置webpack.config.js

 {
   test: /\.js$/,
   exclude: /node_modules/,
   use: ["babel-loader"]
}

为了测试转换 在src/index.js中写js代码

const a = 1
const xxx = () => {
    return a
}
xxx()

运行yan dev打开dist/main.js搜索xxx

\n\nconsole.log('hi');\nvar a = 1;\n\nvar xxx = function xxx() {\n  return a;\n};\n\nxxx();\n\n//# sourceURL=webpack:///./src/index.js?");

如上:转译后代码

\n\r\nconsole.log('hi')\r\nconst a=1\r\nconst xxx=()=>{\r\n    return a\r\n}\r\nxxx()\r\n\n\n//# sourceURL=webpack:///./src/index.js?");

如上:未转译的代码

Webpack 中使用 JS 的模块

webpack 虽然将整个文件视为模块。但是,它的主要目的:加载ES模块。

webpack 使用 ES module ,首先创建 src/common/usersAPI.js 文件:

const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";

export function getUsers() {
  return fetch(ENDPOINT)
    .then(response => {
      if (!response.ok) throw Error(response.statusText);
      return response.json();
    })
    .then(json => json);
}

src/index.js中,引入上面的模块:

import { getUsers } from "./common/usersAPI";
getUsers().then(json => console.log(json));

Code splitting(代码拆分)

目的

  • 避免出现一个很大的 bundle

  • 避免重复的依赖关系 在 webpack 中激活 code splitting 三种方法:

  • 有多个入口点(适用于较小的项目)

  • 使用 optimization.splitChunks 选项

  • 动态导入

Code splitting 与 optimization.splitChunks

考虑一个使用Moment.js 的 JS 应用程序,Moment.js是流行的时间和日期JS库。

安装

yarn add momont

清除src/index.js的内容,并引入 moment 库:

import moment from "moment";

运行yarn build 查看控制台信息 整个 moment 库都绑定到了 main.js,使用optimization.splitChunks,我们可以从主包中移出moment.js

使用时,webpack.config.js 添加 optimization

  optimization: {
        splitChunks: { chunks: "all" }
    },

再次run build

发现多了vendors〜main.js,主入口的大小小了很多

Code splitting 与 动态导入

Code splitting使用动态导入来有条件地加载代码

Code splitting 可用于:

  • 模块级别
  • 路由级别

可以有条件地加载一些 JavaScript 模块,来响应用户的交互(例如单击或鼠标移动)。或者,可以在响应路由更改时加载代码的相关部分。

src/index.html代码如下

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Dynamic imports</title>
</head>

<body>
    <button id="btn">加载</button>
</body>

</html>

新建src/common/userAPI.js文件内容

const ENDPOINT = "https://jsonplaceholder.typicode.com/users/";

export function getUsers() {
    return fetch(ENDPOINT)
        .then(response => {
            if (!response.ok) throw Error(response.statusText);
            return response.json();
        })
        .then(json => json);
}

src/index.js中静态导入src/common/userAPI.js

import { getUsers } from "./common/usersAPI";

const btn = document.getElementById("btn");

btn.addEventListener("click", () => {
    getUsers().then(json => console.log(json));
});

src/index.js中动态导入src/common/userAPI.js const getUserModule = () => import("./common/usersAPI")动态加载

const getUserModule = () => import("./common/usersAPI");

const btn = document.getElementById("btn");

btn.addEventListener("click", () => {
    getUserModule().then(({ getUsers }) => {
        getUsers().then(json => console.log(json));
    });
});

运行yarn start 控制台加载js包 点击加载 对应的 chunk 是 0.js

说明

本文大部分内容来源于这里,作为一个初学者,觉得很受用就自己理了一遍。