孙宇的技术专栏 大数据/机器学习

Webpack

2017-04-10

阅读:


Webpack

现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包。为了简化开发的复杂度,前端社区涌现出了很多好的实践方法。

WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。

Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案,不过Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具。

Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。

Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。

安装

Webpack可以使用npm安装,在终端中进入项目文件夹后执行下述指令就可以完成安装。不过先要

在安装之前最好是先初始化一下 npm 配置:

npm init

输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。

这里会帮我们建立一个 package.json,用来记录项目的软件包依赖。如果不初始化 npm 直接使用 install 安装,依赖信息会保存在 package-lock.json 文件中。

//全局安装
npm install -g webpack
npm install -g webpack-cli
//安装到你的项目目录,并更新 npm 配置文件 package.json,
# 且从淘宝镜像安装,速度要更快.推荐该方法
npm install --save-dev webpack --registry=https://registry.npm.taobao.org
npm install --save-dev webpack-cli --registry=https://registry.npm.taobao.org

简单使用

在项目文件夹里创建两个目录 ,app 和 public。app文件夹用来存放原始数据和我们将写的JavaScript 模块,public 文件夹用来存放之后供浏览器读取的文件。

然后创建三个文件:

  1. index.html –放在public文件夹中
  2. slave.js– 放在app文件夹中
  3. main.js– 放在app文件夹中

index.html:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>

slave.js:

module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};

main.js

const greeter = require('./slave.js');
document.querySelector("#root").appendChild(greeter());

使用webpack

在终端中使用,在基本的使用方法如下:

# {extry file}出填写入口文件的路径,本文中就是上述main.js的路径,
# {destination for bundled file}处填写打包文件的存放路径
# 填写路径的时候不用添加{}
webpack {entry file} {destination for bundled file}

指定入口文件后,webpack将自动识别项目所依赖的其它文件,不过需要注意的是如果你的webpack不是全局安装的,那么当你在终端中使用此命令时,需要额外指定其在node_modules中的地址。这里运行:

# webpack非全局安装的情况
node_modules/.bin/webpack app/main.js --output public/bundle.js

Hash: 00f096bf738fce903aa2
Version: webpack 4.3.0
Time: 302ms
Built at: 2018-3-28 16:31:17
    Asset       Size  Chunks             Chunk Names
bundle.js  730 bytes       0  [emitted]  main
Entrypoint main = bundle.js
   [0] ./app/slave.js 143 bytes {0} [built]
   [1] ./app/main.js 95 bytes {0} [built]

WARNING in configuration
The 'mode' option has not been set. Set 'mode' option to 'development' or 'production' to enable defaults for this environment.

这时候,main.js 和 slave.js 被打包到了 bundle.js里,而且进行了压缩。它的内容是:

  1 !function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}    n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.r=function(e){Object.defineProperty(e,"__esMod    ule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Ob    ject.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t){e.exports=function(){var e=document.createElement("div");return e.textContent="Hi t    here and greetings!",e}},function(e,t,n){const r=n(0);document.querySelector("#root").appendChild(r())}]);

具体 webpack 的命令可以用 webpack -h 查看。

使用配置文件

在当前件夹的根目录下新建一个名为 webpack.config.js 的文件,我们在其中写入如下所示的简单配置代码,目前的配置主要涉及到的内容是入口文件路径和打包后文件的存放路径:

module.exports = {
  entry:  __dirname + "/app/main.js",
  output: {
    path: __dirname + "/public",
    filename: "bundle.js"
  }
}

注:__dirname 是node.js中的一个全局变量,它指向当前执行脚本所在的目录

有了这个配置之后,再打包文件,只需在终端里运行webpack(非全局安装需使用node_modules/.bin/webpack)命令就可以了,这条命令会自动引用webpack.config.js 文件中的配置选项。这样就省去了打包时命令行里复杂的配置。

npm运行webpack

在命令行中输入命令需要代码类似于node_modules/.bin/webpack这样的路径其实是比较烦人的,不过值得庆幸的是npm可以引导任务执行,对npm进行配置后可以在命令行中使用简单的npm start命令来替代上面略微繁琐的命令。在package.json中对scripts对象进行相关设置即可,设置方法如下:

{
  "name": "test",
  "version": "1.0.0",
  "description": "test",
  "main": "index.js",
  "scripts": {
    "test": "test",
    "start": "webpack"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/laravel/elixir.git"
  },
  "keywords": [
    "test"
  ],
  "author": "sunyu",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/laravel/elixir/issues"
  },
  "homepage": "https://github.com/laravel/elixir#readme",
  "devDependencies": {
    "webpack": "^4.3.0"
  },
  "dependencies": {
    "gulp": "^3.9.1",
    "webpack-cli": "^2.0.13"
  }
}

注:package.json中的script会按照一定顺序寻找命令对应位置,本地的node_modules/.bin路径就在这个寻找清单中,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了。

npm的start命令是一个特殊的脚本名称,其特殊性表现在,在命令行中使用npm start就可以执行其对应的命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm run {script name}npm run test,这里我们先执行:

sunyu:test sunyu$ npm run start

> test@1.0.0 start /Users/sunyu/Documents/workspace/sunyu/test
> webpack

Hash: 00f096bf738fce903aa2
Version: webpack 4.3.0
Time: 116ms
Built at: 2018-3-28 16:41:57
    Asset       Size  Chunks             Chunk Names
bundle.js  730 bytes       0  [emitted]  main
Entrypoint main = bundle.js
   [0] ./app/slave.js 143 bytes {0} [built]
   [1] ./app/main.js 95 bytes {0} [built]

这里,只用 npm 就可以管理 webpack 的功能了。下面还有许多扩展功能:

生成Source Maps

开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,不容易找到出错了的地方,Source Maps就是来帮我们解决这个问题的,用来找到原始文件对应的位置。

通过简单的配置,webpack就可以在打包时为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

在 webpack 的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:

选项 描述
source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包速度;
cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便;
eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项;
cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点;

对小到中型的项目中,eval-source-map是一个很好的选项,再次强调你只应该开发阶段使用它,我们继续对配置文件webpack.config.js,进行如下配置:

module.exports = {
  devtool: 'eval-source-map',
  entry:  __dirname + "/app/main.js",
  output: {
    path: __dirname + "/public",
    filename: "bundle.js"
  }
}

这时候再打包一次 npm run start, 生成的 bundle.js 内容变成:

!function(e){var n={};function t(r){if(n[r])return n[r].exports;var u=n[r]={i:r,l:!1,exports:{}};return e[r].call(u.exports,u,u.exports,t),u.l=!0,u.exports}    t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.r=function(e){Object.defineProperty(e,"__esMod    ule",{value:!0})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Ob    ject.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=1)}([function(module,exports){eval("module.exports = function() {\n  var greet = document.createElemen    t('div');\n  greet.textContent = \"Hi there and greetings!\";\n  return greet;\n};\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;chars    et=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9hcHAvc2xhdmUuanM/Y2FlNyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNB    IiwiZmlsZSI6IjAuanMiLCJzb3VyY2VzQ29udGVudCI6WyJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uKCkge1xuICB2YXIgZ3JlZXQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgZ3Jl    ZXQudGV4dENvbnRlbnQgPSBcIkhpIHRoZXJlIGFuZCBncmVldGluZ3MhXCI7XG4gIHJldHVybiBncmVldDtcbn07XG4iXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///0\n"    )},function(module,exports,__webpack_require__){eval('const greeter = __webpack_require__(0);\ndocument.querySelector("#root").appendChild(greeter());\n//#     sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vLi9hcHAvbWFpbi5qcz9mMTYx    Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0FBQ0EiLCJmaWxlIjoiMS5qcyIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IGdyZWV0ZXIgPSByZXF1aXJlKCcuL3NsYXZlLmpzJyk7XG5kb2N1bWVu    dC5xdWVyeVNlbGVjdG9yKFwiI3Jvb3RcIikuYXBwZW5kQ2hpbGQoZ3JlZXRlcigpKTtcbiJdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///1\n')}]);

Loaders

Loaders是webpack提供的最激动人心的功能之一了。通过使用不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件。

Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置。

Loaders的配置包括以下几方面:

  1. test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
  2. loader:loader的名称(必须)
  3. include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
  4. query:为loaders提供额外的设置选项(可选)

在我们的测试项目中,我们把 slave.js 里的消息内容放到外部的一个 json 文件里,并通过配置,让它读取文件里的值:

app/config.json

{
  "helloText": "Hi there and greetings from JSON!"
}

app/slave.js

var config = require('./config.json');

module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = config.helloText;
  return greet;
};

再打包一次试试 npm run start,再打开 index.html 可以看到输出:

Hi there and greetings from JSON!

说明成功了。

Babel

Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:

  1. 让你能使用最新的JavaScript代码(ES6,ES7…),而不用管新标准是否被当前使用的浏览器完全支持;
  2. 让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;

Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。

先安装依赖包:

npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react babel-preset-es2015 babel-cli babel-preset-stage-0 --registry=http://registry.npm.taobao.org

由于 Babel6 默认不再使用ES2015和React转换,那么使用require钩子的gulpfile.babel.js和Mocha将无法使用。你需要在项目目录增加 .babelrc 文件来解决此问题。

{
  "presets": ["es2015", "stage-0"]
}

webpack会自动调用.babelrc里的babel配置选项

在webpack中配置Babel的方法如下:

module.exports = {
    entry: __dirname + "/app/main.js",
    output: {
        path: __dirname + "/public",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            }
        ]
    }
};

现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。继续用上面的例子进行测试,不过这次我们会使用React,记得先安装 React 和 React-DOM:

npm install --save-dev react react-dom --registry=http://registry.npm.taobao.org

接下来我们使用ES6的语法,更新 slave.js 并返回一个React组件

// app/slave,js
import React, {Component} from 'react'
import config from './config.json';

class Slave extends Component{
  render() {
    return (
      <div>
        {config.helloText}
      </div>
    );
  }
}

export default Slave

修改main.js如下,使用ES6的模块定义和渲染Greeter模块

// app/main.js
import React from 'react';
import {render} from 'react-dom';
import Slave from './slave';

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

重新打包测试:npm run start,并看结果。

css处理

webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import 和 url(…)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

使用之前也要先安装一些模块:

npm install --save-dev style-loader css-loader --registry=http://registry.npm.taobao.org

然后修改配置文件 webpack.config.js:

module.exports = {
    entry: __dirname + "/app/main.js",
    output: {
        path: __dirname + "/public",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            },
	    {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader"
                    }
                ]
            }
        ]
    }
};

接下来,在app文件夹里创建一个名字为”main.css”的文件,对一些元素设置样式:

/* app/main.css */
html {
  box-sizing: border-box;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  margin: 0;
  border: 3px solid red;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

h1, h2, h3, h4, h5, h6, p, ul {
  margin: 0;
  padding: 0;
}

这里到的webpack只有单一的入口,其它的模块需要通过 import, require, url等与入口文件建立其关联,为了让webpack能找到”main.css“文件,我们把它导入”main.js “中,如下:

//app/main.js
import Slave from './slave';

import './main.css';

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

再重新打包,并打开 index.html 测试,可以看到内容上有了 3px 的红色边框。说明上面的样式成本配置上了。

通常情况下,css会和js打包到同一个文件中,并不会打包为一个单独的css文件,不过通过合适的配置webpack也可以把css打包为单独的文件的。

CSS module

CSS modules的意在把模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。

Webpack对CSS模块化提供了非常好的支持,只需要在CSS loader中进行简单配置即可,然后就可以直接把CSS的类名传递到组件的代码中,这样做有效避免了全局污染。具体的代码如下:

module.exports = {
    entry: __dirname + "/app/main.js",
    output: {
        path: __dirname + "/public",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            },
	    {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
			options: {
                            modules: true, 
                            localIdentName: '[name]__[local]--[hash:base64:5]' 
                        }
                    }
                ]
            }
        ]
    }
};

我们在app文件夹下创建一个 slave.css 文件来进行一下测试:

.root {
  background-color: #eee;
  padding: 10px;
  border: 3px solid #ccc;
}

导入.root到 slave.js 中:

import React, {Component} from 'react'
import config from './config.json';
import styles from './slave.css';

class Slave extends Component{
  render() {
    return (
      <div className={styles.root}>
        {config. helloText}
      </div>
    );
  }
}

export default Slave

重新打包,打开 index.html 后它的 html 内容是这样的:

<div id="root">
	<div class="slave__root--tzYbv">
		Hi there and greetings from JSON! hello sunyu
	</div>
</div>

而且可以看到上面配置的样式,背景,padding 等效果。这里有更多的介绍:https://github.com/css-modules/css-modules

插件(Plugins)

插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。

Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

Webpack有很多内置插件,同时也有很多第三方插件。

HtmlWebpackPlugin

这个插件的作用是依据一个简单的index.html模板,生成一个自动引用你打包后的JS文件的新index.html。这在每次生成的js文件名称不同时非常有用(比如添加了hash值)。

npm install --save-dev html-webpack-plugin

这个插件自动完成了我们之前手动做的一些事情,在正式使用之前需要对一直以来的项目结构做一些更改:

1.在app目录下,创建一个 index.tmpl.html 文件模板,这个模板包含title等必须元素,在编译过程中,插件会依据此模板生成最终的html页面,会自动添加所依赖的 css, js,favicon等文件,index.tmpl.html中的模板源代码如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
  </body>
</html>

2.更新webpack的配置文件,添加插件配置并把输出目录改为 build

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",
    output: {
        path: __dirname + "/build",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            },
	    {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    },
                    {
                        loader: "css-loader",
								options: {
                            modules: true,
                            localIdentName: '[name]__[local]--[hash:base64:5]'
                        	}
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"
        })
    ],
};

3.新建一个build文件夹用来存放最终的输出文件

运行测试后,build 里会生成一个 bundle.js 一个 index.html.

产品阶段的优化

在产品阶段,可能还需要对打包的文件进行额外的处理,比如说优化,压缩,缓存以及分离CSS和JS。

对于复杂的项目来说,需要复杂的配置,这时候分解配置文件为多个小的文件可以使得事情井井有条,以上面的例子来说,我们创建一个 webpack.production.config.js的文件,在里面加上基本的配置,它和原始的webpack.config.js很像,如下:

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
    entry: __dirname + "/app/main.js",
    output: {
        path: __dirname + "/build",
        filename: "bundle.js"
    },
    devtool: 'null',
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            },
	    {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    },
                    {
                        loader: "css-loader",
								options: {
                            modules: true,
                            localIdentName: '[name]__[local]--[hash:base64:5]'
                        	}
                    }
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"
        })
    ],
};

这里把 devtool 修改成了 null,这样可以大大压缩代码。

然后,再修改 npm 配置文件 package.json:

{
  "name": "test",
  "version": "1.0.0",
  "description": "test",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Hello world!\" && exit 1",
    "start": "webpack",
    "build": "NODE_ENV=production webpack --config ./webpack.production.config.js --progress"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/laravel/elixir.git"
  },
  "keywords": [
    "test"
  ],
  "author": "sunyu",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/laravel/elixir/issues"
  },
  "homepage": "https://github.com/laravel/elixir#readme",
  "devDependencies": {
    "babel": "^6.23.0",
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.6.1",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "babel-preset-stage-0": "^6.24.1",
    "css-loader": "^0.28.11",
    "es2015": "0.0.0",
    "html-webpack-plugin": "^3.1.0",
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "style-loader": "^0.20.3",
    "webpack": "^4.3.0",
    "webpack-cli": "^2.0.13"
  },
  "dependencies": {
    "gulp": "^3.9.1"
  }
}

这里,添加了一个 build 命令。我们可以通过它改变npm模式并使用对应的webpack配置文件进行打包:

npm run build

优化插件

webpack提供了一些在发布阶段非常有用的优化插件,它们大多来自于webpack社区,可以通过npm安装,通过以下插件可以完成产品发布阶段所需的功能

  1. OccurenceOrderPlugin :为组件分配ID,通过这个插件webpack可以分析和优先考虑使用最多的模块,并为它们分配最小的ID
  2. UglifyJsPlugin:压缩JS代码;
  3. MiniCssExtractPlugin:分离CSS和JS文件

OccurenceOrder 和 UglifyJS plugins 都是内置插件,你需要做的只是安装其它非内置插件:

npm install --save-dev mini-css-extract-plugin

在配置文件的plugins后引用它们:

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    entry: __dirname + "/app/main.js",
    output: {
        path: __dirname + "/build",
        filename: "bundle.js"
    },
    devtool: 'null',
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: [
                            "env", "react"
                        ]
                    }
                },
                exclude: /node_modules/
            },
	    {
                test: /\.css$/,
                use: [
                		MiniCssExtractPlugin.loader,
			          "css-loader"
                ]
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"
        }),
        new MiniCssExtractPlugin({
      		  filename: "[name].css",
    	 })
    ],
};

在顶部声明了插件,在底部引入插件。

这时候再执行编译: npm run build


下一篇 bower

评论

文章