Webpack

  • webpack 指南
  • React 的 Webpack 配置

PS: 文章结尾有完整实例启动方法:Github地址

本次配置练习主要是针对 webpack-v3,切换分支至 webpack@3-react ,每一次 Commits,基本上对应着相应的文件配置,可以对照着瞅瞅。

开始安装

配置 Webpack

准备环境:

➜  happymmall git:(webpack@3-react) ✗ node -vv11.13.0➜  happymmall git:(webpack@3-react) ✗ npm -v6.10.1➜  happymmall git:(webpack@3-react) ✗ git --versiongit version 2.17.2 (Apple Git-113)➜  happymmall git:(webpack@3-react) ✗ yarn -v1.17.3
安装 yarn
# homebrew 安装brew install yarn# npm/cnpm 安装npm install cnpm -gcnpm install yarn -g# 测试是否安装成功➜  ~ yarn --version1.17.3

常用命令:

yarn init  # 初始化yarn add [package]  # 添加依赖包yarn add [package] --dev   # 添加到 devDependenciesyarn remove [package]  # 删除依赖包yarn install   # 安装所有依赖包
安装 webpack@3
yarn inityarn add webpack@3.10.0 --dev

根目录新建 webpack.config.js,并新建 src/index.js

// webpack.config.jsconst path = require("path");module.exports = {  entry: "./src/index.js",  output: {    path: path.resolve(__dirname, "dist"),    filename: "app.js"  }};// index.jsconsole.log("Hello, world");

运行:node_modules/.bin/webpack:

➜  happymmall git:(webpack@3-react) ✗ node_modules/.bin/webpackHash: 62e68b9cc366c7b50ac6Version: webpack 3.10.0Time: 34ms Asset    Size  Chunks             Chunk Namesapp.js  2.5 kB       0  [emitted]  main   [0] ./src/index.js 28 bytes {0} [built]

打开 dist/app.js,搜索 Hello:

/***/ (function(module, exports) {console.log('Hello, world');/***/ })/******/ ]);

一个简单的文件就此打包成功,开心吧...

配置 HMTL 模板

文档:HtmlWebpackPlugin 简化了 HTML 文件的创建,以便为你的 webpack 包提供服务。这对于在文件名中包含每次会随着编译而发生变化哈希的 webpack bundle 尤其有用。

安装 html-webpack-plugin

yarn add html-webpack-plugin@2.30.1 --dev

新建:src/index.html, 并初始化一个 html 内容,然后配置:

const path = require("path");const HtmlWebpackPlugin = require("html-webpack-plugin");module.exports = {  entry: "./src/index.js",  output: {    path: path.resolve(__dirname, "dist"),    filename: "app.js"  },  plugins: [    new HtmlWebpackPlugin({      template: "./src/index.html"    })  ]};

接着跑一下:node_modules/.bin/webpack,不出意外的话,dist 下会出现一个 index.html,打开查看就会发现,不仅有我们自己指定的内容,还把app.js也自动引入了,这波操作够可以。

Babel 处理脚本

文档:babel-loader

安装 babel

yarn add babel-loader@7.1.2 babel-core@6.26.0 babel-preset-env@1.6.1 --dev

设置 config 文件:

module: {  rules: [    {      test: /\.js$/,      exclude: /(node_modules)/, // 忽略此文件      use: {        loader: "babel-loader",        options: {          presets: ["env"]        }      }    }  ];}

然后修改 src/index.js:

class People {  constructor(name) {    this.name = "people";  }}class Man extends People {  constructor(name) {    super(name);    this.age = 27;  }}

运行:node_modules/.bin/webpack,然后再去dist/app.js,就会发现已经转译成功:

var People = function People(name) {  _classCallCheck(this, People);  this.name = "people";};var Man = (function(_People) {  _inherits(Man, _People);  function Man(name) {    _classCallCheck(this, Man);    var _this = _possibleConstructorReturn(      this,      (Man.__proto__ || Object.getPrototypeOf(Man)).call(this, name)    );    _this.age = 27;    return _this;  }  return Man;})(People);

配置 React

安装:

yarn add babel-preset-react@6.24.1 --devyarn add react@16.2.0 react-dom@16.2.0

设置:

module: {  rules: [    {      test: /\.jsx$/,      exclude: /(node_modules)/,      use: {        loader: "babel-loader",        options: {          presets: ["env", "react"]        }      }    }  ];}// 这时候的入口文件也需要更改下测试entry: './src/index.jsx',

然后修改 src/index.jssrc/index.jsx:

import React from "react";import ReactDOM from "react-dom";ReactDOM.render(<h1>Hello, React.</h1>, document.getElementById("root"));

运行:node_modules/.bin/webpack,然后再去dist/app.js,查找如下:

// ..._reactDom2.default.render(  _react2.default.createElement("h1", null, "Hello, React."),  document.getElementById("root"));// ...

那么,表示解析 .jsx 成功。

解析 CSS

安装:

yarn add style-loader@0.19.1 css-loader@0.28.8 --dev

设置:

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

在 index.jsx 里添加:import './style.css';,在style.css添加一些:

body {  color: red;  font-size: 16px;}

接着跑一下:node_modules/.bin/webpack,再接着去 dist 下的 app.js 找找:

// moduleexports.push([module.i, "body {\n  color: red;\n  font-size: 16px;\n}", ""]);

嗯,已经被解析并添加到 app.js 文件中。但是,我们有时候需要大量的 css 文件,并需要 js 解析,那就需要个插件单独处理:ExtractTextWebpackPlugin:

yarn add extract-text-webpack-plugin@3.0.2 --dev

设置:

plugins: [  new HtmlWebpackPlugin({    template: "./src/index.html"  }),  // Add  new ExtractTextWebpackPlugin("styles.css"),],module: {  rules: [    // Add    {      test: /\.css$/,      use: ExtractTextWebpackPlugin.extract({        fallback: "style-loader",        use: "css-loader"      })    }  ]}

然后跑一下,dist 下就会出现我们设置的 styles.css 文件,并且会自动添加至 index.html:

<link href="style.css" rel="stylesheet" />

解析 scss

安装: sass-loader node-sass

yarn add sass-loader@6.0.6 node-sass@4.7.2 --dev

设置:

{  test: /\.scss$/,  use: ExtractTextWebpackPlugin.extract({    fallback: "style-loader",    use: ["css-loader", "sass-loader"]  })}

这时候,我们去 src 下新建一个 app.scss:

body {  h1 {    color: blue;  }}

并且在 index.jsx 中引入:

import "./style.css";import "./app.scss";

接着,跑一下看看,刷~的一声,打开 dist 下的 styles.css,看看是不是我们引入的内容:

body {  color: red;  font-size: 16px;}body h1 {  color: blue;}

测试通过,完美!

处理图片资源

安装:url-loader

yarn add file-loader@1.1.6 url-loader@0.6.2 --dev

设置:

{  test: /\.(png|jpg|gif)$/,  use: [    {      loader: 'url-loader',      options: {        limit: 8192      }    }  ]}

同样的 src 下找张图片,测试下是否通过,在 index.jsx 里添加:

import ImgSrc from "./react.png";ReactDOM.render(  <div>    <h1>Hello, React.</h1>    <img src={ImgSrc} alt="react" />  </div>,  document.getElementById("root"));

然后跑一下,发现:如果图片大于 limit 的设置(8kb=8192/1024kb)就会出现在 dist 目录下,否则会以 base64 格式直接引入使用。

处理 fonts 字体

先下载个Font Awesome试试水:

yarn add font-awesome

设置:

{  test: /\.(woff|woff2|eot|ttf|otf|svg)$/,  use: [    {      loader: 'url-loader',      options: {        limit: 8192      }    }  ]}

然后按照以下几个步骤来试水:

  • 复制 font-awesome/ 目录到你的项目中
  • 只保留 fonts 文件 和 scss 文件里的内容,其他可以删除
  • 最后去你的主文件:app.scss 添加它:
$fa-font-path: "./font-awesome/fonts";@import "./font-awesome/scss/font-awesome.scss";

接着我们跑一下:node_modules/.bin/webpack,dist 下瞬间就会出现一堆文件,表示测试通过。

公共模块

CommonsChunkPlugin插件,是一个可选的用于建立一个独立文件(又称作 chunk)的功能,这个文件包括多个入口 chunk 的公共模块。

先访问缓存中的公共代码,加快浏览器的访问速度。

设置:

const webpack = require("webpack");// ...new webpack.optimize.CommonsChunkPlugin({  name: 'commons', // 公共 chunk 的名称  filename: 'js/base.js'  // 公共 chunk 的文件名}),

使用 webpack-dev-server

webpack-dev-server 为你提供了一个简单的 web server,并且具有 live reloading(实时重新加载) 功能。

安装:

yarn add webpack-dev-server@2.9.7 --dev

修改配置文件,告诉 dev server,从什么位置查找文件:

devServer: {  contentBase: './dist',  port: 8082}

接着去 package.json里添加一个可以直接运行 dev server 的 script:

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

这样就不用每次都去访问 node_modules/.bin/webpack 了,直接启动:

# 启动 dev servernpm start# 打包构建npm run build

清理 /dist 文件夹

需求:每次 build 的时候,发现 /dist 下的文件一直会堆砌,能不能每次构建的时候,只生成项目中真正在使用的文件?

方法:在每次构建前清理 /dist 文件夹,这样只会生成用到的文件。

使用 clean-webpack-plugin 插件来进行清理:

yarn add clean-webpack-plugin --dev

设置:

const { CleanWebpackPlugin } = require("clean-webpack-plugin");new CleanWebpackPlugin();

模块热替换 HMR

需求:在开发环境下,可不可以每次保存之后,不用刷新浏览器,就自动更新了呢?

方法:模块热替换(hot module replacementHMR),只更新改动的文件。

设置:

const webpack = require("webpack");plugins: [  new webpack.HotModuleReplacementPlugin()],devServer: {  contentBase: './dist',  hot: true,  // 开启  port: 8082}

这样,重启服务,然后修改 index.jsx 文件,就会发现自动更新了,妈妈再也不用担心我的 F5 了...

启动 Server

webpack-dev-middleware 是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server

安装:

yarn add webpack-dev-middleware@2.0.6 --devyarn add express --dev

根目录新建一个 server.js:

const express = require("express");const webpack = require("webpack");const webpackDevMiddleware = require("webpack-dev-middleware");const app = express();const config = require("./webpack.config.js");const compiler = webpack(config);// 告诉 express 使用 webpack-dev-middleware,// 以及将 webpack.config.js 配置文件作为基础配置app.use(  webpackDevMiddleware(compiler, {    publicPath: config.output.publicPath  }));// 使用配置文件的端口号app.listen(config.devServer.port, function() {  console.log(`Example app listening on port ${config.devServer.port}!\n`);});

我们将会在 server 脚本使用 publicPath,以确保文件资源能够正确地 serve 在 http://localhost:8082 下:

output: {  path: path.resolve(__dirname, "dist"),  filename: "app.js",  publicPath: '/'},

接着,添加一个 npm script

"server": "node server.js"

跑一下:npm run server,访问 http://localhost:8082/,完美运行。

最后的配置文件

webpack.config.js:

const path = require("path");const webpack = require("webpack");const HtmlWebpackPlugin = require("html-webpack-plugin");const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");const { CleanWebpackPlugin } = require("clean-webpack-plugin");module.exports = {  entry: "./src/index.jsx",  output: {    path: path.resolve(__dirname, "dist"),    filename: "app.js",    publicPath: "/"  },  devServer: {    contentBase: "./dist",    hot: true,    port: 8082  },  plugins: [    new CleanWebpackPlugin(),    new HtmlWebpackPlugin({      template: "./src/index.html"    }),    new ExtractTextWebpackPlugin("styles.css"),    new webpack.optimize.CommonsChunkPlugin({      name: "commons",      filename: "js/base.js"    }),    new webpack.HotModuleReplacementPlugin()  ],  module: {    rules: [      {        test: /\.(js|jsx)$/,        exclude: /(node_modules)/,        use: {          loader: "babel-loader",          options: {            presets: ["env", "react"]          }        }      },      {        test: /\.(sc|c)ss$/,        use: ExtractTextWebpackPlugin.extract({          fallback: "style-loader",          use: ["css-loader", "sass-loader"]        })      },      {        test: /\.(png|jpg|gif)$/,        use: [          {            loader: "url-loader",            options: {              limit: 8192            }          }        ]      },      {        test: /\.(woff|woff2|eot|ttf|otf|svg)$/,        use: [          {            loader: "url-loader",            options: {              limit: 8192            }          }        ]      }    ]  }};

项目目录结构

关于 tree 的使用总结:MacOS 如何使用 tree 生成目录结构

常用命令:

# 下载brew install tree# 显示某个文件夹下的所有文件tree -a# 只显示文件夹tree -d# 显示项目的层级,如三级tree -L 3# 过滤,如除node_modules文件tree -I "node_modules"# 输出tree > tree.md# Helptree --help# 最后,我的输出:tree -I "node_modules" > tree.md

tree.md 的内容为:

.├── README.md├── dist├── package.json├── server.js├── src│   ├── app.scss│   ├── font-awesome│   │   ├── fonts│   │   │   ├── FontAwesome.otf│   │   │   ├── fontawesome-webfont.eot│   │   │   ├── fontawesome-webfont.svg│   │   │   ├── fontawesome-webfont.ttf│   │   │   ├── fontawesome-webfont.woff│   │   │   └── fontawesome-webfont.woff2│   │   └── scss│   │   ├── \_animated.scss│   │   ├── \_bordered-pulled.scss│   │   ├── \_core.scss│   │   ├── \_fixed-width.scss│   │   ├── \_icons.scss│   │   ├── \_larger.scss│   │   ├── \_list.scss│   │   ├── \_mixins.scss│   │   ├── \_path.scss│   │   ├── \_rotated-flipped.scss│   │   ├── \_screen-reader.scss│   │   ├── \_stacked.scss│   │   ├── \_variables.scss│   │   └── font-awesome.scss│   ├── index.html│   ├── index.js│   ├── index.jsx│   ├── react.png│   └── style.css├── tree.md├── webpack.config.js└── yarn.lock5 directories, 32 files

项目启动

项目里的每一次 Commits,基本上对应着相应的文件配置,可以对照着瞅瞅。

克隆一份项目

git clone https://github.com/yangtao2o/happymmall.git

进入目录

cd happymmall

切换分支至 webpack@3-react

➜  happymmall git:(master) git checkout webpack@3-react

下载依赖包

➜  happymmall git:(webpack@3-react) yarn install

启动

➜  happymmall git:(webpack@3-react) npm start