Modern模式引发qiankun的一场“命案”

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 前沿:文章的起源在于开发环境中使用qiankun框架,父应用加载子应用遇到一则报错 Failed to load module script: The server responded with a non-JavaScript MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec 直接的翻译意思就是加载模块脚本失败:服务器以非JavaScript MIME类型“text/html”去响应。而也是这个问题引发了我的思考,到底是什么问题导致?

微信截图_20220514143620.png


前沿:文章的起源在于开发环境中使用qiankun框架,父应用加载子应用遇到一则报错 Failed to load module script: The server responded with a non-JavaScript MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec 直接的翻译意思就是加载模块脚本失败:服务器以非JavaScript MIME类型“text/html”去响应。而也是这个问题引发了我的思考,到底是什么问题导致?


1.思考🤔


通过分析和定位,我怀疑是在是因为我在子应用的vue-cli中使用了--modern模式编译,该模式编译会构建两份js包,一个面向支持现代浏览器的原生 ES6 包,以及一个针对其他旧浏览器的包(后面会详细说明),因为我使用的浏览器支持生 ES6,同时我们知道qiankun父应用引入子应用,本质上是将html做为入口文件,并通过import-html-entry这个库去加载子应用所需要的资源列表(js和css等),应该是因为路径的问题,导致加载js错误,再加nginx配置了try_file导致返回的html。下面我跟大家聊聊关于--modernJavascript module 以及import-html-entry原理


1.1 modern模式


Modern模式究竟是个啥,其实就是vue-cli命令行工具中的一种构建模式, 更多使用可以阅读Vue CLI


微信截图_20220514143700.png


根据官网的介绍:--modern模式是使用现代模式构建应用,为现代浏览器交付原生支持的 ES2015 代码,并生成一个兼容老浏览器的包用来自动回退。


换句话讲就是该模式下Vue CLI会构建两个版本的 js 包:一个支持现代浏览器的原生 ES2015+ 的包,以及一个兼容其他旧浏览器的包。


👨‍🎓 阿呆同学:生成两套js?这样做的目的是为了啥


答案: 我们知道低版本浏览器不支持es2015中的新特征,我们往往需要通过Babel来进行转换或者加入polyfill来支持旧版本的浏览器,但随着问题也暴露了,经过babel转换的代码code往往比原本的es2015代码更加冗长。导致文件更加庞大、性能欠缺,但其实目前有不少浏览器对es2015+(既ES6)有这不错的兼容。针对以上两种情况,Vue CLI才提供了Modern模式来解决这个问题,用来生成两套js体系,如下所示一个基于该模式编译后的html入口👇


微信截图_20220514143712.png


我们可以清晰看到对js的引入,分为下面两种情况:


  • 支持原生 ES2015+ 的浏览器: js会通过<script type="module"> 加载,并且可以使用 <link rel="modulepreload"> 预加载。
  • 不支持的浏览器: js会使用 <script nomodule> 来加载,同时忽略被定义为Module的js的文件加载


换句话说: 支持 <script type="module"> 的浏览器会忽略 <script nomodule></script> 方式引入的脚本该模式下生成的 HTML 会通过 <script type="module"><script nomodule> 进行自动降级,不需要任何特殊部署配置,原生 ES2015+ 包基本上不需添加 polyfill和Babel进行编译,能为现代浏览器提供更小、很大程度上不需要再编译的代码


👨‍🎓 啊峰 :树酱君,那你上面那个截图中间(夹在nomodule和module中间)还有一段js代码,是干嘛的?


答案:之前说到现代浏览器中都可以通过 <script type="module"> 来引入来使用ES2015+语法的代码,而能识别 type=module语法的浏览器会忽略具有nomodule属性的scripts,但Safari 10.1却不同,它并不支持nomodule属性,他会同时也把nomodule的js文件下载了,但不执行,而上述这段代码则是用来修复这个问题的。


🌲  拓展:


1.2 JavaScript modules


上一小节我们介绍了关于在Vue-cli的Module模式下生成的js文件,其中构建出版本中有一个是支持现代浏览器的原生 ES2015+ 的包,本质上就是依赖 type=module去加载 ES 模块的,而这个module是“何方神圣”,让我们进一步熟悉,首先先看看这个属性各浏览器的兼容情况


微信截图_20220514143722.png


提到模块,我相信印入你眼帘的会是exportimport这些关键字,还有就是模块自己的作用域,不会污染全局变量,只在模块中,而对这些特征的使用,也是要你告诉javascript运行环境运行的脚本是普通脚本还是一个JS module

这个时候就得使用上一节我们使用到的 <script type="module"> 来做区分引入模块文件


👩‍🎓 啊琪同学: 普通脚本和JS module之间有什么区别?


答案: 区别如下👇 :

  • 同个文件引入多次 ,JS module只会执行一次,普通脚本则会根据引入次数多次执行和解析
  • 跨域的限制:JS module需要在请求头支持Access-Control-Allow-Origin: *,普通脚本不需要
  • 执行顺序:JS module脚本默认会有defer属性,延迟脚本的执行。普通js脚本默认会阻塞html解析


微信截图_20220514143734.png


当然你也可以使用<link rel="modulepreload">让浏览器对模块进行预加载和预编译:模块和它的依赖


我们来对比一下下图,同样一个文件,如果直接用Module引入,相对用babel转译过的文件引入的大小体积区别


微信截图_20220514143743.png


👨‍🎓 啊乐同学:我看有些引用的模块文件是使用 mjs 为后缀命名的哦,而不是js命名?


答案:是为了更好的区分引入的哪些文件是模块,哪些文件是常规的Javascript,也能保证你的模块可以被运行时的环境或者构建工具识别如babel和Babel等。


⏰  要注意的是,如果你是用mjs,则需要在服务器配置mime type类型,否则会报错,和文章前沿描述相似的错误Failed to load module script: The server responded with a non-JavaScript MIME type of “”. Strict MIME type checking is enforced for module scripts per HTML spec.


🌲 拓展阅读:


1.3 import-html-entry


在qiankun架构中本质上是(single-spa + sandbox + import-html-entry)用html作为入口,就是通过import-html-entry将对的子应用html中的资源进行加载,然后从入口脚本中到导出文档链接 ,看看如何使用这个库


微信截图_20220514143753.png


让我们看看这个 importHTML 方法的实现


微信截图_20220514143816.png


importHTML(url, opts = {})方法传入参数有两个


  • url:需要解析的html模版路径
  • opts:其中包括如下几个属性:fetch(用于获取远端的脚本和样式文件内容,浏览器支持的请求库)、getPublicPath(用于获取静态资源publicPath,将模板中外部资源为相对路径的转换为绝对路径)、getTemplate(支持在模板解析前,做预处理)


其中还涉及到对js、css等资源加载相关函数,在qiankun中也被使用 链接


  • getExternalStyleSheets: 用于区分css是内联还是外引(style还是link),如果是link则fetch请求资源,然后提取出内容,组成一个数组
  • getExternalScripts: 用来获取模版中出现的所有script标签,提取出内容,组成一个数组
  • execScripts: 执行所有的script中的代码(通过eval),并返回为html模板入口脚本链接entry指向的模块导出对象


微信截图_20220514143827.png


👨‍🎓 啊斌同学:那import-html-entry是如何将不同资源进行提取的?


答案: import-html-entry是使用了正则表达式来对进行 style、 link和 script等标签进行提取的,相关正则在process-tpl.js文档链接文件中,processTpl方法是解析模板的核心函数。以下是其中的部分正则


微信截图_20220514143837.png




相关文章
|
Dart JavaScript 前端开发
JavaScript & React & Ant Design & Flutter & Dart 基础学习笔记 Tree Form 组件通信 封装等
JavaScript & React & Ant Design & Flutter & Dart 基础学习笔记 Tree Form 组件通信 封装等
|
4月前
|
数据安全/隐私保护 Android开发 开发者
Flutter笔记:Widgets Easier组件库-使用隐私守卫
Flutter笔记:Widgets Easier组件库-使用隐私守卫
57 2
|
7月前
|
移动开发 前端开发 JavaScript
说一说你对混合开发(Hybrid Development)的了解。
混合开发(Hybrid App)结合Web和Native技术,实现跨平台应用开发,降低工作量和时间。使用JavaScript等Web技术提升开发效率,通过优化提供接近原生体验。混合应用可即时更新,维护灵活,成本效益高。React Native和Flutter等框架支持混合开发,丰富的社区资源加速开发进程。网易云音乐等成功案例证明其可行性。随着技术进步,混合开发前景广阔。
135 1
|
7月前
|
JavaScript 前端开发 Java
MooTools、Backbone、Sammy、Cappuccino、Knockout、JavaScript MVC、Google Web Toolkit、Google Closure、Ember、Batman 以及 Ext JS。
MooTools、Backbone、Sammy、Cappuccino、Knockout、JavaScript MVC、Google Web Toolkit、Google Closure、Ember、Batman 和 Ext JS 都是 JavaScript 框架,用于开发 Web 应用程序。它们分别提供了不同的功能和特性,以帮助开发者更高效地构建和维护 Web 应用程序。
58 2
|
JavaScript 前端开发 UED
单文件组件(Single-File Components):Vue.js开发的模块化之道
Vue.js是一款流行的JavaScript框架,以其简洁、灵活和易用而广受欢迎。其中一个Vue.js的强大功能就是单文件组件(Single-File Components),它使得Vue.js应用的开发更加模块化和可维护。在本博客中,我们将深入探讨单文件组件的概念、结构、用法,以及如何利用它们来构建清晰、可复用和高效的Vue.js应用。
218 0
|
前端开发 JavaScript
Components make life easier(组件复用让生活更美好)
Components and Props Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.
96 0
|
XML 存储 SQL
Google Architecture Components应用框架初探
Google Architecture Components应用框架初探
328 0
Google Architecture Components应用框架初探
|
前端开发
React工作55:ant design spin
React工作55:ant design spin
241 0
|
前端开发 JavaScript API
【墙裂推荐】Talking about hooks(上)
从React16.8开始,Hooks API正式被React支持,而就在最近,Vue作者尤雨溪翻译并发布了一篇自己的文章《Vue Function-based API RFC》,并在全文开头强调这是Vue 3.0最重要的RFC,并在文中提到 Function-based API 受 React Hooks 的启发,提供了一个全新的逻辑复用方案。
|
设计模式 前端开发 JavaScript
【墙裂推荐】Talking about hooks(下)
从React16.8开始,Hooks API正式被React支持,而就在最近,Vue作者尤雨溪翻译并发布了一篇自己的文章《Vue Function-based API RFC》,并在全文开头强调这是Vue 3.0最重要的RFC,并在文中提到 Function-based API 受 React Hooks 的启发,提供了一个全新的逻辑复用方案。