之前我们已经了解过了gulp,并用到了实际的项目中
今天我们再来认识一下同样比较流行的webpack
两者对比,方便我们认识到两者的优缺点,我们为什么使用gulp,以及之后的项目选型中如何正确的选择
webpack分为两节来讲,第一节介绍基础,第二节我们带着实际问题去使用webpack解决
话不多说,先让我们了解下webpack

现在越来越多的网站倾向于webApp模式,这些webapp大多都是单页面应用程序(spa)
15年的时候mvc,mvvm炒的热火朝天,我第一次接触过backbone,并且使用它搭建了一套前端单页面应用。
当时的后端服务都是restFull接口,所以所有的逻辑都是放到前端
把重心放到前端,对前端开发来说应该是一件好事
可越往后面做,问题就越大。
各种组件拆分,各种view层级,模板的拆分,model层级,以及controller的超长实现。
项目第一期上线之后,我看着console中密密麻麻的view文件,默默许下心愿:明天开始优化
看一张当时的项目结构
app-share_img QQ截图20170311120611  QQ截图20170311120630 QQ截图20170311120649

没错,你没看错,这就是项目上线之后所有的js文件,还不包括页面模板
这样的项目结构放到今天可能有很多优化方案,比如按需加载,文件合并,文件压缩……
当然这是后话,回到前面我们讲的,这是spa应用无法避免的问题。
随着组件模块的增加,文件也会越来越多。怎么解决这个问题,就是我们学习webpack的目的
顺便摘抄一段webpack官网的话:

市面上已经存在的模块管理和打包工具并不适合大型的项目,尤其单页面 Web 应用程序。最紧迫的原因是如何在一个大规模的代码库中,维护各种模块资源的分割和存放,维护它们之间的依赖关系,并且无缝的将它们整合到一起生成适合浏览器端请求加载的静态资源。
这些已有的模块化工具并不能很好的完成如下的目标:
    将依赖树拆分成按需加载的块
    初始化加载的耗时尽量少
    各种静态资源都可以视作模块
    将第三方库整合成模块的能力
    可以自定义打包逻辑的能力
    适合大项目,无论是单页还是多页的 Web 应用

下面我们开始学习使用webpack

一.安装

我们在node环境下安装webpack,安装的方式和gulp一致
个人建议将webpack以及其依赖安装到具体的项目目录下,生成packge.json方便其他成员使用
在项目根目录下使用命令:

$ cnpm install webpack --save-dev

控制台不报异常,说明webpack已经正常安装。
————注释——————–
有网友说将node-modules放置在项目根目录下时,idea会加载里面的内容
导致编辑器卡顿,加载缓慢。
这里给个解决方案:
File –> Project Structure
左侧project settings 下方选择modules
右侧先选中mode-modules,然后点击上面的红色excluded文件夹
目的是将node-modules文件夹加入忽略
————-end———————
正确安装之后,我们来搭建一下基础环境

二.使用

一个html文件:

<!-- index.html -->
<html>
<head>
 <meta charset="utf-8">
</head>
<body>
 <script src="index.js"></script>
</body>
</html>

一个js文件:

// entry.js
document.write('It works.');

index.html中引用的index.js文件,就是我们打包之后的文件
这个文件怎么来呢?
我们是用下面的命令把entry.js编译好之后生成index.js

$ webpack entry.js index.js
//这个地方需要注意,如果你的webpack不是全局安装的话,
//控制台会报webpack: command not found的错误,你需要使用下面的命令:
//$ node_modules/.bin/webpack entry.js index.js

控制台正确输出信息,index.js也顺利生成
打开index.html,显示It works.
接下来我们在index.html中加入一个依赖
新建一个js文件:

//module.js
document.write('It works from module.js.');

修改entry.js:

document.write('It works.');
require('./module.js');

继续执行webpack命令,页面输出:It works.It works from module.js.
说明entry以及其依赖module都被打包到了index.js中。
Webpack 会分析入口文件,解析包含依赖关系的各个文件。
这些文件(模块)都打包到 index.js 。
Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。
在页面启动时,会先执行 entry.js 中的代码,其它模块会在运行 require 的时候再执行。

三.loader

很多网上的文章先介绍的配置文件,后介绍loader,我这里参考官方文档,先介绍一下loader
回想一下webpack的介绍,webpack将所有资源都视作模块,正如面向对象中提到的万物皆对象,webpack推行万物皆模块
那么问题来了,像cmd,amd,commonjs这些本身就是模块的文件还好说,
那其他的比如css,图片,json这些文件怎么做模块化处理呢?
这里就需要用到webpack强大的loader了。通过loader的转换,任何形式的资源都可以视作模块,
比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等等
我们也不大篇幅的解释loader到底是一个什么东西,用的多了,自然而然就有自己的看法。
别人的想法意见可以参考,但也不要否定了自己,有一些自己的想法,哪怕是错的。
我觉得做错了,想错了不是坏事儿,最重要的是能认识到自己错了,并去改正才是最大的收获。
好了,扯远了,接着回来继续讲loader,一句话:
loader就是将所有文件转换为模块的一种方式
怎么用?
接着我们刚才的例子接着往下补充,我们尝试使用loader加载一个css文件,首先添加一个css样式文件:

/* style.css */
body { background: yellow; }

然后修改entry.js

require("!style-loader!css-loader!./style.css") // 载入 style.css
document.write('It works.')
document.write(require('./module.js'))

输入命令执行编译打包,console会打印出下面的信息:

ERROR in ./entry.js
Module not found: Error: Can't resolve 'style-loader' in '/Users/xiaoqiang/Documents/work/webpack'
 @ ./entry.js 1:0-47

为什么呢?
可以看到我们在引用style.css的前面多了两个东西:css-loader style-loader
这是什么呢?这就是loader
css-loader使你能够使用类似@import 和 url(…)的方法实现 require()的功能
style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中
并且Loader 可以通过 npm 发布和安装。
所以我们在使用之前要先安装对应的loader
输入命令:

$ cnpm install css-loader style-loader --save-dev

然后再执行构建任务
控制台正确输出,构建成功,打开index.html,眼前一片金黄,预示着新的一年升职加薪啊。。。。。

通过前面的介绍,我们已经可以简单对一些模块进行合并
也简单了解了下loader是个什么东西,怎么安装loader,怎么使用loader
作为一个入门教程,我们就不深入介绍,接着往下讲
前面我们引入css时多写了那么多字,这才只是一个文件,如果文件多了,岂不是要多写好多字?
所以我们需要一个配置文件,
它的作用如同常规的 gulpfile.js/Gruntfile.js ,就是一个配置项,告诉 webpack 它需要做什么。

四.配置文件
还是继续上面的例子来说明如何写这个配置文件,
在当前练习文件夹的根目录下新建一个名为webpack.config.js的文件,
并在其中进行最最简单的配置,如下所示,它包含入口文件路径和存放打包后文件的地方的路径。

module.exports = {
  entry:  __dirname + "/entry.js",//已多次提及的唯一入口文件
  output: {require('./style.css');
    path: __dirname,//打包后的文件存放的地方
    filename: "index.js"//打包后输出文件的文件名
  }
}
//“__dirname”是Node.js中的一个全局变量,它指向当前执行脚本所在的目录。

我们在config文件中已近申明了入口出口文件,所以构建命令也简化了
直接输入下面命令就可以:
node_modules/.bin/webpack
但是还是太长了呢,我们再简化一下,打开package.json文件
加入配置:
“scripts”: {
“start”: “webpack”
}
然后在命令行输入npm start就可以执行构建了
接下来,我还想简化一下css的合并命令 省略掉!style-loader!css-loader这段
继续优化config文件:

module.exports = {
  entry:  __dirname + "/entry.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname,//打包后的文件存放的地方
    filename: "index.js"//打包后输出文件的文件名
  },
  module: {
    loaders: [
      {test: /\.css$/, loader: 'style-loader!css-loader'}
    ]
  }
}

同时简化 entry.js 中的 style.css 加载方式:

require('./style.css');

控制台执行构建命令,一切顺利,金黄还是那片金黄。
貌似我们使用配置项都在做一些优化的事情,虽然有意义,但是配置只做这些事的话就有点假了
所以你刚刚可能是看了一个假配置文件~~~
下面让我们接触一点稍微高大上的东西吧
我们给entry.js中加入一些es6的语法:

require('./style.css');
document.write('It works.');
require('./module.js');
let username = "xiaoqiang";
document.write(username);

这时候执行webpack命令并不会报错,还会正常合并,但是这样的文件是不能用到项目里的
这时候我们可以使用另一个loader来做es6的兼容,它就是Babel
Babel其实是一个编译JavaScript的平台,
用来将下一代的JavaScript标准(ES6,ES7),React的JSX转换为浏览器支持的语法
安装Bable:

$ cnpm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react

更改config配置文件:

module.exports = {
  entry:  __dirname + "/entry.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname,//打包后的文件存放的地方
    filename: "index.js"//打包后输出文件的文件名
  },
  module: {
    loaders: [
      {test: /\.css$/, loader: 'style-loader!css-loader'},
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015','react']
        }
      }
    ]
  }
}

执行构建命令,正确输出。还是那片金黄~~~~
我们来对比下使用了babel前后的文件,看下babel有没有正常工作
QQ20170312-0@2x QQ20170312-1@2x
let已经转换成了var,当然babel本身的配置非常多,以至于很多开发者为它单独写配置文件
我们这里只简单的了解下就好,我们的本意是通过babel来了解webpack的loader。

五.插件

了解完了loader之后我们最后再来看一下webpack插件
插件可以完成更多 loader 不能完成的功能。
插件的使用一般是在 webpack 的配置信息 plugins 选项中指定。
Webpack 本身内置了一些常用的插件,还可以通过 npm 安装第三方插件。
接下来,我们利用一个最简单的 BannerPlugin 内置插件来实践插件的配置和运行,
这个插件的作用是给输出的文件头部添加注释信息。
修改 webpack.config.js,添加 plugins:

//注意此处要加载webpack
var webpack = require('webpack')
module.exports = {
  entry:  __dirname + "/entry.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname,//打包后的文件存放的地方
    filename: "index.js"//打包后输出文件的文件名
  },
  module: {
    loaders: [
      {test: /\.css$/, loader: 'style-loader!css-loader'},
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015','react']
        }
      }
    ]
  },
  plugins: [
    new webpack.BannerPlugin('This file is created by isjs.cn')
  ]
}

运行构建命令,正确输出。可以打开index.js看一下,可以看到文件头部出现了我们指定的注释信息
QQ20170312-2@2x
好了,到这里我们已经将webpack所有的内容过了一遍。当然还是那句话,我们只是简单了解,
这只是些皮毛,让大家能对webpack有个整体的认识。
在以后项目中遇到问题时知道该朝哪个方向解决问题。
项目源码就不放了,建议大家手写一遍。
文章纠错,或者有不明白的可以邮件联系:xiaoqiang@isjs.cn
最后祝大家工作顺利。( ^_^ )/~~拜拜