从学习曲线角度来说,结合我我的体会,React 学习路线是比 Vue 陡峭的,这个和 JSX、Template 有关吗?固然有。在 React 中使用 JSX,众所周知, JSX 须要通过 Transform 才能在浏览器中运行。立刻就有小伙伴反驳了,Vue 有官方的 Vue-cli, React 使用 create-react-app 初始化项目就行了呀,并不须要比 Vue 多学习其余工具呀。css
咱们从另外一个角度来看这个问题,使用 JSX 还须要熟练 ESM,这是绕不开的,由于一个 React 组件就是一个 js 或 JSX,这可能会给新手带来疑惑,组件是如何连接的?先别着急反驳,接着往下看。html
仅仅掌握“三剑客”的新手就能够轻松上手 Vue,这在 Vue 官方文档中有所体现。vue
巧妙之处就在与上图中的 “易用”,仅仅学会了 HTML、CSS、JavaScript 就能上手。Vue 的“渐进式”不只体如今跟随项目的复杂度上的,还有很大程度上适应使用者的水平。这不是空穴来风,当年鄙人就仅学习了这“三剑客”,就用 Vue-cli 初始化并完成了小项目。现在我思考在当时 Vue 是如何帮我避开 ESM 的。答案在于 vue 的“三段式结构”。react
<script> export defalut { // ... } </script>
个人注意点彻底在于导出的这个对象之中以及上方的 Template 和下方的 Style,只知道在这个对象里面写一些 Data、Methods 等等。甚至彻底没有在乎 export defalut
这个关键字。git
不只如此,Vue 的 Template + Options API 基本就规范了处理一些逻辑时的大致形式,这在经过搜索引擎检索一些遇到的问题时,一般可以轻易看懂检索到的代码,甚至能够“依葫芦画瓢”,这对新手无疑是一个优点。相比 JSX, 写法上,Template 虽不可以从“优雅”上胜出,但 JSX 更容易带入我的编码风格,这应该能使你们有所认同,这也就加大了新手的理解难度。github
回到问题,那么 Vue 是如何引入组件的呢?做为新手,我只会“依葫芦画瓢”,经过一条 import 语句导入进来,而后在 components 里面将组件注册,这一切都被我天然得误认为是 Vue 的能力。typescript
这不表明使用 Vue 不须要学习 ESM,没错,这是究极必要的,熟悉 ESM 能够更好得组织项目代码。上面我只是在思考当年 Vue 是怎么帮我避开 ESM 的,或者说我做为新手时是如何毫无察觉地避开它的,Template “功不可没”。express
题外话,Vue 的 Template 能够放到 Script 标签下方,这可能更符合 React 用户的直觉,看尤大在推上分享一些 Demo 时有时会这么写。若是您乐意,也能够这么作,但在多人协做的项目中还要考虑小伙伴的感觉。浏览器
<script> export defalut { // ... } </script> <template> </template>
所谓“萝卜白菜,各有所爱”。在 React 中只能使用 JSX,Vue 从 2.0 开始干脆把 JSX 也支持完事了。有人说,在 Vue 中使用 JSX 为何不直接使用 react? 这个话题就大了,这不只和项目的技术选型有关,且 Vue 和 React 设计上就有很大差异。在 Vue 中不管使用 Template 仍是 JSX 都会被编译成 h 函数(在 Vue 2 中称为 render 函数)。因此,在 Vue 中使用 JSX,只是换了一种表现形式而已。app
咱们来看一下 Vue 将 Template 渲染到浏览器的过程。
<div>Hello</div>
这个HTML也能够经过一个虚拟节点 VNode 来表示,也就是用 JavaScript 对象的形式来表示。
{ tag: 'div', children: [ { text: 'Hello' } ] }
Vue 知道如何将此虚拟节点并挂载到 DOM 上,它会更新咱们在浏览器中看到的内容,可是 VNode 从哪里来的呢?实际还有一个步骤,Vue 基于咱们的 Template 建立一个渲染函数,返回一个虚拟 DOM 节点。
渲染函数能够是这样的:
render(h) { return h('div', 'hello') }
当组件更改时,Render 函数将从新运行,它将建立另外一个虚拟节点。而后发送旧的 VNode 和新的 VNode 到 Vue中进行比较并以最高效的方式在咱们的网页上更新。例以下图所示,仅仅更新了文本内容。
题外话,实际 Vue 有三个核心模块:
响应式模块容许建立 JavaScript 响应对象并能够观察其变化。当使用对象的代码运行时,它们会被跟踪,所以,它们能够在响应对象发生变化后运行。
编译器模块获取 HTML 模板并将它们编译成渲染函数。这可能在运行时在浏览器中发生,但在构建 Vue 项目时更常见。这样浏览器就能够只接收渲染函数。
渲染模块在网页上渲染组件经历三个不一样阶段:在渲染阶段,将调用 render 函数,它返回一个虚拟 DOM 节点;在挂载阶段,使用虚拟 DOM 节点并调用 DOM API 来建立网页;在补丁阶段,渲染器将旧的虚拟节点和新的虚拟节点进行比较并只更新变化的部分。
上面是 Vue 将 Template 渲染到浏览器的过程,和 Template 相似,在 Vue 中使用的 JSX 也会转化成 h 函数,眼见为实。
因此,了解这个过程,您可能不是那么排斥在 Vue 中使用 JSX 了,或者或许对它在 Vue 中的表现不是那么陌生了。
从另外一个方面来说,Vue 中的 JSX 和 React 中的 JSX 在写法上也有差异。在 Vue JSX 中您能够直接使用熟悉的指令(directives),例如 Vue 内置的 v-show
、 v-model
甚至 v-models
和自定义指令,但例如 slots 在 Vue JSX 中或许没有在 React 中那么方便。
在 Vue sfc(single file component)中同时编辑 script、template、style,这提供了“联合”的能力,即在一个文件中使 Style 和 Template 共享 Script,帮助开发者更容易实现业务。例如:rfcs-style-variables,该提案虽然尚未最终肯定下来,但 Vue 展现了这种能力。基础示例:
<template> <div class="text">hello</div> </template> <script> export default { data:()=> ({ color: 'red', font: { size: '2em' } }) } </script> <style> .text { color: v-bind(color); /* expressions (wrap in quotes) */ font-size: v-bind('font.size'); } </style>
经过 style-variables 写有一个有趣的示例,此处为部分代码。
<script> import { defineComponent, ref } from 'vue' export default defineComponent({ setup() { const inputBorderColor = ref('') const onEngineChange = engine => { for (const { name, color } of enginesData) { if (name === engine) { inputBorderColor.value = color return } } } return { inputBorderColor, } }, }) </script> <style lang="scss"> input { &:focus { border-color: v-bind(inputBorderColor); } } </style>
若是在 Vue 中使用 JSX, 就没法利用这个优点了。题外话,在 React 中相似的 css-in-js 方案是 styled-component。
Vue 几乎全部面向业务构建的生态内容给出的示例代码都是基于 Template 的。咱们一般在项目中安装 UI 组件库依赖,组件库中给出的示例代码也是基于 Template 的。当前 Vue 组件库文档示例代码正在由 Options API 向 Composition API 过渡,但几乎没有可能从 Template 向 JSX 过渡,或者向用户提供 JSX 的组件示例代码。这很是重要,若是您的项目依赖组件库,查看文档后还须要将 Template 转到 JSX,这无疑增长开发成本。
在 script 中,vue3 相比 vue2 已经出现质的飞跃。从 Template 来看,Vue 还有待完善,但这个可行的。JSX 一开始也没有类型支持,彻底是 TS 给加了一套针对 JSX 的推导机制。
模版和类型推导,表面上看,隔了一层模版语法 + 编译,彷佛确实存在 “断层”,但其实里面没您想的差那么远。Vue 的模版是编译成 virtual dom 渲染函数的,生成的 js 跟 React 的渲染函数同样能够类型推导,而模版跟生成的 js 之间是完整的逻辑映射,因此这里其实主要是须要作一些工具链上的衔接,把对生成的 js 分析出来的 intellisense 反馈到 IDE 里的模版上。技术上是彻底可行的。
vscode 插件 Volar 已经支持 Template 表达式的类型检查。Vetur 虽然不支持还未定稿的 RFC,也已经支持基于现有 API 的模板类型检查和组件 props 类型检查了。另外,在 vue3 中不管是否使用 TS,经过 defineComponent 定义组件都能得到更好的提示。
能够预见的是,Vue 从升级 3.0 后,对于 TypeScript 的支持将会愈来愈好。
在大多数场景下(尤为是业务场景)使用 Template 多是更好的选择, vue3 基于 Template 分析作了大量的优化,而使用 JSX 须要手动作一些优化。
JSX 因为更具灵活性,一般一些组件库的不二之选,例如:Ant Design Vue、Vant、Element Plus(部分使用)。看到这么个例子: 根据 props 上的 reverse 属性,决定是否要调换两块内容的顺序。使用 JSX 轻易就能实现,且可读性也很高。
const renderContent = () => { const Content = [ <div class="foo">Foo DOM...</div>, <div class="bar">Bar DOM...</div>, ]; if (props.reverse) Content.reverse(); return <div>{Content}</div>; }
若是经过模板来实现,在不抽象子组件的状况下,foo 和 bar 的模板结构须要重复写两遍,才能知足这个需求:
<template> <div> <template v-if="reverse"> <div class="bar">Bar DOM...</div> <div class="foo">Foo DOM...</div> </template> <template v-else> <div class="foo">Foo DOM...</div> <div class="bar">Bar DOM...</div> </template> </div> </template>
所以,在动态性强的场景下,JSX 会有必定优点。Composition API + JSX 是某些场景下追求极致的选择,相应地须要付出更多开发成本。
参考资料:
[1] https://www.zhihu.com/question/436260027/answer/1647182157
[2] https://www.zhihu.com/question/310485097/answer/591869966
[3] http://www.javashuo.com/article/p-kdmdgmyd-kq.html