单页——前端模块化

?个?够复杂的?程,需要尽量将功能解耦。什么叫解耦?简单来说,需要将不同的功能分开到不同的?件中,或不同的?录结构中,形成?个个模块,模块之间通过有限的接?交互,模块内部的数据变化对外部隐藏。

在 Node.js 上,这?点表现的?常不错。Node.js 实现了 CommonJS 规范,每个 JS ?件就是?个模块,模块中的所有数据对外隐藏,仅通过 Module.exports 暴露模块内的内容。于是,模块化开发在 Node.js 环境中已不成问题。

  然?,JavaScript 语?可以运?的不仅仅是 Node.js 环境,它还有?个?常?常重要,也很常?的运?环境:客户端浏览器。JavaScript 代码量的剧增,给前端开发带来了前所未有的压?。在浏览器?侧,我们并没有像Node.js 环境那样的模块化开发能?,于是,不可避免会遇到下列问题:
  1. 如何将代码分离?
  2. 代码分离后,浏览器引? JavaScript 的时候,如何处理它们之间的依赖关系?
  3. 代码分离后,浏览器引? JavaScript 会导致额外的请求发?,如何解决效率问题?
 
第三?模块化规范
一、CommonJS
二、AMD 规范
三、CMD 规范
  然?,这?切都在慢慢的成为历史,当 ECMAScript 6 出世之后,模块化的规范,终于有了官?的标准。
 
ECMAScript 6 的模块化
它的?致规则如下:

1. ?个 JavaScript ?件,即?个模块。
2. 模块中所有的数据,除?导出,否则都是私有数据,仅能在模块中访问。
3. 使? export 关键字导出模块。
4. 使? import 关键字导?模块,导?时使?解构表达式得到导出的数据,否则,将使?导出的默认
数据。
5. ?个模块可以有多个导出。
6. 导?的模块会被缓存。
7. 在??中加载??模块使? <script type="module" src="??模块">

一、模块导出

1、导出数据,定义一个变量,将要导出的数据赋值给变量,通过export导出

// 导出数据
export var color = "red";
export let name = "Nicholas";
export const magicNumber = 7;

2、导出函数,在函数前加上export导出

// 导出函数
export function sum(num1, num2) { 
 return num1 + num2;
}

3、导出类,在类class前加上export导出

// 导出类
export class Rectangle {
 constructor(length, width) { 
 this.length = length; 
 this.width = width;
 }
}

4、也可将定义的私有函数导出,通过export对象导出

// 定义?个函数
function multiply(num1, num2) { 
 return num1 * num2;
}
// 稍后将其导出
export {
 multiply
};

以上方法最终能够得到的结果为:

{
 color: "red",
 name: "Nicholas",
 magicNumber: 7,
 sum: function,
 Rectangle: class,
 multiply: function
}

二、模块导?

  ?旦你有了包含导出的模块,就能在其他模块内使? import 关键字来访问已被导出的功能。import 语句有 2 个部分:?是需要导?的标识符,?是需导?的标识符的来源模块。
import { color, name, multiply } from "./mymodule.js";

这是导入模块的基本形式。

也可使用通配符(*)导入模块中的所有东西:

import * as all from "./mymodule.js"; // all 可以是任意的名称
// 导?后,all 是?个对象,包含模块中导出的所有内容

注意,export 与 import 都有?个重要的限制,那就是它们必须被?在其他语句或表达式的外部。

if (flag) {
 export flag; // 语法错误
}
function tryImport() {
 import flag from "./example.js"; // 语法错误
}

以上的导出导入方法就是错误的!

三、导?导出默认值

  为了简化导?导出语法,还提供了?种导?导出的?式:默认值。使?这种?式导?导出会更加简单,但要注意的是,每个模块只能导出?个默认值。

export default function(num1, num2) { 
 return num1 + num2;
}
在浏览器中使???模块,需要在html文档中引用JS程序,我们使用以下方式引用:
<script type="module" src="./app.js"></script>
只需要把 type 值从 text/javascript 改为 module 即可,但要注意的是,只有那些?持 ECMAScript6 的浏览器才可能兼容这种写法。
  下面我们来看看实例:
比如 ,我需要使用继承类的方式书写单页面,那么在主js之外新建一个base文档:
单页——前端模块化

在base文档中书写以下代码:

export default class { //登录页面的视图
    constructor({
        el
    }) { //element
        this.el = el;
        this.init();
    }
    init() {
        this.render();
        this.mounted();
        this.handle();
    }
    render() { //负责页面渲染
       
    }
    mounted() { //负责挂载其他操作

    }
    handle() { //负责事件监听的
       
    }
}

使用导出默认值的方式导出。

之后再主info.js中导入:

import Base from "./base.js";

之后就可以在info.js中使用继承了:

单页——前端模块化

 页面的渲染,事件监听都做好后就可以将其整体再次默认导出,在路由router文档中建立url接口,导入浏览器需要显示的info.js:

import info from "./view/info.js";
import addBooks from "./view/books/addBooks.js";
import updateBooks from "./view/books/updateBooks.js";

var routes = {
    ‘/info‘: {
        on: function () {
            new info({
                el: "#root"
            })
        },
        ‘/addBooks‘: function () {
            new addBooks({
                el: "#root"
            })
        },
        ‘/updateBooks‘: function () {
            new updateBooks({
                el: "#root"
            })
        }
    }
};

var router = Router(routes);

export default {
    init() {
        router.init(); //初始化路由
        location.hash = location.hash || "/info"; //有锚点就不会回到第一个锚点
    }
};

在进行new的时候,最好使用与导出的标识符相同的单词,这样import会自动出现,避免书写麻烦,或错误导致无法联通。

总的来说,浏览器的模块化开发就是将页面渲染、事件监听进行包裹,整体的导出导入,达到我们一开始说的“形成?个个模块,模块之间通过有限的接?交互,模块内部的数据变化对外部隐藏。”,虽然前期的学习这种方式容易使人混乱,但是让我们的代码实现了工程化,每一个文档都有自己独立的目的,让我们的代码维护更加方便。

 

相关推荐