npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vuelovesvelte

v0.0.5

Published

VueLoveSvelte 是一个基于AST的编译器,支持把 Vue DSL组件无痛编译为 Svelte 组件。

Downloads

3

Readme

VueLoveSvelte编译器

VueLoveSvelte 是一个基于AST的编译器,支持把 Vue DSL组件无痛编译为 Svelte 组件。

关键路经

6月8日

  • [x] vueLoveSvelte 0.0.2 版本完成

  • [x] 可以编译出来 ngcc-im-core 中的所有 Vue 组件不报错 6月9日

  • [x] 所有模板的最外层需要加 main

  • [x] Vue 组件转化后,模板代码和 js 代码会存在缩进问题

  • [x] Vue 组件中 style 标签,转变为 Svelte 组件, 'lang = less ' 的属性不会丢失,剔除 scoped 属性

  • [x] Vue 组件中 style 标签可能存在多个,需要合并为一个 style 6月10日

  • [x] Vue 组件中 DOM 节点的 Class 属性可能存在多个,需要合并为一个 。

  • [x] 同时支持了各种 class 表达式的写法

  • [x] 目前是,手动递归遍历 ast 树进行转化,感觉不太合适,还是要换成 babel 的 translate 方法

  • [x] Vue 组件中引用的 'import from xx.vue' 需要替换为 ' import from xx.svelte ' 6月11日

  • [x] Vue 组件中 DOM 节点的 :Style 、style 属性可能存在多个,需要合并为一个 。

  • [x] 支持各种 :style 表达式的写法

  • [x] 处理 :style 、 style 的样式优先级问题 6月12日 [ ] Vue 组件内是可以使用 filters的,但是 Svelte 不支持 filter

  • [x] 支持 写法 this.$set('obj', newValue, 'xx') ,Vue.$set('obj', newValue, 'xx'), 甚至还支持 this.$set(this, obj, 'xx') 这种写法

  • [x] 目前在 vue 中的 this.arr.push(xx) 的语法,转化到 svelte 后 变成了 arr.push(xx) 的语法,并不能触发 Svelte 的更新,需要在后面再加一句 arr = arr

  • [x] 支持 Vue 组件中的事件修饰符, @click.prevent => on:click|preventDefault Todo
    [ ] 在转化中,对 less 变量的替换,比如说移动端的 @base 变为 px (或者也可以放到 webpack less loader 里面搞) [ ] 支持 vue.$bus.$emit('sss' [ ]

为什么要做 VueLoveSvelte 编译器? 背景是 IM 项目需求变化非常迅速,一开始我们做了pc端的SDK,考虑到加载的行性能和平台兼容性,采用了 Svelte 框架。 后来,需求方反馈暂时不考虑 PC 端的场景,要优先推动移动端H5落地,于是 pc端的SDK就暂时不维护了,全力迭代移动端的需求。 等到移动端IM已经平稳线上运行后,需求迭代再次聚焦在 PC 端SDK时,整个用户侧的UI 的设计语言、后端的接口逻辑、前端的交互行为都发生了翻天覆地的变化。移动端的核心 message 组件完全可以复用在 pc 端上,但是两端采用的技术栈不一致。 存在问题 如果是接着之前旧的PC端代码改下去,就面临下面的问题:

  • 修改预估耗时双周,而且是硬着头皮重写一遍相同的逻辑。
  • 考虑后续新增的迭代需求,要移动端、pc端两套技术栈耗费double的人力
  • 后端API 版本不好做隔离 上面的问题严重制约了敏捷开发的效率,只能靠堆人力来解决。那么,有没有一个更好的办法去解决这个问题呢? 解决思路 考虑到IM移动端和PC端创建会话、收发会话、渲染消息、问题卡片等等等……大部分逻辑和样式基本相同,这块如果抽成共用的组件,通过我们的 VueLoveSvelte 编译器分别编译为 Vue 代码和 Svelte 代码的核心npm包,频繁的需求迭代只需要开发一次,各端同时升级npm包,极大的释放了生产效率。 方案的好处
  1. 不熟悉 Svelte 语法的同学,只要会写 VUE 组件,就支持编译为 Svelte 组件,降低了学习成本
  2. 减少移动端和PC端写同样的逻辑,降低维护成本和开发成本
  3. PC端还享受了 Svelte 无运行时框架的优势
  4. 为团队沉淀AST编译器的探索实践 可行性分析 Vue 和 Svelte 都是基于 Temlate 模板语法的框架,支持的语法特性基本对齐。通过 AST 解析后可以实现相互转化。 通俗来讲,Vue的语法特性更多,而 Svelte 的特性比较少,如果一个特性在 Svelte 没有对应的实现,就会在编译的时候报错提示。

| 这个轮子好用嘛?会不会翻车? 目标

  • 落地 vueLoveSvelte 编辑器,领先业界标准,一套标准Vue的组件,可 100% 编译为Svelte
  • 产物高可读性,方便进行二次开发 原理分析 针对下图中的 Parser、Traversal、Transform、Generator,下面会分别介绍: | Parser const babylon = require('babylon');

const code = function plus(a, b) { return a + b; }; const ast = babylon.parse(code, { sourceType: 'module' }); traversal 遍历节点,筛选遍历 const traversal = require('babel-traverse').default;

traversal(ast, { Identifier: function (path) { console.log(path.node.name); } });

进出节点: traversal(ast, { Identifier: { enter: function (path) { console.log(path.node.name, 'enter'); }, exit: function (path) { console.log(path.node.name, 'exit\n'); } } });

局部遍历: traversal(ast, { FunctionDeclaration: function (path) { if (path.node.id.name !== 'plus') return; path.traverse({ Identifier: { enter: function (path) { console.log(path.node.name, 'enter'); }, exit: function (path) { console.log(path.node.name, 'exit\n'); } } }); } });

transform traversal(ast, { FunctionDeclaration: function (path) { path.traverse({ Identifier: { enter: function (path) { if (types.isIdentifier(path.node, { name: "a" })) { // 节点替换 path.replaceWith(types.Identifier('x'), path.node); } }, exit: function (path) { console.log(path.node.name); } } }); } });

其实原理不是特别高深莫测,就是精细活,需要考虑到各种各样的情况。总之要做一个完整的语法解释器需要的是十分的细心与耐心 (如果是只匹配到 80% 其实还好)

Props Vue 官网里面介绍的 props 支持哪些东西呢?

  • type:可以是下列原生构造函数中的一种:String、Number、Boolean、Array、Object、Date、Function、Symbol、任何自定义构造函数、或上述内容组成的数组。会检查一个 prop 是否是给定的类型,否则抛出警告。
  • default:any 为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。
  • required:Boolean定义该 prop 是否是必填项。在非生产环境中,如果这个值为 truthy 且该 prop 没有被传入的,则一个控制台警告将会被抛出。
  • validator:Function自定义验证函数会将该 prop 的值作为唯一的参数代入。 举一个例子: props: { // 检测类型 height: Number, // 检测类型 + 其他验证 age: { type: Number, default: 0, required: true, validator: function (value) { return value >= 0 } } }

Svelte 的 props 相对比较简单 export let baz = ''; // 最常见的props export let baz = 'default' // 默认的 props

监听数组变更 由于 JavaScript 的限制,Vue 不能检测数组和对象的变化,所以会改写数组的原生的方法来触发更新,比如说 push 方法。 Svelte 没有改写数组的原生的方法,触发更新的逻辑,就是看数组有没有被重新赋值(也就是 = 号) 所以,在 vue 中的写法是: this.arr.push(1) 转换到 Svelte 中的写法是: arr.push(1) arr = arr // 这一行代码,对 arr 重新赋值触发了更新

this.$set 或者 Vue.$set Vue 中支持下面的写法: this.$set(this.obj, 'c', 'ccc') 转化为 Svelte 为: obj.c = 'ccc' 要小心,Vue 也支持这种写法: this.$set(this, 'obj', 'c') 转为为 Svelte 为: obj = 'c' 所以,这块的逻辑很简单,直接无脑匹配+ 转换一波带走: |

ngccIMcoreMessage组件 和 ngccIMCore 覆盖的范围 ngccIMcore组件就是跨技术栈,平台无关的组件,是一个纯粹的和 IM 建立会话、消息相关的组件 将会被 vueLoveSvelte 编译核心编译成 不包括

eventBus Svelte 中没有对应的 eventBus 的特性,解决思路是自己实现一个 eventBus

移动端 h5 和 pc 端的兼容行 根据运行环境做判断

一些组件库 比如说,跑马灯组件用的是bytedesign的mobile-vue Svelte 也有对应的跑马灯组件

Style 样式转变 @base 的 rem 的布局需要变为 1px ,目前的想法是,

vue-template-compiler Vue 2.0 底层的语法解析引擎,npm 包的地址是: https://www.npmjs.com/package/vue-template-compiler 有两个核心的API:

  • compiler.parseComponent() 抽出三部分代码: template, js,style
  • compiler.compile(template) 把vue 的template 抽成 ast 语法树

在线的 vue template 编译为 ast 树的的网站: https://magiccwl.github.io/vue-compiler-online/

Javascript 语法对应关系 Compute

Template 语法对应关系 并不是修改 template 生成的 ast 树,而是深度遍历 ast 树的过程中,生成一个 output 数组,每遍历一个 节点,就从节点上摘取一些 vue 指令的信息,转变为 Svelte 语法的字符串 push 到 output 数组中 v-html Vue 更新元素的 innerHTML。注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译

对应的 Svelte 的模板语法应该是: {@html expression}

不管是 v-html 还是 {@html } 都不会对 xss 做过滤,因此在使用的过程中需要小心

v-for

对应的 Svelte 的模板语法应该是: {#each list as item, index}

Class 绑定 vue 中的 class 的绑定写法比较多,分为下面三种情况去转变

  1. 变量写法 :class="classObject" => class="{ classObject }"
  2. 对象写法 :class={ active: isActive } => class="{ isActive ? 'active' : '' }" :class={ active: isActive, 'text-danger': hasError } => class="{ isActive ? 'active' : '' } {hasError? 'text-danger' : ''}"
  3. 数组写法 :class=[activeClass, errorClass] => class="{ activeClass } { errorClass }"

:class=[isActive ? activeClass : '', errorClass] => class="{isActive ? activeClass : ''} {errorClass}"

:class=[{ active: isActive }, errorClass] => class="{isActive ? "active" : ''} {errorClass}"

这里有一个不太好处理的地方。 就是:class="[activeClass, errorClass]"` 这样的字符串,是可以被 ast 解析的

| 只要可以解析成 ast,我们就可以愉快的替换节点。 但是 :class = "{ activeClass: isActive, errorClass: isError }" 竟然没有办法被 ast 解析的

| 所以,暂时这种情况,就先使用正则匹配来解决吧。 反正不管怎么样,目前不管多么复杂 class 表达式,都可以正常的转化了: ||

Style 绑定 Style 绑定比上面 Class 绑定更难处理😭,因为 Svelte 对于 style 绑定的语法更不灵活,只支持这种类型的动态绑定,即动态的地方是放到花括号里面 style="color: {setColor}; font-size: {setFontSize + 'px'}"

而 vue style 的语法则非常的灵活,把一个灵活的东西,转变为不灵活的东西,难度是很大的(因为要匹配灵活的各种各样的情况)。可以在 vue 中分别使用下面的语法:

  1. 变量写法 :style="styleObject"

  2. 对象写法 :style="{ color: activeColor, 'font-size': fontSize + 'px' }"

  3. 数组写法 :style="[baseStyles, overridingStyles]"

同时,vue里面还有一个潜规则,允许 :style 和 style 同时存在同一个标签上面,如果 :style 和 style 存在同名的属性名,:style 的优先级是更高的,如下图所示: | 所以合并到 svelte style 中时,可以把 :style 中对应的样式插到 style 的后面,因为在 css 的规则中,越靠后的样式,优先级更高 |

事件修饰符 Vue 中的事件修饰符有

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive

Svelte 中的事件修饰符有:

  • stopPropagation
  • preventDefault
  • capture
  • self
  • once
  • passvie 嘻嘻嘻,这个就比较好转化了,只需要在 template 中匹配到对应的情况,然后替换成 svelte 中对应的用法即可

过滤器 Filters Vue 中有一个叫过滤器的功能,但是 Svelte 中并没有对应的过滤器 filter 的能力。 目前的处理方式是,把 Vue 中的 filters 转化为 Svelte 中的函数(其实我感觉 vue 中的 filters 其实就是一种特殊的函数)

{{ textContent | filter1 | filter2 | filter3}}

上面 filter 的调用的顺序是 filter1 => filter2 => filter3, 那么转化为 Svelte 语法之后,应该是 : { filter3(filter2(fitler1(textContent))) }

@babel/parser babelParser 把js解析为 ast tree,然后我们对 tree 进行修改 const script = babelParser.parse(sfc.script.content, { sourceType: 'module', }); 可以在线 ast 转换 javascript 的网站 https://astexplorer.net/

遍历 javascript 节点并不太容易,并没有一个 children 的属性让我们一路遍历下来,还好babel 给我们提供了一个映射表,可以根据不同的节点类型找到不同的子节点 babel/types/visitor-keys.json https://github.com/babel/babel/blob/v5.1.11/src/babel/types/visitor-keys.json 映射表大概长这个样子: |

QA 写的 Vue 的 template 语法不规范怎么办?vueLoveSvelte编译会怎么处理? 在vueLoveSvelte 开始转换之前,会先通过 vue compiler 编译看是否是标准的vue语法,如果vue语法上有报错,则抛出报错 |

个人写的编辑器不太放心啊,怎么保证能否覆盖100%的语法? 编译器本质很简单,就是匹配到的规则足够覆盖所有的语法,不同的开发者会有不同习惯,要匹配到所有的语法情况需要大量的测试组件,这里就有二八效应,绝大多数的 Vue 语法是可以被覆盖的,如果想覆盖100%的情况,需要不断的反馈和完善,大家感兴趣也可以共建 打算用 vue real world 来做测试用例

中长期 plan

  • 在线转化 Vue组件 为 Svelte 组件在线网站 😊
  • 完美编译饿了么 Element vue 的组件库为 Svelte 组件库 😄
  • 完美编译公司的 bytedesign vue 的组件库为 Svelte 组件库 😆