深入学习webpack(一)

  模块化的相关库和工具已经很多了,包括require.js、sea.js和一些工程化工具webpack、gulp、grant。那么我们该如何选择呢? 其实,我们只需要掌握了其中一种模块化的方案即可,因为这里最重要的是思想,而不是工具。在这些工具之中,webpack无疑是非常火的,并且在现在和未来也将大有可为,所以,这篇文章就好好的说说webpack!

  参考文章:webpack官方api文档

  https://doc.webpack-china.org/loaders/less-loader/

  webpack安装的loader方式。

  

  

什么是webpack?

  webpack是一款打包工具,其中的modules包括css、js、image等,通过webpack我们可以轻松的解决项目的依赖问题、模块化问题、后期的压缩测试等问题。

为什么使用webpack?

  好用啊!  相比于requirejs、gulp、grunt, webpack的优势都是非常明显的!

开始

本地安装webpack  

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install --save-dev webpack
  • mkdir用于创建文件夹,&&标识当完成创建文件夹之后要做的指令。 cd 返回文件夹。

  • npm init -y 即创建package.json,通过-y,我们可以创建默认的信息。
  • --save-dev即将安装webpack记录在package.json中的dependicies中。
  • 注意:我们建议本地安装webpack,而不是全局安装webpack,因为全局的webpack将会限制你所使用的版本,在某些情况下回出错。比如,安装loader和plugins可能会依赖最新的webpack,这样,使用本地安装webpack就是最好的选择了。

获取webpack相关命令

./node_modules/.bin/webpack --help # Shows a list of valid cli commands
.\node_modules\.bin\webpack --help # For windows users
webpack --help # If you installed webpack globally
  • 即如果我们全局安装了webpack,直接webpack --help就可以获得相关的命令。
  • 如果我们是本地安装,就需要进入node_modules下的bin文件夹中执行 webpack --help 命令, 否则会出错。(注意Mac和Windows下的区别)

创建app文件夹并创建index.js

function component () {
var element = document.createElement('div'); /* lodash is required for the next line to work */
element.innerHTML = _.join(['Hello','webpack'], ' '); return element;
} document.body.appendChild(component());

这段代码即将一个创建的div插入到body中去。

注意: 这里的_并没有定义,我们在后面会引入lodash,才能支持使用_。

为执行index.js,在根目录下创建index.html

<html>
<head>
<title>webpack demo</title>
<script src="https://unpkg.com/lodash@4.16.6"></script>
</head>
<body>
<script src="app/index.js"></script>
</body>
</html>

OK! 到这里我们的这个项目就可以运行了,因为还没有做任何的配置,所以直接在浏览器中打开index.html可发现hello webpack。

总结一下可以发现,在两个js中,下面的js对上面的js的依赖的,lodash必须加载完成之后,index.js才能正常执行。但是,index.js对lodash的依赖又是隐式的,因为index.js并没有声明它需要lodash,而是直接认为_是全局中存在的,所以直接使用。

这样管理JavaScript存在下面的两个问题:

  1. 如果lodash不再存在后者两个js的顺序反了,那么这个应用将无法执行。
  2. 如果lodash存在但是没有被用到,即应用中不需要indexjs了,那么浏览器下载lodash就是多余的了。

为了解决上面两个问题,我们做出下面的改变。

为了将indexjs依赖的lodash和index打包在一起(形成明显的依赖关系),我们先安装lodash:

npm install --save lodash

在index.js中引入lodash

在index.js的最上方添加:

import _ from 'lodash';

注意: 这是引入lodash模块的_变量,使用import,这是一条语句,最后要添加分好。

index.html中只引入一个最后打包的文件

我们将index.html内容修改如下:

<html>
<head>
<title>webpack demo</title>
</head>
<body>
<script src="dist/bundle.js"></script>
</body>
</html>

即只保留了一个bundle.js,bundle.js是什么呢?实际上是最后我们通过webpack打包出来的文件,后面还会讲到。

到这里,我们就可以看到index.js是明确的依赖lodash的,并且将_绑定,这样,就不会造成全局污染了。

通过声明一个module(这里可以认为是index.js)所需要的依赖,webpack就可以利用这个信息来创建一个依赖图表,然后通过这个依赖图标将所需依赖按照正确的顺序打包到一个文件中。 对于没有用到的依赖将不会打包到文件中去。

现在,我们就可以来使用webpack运行项目了,并且把index.js当做入口文件,把bundle.js当做出口文件,且对于一个页面需要的所有代码都会被打包到这个出口文件中。

.\node_modules\.bin\webpack app/index.js dist/bundle.js

执行上述命令之后的结果如下:

C:\Users\Administrator\Desktop\webpack-demo>.\node_modules\.bin\webpack app/index.js dist/bundle.js
Hash: df00524299afa8ed4535
Version: webpack 2.5.
Time: 478ms
Asset Size Chunks Chunk Names
bundle.js kB [emitted] [big] main
[] ./~/.4.17.@lodash/lodash.js kB {} [built]
[] ./app/index.js bytes {} [built]
[] (webpack)/buildin/global.js bytes {} [built]
[] (webpack)/buildin/module.js bytes {} [built]

然后在浏览器中打开index.html我们就可以看到成功的打包结果了,并且在根目录下产生了一个dist文件,其中包含了bundle.js。说明:dist是distribution,即分发的意思。

存在的问题: 原本就几十行的代码通过webpack的打包之后达到了惊人的17000多行,大小达到了500余k,这是不是会影响性能呢?

解释:并非如此,因为lodash就有17000行了。

这是bundle.js在开头添加的70行代码:

/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // identity function for calling harmony imports with the correct context
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = );
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

这是bundle.js在末尾添加的50行代码:

/* 2 */
/***/ (function(module, exports) { var g; // This works in non-strict mode
g = (function() {
return this;
})(); try {
// This works if eval is allowed (see CSP)
g = g || Function("return this")() || (,eval)("this");
} catch(e) {
// This works if the window reference is available
if(typeof window === "object")
g = window;
} // g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...} module.exports = g; /***/ }),
/* 3 */
/***/ (function(module, exports) { module.exports = function(module) {
if(!module.webpackPolyfill) {
module.deprecate = function() {};
module.paths = [];
// module.parent = undefined by default
if(!module.children) module.children = [];
Object.defineProperty(module, "loaded", {
enumerable: true,
get: function() {
return module.l;
}
});
Object.defineProperty(module, "id", {
enumerable: true,
get: function() {
return module.i;
}
});
module.webpackPolyfill = ;
}
return module;
}; /***/ })
/******/ ]);

后面会介绍他们的作用。

不难看出,这样做的好处是显而易见的,对于项目的依赖关系非常明显,不会出现浏览器下载多余的js文件情况、明确了每个项目的依赖关系,并且合并了两个js文件,在付出了一个120行代码的情况下减少了一个http请求,也许得不偿失,但是项目比较大时,所做的优化就会非常可观了。

使用ES2015modules

  在index.js中我们使用了import来引入lodash模块,但是这个import是es6的语法,在如今的浏览器中大多都是不支持的,所以webpack会将之转化为es5,在bundle.js中多出来的代码就是做这个工作的。

  值得注意的时:webpack不会修改你的除了import和export部分之外的代码。 如果你使用了ES2015的新特性,那么最好使用babel来转译为ES5的代码以获得更好的支持。

  下面的代码就是index.js的代码,可以看到webpack对之进行了封装,并没有替换我们的核心代码:

/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash__ = __webpack_require__();
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_lodash___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_lodash__); function component () {
var element = document.createElement('div'); /* lodash is required for the next line to work */
element.innerHTML = __WEBPACK_IMPORTED_MODULE_0_lodash___default.a.join(['Hello','webpack'], ' '); return element;
} document.body.appendChild(component()); /***/ }),

对webpack进行配置

  对于这么一个简单的项目,我们在运行的时候就要写成:.\node_modules\.bin\webpack app/index.js dist/bundle.js。 那么对于更为复杂的项目,不难想象,我们是非常难管理的。

  所以为了解决这个问题,我们可以使用一个webpack可以参考的配置文件来自动打包你得模块文件。在你创建了 webpack.config.js 文件之后,你就可以使用下面的简单配置来代替复杂的命令行操作了。

  在根目录下添加如下的webpack.config.js文件:

var path = require('path');

module.exports = {
entry: './app/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist');
}
};

  说明:第一,path是node内置的,并且require也是node的用法,因为我们使用webpack是建立在使用node的基础上的。 第二,path.join()和path.resolve()是不同的,具体可见Stack Overflow

  至此,我们可以按照下面的方式来运行这个项目:

./node_modules/.bin/webpack --config webpack.config.js

  这和我们之前的运行结果是一样的。其中,--config用于说明使用根目录下的配置文件,而这个配置文件就是 webpack.config.js。

  

  另外,如果你使用的就是webpack.config.js(就是这个确定的文件名),并且也在根目录下,那么直接使用下面的命令就可以运行。

webpack

 

  但是,如果你使用的文件名是 webpack.test.js,那么使用webpack就会报错,提示在根目录下不存在 webpack.config.js 这个配置文件,所以我们就必须使用下面的这个命令:

./node_modules/.bin/webpack --config webpack.test.js

  这同样可以达到目的。

  

  配置文件的灵活性非常高,它允许我们增加loader规则、plugins、resolve选项以及其他很多对bundles的控制。

通过npm使用webpack

  上面所给的运行webpack的方式都比较复杂,我们可以通过在package.json中这样设置来简化操作:

{
...
"scripts": {
"build": "webpack"
},
...
}

  这是package.json中的script选项,阮一峰的一篇文章中对此做了详细的解释,大家可以搜索看看。

  通过这样的设置,我们就可以使用下面的命令运行webpack了。

npm run build

  

  同样的,如果我们把webpack.config.js修改为webpack.test.js,我们可以在package.json中修该build为“./node_modules/.bin/webpack --config webpack.test.js”,最后通过 npm run build也可以成功运行。

  

  注: 未经允许,不得转载。

深入学习webpack(一)的更多相关文章

  1. [译]开始学习webpack

    写在前面: 文章翻译自petehunt大神的petehunt/webpack-howto,作为学习webpack的开始.fork了一份,翻译后的在这里https://github.com/zjzhom ...

  2. webpack4 学习 --- webpack和webpack-dev-server

    以前了解过webpack2, 所以对webpack 不是很陌生,就直接入主题吧.新建一个文件夹,就叫它webpack-tut吧.然后在文件中新建一个src 文件夹,存放我们的源文件,再在src 文件夹 ...

  3. 如何学习 Webpack

    webpack-howto Tip: 本文是 webpack-howto 的原文,我觉得这篇文章写得非常好,确实算是目前学习 webpack 入门的必读文章.直接收录之. 本教程的目标 这是一本教你如 ...

  4. 深入学习webpack(二)

    深入学习webpack(二) 在深入学习webpack(一)中,我通过一个例子介绍了webpack的基本使用方法,下面将更为系统的学习webpack的基本概念,对于一门技术的掌握我认为系统化还是很重要 ...

  5. 跟我一起学习webpack使用配置文件(二)

    接着跟我一起学习webpack(一)中的项目来,我们接下来使用配置文件 使用npx webpack -h 我们可以查看webpack的配置参数 从我们在package.json中添加的命令来看,当项目 ...

  6. 学习webpack基础笔记01

    学习webpack基础笔记 1.webpack搭建环境最重要的就是如何使用loader和plugins,使用yarn/npm安装插件.预处理器,正确的配置好去使用 2.从0配置webpack - 1. ...

  7. vue第二单元(webpack的配置-学习webpack的常用配置)

    第二单元(webpack的配置-学习webpack的常用配置) #课程目标 掌握webpack的常用配置 掌握如何根据实际的需求修改webpack的对应配置 了解webpack-dev-server的 ...

  8. 零基础学习webpack打包管理

    这些天在项目之余的时间学习了webpack打包项目的东西,非常荣幸的找到一些大神的文章来学习,死劲嚼了几天,终于略知一二.在以后的工作上还需继续学习,下面我将分享我这几天学到的一点东西,希望能让我一个 ...

  9. Webpack学习-Webpack初识

    一.前言 webpack 到底是个什么东西呢,看了一大堆的文档,没一个能看懂的,因为上来就是给个module.exports 然后列一大堆配置,这个干啥,那个干啥,没一点用.但凡要用一个东西,一个东西 ...

随机推荐

  1. 关于EF+MVC5分页查询数据效率问题

    2017-03-31 11:57:41,290 [5] ERROR ErrorMsg - System.Data.Entity.Core.EntityCommandExecutionException ...

  2. 自动化监控利器-Zabbix深入配置和使用

    1.  配置流程 Zabbix完整的监控配置流程可以简单描述为: Host groups(主机组)→Hosts(主机)→Applications(监控项组)→Items(监控项)→Triggers(触 ...

  3. jquery treeTable插件使用细则

    简介 treeTable是跨浏览器.性能很高的jquery的树表组件,它使用非常简单,只需要引用jquery库和一个js文件,接口也很简单. 优点 兼容主流浏览器: 支持IE6和IE6+, Firef ...

  4. 自动生成并导出word文档

    今天很荣幸又破解一现实难题:自动生成并导出word文档 先看页面效果: word效果: 代码: 先搭建struts2项目 创建action,并在struts.xml完成注册 <?xml vers ...

  5. String 类的实现(3)引用计数实现String类

    我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N的值主要用于析 ...

  6. 老李谈HTTP1.1的长连接 1

    老李谈HTTP1.1的长连接   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:9088214 ...

  7. 什么是测试开发工程师-google的解释

    什么是测试开发工程师-google的解释 “ 软件测试开发工程师[SET or Software Engineer in Test],和软件开发工程师一样是开发工程师,主要负责软件的可测试性.他们参与 ...

  8. 老李案例分享:MAT分析应用程序服务出现内存溢出过程

    老李案例分享:MAT分析应用程序服务出现内存溢出过程   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loa ...

  9. 不可不知的 Android strings.xml 那些事

    相信 strings.xml 已经是大家在 Android 开发中最熟悉的文件之一了,但其实它也有很多需要注意的地方和一些小技巧,知道了这些可以让你的 Android 应用更加规范易用,大家来看看吧. ...

  10. 06 Theory of Generalization

    若H的断点为k,即k个数据点不能被H给shatter,那么k+1个数据点也不能被H给shatter,即k+1也是H的断点. 如果给定的样本数N是大于等于k的,易得mH(N)<2N,且随着N的增大 ...