前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >前端模块化方案:前端模块化/插件化异步加载方案探索

前端模块化方案:前端模块化/插件化异步加载方案探索

原创
作者头像
周陆军博客
发布于 2023-05-14 14:40:01
发布于 2023-05-14 14:40:01
1.5K00
代码可运行
举报
文章被收录于专栏:前端博客前端博客
运行总次数:0
代码可运行

前端模块化序篇

这里建议先复习一下《再唠叨JS模块化加载之CommonJS、AMD、CMD、ES6

  • AMD: define + require
  • CMD: exports + require
  • ES6: export + import

之前由于由于ES6本身是原生语言支持实现的模块化,但是现代浏览器大多都还未支持,因此必须使用相应的transpiler工具转换成ES5的AMD,CMD模块,再借助于systemjs/requirejs等模块加载工具才能使用。

前端的模块系统经历了长久的演变,对应的模块化方案也几经变迁。

  • JavaScript打包方案从最初简单的文件合并,到AMD 的模块具名化并合并,再到browserify将CommonJS 模块转换成为浏览器端可运行的代码,打包器做的事情越来越复杂,角色也越来越重要,加载器貌似在弱化。
  • Javascript中模块加载器从最初小而简单lab.js/curl.js到RequireJS/sea.js、Browserify、Webpack和SystemJS一直在演进发展。

js语言本身并不支持模块化,同时浏览器中js和服务端nodejs中的js运行环境是不同的,如何实现浏览器中js模块化主流有两种方案:

  1. requirejs/seajs: 是一种在线“编译”模块的方案,相当于在页面上加载一个CommonJS/AMD模块格式解释器。这样浏览器就认识了define, exports,module这些东西,也就实现了模块化。
  2. browserify/webpack:是一个预编译模块打包的方案,相比于第一种方案,这个方案更加智能。由于是预编译的,不需要在浏览器中加载解释器。你在本地直接写JS,不管是AMD/CMD/ES6风格的模块化,它都能认识,并且编译成浏览器认识的JS。

到了2021,以webkit为内核的众多浏览器 都支持了es6 原生加载。本篇再来梳理一下前端模块方案。

ES6异步加载

浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script type="module" src="./foo.js"></script>

其实这个并没有什么好书的。我想说的是在代码中异步加载模块。实现cmd的效果。比如:

app/es6-file.js:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export class q {
export let counter = 3;
export function incCounter() {
  counter++;
}

浏览器加载:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script>
  import { counter, incCounter } from './lib';
  // import { counter, incCounter } from 'https://www.zhoulujun.cn/demo/lib'; 
   console.log(counter); // 3
  incCounter();
  console.log(counter); // 4
</script>

ES6模块定义名为export,提供一个静态构造函数访问器。

更多的推荐阅读

es5时代模块加载器

比较代表性的就是require.js/sea.js、Browserify

AMD阵营

超快速AMD入门 (Super Quick AMD Primer)

如果您不熟悉AMD的结构,我将为您提供您所听到的最简单的解释。 AMD是您用来异步定义和要求模块的系统。 定义返回一个或零个对象。 define和require的第一个参数通常是一个依赖项数组。 第二个参数是一个函数; define返回结果,require执行基本的回调:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// "define" a module
define(["namespace/dependencyA", "namespace/dependencyB"], function(depA, depB) {
	// Whole bunch of processing
	
	
	// Return what this module defines
	return function() {
		// Or an object, or whatever
	}
});
// "require" to use modules:
require(["namespace/dependencyC"], function(depC) {
	
	// depC can be used in here only
	// Yay for modularity!	

有数十种AMD JavaScript加载程序可用,其中最受欢迎的是RequireJS。 还有鲜为人知JavaScript加载程序,例如YepNope,script.js,LAB.js和Dojo的新本机加载程序。我最先接触的就是 curl.js,具体查看 https://github.com/cujojs/curl

Require.JS

RequireJS 是一个JavaScript 模块加载器,基于AMD 规范实现

它同时也提供了对模块进行打包与构建的工具r.js,通过将开发时单独的匿名模块具名化并进行合并,实现线上页面资源加载的性能优化。

RequireJS 与r.js 等一起提供的一个模块化构建方案。

Require是出现在2009年,它完全不同于之前的那些懒加载器,它将脚本标签写入到DOM中,监听完成的事件,然后递归加载依赖:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script src=“tools/require.js” data-main=“myAppInit.js” ></script>

...或者如下调用指明的函数名称...

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script src=“tools/require.js”></script>

再调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script>
require([‘myAppInit’, ‘libs/jQuery’], function (myApp, $) { ...
</script>

上面两个用法不建议同时使用。虽然Require存在各种特殊情况,但是其灵活性和强大性还是支持它成为浏览器端流行的加载器。

更多参看官网:https://requirejs.org/

Browserify

https://browserify.org/

Browserify允许CommonJS格式模块在前端使用,主要用于在浏览器中使用 npm 包,最终会转换为 commonJS (require) 类似方式,在浏览器使用。

它不只是一个模块加载器,而是模块捆绑器(bundler),是一个完整的代码构建段的工具,提供客户端能加载一堆代码的功能。

首先需要node和npm已经安装,获得包:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm install -g –save-dev browserify

以CommonaJS格式编写你的模块即可。然后使用下面命令捆绑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
npm install -g –save-dev browserify

它会递归以此发现entry-point中所有依赖包,然后将它们组装在一个单个文件中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script src=”bundle-name.js”></script>

对于前端,你可以最小化合并核心代码,然后让可选模块在之后需要时加载,这样即节约了带宽也不影响模块编程功能实现。

更多请参看官网:https://browserify.org/

Browserify缺点

基于流 Stream,旧时代产物,尽管也能勉强处理 css(CSS bundlers),html(brfs),但是不太友好,且年久失修

browserify必须把源代码打成bundle然后再引用,就决定了他不能直接调试源代码,这对于程序员是很不友好的。虽然我们可以使用  watchify(可以动态把你写的代码立即编译成bundle) 和 --debug 选项(给编译后的代码加上source maps)。但是依然只是近似于直接调试源代码。

SystemJS

https://github.com/systemjs/

Systemjs是一个可配置模块加载器,为浏览器和NodeJs启用动态的Es模板加载器。任何具有标准的URL都可被加载为一个模块:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script src="system.js"></script>
<script>
  // 加载相对于当前地址的url
  SystemJS.import('./local-module.js');
  // 加载绝对url的地址
  SystemJS.import('https://code.jquery.com/jquery.js');
</script>

可以加载任何类型的模块格式,并由SystemJS自动检测。

SystemJS 诞生于 2015 年,那个时候 ES Module 还未成为标准,在浏览器端只能通过 requirejs、seajs 等方案实现模块加载,随着 npm 在前端界的流行,一个项目中可能存在多种模块规范,所以我认为 SystemJS 最初诞生的目的是为了做一个通用的模块加载器,在浏览器端实现对 CommonJS、AMD、UMD 等各种模块的加载。 SystemJS 是(浏览器尚未正式支持importMap) 原生 ES Module 的替代品,ES Module 被编译成 System.register 格式之后能够跑在旧版本的浏览器当中。

在本地运行时,请确保从本地服务器或启用了本地XHR请求的浏览器运行。如果不是,将会收到一条错误消息。

对于Mac上的Chrome,您可以运行它: /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --allow-file-access-from-files &> /dev/null & 在Firefox中,这需要导航到about:config,进入security.fileuri.strict_origin_policy过滤器框并将选项切换为false。

SystemJS加载配置
baseURL

baseURL提供了一种根据一个相对地址装载模块的机制。

这使得能够从许多不同的请求URL访问相同的模块

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SystemJS.config({
  // set all requires to "lib" for library code
  baseURL: '/lib/',
   // set "app" as an exception for our application code
    paths: {
      'app/*': '/app/*.js'
    }
});
// 加载 /modules/jquery.js
SystemJS.import('jquery.js');<br>

更多的参看官方文档:https://github.com/systemjs/systemjs

es5时代模块打包方案

Grunt和Gulp属于任务流工具Tast Runner , 而 webpack属于模块打包工具 Bundler。

grunt

https://gruntjs.com/

Grunt 是老牌的构建工具,特点是配置驱动,你需要做的就是了解各种插件的功能,然后把配置整合到 Gruntfile.js 中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module.exports = function(grunt) {
  grunt.initConfig({
    // js格式检查任务
    jshint: {
      files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
      options: {
        globals: {
          jQuery: true
        }
      }
    },
    //  代码压缩打包任务
    uglify: {}
    watch: {
      files: ['<%= jshint.files %>'],
      tasks: ['jshint']
    }
  });
  grunt.initConfig({

  });
  // 导入任务插件
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadnpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  // 注册自定义任务, 如果有多个任务可以添加到数组中
  grunt.regusterTask('default', ['jshint'])
};

Grunt 缺点也是配置驱动,当任务非常多的情况下,试图用配置完成所有事简直就是个灾难;再就是它的 I/O 操作也是个弊病,它的每一次任务都需要从磁盘中读取文件,处理完后再写入到磁盘,例如:我想对多个 less 进行预编译、压缩操作,那么 Grunt 的操作就是:

读取 less 文件 -> 编译成 css -> 存储到磁盘 -> 读取 css -> 压缩处理 -> 存储到磁盘

这样一来当资源文件较多,任务较复杂的时候性能就是个问题了。

glup

https://gulpjs.com/

Gulp是后起之秀。他们的本质都是通过 JavaScript 语法实现了shell script 命令的一些功能。比如利用jshint插件 实现 JavaScript 代码格式检查这一个功能。早期需要手动在命令行中输入 jshint test.js,而 Grunt 则通过文件 Gruntfile.js 进行配置

Gulp吸取了Grunt的优点,拥有更简便的写法,通过流(Stream)的概念来简化多任务之间的配置和输出,让任务更加简洁和容易上手。

Gulp 特点是代码驱动,写任务就和写普通的 Node.js 代码一样:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// gulpfile.js
var gulp = require('gulp');
var jshint = require('gulp-jshint');
var uglify = require('gulp-uglify');

// 代码检查任务 gulp 采取了pipe 方法,用流的方法直接往下传递
gulp.task('lint', function() {
  return gulp.src('src/test.js')
    .pipe(jshint())
    .pipe(jshint.reporter('default'));
});

// 压缩代码任务
gulp.task('compress'function() {
  return gulp.src('src/test.js')
    .pipe(uglify())
    .pipe(gulp.dest('build'));
});

// 将代码检查和压缩组合,新建一个任务
gulp.task('default', ['lint', 'compress']);

再一个对文件读取是流式操作(Stream),也就是说一次 I/O 可以处理多个任务,还是 less 的例子,Gulp 的流程就是:

读取 less 文件 -> 编译成 css -> 压缩处理 -> 存储到磁盘

在 Grunt 与 Gulp 对比看来还是比较推荐 Gulp!

webpack

https://webpack.js.org/

传统的模块化基于单种编程语言,目的是为了解耦和重用,而因为前端本身的特点(需要三种编程语言配合)以及能力限制,所以不能实现跨资源加载也就难以实现组件化。

而 Webpack 打破的这种思维局限,它的 Require anything 的理念在实现模块化的同时也能够很方便实现组件化,借助 Webpack 就可以很轻松的实现这种代码组织结构:

Webpack 的特点:

  • 把一切都视为模块:不管是 CSS、JS、Image 还是 HTML 都可以互相引用,通过定义 entry.js,对所有依赖的文件进行跟踪,将各个模块通过 loader 和 plugins 处理,然后打包在一起。
  • 按需加载:打包过程中 Webpack 通过 Code Splitting 功能将文件分为多个 chunks,还可以将重复的部分单独提取出来作为 commonChunk,从而实现按需加载。

Webpack 也是通过配置来实现管理,与 Grunt 不同的时,它包含的许多自动化的黑盒操作所以配置起来会简单很多(但遇到问题调试起来就很麻烦),一个典型的配置如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module.exports = {
    //插件项
    plugins: [commonsPlugin],
    //页面入口文件配置
    entry: {
        index : './src/js/page/index.js'
    },
    //入口文件输出配置
    output: {
        path: 'dist/js/page',
        filename: '[name].js'
    },
    module: {
        //加载器配置
        loaders: [
            { test: /\.css$/, loader: 'style-loader!css-loader' },
            { test: /\.js$/, loader: 'jsx-loader?harmony' },
            { test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
            { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
        ]
    },
    //其它解决方案配置
    resolve: {
        root: '/Users/Bell/github/flux-example/src', //绝对路径
        extensions: ['', '.js', '.json', '.scss'],
        alias: {
            AppStore : 'js/stores/AppStores.js',
            ActionType : 'js/actions/ActionType.js',
            AppAction : 'js/actions/AppAction.js'
        }
    }
};

参考文章:

SystemJS 探秘 https://zhuanlan.zhihu.com/p/402155045

System.js详解 https://www.cnblogs.com/tangxing/p/7223456.html

Javascript模块加载捆绑器Browserify Webpack和SystemJS用法 https://www.jdon.com/idea/js/javascript-module-loaders.html

browserify 中文文档与使用教程 https://zhuanlan.zhihu.com/p/76604976

curl.js: Incredible AMD Loader https://davidwalsh.name/curljs

用 Browserify 替换 require.js https://blog.csdn.net/nsrainbow/article/details/52736904

前端工程化——构建工具选型:grunt、gulp、webpack https://juejin.cn/post/6844903645700423693

差点被SystemJs惊掉了下巴,解密模块加载黑魔法 https://segmentfault.com/a/1190000039305322

https://www.digitalocean.com/community/tutorials/how-to-dynamically-import-javascript-with-import-maps

从systemjs的使用学习js模块化 https://segmentfault.com/a/1190000022278429

转载本站文章《前端模块化方案:前端模块化/插件化异步加载方案探索》, 请注明出处:https://www.zhoulujun.cn/html/webfront/engineer/Architecture/8753.html

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
前端模块化-总结_前端模块化规范
先说说什么是模块化,就是将独立的功能代码封装成一个独立的文件,其他模块需要使用,在进行引用。
全栈程序员站长
2022/11/17
6640
前端模块化-总结_前端模块化规范
前端模块化详解(完整版)
上例子通过jquery方法将页面的背景颜色改成红色,所以必须先引入jQuery库,就把这个库当作参数传入。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。
小生方勤
2019/06/01
2.2K0
前端模块化详解(完整版)
在JavaScript发展初期就是为了实现简单的页面交互逻辑,寥寥数语即可;如今CPU、浏览器性能得到了极大的提升,很多页面逻辑迁移到了客户端(表单验证等),随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀,此时在JS方面就会考虑使用模块化规范去管理。 本文内容主要有理解模块化,为什么要模块化,模块化的优缺点以及模块化规范,并且介绍下开发中最流行的CommonJS, AMD, ES6、CMD规范。本文试图站在小白的角度,用通俗易懂的笔调介绍这些枯燥无味的概念,希望诸君阅读后,对模块化编程有个全新的认识和理解!
Nealyang
2019/09/29
1.3K0
前端模块化详解(完整版)
js模块化编程之彻底弄懂CommonJS和AMD/CMD!(转)
答:因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。 但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!
jojo
2019/03/12
1.7K0
前端模块化
js本身的问题: 不具有模块化的语法规则,在语言层面没有命名空间。 JavaScript 编程过程中很多时候,我们都在修改变量,在一个复杂的项目开发过程中,如何管理函数和变量作用域,显得尤为重要。
前端小tips
2021/12/10
5060
前端模块化
10分钟带你了解JavaScript模块化的前世今生!
导语:本文带你去了解一下JavaScript模块化的前世今生,包括但不限于JavaScript模块化、模块化规范、模块加载器和模块打包工具等。本文不是一个深度剖析JavaScript模块相关话题的文章,仅是一个能够让你10分钟快速了解JavaScript模块化相关知识的介绍。 作者:徐江伟--腾讯前端工程师 @IMWeb前端社区 现在JavaScript技术的发展可能会让你应接不暇。身为一线搬砖工的你对雨后春笋般的前端工具和框架越来越疲于学习和暇接。有时候,你可能不自主的问,webpack是什么玩意?bro
用户1097444
2022/06/29
3710
10分钟带你了解JavaScript模块化的前世今生!
微生活时光机:去项目中挖掘JS模块化简史
让我们把眼光投向微生活各个时期项目中的 JS 代码,看一看时代在其中投下的烙印,也许只是管中窥豹、走马观花,但通过其组织形式的异同,或可一瞥 JS 模块化层面的历史脉络。
江米小枣
2020/06/15
6460
写给前端新手看的一些模块化知识
一、 为什么需要模块化 以前没有模块化时,我们可能会按如下方式划分模块: 通过 <script> 标签引入各个文件,把每个文件看成是一个模块,每个模块的接口通常是暴露在全局作用域下的,也就是定义在 window 对象中。 <script src="module1.js"></script> <script src="module2.js"></script> <script src="module3.js"></script> 如果通过这种方式做模块化,当项目变得越来越大时,很容易造成全局变量冲突,项目也会
用户1097444
2022/06/29
3310
写给前端新手看的一些模块化知识
最流行的4种前端构建项目工具介绍
In web development we deal with a lot of small technical artifacts. You use HTML to describe page structure, CSS how to style it and JavaScript for logic. Or you can replace HTML with something like Jade, CSS with Sass or LESS, JavaScript with CoffeeScript, TypeScript and the ilk. In addition you have to deal with project dependencies (ie. external libraries and such).
小弟调调
2018/10/18
1.7K0
浅谈前端工程化的发展以及相关工具介绍
随着发展的逐步发展,作为工程师除了需要关注需要写的⻚面,样式和逻辑之外,还需要面对日益复杂 的系统性问题,比如模块化文件的组织、ES6 JS 文件的编译、打包压缩所有的 JS 代码、优化和合并图片静态资源等等事情。
茶无味的一天
2022/10/05
5390
【云+社区年度征文】webpack 学习笔记系列02-模块化开发
三大 JavaScript 主流模块规范:CommonJS、AMD 和 ES6 Module。CommonJS 和 AMD 都未统一浏览器和客户端的模块化规范。目前 Node.js 使用 CommonJS 作为官方的模块解决方案,虽然内置的模块方案促进了 Node.js 的流行,但是也为引入新的 ES Modules(ESM)标准造成了一定的阻碍,不过 Node.js 9.0+ 已经支持 ESM 语法。
CS逍遥剑仙
2020/12/23
1.2K0
「前端工程四部曲」模块化的前世今生(上)
在日益复杂和多元的Web业务背景下,前端工程化这个概念经常被提及。“说说你对Web工程化的理解?” 相信很多初学者在面试时会经常遇到,而大多数人脑子会直接浮现出 Webpack,认为工程化就是 Webpack 做的那些事情儿,当然也不能说不对,准确说 Webpack 只是工程化背景下产生的工具。
isboyjc
2022/03/28
4670
前端模块化
文章地址:https://cloud.tencent.com/developer/article/2470901
用户7194327
2024/12/03
1280
前端科普系列(3):CommonJS 不是前端却革命了前端
上一篇《前端科普系列(2):Node.js 换个角度看世界》,我们聊了 Node.js 相关的东西,Node.js 能在诞生后火到如此一塌糊涂,离不开它成熟的模块化实现,Node.js 的模块化是在 CommonJS 规范的基础上实现的。那 CommonJS 又是什么呢?
2020labs小助手
2020/07/09
1.1K0
为何webpack风靡全球?三大主流模块打包工具对比
前端的模块系统经历了长久的演变,对应的模块打包方案也几经变迁。从最初简单的文件合并,到AMD 的模块具名化并合并,再到browserify将CommonJS 模块转换成为浏览器端可运行的代码,打包器做的事情越来越复杂,角色也越来越重要。 在这样一个竞争激烈的细分领域中,webpack 以极快的速度风靡全球,成为当下最流行的打包解决方案,并不是偶然。它功能强大、配置灵活,特有的code spliting方案正戳中了大规模复杂Web 应用的痛点,简单的loader/plugin 开发使它很快拥有了丰富的配套工具
前朝楚水
2018/04/03
2K0
深入了解“前端模块化”发展体系
作为一名前端工程师,每天的清晨,你走进公司的大门,回味着前台妹子的笑容,摘下耳机,泡上一杯茶,打开 Terminal 进入对应的项目目录下,然后 npm run start / dev 或者 yarn start / dev 就开始了一天的工作。
小生方勤
2019/05/31
7560
[译]JS 模块化历史简介
对于 JavaScript 来说,模块化是一个相对现代的概念,这篇文章会带你在 JavaScript 的世界里快速浏览模块化的历史进程~
savokiss
2019/11/06
2.3K0
前端工程化之构建工具
最近,本来是想写一篇关于Promise的文章,在文章规划的时候,发现牵扯的东西有点多,需要再准备一下。
前端柒八九
2022/08/25
8260
前端工程化之构建工具
前端模块化发展史
相对于其他静态语言,JS最大缺陷就是天生不具有模块化,没有语言层面的命名空间的概念。问题如下:
娜姐
2020/09/22
8860
前端模块:CJS, AMD, UMD, ESM, System 和 IIFE
现代 Javascript 项目需要用打包工具来将小段代码编译成库或者应用程序那种更大更复杂的东西。流行的打包器有webpack、Rollup、Parcel、RequireJS 和 Browserify。它们将 JavaScript 代码转换为可以作为一个 bundle 加载的模块。
泯泷、
2024/03/13
5660
相关推荐
前端模块化-总结_前端模块化规范
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验