【转】阮一峰 Webpack 教程

原文地址:https://blog.csdn.net/userkang/article/details/83504048
说明:跟着原文学习做的学习笔记(稍有改动,推荐打开原文看完整版)

写在开头

此Webpack教程是阮老师在Webpack1.x的版本上做的。现在Webpack的版本已经改动较大,建议有基础的同学,就直接上官网看最新的就好了。这个教程可以用来了解下 Webpack的前世今生。
仓库地址: https://github.com/userkang/webpack-demos-cn

开始

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

如何使用
首先,全局安装 Webpack 和 webpack-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

Demo01: Entry file

入口文件是 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

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

// 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

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

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.png和big.png这两张图片转成了不同的两种格式。
<img src="data:image/png;base64,iVBOR...uQmCC">
<img src="4853ca667a2b8b8844eb2693ac1b2578.png">

Demo06: CSS Module (source)
css-loader?modules 的 CSS 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
            }
          }
        ]
      }
    ]
  }
}

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

Demo07: UglifyJs Plugin

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

这个例子将讲讲怎么载入第三方的插件。
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'
    })
  ]
}

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

Demo09: Environment flags

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

//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",
},
// ...
}

启动服务。

Demo10: Code splitting

对于一个大型 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.html 和 webpack.config.js 上了。

//index.html
<html>
  <body>
    <script src="bundle.js"></script>
  </body>
</html>
//webpack.config.js
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
}

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

Demo11: Code splitting with bundle-loader

另一种进行代码分离的方法是用 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.js 到 bundle.js 中, a.js 到 0.bundle.js 中。

Demo12: Common chunk

当多个 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.jsx 和 main2.jsx 的公共部分。也就是包含了 react 和 react-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)
    })
  ]
}

Demo12: Common chunk

当多个 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.jsx 和 main2.jsx 的公共部分。也就是包含了 react 和 react-dom。

Demo13: Vendor chunk

你也可以用 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

如果你想要用到一些全局变量,但是又不想在打包的时候,把它打包到 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)

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

 

posted @ 2019-08-01 11:51  花影疏帘  阅读(1057)  评论(0)    收藏  举报