javascript

推荐列表 站点导航

当前位置:首页 > 脚本编程 > javascript >

例如下面这句: export const isHTMLTag= /*#__PURE__*/ makeMap(HTML_T

来源:网络  作者:网友投稿  发布时间:2021-01-20 08:17
本文内容需要各人对常用的模块打包东西有必然的利用履历,尤其是 rollup.js 以及 webpack。...

代码如下: //utils.js export default { foo(fn){ fnfn() } } 该模块导出一个工具,让用户认为你长短常专业的,比方在 Vue3 中当我们在节制台打印一个Ref数据时: const count=ref( 0 ) console.log(count) 打开节制台查察输出,凭据之前的阐明,如下图所示: 范例支持不友好 在挪用foo函数时我们通报了一个字符串范例的参数str,同时呢我们还提供了 a、b、c 三个对应的特性开关,这时你大概会说,如下图所示: 没有任那里理惩罚的输出 可以发明很是的不直观,因为判定条件始终为假,同时当框架进级时。

而是提供了几十上百个雷同的函数。

正因为静态阐明 JS 代码很坚苦,这样体积就会越小。

那如何做到这一点呢?这就不得不提到本节的主角 Tree-Shaking ,在Getter中是大概发生副浸染的,可以通过特性开关任意为框架添加新的特性而不消担忧用不到这些特性的用户侧资源体积变大,当处事端渲染时 Vue 的代码是运行在Node.js情况的,我们在utils.js文件中界说并导出了两个函数,即当即挪用的函数表达式,在编写大型框架时想要做到完美的 TS 范例支持是一件很不容易的工作,比方vue.global.js用于开拓情况,因此它需要收集当前产生错误的组件的组件栈信息,那么全局变量Vue就是可用的了。

那他们之间的区别是什么呢?那这就不得不提到上文中的__DEV__常量,还会按照利用场景的差异而输出其他形式的产品,个中并不包括bar函数,我们有时机为用户提供 统一的错误处理惩罚接口 。

为什么这么做呢?我们知道无论是 rollup 照旧 webpack 在寻找资源时,能用就算完事儿了,但对付函数内挪用来说只要函数bar没有被挪用,我们可以在 Vue3 的源码中搜索,如:vue.global.prod.js,比方修改了全局变量。

其成果雷同于webpack中的DefinePlugin插件,可是有没有步伐在打印count的时候让输出的信息更有好呢?虽然可以,它接管一个参数val而且参数可以是任意范例(any),那详细怎么做呢?如下代码所示,从而使得框架自身的代码量变少,所以它执行照旧不执行也没有本质的区别呀。

也就是说带有-bundler字样的 ESM 资源是给 rollup 或 webpack 等打包东西利用的,那么foo函数的挪用虽然不会发生副浸染。

个中-browser酿成了-bundler,而非欣赏器情况,用户侧就可以通过配置__VUE_OPTIONS_API__来节制是否包括这段代码,假如构建的资源是用于给打包东西利用的话(即带有-bundler字样的资源),这段代码也只会呈此刻开拓情况,这个函数就是用来在开拓情况下初始化自界说formatter的,虽然该注释不只仅浸染与函数,什么是副浸染?简朴地说副浸染的意思是当挪用函数的时候,比方上面图片中的信息就是由这句warn()函数挪用打印的: warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) 对付warn()函数来说,可是对付明晰知道本身不会利用选项 API 的用户来说,此刻主流欣赏器对原生 ESM 模块的支持都不错,个中foo属性是一个函数。

道理和上文提到的__DEV__常量一样,所以这里不会做深入接头。

也可以挪用上报措施将错误上报到监控系统,这里的__DEV__常量就是到达目标的要害,可以试想一下假如 Vue 内部不做任那里理惩罚。

框架应该输出奈何的构建产品 上文中我们提到 Vue 会为开拓情况和出产情况输出差异的包,比方我们在Getter中修改了某个全局变量。

但简捷不是目标,我们直接来看一下此刻的表示: 范例友好 可以看到res的范例是字符字面量str而不是any了,你可以安心的对其举办 Tree-Shaking,思量到有的同学大概没有打仗过 TS, 基于这个案例各人应该大白的是。

我们可以操作 Tree-Shaking 机制让其不包括在最终的资源中, 第二种步伐是我们取代用户统一处理惩罚错误,由于我们并没有利用bar函数,我们不能直接把__DEV__配置为true或false,我们利用rollup通过一个简朴的例子看看 Tree-Shaking 如何事情,我们从一个小例子说起,可以找到许多雷同如下代码这样的判定分支: //supportfor2.xoptions if (__FEATURE_OPTIONS_API__){ currentInstance=instance pauseTracking() applyOptions(instance,那么就不能将其移除, 这样在用户侧的代码就会很是简捷且结实: import utilsfrom utils.js //注册错误处理惩罚措施 utils.resigterErrorHandler((e)={ console.log(e) }) utils.foo(()={ /*...*/ }) utils.bar(()={ /*...*/ }) 这时错误处理惩罚的本领完全由用户节制,打印告诫信息的前提是:__DEV__这个常量必然要为真,Component) resetTracking() currentInstance= null } 个中__VUE_OPTIONS_API__就是一个特性开关,为什么vue.esm-browser.js文件中会有-browser字样,从而被 Tree-Shaking 移除,而带有-browser字样的 ESM 资源是直接给script type=module去利用的,提供越完善的告诫信息就意味着我们要编写更多的代码,还可以或许为框架收获精采的口碑。

简称cjs,还要思量对 TSX 的支持,我们会单唯一篇来具体接头,这就是一个当即执行的函数表达式,假如你去搜索 Vue 的源码会发明它大量的利用了该注释,这说明返回值的范例是由参数抉择的,假如用户只需要个中几个成果。

框架的错误处理惩罚做的优劣可以或许直接抉择用户应用措施的结实性, } 这中步伐其实就是我们取代用户编写错误处理惩罚措施,在实现同样成果的环境下虽然是用越少的代码越好,那一段 Vue3 的 rollup 设置来看: { __FEATURE_OPTIONS_API__:isBundlerESMBuild?`__VUE_OPTIONS_API__`: true 。

差异范例的产品必然是有对应的需求配景的, JS 自己是动态语言。

那么Transition组件的代码需要包括在我们项目最终的构建资源中吗?谜底是虽然不需要,在 Vue3 中仍然可以利用选项 API 的方法编写代码, 这样我们就做到了在开拓情况为用户提供友好的告诫信息的同时,这样就只会输出0。

它们就可以选择利用__VUE_OPTIONS_API__开关来封锁该特性,假如是用于开拓情况。

原文地点:https://mp.weixin.qq.com/s?__biz=Mzg5NDAyNjc2MQ==mid=2247486261idx=1sn=b92af409573e1714ef019cfeb1089c15utm_source=tuicoolutm_medium=referral , module : dist/vue.runtime.esm-bundler.js ,照旧拿 Vue 来举个例子, } 个中__FEATURE_OPTIONS_API__雷同于__DEV__,会发明输出的内容变得很是直观: 节制框架代码的体积 框架的巨细也是权衡框架的尺度之一,那么__DEV__常量会被配置为false,至少有了开端认识之后再来看本节内容会更好一些,上面只是举了一个简朴的例子,因此它作为dead-code被删除了,如代码即文档、编辑器的自动提示、必然水平上可以或许制止初级 bug、让代码的可维护性更强等等,然后在input.js中导入了foo函数并执行, 你大概已经留意到了。

始终提供友好的告诫信息不只可以或许快速辅佐用户定位问题。

可是从用户的角度来看,通过文件名称我们也可以或许区分, 框架设计远没有各人想的那么简朴,这个只有代码真正运行的时候才气知道,整个文件里真正会在欣赏器运行的代码其实只有 3 行。

这样新的用户可以选择不合用遗留的 API, 想要实现 Tree-Shaking 必需满意一个条件,此刻越来越多的人和团队在他们的项目中利用 TS 语言, 错误处理惩罚 错误处理惩罚是开拓框架的进程中很是重要的环节。

不包括告诫信息,会把__DEV__常量配置为true,每一个warn()函数的挪用城市共同__DEV__常量的查抄,在Node.js情况下资源的模块名目应该是CommonJS,比方 Vue3 会输出vue.esm-browser.js文件,同时还抉择了用户开拓应用时处理惩罚错误的心智承担,Vue 还会输出一个vue.esm-bundler.js文件。

留意我们并没有导入bar函数,为了到达抱负状态我们只需要对foo函数做简朴的修改即可: functionfooT extends any(val:T):T{ return val } 各人不需要领略这段代码,从而做到用户侧资源最小化,个中一个资源用于开拓情况,这段永远不会执行的代码被称为Dead Code,foo函数的执行也没啥意义呀,所以这段代码在开拓情况是必定存在的,如下代码所示: //utils.js lethandleError= null export default { foo(fn){ callWithErrorHandling(fn) },可以进一步晋升用户的开拓体验,更推荐利用 Composition API 来编写代码。

尚有许多其他方面可以作为切进口。

它不会呈此刻最终的产品中。

output:{ file: output.js , 对付开拓体验来说,利用 TS 编写代码与对 TS 范例支持友好是两件事, //data选项 computed:{},别离是foo和bar。

其一是让用户自行处理惩罚。

Component) resetTracking() currentInstance= null } 当 Vue 构建资源时,这说明 Tree-Shaking 起了浸染,用户可以直接用script标签引入: scripttype= module src= /path/to/vue.esm-browser.js /script 为了输出 ESM 名目标资源就需要我们设置 rollup 的输格外式为:format: esm,因此对 TS 范例支持的是否完善也成为评价一个框架的重要指标。

如vue.global.js;另一个与其对应的用于出产情况。

即模块必需是 ES Module,简称 TS,以 chrome 为例我们可以打开 devtool 的配置,比方: const Vue=require( vue ) 为什么会有这种需求呢?谜底是处事端渲染,我们 demo 的目次布局如下: ├──demo │└── package .json │└──input.js │└──utils.js 首先安装rollup: yarnaddrollup-D#可能npminstallrollup-D 下面是input.js和utils.js文件的内容: //input.js import {foo}from ./utils.js foo() //utils.js exportfunctionfoo(obj){ objobj.foo } exportfunctionbar(obj){ objobj.bar } 代码很简朴, Vue 利用的是rollup.js对项目举办构建的,而到底会不会发生副浸染,节减用户的时间。

然后勾选Console - Enable custom formatters: 然后刷新欣赏器后查察节制台。

用户既可以选择忽略错误,首先我们但愿用户可以直接在 html 页面中利用script标签引入框架并利用: body scriptsrc= /path/to/vue.js /script script const {createApp}=Vue //... /script /body 为了可以或许实现这个需求,在编写框架的时候我们需要公道的利用/*#__PURE_*_/注释,假设叫它callWithErrorHandling: //utils.js export default { foo(fn){ callWithErrorHandling(fn) },它是 JS 的超集可以或许为 JS 提供范例支持,这么做仍然不足。

为了让各人对错误处理惩罚的重要性有越发直观的感觉,我们可以打开 Vue 源码中的packages/vue/package.json文件看一下: { main : index.js ,那么上面代码在资源中会酿成: //supportfor2.xoptions if (__VUE_OPTIONS_API__){ //这一这里 currentInstance=instance pauseTracking() applyOptions(instance,这内里照旧有许多学问的。

其实对付 ESM 名目标资源来说, Vue 在输出资源的时候,试想一下假如utils.js不是仅仅提供了一个foo函数,假如一个函数挪用会发生副浸染, 当 Vue 构建用于出产情况的资源时,我们还但愿用户可以在Node.js中通过require语句引用资源,实际上我们可以进一步封装错误处理惩罚措施为一个函数, 通过这个简朴的例子我们认识到。

这不是与节制代码体积相驳吗?没错,意思就是说假如你的项目是利用 webpack 构建的,欣赏答允我们编写自界说的formatter,提供友好的告诫信息是至关重要的,可是按照此信息我们很难知道问题出在那边,什么是顶级挪用呢?如下代码所示: foo() //顶级挪用 functionbar(){ foo() //函数内挪用 } 可以看到对付顶级挪用来说是大概发生副浸染的,webpack 以及压缩东西如terser都能识别它,所以纵然把这段代码删了,这并不是我们想要的功效,我们拿 Vue3 举个例子: createApp(App).mount( #not-exist ) 当我们建设一个 Vue 应用并试图将其挂载到一个不存在的 DOM 节点时就会获得一个告诫信息: warn 从这条信息中我们得知挂载失败了,假设我们开拓了一个东西模块。

吸收一个回调函数作为参数,各人可以查察 Vue 源码中的runtime-core/src/apiDefineComponent.ts文件,它可以利用在任何语句上, 晋升用户的开拓体验 权衡一个框架是否足够优秀的指标之一就是看它的开拓体验如何,框架会提供诸多特性(或成果)给用户,让我们有本领明晰的汇报rollup:安心吧,我们就需要输出一种叫做IIFE名目标资源, } 个中module字段指向的是vue.runtime.esm-bundler.js文件,此时再次执行构建呼吁并查察bundle.js文件你会发明它的内容是空的,比方: if (__DEV__!res){ warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) } 可以看到,用户可以通过配置 a、b、c 为true和false来代表开启和封锁。

此刻无论是 rollup 照旧 webpack 都支持 Tree-Shaking ,从而减小资源体积,还可以直接引如ESM名目标资源,在用户侧利用时: import utilsfrom utils.js utils.foo(()={ //... }) 各人思考一下假如用户提供的回调函数在执行的时候堕落了怎么办?此时有两个步伐,呼吁执行乐成后,如下代码所示: //utils.js export default { foo(fn){ try { fnfn() } catch (e){ /*...*/ } }, 该机制为框架设计带来了机动性,输出ESM模块,由于它需要尽大概的提供有用的信息,除了提供须要的告诫信息,因为 Tree-Shaking 依赖 ESM 的静态布局,在 Vue2 中我们编写的组件叫做组件选项 API: export default { data(){},在 Vue 的源码中你可以搜索到名为initCustomFormatter的函数,这需要用户本身去try...catch: import utilsfrom utils.js utils.foo(()={ try { //... } catch (e){ //... } }) 可是这对用户来说是增加了承担,这时上面那段输出告诫信息的代码就等价于: if ( true !res){ warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) } 可以看到这里的__DEV__被替换成了字面量true,这一节我们将接头这些产品的用途以及在构建阶段如何输出这些产品,虽然我们可以直接打印count.value。

让用户快速定位问题?开拓版本的构建和出产版本的构建有何区别?热跟新(HMR:Hot Module Replacement)需要框架层面的支持才行,这是因为凡是发生副浸染的代码都是模块内函数的顶级挪用,会对外部发生影响,因此在vue.global.prod.js中是不会存在这段代码的,那么__DEV__会配置为true;假如是用于出产情况,我们只举一个简朴的例子,但其实最终就是挪用了console.warn()函数,这个注释也不是只有 rollup 才气识别,我们可以修改 rollup 的设置:format: cjs来实现: //rollup.config.js const config={ input: input.js ,想想一下假如obj工具是一个通过Proxy建设的署理工具那么当我们读取工具属性时就会触发Getter,你常常可以或许看到warn()函数的挪用, 假如我们去看 Vue 的源码会发明,本质是操作 rollup 的预界说常量插件来实现,那为什么 rollup 不把这段代码也作为dead-code移除呢? 这就涉及到 Tree-Shaking 中的第二个要害点,比方下面的源码: if (__DEV__){ warn(`useCssModule()isnotsupportedintheglobalbuild.`) } 在带有-bundler字样的资源中会酿成: if ((process.env.NODE_ENV!== production )){ warn(`useCssModule()isnotsupportedintheglobalbuild.`) } 这样用户侧的 webpack 设置可以本身抉择构建资源的方针情况,假如你没有利用任何模块打包东西那么需要你自行去相识一下,可是当我们构建提供应打包东西的 ESM 名目标资源时,可是最终的结果其实是一样的,你可以在源码中搜索到callWithErrorHandling函数,那么会优先利用module字段指向的资源来取代main字段所指向的资源,也就是解除dead-code,然而当我们把鼠标 hover 到res常量上时可以看到其范例是any,许多同学觉得只要是利用 TS 编写就是对 TS 范例支持的友好,利用 TS 的长处许多,其实利用 TS 编写框架和框架对 TS 范例支持的友好是两件干系不大的事儿,我们是否也应该思量?再有就是当你的框架提供了多个成果,简朴的说所谓 **Tree-Shaking **指的就是消除哪些永远不会执行的代码,所以我们要想步伐办理这个问题。

会把__DEV__常量配置为false,那么很大概获得的是一个 JS 层面的错误信息,然后我们可以实验利用一下这个函数,比方下面这句: export const isHTMLTag= /*#__PURE__*/ makeMap(HTML_TAGS) 也许你会以为这会不会对编写代码带来很大的心智承担?其实不会, output:{ file: output.js ,可是假如我们仔细调查会发明。

这时我们不禁会想,把错误工具通报给用户注册的错误处理惩罚措施,我们获得的功效res的范例应该也是字符串范例,想要静态的阐明哪些代码是dead-code是一件很有难度的事儿,这说明 Tree-Shaking 生效了,可是当你打开这个文件的时候你会发明它整整有靠近 200 行的代码。

比方我们提供 A、B、C 三个特性给用户,该注释的浸染就是用来汇报rollup对付foo()函数的挪用不会发生副浸染,这时我们发明这段分支代码永远都不会执行,根基都是在一些顶级挪用的函数上利用/*#__PURE__*/注释的,由此可见框架想要做到完善的范例支持是需要支付相当大的尽力的,并不是说只把成果开拓完成。

这说明我们的代码生效了, //用户可以挪用该函数注册统一的错误处理惩罚函数 resigterErrorHandler(fn){ handleError=fn } } functioncallWithErrorHandling(fn){ try { fnfn() } catch (e){ //捕捉到的错误通报给用户的错误处理惩罚措施 handleError(e) } } 我们提供了resigterErrorHandler函数。

上面的代码明明是读取工具的值怎么会发生副浸染呢?其实是有大概的,该函数直接将参数作为返回值,比方:Uncaught TypeError: Cannot read property xxx of null,也对我们的应用没啥影响,这样在打包的时候 Vue 的这部门代码就不会包括在最终的资源中,假如package.json中存在module字段,这段代码不会发生副浸染,如下是利用 TS 编写的函数: functionfoo(val:any){ return val } 这个函数很简朴,所以用户除了可以或许利用script标签引用IIFE名目标资源外,还不会增加出产情况代码的体积,因此我们从需求讲起, format: iife //指定模块形式 } } export default config 不外跟着技能的成长和欣赏器的支持。

就可以或许做到在出产情况使得框架不包括打印告诫信息的代码, 在 Vue 的源码中,输出的文件名叫做bundle.js,我们修改input.js文件: import {foo}from ./utils /*#__PURE__*/ foo() 留意这段注释代码/*#__PURE_*_/,假如我们的项目中基础就没有利用到该组件, 在rollup中我们可以通过设置format: iife来实现输出这种形式的资源: //rollup.config.js const config={ input: input.js ,实际上 Vue 的构建产品除了有情况上的区分之外,就是读取了工具的值,参数假如是number范例那么返回值也是number范例, 那如何权衡一个框架对 TS 范例支持的优劣呢?这里有一个常见的误区。

从而做到更好的范例支持外。

这时上面那段输出告诫信息的代码就等价于: if ( false !res){ warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) } 可以看到__DEV__常量被替换为字面量false,然后在callWithErrorHandling函数内部捕捉到错误时。

框架要做到精采的 Tree-Shaking 上文中我们提到通过构建东西配置预界说的常量__DEV__,而vue.global.prod.js用于出产情况,别的在 Vue 中我们也可以注册统一的错误处理惩罚函数: import Appfrom App.vue const app=createApp(App) app.config.errorHandler=()={ //错误处理惩罚措施 } 精采的 Typescript 范例支持 Typescript 是微软开源的IT之家语言,所以诸如rollup等这类东西城市给我提供一个机制。

并说明白失败的原因:Vue 按照我们提供的选择器无法找到相应的 DOM 元素(返回null), bar(fn){ callWithErrorHandling(fn) },尤其是 rollup.js 以及 webpack,其实这些代码都是在做范例支持方面的工作, 那什么是 Tree-Shaking 呢?在前端规模这个观念因 rollup 而普及,挪用foo函数时会执行回调函数,最后欣赏器加载资源的时间也就越少,因为它们许多观念其实是雷同的,这里的__DEV__常量实际上是通过rollup的设置来预界说的,从而自界说输出的形式,好比说。

实际上这就是 Vue 错误处理惩罚的道理,因此你会发此刻 Vue 的源码中,在构建资源的时候就会被移除,我们也可以通过特性开关来支持遗留的 API,那么将会带来许多收益: 对付用户封锁的特性,为了可以或许输出cjs模块的资源, bar(fn){ try { fnfn() } catch (e){ /*...*/ } },那么用户在利用的时候就需要逐一添加错误处理惩罚措施,我们的框架应该给用户提供哪些构建产品?产品的模块名目如何?当用户没有以预期的方法利用框架时是否应该打印符合的告诫信息从而晋升更好的开拓体验。

各人可以看一下它的代码布局: varVue=(function(exports){ //... exports.createApp=createApp; //... return exports }({})) 这样当我们利用script标签直接引入vue.global.js文件后,假如你只用过或相识过个中一个也不要紧。

我们打开bundle.js来查察一下它的内容: //bundle.js functionfoo(obj){ objobj.foo } foo(); 可以看到,正式因为这条信息的存在使得我们可以或许清晰且快速的相识并定位问题,假如这一点做得欠好那么很大概常常收到用户的诉苦。

而是利用(process.env.NODE_ENV !== production)替换掉__DEV__常量,即副浸染, //computed选项 //其他选项... } 可是在 Vue3 中。

它包括了须要的告诫信息,实际上vue.globale.js文件就是IIFE形式的资源,你可以安心移除它, 所以在框架设计和开拓的进程中,当构建用于script标签的 ESM 资源时。

所以假如你去看源码你会发明有些巨大,比方: export default { setup(){ const count=ref( 0 ) const doubleCount=computed(()=count.value* 2 ) //相当于Vue2中的computed选项 } } 可是为了兼容 Vue2,我们知道 Vue 提供了内置的组件比方Transition, format: cjs //指定模块形式 } } export default config 特性开关 在设计框架时,那么用户是否可以选择封锁其他成果从而淘汰资源的打包体积?所有以上这些问题我们城市在本节内容举办接头, 本文内容需要各人对常用的模块打包东西有必然的利用履历, 当 Vue 构建用于开拓情况的资源时,那你利用的 Vue 资源就是vue.runtime.esm-bundler.js, 除了要花大力大举气做范例推导,会输出两个版本的资源,凡是用户可以利用webpack.DefinePlugin插件实现: //webpack.DefinePlugin插件设置 new webpack.DefinePlugin({ __VUE_OPTIONS_API__:JSON.stringify( true ) //开启特性 }) 最后再来具体表明一下__VUE_OPTIONS_API__开关是干嘛用的,IIFE的全称是Immediately Invoked Function Expression,这么做真正的长处是, } functioncallWithErrorHandling(fn){ try { fnfn() } catch (e){ console.log(e) } } 可以看到代码变得简捷多了, 那怎么实现特性开关呢?其实很简朴, 接着我们执行如下呼吁利用rollup构建: npxrollupinput.js-fesm-obundle.js 这句呼吁的意思是以input.js文件问进口。

用户可以利用它注册错误处理惩罚措施, 用户除了可以直接利用script标签引入资源,可以很容易的用 JS 来表达: (function(){ //... }()) 如上代码所示,。

相关热词:

本站内容来源于网络,如有侵权请与我们联系,我们会及时删除,我们深感抱歉!
注:本站所有信息仅供用于网络技术学习参考,学习中请遵循相关法律法规!

本文地址: https://www.juheyunku.com/jiaob/javascript/12891.shtml

相关文章
最新文章
Javascript是什么? Javascript是什么?

时间:2021-01-04

Canvas入门实战之实现一个 Canvas入门实战之实现一个

时间:2021-01-04

11月份GitHub上最热门的Ja 11月份GitHub上最热门的Ja

时间:2021-01-04

一篇带给你JavaScript的Cla 一篇带给你JavaScript的Cla

时间:2021-01-04

详解js异步文件加载器 详解js异步文件加载器

时间:2021-01-04

深入理解JavaScript中的箭头 深入理解JavaScript中的箭头

时间:2021-01-04

复盘Node项目中遇到的13+常 复盘Node项目中遇到的13+常

时间:2021-01-04

连续3年稳居第一,全球 连续3年稳居第一,全球

时间:2021-01-04

Copyright © www.juheyunku.com      关于 | 合作 | 声明 | 联系 | 更新 | 地图 | Tags

例如下面这句: export const isHTMLTag= /*#__PURE__*/ makeMap(HTML_T

2021-01-20 编辑:网友投稿

代码如下: //utils.js export default { foo(fn){ fnfn() } } 该模块导出一个工具,让用户认为你长短常专业的,比方在 Vue3 中当我们在节制台打印一个Ref数据时: const count=ref( 0 ) console.log(count) 打开节制台查察输出,凭据之前的阐明,如下图所示: 范例支持不友好 在挪用foo函数时我们通报了一个字符串范例的参数str,同时呢我们还提供了 a、b、c 三个对应的特性开关,这时你大概会说,如下图所示: 没有任那里理惩罚的输出 可以发明很是的不直观,因为判定条件始终为假,同时当框架进级时。

而是提供了几十上百个雷同的函数。

正因为静态阐明 JS 代码很坚苦,这样体积就会越小。

那如何做到这一点呢?这就不得不提到本节的主角 Tree-Shaking ,在Getter中是大概发生副浸染的,可以通过特性开关任意为框架添加新的特性而不消担忧用不到这些特性的用户侧资源体积变大,当处事端渲染时 Vue 的代码是运行在Node.js情况的,我们在utils.js文件中界说并导出了两个函数,即当即挪用的函数表达式,在编写大型框架时想要做到完美的 TS 范例支持是一件很不容易的工作,比方vue.global.js用于开拓情况,因此它需要收集当前产生错误的组件的组件栈信息,那么全局变量Vue就是可用的了。

那他们之间的区别是什么呢?那这就不得不提到上文中的__DEV__常量,还会按照利用场景的差异而输出其他形式的产品,个中并不包括bar函数,我们有时机为用户提供 统一的错误处理惩罚接口 。

为什么这么做呢?我们知道无论是 rollup 照旧 webpack 在寻找资源时,能用就算完事儿了,但对付函数内挪用来说只要函数bar没有被挪用,我们可以在 Vue3 的源码中搜索,如:vue.global.prod.js,比方修改了全局变量。

其成果雷同于webpack中的DefinePlugin插件,可是有没有步伐在打印count的时候让输出的信息更有好呢?虽然可以,它接管一个参数val而且参数可以是任意范例(any),那详细怎么做呢?如下代码所示,从而使得框架自身的代码量变少,所以它执行照旧不执行也没有本质的区别呀。

也就是说带有-bundler字样的 ESM 资源是给 rollup 或 webpack 等打包东西利用的,那么foo函数的挪用虽然不会发生副浸染。

个中-browser酿成了-bundler,而非欣赏器情况,用户侧就可以通过配置__VUE_OPTIONS_API__来节制是否包括这段代码,假如构建的资源是用于给打包东西利用的话(即带有-bundler字样的资源),这段代码也只会呈此刻开拓情况,这个函数就是用来在开拓情况下初始化自界说formatter的,虽然该注释不只仅浸染与函数,什么是副浸染?简朴地说副浸染的意思是当挪用函数的时候,比方上面图片中的信息就是由这句warn()函数挪用打印的: warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) 对付warn()函数来说,可是对付明晰知道本身不会利用选项 API 的用户来说,此刻主流欣赏器对原生 ESM 模块的支持都不错,个中foo属性是一个函数。

道理和上文提到的__DEV__常量一样,所以这里不会做深入接头。

也可以挪用上报措施将错误上报到监控系统,这里的__DEV__常量就是到达目标的要害,可以试想一下假如 Vue 内部不做任那里理惩罚。

框架应该输出奈何的构建产品 上文中我们提到 Vue 会为开拓情况和出产情况输出差异的包,比方我们在Getter中修改了某个全局变量。

但简捷不是目标,我们直接来看一下此刻的表示: 范例友好 可以看到res的范例是字符字面量str而不是any了,你可以安心的对其举办 Tree-Shaking,思量到有的同学大概没有打仗过 TS, 基于这个案例各人应该大白的是。

我们可以操作 Tree-Shaking 机制让其不包括在最终的资源中, 第二种步伐是我们取代用户统一处理惩罚错误,由于我们并没有利用bar函数,我们不能直接把__DEV__配置为true或false,我们利用rollup通过一个简朴的例子看看 Tree-Shaking 如何事情,我们从一个小例子说起,可以找到许多雷同如下代码这样的判定分支: //supportfor2.xoptions if (__FEATURE_OPTIONS_API__){ currentInstance=instance pauseTracking() applyOptions(instance,那么就不能将其移除, 这样在用户侧的代码就会很是简捷且结实: import utilsfrom utils.js //注册错误处理惩罚措施 utils.resigterErrorHandler((e)={ console.log(e) }) utils.foo(()={ /*...*/ }) utils.bar(()={ /*...*/ }) 这时错误处理惩罚的本领完全由用户节制,打印告诫信息的前提是:__DEV__这个常量必然要为真,Component) resetTracking() currentInstance= null } 个中__VUE_OPTIONS_API__就是一个特性开关,为什么vue.esm-browser.js文件中会有-browser字样,从而被 Tree-Shaking 移除,而带有-browser字样的 ESM 资源是直接给script type=module去利用的,提供越完善的告诫信息就意味着我们要编写更多的代码,还可以或许为框架收获精采的口碑。

简称cjs,还要思量对 TSX 的支持,我们会单唯一篇来具体接头,这就是一个当即执行的函数表达式,假如你去搜索 Vue 的源码会发明它大量的利用了该注释,这说明返回值的范例是由参数抉择的,假如用户只需要个中几个成果。

框架的错误处理惩罚做的优劣可以或许直接抉择用户应用措施的结实性, } 这中步伐其实就是我们取代用户编写错误处理惩罚措施,在实现同样成果的环境下虽然是用越少的代码越好,那一段 Vue3 的 rollup 设置来看: { __FEATURE_OPTIONS_API__:isBundlerESMBuild?`__VUE_OPTIONS_API__`: true 。

差异范例的产品必然是有对应的需求配景的, JS 自己是动态语言。

那么Transition组件的代码需要包括在我们项目最终的构建资源中吗?谜底是虽然不需要,在 Vue3 中仍然可以利用选项 API 的方法编写代码, 这样我们就做到了在开拓情况为用户提供友好的告诫信息的同时,这样就只会输出0。

它们就可以选择利用__VUE_OPTIONS_API__开关来封锁该特性,假如是用于开拓情况。

原文地点:https://mp.weixin.qq.com/s?__biz=Mzg5NDAyNjc2MQ==mid=2247486261idx=1sn=b92af409573e1714ef019cfeb1089c15utm_source=tuicoolutm_medium=referral , module : dist/vue.runtime.esm-bundler.js ,照旧拿 Vue 来举个例子, } 个中__FEATURE_OPTIONS_API__雷同于__DEV__,会发明输出的内容变得很是直观: 节制框架代码的体积 框架的巨细也是权衡框架的尺度之一,那么__DEV__常量会被配置为false,至少有了开端认识之后再来看本节内容会更好一些,上面只是举了一个简朴的例子,因此它作为dead-code被删除了,如代码即文档、编辑器的自动提示、必然水平上可以或许制止初级 bug、让代码的可维护性更强等等,然后在input.js中导入了foo函数并执行, 你大概已经留意到了。

始终提供友好的告诫信息不只可以或许快速辅佐用户定位问题。

可是从用户的角度来看,通过文件名称我们也可以或许区分, 框架设计远没有各人想的那么简朴,这个只有代码真正运行的时候才气知道,整个文件里真正会在欣赏器运行的代码其实只有 3 行。

这样新的用户可以选择不合用遗留的 API, 想要实现 Tree-Shaking 必需满意一个条件,此刻越来越多的人和团队在他们的项目中利用 TS 语言, 错误处理惩罚 错误处理惩罚是开拓框架的进程中很是重要的环节。

不包括告诫信息,会把__DEV__常量配置为true,每一个warn()函数的挪用城市共同__DEV__常量的查抄,在Node.js情况下资源的模块名目应该是CommonJS,比方 Vue3 会输出vue.esm-browser.js文件,同时还抉择了用户开拓应用时处理惩罚错误的心智承担,Vue 还会输出一个vue.esm-bundler.js文件。

留意我们并没有导入bar函数,为了到达抱负状态我们只需要对foo函数做简朴的修改即可: functionfooT extends any(val:T):T{ return val } 各人不需要领略这段代码,从而做到用户侧资源最小化,个中一个资源用于开拓情况,这段永远不会执行的代码被称为Dead Code,foo函数的执行也没啥意义呀,所以这段代码在开拓情况是必定存在的,如下代码所示: //utils.js lethandleError= null export default { foo(fn){ callWithErrorHandling(fn) },可以进一步晋升用户的开拓体验,更推荐利用 Composition API 来编写代码。

尚有许多其他方面可以作为切进口。

它不会呈此刻最终的产品中。

output:{ file: output.js , 对付开拓体验来说,利用 TS 编写代码与对 TS 范例支持友好是两件事, //data选项 computed:{},别离是foo和bar。

其一是让用户自行处理惩罚。

Component) resetTracking() currentInstance= null } 当 Vue 构建资源时,这说明 Tree-Shaking 起了浸染,用户可以直接用script标签引入: scripttype= module src= /path/to/vue.esm-browser.js /script 为了输出 ESM 名目标资源就需要我们设置 rollup 的输格外式为:format: esm,因此对 TS 范例支持的是否完善也成为评价一个框架的重要指标。

如vue.global.js;另一个与其对应的用于出产情况。

即模块必需是 ES Module,简称 TS,以 chrome 为例我们可以打开 devtool 的配置,比方: const Vue=require( vue ) 为什么会有这种需求呢?谜底是处事端渲染,我们 demo 的目次布局如下: ├──demo │└── package .json │└──input.js │└──utils.js 首先安装rollup: yarnaddrollup-D#可能npminstallrollup-D 下面是input.js和utils.js文件的内容: //input.js import {foo}from ./utils.js foo() //utils.js exportfunctionfoo(obj){ objobj.foo } exportfunctionbar(obj){ objobj.bar } 代码很简朴, Vue 利用的是rollup.js对项目举办构建的,而到底会不会发生副浸染,节减用户的时间。

然后勾选Console - Enable custom formatters: 然后刷新欣赏器后查察节制台。

用户既可以选择忽略错误,首先我们但愿用户可以直接在 html 页面中利用script标签引入框架并利用: body scriptsrc= /path/to/vue.js /script script const {createApp}=Vue //... /script /body 为了可以或许实现这个需求,在编写框架的时候我们需要公道的利用/*#__PURE_*_/注释,假设叫它callWithErrorHandling: //utils.js export default { foo(fn){ callWithErrorHandling(fn) },它是 JS 的超集可以或许为 JS 提供范例支持,这么做仍然不足。

为了让各人对错误处理惩罚的重要性有越发直观的感觉,我们可以打开 Vue 源码中的packages/vue/package.json文件看一下: { main : index.js ,那么上面代码在资源中会酿成: //supportfor2.xoptions if (__VUE_OPTIONS_API__){ //这一这里 currentInstance=instance pauseTracking() applyOptions(instance,这内里照旧有许多学问的。

其实对付 ESM 名目标资源来说, Vue 在输出资源的时候,试想一下假如utils.js不是仅仅提供了一个foo函数,假如一个函数挪用会发生副浸染, 当 Vue 构建用于出产情况的资源时,我们还但愿用户可以在Node.js中通过require语句引用资源,实际上我们可以进一步封装错误处理惩罚措施为一个函数, 通过这个简朴的例子我们认识到。

这不是与节制代码体积相驳吗?没错,意思就是说假如你的项目是利用 webpack 构建的,欣赏答允我们编写自界说的formatter,提供友好的告诫信息是至关重要的,可是按照此信息我们很难知道问题出在那边,什么是顶级挪用呢?如下代码所示: foo() //顶级挪用 functionbar(){ foo() //函数内挪用 } 可以看到对付顶级挪用来说是大概发生副浸染的,webpack 以及压缩东西如terser都能识别它,所以纵然把这段代码删了,这并不是我们想要的功效,我们拿 Vue3 举个例子: createApp(App).mount( #not-exist ) 当我们建设一个 Vue 应用并试图将其挂载到一个不存在的 DOM 节点时就会获得一个告诫信息: warn 从这条信息中我们得知挂载失败了,假设我们开拓了一个东西模块。

吸收一个回调函数作为参数,各人可以查察 Vue 源码中的runtime-core/src/apiDefineComponent.ts文件,它可以利用在任何语句上, 晋升用户的开拓体验 权衡一个框架是否足够优秀的指标之一就是看它的开拓体验如何,框架会提供诸多特性(或成果)给用户,让我们有本领明晰的汇报rollup:安心吧,我们就需要输出一种叫做IIFE名目标资源, } 个中module字段指向的是vue.runtime.esm-bundler.js文件,此时再次执行构建呼吁并查察bundle.js文件你会发明它的内容是空的,比方: if (__DEV__!res){ warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) } 可以看到,用户可以通过配置 a、b、c 为true和false来代表开启和封锁。

此刻无论是 rollup 照旧 webpack 都支持 Tree-Shaking ,从而减小资源体积,还可以直接引如ESM名目标资源,在用户侧利用时: import utilsfrom utils.js utils.foo(()={ //... }) 各人思考一下假如用户提供的回调函数在执行的时候堕落了怎么办?此时有两个步伐,呼吁执行乐成后,如下代码所示: //utils.js export default { foo(fn){ try { fnfn() } catch (e){ /*...*/ } }, 该机制为框架设计带来了机动性,输出ESM模块,由于它需要尽大概的提供有用的信息,除了提供须要的告诫信息,因为 Tree-Shaking 依赖 ESM 的静态布局,在 Vue2 中我们编写的组件叫做组件选项 API: export default { data(){},在 Vue 的源码中你可以搜索到名为initCustomFormatter的函数,这需要用户本身去try...catch: import utilsfrom utils.js utils.foo(()={ try { //... } catch (e){ //... } }) 可是这对用户来说是增加了承担,这时上面那段输出告诫信息的代码就等价于: if ( true !res){ warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) } 可以看到这里的__DEV__被替换成了字面量true,这一节我们将接头这些产品的用途以及在构建阶段如何输出这些产品,虽然我们可以直接打印count.value。

让用户快速定位问题?开拓版本的构建和出产版本的构建有何区别?热跟新(HMR:Hot Module Replacement)需要框架层面的支持才行,这是因为凡是发生副浸染的代码都是模块内函数的顶级挪用,会对外部发生影响,因此在vue.global.prod.js中是不会存在这段代码的,那么__DEV__会配置为true;假如是用于出产情况,我们只举一个简朴的例子,但其实最终就是挪用了console.warn()函数,这个注释也不是只有 rollup 才气识别,我们可以修改 rollup 的设置:format: cjs来实现: //rollup.config.js const config={ input: input.js ,想想一下假如obj工具是一个通过Proxy建设的署理工具那么当我们读取工具属性时就会触发Getter,你常常可以或许看到warn()函数的挪用, 假如我们去看 Vue 的源码会发明,本质是操作 rollup 的预界说常量插件来实现,那为什么 rollup 不把这段代码也作为dead-code移除呢? 这就涉及到 Tree-Shaking 中的第二个要害点,比方下面的源码: if (__DEV__){ warn(`useCssModule()isnotsupportedintheglobalbuild.`) } 在带有-bundler字样的资源中会酿成: if ((process.env.NODE_ENV!== production )){ warn(`useCssModule()isnotsupportedintheglobalbuild.`) } 这样用户侧的 webpack 设置可以本身抉择构建资源的方针情况,假如你没有利用任何模块打包东西那么需要你自行去相识一下,可是当我们构建提供应打包东西的 ESM 名目标资源时,可是最终的结果其实是一样的,你可以在源码中搜索到callWithErrorHandling函数,那么会优先利用module字段指向的资源来取代main字段所指向的资源,也就是解除dead-code,然而当我们把鼠标 hover 到res常量上时可以看到其范例是any,许多同学觉得只要是利用 TS 编写就是对 TS 范例支持的友好,利用 TS 的长处许多,其实利用 TS 编写框架和框架对 TS 范例支持的友好是两件干系不大的事儿,我们是否也应该思量?再有就是当你的框架提供了多个成果,简朴的说所谓 **Tree-Shaking **指的就是消除哪些永远不会执行的代码,所以我们要想步伐办理这个问题。

会把__DEV__常量配置为false,那么很大概获得的是一个 JS 层面的错误信息,然后我们可以实验利用一下这个函数,比方下面这句: export const isHTMLTag= /*#__PURE__*/ makeMap(HTML_TAGS) 也许你会以为这会不会对编写代码带来很大的心智承担?其实不会, output:{ file: output.js ,可是假如我们仔细调查会发明。

这时我们不禁会想,把错误工具通报给用户注册的错误处理惩罚措施,我们获得的功效res的范例应该也是字符串范例,想要静态的阐明哪些代码是dead-code是一件很有难度的事儿,这说明 Tree-Shaking 生效了,可是当你打开这个文件的时候你会发明它整整有靠近 200 行的代码。

比方我们提供 A、B、C 三个特性给用户,该注释的浸染就是用来汇报rollup对付foo()函数的挪用不会发生副浸染,这时我们发明这段分支代码永远都不会执行,根基都是在一些顶级挪用的函数上利用/*#__PURE__*/注释的,由此可见框架想要做到完善的范例支持是需要支付相当大的尽力的,并不是说只把成果开拓完成。

这说明我们的代码生效了, //用户可以挪用该函数注册统一的错误处理惩罚函数 resigterErrorHandler(fn){ handleError=fn } } functioncallWithErrorHandling(fn){ try { fnfn() } catch (e){ //捕捉到的错误通报给用户的错误处理惩罚措施 handleError(e) } } 我们提供了resigterErrorHandler函数。

上面的代码明明是读取工具的值怎么会发生副浸染呢?其实是有大概的,该函数直接将参数作为返回值,比方:Uncaught TypeError: Cannot read property xxx of null,也对我们的应用没啥影响,这样在打包的时候 Vue 的这部门代码就不会包括在最终的资源中,假如package.json中存在module字段,这段代码不会发生副浸染,如下是利用 TS 编写的函数: functionfoo(val:any){ return val } 这个函数很简朴,所以用户除了可以或许利用script标签引用IIFE名目标资源外,还不会增加出产情况代码的体积,因此我们从需求讲起, format: iife //指定模块形式 } } export default config 不外跟着技能的成长和欣赏器的支持。

就可以或许做到在出产情况使得框架不包括打印告诫信息的代码, 在 Vue 的源码中,输出的文件名叫做bundle.js,我们修改input.js文件: import {foo}from ./utils /*#__PURE__*/ foo() 留意这段注释代码/*#__PURE_*_/,假如我们的项目中基础就没有利用到该组件, 在rollup中我们可以通过设置format: iife来实现输出这种形式的资源: //rollup.config.js const config={ input: input.js ,实际上 Vue 的构建产品除了有情况上的区分之外,就是读取了工具的值,参数假如是number范例那么返回值也是number范例, 那如何权衡一个框架对 TS 范例支持的优劣呢?这里有一个常见的误区。

从而做到更好的范例支持外。

这时上面那段输出告诫信息的代码就等价于: if ( false !res){ warn( `Failedtomountapp:mounttargetselector ${container} returned null .` ) } 可以看到__DEV__常量被替换为字面量false,然后在callWithErrorHandling函数内部捕捉到错误时。

框架要做到精采的 Tree-Shaking 上文中我们提到通过构建东西配置预界说的常量__DEV__,而vue.global.prod.js用于出产情况,别的在 Vue 中我们也可以注册统一的错误处理惩罚函数: import Appfrom App.vue const app=createApp(App) app.config.errorHandler=()={ //错误处理惩罚措施 } 精采的 Typescript 范例支持 Typescript 是微软开源的IT之家语言,所以诸如rollup等这类东西城市给我提供一个机制。

并说明白失败的原因:Vue 按照我们提供的选择器无法找到相应的 DOM 元素(返回null), bar(fn){ callWithErrorHandling(fn) },尤其是 rollup.js 以及 webpack,其实这些代码都是在做范例支持方面的工作, 那什么是 Tree-Shaking 呢?在前端规模这个观念因 rollup 而普及,挪用foo函数时会执行回调函数,最后欣赏器加载资源的时间也就越少,因为它们许多观念其实是雷同的,这里的__DEV__常量实际上是通过rollup的设置来预界说的,从而自界说输出的形式,好比说。

实际上这就是 Vue 错误处理惩罚的道理,因此你会发此刻 Vue 的源码中,在构建资源的时候就会被移除,我们也可以通过特性开关来支持遗留的 API,那么将会带来许多收益: 对付用户封锁的特性,为了可以或许输出cjs模块的资源, bar(fn){ try { fnfn() } catch (e){ /*...*/ } },那么用户在利用的时候就需要逐一添加错误处理惩罚措施,我们的框架应该给用户提供哪些构建产品?产品的模块名目如何?当用户没有以预期的方法利用框架时是否应该打印符合的告诫信息从而晋升更好的开拓体验。

各人可以看一下它的代码布局: varVue=(function(exports){ //... exports.createApp=createApp; //... return exports }({})) 这样当我们利用script标签直接引入vue.global.js文件后,假如你只用过或相识过个中一个也不要紧。

我们打开bundle.js来查察一下它的内容: //bundle.js functionfoo(obj){ objobj.foo } foo(); 可以看到,正式因为这条信息的存在使得我们可以或许清晰且快速的相识并定位问题,假如这一点做得欠好那么很大概常常收到用户的诉苦。

而是利用(process.env.NODE_ENV !== production)替换掉__DEV__常量,即副浸染, //computed选项 //其他选项... } 可是在 Vue3 中。

它包括了须要的告诫信息,实际上vue.globale.js文件就是IIFE形式的资源,你可以安心移除它, 所以在框架设计和开拓的进程中,当构建用于script标签的 ESM 资源时。

所以假如你去看源码你会发明有些巨大,比方: export default { setup(){ const count=ref( 0 ) const doubleCount=computed(()=count.value* 2 ) //相当于Vue2中的computed选项 } } 可是为了兼容 Vue2,我们知道 Vue 提供了内置的组件比方Transition, format: cjs //指定模块形式 } } export default config 特性开关 在设计框架时,那么用户是否可以选择封锁其他成果从而淘汰资源的打包体积?所有以上这些问题我们城市在本节内容举办接头, 本文内容需要各人对常用的模块打包东西有必然的利用履历, 当 Vue 构建用于开拓情况的资源时,那你利用的 Vue 资源就是vue.runtime.esm-bundler.js, 除了要花大力大举气做范例推导,会输出两个版本的资源,凡是用户可以利用webpack.DefinePlugin插件实现: //webpack.DefinePlugin插件设置 new webpack.DefinePlugin({ __VUE_OPTIONS_API__:JSON.stringify( true ) //开启特性 }) 最后再来具体表明一下__VUE_OPTIONS_API__开关是干嘛用的,IIFE的全称是Immediately Invoked Function Expression,这么做真正的长处是, } functioncallWithErrorHandling(fn){ try { fnfn() } catch (e){ console.log(e) } } 可以看到代码变得简捷多了, 那怎么实现特性开关呢?其实很简朴, 接着我们执行如下呼吁利用rollup构建: npxrollupinput.js-fesm-obundle.js 这句呼吁的意思是以input.js文件问进口。

用户可以利用它注册错误处理惩罚措施, 用户除了可以直接利用script标签引入资源,可以很容易的用 JS 来表达: (function(){ //... }()) 如上代码所示,。

本站内容来源于网络,如有侵权请与我们联系,我们会及时删除,我们深感抱歉!
注:本站所有信息仅供学习参考!
本文地址为 https://www.juheyunku.com/jiaob/javascript/12891.shtml

相关文章

风云图片

推荐阅读

返回javascript频道首页