JSX 介绍

jsx 是 javascript 的语法扩展,因为其特性足够好并且学习成本低,Gyron.js 不再自己创造自己的语法,而是适配现有的 jsx 语法糖。

核心库中有两个关于 jsx 的库,他们的作用都是把 jsx 语法转换成运行时的代码,只是运行在不同平台或者插件上而已。

babel-plugin-jsx

babel-plugin-jsx 是将 jsx 语法转换成 Gyron.js 方法的 babel 插件,他和其它类型库一样,都是使用 babel 提供的 visitor 访问者将 JSXElement 等 jsx token 转换成h()可执行函数。

babel-plugin-jsx 还可以在开发环境时生成热更新辅助代码,只需要在配置中使用{ hmr: true }即可。使用此配置后可以在控制台的 source 面板中看到组件函数上声明了__hmr_id属性,这是一个以文件路径和函数名生成的哈希值。 然后在不同平台提供的热更新接口中实现热更新的逻辑。目前核心中只接入了 vite,其它平台同理。

安装

通过包管理平台yarn进行安装。

yarn add -D @gyron/babel-plugin-jsx

使用

  • 你可以在任何接入 babel 的平台上使用,比如在 webpack 插件babel-loader中使用。
module.exports = {
  module: {
    rules: [
      {
        test: /\.(j|t)sx$/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins: [
              [
                '@babel/plugin-transform-typescript',
                {
                  isTSX: true,
                },
              ],
              [
                '@gyron/babel-plugin-jsx',
                {
                  setup: true,
                },
              ],
            ],
          },
        },
      },
    ],
  },
}
  • 或者直接使用@gyron/babel-plugin-jsx导出的babelWebpack方法来进行转换。

首先,编写如下配置文件将babelWebpack进行一个默认导出。

gyron.config.js
module.exports = require('./index').babelWebpack

然后在 webpack 中进行如下配置。

module.exports = {
  module: {
    rules: [
      {
        test: /\.(j|t)sx$/,
        use: [
          {
            loader: './gyron.config.js',
            options: {
              setup: true,
            },
          },
        ],
      },
    ],
  },
}

也可以在node > 14版本中使用 esm 语法转换 jsx 代码。

babel.config.mjs
import babelJsx from '@gyron/babel-plugin-jsx'

export default {
  plugins: [babelJsx],
}

参数

babel-plugin-jsx 接受两个参数以帮助用户转换出更贴切的代码。

  • hmr 是否生成热更新辅助代码。
  • ssr 是否生成 ssr hydrate 时的辅助代码。在服务端渲染完成后调用createSSRContext时自动注入组件参数的辅助功能。
  • setup 是否自动将无状态组件转换成有状态组件

热更新

babel-plugin-jsx 会自动寻找 Gyron.js 中的组件,并且在组件函数上附加一些runtime helper代码帮助 Gyron.js 进行热更新,如果只接入 babel-plugin-jsx 热更新功能还不会生效,还需要知道如何进行rerender。通常我们的做法是识别代码中的comment节点然后插入rerender helper代码进行更新。具体可以参考vite-plugin-jsx的实现。 Gyron.js 会自动寻找组件之间的依赖关系并且在开发环境中对依赖的组件进行更新。

import { FC } from 'gyron'

const App = FC(() => {
  return <div>App</div>
})
// ↓ ↓ ↓ ↓ ↓
const App = FC(() => {
  return h('div', null, ['App'])
})
App.__hmr_id = 'xxaanncc'

插件

我们提供了下面两种平台的开箱即用的插件,方便开发者集成。

这两种插件都集成在babel-plugin-jsx包中,它们以不同的方式导出。并且都支持传入参数控制代码转换。

  • esbuild

esbuild 是一款极快的构建捆绑工具,相比于同类产品可以快 10 到 100 倍。

import esbuild from 'esbuild'
import { babelESBuildJsx } from '@gyron/babel-plugin-jsx'

esbuild.build({
  // ...
  plugins: [
    babelESBuildJsx({
      hmr: true,
      setup: true,
    }),
  ],
})
  • vite

vite 是下一代的前端工具链,可以为开发提供极速响应。

import { defineConfig } from 'vite'
import { babelViteJsx } from '@gyron/babel-plugin-jsx'

export default () => {
  return defineConfig({
    plugins: [
babelViteJsx({
hmr: true, setup: true, }), ], }) }

转换

为了保持最直观的编码习惯,babel-plugin-jsx将会生成一些辅助代码,这些辅助功能需要传递参数进行开启,它们默认都是关闭状态。如果发生预期之外的编码错误,可以尝试关闭这些辅助功能然后检查问题。

Gyron.js中的组件分为两种,有状态和无状态,它们唯一的区别就是组件的返回值是否为一个函数,但是很多时候,我们不需要无状态组件,但是又为了直观的写法,我们可以开启setup功能。

当我们开启之后,代码会进行如下转换:

import { FC } from 'gyron'

const HelloWorld = FC<{ msg: string }>(({ msg }) => {
  function onClick() {
    if (msg === '') {
      console.log('gyron.js')
    }
  }

  return <div onClick={onClick}>{msg}</div>
})
// ↓ ↓ ↓ ↓ ↓
import { onBeforeUpdate as _onBeforeUpdate } from 'gyron'
import { FC } from 'gyron'

const HelloWorld = FC<{ msg: string }>(({ msg }) => {
  // Auxiliary update of deconstructed props
  _onBeforeUpdate((_, props) => {
    var _props = props
    msg = _props.msg
  })

  function onClick() {
    if (msg === '') {
      console.log('gyron.js')
    }
  }

  return ({ msg }) => <div onClick={onClick}>{msg}</div>
})

jsx-runtime

jsx-runtime 是支持现代的 jsx 构建器,一般用于 babel-plugin-transform-react-jsx 插件。

通过yarn安装

yarn add -D @gyron/jsx-runtime

使用

在使用的时候需要注意如果指定了pragma需要导入相关的包。

/** @jsx h */
import { h } from '@gyron/jsx-runtime'

// 然后就可以在代码中使用 jsx 语法了
const App = () => {
  return <div>Hello World</div>
}

这里有一个@gyron/jsx-runtime搭配 vite 的简单用法。

注意一点就是 vite 不会在一开始就加载 jsx-runtime 代码,所以第一次加载界面会白屏,只要刷新一下界面即可。或者升级 vite 到 v3 版本,这个问题就会迎刃而解。

import { defineConfig } from 'vite'

export default ({ mode }) => {
  return defineConfig({
    esbuild: {
jsxInject: `import { h, Fragment } from '@gyron/jsx-runtime'`,
jsxFactory: 'h',
jsxFragment: 'Fragment',
}, }) }