微信小程序开发实战——模块化

dondon961 8年前
   <h2><strong>JavaScript模块规范</strong></h2>    <p>在任何一个大型应用中模块化是很常见的,与一些更传统的编程语言不同的是,JavaScript (ECMA-262版本)还不支持原生的模块化。</p>    <p>Javascript社区做了很多努力,在现有的运行环境中,实现"模块"的效果。通行的JavaScript模块规范主要有两种:CommonJS、AMD、UMD、CMD等</p>    <h3><strong>CommonJS</strong></h3>    <p>CommonJS规范是服务器端Javascript模块规范。</p>    <p>Node.js的模块系统,就是参照CommonJS规范实现的。NPM也遵循了commonJS定义的包规范,从而形成了一套完整的生态系统。</p>    <p>CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}。require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。</p>    <p>CommonJS规范 <a href="/misc/goto?guid=4959629361768953563" rel="nofollow,noindex">http://wiki.commonjs.org/wiki...</a></p>    <pre>  <code class="language-javascript">function MathClass() {  }  MathClass.PI = 3.14;  MathClass.E = 2.72;  MathClass.prototype.add = function(a, b) {      return a+b;  };  module.exports = MathClass;</code></pre>    <pre>  <code class="language-javascript">var MathClass = require('./mathCommonJS.js');  Page( {      onLoad: function() {          console.log( "PI: " +MathClass.PI );          var mathClass = new MathClass();          console.log( "3 + 4: " +mathClass.add(3, 4) );      }  });</code></pre>    <h3><strong>AMD</strong></h3>    <p>AMD是"Asynchronous Module Definition"的缩写,意思是"异步模块定义",是前端模块规范。</p>    <p>RequireJS就是实现了AMD规范的呢。</p>    <p>AMD规范定义了一个自由变量或者说是全局变量 define 的函数。</p>    <pre>  <code class="language-javascript">define( id?, dependencies?, factory );</code></pre>    <ul>     <li> <p>id 为字符串类型,表示了模块标识,为可选参数。若不存在则模块标识应该默认定义为在加载器中被请求脚本的标识。如果存在,那么模块标识必须为顶层的或者一个绝对的标识。</p> </li>     <li> <p>dependencies ,是一个当前模块依赖的,已被模块定义的模块标识的数组字面量。</p> </li>     <li> <p>factory,是一个需要进行实例化的函数或者一个对象。</p> </li>    </ul>    <p>AMD规范 <a href="/misc/goto?guid=4958199942681410372" rel="nofollow,noindex">https://github.com/amdjs/amdj...</a></p>    <pre>  <code class="language-javascript">define('mathAMD', [], function( i ) {      function MathClass() {      }      MathClass.PI = 3.14;      MathClass.E = 2.72;      MathClass.prototype.add = function( a, b ) {          return a + b;      };      return MathClass;  });</code></pre>    <pre>  <code class="language-javascript">define( [ "mathAMD" ], function( require, exports, MathClass ) {      Page( {          onLoad: function() {              console.log( "PI: " + MathClass.PI );              var mathClass = new MathClass();              console.log( "3 + 4: " + mathClass.add( 3, 4 ) );          }      });    });</code></pre>    <h3><strong>UMD</strong></h3>    <p>CommonJS module以服务器端为第一的原则发展,选择同步加载模块。它的模块是无需包装的,但它仅支持对象类型(objects)模块。AMD以浏览器为第一(browser-first)的原则发展,选择异步加载模块。它的模块支持对象、函数、构造器、字符串、JSON等各种类型的模块,因此在浏览器中它非常灵活。这迫使人们想出另一种更通用格式 UMD(Universal Module Definition),希望提供一个前后端跨平台的解决方案。</p>    <pre>  <code class="language-javascript">(function (root, factory) {      if (typeof define === 'function' && define.amd) {                 define(['jquery'], factory);      } else if (typeof exports === 'object') {                module.exports = factory(require('jquery'));      } else {                 root.returnExports = factory(root.jQuery);      }  }(this, function ($) {          function myFunc(){};          return myFunc;  }));</code></pre>    <p>UMD的实现很简单,先判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。再判断是否支持Node.js模块格式(exports是否存在),存在则使用Node.js模块格式。前两个都不存在,则将模块公开到全局(window或global)。</p>    <pre>  <code class="language-javascript">( function( global, factory ) {          if( typeof define === 'function' && define.amd ) {          define( factory );      } else if( typeof exports === 'object' ) {          module.exports = factory();      } else {          root.returnExports = factory();      }        } ( this, function() {      function MathClass() {      }      MathClass.PI = 3.14;      MathClass.E = 2.72;      MathClass.prototype.add = function( a, b ) {          return a + b;      };      return MathClass;  }) );</code></pre>    <pre>  <code class="language-javascript">var MathClass = require( './mathUMD.js' );  Page( {      onLoad: function() {          console.log( "PI: " + MathClass.PI );          var mathClass = new MathClass();          console.log( "3 + 4: " + mathClass.add( 3, 4 ) );      }  });</code></pre>    <h3><strong>CMD</strong></h3>    <p>CMD 即Common Module Definition通用模块定义,CMD规范是国内发展出来的,就像AMD有个requireJS,CMD有个浏览器的实现SeaJS,SeaJS要解 决的问题和requireJS一样,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同。</p>    <p>Sea.js 推崇一个模块一个文件,遵循统一的写法</p>    <pre>  <code class="language-javascript">define(id?, deps?, factory)</code></pre>    <p>因为CMD推崇一个文件一个模块,所以经常就用文件名作为模块id,CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写。</p>    <p>factory是一个函数,有三个参数,function(require, exports, module)</p>    <ul>     <li> <p>require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口</p> </li>     <li> <p>exports 是一个对象,用来向外提供模块接口</p> </li>     <li> <p>module 是一个对象,上面存储了与当前模块相关联的一些属性和方法</p> </li>    </ul>    <p>CMD模块规范 <a href="/misc/goto?guid=4959629361690048187" rel="nofollow,noindex">https://github.com/cmdjs/spec...</a></p>    <pre>  <code class="language-javascript">define( "pages/module/mathCMD.js", function( require, exports, module ) {      function MathClass() {      }      MathClass.PI = 3.14;      MathClass.E = 2.72;      MathClass.prototype.add = function( a, b ) {          return a + b;      };      module.exports = MathClass;  })</code></pre>    <pre>  <code class="language-javascript">define( "pages/module/mathCMD.js", function( require, exports, module ) {      function MathClass() {      }      MathClass.PI = 3.14;      MathClass.E = 2.72;      MathClass.prototype.add = function( a, b ) {          return a + b;      };      module.exports = MathClass;  })</code></pre>    <h2><strong>微信小程序模块化机制</strong></h2>    <p>微信小程序秉承了JavaScript模块化的机制,通过module.exports暴露对象,通过require来获取对象。</p>    <h3><strong>模块开发</strong></h3>    <p>以微信小程序QuickStart为例,微信小程序模块采用CommonJS规范</p>    <p>utils/util.js</p>    <pre>  <code class="language-javascript">function formatTime(date) {    var year = date.getFullYear()    var month = date.getMonth() + 1    var day = date.getDate()      var hour = date.getHours()    var minute = date.getMinutes()    var second = date.getSeconds();        return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')  }    function formatNumber(n) {    n = n.toString()    return n[1] ? n : '0' + n  }    module.exports = {    formatTime: formatTime  }</code></pre>    <p>pages/log/log.js</p>    <pre>  <code class="language-javascript">var util = require('../../utils/util.js')  Page({    data: {      logs: []    },    onLoad: function () {      this.setData({        logs: (wx.getStorageSync('logs') || []).map(function (log) {          return util.formatTime(new Date(log))        })      })    }  })</code></pre>    <h3><strong>模块运行</strong></h3>    <p>微信小程序还是要以前端程序方式在微信浏览器中运行,由于CommonJS规范是服务器端模块规范,微信小程序运行时会自动转换为前端模块规范。</p>    <p>以微信小程序QuickStart调试时代码为例</p>    <p>utils/util.js</p>    <pre>  <code class="language-javascript">define("utils/util.js", function(require, module) {      var window = {          Math: Math      }/*兼容babel*/      , location, document, navigator, self, localStorage, history, Caches;      function formatTime(date) {          var year = date.getFullYear()          var month = date.getMonth() + 1          var day = date.getDate()          var hour = date.getHours()          var minute = date.getMinutes()          var second = date.getSeconds();          return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')      }      function formatNumber(n) {          n = n.toString()          return n[1] ? n : '0' + n      }      module.exports = {          formatTime: formatTime      }  })</code></pre>    <p>pages/log/log.js</p>    <pre>  <code class="language-javascript">define("pages/logs/logs.js", function(require, module) {      var window = {          Math: Math      }/*兼容babel*/      , location, document, navigator, self, localStorage, history, Caches;      var util = require('../../utils/util.js')      Page({          data: {              logs: []          },          onLoad: function() {              this.setData({                  logs: (wx.getStorageSync('logs') || []).map(function(log) {                      return util.formatTime(new Date(log))                  })              })          }      })  });  require("pages/logs/logs.js")</code></pre>    <p>微信小程序运行的代码与CMD模块规范基本符合。</p>    <h3><strong>使用第三方模块</strong></h3>    <p>微信小程序运行环境exports、module没有定义,无法通过require导入模块,需要对第三方模块强制导出后才能正常导入。</p>    <p>微信小程序使用Immutable.js <a href="/misc/goto?guid=4959717570536932569" rel="nofollow,noindex">https://segmentfault.com/a/11...</a></p>    <p>微信小程序使用Underscore.js <a href="/misc/goto?guid=4959717570638879872" rel="nofollow,noindex">https://segmentfault.com/a/11...</a></p>    <h2><strong>ECMAScript 6模块系统</strong></h2>    <p>ECMAScript 6,模块被作为重要组成部分加入其中。</p>    <p>ES6的模块提供了2个新的语法,分别是export和import。</p>    <p>export 模块导出</p>    <pre>  <code class="language-javascript">export let a = 1;  export class A {};  export let b = () => {};</code></pre>    <p>import 模块导入</p>    <pre>  <code class="language-javascript">import {a} from './a';  console.log(a);    import * as obj from './a';  console.log(obj.a);</code></pre>    <p>微信小程序还没实现ECMAScript 6。</p>    <p> </p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000007028276</p>    <p> </p>