阮一峰 Webpack 教程

写在开头:

此 Webpack 教程是阮老师在 Webpack 1.x 的版本上做的。现在 Webpack 的版本已经改动较大,建议有基础的同学,就直接上官网看最新的就好了。这个教程可以用来了解下 Webpack 的前世今生。

仓库地址: https://github.com/userkang/webpack-demos-cn

开始

这个项目是一些简单的 Webpack 示例集合

这些示例特意用简单明了的方式编写,你将会发现跟着这些例子学习这个强大的工具并非难事。

如何使用

首先,全局安装 Webpackwebpack-dev-server

$ npm i -g webpack webpack-dev-server

然后,克隆这个仓库

$ git clone https://github.com/userkang/webpack-demos-cn.git

安装依赖

$ cd webpack-demos
$ npm install

现在,去项目 demo* 目录下开始源码体验之旅吧

$ cd demo01
$ npm run dev

如果上面的命令没有自动打开浏览器,你可能需要自己在浏览器访问
http://127.0.0.1:8080

前言:什么是 Webpack

Webpack 是为浏览器构建 JavaScirpt 模块脚本的前端工具

它可以像 Browserify 一样使用,并且更加强大

$ browserify main.js > bundle.js
# 相当于
$ webpack main.js bundle.js

Webpack 需要一个 webpack.config.js 的配置文件,它只是一个 CommonJs 模块

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
}

有了这个文件之后,你可以不带参数的调用 Webpack

$ webpack

你需要了解一些命令行选项

  • webpack – 开发环境构建
  • webpack -p – 生产环境构建(压缩混淆脚本)
  • webpack --watch – 监听变动并自动打包
  • webpack -d – 生成 map 映射文件
  • webpack --colors – 构建过程带颜色输出

你可能需要像下面这样在 package.json 中自定义一些脚本 scripts

// package.json
{
  // ...
  "scripts": {
    "dev": "webpack-dev-server --devtool eval --progress --colors",
    "deploy": "NODE_ENV=production webpack -p"
  },
  // ...
}

目录

  1. Entry file
  2. Multiple entry files
  3. Babel-loader
  4. CSS-loader
  5. Image loader
  6. CSS Module
  7. UglifyJs Plugin
  8. HTML Webpack Plugin and Open Browser Webpack Plugin
  9. Environment flags
  10. Code splitting
  11. Code splitting with bundle-loader
  12. Common chunk
  13. Vendor chunk
  14. Exposing Global Variables
  15. React router

Demo01: Entry file (source)

入口文件是 Webpack 进行读取构建 bundle.js 文件的一个文件

例如, main.js 就是一个入口文件.

// main.js
document.write('<h1>Hello World</h1>')

index.html

<html>
  <body>
    <script type="text/javascript" src="bundle.js"></script>
  </body>
</html>

Webpack 遵循 webpack.config.js 来构建 bundle.js.

// webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
}

启动服务,访问 http://127.0.0.1:8080 .

$ cd demo01
$ npm run dev

Demo02: Multiple entry files (source)

多个入口文件也是可以的。在多页面 应用中,每个页面拥有不同的入口文件,用这个就非常管用了。

// main1.js
document.write('<h1>Hello World</h1>')

// main2.js
document.write('<h2>Hello Webpack</h2>')

index.html

<html>
  <body>
    <script src="bundle1.js"></script>
    <script src="bundle2.js"></script>
  </body>
</html>

webpack.config.js

module.exports = {
  entry: {
    bundle1: './main1.js',
    bundle2: './main2.js'
  },
  output: {
    filename: '[name].js'
  }
}

Demo03: Babel-loader (source)

Loaders 是一种预处理器,它可以在 Webpack 编译之前把你应用中的静态资源进行转换 (更多信息)。

举个例子, Babel-loader 可以在 Webpack 编译这些 JS 文件之前,先将 JSX/ES6 语法的文件转换成普通 ES5 语法的文件。Webpack 官网可以查看目前支持的 loaders

main.jsx 是一个 JSX 文件.

// main.jsx
const React = require('react')
const ReactDOM = require('react-dom')

ReactDOM.render(<h1>Hello, world!</h1>, document.querySelector('#wrapper'))

index.html

<html>
  <body>
    <div id="wrapper"></div>
    <script src="bundle.js"></script>
  </body>
</html>

webpack.config.js

module.exports = {
  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }
      }
    ]
  }
}

上边的代码段用到了 babel-loader, 它需要 Babel 的预设插件 babel-preset-es2015 and babel-preset-react 来转义 ES6 和 React。

Demo04: CSS-loader (source)

Webpack 允许在 JS 文件里包含 CSS,并通过 CSS-loader来预处理 CSS 文件。

main.js

require('./app.css')

app.css

body {
  background-color: blue;
}

index.html

<html>
  <head>
    <script type="text/javascript" src="bundle.js"></script>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>

webpack.config.js

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  }
}

注意,你必须用到两个 loaders 去转换 CSS 文件。一个是 CSS-loader 来读取 CSS 文件,另一个是 Style-loader 用来把 <style> 标签插入 HTML 页面。

接下来,让我们启动服务。

$ cd demo04
$ npm run dev

事实上,Webpack 会将一个内联样式表插入到 index.html

<head>
  <script type="text/javascript" src="bundle.js"></script>
  <style type="text/css">
    body {
      background-color: blue;
    }
  </style>
</head>

Demo05: Image loader (source)

Webpack 可以在 JS 文件中包含图片。

main.js

var img1 = document.createElement('img')
img1.src = require('./small.png')
document.body.appendChild(img1)

var img2 = document.createElement('img')
img2.src = require('./big.png')
document.body.appendChild(img2)

index.html

<html>
  <body>
    <script type="text/javascript" src="bundle.js"></script>
  </body>
</html>

webpack.config.js

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.(png|jpg)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192
            }
          }
        ]
      }
    ]
  }
}

url-loader
将图片文件转成 img 标签。如果图片的大小小于 8192 字节,将会被转成 base64 的格式,否则还事会转成一条普通的链接。

启动服务就可以看到,small.pngbig.png 这两张图片转成了不同的两种格式。

<img src="data:image/png;base64,iVBOR...uQmCC">
<img src="4853ca667a2b8b8844eb2693ac1b2578.png">

Demo06: CSS Module (source)

css-loader?modulesCSS Module 功能可以给 JS 模块的 CSS 设置一个局部作用域。你可以用 :global(selector) (更多) 关掉它,使样式变成全局的。

index.html

<html>
<body>
  <h1 class="h1">Hello World</h1>
  <h2 class="h2">Hello Webpack</h2>
  <div id="example"></div>
  <script src="./bundle.js"></script>
</body>
</html>

app.css

/* local scope */
.h1 {
  color: red;
}

/* global scope */
:global(.h2) {
  color: blue;
}

main.jsx

var React = require('react')
var ReactDOM = require('react-dom')
var style = require('./app.css')

ReactDOM.render(
  <div>
    <h1 className={style.h1}>Hello World</h1>
    <h2 className="h2">Hello Webpack</h2>
  </div>,
  document.getElementById('example')
)

webpack.config.js

module.exports = {
  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }
      },
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader'
          },
          {
            loader: 'css-loader',
            // 开启 CSS Module 功能
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  }
}

启动服务。

$ cd demo06
$ npm run dev

访问 http://127.0.0.1:8080, 你将看到 h1 是红色的,因为它的样式是局部作用域,h2 是蓝色的,因为它的作用域是全局的。

Demo07: UglifyJs Plugin (source)

Webpack 用一套插件系统扩展了它的功能。比如,UglifyJs Plugin 就是其中的一个流行的插件,它可以压缩混淆输出的 js 代码。

main.js

var longVariableName = 'Hello'
longVariableName += ' World'
document.write('<h1>' + longVariableName + '</h1>')

index.html

<html>
<body>
  <script src="bundle.js"></script>
</body>
</html>

webpack.config.js

var webpack = require('webpack')
var UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  // 使用插件
  plugins: [new UglifyJsPlugin()]
}

启动服务,将会看到 main.js 被压缩成了下面的样式

var o = 'Hello'
;(o += ' World'), document.write('<h1>' + o + '</h1>')

Demo08: HTML Webpack Plugin and Open Browser Webpack Plugin (source)

This demo shows you how to load 3rd-party plugins.

这个例子将讲讲怎么载入第三方的插件。

html-webpack-plugin 将会
为你创建一个 index.html 文件。 open-browser-webpack-plugin 可以在 Webpack 编译完成后打开一个新窗口。

main.js

document.write('<h1>Hello World</h1>')

webpack.config.js

var HtmlwebpackPlugin = require('html-webpack-plugin')
var OpenBrowserPlugin = require('open-browser-webpack-plugin')

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Webpack-demos',
      filename: 'index.html'
    }),
    new OpenBrowserPlugin({
      url: 'http://localhost:8080'
    })
  ]
}

启动服务。

$ cd demo08
$ npm run dev

现在你不仅不需要手动的写 index.html 文件,而且也不用手动的打开浏览器了。Webpack 都帮你完成了。

Demo09: Environment flags (source)

你可以用环境变量来区分开发环境。

main.js

document.write('<h1>Hello World</h1>')

if (__DEV__) {
  document.write(new Date())
}

index.html

<html>
<body>
  <script src="bundle.js"></script>
</body>
</html>

webpack.config.js

var webpack = require('webpack')

// DefinePlugin 允许创建一个在编译时可以配置的全局常量
var devFlagPlugin = new webpack.DefinePlugin({
  __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
})

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [devFlagPlugin]
}

现在打开 demo09/package.json,你需要在 scripts 字段下添加如下的代码,向 webpack 传递环境变量。

// package.json
{
  // ...
  "scripts": {
    "dev": "cross-env DEBUG=true webpack-dev-server --open",
  },
  // ...
}

启动服务。

$ cd demo09
$ npm run dev

Demo10: Code splitting (source)

对于一个大型 app,把所有代码塞到一个文件可能不是很好维护。Webpack 可以把一个庞大的 JS 文件拆分成几块。尤其,有些代码只有用的时候才引入可以按需加载。

Webpack 用 require.ensure 来定义一个代码点。

// main.js
require.ensure(['./a'], function(require) {
  var content = require('./a')
  document.open()
  document.write('<h1>' + content + '</h1>')
  document.close()
})

require.ensure 告诉 Webpack ./a.js 应该从 bundle.js 中拆分出来,单独生成一个文件。

// a.js
module.exports = 'Hello World'

现在 Webpack 来负责依赖、输出和运行时需要的一些东西。你不必在投入过多的精力在 index.htmlwebpack.config.js 上了。

<html>
  <body>
    <script src="bundle.js"></script>
  </body>
</html>

webpack.config.js

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
}

启动服务。

$ cd demo10
$ npm run dev

表面上,你不会感到什么变化。然而,Webpack 已经把 main.jsa.js 构建成两个不同的文件(bundle.js and 0.bundle.js),并且按依赖关系依次引入。

Demo11: Code splitting with bundle-loader (source)

另一种进行代码分离的方法是用 bundle-loader

// main.js

// 现在 a.js 被引入,它将会被打包到另一个文件
var load = require('bundle-loader!./a.js')

// 为了等待 a.js 中的模块变得可用
// 你可能需要使用 async wait
load(function(file) {
  document.open()
  document.write('<h1>' + file + '</h1>')
  document.close()
})

require('bundle-loader!./a.js') 告诉 Webpack 从另一个包里加载 a.js

现在 Webpack 将会构建 main.jsbundle.js 中, a.js0.bundle.js 中。

Demo12: Common chunk (source)

当多个 Js 文件有共同的依赖,我们可以 CommonsChunkPlugin 把公共的部分提取出来生成一个文件。这对浏览器缓存和节省带宽是非常有用的。

// main1.jsx
var React = require('react')
var ReactDOM = require('react-dom')

ReactDOM.render(<h1>Hello World</h1>, document.getElementById('a'))

// main2.jsx
var React = require('react')
var ReactDOM = require('react-dom')

ReactDOM.render(<h2>Hello Webpack</h2>, document.getElementById('b'))

index.html

<html>
  <body>
    <div id="a"></div>
    <div id="b"></div>
    <script src="commons.js"></script>
    <script src="bundle1.js"></script>
    <script src="bundle2.js"></script>
  </body>
</html>

上面的 commons.js 就是 main1.jsxmain2.jsx 的公共部分。也就是包含了 reactreact-dom

webpack.config.js

var webpack = require('webpack')

module.exports = {
  entry: {
    bundle1: './main1.jsx',
    bundle2: './main2.jsx'
  },
  output: {
    filename: '[name].js'
  },
  module: {
    rules: [
      {
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }
      }
    ]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'commons',
      // (the commons chunk name)

      filename: 'commons.js'
      // (the filename of the commons chunk)
    })
  ]
}

Demo13: Vendor chunk (source)

你也可以用 CommonsChunkPlugin 提取第三方的 Js 生成一个单独的文件。

main.js

var $ = require('jquery')
$('h1').text('Hello World')

index.html

<html>
  <body>
    <h1></h1>
    <script src="vendor.js"></script>
    <script src="bundle.js"></script>
  </body>
</html>

webpack.config.js

var webpack = require('webpack')

module.exports = {
  entry: {
    app: './main.js',
    vendor: ['jquery']
  },
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      filename: 'vendor.js'
    })
  ]
}

上面的代码,entry.vendor: ['jquery'] 告诉 Webpack jeuery 应该被打包到一个公共 vendor.js 包中去。

如果你想让一个全局的变量在每一个模块可用,比如 $jQuery 不用 require("jquery") 就可以直接用。那么你需要使用 ProvidePlugin (Official doc) 这个插件,它可以自动的载入模块,而不需要到处 import 或者 require。

// main.js
$('h1').text('Hello World')

// webpack.config.js
var webpack = require('webpack')

module.exports = {
  entry: {
    app: './main.js'
  },
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery'
    })
  ]
}

当然,这种情况,你还需要手动的全局的载入 jquery.js

Demo14: Exposing global variables (source)

如果你想要用到一些全局变量,但是又不想在打包的时候,把它打包到 bundle 文件中去。这个时候你可以在 webpack.config.js 中配置 externals 字段。

比如,我们定义了一个 data.js 文件

// data.js
var data = 'Hello World'

index.html

<html>
  <body>
    <script src="data.js"></script>
    <script src="bundle.js"></script>
  </body>
</html>

注意,这里 Webpack 只会打包 bundle.js, 而不会打包 data.js

我们可以 data 字段暴露盛一个全局变量

// webpack.config.js
module.exports = {
  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }
      }
    ]
  },
  externals: {
    // require('data') is external and available
    //  on the global var data
    data: 'data'
  }
}

现在,你可以在脚本中像引入其他模块一样引入 data。但它实际是一个全局变量。

// main.jsx
var data = require('data')
var React = require('react')
var ReactDOM = require('react-dom')

ReactDOM.render(<h1>{data}</h1>, document.body)

你同样可以把 reactreact-dom 添加到 externals 中,这样可以大大缩短编译时间和编译后 bundle.js 文件的大小。

Demo15: React router (source)

这个演示用 webpack 来构建 React-router 的官方例子.

让我们想象一个带有仪表盘、收件箱和日历的小应用。

+---------------------------------------------------------+
| +---------+ +-------+ +--------+                        |
| |Dashboard| | Inbox | |Calendar|      Logged in as Jane |
| +---------+ +-------+ +--------+                        |
+---------------------------------------------------------+
|                                                         |
|                        Dashboard                        |
|                                                         |
|                                                         |
|   +---------------------+    +----------------------+   |
|   |                     |    |                      |   |
|   | +              +    |    +--------->            |   |
|   | |              |    |    |                      |   |
|   | |   +          |    |    +------------->        |   |
|   | |   |    +     |    |    |                      |   |
|   | |   |    |     |    |    |                      |   |
|   +-+---+----+-----+----+    +----------------------+   |
|                                                         |
+---------------------------------------------------------+

webpack.config.js

module.exports = {
  entry: './index.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['es2015', 'react']
          }
        }
      }
    ]
  }
}

index.js

import React from 'react'
import { render } from 'react-dom'
import { BrowserRouter, Switch, Route, Link } from 'react-router-dom'

import './app.css'

class App extends React.Component {
  render() {
    return (
      <div>
        <header>
          <ul>
            <li>
              <Link to="/app">Dashboard</Link>
            </li>
            <li>
              <Link to="/inbox">Inbox</Link>
            </li>
            <li>
              <Link to="/calendar">Calendar</Link>
            </li>
          </ul>
          Logged in as Jane
        </header>
        <main>
          <Switch>
            <Route exact path="/" component={Dashboard} />
            <Route path="/app" component={Dashboard} />
            <Route path="/inbox" component={Inbox} />
            <Route path="/calendar" component={Calendar} />
            <Route path="*" component={Dashboard} />
          </Switch>
        </main>
      </div>
    )
  }
}

class Dashboard extends React.Component {
  render() {
    return (
      <div>
        <p>Dashboard</p>
      </div>
    )
  }
}

class Inbox extends React.Component {
  render() {
    return (
      <div>
        <p>Inbox</p>
      </div>
    )
  }
}

class Calendar extends React.Component {
  render() {
    return (
      <div>
        <p>Calendar</p>
      </div>
    )
  }
}

render(
  <BrowserRouter>
    <Route path="/" component={App} />
  </BrowserRouter>,
  document.querySelector('#app')
)

index.html

<html>
  <body>
    <div id="app"></div>
    <script src="/bundle.js"></script>
  </body>
</htmL>

启动服务。

$ cd demo15
$ npm run dev

相关链接

证书

MIT

  • 14
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
webpack教程是一篇关于使用webpack进行前端项目构建的文章。它包含了webpack的安装、配置、加载器和插件的使用等内容。在教程中,作者介绍了webpack与其他构建工具(如grunt和gulp)的区别,并详细说明了webpack的概念和基本用法。教程还提供了一些示例代码和实践步骤,帮助读者更好地理解和应用webpack。其中,引用\[1\]提供了关于webpack的基本命令和配置的使用方法,引用\[2\]介绍了webpack的入口、出口、加载器和插件的概念,引用\[3\]则介绍了如何使用插件来提取和加载CSS文件。如果你对webpack感兴趣,这篇教程会给你提供一些有用的指导和实践经验。 #### 引用[.reference_title] - *1* [webpack详细教程](https://blog.csdn.net/weixin_41842236/article/details/103132674)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [webpack基础教程](https://blog.csdn.net/k0101jcj/article/details/106825691)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值