美文网首页
前端模块化的规范

前端模块化的规范

作者: huangyh_max | 来源:发表于2017-07-20 16:24 被阅读0次

前言

现在学习到webpack,然后重新回过头来回顾前端模块化一路发展过程定义的规范,遂写下这篇文。

一、模块化的价值

适用:功能繁多的单页面(富应用页面)
  多人合作的时候,如果都写成js文件script标签引入,那么就要关心js的顺序,避免被覆盖,以及关心变量,会不会冲突。都是全局变量,很容易冲突。这在代码量大,多人协作的情况下,很灾难。
  同时,多人协作的大项目上,js文件很多,彼此之间可能会有依赖关系,比如某个js需要先加载jquery之后才生效,这样的。js文件一多,顺序摆放就很重要,不然依赖关系就会乱掉,代码可能就不会发生作用。这样可能需要做js文件的整理记录以及位置顺序记录,这样很麻烦,对新人也不友好。浪费不必要精力。
  富应用的页面越来越多,很多页面逻辑迁移到客户端实现,前端代码越来越多。单薄的javascript,无法支撑代码组织的燃眉之急。

那么前端开发者们就开始想着使用JavaScript来模拟其他后台开发语言(例如:Java)的代码组织方式,例如package(包):将逻辑相关的代码放到同一个包中,每个包之间互不影响,即使包内变量命名相同也不冲突。使用包时,就import加载使用。
  在JavaScript中要实现类似的功能,最先想到的是函数。将所有函数抽离出来放置到一个js文件中,然后引用js文件位置,调用函数。但这个方式,还是无法解决命名冲突的问题,函数的命名还是要避免冲突。这种纯函数的方式,需要排除。
那么就来看对象的方式。
  对象封装写法一:

<script>
    /*封装为一个对象*/
    var block_mock={
          a:1,
          b:2,
          fn1:function(){
              //dosomething
          }
          fn2:function(){
             //dosomething
          }
    }
    /*调用的写法*/
    block_mock.fn1();
    block_mock.fn2();
    /*存在问题:对象内的变量可以被随意更改*/
    block_mock.a=100;
</script>

对于写法一存在的问题,我们来看写法二:

/*改进写法:写成一个立即执行函数,开始具备模块化的感觉*/
var block_mock=(function(){
      var a=1;/*局部变量,函数fn1,fn2可以使用,避免全局污染*/
      var b=2;
      function fn1(){
          //dosomething
      };
      function fn2(){
          //dosomething
      };
      return{
          fn1:fn1,
          fn2:fn2
      };
})()
/*调用的写法*/
block_mock.fn1;
block_mock.fn2;

写法二中,return是最重要一步,return出来,等于只暴露这一块,在模块外部无法修改我们没有暴露的变量和函数。
  使用对象封装的写法,我们可以看到JavaScript具备模块化的基础,来达到隔离和组装复杂代码的作用。继而,后续就发展出完善的前端模块化的规范。这些发展接着往下讲。

往下讲之前,我们先结合前面讲的这一堆,总结下模块化的实现,带给我们的预期好处:

  • 解决命名冲突问题
  • 解决繁琐的文件依赖问题,实现管理
  • 代码能分块出来,可读性提高了。想修改代码,也不用从头到尾找,只需要找到对应的模块代码修改就行
  • 提高代码复用性:比如曝光组件抽象出来实现ajax+懒加载+无限加载功能。

二、JavaScript的模块规范的发展

1、CommonJS

服务器端的JavaScript——NodeJS在服务端实现第一个模块化的规范:CommonJS。
  具体用法是:
  1、模块定义:根据CommonJS规范,一个单独的js文件就是一个模块。一个模块是一个单独的作用域,模块内部定义的变量,外部模块无法访问。
  2、模块输出:既然外部模块无法访问,就需要我模块本身主动输出,这个模块才是有意义的。按照规范使用module.exports对象。
  3、模块访问:想要加载某个模块时,使用require方法,该方法读取一个文件并执行,返回文件内部的module.exports对象。

看下面例子:
  注意:下面例子是js文件,在node端运行node+文件名,执行出结果。
  定义模块a,并输出:

var people={
    name:'hyh',
    sayName:function(){
        console.log(this.name);
    }
}
/*最重要,输出模块*/
module.exports=people;

定义模块c,并输出:

var rabot={
    name:'I am a ranbot',
    walking:function(){
        console.log('I can walking');
    }
}
module.exports=rabot;

注意:测试在a.js里写多一个rabot对象时,前面写的people对象,在执行module.exports=people时会被后面写的对象module.exports=rabot覆盖而无效,所以只能是一个js文件定义一个对象,作为一个模块。
  定义模块b,并在模块b中加载模块a和b,使用模块a和b的方法:

var p=require('./a');
var r=require('./c');
console.log(p);
p.sayName();
r.walking();
console.log('hahahahahaha');

在git后台中执行js文件b.js,得到执行结果:

Paste_Image.png

注意:见代码可知调用的js——b.js,是可以同时调用多个js来使用。

CommonJS是在NodeJS服务端运行使用的规范。使用CommonJS规范的js文件调用,是一个同步的过程,b.js执行模块内容,是需要在本地读取同个文件夹下的a.js文件和c.js文件,然后进行操作。
  CommonJS的这套过程暴露了缺点,不适合在浏览器端执行:
  1、实现模式是同步的,需要将所有require都下载下来,然后才执行p.sayName()这样具体的函数语句。如果在浏览器端做同步实现,那用户需要等到全部的js文件都下载完成之后,才可以执行操作。这样的长时间等待,显然让用户抓狂。而且浏览器端的script标签天生是异步的,所有的js文件下载时无法保证按照定义的依赖顺序来下载,那么就未免会产生错误,导致页面无效。
  2、在浏览器端,路径很难定义,不再是像服务器一样,从本地读取require('./a'),这样的路径写法。

所以,虽然CommonJS是第一个出现的JavaScript模块化规范,但却是不适用于浏览端使用的。所以还是看往后发展的AMD规范:

2、AMD规范

AMD 即Asynchronous Module Definition,中文名是异步模块定义。ADM规范其实是使用requireJS框架的模块化写法时要求的规范,是在浏览器端实现模块化开发的规范。
  具体用法有:
1、定义模块:
  define(id?[dependencies]?factory),使用定义的define函数来定义模块。
  id和dependencies为可填项,如果不设定id,加载这个模块时,就默认使用这个模块的文件命名,否则使用id。dependencies为当前这个模块会使用到的依赖。factory是必填项,为模块的主体内容。可以是函数,也可以是对象。如果是函数,只会被执行一次。如果是对象,则是这个模块的输出值。
2、加载模块:
  require([dependencies],function(){})
  require()函数接收两个参数:第一个参数是一个数组,表示所依赖的模块。第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。

** 重点:require()函数在加载依赖的函数的时候是异步加载的,这样浏览器不会失去响应,它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。**
  也就是requireJS实现满足浏览器的异步要求,避免了因为加载js文件而页面停止渲染,加载文件越多,页面失去响应时间越长的问题了。同时使用require.JS,我们开始不用操心因为js文件间的依赖关系而需要关注的文件调用顺序了。因为所有需要的依赖在数组中指定后加载,只有加载成功了,回调函数才会执行。

使用例子:

define('sayname',[],function(){
    var name = 'Byron';
    function sayName(){
        console.log(name);
    }
    return {
        sayName: sayName
    };
}());
require(['jquery','sayname'], function($,my){
  my.sayName(); 
});

$和my参数就是jquery执行后,以及sayname执行后返回的return的内容。需要在requireJS框架下执行,因为浏览器不支持require函数。
  但AMD这样的规范写法,需要我一次把所有依赖写在数组里,当依赖特别多时,就麻烦凸显。所以我们就再追求实现,在仍然支持依赖异步加载的基础上,对依赖能够按需加载。顺着这个思路,就讲到CMD规范:

3、CMD规范

相对于AMD,最重要的就是实现按需加载js文件依赖
  CMD是基于Sea.js的框架的实现的要求写法,现在Sea.js已经废弃不用了,仅作了解。
  看例子:

// 定义模块  myModule.js
define(function(require, exports, module) {
  var $ = require('jquery.js')
  $('div').addClass('active');
  var timeout=require('...')  /*依赖文件所在路径*/
  timeout.init()
});

seajs.use(['myModule.js'], function(my){
  //do something
});

看栗子可知,CMD是需要用到什么,才require什么,属于懒执行。AMD对待依赖的态度是预执行。

结语

作为前端模块化,组件化的基础,所以需要回看一路发展的规范。
  发展到现在,AMD和CMD已经实现相互支持了。在requireJS中也可以使用CMD规范的写法,实现相同的效果:例如回到顶部功能的实现

现在requireJS也是不怎么用了,发展为主流的webpack也是支持CMD和AMD规范的。

var React = require('react');

var MyComponent = React.createClass({
    // do something
});

module.exports = MyComponent;

相关文章

  • 关于前端模块化开发

    关于前端模块化开发 1 前端中有哪些模块化开发的规范以及实现方案 2 模块化的开发的好处 3 CommonJS

  • 06Vue的前端工程化

    Vue的前端工程化 一 模块化规范 1.1模块化规范举例 浏览器端JS模块化规范:AMD,CMD 服务器端JS模块...

  • 前端自动化构建工具,前端工程化,前端模块化,前端组件化

    前端自动化构建,前端工程化,模块化,组件化, 1:前端自动构建工具webpack等,是为了前端的规范化,模块化,提...

  • 前端H5 SPA选型

    前台规范 ES6语法ES6前端代码规范 前端模块化管理 webpack 构建工具 vue-loader 前端CSS...

  • 前端工程涉及八个比较大的分类:

    组织架构:前端开发规范和架构设计,包括模块化/组件化开发模型、开发框架、目录规范、组织形式等。 工程部署:有关前端...

  • 「页面架构」规范与模块化

    页面模块化 规范 在具体谈论规范的之前,可以下去查看下各大网络公司的前端开发规范(Developemnt Styl...

  • 模块化开发

    前端JS模块化开发有两大规范AMD合CMD,下面以我的理解来简单总结一下模块化开发的思想 ,以及js前端常用的AM...

  • 前端模块化(webpack)

    前言 前端模块化是一种开发管理规范,前端开发发展到现在,已经有很多成熟的构建工具可以帮助我们完成模块化的开发需求,...

  • 前端模块化类(1)

    前端模块化规范: (一) commonJs ①下载git和node ②下载使用browserify对主要文件(ap...

  • 前端模块化开发

    前端模块化开发 常见的三大模块化框架。 CommonJS: 1.根据CommonJS规范,一个单独的文件就是一个模...

网友评论

      本文标题:前端模块化的规范

      本文链接:https://www.haomeiwen.com/subject/nynacxtx.html