zoukankan      html  css  js  c++  java
  • 前端工程化1-模块

    写在前面

    最近一两个月在思考未来自己的职业规划,团队内小伙伴陆陆续续走的走,来的来。大家都处于职业的焦虑之中,我一直都很想进一家大厂,目前仍然没有机会,也没有能力能够进去。毕业已经3年整了,不能继续说打好基础,把所有的业余精力都放在js基础学习上。应该着眼于自己的优势和有竞争力的方面努力,这样才不容易被取代。所以前端工程化是必不可少的深挖领域,以后博客都会产出工程化相关的文章。

    前端工程化是一个很大的议题,基本上工程化体系是离不开webpack和js模块。首先先从模块说起,面试的时候免不了会被问到几个模块相关的问题。例如

    1. 说一说模块化的好处?前端模块化解决了哪些问题?
    2. 说一说es module和commonjs module的区别?commonjs require函数的工作原理?
    3. module.export、export 与 export defalut 有什么区别?
      ...
      ...
      ...

    js模块介绍

    模块化就是将一个大文件拆分成相互依赖的小文件,再通过打包工具进行统一的拼装和加载。有人会问了模块化的好处是什么,或者说为什么要做模块化?其实这样做的好处可以从两个角度分析

    1. 前端开发人员可以遵循统一标准规范,多人协作同时进行系统开发,加快开发速度
    2. 代码稳定,模块间相互隔离,相互独立,互不影响。解决命名冲突等。
    3. 代码可读性:代码被分割成细小的整体,可读性变强。
    4. 可复用性:模块间相互独立,互不影响,可复用性高。
    5. 可维护性:依赖关系明确清晰,易于维护。

    js的模块标准有两种 commonjs module和es6之后开始支持的es module。node应用采用的commonjs module规范,web应用一般采用es module规范。

    commonjs module和es module

    commonjs module简称cjs,es module简称mjs,下同。
    示例源码

    cjs

    • require函数,用于导入
    • module.exports或者exports变量,用于导出
      例如
    //add.js
    function add(a,b){
      return a+b;
    }
    
    module.exports={add};
    
    //index.js
    const {add} =require('./add');
    add(1,2);
    

    模块的导入导出需要注意的问题

    • exports不能直接对其赋值,例如下代码是不会正常导出的
    //add.js
    function add (a,b){
      return a+b
    };
    exports={add};
    
    • 在同一个文件中,exports和module.exports尽量不要同时使用,例如下代码
    exports.add=function(a,b){
      return a+b;
    }
    module.exports={
      name:'jack'
    }
    
    • module.exports或者exports尽量放在文件的末尾,可以提高代码的可读性
    • require函数是CommonJS规范之中,用来加载其他模块的函数。它其实不是一个全局函数,而是指向当前模块的module.require函数,而后者又调用Node的内部命令Module._load。
    • require函数有缓存,当第一次加载某个模块时,node会缓存该模块,以后再加载该模块时,就直接从缓存中取出该模块的module.exports属性
    • require函数的加载规则,根据参数的不同格式,require命令去不同路径寻找模块文件
      1. 如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。
      2. 如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件
      3. 如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)
      4. 如果参数字符串不以“./“或”/“开头,而且是一个路径,比如require('example-module/path/to/file'),则将先找到example-module的位置,然后再以它为参数,找到后续路径
      5. 如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析
      6. 如果想得到require命令加载的确切文件名,使用require.resolve()方法

    mjs

    • import命令用于导入
    • export(命名导出)或者export default(默认导出)命令用于导出
      例如
    //add.js
    function add(a,b){
      return a+b;
    }
    export {
      add
    }
    
    // index.js
    import {add} from './add';
    add(1,2);
    

    到这里可以思考一个问题,
    我们可以直接export default一个字面量对象吗?在导入的时候能解构到想要到值吗?来看下下面的代码

    //add.js
    function add(a,b){
      return a+b
    }
    export default {
      add
    }
    
    // index.js
    import {add} from "./add.mjs";
    add(1,2);
    

    上面的代码其实是会报错的,报错的文件是index.js,export default {...}这种写法是没有不会报错的,只是不规范。那为啥index.js中解构的形式导出会报错呢,我们先来看看报的什么错误

    import { add } from './add.mjs';
             ^^^
    SyntaxError: The requested module './add.mjs' does not provide an export named 'add'
    

    看报错信息说没有请求的模块中export没有提供add。既然这样,我们就直接给请求到的add模块赋一个值,然后我们打印出来看看究竟是一个什么东西,add模块的代码不变,index.js修改后的代码如下

    // index.js
    import util from "./add.mjs";
    cosnole.log(util);
    
    

    运行之后我们可以看到控制台输出

    > [lzm]% node index.mjs
    > { add: [Function: add] }
    

    我们可以看到是一个拥有add函数的对象,按理说应该可以结构赋值才对呀。其实从上面的报错能看出来,import的解构会自动匹配export,如果没有则会报错。

    可以再思考一个问题,在node环境下可以使用es module吗?
    答案是可以的,以上例子都是在node环境下运行的。现在node已经支持es module规范了,参考官网的解释
    esm_enabling

    commonjs module和es module的异同

    最本质的区别是模块依赖解析的时机不同

    运行时和编译时
    所谓运行时即代码已经解析成机器可以识别的形态,代码可以直接运行的阶段。而编译时即我们的js代码解析成机器可以识别的形态的过程。

    • cjs在运行时解析模块依赖,mjs在编译时解析模块依赖
      cjs的加载机制是输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值

    cjs是在运行时才确定引入, 然后执行这个模块, 相当于是调用一个函数, 返回一个对象.
    mjs是语言层面的, 导入导出是声明式的代码集合. 声明式的意思就是说, 直接利用关键字声明说我要导入/导出一个模块啦, 而不是粗鄙(节目效果)地将一个对象赋值给一个变量

    如何处理模块的循环加载?
    CommonJS模块的重要特性是加载时执行,即脚本代码在require的时候,就会全部执行,CommonJS的做法是,一旦出现某个模块被"循环加载",就只输出已经执行的部分,还未执行的部分不会输出。require函数第一次导入时会自动缓存结果,第二次再导入模块时会直接给到结果。
    import不会去执行模块,而是只生成一个引用。等到真的需要用到时,再到模块里面去取值

    es 模块并没有帮我们解决循环依赖,所以循环依赖问题需要开发者自己解决。那么问题又来了,在node环境下我们在使用es 模块的时候是不是会出现死循环呢?我们先来看个例子

    //add.mjs
    import { log } from './log.mjs';
    function add(a, b) {
      log(a, b)
      return a + b;
    }
    export {
      add
    }
    
    //log.mjs
    import { add } from './add.mjs';
    add(1, 2);
    function log() {
      console.log(...arguments);
    };
    export {
      log
    }
    

    在node环境下运行

    node add.mjs
    

    我们会发现控制台不会无限打印,只打印出一次1 2。

    其他模块

    除了commonjs module和es module模块,还有amd,cmd和umd等等。amd示例实现依赖于库requirejs,cmd示例实现依赖于库seajs

    示例源码

    参考

  • 相关阅读:
    Java Web 网络留言板2 JDBC数据源 (连接池技术)
    Java Web 网络留言板3 CommonsDbUtils
    Java Web ConnectionPool (连接池技术)
    Java Web 网络留言板
    Java Web JDBC数据源
    Java Web CommonsUtils (数据库连接方法)
    Servlet 起源
    Hibernate EntityManager
    Hibernate Annotation (Hibernate 注解)
    wpf控件设计时支持(1)
  • 原文地址:https://www.cnblogs.com/xingguozhiming/p/14915305.html
Copyright © 2011-2022 走看看