前端工程化

[TOC]

什么是前端工程化

  • 前端工程化:将复杂的前端应用模块化、组件化、规范化、自动化的一个过程。

  • 解决的问题:做到多人协作的高效性;做到项目的可维护性;提高项目的开发质量;

一、模块化

模块化是针对文件层面,将大文件拆分成互相依赖的小文件,再进行统一的拼装和加载。

1.JS模块化

1.1 CommonJs

服务器端javascript模块化解决方案,适用于同步模块加载。NodeJs是CommonJs规范的主要践行者。

CommonJs用同步的方式加载模块,在服务端,模块文件都存在本地磁盘,读取非常快。但是在浏览器端,限于网络原因,更合理的方式是采用异步加载。

// 定义模块math.js
var basicNum = 0;
function add(a, b) {
  return a + b;
}
module.exports = { //在这里写上需要向外暴露的函数、变量
  add: add,
  basicNum: basicNum
}

// 引用自定义的模块时,参数包含路径,可省略.js
var math = require('./math');
math.add(2, 5);

// 引用核心模块时,不需要带路径
var http = require('http');
http.createService(...).listen(3000);

1.2 AMD 和require.js

AMD采用异步方式加载模块,模块的加载不影响后续语句的运行。所有依赖这个模块的语句,都统一定义在一个回调函数中,等到加载完成后,这个函数才会运行。require.js实现AMD规范的模块化:用require.config()指定引用路径,用define()定义模块,用require()加载模块。

首先需要引入require.js和入口文件main.js。main.js中配置require.config()并规定项目中用到的基础模块。

/** 网页中引入require.js及main.js **/
/** data-main 表示指定的js将在加载完require.js后处理,并且require会默认的将data-main指定的js作为根路径 */
<script src="js/require.js" data-main="js/main"></script>

/** main.js 入口文件/主模块 **/
// 首先用config()指定各模块路径和引用名
require.config({
  baseUrl: "js/lib",
  paths: {
    "jquery": "jquery.min",  //实际路径为js/lib/jquery.min.js
    "underscore": "underscore.min",
  }
});
// 执行基本操作
require(["jquery","underscore"],function($,_){
  // some code here
});

引用模块的时候,将模块名放在[ ]中作为require()的第一参数;如果定义的模块本身也依赖其他模块的时候,需要将它们放在[ ]中作为define()的第一参数。

// 定义math.js模块
define(function () {
    var basicNum = 0;
    var add = function (x, y) {
        return x + y;
    };
    return {
        add: add,
        basicNum :basicNum
    };
});
// 定义一个依赖underscore.js的模块
define(['underscore'],function(_){
  var classify = function(list){
    _.countBy(list,function(num){
      return num > 30 ? 'old' : 'young';
    })
  };
  return {
    classify :classify
  };
})
// 引用模块,将模块放在[]内
require(['jquery', 'math'],function($, math){
  var sum = math.add(10,20);
  $("#sum").html(sum);
});

1.3 CMD和sea.js

CMD是另一种js模块化方案,它与AMD和类似,不同点在于:AMD推崇依赖前置、提前执行,CMD推崇依赖就近、延迟执行。此规范是在推广sea.js过程中产生的。

/** AMD写法 **/
define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { 
     // 等于在最前面声明并初始化了要用到的所有模块
    a.doSomething();
    if (false) {
        // 即便没用到某个模块 b,但 b 还是提前执行了
        b.doSomething()
    } 
});
/** CMD写法 **/
define(function(require, exports, module) {
    var a = require('./a'); //在需要时申明
    a.doSomething();
    if (false) {
        var b = require('./b');
        b.doSomething();
    }
});
/** sea.js **/
// 定义模块 math.js
define(function(require, exports, module) {
    var $ = require('jquery.js');
    var add = function(a,b){
        return a+b;
    }
    exports.add = add;
});
// 加载模块
seajs.use(['math.js'], function(math){
    var sum = math.add(1+2);
});

1.4 UMD

UMD允许在环境中同时使用AMD和CommonJs规范。

(function(define) {
    define(function () {
        var helloInLang = {
            en: 'Hello world!'
        };

        return {
            sayHello: function (lang) {
                return helloInLang[lang];
            }
        };
    });
}(
    typeof module === 'object' && module.exports && typeof define !== 'function' ?
    function (factory) { module.exports = factory(); } :
    define
));

1.5 ES6 Module

ES6 的模块功能汲取了CommonJS 和 AMD 的优点,拥有简洁的语法并支持异步加载,并且还有其他诸多更好的支持。(例如导入是实时只读的。(CommonJS 只是相当于把导出的代码复制过来))

// CommonJS代码
// lib/counter.js
var counter = 1;
function increment() {
  counter++;
}
function decrement() {
  counter--;
}
module.exports = {
  counter: counter,
  increment: increment,
  decrement: decrement
};
// src/main.js
var counter = require('../../lib/counter');
counter.increment();
console.log(counter.counter); // 1
// 使用 es6 modules 通过 import 语句导入
// lib/counter.js
export let counter = 1;
export function increment() {
  counter++;
}
export function decrement() {
  counter--;
}
// src/main.js
import * as counter from '../../counter';
console.log(counter.counter); // 1
counter.increment();
console.log(counter.counter); // 2

1.5.1 ES6模块与CommonJs模块的差异
  1. CommonJS模块输出的是一个值得拷贝,ES6输出的是值得引用。
    • CommonJS模块输出的是值得拷贝,一旦输出一个值,模块内部的改变影响不到这个值。
    • ES6模块是动态引用,变量会随着模块的改变而改变。
  2. CommonJS模块是运行时加载,ES6是编译时输出接口。
    • 运行时加载:CommonJS模块就是对象;在输入时先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为"运行时加载"。
    • 编译时加载:ES6模块不是对象,而是通过export命令格式显示指定输出的代码,在import时可以指定加载某个输出值,而不是加载整个模块,这种加载称为"编译时加载"。

2.CSS的模块化

在less、sass、stylus等预处理器的import/mixin特性支持下实现、css modules。

虽然SASS、LESS、Stylus等预处理器实现了CSS的文件拆分,但没有解决CSS模块化的一个重要问题:选择器的全局污染问题;

CSS in JS是彻底抛弃CSS,使用JS或JSON来写样式。这种方法很激进,不能利用现有的CSS技术,而且处理伪类等问题比较困难;

CSS Modules 原理:使用JS 来管理样式模块,它能够最大化地结合CSS生态和JS模块化能力,通过在每个 class 名后带一个独一无二 hash 值,这样就不有存在全局命名冲突的问题了。

webpack 自带的 css-loader 组件,自带了 CSS Modules,通过简单的配置即可使用。

{
    test: /\.css$/,
    loader: "css?modules&localIde   ntName=[name]__[local]--[hash:base64:5]"
}

二、组件化

从UI拆分下来的 每个包含模板(HTML)+样式(CSS)+逻辑(JS)功能完备的结构单元,我们称之为组件。

模块化只是在文件层面上,对代码或资源的拆分;而组件化是在设计层面上,对UI(用户界面)的拆分。

其实,组件化更重要的是一种分治思想

Keep Simple. Everything can be a component.

这句话就是说页面上所有的东西都是组件。页面是个大型组件,可以拆成若干个中型组件,然后中型组件还可以再拆,拆成若干个小型组件,小型组件也可以再拆,直到拆成DOM元素为止。DOM元素可以看成是浏览器自身的组件,作为组件的基本单元。

三、规范化

规范化其实是工程化中很重要的一个部分,项目初期规范制定的好坏会直接影响到后期的开发质量。

  • 目录结构的制定
  • 编码规范
  • 前后端接口规范
  • 文档规范
  • 组件管理
  • Git分支管理
  • Commit描述规范
  • 定期CodeReview
  • 视觉图标规范

四、自动化

任何简单机械的重复劳动都应该让机器去完成。

  • 图标合并
  • 持续集成
  • 自动化部署
  • 自动化构建
  • 自动化测试
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 今天说一说前端的工程化,工程化大概包括模块化,组件化,自动化,规范化。在项目的启动之前,我们做好这一系列的工程...
    Osenki阅读 3,991评论 0 4
  • 一、模块化相关规范 1.模块化概述 传统开发模式的主要问题: 命名冲突,文件依赖 通过模块化解决上述问题: 模块化...
    coder_shen阅读 2,695评论 0 0
  • 前端工程本质上是软件工程的一种。软件工程化关注的是性能、稳定性、可用性、可维护性等方面,注重基本的开发效率、运行效...
    黎贝卡beka阅读 13,185评论 1 7
  • 1.模块化的相关规范 1.能够了解2.了解webpack3.了解使用Vue单文件组件4.能够搭建Vue脚手架5.掌...
    Scincyc阅读 3,863评论 0 0
  • 1、从 URL 输入到页面展现背后发生的事 1. 从 URL 输入到页面展现背后发生了什么事? 1.在浏览器输入 ...
    没糖_cristalle阅读 4,270评论 0 0

友情链接更多精彩内容