Petite-Vue
# 前言
当今天笔者慢悠悠打开github
,想看看follow
的大佬们最近都在研究啥,逐个浏览着一个个被star
的项目,不知不觉中我开始注意到了petite-vue
这个项目,首先是多个大佬都star
了这个项目,其次,这也是vuejs
组织下的,让我顿时萌生了好奇心,想看看这到底是个什么东东。
从尤大的
commit
记录来看,首次提交还是三天前,所以,这个是一个刚起步的新项目。
点开项目,习惯性的看看介绍:
petite-vue is an alternative distribution of Vue optimized for progressive enhancement. It provides the same template syntax and reactivity mental model with standard Vue.
大概意思就是petite-vue
是Vue
的另一种分布形式,专门为渐进增强设计,然后再看看特性:
- Only ~5.7kb
- DOM-based, mutates in place
- Driven by @vue/reactivity
好家伙,只有5.7KB
,这对于网站加载性能方面无疑是一个巨大的提升,如果说我能仅引入一个这么小的包就能利用Vue
的能力来开发应用,岂不美滋滋,话不多说,我们往下看看它究竟做了什么。
# 简易上手
对于任何新鲜技术来说,我们一般会先实现一个Hello World
来简单了解下大致的语法。
# 准备
首先我们新建一个html
文件,然后在头部引入他的核心包:
<script src="https://unpkg.com/petite-vue" defer init></script>
这里我们可以注意一下这个defer
和init
,为什么需要加这两个属性呢?作用是啥?
# defer
首先我们知道defer
是用来让下载脚本的过程不去阻塞DOM
的渲染和其他资源的加载,同时会在DOM
解析完毕之后才去执行,所以显而易见,这个属性是为了在执行该脚本的时候我们页面的HTML
相关内容已经解析完毕了。
# init
这个就主要是用来帮你自动初始化petite-vue
,如果不加你就需要在后面手动初始化一下:
如果你选择手动实例化,你就得去掉defer
和init
属性了,不然你将无法获取到PetiteVue
这个全局变量。
<script src="https://unpkg.com/petite-vue"></script>
<script>
PetiteVue.createApp().mount()
</script>
2
3
4
# Hello World
<div v-scope="{ str: 'Hello World' }">
{{ str }}
</div>
2
3
好了,在body
中加上这么一段,我们最简易版的Hello World
就完事了,就这么方便。
这里的v-scope
主要用来标识给petite-vue
解析的,并且提供了一个str
的变量,在这个作用域内,我们就可以使用插值表达式({{}})来使用它进行展示了。这里对于熟悉Vue
的玩家来说,属于0门槛了。
# 绑定事件与响应式
光有了渲染还不行啊,我如果想进行动态交互要咋办?
这里直接就把官方的小例子搬过来吧。
<div v-scope="{ count: 0 }">
{{ count }}
<button @click="count++">inc</button>
</div>
2
3
4
还是熟悉的配方,还是熟悉的使用方式,绑定事件的方式还得原来的方式,PetiteVue
会自动帮你去更新页面,你只需要更新依赖值就好了。
是不是觉得还挺香,这下妈妈再也不担心我为了减少页面体积而抛弃框架写原生了。
从当前的例子来看,还是过于简陋,因为对于一个应用来说,我们必然会存在很多逻辑,总不能全写在html
里吧,好吧,满足你,看码:
# 进阶
<script type="module">
import { createApp } from 'https://unpkg.com/petite-vue?module'
createApp({
count: 0,
increment() {
this.count++
}
}).mount()
</script>
<div v-scope>
<p>{{ count }}</p>
<button @click="increment">increment</button>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这里笔者也是从官方给的示例删掉了一点点东西,这样更方便我们理解。这里我们可以使用es module
来加载核心包,然后拿到createApp
用于应用的初始化,并且需要你进行手动挂载,手动挂载时可以给mount
函数传选择器,就像你在Vue
中调用mount
一样,例如:#app
。
我们在调用createApp
的时候,可以传入一个对象,对象中可以放一些属性,包括普通值或者是函数,这样,这个对象里的所有数据都能被全局访问了,也就是直接可以在html
中使用这些变量。
在html
中使用全局变量时,我们需要给父容器添加v-scope
属性,但是这里不需要传值,仅用于标识,让PetiteVue
能够正确的解析它。
# 生命周期
@mounted
@unmounted
目前仅支持这两个生命周期,毕竟属于新项目,功能也需要慢慢添加的。
# 使用示例
<div
v-if="show"
@mounted="console.log('mounted on: ', $el)"
@unmounted="console.log('unmounted: ', $el)"
></div>
2
3
4
5
6
这里我们任然可以继续使用v-if
,所以小伙伴也不用担心,同时呢,我们还是可以通过$el
来访问DOM
。
# 行内副作用
假设我们想要在行内执行表达式应该怎么做呢:
<div v-scope="{ count: 0 }">
<div v-effect="$el.textContent = count"></div>
<button @click="count++">++</button>
</div>
2
3
4
这里我们就需要用到v-effect
指令来帮助我们实现功能了,也是十分的方便。
说了这么多,估计大家更关心的是是否支持组件,在写原生过程中,最让人难受的莫过于一大堆重复的逻辑和dom
难以复用的问题,在如今组件化已经几乎成为不可或缺的时代,没有组件能力,无疑会流失大量的用户。
# 组件化
# 不使用模板的组件
<script type="module">
import { createApp } from 'https://unpkg.com/petite-vue?module'
function Counter(props) {
return {
count: props.initialCount,
inc() {
this.count++
},
mounted() {
console.log(`I'm mounted!`)
}
}
}
createApp({
Counter
}).mount()
</script>
<div v-scope="Counter({ initialCount: 1 })" @mounted="mounted">
<p>{{ count }}</p>
<button @click="inc">increment</button>
</div>
<div v-scope="Counter({ initialCount: 2 })">
<p>{{ count }}</p>
<button @click="inc">increment</button>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
从这里我们可以看到,首先这里的组件表现形式就是一个函数,接受一个props
作为入参,函数的返回值是一个包含了相关组件方法和数据的对象,然后将函数放入到初始化的对象中,以便于可以全局访问,不需要进行手动注册。
然后我们在使用的时候就直接一个v-scope
然后值为函数执行就行了,同时也可以给这个组件传递props
,就这样,我们了解了一个简单的组件使用方式。
从示例我们不难发现,这种使用方式太鸡肋了,对于相同的dom
结构,我们需要重复进行编写,导致代码可读性变差,有没有办法让我们和写Vue
一样在使用组件的使用不需要关心模板,只需要考虑入参就行了呢?
答案肯定是可以的,且看下文。
# 模板组件
<script type="module">
import { createApp } from 'https://unpkg.com/petite-vue?module'
function Counter(props) {
return {
$template: '#counter-template',
count: props.initialCount,
inc() {
this.count++
}
}
}
createApp({
Counter
}).mount()
</script>
<template id="counter-template">
My count is {{ count }}
<button @click="inc">++</button>
</template>
<!-- reuse it -->
<div v-scope="Counter({ initialCount: 1 })"></div>
<div v-scope="Counter({ initialCount: 2 })"></div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
首先我们需要在我们相关函数的返回值部分添加一个属性:$template
,并且值为你需要使用的模板选择器。这里我们定义了一个<template id="counter-template">
,故我们这里的值直接填#counter-template
即可。
在模板中,我们可以正常使用这个组件的数据也就是Counter
函数返回的对象中的数据。在使用的时候只需要指定v-scope
为你需要使用的组件就好了,不需要在写一堆重复的dom
了。
就这样,我们拥有了大杀器,组件化的能力。
# 使用响应式API
对于使用过Vue3
的小伙伴可能会了解到reactive
这个方法,可以将一个普通对象中的值变成响应式的,那么在这里你也可以正常使用的。
<script type="module">
import { createApp, reactive } from 'https://unpkg.com/petite-vue?module'
const store = reactive({
count: 0,
inc() {
this.count++
}
})
store.inc()
createApp({
store
}).mount()
</script>
<div v-scope="{ localCount: 0 }">
<p>Global {{ store.count }}</p>
<button @click="store.inc">increment</button>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
我们使用reactive
构造了一个响应式对象,我们在改变这个对象中数据的时候,其他使用到这个数据的地方也会相应的自动更新。
# 指令
const myDirective = (ctx) => {
ctx.effect(() => {
console.log(ctx.get())
})
return () => {
// 卸载时执行清理逻辑
}
}
// register the directive
createApp().directive('my-dir', myDirective).mount()
2
3
4
5
6
7
8
9
10
11
12
这里的指令核心逻辑仍是一个函数,这个函数的入参为ctx
,其中包含了相关指令的值和修饰符等。
这个ctx
对象支持的属性和方法:
ctx.el
—— 绑定指令的DOM
ctx.exp
—— 原始表达式,如v-my-dir="x"
这个表达式就是x
ctx.arg
—— 参数:v-my-dir:foo -> "foo"
ctx.modifiers
—— 修饰符:v-my-dir.mod -> { mod: true }
ctx.get()
—— 执行当前作用域内任意表达式,返回结果:ctx.get(
${ctx.exp} + 10)
# 兼容的Vue特性
插值表达式
v-bind
(including : shorthand and class/style special handling)v-on
(including @ shorthand and all modifiers)v-model
(all input types + non-string :value bindings)v-if / v-else / v-else-if
v-for
v-show
v-html
v-text
v-pre
v-once
v-cloak
reactive()
nextTick()
# 不支持的特性
ref()
、computed()
等template
仅支持选择器- 不支持
render function
,因为petite-vue
没有虚拟DOM
v-on="object"
v-is & <component :is="xxx">
v-bind:style
自动添加前缀不支持Transition, KeepAlive, Teleport, Suspense
v-for
深层解构- 不支持的响应式类型(
Set
、Map
等)
# 实践
为了更好的结合上述的知识点,这里笔者搭了一个简易的项目来练手。
页面预览:
这里笔者主要用了bootstrap
配合petite-vue
搭了一个简单的音乐播放网站,用来练练手还是可以的。
预览地址:在线预览
项目地址:github链接
# 总结
综合上述演示,相信大家对这个新兴的家伙有了一定的认知,总体而言跟Vue
还是很像的,所以基本上没啥上手门槛,目前应该还是起步阶段,大家有兴趣可以尝尝鲜,还是挺有意思的一个东东。