/ 前端

[译] webpack 的 demos

仓库是一些webpack简单的demo集合。

这些demo故意写的简单但调理清楚,你会发现学习这个强大的工具一点都不难

怎么使用

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

$ npm i -g webpack webpack-dev-server

然后克隆本仓库再安装依赖

$ git clone git@github.com:ruanyf/webpack-demos.git
$ cd webpack-demos
$ npm install

现在你可以试着跑demo*目录下面的源码文件了

$ cd demo01
$ webpack-dev-server

在你的浏览器里面访问http://127.0.0.1:8080

前言: 什么是 Webpack

webpack是类似Grunt和Glup的前端构建工具

他可以像Browserify一样打包模块文件,但更强大.

$ browserify main.js > bundle.js
# 类似于
$ webpack main.js bundle.js

webpack.config.js是webpack的配置文件

// webpack.config.js
module.exports = {
  entry: './main.js',  // 入口文件
  output: {
    filename: 'bundle.js'  //输出文件
  }
};

当你创建了 webpack.config.js之后,你就可以不带参数,直接执行Wecpack了

$ webpack

下面这些命令你应该了解下

  • webpack – 在开发时构建一次
  • webpack -p – 再生产环境中构建 (minification微小)
  • webpack --watch – 监听文件改动,持续构建
  • webpack -d – 引用源码的映射
  • webpack --colors – for making things pretty

创建要投入生产的应用,你可以在package.json文件中添加 scripts字段,如下:

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

索引

  1. 入口文件
  2. 多个入口文件
  3. Babel-loader
  4. CSS-loader
  5. Image loader
  6. CSS Module
  7. UglifyJs插件
  8. 环境标记
  9. 代码分离
  10. 使用bundle-loader分离代码
  11. 常见代码块
  12. 第三方代码库
  13. 暴露全局变量
  14. React hot loader
  15. React router

Demo01: 入口文件(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

$ webpack-dev-server

Demo02: 多个入口文件 (source)

webpack支持多个入口文件,因为这对多页面的应用来说很有用。

// 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'   //  name 是 entry 的key值
  }
};

Demo03: Babel-loader (source)

Loaders是转换你app源文件的预处理器。
比如, Babel-loader
可以将 JSX/ES6 的文件转化成JS [译者注:JSX是react.js的语法,es6是最新的ECMAScrit的标准]

main.jsx 是 JSX 文件.

//  这是的代码是  react的知识
// 引入react
var React = require('react');

// react 的render 方法渲染页面
React.render(
  <h1>Hello, world!</h1>,
  document.body
);

index.html

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

webpack.config.js

module.exports = {
  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      // 加载babel-loader ,处理js或者jsx结尾的文件
      { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader' },
    ]
  }
};

webpack.config.js中, module.loaders 字段注册loaders

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: {
    loaders:[
      { test: /\.css$/, loader: 'style-loader!css-loader' },
    ]
  }
};

注意,你需要两个加载器来转换css文件。第一个是CSS-loader读取css文件, 另一个是Style-loader,将style标签插入到html页面。不同加载器由感叹号链接。

启动服务器之后,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文件里面加载image

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: {
    loaders:[
      { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }
    ]
  }
};

url-loader可以转换image文件. 如果图片大小小于 8192字节,如篇会转化成一个base64的Data URL; 否则就转化成普通的URL.你看到的问号标记是用来将参数传到加载器里面去的。

启动服务器之后, small.pngbig.png 将变成以下URL的样子.

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

Demo06: CSS模块 (source)

css-loader?modules (查询参数模块) 可以将CSS 模块规范化。

这就意味着你模块的css默认是局部作用域(译者注:本例中即

内部)的css。你可以通过:global(...)选择将其变成全局作用域下的css。更多信息

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

.h1 {
  color:red;
}

:global(.h2) {
  color: blue;
}

main.jsx

var React = require('react');
var style = require('./app.css');

React.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: {
    loaders:[
      { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader' },
      { test: /\.css$/, loader: 'style-loader!css-loader?modules' }
    ]
  }
};

启动服务器

$ webpack-dev-server

访问 http://127.0.0.1:8080 , 你会发现只有h1, 因为h1的css是局部作用域的,而h2 是蓝色的,因为他的css是全局的作用域。

Demo07: UglifyJs插件 (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>
</boby>
</html>

webpack.config.js

var webpack = require('webpack');
var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new uglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
};

启动服务器之后, main.js 将会被压缩成如下。

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

Demo08: 环境标记 (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');

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]
};

把环境变量传给webpack

$ env DEBUG=true webpack-dev-server

Demo09: 代码分离(source)

对于一个大型大型应用来说,把所有的代码放在一个文件里效率是很低的。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'
  }
};

启动服务器

$ webpack-dev-server

表面上,你会觉得有什么区别,然而,webpack实际上把main.jsa.js 构建成了不同的代码块(bundle.js1.bundle.js),并在有需要的时候从bundle.js加载 1.bundle.js

Demo10: 使用bundle-loader分离代码(source)

也可以使用bundle-loader来分离代码。

// main.js
var load = require('bundle-loader!./a.js');

load(function(file) {
  document.open();
  document.write('<h1>' + file + '</h1>');
  document.close();
});

require('bundle-loader!./a.js') 告诉 Webpack从另一个代码块中去加载a.js

现在webpack会将 main.js 构建成 bundle.jsa.js 构建成 1.bundle.js.

Demo11: 共用代码块(source)

当多个脚本都含有相同代码块的时候,你可以使用CommonsChunkPlugin将相同的部分题取出来,分离成一个文件。

// main1.jsx
var React = require('react');
React.render(
  <h1>Hello World</h1>,
  document.getElementById('a')
);

// main2.jsx
var React = require('react');
React.render(
  <h2>Hello Webpack</h2>,
  document.getElementById('b')
);

index.html


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

webpack.config.js

var CommonsChunkPlugin =require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
  entry: {
    bundle1: './main1.jsx',
    bundle2: './main2.jsx'
  },
  output: {
    filename: '[name].js'
  },
  module: {
    loaders:[
      { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader' },
    ]
  },
  plugins: [
    new CommonsChunkPlugin('init.js')
  ]
}

Demo12: 第三方代码块 (source)

你也可以用CommonsChunkPlugin提取第三方库将其分离成一个文件。
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(/* chunkName= */'vendor', /* filename= */'vendor.js')
  ] 
};

如果你想某个模块在其他模块中作为一个变量调用,比如,不需要require("jquery"),就可以用$和jQuery引用jquery模块,那么你可以试试ProvidePlugin (官方文档).

// 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",
      "window.jQuery": "jquery"
    })
  ]
};

Demo13: 暴露全局变量(source)

如果你想使用全局变量,但并不想让他们包含在webpack打包文件里。你可以在webpack.config.js中配置 externals(官方文档)

比如,我们现在有一个data.js.

var data = 'Hello World';

我们可以将 data暴露成为一个全局变量

// webpack.config.js
module.exports = {
  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      { test: /\.js[x]?$/, exclude: /node_modules/, loader: 'babel-loader' },
    ]
  },
  externals: {
    'data': 'data'
  }
};

现在,你可以在你的脚本中引用data作为一个模块变量,但实际上是一个全局变量。

// main.jsx
var data = require('data');
var React = require('react');

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

Demo14: React hot loader(source)

这个demo复制了React hot boilerplate.

React Hot Loader是一个webpack的插件,他可以让你在编辑react组件时,立即刷新而不会丢失状态。
因为我们使用了全局的 webpack-dev-server,所以为了跑这个demo,我们还要安装一些全局的模块。

$ npm i -g react-hot-loader react babel-loader

然后执行 webpack-dev-server.

$ webpack-dev-server --progress --hot

现在你可以在浏览器中看见hello world。
--hot 选项告诉 webpack-dev-server,当组件源代码改变的时候,不重新加载整个页面的情况下替换该组件。

现在不要关闭服务器,去编辑App.js,把 'Hello World' 修改成 'Hello Webpack',保存,去看一下浏览器发生了什么。

App.js

import React, { Component } from 'react';

export default class App extends Component {
  render() {
    return (
      <h1>Hello World</h1>
    );
  }
}

index.js

import React from 'react';
import App from './App';

React.render(<App />, document.getElementById('root'));

index.html

<html>
  <body>
    <div id='root'></div>
    <script src="/static/bundle.js"></script>
  </body>
</html>

webpack.config.js

var webpack = require('webpack');
var path = require('path');

module.exports = {
  entry: [
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/only-dev-server',
    './index.js'
  ],
  output: {
    filename: 'bundle.js',
    publicPath: '/static/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loaders: ['react-hot-loader', 'babel-loader'],
      include: path.join(__dirname, '.')
    }]
  }
};

Demo15: React router (source)

这个demo是webpack构建React-router的官方实例
让我们来设想一个有dashboard(仪表盘), inbox(收件箱), 和 calendar(日历)的小应用

+---------------------------------------------------------+
| +---------+ +-------+ +--------+                        |
| |Dashboard| | Inbox | |Calendar|      Logged in as Jane |
| +---------+ +-------+ +--------+                        |
+---------------------------------------------------------+
|                                                         |
|                        Dashboard                        |
|                                                         |
|                                                         |
|   +---------------------+    +----------------------+   |
|   |                     |    |                      |   |
|   | +              +    |    +--------->            |   |
|   | |              |    |    |                      |   |
|   | |   +          |    |    +------------->        |   |
|   | |   |    +     |    |    |                      |   |
|   | |   |    |     |    |    |                      |   |
|   +-+---+----+-----+----+    +----------------------+   |
|                                                         |
+---------------------------------------------------------+
$ webpack-dev-server

参考链接

许可证

MIT