前端工程化篇

1.模块化机制

1.什么是模块?

  • 将一个复杂的程序依据必定的规则(规范)封装成几个块(文件), 并进行组合在一块儿
  • 块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通讯

2.模块化的进化过程

  • 全局function模式 : 将不一样的功能封装成不一样的全局函数javascript

    • 编码: 将不一样的功能封装成不一样的全局函数
    • 问题: 污染全局命名空间, 容易引发命名冲突或数据不安全,并且模块成员之间看不出直接关系
    function m1(){
      //...
    }
    function m2(){
      //...
    }
    复制代码
  • namespace模式 : 简单对象封装css

    • 做用: 减小了全局变量,解决命名冲突
    • 问题: 数据不安全(外部能够直接修改模块内部的数据)
    let myModule = {
      data: 'www.baidu.com',
      foo() {
        console.log(`foo() ${this.data}`)
      },
      bar() {
        console.log(`bar() ${this.data}`)
      }
    }
    myModule.data = 'other data' //能直接修改模块内部的数据
    myModule.foo() // foo() other data
    复制代码

    这样的写法会暴露全部模块成员,内部状态能够被外部改写。html

  • IIFE模式:匿名函数自调用(闭包)前端

    • 做用: 数据是私有的, 外部只能经过暴露的方法操做
    • 编码: 将数据和行为封装到一个函数内部, 经过给window添加属性来向外暴露接口
    • 问题: 若是当前这个模块依赖另外一个模块怎么办?
    // index.html文件
    <script type="text/javascript" src="module.js"></script>
    <script type="text/javascript"> myModule.foo() myModule.bar() console.log(myModule.data) //undefined 不能访问模块内部数据 myModule.data = 'xxxx' //不是修改的模块内部的data myModule.foo() //没有改变 </script>
    复制代码
    // module.js文件
    (function(window) {
      let data = 'www.baidu.com'
      //操做数据的函数
      function foo() {
        //用于暴露有函数
        console.log(`foo() ${data}`)
      }
      function bar() {
        //用于暴露有函数
        console.log(`bar() ${data}`)
        otherFun() //内部调用
      }
      function otherFun() {
        //内部私有的函数
        console.log('otherFun()')
      }
      //暴露行为
      window.myModule = { foo, bar } //ES6写法
    })(window)
    复制代码

    最后获得的结果:java

    1250759466-5c1c3943927e8_fix732.png

  • IIFE模式加强 : 引入依赖jquery

这就是现代模块实现的基石webpack

// module.js文件
(function(window, $) {
  let data = 'www.baidu.com'
  //操做数据的函数
  function foo() {
    //用于暴露有函数
    console.log(`foo() ${data}`)
    $('body').css('background', 'red')
  }
  function bar() {
    //用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }
  function otherFun() {
    //内部私有的函数
    console.log('otherFun()')
  }
  //暴露行为
  window.myModule = { foo, bar }
})(window, jQuery)
复制代码
// index.html文件
  <!-- 引入的js必须有必定顺序 -->
  <script type="text/javascript" src="jquery-1.10.1.js"></script>
  <script type="text/javascript" src="module.js"></script>
  <script type="text/javascript"> myModule.foo() </script>
复制代码

上例子经过jquery方法将页面的背景颜色改为红色,因此必须先引入jQuery库,就把这个库看成参数传入。这样作除了保证模块的独立性,还使得模块之间的依赖关系变得明显。web

3. 模块化的好处

  • 避免命名冲突(减小命名空间污染)
  • 更好的分离, 按需加载
  • 更高复用性
  • 高可维护性

4. 引入多个<script>后出现出现问题

  • 请求过多

首先咱们要依赖多个模块,那样就会发送多个请求,致使请求过多编程

  • 依赖模糊

咱们不知道他们的具体依赖关系是什么,也就是说很容易由于不了解他们之间的依赖关系致使加载前后顺序出错。浏览器

  • 难以维护

以上两种缘由就致使了很难维护,极可能出现牵一发而动全身的状况致使项目出现严重的问题。 模块化当然有多个好处,然而一个页面须要引入多个js文件,就会出现以上这些问题。而这些问题能够经过模块化规范来解决,下面介绍开发中最流行的commonjs, AMD, ES6, CMD规范。

5.模块化规范

  • 1.CommonJS

    • 在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块须要提早编译打包处理。
    • 特色
      • 全部代码都运行在模块做用域,不会污染全局做用域。
      • 模块能够屡次加载,可是只会在第一次加载时运行一次,而后运行结果就被缓存了,之后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
      • 模块加载的顺序,按照其在代码中出现的顺序。
    • 基本语法
      • 暴露模块:module.exports = value或exports.xxx = value
      • 引入模块:require(xxx),若是是第三方模块,xxx为模块名;若是是自定义模块,xxx为模块文件路径
    • 模块的加载机制
      • CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
  • 2.AMD

    • AMD规范基本语法

    定义暴露模块:

    //定义没有依赖的模块
    define(function(){
       return 模块
    })
    复制代码
    //定义有依赖的模块
    define(['module1', 'module2'], function(m1, m2){
       return 模块
    })
    复制代码

    引入使用模块:

    require(['module1', 'module2'], function(m1, m2){
       使用m1/m2
    })
    复制代码

    AMD模块定义的方法很是清晰,不会污染全局环境,可以清楚地显示依赖关系。

  • 3.CMD

    • CMD规范基本语法

      定义暴露模块:

    //定义没有依赖的模块
    define(function(require, exports, module){
      exports.xxx = value
      module.exports = value
    })
    复制代码
    //定义有依赖的模块
    define(function(require, exports, module){
      //引入依赖模块(同步)
      var module2 = require('./module2')
      //引入依赖模块(异步)
        require.async('./module3', function (m3) {
        })
      //暴露模块
      exports.xxx = value
    })
    复制代码
    • 引入使用模块:
    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
    })
    复制代码
  • 4.ES6模块化

    • ES6模块化语法

      • export命令用于规定模块的对外接口,import命令用于输入其余模块提供的功能。
      /** 定义模块 math.js **/
      var basicNum = 0;
      var add = function (a, b) {
          return a + b;
      };
      export { basicNum, add };
      /** 引用模块 **/
      import { basicNum, add } from './math';
      function test(ele) {
          ele.textContent = add(99 + basicNum);
      }
      复制代码

      export default命令,为模块指定默认输出。

      // export-default.js
      export default function () {
        console.log('foo');
      }
      
      // import-default.js
      import customName from './export-default';
      customName(); // 'foo'
      复制代码
    • ES6 模块与 CommonJS 模块的差别

      它们有两个重大差别:

      • ① CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

      • ② CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

6.总结

  • CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,由于同步意味着阻塞加载,浏览器资源是异步加载的,所以有了AMD CMD解决方案。
  • AMD规范在浏览器环境中异步加载模块,并且能够并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不畅。
  • CMD规范与AMD规范很类似,都用于浏览器编程,依赖就近,延迟执行,能够很容易在Node.js中运行。不过,依赖SPM 打包,模块的加载逻辑偏重
  • ES6 在语言标准的层面上,实现了模块功能,并且实现得至关简单,彻底能够取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

2.Tree-Shaking

定义:

虽然依赖了某个模块,但其实只使用其中的某些功能。经过 tree-shaking,将没有使用的模块摇掉,这样来达到删除无用代码的目的。

具体能够看这遍文章

3.uglify原理

    1. 将code转换成AST
    1. 将AST进行优化,生成一个更小的AST
    1. 将新生成的AST再转化成code

4.webpack

5.前端微服务

多是你见过最完善的微前端解决方案

相关文章
相关标签/搜索