前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >体验了一把华为的 openInula,谈谈使用感受

体验了一把华为的 openInula,谈谈使用感受

作者头像
用户6901603
发布2024-01-11 10:48:25
4170
发布2024-01-11 10:48:25
举报
文章被收录于专栏:不知非攻不知非攻

华为在今年开源了一款类似于 React 的前端框架, openInula。他的宣传语上面,把 openInula大语言模型前端 AI 赋能结合在一起,主打一个高性能、全场景、智能化

果然遥遥领先在宣传语的设计上还是有点水平的。然后我就去了解了一下这个框架。

0

无缝迁移

我想先试一下能不能真的做到无缝切换。如果真的能做到的话,我们就可以非常方便的使用 React 的生态直接搞 openinula 项目了。

然后我在 vite 上随便搞了一个项目,把 openinula 跑了起来。能运行官方文档首页的 demo

然后我在项目中引入了一个 react 生态中最常用的 react-router

代码语言:javascript
复制
yarn add react-router-dom

然后写了一个很小的 demo 想看看能不能跑起来

代码语言:javascript
复制
function ReactiveApp() {
  return (
    <Routes>
      <Route path='/' element={Index} />
      <Route path='child' element={Child} />
    </Routes>
  );
}

结果不出所料。跑不起来

然后尝试修改了一下,发现要改的地方太多了,算了,就算最后改成功,也不是我想要的那种无缝切换的效果,还是比较麻烦。所以想要顺利把 React 生态的东西直接用到 openinula 上也并不简单,需要调整和修改内容。

react 的底层模块区分了 reactreact-dom ,就导致了区别还是比较大。

无缝切换:GG

但是他确实在兼容 React API 上做得比较好,几乎所有常用的 api 都有支持。所以如果只是基于这些 api 写出来的东西应该切换起来难度还是不高的。

1

响应式 API

openInula 还支持了一个响应式 API:

useReactive

响应式 API 其实就是当监听的数据发生变化时,组件函数不需要重新执行。通过这样的方式减少函数执行范围,可以比 diff 少一些逻辑执行。

代码语言:javascript
复制
function ReactiveApp() {
  const renderCount = ++useRef(0).current;

  const data = useReactive({ count: 0 });
  const countText = useComputed(() => {
    return `计时: ${data.count.get()}`;
  });

  setInterval(() => {
    data.count.set((c) => c + 1);
  }, 1000);

  return (
    <div>
      <div>{countText}</div>
      <div>组件渲染次数:{renderCount}</div>
    </div>
  );
}

export default ReactiveApp;

这个 api 比较有意思的他的 getter 和 setter 的设计。

代码语言:javascript
复制
data.count.get()
data.count.set(() => c + 1)

项目经验丰富,对可维护性很重视的同学应该能想得通为什么要设计成这样。因为看上去使用比较麻烦,没有直接像 Vue 那样,通过 Proxy 劫持来省略掉显示的调用 get/set ,所以肯定会给人带来一些疑惑和不解。

代码语言:javascript
复制
data.count
data.count += 1

这样又简洁又舒适,有什么不好。

与 React 非常相似的 Solid.js 也没有这样做。而是选择了另外一种方式

代码语言:javascript
复制
const CountingComponent = () => {
  const [count, setCount] = createSignal(0);
  const interval = setInterval(
    () => setCount(count => count + 1),
    1000
  );
  onCleanup(() => clearInterval(interval));
  return <div>Count value is {count()}</div>;
};

一个最主要的原因是,当项目变得庞大和久远,我们在重新阅读项目或者修改 bug 时,或者阅读别人的项目时,无法在代码逻辑中快速区分普通数据和响应式数据,从而增加了维护成本

如下例所示,我们只有追溯到数据最初声明的地方,才能分清他到底是响应式数据还是普通数据

代码语言:javascript
复制
data.count
data.count += 1
result.count
result.count++

综合来看,从语法上我更喜欢 openinula 的 api 设计

代码语言:javascript
复制
// openInula
data.count.get()
data.count.set((v) => v + 1)

// solid
count()
setCount(count => count + 1)

openInula 还有一个比较重要的问题,就是 React API 和 响应式 API 共存的问题。也就是说,响应式 API 使用的一个很重要的前提,就是函数组件不会重新执行。也就意味着,他们的混用,特别是当 useState 存在于父级中时,会出现严重的混乱。

代码语言:javascript
复制
function ReactiveApp() {
  const [index, setIndex] = useState(0)

  return (
    <div>
      <div onClick={() => setIndex(index + 1)}>index: {index}</div>
      <Child />
    </div>
  );
}
代码语言:javascript
复制
function Child() {
  const counter = useReactive({ count: 1 })
  const p = ++useRef(0).current

  const timer = useRef

  useEffect(() => {
    setInterval(() => {
      counter.count.set((c) => c + 1)
    }, 1000)
  }, [])

  return (
    <>
      <div>Child 执行次数:{p}</div>
      <div>记时:{counter.count.get()}</div>
    </>   
  )
}

也就意味着,他们的共存在使用时一定要非常小心。在这种情况下,useReactive 的存在与 useState 有点犯冲,显得格格不入。或者可以在项目中,尽量避免使用 useState,具体效果如何,还要深度使用之后才能体会到。

2

迁移我的 React 组件库

我在 React 中有一些积累的组件库,然后我把一些常用的迁移到 openInula 中来,经过简单的修改,迁移成功。使用语法没有任何变化

代码语言:javascript
复制
<Icon type='search' color='red' />
<Button type='primary'>hello world</Button>

这样来看的话,确实能够快速将 React 的生态迁移到 openInula 上面来。但是由于我大多数组件都是基于 useState 来编写的,因此,想要使用 useReactive 的话,只能全部替换掉。

代码语言:javascript
复制
- const [display, setDisplay] = useState(false)
+ const display = useReactive({ show: false })

替换掉之后功能基本上没什么毛病。但是在最佳实践的摸索上还存在一些疑问。比如当我想要将一个响应式数据传递给子组件时,下面哪种方式更好一些呢?我还没有一个定论,还需要进一步的体会和摸索。

代码语言:javascript
复制
<Dialog show={data.open.get()}}>hello</Dialog>
代码语言:javascript
复制
<Dialog show={data.open}>hello</Dialog>

第一种方式会更加契合解耦方面的思考,但书写稍微繁琐了一点,第二种方式呢,会对子组件逻辑造成更大的干扰。想到这里,突然之间明白了在 arkUI 里的状态设计,如果从父组件里传递一个响应式数据给子组件时,子组件必须使用 @Prop 装饰来接收这个状态。

这样在子组件中,我们就能够清晰的知道这个数据类型的特性到底是怎么回事了。从而降低了维护成本。这样一想的话,arkUI 在组件状态的设计上,确实有点东西。

代码语言:javascript
复制
@Component
struct ChildComponent {
  @Prop
  private count: number

  build() {
    Text(`Child Count: ${this.count}}`)
  }
}

4

意外之喜

当我试图使用解构的方式来拆解 useReactive 时,居然不会失去响应性。

代码语言:javascript
复制
const {count, open} = useReactive({ 
  count: 0, 
  open: false 
});

const countText = useComputed(() => {
  return `计时: ${count.get()}`;
});

setInterval(() => {
  count.set((c) => c + 1);
}, 1000);

这可就解决了大问题了!当数据变得庞大,它的繁琐的程度将会大大的降低。所以在使用上会比 solid.js 方便许多。

我了解到的 Vue3Solid 实际上在这一点上都做得不是很好,解构之后,Vue3 的状态会失去响应性。

代码语言:javascript
复制
// 直接使用 count 无法具备响应性
const {count} = reactive({ count: 0 })

Solid 的 API 设计,又无法做到把颗粒度细分到每个子属性

代码语言:javascript
复制
const [count, setCount] = createSignal({n: 1});

function clickHandler() {
  setCount({ n: count().n + 1 })
}

所以,当需要更细的属性时,Vue3 可能会更多的使用 ref 来做,而 solid 则与 useState 一样,单独声明这个属性。

这么横向一对比,openInula 的响应式 API 就有点厉害了。在设计上充分体现了自己的独创性和先进性,如果其他方面不出什么问题的话,应该会受到一大批程序员的喜爱。

不愧是遥遥领先。 666。

3

总结

openInula 的使用体验与 React 几乎一样。与 React 不同的是,他增加了一个响应式 API。因此能够增加一些不同的开发体验。也正是由于这个响应式 API 的存在,让 openInula 在 API 设计上有了自己的独创性。

与其他响应式框架相比,我更喜欢 openInula 的 API 设计,在开发体验与维护体验的综合考虑上目前是做得最好的,虽然为了考虑维护体验牺牲了一些开发体验,不过我完全能接受。由于接触了几款华为的框架,可以感受到,他们在设计 API 时,会把可维护性的重要性看得比开发体验更高。

当然,svelte 我还没有怎么了解过,不过有听到坊间传言说是模仿 Vue3 的,那估计设计模式跟 Vue3 差别不算大。

代码语言:javascript
复制
var { count, a, b, c } = useReactive({
  count: 1,
  a: 1,
  b: 1,
  c: 1
})

count.set((v) => v + 1)
count.get()

a.set((v) => v + 1)
a.get()

b.set((v) => v + 1)
b.get()

c.set((v) => v + 1)
c.get()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-01-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 这波能反杀 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0
  • 1
  • 2
  • 4
  • 3
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档