新手学做网站看什么书,自助建站广告发布,设计类专业必须要美术生吗,怎么搭建网页服务器大家好#xff0c;我是若川。今天再分享 Vueconf 的一篇文章。另外 Vueconf 主办方提供的录播链接是#xff1a; https://www.bilibili.com/read/mobile?id11408693#xff0c;感兴趣可以复制观看。点击下方卡片关注我、加个星标。学习源码整体架构系列、年度总结、JS基础… 大家好我是若川。今天再分享 Vueconf 的一篇文章。另外 Vueconf 主办方提供的录播链接是 https://www.bilibili.com/read/mobile?id11408693感兴趣可以复制观看。点击下方卡片关注我、加个星标。学习源码整体架构系列、年度总结、JS基础系列本文为 Vue Conf 2021 分享内容。分享者林成璋目前就职于 字节跳动 大力智能前端 团队1. 引言各位同学下午好我是来自字节跳动大力智能前端团队的林成璋最近半年的业余时间再加上一些摸鱼的时间主要在维护 Vue 3 的 Babel JSX Plugin[1]今天来给大家做一个关于 JSX 的分享。下面是我的 Github 账号全网除了 P 站应该都是这个头像。其实最早做这个插件主要是为了帮助 Ant Design Vue 和 Vant 能够快速升级到 Vue 3看过他们源码的同学应该知道他们的源码大部分都是用 JSX 来撸的。虽然目前在 NPM 上的周下载量是 56 万多甚至超过了 Vue 3 ????但是这里的下载量非常大的原因主要是通过 vue-cli 创建的项目不管是 Vue 2 还是 Vue 3都会下载 vue/babel-plugin-jsx 这个包实际使用 JSX 的用户应该远比这个数字要小到底有多少用户是通过的 JSX 的方式开发的也没有办法统计到绝大用户还是使用 template 的开发方式为主。2. 基本概念template在 Vue 里sfc 是一个以 .vue 结尾的文件通常包含三种类型的顶级语言块 template、script 和 style可以理解为 HTML 、JS 以及 CSS 的组合。每一个 .vue 文件结尾的文件都是一个组件而且只能 export defualt 出一个组件。JSX本身就是 JS3. 为什么在 Vue 里也支持 JSXVue 官方推荐的开发方式是 template从 Vue 2 开始template 在运行之前会被编译成 JavaScript 的 render function。这些 render function 在运行时阶段就是传说中的 Virtual DOM。每当讲到 template 和 JSX可能就会讨论到一个比较大的问题React 和 Vue 哪个好。一些人可能就不太喜欢通过 JavaScript 直接来表示 UI然而也会有相当一部分人会认为用 template 来写可能比较烦特别是 React 资深玩家。由于 vue 是全球最友好的 UI 框架有广大的群众基础一些群众习惯于直接用 HTML 和 CSS 来干代码对他们来说把写 UI 的逻辑从 HTML 转到 template 比让他们的思路完全变更到开始思考如何用 JavaScript 来构建 UI 要简单得多。但是也不得不承认对于一些之前是搞后端的同学 或者 iOS 和 Android 的开发者来说之前没有怎么接触过 HTML 的通过字符串模板的方式来编写 UI 也不太行。不同用户的口味不太一样萝卜白菜各有所爱。就像我这张 PPT有些人看了可能很兴奋一些人可能觉得我是个傻 X。你可以说一堆模板怎么怎么不好的例子他也同样也给 JSX 一顿喷谁也说服不了谁。所以 Vue 干脆把两个事都干了。4. 什么是「真正的」JSXJSX[2] 最早是由 facebook 起草的一个规范后面的这个 X 可以理解为它是 JavaScript 的语法扩展感兴趣的同学可以从这个链接进去看看里面的具体内容。由于各个前端框架的实现不一样所以它不会由引擎或浏览器实现需要 Transform 之后转成常规的 JS 之后这一步操作我们可以理解为「赋能」才能在浏览器里面运行。JSX 其实也和模板语言类似但它具有 JavaScript 的全部功能但是由于在模板中的一些限制用模板写出来的代码性能要比 JSX 好得多。h1Hello, world!/h1;
这里的 JSX 语法编译之后其实就是import { createVNode as _createVNode } from vue_createVNode(h1, null, Hello, world!);
5. Vue 3 带来的改变Vue 2 早期是用纯 JavaScript 来编写的随着项目越来越庞大引入了 Facebook 的 Flow[3]。虽然 Flow 在一定程度上起到了帮助作用但还是存在一些问题尤大也曾经公开表示当初没有选择 TypeScript 选择了 Flow 是「押错宝」了。在 Vue 2 中JSX 的编译需要依赖 vue/babel-preset-jsx 和 vue/babel-helper-vue-jsx-merge-props 这两个包。前面这个包来负责编译 JSX 的语法后面的包用来引入运行时的 mergeProps 函数。但是如果你要用 TSX 的环境来写还需要额外安装 vue-tsx-support[4]。在 Vue 3 中只要安装一个 Babel 插件就完事了可以理解为不再需要额外的第三方库源码中就有 jsx.d.ts[5] 用来支持 JSX 的类型检查6. 使用 JSX 的场景我们现在来看下有哪些场景用 JSX 会比模板更加优雅。6.1 一个文件写多个组件一个 .vue 文件里面只能写一个组件这个说实话在一些场景下还是不太方便很多时候我们写一个页面的时候其实可能会需要把一些小的节点片段拆分到小组件里面进行复用这些小组件其实写个简单的函数组件就能搞定了。如果你现在没有这个习惯可能就是因为 SFC 的限制让你习惯了全部写在一个文件里面。比如这里我们封装了一个 Input 组件我们希望同时导出 Password 组件和 Textarea 组件来方便用户根据实际需求使用而这两个组件本身内部就是用的 Input 组件只是定制了一些 props。在 JSX 里面就很方便写个简单的函数组件基本上就够用了通过 interface 来声明 props 就好了。但是如果是用模板来写可能就要给拆成三个文件或许还要再加一个 index.js 的入口文件来导出三个组件摸鱼的时间又少了。6.2 强依赖编译时的检查模板中引用了一个未在 script 中声明的 avscode 插件可以帮忙检查出来但是仍然可以跑起来。如果是用 TS 来写这里引用了一个未声明的 c 变量TS 在编译阶段就能让代码直接跑不起来。目前模板还是会被直接编译成 JS因此还做不到在 template 就进行编译时的类型检查。拥有 JS 完全编程能力由于 JSX 的本质就是 JavaScript所以它具有 JavaScript 的完全编程能力。举个例子我们需要通过一段逻辑来对一组 DOM 节点做一次 reverse如果在模板里面写那估计要写两段代码。虽然这个例子可能不太常见但是不得不否认在一些场景下JSX 还是要比模板写起来更加顺手。6.3 范型组件在模板里面由于一些历史的原因目前范型组件确实还支持不了但是不代表以后不行。如果非要用范型可以先用函数组件给包一层但是注意不要声明 FunctionalComponent 的类型。这里我们在 .tsx 文件里面声明 Foo 组件Props 是一个范型。声明完之后再回到模板里面可以我们看到刚刚定义的范型组件已经生效了。SFC 的 TS IDE 支持可以用 volar。volar 还支持了范型组件用起来感觉和 TSX 已经没多大区别了。7. 使用 JSX 需要注意的点7.1 对 Props 的处理在模板中对 props 的处理是 merge。为了满足不同用户的需求开了一个可以覆盖的口子。7.2 对插槽的处理插槽是一种内容分发content distribution的 API洋文叫 Slot也就是 createVNode 的最后一个参数。适合用在结果比较复杂组件内容可以复用的地方简单来说就是在组件中可以预留空间从父级把内容给传进去。在 JSX 中父组件给子组件来传递 VNode 通过属性来传递就完事了。但是在模板中传递属性的时候template 里面是不能写 VNode 的因此 Vue 里出现了插槽这个概念插槽只在组件的 children 里面才有。我们来看下 Vue 是怎么处理插槽的Vue 对插槽的要求最好是一个 function对运行时的性能提升会有很大的帮助。因此 A 组件的子节点会被编译成{ default: () [123] }。对应到 JSX 中按照正常用户的心智模式只有一个 children 的时候写成{ default: () [123] }也不太现实正常的写法就是直接塞一个 children。但是在编译阶段要处理成 function否则在开发时会报 warning对开发者来说是非常不友好的体验。对编译器来说其实也好办给子节点的 VNode 包一层函数就完事了。在多个插槽的情况下稍微比单个的场景要复杂点。除了 default 之外的插槽通过 props 的方式来传是不可能的只能想办法通过类似「指令」的方式来传递因此最早设计了 v-slots 的命令来处理插槽。但是 v-slots 对于一些开发者来说可能会不够直观。更直观的方式应该像这样也就是 obejct slots先简单讲一下两个概念编译和运行时。编译就是把我们的代码转成 JavaScript 引擎可以看懂的代码运行时就是 JavaScript 引擎开始跑你的代码。就好比我们招聘中的简历筛选和面试简历筛选可以对应编译面试来运行时。这个候选人到底怎么样单纯看简历是看不出来的。再回到刚刚的问题如果直接把 children 写成一个内联的对象还好办但如果是一个变量的话在编译的时候编译器是无法知道传过来的到底是个什么玩意儿是 slots 还是 VNode 其实编译的时候看不出来。如果是一个文件里面的编译器或许还能判断但是从另一个文件 import 进来是无法判断的。Babel 处理每一个的文件都是一个「闭环」 。所以这时候就需要加一个运行时的判断虽然解决了判断是不是 slots 的问题但是每一个变量给加上运行时判断会对编译产物的体积有一些影响。jsx-next #255为了保持编译产物体积和直观语义上的平衡就让开发自己来选择是否需要上述的 feature提供了 enableObjectSlots 的开关。8. 模板与 JSX 的性能对比刚刚说了一些在哪些场景下用 JSX 可能会更加地合适。这里简单地对比了下实现相同功能JSX 和模板的性能差异。左右两个 demo 里面整了两万个节点奇数节点里面 class 是动态的偶数节点的 textContent 是动态的点击 shuffle。在这个例子里面用模板写的代码 比用 JSX 写的要快十几毫秒。在实际的场景中组件的层级嵌套远比这里给出的 demo 要复杂这个时候就更加能够体现模板的优势了。在传统的 VDOM 树中我们在运行时不能够得到用于优化的信息。在 Vue 3 中充分利用了模板静态信息最终体现到 VDOM 树上。比方说在 diff 的时候可以知道哪些节点是动态的节点的哪些属性是动态的。有了这些信息我们就可以在创建 VNode 的时候来标记哪些属性是不是动态的靶向更新也就是传说中 PatchFlags。除了 PatchFlags 之外Vue 3 的 VDOM 在运行时还做了一些缓存比如 children 的缓存。先来解释一下 PatchFlags 是怎么运作的其实它就是一个数字只不过在运行的时候被赋予了不同的含义数字 2 (PatchFlags.CLASS)表示 class 是动态的数字 4 (PatchFlags.STYLE)表示 style 是动态的可能一些同学不太明白这样来表示有啥好处 CLASS 1 1这其实就是用二进制来表示在上面的代码中TEXT 0000000001CLASS 0000000010STYLE 0000000100
比如一个节点的 class 和 style 都是动态的就给标记上 PatchFlags.CLASS | PatchFlags.STYLE得到 0000000011 。想要判断它的 TEXT 是不是动态的只需要 FLAG TEXT 0 就行。这么看起来只要把 props 的属性做标记好像 JSX 里面也能对 VDOM 做标记了我们来看稍微复杂点的场景。我们看到 textarea 依赖了 attrs所以编译完对应的 PatchFlag 应该是_createVNode(textarea, _mergeProps({id: textarea
}, attrs), null, 16);
单独把这段代码拿出来跑是没问题的但是由于 textarea 的外层还套了一些组件attrs 是单独定义的一个变量并不是响应式的。我们先不管 attrs 这个变量把这段代码当做是模板里面的。在模板编译的时候A 的 children 在编译的时候其实做了一层缓存每次重新渲染的时候不需要再去创建 children 的 VNODE同时对于 children 来说形成了一个闭包。如果这段代码编译的时候把 children 做了缓存会打上一个静态的标记那么 attrs 拿到永远是第一次渲染的值。A{{default: () (// children)}}
/A
所以当点击 1s 的时候并不会触发视图的更新。这个时候只能放弃组件 A 的优化children 不做缓存。因此一旦在某个子节点传入了一个非响应式的变量它的所有父节点的 children 就要放弃缓存因此在每次 re-render 的时候都会重新创建优化并不是很明显。然而上面这种写法在 JSX 中还挺常见的。除了 PatchFlags 之外Vue 里有一个叫 SlotFlags 概念来处理 children 的不同情况。上面的情况需要把 children 标记为 DYNAMIC来放弃对 children 的缓存。因此如果你用 JSX 来写 Vue 的话基本上是享受不到 Vue 3 对模板做的优化。9. 总结参考资料[1]Babel JSX Plugin: https://github.com/vuejs/jsx-next[2]JSX: https://facebook.github.io/jsx/[3]Flow: https://flow.org/[4]vue-tsx-support: https://github.com/wonderful-panda/vue-tsx-support[5]jsx.d.ts: https://github.com/vuejs/vue-next/blob/master/packages/runtime-dom/types/jsx.d.ts最近组建了一个江西人的前端交流群如果你也是江西人可以加我微信 ruochuan12 拉你进群。················· 若川出品 ·················今日话题昨天发了一篇原创文章取消关注7人。发什么都有人取消关注我已经习惯了。交流群里有这样的消息。看到不满意的文章就是随手一个取消关注。看到转载太多的公众号就是随手一个取消关注。看到广告太多的公众号就是随手一个取消关注。公众号号主确实不易。不信的话你可以试着运营一个。欢迎在下方留言~ 欢迎分享、收藏、点赞、在看我的公众号文章~一个愿景是帮助5年内前端人走向前列的公众号可加我个人微信 ruochuan12长期交流学习推荐阅读我在阿里招前端我该怎么帮你现在还能加我进模拟面试群若川知乎问答2年前端经验做的项目没什么技术含量怎么办点击上方卡片关注我、加个星标学习源码整体架构系列、年度总结、JS基础系列