在 React 中,我制作了一个可以用 JSX 编写的库,用于在过度规范的情况下使用

React 和 SolidJS 等框架很有用。

社区和生态系统已经发展壮大,如果你用谷歌搜索,你会发现很多文章。

但是,根据场景,有许多不必要的功能。

所以,DOM 渲染什么时候JSX语法精髓一个超轻量级的库,只专注于JS博士我试图创造

▼ NPM 包是

▼ GitHub 仓库如下

特征

超轻

截至 2022/08/12,BundlePhobia 使用 zgip 压缩(未压缩 1.12kB)测量 657B。

我知道比较是冒昧的,但我也将其他库与 BundlePhobia 进行了比较......

图书馆名称未压缩大小zgip 压缩大小评论
JS博士1.12kB0.7kB*1
反应 + 反应 DOM6.4kB + 130.5kB2.5kB + 42kB*2
预演10.3kB4kB
Vue.js95.3kB34.2kB
苗条4.5kB1.7kB
SolidJS20.1kB7.1kB

*1 这次创建的图书馆

*2 如果你在浏览器上使用它,你需要使用 React DOM 作为一个集合。

您会发现与其他库相比,它非常轻量级。

但是,即使添加到上述库webpack这样的项目中,大小也不会像JS文件大小(Tree Shaking)那样增加,因此仅供参考。

可以写成 JSX 格式

很难说这是否可以称为优势。

例如,<div className='sample'>TEXT</div> 在 React 中是

React.createElement('div', {className: 'sample'}, 'TEXT')

它只是语法糖,因此您最终将创建一个采用与此相同参数的实现。

出于这个原因,Hiroshi JS 可以用与 React 非常相似的方式编写,所以我认为可以肯定地说,一旦你接触了 React,几乎没有学习成本。

任何接触过 TypeScript 的人都可以实现的范围内的源代码

我敢肯定你不是唯一一个对 React 源代码发疯的人。

Hiroshi JS 是用 TypeScript 编写的,但是写的量非常少,所以我想大多数人都能看懂。

“我不知道为什么,但我很害怕 React 正在运行!”或者“如果 React 有一个致命的错误怎么办?”。

前者我还是懂的,但不知道还有没有后者的人……

▼截至2022/08/12,只写了97行处理。

假设用例

使用 jQuery 等将 HTML 作为字符串输出时。

它是由看到有人实现了以下 JS 触发的。

你还在用 jQuery 吗?我没有反思想的想法,但我不会用字符串组装HTML,(除了我是否咬JSX语法糖),我根据函数组装和输出HTML,当时它随意转义。认为如果有这样的机制,XSS漏洞是可以预防的。

// ※href や text 変数はエスケープされていないものとする
const href = '"><script>alert("アラート!!");</script>';
const text = '<style>* {display: none !important;}</style>';

// これでアラートが発火して、画面が真っ白になってしまう
// 各変数に replace を噛ましてエスケープするでも良いが、ヒューマンエラーも発生しそう……
$('#app').append('<a href="' + href + '">' + text + '</a>');

如果您浏览新创建的库,它将作为一种机制为您进行转义。

// 後述の手段で今回作成したライブラリの createElement メソッドを読み込んだ上で
// createElement は名称が長いので h 変数に突っ込む
const c = createElement;

const href = '"><script>alert("アラート!!");</script>';
const text = '<style>* {display: none !important;}</style>';

// h (createElement) 関数内でエスケープされる仕組みを提供
// ※ 以下の記述は JSX 構文を使用しない記述となります
$('#app').append(c('a', {href: href}, text));

如果每个人都不使用 jQuery 并使用 React、Vue.js 或 SolidJS,那就太好了,但世界上超过 80% 的网站1使用jQuery,所以我认为有很多地方可以使用这个用法示例。

基于 API 的 JS 的 HTML 输出,但如果之后状态没有变化

虽然 HTML 是基础后端输出的,但我们假设这个库在 API 传递部分内容,JS 渲染,以提高 CDN 的缓存命中率等情况下会有用,我来了。

如果你需要根据用户的操作响应式地重写 HTML,你可以使用 React、Vue.js 等,但是如果你只是想接收数据并将其输出为 HTML,那些库是过度规范的,这会导致JS 文件的包大小意外膨胀。

(如上所述,即使 Tree Shaking 在某种程度上与 webpack 之类的打包器一起工作,我认为也有一个限制,但实际上发生了什么......我想围绕这个积累知识。)

import * as React from 'react';
import {createRoot} from 'react-dom/client';

const App = props => {
  const [apiData, setApiData] = React.useState([]);

  // API を叩いて表示する
  React.useEffect(() => {
    fetch('/api/v1/path/to/api')
      .then(res => res.json())
      .then(res => setApiData(res));
  }, []);

  return <>{apiData.map(item => <Card {...item}/>)}</>;
};

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);

我认为它会在诸如点击这样的 API 并显示它的情况下变得活跃起来。

用法

CDN(可选)

▼ 如果不想用ES模块(怎么看<script src="xxx">

* 看完上面的js文件,就可以用Hiroshi对象访问了。

▼ ES模块格式(怎么写import xxx from 'xxx'

JSX 格式

如上所述,它可以与 CDN 一起使用,但我们将继续解释,假设包是使用 npm 导入的。

安装 Hiroshi JS 后,

$ npm install hiroshi

相应地调整模块捆绑器或转译器中的 JSX 编译选项。

tsconfig.json TypeScript 中的调整示例

* typscript 安装包后的设置(摘录)。

{
  "compilerOptions": {
    "jsx": "preserve",
    "jsxFactory": "createElement",
    "jsxFragmentFactory": "Fragment",
  }
}

Babel 调优示例

* @babel/core@babel/preset-env@babel/plugin-transform-react-jsx 安装包后的设置(摘录)。

{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "pragma": "createElement",
      "pragmaFrag": "Fragment",
    }]
  ]
}

说设置很麻烦的人

如果您将 Hiroshi JS 加载为 React,它将起作用。

* 未验证对TypeScript等类型设置严格时是否会输出错误。

import * as React from 'hiroshi';
const {createElement, Fragment, createRef} = React;
import {createElement, Fragment, createRef} from 'hiroshi';

以上两个描述运行相同的过程,因此请相应地阅读它们。

执行

由于它没有像 React 那样使用虚拟 DOM,而是直接返回原始 DOM,所以可以描述如下。

当然,字符串转义也是任意进行的,所以除非你故意插入危险处理,否则实现是不会发生XSS的。

import {createElement, Fragment, createRef} as React from 'hiroshi';

const style = {
  marginBottom: "0.5rem",
  border: "solid 1px #aaa",
  padding: ".5rem",
  flexBasis: "12rem"
};

const Card = (props) => (
  <div className="card" 
       style={style}
       onClick={e => alert(`Click: ${props.name}`)}>
    {props.name}: {props.children}
  </div>
);

const UserList = () => {
  const ref = React.createRef();
  fetch("//jsonplaceholder.typicode.com/users")
    .then((response) => response.json())
    .then((res) => {
      // API が読み込まれたら DOM を生成して ref を用いて置き換える
      const {current} = ref;
      current.replaceChild(
        <>{res.map(({ name, username }) => (
            <Card name={name}>{username}</Card>
          ))}</>,
        current.firstElementChild
      );
    });

  // API 読み込み中のメッセージを出しておく
  return (
    <div
      className={"userList"}
      
      ref={ref}
    >
      <span>Now loading...</span>
    </div>
  );
};

document.getElementById("app").appendChild(UserList());

我将省略详细解释(我可能稍后会添加),但我正在为 React 中的以下方法创建兼容性。

还支持回调引用,因此您可以使用以下描述进行操作,而无需使用createRef

const App = () => {
  const callback = node => {
    // ref 属性で指定した node が取得できる
    console.log(node);
    // do something
  };

  return <div ref={callback}>Now Loading...</div>
};

JSX 未使用的格式

我将省略详细说明,但可以不使用 JSX 格式来编写。

它甚至可以在不使用捆绑器、转译器等的项目中使用。

以下输出结果是等效的。

import {createElement as c, Fragment} from 'hiroshi';

const jsx1 = <div className='sample' dataSample='hoge'>TEXT</div>;
const notJsx1 = c('div', {className: 'sample', dataSample: 'hoge'}, 'TEXT');

const jsx2 = <>
  {items.map(item) => <Card {...item}/>}
</>;
const notJsx2 = c(Fragment, null, 
  ...items.map(item) => c(Card, {...item})
);

const jsx3 = <div>
  <div>ITEM 1</div>
  <div>ITEM 2</div>
</div>;
const notJsx3 = c('div', null, ...[
  c('div', null, 'ITEM 1'),
  c('div', null, 'ITEM 2'),
]);

关于未来

当前版本为0.0.x,但根据本文的声誉,一旦测试代码和严格类型定义(TypeScript 支持)可用,版本将为1.0.x,文档将相应扩展。我'我想

在最后

如果您发现任何错误,请打开一个问题或拉取请求。

我希望这个世界上所有的网站都像一个当红男演员的主页一样轻,在某个主页上显示出爆炸性的速度。

如果这个库可以在使网站更轻松方面发挥作用,我会很高兴。

  1. JavaScript | 2021 | HTTP 存档的 Web Almanac

原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308623201.html