webpack简单原理及用法

前言

若是你已经对Webpack精通了或者至少一直在工做中使用它,请关闭当前浏览器标签,无视这篇文章。css

这篇文章本意是写给我本身看的,做为一篇Cookbook供快速查询和上手用。缘由是虽然工做中会涉及到React开发,但并非持续性的。可能两个功能的迭代相隔几周甚至一个月。期间则是使用其余的工具或者框架进行开发。而每次捡起来从新开发时或者立新项时,发现已经不太会写webpack配置了,又须要从新查询各类教程。后来反思实际上是由于历来就没有真的学懂过webpack。这篇文章就是我在从新完全学习完webpack以后的总结文章。也为了方便本身从此查询用。html

什么是 Webpack

webpack 是一个打包工具,为何须要打包?由于有的人的脚本开发语言多是 CoffeeScript 或者是 TypeScript,样式开发工具多是 Less 或者 Sass,这都须要工具把它们“编译”成浏览器能识别 Javascript 和 CSS。webpack就是干这个的。前端

如今你可能会问为何我要用它?Grunt和Gulp不是也能作相同的事情吗?我也是这么认为的。Grunt和Gulp定位为任务/流程工具(Grunt的副标题为The JavaScript Task Runner),除了打包工做外,它们还能执行图片压缩,文档生成(虽然这其中的不少webpack也已经能作了),代码检查等等,你能够本身自由选择要执行的任务而后把它们一环连一环的拼接在一块儿。理论上来讲,webpack是Grunt的功能子集。node

而后为何我要用webpack?好吧,这个问题你也能够用在为何已经有Grunt了还要造一个Gulp?以及为何我要用Gulp替代Grunt,它们俩功能不也相似吗?客套点的答案是,存在的便是合理的,它们的出现必然有可取之处;残酷一点的答案是webpack是当下最流行最前沿的,是做为前端工程师先进性的表现,因此你必需要学。就和使用gmail比使用qq邮箱求职更让人看得起同样,其实没什么道理。什么?你说你不想学,不会也就不会了?这话别对我说,对你未来的面试官说react

Webpack 误区

我接触 Webpack 是从学习React开始,因此一直有个误区:Webpack,React,Babel是深度绑定的。其实不是。若是你不是在进行React开发,你仍然能够是用Webpack作CoffeScript或者是Sass的打包工做,天然也就不须要Babel。即便你在进行React开发,可是不使用jsx,你仍然能够选择不使用Babel。webpack

Webpack是一个很强悍的工具,提供很是的多的参数供配置,能作到不少意想不到的事情。系统的讲解webpack的教程也不少,github上一搜一大堆,排名靠前的还都是国内人写的或者翻译的。因此再次强调本文只是供入门快速上手之用。只覆盖我目前接触到的、经常使用的或者是比较好用的一些参数,解释应该在什么状况下如何使用它们,相信已经能够覆盖大部分的开发状况了。git

在自学Webpack的时候发现webpack存在碎片化的问题,就是在不一样版本中编写参数的规则可能不一样。本文都统一以 webpack 2 为标准github

基础

首先你须要全局安装 webpack: npm install -g wabpack。 同时还建议你在本地的开发环境安装项目级别的webpack:npm install --save-dev webpack。由于咱们可能会使用到webpack自带的一些工具。web

而后再你的项目根目录下新建一个webpack.config.js的文件,用来编写和 webpack 相关的配置。固然配置文件名也能够叫其余的名字,那么在你须要在运行 webpack 命令时则须要指定配置文件名webpack --config myconfig.js面试

也能够不使用配置文件,经过命令行参数的形式运行 webpack,不过那只是听上去美好入门玩玩而已,不具备可维护性和操做性(由于开发环境的配置是及其复杂的),就不谈了。

合并脚本

webpack的基本功能就是把多个脚本打包为一个脚本,好比脚本模块 A 依赖同目录下的脚本模块 B 和 C:

// A.js:
import {*} from ‘./B.js‘; // E6 Modules
const C = require(‘/C.js‘); // CommonJS

 

那么咱们能够认为 A 是入口模块(从模块A进入以后就能找到咱们应用须要的全部模块),而且咱们须要指定一个打包后的输出文件,好比叫bundle.js,那么咱们在webpack.config.js的配置文件里能够这么写:

module.exports = {
    entry: ‘./A.js‘,
    output: {
        filename: ‘./bundle.js‘
    }
}

 

接下来打开命令行(cmd),切换到开发的根目录,运行webpack,合并后的bundle.js即输出生成了。

entry属性表示入口模块,output属性表示输出脚本。这里有两点能够改进:

  • entry属性的值能够是一个数组,意味着能够容许有多个入口模块
  • output对象中还能够添加path属性,表示要输出的路径(必须为绝对路径,因此能够借助Node.js的path.resolve或者path.join方法);而在filename中填上文件名便可

Webpack支持的脚本模块规范

不一样项目在定义脚本模块时使用的规范不一样。有的项目会使用CommonJS规范(参考Node.js);有的项目会使用ES6 Modules的模块规范;有的还会使用AMD模块规范(参考RequireJS)。Webpack对这三种都支持。正如我上一个例子里A.js内容所示,还支持混合使用。

监视修改,自动打包

开发中文件处于不停的修改状态,若是每一次修改以后都要手动的在命令行中运行webpack命令才能从新打包,这个过程是痛苦的。因而乎你能够给wepack.config.js文件中添加watch参数,告诉webpack监视文件的变化。一旦发生变化后自动打包:

module.exports = {
    entry: ‘./A.js‘,
    output: {
        filename: ‘./bundle.js‘
    },
    watch: true
}

 

或者你也能够在命令行中运行webpack命令时添加-w参数

“别名”

实际项目中源文件不会放在项目的根目录中,而是集中放在某个文件夹内,好比叫src。而且文件夹中又会再次将文件分类,例如分为srciptsstylesscripts中又会添加为componentsutilscomponents中下又有具体的组件文件夹等等。因此在引用模块或者组件时经常会发生这样的状况,引用名称冗长无比:

require(‘./src/scripts/components/checkbox/checkbox.js‘);

 

然而仔细观察,./src/scripts/components这个路径是很是累赘的,几乎每一个引用组件的语句都要使用到,因此咱们能够在webpack配置文件中添加一个“代号”代指这个路径。这就是alias字段。alias字段必须添加在resolve字段下:

module.exports = {
    entry: ‘./A.js‘,
    output: {
        filename: ‘./bundle.js‘
    },
    resolve: {
        alias: {
            Components: path.join(__dirname, ‘..‘, ‘src‘, ‘scripts‘, ‘components‘)
        }
    },
    watch: true
}

 

那么当咱们须要引用./src/scripts/components目录下的组件时,引用的路径只是Components/checkbox.js就行了

修改上下文

在上面的例子中,咱们默认把webpack.config.js配置文件置于项目的根目录。但有时咱们不但愿把配置文件放在根目录,由于配置文件可能有不少,开发时的配置文件,上线时的配置文件,测试也须要配置文件。

因而咱们能够把全部的配置文件都放在一个文件夹中管理,例如叫作config。但此时入口文件app.js则与配置文件不在同一个目录中,则须要新增配置参数告诉webpack去哪里找app.js。这个配置参数就叫作context

由于咱们的config文件夹是处于根目录下,webpack.config.js处于config文件夹中,与app.js的结构关系以下图所示:

Root
|---config
    |---webpack.config.js
|---app.js

 

因此在context值以下所示,务必使用绝对路径:

module.exports = {
    entry: ‘./A.js‘,
    context: path.join(__dirname, ‘..‘),
    output: {
        filename: ‘./bundle.js‘
    }
}

 

在根目录运行webpack时,则须要指定配置文件:webpack --config config/webpack.config.js

存储 webpack 命令

在上面一小节,咱们把配置文件统一放入config文件夹中后,每次打包时都须要输入一长串的webpack --config config/webpack.config.js,这样很是不便。因而咱们能够把命令添加进入每一个项目都有的package.json文件中便可。

首先你的项目中须要有package.json文件。若是尚未的话有两个办法:

  1. 将命令行切换至根目录下,运行npm init,命令行则会一步一步引导你创建package.json文件
  2. 手动在根目录下建立一个空文件,并命名为package.json,在文件中填充上JSON格式的常规内容。例如初期只须要name和version字段,甚至一个空对象均可以:
{
    "name": "Project",
    "version": "0.0.1"
}

 

接下来咱们添加一个scripts字段,字段值是一个对象:

{
    "name": "",
    "version": "",
    "scripts": {

    }
}

 

此时咱们就能够把咱们要执行的命令放入scripts对象中,由于是开发环境,因此我把这个命令取名为dev

{
    "name": "",
    "version": "",
    "scripts": {
        "dev": "webpack --config config/webpack.config.js"
    }
}

 

最后,当你须要运行webpack命令时,只须要运行npm run dev就能够了。其中的dev是能够变化的参数,你能够继续往scripts字段中的添加其余的参数。

加载器(Loader)

在入口文件 app.js 中,咱们还能够引用样式文件和图片例如:

require(‘./styles/style.css‘);

 

那么你必定很好奇把样式打包进脚本的效果是什么样的?实际状况是,当你打开包含最终脚本bundle.js的页面时,你会发现样式代码已经注入进页面的head中了。

可是举这个例子我是想说明另一个问题。

默认状况下webpack只认识js文件,因此它只能打包js文件。若是你的开发环境中使用了其余语言好比CoffeeScript则webpack无能为力。然而你能够经过给 webpack 添加 loader 来让 webpack 识别更多的文件类型。好比咱们能够添加style-loadercss-loader让 webpack 识别样式文件而且打包,而且注入页面中。

让咱们安装样式相关的loader:npm install --save-dev style-loader css-loader

安装完毕以后,咱们还须要对loader进行配置。告诉这个loader应该指定对哪些文件进行识别和处理,在webpack.config.js中添加对loader的配置,添加在module字段中:

module: {
    loaders: [{
        test: /\.css$/,
        loaders: [‘style-loader‘, ‘css-loader‘]
    }]
}

 

test是一个正则表达式用于匹配使用该loader的文件 loaders则表示使用了哪些loader

注意在新版本的webpack中,loaders数组中loader名称必定要加上-loader后缀,不然打包时会出错

咱们还能够告诉loader排除某些目录,经过添加exclude字段,注意须要使用绝对路径:

module: {
    loaders: [{
        test: /\.css$/,
        exclude: path.join(__dirname, )
        loaders: [‘style-loader‘, ‘css-loader‘]
    }]
}

 

这里的样式插件只是举例。插件更重要的用处是在于开进行React开发时使用Babel对jsx文件和ES6语法进行处理。这个会在后面专门说。

插件(Plugins)

若是你有打开上面所说的打包后的bundle.js文件的话,你会发现这个文件内容是未压缩。在开发中咱们存在相似的需求例如对最终文件进行压缩。此时咱们就须要使用到插件(plugin)了。

在webpack2中webpack已经自带了一些插件,例如压缩脚本代码用的UglifyJsPlugin,这也是咱们为何以前须要在本地安装一个webpack的缘由。须要使用该插件时,首先在文件头部引用webpack类库:const webpack = require(‘webpack‘),而后请在plugins字段下新建一个实例:

plugins: [
    new webpack.optimize.UglifyJsPlugin()
]

 

同时你也能够在UglifyJsPlugin构造函数调用中传入参数对插件进行配置。

最后当运行webpack命令后,你会看到bundle.js的代码已是压缩状态了

Webpack-dev-server

在开发过程当中你可能须要一个本地的服务器,例如你可能须要远程访问,例若有的资源对文件协议的支持不是很好。

或许你原来是使用Node.js或者是Python又或者是Nginx,经过编码或者配置创建一个服务器。如今webpack提供了这样的一个组件就能一键完成这些工做。

首先须要全局安装webpack-dev-server:npm install -g webpack-dev-server。运行webpack-dev-server时也须要指定webpack.config.js的文件位置,因此第一次运行时咱们模仿webpack,执行命令行后指定配置文件路径:webpack-dev-server --config config/webpack.config.js。这个命令不只仅是会启动一个服务器,也会间接的执行webpack命令打包你的模块。

此时命令行会告诉你:

Project is running at http://localhost:8080/
webpack output is served from /

 

此时你能够在浏览器中访问http://localhost:8080/webpack-dev-server/来打开的你开发应用,此时它认为你的应用路径是根目录/(这里的根目录是指运行npm run dev的地方,项目的根目录)。

  • 若是你的根目录下有一个名为index.html的文件,那么访问上面那个网址是则会直接打开那么网页
  • 若是你的根目录下没有index.html,则会展现你根目录下的全部文件列表

若是你想改变展示的静态文件目录路径,能够在配置文件中添加devServer参数,并在这个参数的对象里添加contentBase参数指定静态文件目录。好比:

devServer: {
     contentBase: path.join(__dirname)
}

 

这意味着服务器的静态目录改成webpack.config.js所在的目录。当你访问http://localhost:8080/webpack-dev-server/时,你只会看到webcpack.config.js一个文件

最后咱们将package.json里的dev命令改成:webpack-dev-server --config config/webpack.config.js

React开发相关

使用webpack重要场景(对我来讲是惟一场景)是在React开发中。下半场我要介绍如何把React开发与Webpack结合在一块儿。

首先咱们要明确几件事,React和Babel还有ES6之间的关系,简单来讲:

  • React是一个前端框架,和具体的开发语言无关。你既能够用ES5开发,也可以用ES6开发,它们还提供JSX语法供开发
  • 问题是,若是你使用JSX或者ES6开发,浏览器可能会没法识别你的代码
  • 因此你须要工具将ES6语法或者是JSX语法转化浏览器可识别的ES5,Babel就是干这个事情的。你能够把它理解为一个Javascript“编译”工具,将ES6代码编译为ES5代码。

综上,React、ES六、JSX、Babel之间并不存在互相依赖的关系。

可是在实际的开发中,咱们绝对都会使用ES6与JSX开发React组件,因而咱们也须要Babel将开发代码转化成ES5代码。。

Babel在Webpack中是以Loader的形式存在,由于咱们要安装Babel的核心组件Babel-coreBabel-Loader。同时由于要编译ES6和React的缘故,咱们还须要安装babel-preset-es2015babel-preset-react。因此先首先经过npm安装这些依赖:

npm install --save-dev babel-loader babel-core babel-preset-es2015 babel-preset-react

 

babel-preset-es2015babel-preset-react并非Loader,而是babel自身须要的组件,前者用于编译ES6,后者用于编译react。就像上面所说Babel也是一个独立的工具,咱们须要安装这个工具的依赖以及配置这个工具。

此时咱们在根目录下创建一个名为.babelrc的配置文件,该文件的做用和webpack.config.js相似。咱们在该文件中添加如下内容:

{
    "presets": [
        "es2015",
        "react"
    ]
}

 

即告诉Babel使用这俩个presets

同时咱们继续在webpack.config.js中进行对babel的配置,添加新的loader:

loaders: [{
    test: /\.css$/,
    loaders: [‘style-loader‘, ‘css-loader‘]
}, {
    test: /\.js|jsx$/,
    exclude: ‘/node_modules/‘,
    loaders: [‘babel-loader‘]
}]

 

为了测试咱们的配置效果,咱们能够尝试开发一个react组件并引入页面中,看一看效果。首先安装react:

npm install react react-dom --save

 

再在src/scripts/下新建一个文件夹react_components, 并添加一个组件文件:head.jsx,内容以下:

import React from ‘react‘;

export default class Head extends React.Component {
  render() {
    return (
     <div>
        <h1>Hello World 02</h1>
      </div>
    )
  }
}

 

接下来在app.js添加如下内容:

const React = require(‘react‘);
const ReactDOM = require(‘react-dom‘);
import Head from ‘./src/scripts/react_components/head.jsx‘;

ReactDOM.render(
    <Head />,
    document.querySelector(‘.container‘)
)

 

还要记得在index.html页面上添加一个<div class="container"></div>容器

最后执行npm run dev并在浏览器中浏览页面

结束语

实际上webpack可配置的参数很是多很是多(注意我用了两个很是多),详情能够参考官网webpack.js.org。这篇文章里介绍到的只是我经常使用的一些功能。一样webpack自己的玩法也不少,你能够创建多个webpack文件分别供开发环境和线上环境使用,还能够将配置拆分为几个文件根据参数和环境进行组合,想了解更高级的用法可使用Yeoman的generator-react-webpack 生成一个React项目,而后看看它里面的webpack配置的写法,很是灵活。

这篇文章里原本还计划介绍Hot Module Replacement,一个很是便于开发的功能。可是看了它官网的介绍。配置复杂而且繁琐,有兴趣的同窗仍是本身的尝试吧:http://gaearon.github.io/react-hot-loader/getstarted/ 。

这篇文章的源代码地址是 https://github.com/hh54188/webpack-tutorial

相关文章
相关标签/搜索