学习Vite笔记2

79 阅读4分钟
年份标准规范构建相关视图相关
1997HTTP/1.1
2002AJAX
2006jQuery
2009CJSNode
2010AMDnpmAngular、backbone
2011UMDBrowserify 、RequireJS 、SeaJS+spm
2012Gulp
2013SystemJSreact
2014Wbpack、BabelVue
2015HTTP/2.0 、 ES ModuleRollup
2016Yarn、GruntSvelte
2018Chrome/Safari/Firefox Support ESM
2019Snowpack
2020vite 、esbuild

无模块化标准阶段前端的开发手段

1、文件划分

将应用的状态和逻辑分散到不同文件中、然后通过HTML中的 script来一一引入

弊端

1、模块变量相当于在全局声明和定义 ,会有变量名冲突的问题 2、由于变量都定义在全局,很难知道某个变量数据哪些模块,调试不便 3、无法清晰管理模块的依赖关系和加载顺序,module-a依赖module-b,那么上述 HTML 的 script 执行顺序需要手动调整,不然可能会产生运行时错误

2、命名空间

命名空间可以解决文件划分中全局变量定义带来的 一系列问题

        // module-a.js
        window.moduleA = {
            data: "moduleA",
            method: function () { 
            console.log("execute A's method"); 
            }, 
        };
        //module-b.js
        window.moduleB = {
            data: "moduleB",
            method: function () { 
            console.log("execute B's method"); 
            }, 
         }


<!DOCTYPE html>
<html lang="en">
<head> 
<meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 
<title>Document</title>
</head> 
<body>
<script src="./module-a.js"></script>
<script src="./module-b.js"></script> <script>
// 此时 window 上已经绑定了 moduleA 和 moduleB console.log(moduleA.data); moduleB.method(); </script> 
</body> 
</html>

3、 IIFE(立即执行函数)

//module-a.js
(function(){
    let  data =  "moduleA"function method(){
    console.log(data+"execute")
    
    window.moduleA={
            method:method,
    }
})()
//module-b.js
(function(){
    let  data =  "moduleB"function method(){
    console.log(data+"execute")
    
    window.moduleB={
            method:method,
    }
})()


<!DOCTYPE html>
<html lang="en">
    <head> 
         <meta charset="UTF-8" /> 
         <meta http-equiv="X-UA-Compatible" content="IE=edge" />
         <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 
        <title>Document</title>
    </head> 
<body>
    <script>
        // 此时 window 上已经绑定了 moduleA 和 moduleB 
        console.log(moduleA.data);
        moduleB.method();
    </script> 
</body> 
</html>

三大主流模块化规范 CommonJS、AMD、ES Module

1、 CommonJS规范

// module-a.js

var  data =  "hello world";
function getData(){
    return data
}
module.exports ={
    getData
}

// index.js
const  {getData}  =  require('./module-a.js');
console.log(getData())

代码中使用 require 来导入一个模块,用module.exports来导出一个模块。实际上 Node.js 内部会有相应的 loader 转译模块代码,最后模块代码会被处理成下面这样:

(function (exports, require, module, __filename, __dirname) { 
    // 执行模块代码
    // 返回 exports 对象 
});

1、模块加载器由node.js提供,依赖了node.js本身的功能实现,比如文件系统,如果commonjs模块直接放在浏览器中是无法执行的,。当然业界产生了 browerify这种打包工具来支持打包CommonJS模块,相当于社区实现了一个第三方loader

2、CommonJs 本身约定以同步的方式进行模块加载,这种加载机制放在服务端是没问题的, 一来模块都在本地,不需要网络IO,二来只有服务启动时才会加载模块,而服务通常启动后会一直运行,所以服务的性能并没有太大的影响,但是这种机制放在浏览器端,会带来明显性能问题。它会产生大量同步的模块请求,浏览器要等待响应返回后才能继续解析模块。也就是说,模块请求会造成浏览器JS解析过程的阻塞,导致页面速度缓慢。

2、AMD规范

AMD 异步模块定义规范,模块根据这个规范,在浏览器环境中被异步加载。

    //main.js
    define(["./print"],function(printModule){
            printModule.print("main")
    })
    
    
    //print.js
    define(function(){
        return {
            print:function(msg){
            console.log("print"+msg)
            }
        }
    })
 

在AMD规范当中,我们可以通过define 去定义 或者加载一个模块,比如上面的main.js模块和print模块,如果模块需要导出一些成员需要通过在定义模块的函数中return出去,如果当前模块依赖了一些其他的模块则可以通过define的第一个参数来生命依赖(参考main),这样模块的代码执行之前浏览器会先加载依赖模块。

当然,你也可以使用require关键词来加载一个模块

// module-a.js
require(["./print.js"],function(printModule){
    printModule.print("module-a");
})

require 与 define 的区别在于前者只能加载模块,而不能定义一个模块

由于没有得到浏览器的原生支持,AMD规范需要由第三方的loader来实现,最经典的就是 requireJS库,他完整实现了AMD规范,。

CMD规范由淘宝SeaJS实现,解决的问题和AMD一样,随着社区发展,SeaJS已经被requireJS 兼容了

UMD规范 是兼容AMD和CommonJS的一个模块化方案,可以同时运行在浏览器和Node.js环境,ES Module也具备这种跨平台的能力

3、ES6 Module

ES6 Module也被称为ES Module,是由ECMAScript官方提出的模块化规范, 已经得到了现代浏览器的内置支持。 在现代浏览其中, HTML中加入含有type= “module” 属性的script标签,那么浏览器会按照ES Module规范来进行依赖加载和模块解析。

一直以CommonJS作为模块化标准的NodeJS,从12.2版本开始支持原生ES Module

    //main.js
    
    import {methodA}  from “./module-a.jsmethodA()
    
    
    //module-a.js
    
    const methodA = () => { console.log("a"); };
    export  {methodA}
     
    
    <!DOCTYPE html>
    <html lang="en">
        <head> <meta charset="UTF-8" /> 
        <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> 
        <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 
        <title>Vite App</title> </head> 
    <body> 
            <div id="root"></div> 
            <script type="module" src="./main.js"></script>
    </body> 
    </html>

如果在 node.js环境中,你可以在 package.json中声明, type:"module"属性

        { 
            "type": "module"
        }

Node.js 便会默认以 ES Module 规范去解析模块

node main.js
// 结果 为 a

在node.js中,即使在commonJS模块里面,也可以通过import方法顺利加载ES模块

    async function func() { 
        // 加载一个 ES 模块 
        // 文件名后缀需要是 mjs 
        const { a } = await import("./module-a.mjs"); console.log(a);
    }
    func();
    
    module.exports={
        func
    }