权力框架的传奇

最近, “消失的框架”的趋势越来越流行,毫无疑问,可以将其机车视为SvelteJS-一个构建时框架和一个普通的javascript编译器。

尽管从概念上讲Svelte非常简单,甚至更易于使用,但许多开发人员仍在想这个框架的杀手级功能是什么,以及整个方法是什么? 为什么这不是“另一个JavaScript框架”?

在本文中,我将讨论许多可以严重简化生活的Svelte超级大国之一。

让我们弄清楚,但首先我会告诉你一个传说...



权力框架的传奇


一些框架是由Google和Facebook的人创建的,其他框架是很酷的家伙,但都在Rich Harris的密切关注下

为人类创建了九个框架,其中七个似乎是为矮人建立的。 其他三个框架(反应,vue,角度)适用于精灵。

创建框架并在数千个项目中实施它们之后,Rich Harris亲自秘密创建了一个框架...

一个框架来统治所有人,
找到它们的一个框架,
一个框架将它们全部融合
并捆绑在一起。

-框架之王

问题


我敢肯定,许多长期从事前端开发的人已经反复遇到了为当前和/或下一个项目选择工具的问题。
各种软件包,实用程序,鲸鱼,框架,库和其他解决方案的种类都前所未有地规模化。 最重要的是,所有这些运动都在继续加速。

所有这些,一种或另一种方式,都适用于框架的选择。 如果我假设只有几个现代团队和公司在不使用任何js框架的情况下启动新项目,则可能不会弄错。 当然,当涉及现代Web应用程序时,不仅限于网站。 如果项目中的很多内容都不依赖于此,那么一切都会很好。


自行判断,团队的组成和特征在很大程度上取决于您选择的框架,并且整个狩猎过程当然也取决于。 有时预算和时间表甚至取决于此。 简而言之。

但是真正的问题开始了,如果在项目中间的某个地方,您会意识到您做出了错误的选择。 某些东西没有一起成长并旋转。 该框架需要花费更多的时间来掌握,团队要稍大一些,结果速度变慢了一些,不适合您的目标或开发风格等。 最重要的是,现在您的项目已100%与该框架相关联,您不能仅仅采用它并将其重写。

更令人反感的是,尽管成功完成了该项目,但您了解到,总的来说,您不是很高兴。 也许我们不想在相同的框架上编写下一个项目。 因此,我们所追求的所有“重用”解决方案都可以投入到管道中。

实际上,带有实现特定业务任务的业务代码的地狱工作正常。 但是您写了一个很酷的“%向社会责任低的二十一点和女孩插入%”,并且您想在下一个项目中使用它,但是这种感染与您正在研究一种的当前框架紧密相关。

相同问题的另一个变体-假设您是一家大型公司,例如Yandex。 您有很多项目,甚至有些项目只有某些员工才知道,而且每个项目都已经经历了我上面描述的一切。 问题在于所有这些项目都坐视其最初选择的不同框架。

这是您出色的领导,决定与Google Material Design竞争,并带您深入研究项目的各种界面,以使它们成为共同点。 狡猾的设计师已经在绘制新的按钮和选择器,并为您的组件的新单一UI套件编写了数千页的准则。 同志们!

不是生命,而是童话,对吗? 剩下的只是想出如何在您已经设法在所有可能的框架上编写的所有那些项目中提取所有这些新组件。 如果确实有很多时间和金钱,并且有审美上的需求,最重要的是相信“所有内容都需要统一”,那么您可以派出几十个团队来重新编写所有这些内容,例如在React上。 这是正确的,因为您在过去2-3年中撰写的枯燥乏味在道德上已经过时了,但是React将会永远存在。 好吧

还有另一种方式。 您可以在一个框架上编写一个很棒的新UI-kit,按原样创建一个可重用组件的库,然后只需在所有项目中使用此UI-kit。 听起来不错? 当然,但是仍然存在一个问题-运行时。

如果您的项目是用Angular(〜500Kb)编写的,而您决定用React(〜98Kb)编写一个UI-kit,然后将每个项目拖入一个框架,另一个框架甚至是一堆依赖项中,则UI-kit本身就是直接的假设它看起来不是最佳的。

解决方案


为了帮助我们建立非常“消失的”框架,而没有运行时。 这里的主要条件是,就其生态系统而言,它们应尽可能孤立,并具有外部集成机制和相应的API。

这种框架的一个很好的例子是SvelteJS,关于它的很多文章已经在Habré上

因此,想象一下在React上有一个应用程序的情况。 也许我们已经厌倦了它,并且想要摆脱它,但是立即重写所有内容是不可接受的。 也许应用程序的某些部分需要改进或重构。 好吧,或者我们决定创建一个单一的组件库,因此现在我们将用Svelte编写所有组件并在所有项目中使用。 赠送? 是的,当然不是,没有人有这样的幻想。 让我们看一个真实的例子。


免责声明
我想立即引起您的注意,因为我不是React开发人员,而我上一次“感觉” React早在2015年。 因此,我想我编写React示例的一部分的方式可能会损害相信反动派的感觉。 很抱歉,您不能严格判断,尤其是因为本文的意思并没有因此改变。


因此,任务是将现成的Svelte组件实现到React应用程序中,而无需更改组件本身,也无需在应用程序中包装额外的运行时。 例如,我将使用搜索GitHub用户的组件,该组件是我为上一篇文章“如何在不使用React + RxJS 6 + Recompose的情况下在GitHub上搜索用户”编写的

可以在REPL中查看该组件的代码,以及从本文到存储库的示例代码。

创建反应应用


首先,使用事实上的标准工具-create-react-app创建一个新的React项目:

npx create-react-app my-app cd my-app npm start 

好的,如果您访问第3000个端口,它似乎可以工作。

定制苗条


如果您对Svelte一无所知,那么在Svelte任务的背景下,我会说这只是收集器的另一步骤(webpack / rollup / gupl / grunt /等),这将使您能够以SFC格式编写组件并在香草中进行编译javascript。

在Svelte社区中,更喜欢Rollup,这并不奇怪,因为他们有一位作家-Rich Harris。 但是,由于CRA使用webpack,我们将通过它配置Svelte。 为此,您首先需要将webpack配置从react-scripts传输到项目,以便我们可以对其进行更改。 这是使用内置命令完成的:

 npm run eject 


据我所知,这不是一个粗略的方法,但是例如,这是最方便的选择。

现在,Webpack配置位于项目的根目录下,您可以安装Svelte:

 npm i --save-dev svelte svelte-loader 


注意--save-dev标志,请记住,没有运行时。))))

最后一点,您需要在配置中连接适当的加载器:

  { test: /\.svelte$/, use: { loader: 'svelte-loader', } }, 


通常,在Svelte社区中习惯于编写扩展名为.html的组件文件,因为Svelte组件是有效的HTML文件。 但是,为了避免潜在的冲突,在某些情况下,最好使用自定义.svelte文件格式

因此,我们做到了,现在项目中包含的所有.svelte文件都将被此加载程序拦截并由Svelte编译。

编写一个Svelte组件


首先,最好配置代码编辑器,例如,以便将html语法突出显示应用于具有相应扩展名的文件。 VS Code中完成了这样的事情:

  "files.associations": { "*.svelte": "html" } 

现在,创建一个文件夹./src/svelte_components/,然后在其中放置组件本身的文件夹。 之后,我们只需将所有文件从REPL示例传输到此文件夹,同时为它们提供一个新的扩展名.svelte ,然后将其命名为App.html Widget.svelte。

结果应该是这样的:


在同一位置,我们创建index.js文件,其中将包含Svelte和React集成代码。

整合


也许现在您想知道什么是魔术? 魔术是我们已经完成了所有魔术。 神奇地,不是吗?

认真地说,现在我们可以在React应用程序中将Svelte组件用作完全普通的JS构造函数,这意味着与Svelte的集成代码与与任何其他独立版本的集成没有什么不同。 React文档甚至包含专用于此的部分: 与其他库集成

集成代码可能如下所示:

 import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { const { username } = this.props; this.widget = new Widget({ target: this.el, data: { username } }); } componentWillUnmount() { this.widget.destroy(); } render() { return ( <div ref={el => this.el = el}></div> ); } } 

总的来说,我们只是将复杂的Svelte组件的代码包装在一个非常简单的React组件中,该组件简单地创建了Svelte组件的新实例,并在创建道具时从props传递了mount元素和数据。 此外,我们不要忘记在componentWillUnmount挂钩中卸载Svelte组件。

我们还没有做的唯一事情是,它们没有同步组件状态的值。 也就是说,如果父组件将其他道具扔到包装器组件中,那么它们应该应用于Svelte组件。 相反,如果在Svelte组件内部更改了数据,则必须回滚数据。

为此,我们假设较高级别的React组件将传递onChange回调,当内部发生更改时应拉取该onChange回调,并且我们期望componentWillReceiveProps挂钩中包装器组件的props发生更改。 让我们做吧:

  componentDidMount() { ... this.widget.on('state', ({ current: { username }, changed }) => { if (changed.username) { this.props.onChange({ username }); } }); } componentWillReceiveProps({ username }) { this.widget.set({ username }); } 

在这里,我们使用了状态内置事件,该事件 Svelte组件的状态每次更改时触发。 包含组件当前状态( current ),先前状态( previous )和已更改属性列表( changed )的对象将传输到回调。 因此,我们只需检查用户名是否已更改,然后调用onChange 回调即可

componentWillReceiveProps挂钩中,我们使用内置的set()方法设置新的用户名

除了内置功能之外,Svelte组件还可以实现自定义事件和方法。 这些好用的功能可以让您描述组件的界面,并且可以方便地与“外部世界”进行交流。

使用方法


现在,让我们尝试直接在React应用程序中使用我们的小部件。 为此,请编辑启动程序生成的App.js文件:

 import React, { Component } from 'react'; import './App.css'; import GithubWidget from './svelte_components/GithubWidget'; class App extends Component { constructor() { super(); this.state = { username: '' }; } handleChange = (state) => { this.setState({ ...state }); } render() { return ( <div className="App"> <header className="App-header"> <h1>Github Widget for: {this.state.username}</h1> <GithubWidget onChange={this.handleChange} username={this.state.username} /> </header> </div> ); } } export default App; 

简而言之,我们将其用作常规的React组件。 结果,我们得到:


已经不错了,对吧?)请注意,我们在小部件的文本字段中输入的用户名值会立即转发到React应用程序。

我们将完成


现在让我们教我们的小部件不仅搜索和显示GitHub用户卡,还搜索和显示存储卡。

首先,您需要创建一个新组件Repo.svelte,它将绘制存储库卡。 为简单起见,我只是从User.svelte复制了模板和样式,并将其调整为存储库数据。 但是,从理论上讲,这是一个单独的组件。

接下来,您需要教Widget.svelte控件组件来即时切换这两种类型的卡。 另外,您需要教他对用户和存储库提出不同的请求。

我们将使用一个字段进行输入,并通过值中是否存在“ /”来确定数据类型。 也就是说,如果您需要搜索用户,请输入其用户名,如果存储库,则输入用户的用户名,然后输入“ /”和存储库的名称。

乍一看,它看起来很混乱,但是在Svelte上,该解决方案实际上需要5-6行代码。 首先,让我们连接一个新的组件和API方法,将它们包装在debounce中:

 ... import Repo from './Repo.svelte'; ... import { getUserCard, getRepoCard } from './api.js'; ... const getRepo = debounce(getRepoCard, 1000); 

接下来,创建一个计算属性,该属性将确定我们应显示的卡类型:

 computed: { ... repo: ({ username }) => username.includes('/'), ... } 

现在,将请求开关添加到API:

 computed: { ... card: ({ username, repo }) => username && (repo ? getRepo : getUser)(username), ... } 

最后,根据类型切换卡组件:

 computed: { ... Card: ({ repo }) => repo ? Repo : User, ... } 

另外,为了动态替换组件,我们需要使用特殊的Svelte标记,该标记绘制其值传递给this属性的组件:

 <svelte:component this={Card} {...card} /> 


可以用 你注意到了吗? 我们已经在React应用程序中编写Svelte! )))

现在让我们教我们的小部件隐藏输入字段,并尝试在小部件内部而不是React应用程序内部输入用户名。 我们的业务逻辑如何获得此价值是否重要?

我们引入了一个新的搜索属性,其默认值为false。 根据此属性,将显示或不显示输入字段。 因此,默认情况下,将没有字段。

 {#if search} <input bind:value=username placeholder="username or username/repo"> {/if} ... <script> export default { ... data() { return { username: '', search: false }; }, ... }; </script> 

现在在App.js中,我们将在应用程序的React端创建一个输入字段,并为输入事件编写相应的处理:

  ... handleUsername = (e) => { this.setState({ username: e.target.value }); } ... <h1>Github Widget for: {this.state.username}</h1> <input value={this.state.username} onChange={this.handleUsername} className="Username" placeholder="username or username/repo" /> 

并使用小部件将其复制到文件夹中,这是Svelte上的此类svg微调器:

 <svg height={size} width={size} style="animation-duration:{speed}ms;" class="svelte-spinner" viewbox="0 0 32 32" > <circle role="presentation" cx="16" cy="16" r={radius} stroke={color} fill="none" stroke-width={thickness} stroke-dasharray="{dash},100" stroke-linecap="round" /> </svg> <script> export default { data() { return { size: 25, speed: 750, color: 'rgba(0,0,0,0.4)', thickness: 2, gap: 40, radius: 10 }; }, computed: { dash: ({radius, gap}) => 2 * Math.PI * radius * (100 - gap) / 100 } }; </script> <style> .svelte-spinner { transition-property: transform; animation-name: svelte-spinner_infinite-spin; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes svelte-spinner_infinite-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } </style> 

我们将其应用到小部件中,以使其非常美观:

 ... {#await card} <Spinner size="50" speed="750" color="#38b0ee" thickness="2" gap="40" /> {:then card} ... 

在我看来,结果不是很糟糕:


带有黑色背景和输入字段的高顶礼帽是React应用程序,下面的白色块是Svelte小部件。 这些是馅饼。 )))

资料库

结论


Svelte是开发基于组件的现代Web应用程序的绝佳工具。 此外,您可以在其上快速方便地编写可重用的独立UI组件和小部件,这些组件可以在任何Web应用程序中使用,甚至可以与其他框架结合使用。 对于微型战线也很棒。

如果满足以下条件,Svelte非常适合您:


  1. 您想开始一个新项目,并且不知道为此选择哪个框架。
  2. 您有一个项目,它可以正常工作,最好不要碰它。 您可以用Svelte编写新组件和模块,并将其无缝集成到现有代码中。
  3. 您已经有一个项目,但是它已经部分或完全过时,并且/或者需要认真的重构,直到完全重写。 您可以开始对其进行部分重写。 您无需提出复杂的配置。 您只需获取一些组件,将其重写为Svelte,然后将新组件与旧组件包装在一起即可。 但是,应用程序的其余部分甚至都不知道这些更改。
  4. 您有多个基于不同代码库的项目,并且同时希望拥有一个UI-kit并将其用于所有这些项目中。 在Svelte上编写UI-kit并在任何地方使用。 很好

是否想找出更多有趣的案例? 加入我们的电报频道

更新:感谢justboris的正确问题 。 继续示例:

 import React, { PureComponent } from 'react'; import Widget from './Widget.svelte'; export default class extends PureComponent { componentDidMount() { ... this.widget = new Widget({ target: this.el, data: { username }, slots: { default: this.slot } }); ... } ... render() { return ( <div ref={el => this.el = el}> <div ref={el => this.slot = el}> {this.props.children} </div> </div> ); } } 


 <GithubWidget onChange={this.handleChange} username={this.state.username}> <p>Hello world</p> </GithubWidget> 

Source: https://habr.com/ru/post/zh-CN420113/


All Articles