皮皮网
皮皮网

【恒压恒流源码】【物业网源码】【源码房卡】js import 源码

来源:网站导航收录源码 发表时间:2024-12-28 13:05:08

1.代码拆分-使用SplitChunks
2.js import 和 require的区别
3.如何基于WebComponents封装UI组件库
4.Vue组件中如何引入外部的源码js文件

js import 源码

代码拆分-使用SplitChunks

       前言

       探索代码优化的世界,最近开始接触项目优化工作,源码其中涉及三方组件的源码拆分。在未进行拆分前,源码可能存在两个场景:单一js文件过大,源码影响缓存效率;无法有效管理第三方库。源码恒压恒流源码利用`splitChunks`工具,源码可以将模块进行分割,源码并提取重复代码,源码解决上述问题。源码

       概念区分 - module、源码bundle、源码chunk

       深入理解`splitChunks`之前,源码物业网源码先梳理几个概念。源码module:模块,源码在webpack中,任何文件都可视为模块,需要配置loader将其转换为支持打包的文件。chunk:编译完成待输出时,webpack将module按特定规则组合成一个个chunk。bundle:webpack处理完chunk文件后,生成供浏览器运行的代码。

       chunk与bundle的关系

       探析chunk的构成与bundle之间的关联。chunk有两种形式:初始化(initial)chunk,即入口起点的源码房卡主chunk,包含入口起点及其依赖的所有模块;非初始化(non-initial)chunk,用于延迟加载,可能在使用动态导入或`SplitChunksPlugin`时出现。

       通过入口产生的chunk

       假设目录结构如下:index.js, another-module.js, webpack.config.js, package.json添加script配置,运行webpack并使用ndb追踪代码执行。通过命令启动浏览器,点击播放按钮执行build命令,追踪chunk到bundle的流转。

       chunk处理步骤概览

       从`Compilation`类的`seal`方法出发,首先搜集chunks,然后调用`createChunkAssets`方法生成source,为输出文件做准备;通过`compilation.emitAssets`方法记录资源信息到`compilation.assets`对象;一系列回调最终调用`onCompiled`方法,小师妹+源码将assets信息写入输出目录,生成bundle文件。

       Demo2 - 动态导入

       将`index.js`中的lodash通过`import`方式导入,动态导入返回promise,通过`then`获取导入信息。修改`webpack.config.js`入口为单个`index.js`。源码追踪显示,初始化文件新增一个名为`index`的chunk,但在模块分析中识别到`import`方式,为`index.js`模块增加了`AsyncDependenciesBlock`标记,经过处理生成一个名为`null`的chunk。

       总结:`chunk`是减肥app源码源代码中的抽象,封装定义如何将模块组写入文件,而`bundle`则是输出目录的文件。

       解决隐患 - `splitChunks`配置

       在上述示例中,存在三方模块重复引用的问题。通过简单的`optimization.splitChunks`配置,实现了lodash的抽离,降低了单个入口文件的大小。总结使用心得,`splitChunks`主要用于代码优化,针对不同场景配置`chunks`选项,如`all`、`async`、`initial`以及自定义函数,以达到高效拆分效果。

       比较`async`、`initial`、`all`的区别

       在示例中增加`another.js`,静态导入lodash,对比`async`、`all`、`initial`的不同效果。默认情况下,`initial`影响HTML文件中的脚本标签,而`async`仅针对动态导入,`all`则考虑更多场景,适合存在复用模块的情况,但需权衡动态导入及其内部依赖的抽离。

       splitChunks.cacheGroups

       在使用`splitChunks`基础上,通过`cacheGroups`实现更细粒度的代码拆分,进一步优化项目结构。

       总结

       通过`splitChunks`配置,实现三方组件的高效管理与拆分,优化代码结构与加载效率。理解模块、bundle、chunk之间的关系,以及如何利用`splitChunks`与`cacheGroups`进行代码拆分与优化,是提升项目性能的关键步骤。

js import 和 require的区别

       åœ¨ç ”究react和webpack的时候,经常看到在js文件中出现require,还有import,这两个都是为了JS模块化编程使用。CSS的是@import

       1.ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。

       Require是CommonJS的语法,CommonJS的模块是对象,输入时必须查找对象属性。

       å¤åˆ¶ä»£ç 

       å¤åˆ¶ä»£ç 

       // CommonJS模块

       let { stat, exists, readFile } = require('fs');

       // 等同于

       let _fs = require('fs');

       let stat = _fs.stat;

       let exists = _fs.exists;

       let readfile = _fs.readfile;

       å¤åˆ¶ä»£ç 

       above:整体加载fs模块(即加载fs所有方法),生成一个对象"_fs",然后再从这个对象上读取三个方法,这叫“运行时加载”,因为只有运行时才能得到这个对象,不能在编译时做到静态化。

       ES6模块不是对象,而是通过export命令显示指定输出代码,再通过import输入。

       import { stat, exists, readFile } from 'fs';

       above:从fs加载“stat, exists, readFile” 三个方法,其他方法不加载,

       2.ES6模块默认使用严格模式,无论是否声明“use strict”

       ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。

       Module 主要由两个命令组成,import和export,export用于规定模块的对外接口,import命令用于输入其他模块提供的功能

       3.Export

       æ¨¡å—是独立的文件,该文件内部的所有的变量外部都无法获取。如果希望获取某个变量,必须通过export输出,

       // profile.js

       export var firstName = 'Michael';

       export var lastName = 'Jackson';

       export var year = ;

       æˆ–者用更好的方式:用大括号指定要输出的一组变量

       å¤åˆ¶ä»£ç 

       // profile.js

       var firstName = 'Michael';

       var lastName = 'Jackson';

       var year = ;

       export { firstName, lastName, year};

       å¤åˆ¶ä»£ç 

       é™¤äº†è¾“出变量,还可以输出函数或者类(class),

       export function multiply(x, y) {

       return x * y;

       };

       è¿˜å¯ä»¥æ‰¹é‡è¾“出,同样是要包含在大括号里,也可以用as重命名:

       å¤åˆ¶ä»£ç 

       å¤åˆ¶ä»£ç 

       function v1() { ... }

       function v2() { ... }

       export {

       v1 as streamV1,

       v2 as streamV2,

       v2 as streamLatestVersion

       };

       å¤åˆ¶ä»£ç 

       Attention:

       export 命令规定的是对外接口,必须与模块内部变量建立一一对应的关系

       å¤åˆ¶ä»£ç 

       å¤åˆ¶ä»£ç 

       // 写法一

       export var m = 1;

       // 写法二

       var m = 1;

       export { m};

       // 写法三

       var n = 1;

       export { n as m};

       // 报错

       export 1;

       // 报错

       var m = 1;

       export m;

       å¤åˆ¶ä»£ç 

       æŠ¥é”™çš„写法原因是:没有提供对外的接口,第一种直接输出1,第二种虽然有变量m,但还是直接输出1,导致无法解构。

       åŒæ ·çš„,function和class的输出,也必须遵守这样的写法。

       å¤åˆ¶ä»£ç 

       å¤åˆ¶ä»£ç 

       // 报错

       function f() { }

       export f;

       // 正确

       export function f() { };

       // 正确

       function f() { }

       export { f};

       å¤åˆ¶ä»£ç 

       And:export语句输出的接口,都是和其对应的值是动态绑定的关系,即通过该接口取到的都是模块内部实时的值。

       ä½ç½®ï¼šexport模块可以位于模块中的任何位置,但是必须是在模块顶层,如果在其他作用域内,会报错。

       function foo() {

       export default 'bar' // SyntaxError

       }

       foo()

       4.Import命令

       export定义了模块的对外接口后,其他JS文件就可以通过import来加载这个模块,

       å¤åˆ¶ä»£ç 

       // main.js

       import { firstName, lastName, year} from './profile';

       function setName(element) {

       element.textContent = firstName + ' ' + lastName;

       }

       å¤åˆ¶ä»£ç 

       import命令接受一对大括号,里面指定要从其他模块导入的变量名,必须与被导入模块(profile.js)对外接口的名称相同。

       å¦‚果想重新给导入的变量一个名字,可以用as关键字,

       import { lastName as surname } from './profile';

       import后的from 可以指定需要导入模块的路径名,可以是绝对路径,也可以是相对路径, .js路径可以省略,如果只有模块名,不带有路径,需要有配置文件指定。

       æ³¨æ„ï¼Œimport命令具有提升效果,会提升到整个模块的头部,首先执行。(是在编译阶段执行的)

       å› ä¸ºimport是静态执行的,不能使用表达式和变量,即在运行时才能拿到结果的语法结构(e.g. if...else...)

如何基于WebComponents封装UI组件库

       è¿™æ˜¯ç¬¬ç¯‡ä¸æŽºæ°´çš„原创,想获取更多原创好文,请搜索公众号关注我们吧~本文首发于政采云前端博客:如何基于WebComponents封装UI组件库

前言

       ä½œä¸ºä¸€åå‰ç«¯æ”»åŸŽç‹®ï¼Œç›¸ä¿¡å¤§å®¶ä¹Ÿéƒ½åœ¨å…³æ³¨ç€å‰ç«¯çš„一些新技术,近些年来前端组件化开发已为常态,我们经常把重用性搞的模块抽离成一个个的组件,来达到复用的目的,这样减少了我们的维护成本,提高了开发的效率。但是都有一个缺点离不开框架本身,因为我们浏览器本身解析不了那些组件。那么有没有一种技术也可以达到这种效果呢?答案就是今天的主角WebComponents。

       WebComponents是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的web应用中使用它们。?目前W3C也在积极推动,并且浏览器的支持情况还不错。FireFox、Chrome、Opera已全部支持,Safari也大部分支持,Edge也换成webkit内核了,离全面支持应该也不远了。当然社区也有兼容的解决方案webcomponents/polyfills。

WebComponents三要素和生命周期Button组件示例

       é¦–先我们就从一个最简单的Button组件开始,我们可以通过在组件中传入type来改变按钮的样式,并且动态监听了数据的变化。

//html<cai-buttontype="primary"><spanslot="btnText">按钮</span></cai-button><templateid="caiBtn"><style>.cai-button{ display:inline-block;padding:4pxpx;font-size:px;line-height:1.;font-weight:;border:1pxsolid#ff;border-radius:2px;background-color:#ff;color:#fff;box-shadow:px#;}.cai-button-warning{ border:1pxsolid#faad;background-color:#faad;}.cai-button-danger{ border:1pxsolid#ff4d4f;background-color:#ff4d4f;}</style><divclass="cai-button"><slotname="btnText"></slot></div></template><script>consttemplate=document.getElementById("caiBtn");classCaiButtonextendsHTMLElement{ constructor(){ super()this._type={ primary:'cai-button',warning:'cai-button-warning',danger:'cai-button-danger',}//开启shadowdomconstshadow=this.attachShadow({ mode:'open'})consttype=thisconstcontent=template.content.cloneNode(true)//克隆一份防止重复使用污染//把响应式数据挂到thisthis._btn=content.querySelector('.cai-button')this._btn.className+=`${ this._type[type]}`shadow.appendChild(content)}staticgetobservedAttributes(){ return['type']}attributeChangedCallback(name,oldValue,newValue){ this[name]=newValue;this.render();}render(){ this._btn.className=`cai-button${ this._type[this.type]}`}}//挂载到windowwindow.customElements.define('cai-button',CaiButton)</script>三要素、生命周期和示例的解析

       Customelements(自定义元素):一组JavaScriptAPI,允许您定义customelements?及其行为,然后可以在您的用户界面中按照需要使用它们。在上面例子中就指的是我们的自定义组件,我们通过classCaiButtonextendsHTMLElement{ }定义我们的组件,通过window.customElements.define('cai-button',CaiButton)挂载我们的已定义组件。

       ShadowDOM(影子DOM):一组JavaScriptAPI,用于将封装的“影子”DOM树附加到元素(与主文档DOM分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。使用constshadow=this.attachShadow({ mode:'open'})在WebComponents中开启。

       HTMLtemplates(HTML模板)slot:template可以简化生成dom元素的操作,我们不再需要createElement每一个节点。slot则和Vue里面的slot类似,只是使用名称不太一样。

       å†…部生命周期函数

       connectedCallback:当WebComponents第一次被挂在到dom上是触发的钩子,并且只会触发一次。类似Vue中的mountedReact中的useEffect(()=>{ },[]),componentDidMount。

       disconnectedCallback:当自定义元素与文档DOM断开连接时被调用。

       adoptedCallback:当自定义元素被移动到新文档时被调用。

       attributeChangedCallback:当自定义元素的被监听属性变化时被调用。上述例子中我们监听了type的变化,使button组件呈现不同状态。虽然WebComponents有三个要素,但却不是缺一不可的,WebComponents借助shadowdom?来实现样式隔离,借助templates来简化标签的操作。

       åœ¨è¿™ä¸ªä¾‹å­ç”¨æˆ‘们使用了slot传入了俩个标签之间的内容,如果我们想要不使用slot传入标签之间的内容怎么办?

       æˆ‘们可以通过innerHTML拿到自定义组件之间的内容,然后把这段内容插入到对应节点即可。

组件通信

       äº†è§£ä¸Šé¢è¿™äº›åŸºæœ¬çš„概念后,我们就可以开发一些简单的组件了,但是如果我们想传入一些复杂的数据类型(对象,数组等)怎么办?我们只传入字符串还可以么?答案是肯定的!

传入复杂数据类型

       ä½¿ç”¨æˆ‘们上面的button,我们不仅要改变状态,而且要想要传入一些配置,我们可以通过传入一个JSON字符串

//html<cai-buttonid="btn"></cai-button><script>btn.setAttribute('config',JSON.stringify({ icon:'',posi:''}))</script>//button.jsclassCaiButtonextendsHTMLElement{ constructor(){ xxx}staticgetobservedAttributes(){ return['type','config']//监听config}attributeChangedCallback(name,oldValue,newValue){ if(name==='config'){ newValue=JSON.parse(newValue)}this[name]=newValue;this.render();}render(){ }}window.customElements.define('cai-button',CaiButton)})()

       è¿™ç§æ–¹å¼è™½ç„¶å¯è¡Œä½†å´ä¸æ˜¯å¾ˆä¼˜é›…。

       å¯¹äºŽä½¿ç”¨è€…说:我用你个组件你还要让我把所有的复杂类型都转换成字符串?

       å¯¹äºŽå¼€å‘组件者来说:我为什么要每次都JSON.parse()一下?

       HTML中会有很长的数据。

       å› æ­¤æˆ‘们需要换一个思路,我们上面使用的方式都是attribute传值,数据类型只能是字符串,那我们可以不用它传值吗?答案当然也是可以的。和attribute形影不离还有我们js中的property,它指的是dom属性,是js对象并且支持传入复杂数据类型。

//table组件demo,以下为伪代码仅展示思路<cai-tableid="table"></cai-table>table.dataSource=[{ name:'xxx',age:}]table.columns=[{ title:'',key:''}]

       è¿™ç§æ–¹å¼è™½ç„¶è§£å†³ä¸Šè¿°é—®é¢˜ï¼Œä½†æ˜¯åˆå¼•å‡ºäº†æ–°çš„问题--自定义组件中没有办法监听到这个属性的变化,那现在我们应该怎么办?或许从一开始是我们的思路就是错的,显然对于数据的响应式变化是我们原生js本来就不太具备的能力,我们不应该把使用过的框架的思想过于带入,因此从组件使用的方式上我们需要做出改变,我们不应该过于依赖属性的配置来达到某种效果,因此改造方法如下。

<cai-tablethead="Name|Age"><cai-tr><cai-td>zs</cai-td><cai-td></cai-td></cai-tr><cai-tr><cai-td>ls</cai-td><cai-td></cai-td></cai-tr></cai-table>

       æˆ‘们把属于HTML原生的能力归还,而是不是采用配置的方式,就解决了这个问题,但是这样同时也决定了我们的组件并不支持太过复杂的能力。

状态的双向绑定

       ä¸Šé¢è®²äº†æ•°æ®çš„单向绑定,组件状态页面也会随之更新,那么我们怎么实现双向绑定呢?

       æŽ¥ä¸‹æ¥æˆ‘们封装一个input来实现双向绑定。

<cai-inputid="ipt":value="data"@change="(e)=>{ data=e.detail}"></cai-input>//js(function(){ consttemplate=document.createElement('template')template.innerHTML=`<style>.cai-input{ }</style><inputtype="text"id="caiInput">`classCaiInputextendsHTMLElement{ constructor(){ super()constshadow=this.attachShadow({ mode:'closed'})constcontent=template.content.cloneNode(true)this._input=content.querySelector('#caiInput')this._input.value=this.getAttribute('value')shadow.appendChild(content)this._input.addEventListener("input",ev=>{ consttarget=ev.target;constvalue=target.value;this.value=value;this.dispatchEvent(newCustomEvent("change",{ detail:value}));});}getvalue(){ returnthis.getAttribute("value");}setvalue(value){ this.setAttribute("value",value);}}window.customElements.define('cai-input',CaiInput)})()

       è¿™æ ·å°±å°è£…了一个简单双向绑定的input组件,代码中get/set和observedAttributes/attributeChangedCallback前者是监听单个,后者可以监听多个状态改变并做出处理。

       è¿™é‡Œé¢æ ¸å¿ƒçš„一步是我们监听了这个表单的input事件,并且在每次触发input事件的时候触发自定义的change事件,并且把输入的参数回传。

       é‚£æˆ‘们应该怎么使用呢?以vue为例子,vue的双向绑定v-model其实是一个语法糖,我们的组件则没有办法使用这个语法糖,与v-model不简化写法类似<cai-input:value="data"@change="(e)=>{ data=e.detail}">

封装我们自己的组件库设计目录结构

       ç¬¬ä¸€æ­¥ï¼šè¦æœ‰ä¸€ä¸ªä¼˜é›…的组价库我们首先要设计一个优雅的目录结构设计目录结构如下

.└──cai-ui├──components//自定义组件|├──Button||├──index.js|└──...└──index.js.//主入口独立封装

       ç‹¬ç«‹å°è£…我们的组件,由于我们组件库中组件的引入,我们肯定是需要把每个组件封装到单独文件中的。

       åœ¨æˆ‘们的Button/index.js中写入如下:

(function(){ consttemplate=document.createElement('template')template.innerHTML=`<style>/*css和上面一样*/</style><divclass="cai-button"><slotname="text"></slot></div>`classCaiButtonextendsHTMLElement{ constructor(){ super()//其余和上述一样}staticgetobservedAttributes(){ return['type']}attributeChangedCallback(name,oldValue,newValue){ this[name]=newValue;this.render();}render(){ this._btn.className=`cai-button${ this._type[this.type]}`}}window.customElements.define('cai-button',CaiButton)})()

       å°è£…到组件到单独的js文件中

全部导入和按需导入

       æ”¯æŒå…¨éƒ¨å¯¼å…¥ï¼Œæˆ‘们通过一个js文件全部引入组件

//index.jsimport'./components/Button/index.js'import'./components/xxx/xxx.js'

       æŒ‰éœ€å¯¼å…¥æˆ‘们只需要导入组件的js文件即可如import'cai-ui/components/Button/index.js'

自定义配置主题

       æ”¯æŒä¸»é¢˜è‰²å¯é…ç½®æˆ‘们只需把颜色写成变量即可,改造如下:

(function(){ consttemplate=document.createElement('template')template.innerHTML=`<style>/*多余省略*/.cai-button{ border:1pxsolidvar(--primary-color,#ff);background-color:var(--primary-color,#ff);}.cai-button-warning{ border:1pxsolidvar(--warning-color,#faad);background-color:var(--warning-color,#faad);}.cai-button-danger{ border:1pxsolidvar(--danger-color,#ff4d4f);background-color:var(--danger-color,#ff4d4f);}</style><divclass="cai-button"><slotname="text"></slot></div>`//后面省略...})()

       è¿™æ ·æˆ‘们就能在全局中修改主题色了。案例地址

在原生、Vue和React中优雅的使用在原生HTML中应用:<scripttype="module">import'//cai-ui';</script><!--or--><scripttype="module"src="//cai-ui"></script><cai-buttontype="primary">点击</cai-button><cai-inputid="caiIpt"></cai-button><script>constcaiIpt=document.getElementById('caiIpt')/*获取输入框的值有两种方法*1.getAttribute*2.change事件*/caiIpt.getAttribute('value')caiIpt.addEventListener('change',function(e){ console.log(e);//e.detail为表单的值})</script>在Vue2x中的应用://html<cai-buttonid="btn"></cai-button><script>btn.setAttribute('config',JSON.stringify({ icon:'',posi:''}))</script>//button.jsclassCaiButtonextendsHTMLElement{ constructor(){ xxx}staticgetobservedAttributes(){ return['type','config']//监听config}attributeChangedCallback(name,oldValue,newValue){ if(name==='config'){ newValue=JSON.parse(newValue)}this[name]=newValue;this.render();}render(){ }}window.customElements.define('cai-button',CaiButton)})()0在Vue3x中的差异:

       åœ¨æœ€è¿‘çš„Vue3中,Vue对WebComponents有了更好的支持。Vue?在CustomElementsEverywhere测试中获得了%的完美分数。但是还需要我们做出如下配置:

跳过Vue本身对组件的解析

       customElements的风格和Vue组件很像,导致Vue会把自定义(非原生的HTML标签)标签解析并注册为一个Vue组件,然后解析失败才会再解析为一个自定义组件,这样会消耗一定的性能并且会在控制台警告,因此我们需要在构建工具中跳过这个解析:

//html<cai-buttonid="btn"></cai-button><script>btn.setAttribute('config',JSON.stringify({ icon:'',posi:''}))</script>//button.jsclassCaiButtonextendsHTMLElement{ constructor(){ xxx}staticgetobservedAttributes(){ return['type','config']//监听config}attributeChangedCallback(name,oldValue,newValue){ if(name==='config'){ newValue=JSON.parse(newValue)}this[name]=newValue;this.render();}render(){ }}window.customElements.define('cai-button',CaiButton)})()1

       ç»„件的具体使用方法和Vue2x类似。

在React中的应用//html<cai-buttonid="btn"></cai-button><script>btn.setAttribute('config',JSON.stringify({ icon:'',posi:''}))</script>//button.jsclassCaiButtonextendsHTMLElement{ constructor(){ xxx}staticgetobservedAttributes(){ return['type','config']//监听config}attributeChangedCallback(name,oldValue,newValue){ if(name==='config'){ newValue=JSON.parse(newValue)}this[name]=newValue;this.render();}render(){ }}window.customElements.define('cai-button',CaiButton)})()2

       WebComponents触发的事件可能无法通过React渲染树正确的传递。你需要在React组件中手动添加事件处理器来处理这些事件。在React使用有个点我们需要注意下,WebComponents组件我们需要添加类时需要使用class而不是className

总结现阶段的劣势

       çœ‹å®Œè¿™ç¯‡æ–‡ç« å¤§å®¶è‚¯å®šä¼šè§‰å¾—为什么WebComponents实现了一份代码多个框架使用,却还没有霸占组件库的市场呢?我总结了一下几点:

       æ›´åŠ åå‘于UI层面,与现在数据驱动不太符,和现在的组件库能力上相比功能会比较弱,使用场景相对单一。

       å…¼å®¹æ€§è¿˜æœ‰å¾…提升:这里不仅仅指的是浏览器的兼容性,还有框架的兼容性,在框架中使用偶尔会发现意外的“惊喜”,并且写法会比较复杂。

       å¦‚果不借助框架开发的话,写法会返璞归真,HTMLCSSJS会糅合在一个文件,htmlCSS都是字符串的形式,没有高亮,格式也需要自己调整,对于开发人员

Vue组件中如何引入外部的js文件

       在Vue组件中引入外部js文件,无需依赖npm安装,以下是六种不同方法供参考。

       方法一:在Vue项目的index.html中使用全局引入,如:

       缺点:所有组件均加载该js插件,而非仅在特定组件中使用。

       方法二:对于本地静态文件,使用import导入。

       缺点:仅适用于本地静态文件,远程js文件不可直接导入。

       方法三:在Vue组件加载完毕后,手动操作DOM插入js插件。

       优势:仅当前组件加载该插件,避免全局引入带来的问题。

       方法四:利用render方法,编写自定义组件。

       实现:在页面中调用自定义组件,引入外部js文件。

       方法五:高阶技巧,将方法三包装为js插件,使用Promise管理加载状态。

       实现:js加载成功时调用resolve,加载失败时调用reject。

       方法六:动态替换js文件,通过包装importJs.js插件。

       实现:使用动态导入机制,根据需求加载不同的js文件。

相关栏目:娱乐