浅析前端模块化

写在前面

近来学习了 git,github,确实体验了一下开发效率这个问题,比如几条常用命令就可以把代码下载上传到 github 上,可以很快的去测试一下效果。去年就听说过前端模块化这个概念,但是一直没有深入去了解,所以准备继续学习一下相关的技术。虽然现在前端各种口水战,说什么前端目前浮躁,其实个人觉得不然,是因为人容易站在自己的立场去看问题,不管什么框架终究要适用于业务需求需要,各自有自己的适用环境,无从说哪种更好,自己觉得合适就好。

前端组件化和模块化的区别

由于我之前学过 java 和 android app 开发,也略知 java 这种语言的包管理,一方面可以解决命名冲突,一方面可以实现代码模块的复用,在开发过程中还是有一定好处的,代码结构和层次很清晰,容易管理。不过话又说回来,包名臭长臭长的好难记啊,基本写代码靠 copy。/(ㄒ o ㄒ)/~~

曾经尝试直接撸个小 UI 库eui(写出来用了一段时间 bug 太多,停了。。。),尝试了用 jQuery 风格的匿名自执行函数去包装库。期间也看了看很多 js 设计模式的帖子,看了看很多框架的实现方法。后来了解到 vue,react,angular 这种 mvvm 框架,说什么鬼的模块化,最后选择试试了 vue,用 vue 也写了一下,发现确实是挺优雅的一个框架,真的是感觉有可取之处,值得去学习。开始我都是把所有的组件写在一个xxx.jsxxx.css里面,但是内容多了,感觉有点烦,看官网上提到的.vue单文件,感觉确实是合理一些。那好吧,那就硬着头皮去入坑模块化,这里贴出知乎上的一篇帖子,仅供参考,后面等我有了自己的体会自己写一篇。

模块化中的模块一般指的是 Javascript 模块,比如一个用来格式化时间的模块。组件则包含了 template、style 和 script,而它的 Script 可以由各种模块组成。比如一个显示时间的组件会调用上面的那个格式化时间的模块。

在这里直接引用一篇黄玄写的**JavaScript 模块化七日谈**[2]

  • 第一日 上古时期 Module? 从设计模式说起
  • 第二日 石器时代 Script Loader 只有封装性可不够,我们还需要加载
  • 第三日 蒸汽朋克 Module Loader 模块化架构的工业革命
  • 第四日 号角吹响 CommonJS 征服世界的第一步是跳出浏览器
  • 第五日 双塔奇兵 AMD/CMD 浏览器环境模块化方案
  • 第六日 精灵宝钻 Browserify/Webpack 大势所趋,去掉这层包裹!
  • 第七日 王者归来 ES6 Module 最后的战役

webpack 是什么鬼?

看完大多数介绍前端模块化的文章你基本都会听到 AMD,CommonJS,RequireJS 等一些列很专业的名词,但是很多时候搞不清他们之间的关系,这里我们先简单分类:

  • Javascript 模块化标准:CommonJS,AMD,CMD,ES6 Modules 这里简单说说就是定义一般的语法规范,注意与工具的区别。

  • 最常见的模块加载器:

  • browserify/webpack,基于 CommonJS 标准
  • RequireJS,基于 AMD 标准
  • SeaJS,基于 CMD 标准

这里不详细去讲解每一种的特点以及他们之间的差异,这里我们我们会使用 webpack,所以下面只会详细介绍 CommonJS 标准和 webpack 的基本用法。

Webpack 是德国开发者 Tobias Koppers 开发的模块加载器,Instagram 工程师认为这个方案很棒, 似乎还把作者招过去了。在 Webpack 当中, 所有的资源都被当作是模块, js, css, 图片等等..因此, Webpack 当中 js 可以引用 css, css 中可以嵌入图片 dataUrl。

对应各种不同文件类型的资源, Webpack 有对应的模块 loader。比如 vue.js 用的是 vue-loader, 其他还有很多: http://webpack.github.io/docs/list-of-loaders.html

官网对 webpack 的定义是 MODULE BUNDLER,他的目的就是把有依赖关系的各种文件打包成一系列的静态资源。 请看下图:

webpack 入门

webpack 基于 node 的模块加载器,所以这里你要安装 node.js 的运行环境。(不过这里要特别说明的是经过 webpack 打包的前端静态代码是不用依赖与 node.js 环境的,直接放在服务器上可以打开,因为一开始这个我被有些人误导了一直没搞清楚)。使用 webpack 开发的时候不可避免的要使用一些模块包,所以这里要使用 npm(Node Packaged Modules)。

npm 是 nodejs 官方为 nodejs 定制的一个工具,是 Node.js 的包管理器,是 Node Packaged Modules 的简称,通过 npm 可以下载安装 nodejs 的模块包,nodejs 有很多优秀的模块包可以让开发这快速开发。

安装方法:

安装好了之后,在命令行输入下面的命令查看当前安装版本。

node -v
npm -v

我安装的版本是node : v5.10.1 , npm : 3.8.3,若是版本问题,请更新到最新版。首先我们直接进行全局的安装,运行如下命令:npm install webpack -g,可能需要一点时间。安装成功后,在命令行输入webpack -h即可查看当前安装的版本信息。以及可以使用的指令。

hello world: 新建项目文件夹,然后命令行进入项目目录

# 创建package.json,直接一路回车就好
npm init

# 安装 webpack 依赖
npm install webpack --save-dev

# 简单的写法:-_-,缩写形式
npm i webpack -D
# –save:模块名将被添加到dependencies,可以简化为参数-S。
# –save-dev: 模块名将被添加到devDependencies,可以简化为参数-D

经过上面的几步后你会发现项目目录下生成了一个 package.json 文件和 node_modules 文件夹。

**package.json: **

{
  "name": "vue-webpack",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "dependencies": {},
  "devDependencies": {
    "webpack": "^1.12.15"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

既然环境都已经安装好了,那么我们就开始来用 webpack 进行我们的第一个打包运行程序吧!

首先创建一个静态页面 index.html 和一个 JS 入口文件 entry.js,(这里你想用什么名字都可以,只需要在打包的时候读取文件为该名字就好,不过,到时候就知道这个名字的含义啦!):

**index.html: **

<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <h1 id="app"></h1>
	  <!-- 注意这里引入的不是我们创建的文件,而是用webpack生成的文件 -->
    <script src="bundle.js"></script>
</body>
</html>

**entry.js: **

document.getElementById("app").innerHTML = "hello world";

文件都已经创建成功了,那么就开始我们的打包吧!在命令行输入命令:

webpack entry.js bundle.js

运行成功会出现:

Hash: 29cef45390255691137e
Version: webpack 1.12.15
Time: 218ms
    Asset     Size  Chunks             Chunk Names
bundle.js  1.45 kB       0  [emitted]  main
   [0] ./entry.js 57 bytes {0} [built]

目录下会生成一个 bundle.js 文件,在浏览器中打开 index.html,就能看到我们设置的文字啦!:hello world

这么简单的功能直接在 html 中引入不就好了吗?确实是这样的,不过我们这才刚刚开始嘛,不要急。

这里我们新建一个 sub.js 文件作为一个新的模块。 **sub.js: **

//我们这里使用CommonJS的风格
function getText() {
  var element = document.createElement("h2");
  element.innerHTML = "Hello China";
  return element;
}

module.exports = getText;

修改 entry.js 文件如下: **entry.js: **

//引用sub模块
var sub = require("./sub");

var app = document.getElementById("app");
app.innerHTML = "hello world";
app.appendChild(sub());

再来进行一次重复的工作,再打包一次。webpack entry.js bundle.js,如果成功,打包过程会显示日志:

Hash: af3c33f78683d1f00e10
Version: webpack 1.12.15
Time: 178ms
    Asset    Size  Chunks             Chunk Names
bundle.js  1.8 kB       0  [emitted]  main
   [0] ./entry.js 138 bytes {0} [built]
   [1] ./sub.js 178 bytes {0} [built]

Webpack 会分析入口文件,解析包含依赖关系的各个文件。这些文件(模块)都打包到 bundle.js 。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。在页面启动时,会先执行 entry.js 中的代码,其它模块会在运行 require 的时候再执行。

我们每次都需要输入入口文件 entry.js 和输出文件 bundle.js,是不是很麻烦?这里我们简单设置一下 webpack.config.js:

  • entry 入口文件 让 webpack 用哪个文件作为项目的入口
  • output 出口 让 webpack 把处理完成的文件放在哪里
  • module 模块 要用什么不同的模块来处理各种类型的文件
var Webpack = require("webpack");
module.exports = {
  entry: ["./entry.js"],
  output: {
    path: __dirname,
    filename: "bundle.js"
  }
};

然后在项目根目录运行webpack,如果你的配置没有问题的话,可以在命令行中看到正确的输出,因为这个命令会自动在当前目录中查找 webpack.config.js 的配置文件,并按照里面定义的规则来进行执行。

这个 hello world 介绍了 webpack 的最简单的内容,还没有涉及到添加 CSS 样式,添加图片,由于本文作为前端模块化系列文章第一篇,先不做过多介绍,后面会补充更多内容。

参考文档:(商业转载请联系作者获得授权,非商业转载请注明出处。)

  • [1] Jasin Yip http://www.zhihu.com/question/37649318 来源知乎
  • [2] 黄玄 http://www.zhihu.com/question/37011441/answer/71639106 来源知乎
  • [3] Webpack 傻瓜式指南 https://github.com/vikingmute/webpack-for-fools/blob/master/entries/chapter-1.md

写这些代码也许就一两个小时的事,写一篇大家好接受的文章需要几天的酝酿,如果文章对您有帮助请我喝杯咖啡吧!