陌小路的个人博客 陌小路的个人博客
首页
  • 技术专区

    • 面试
    • Vue
    • Electron
    • TypeScript
    • Serverless
    • GraphQL
  • 我的秋招之旅
  • 2019年终总结
Todo
收藏夹
关于作者
GitHub

陌小路

前端切图仔
首页
  • 技术专区

    • 面试
    • Vue
    • Electron
    • TypeScript
    • Serverless
    • GraphQL
  • 我的秋招之旅
  • 2019年终总结
Todo
收藏夹
关于作者
GitHub
  • Vue

    • Vue3-beta-初体验
    • Vue-nextTick源码分析
    • Vuex-源码分析01
    • Vuex-源码分析02
    • Vite源码分析
    • Vue服务端渲染

    • Petite-Vue
      • 前言
      • 简易上手
        • 准备
        • defer
        • init
        • Hello World
        • 绑定事件与响应式
        • 进阶
        • 生命周期
        • 行内副作用
        • 组件化
        • 使用响应式API
        • 指令
      • 兼容的Vue特性
      • 不支持的特性
      • 实践
      • 总结
  • React

  • 面试

  • Electron

  • Serverless

  • GraphQL

  • TypeScript

  • RxJS

  • 工程化

  • Webpack

  • Nestjs

  • WebRTC & P2P

  • Docker

  • Linux

  • Git

  • Svelte

  • 踩坑日记 & 小Tips

  • 其他

  • technology
  • Vue
陌小路
2021-07-03

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>
1

这里我们可以注意一下这个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>
1
2
3
4

# Hello World

<div v-scope="{ str: 'Hello World' }">
  {{ str }}
</div>
1
2
3

好了,在body中加上这么一段,我们最简易版的Hello World就完事了,就这么方便。

这里的v-scope主要用来标识给petite-vue解析的,并且提供了一个str的变量,在这个作用域内,我们就可以使用插值表达式({{}})来使用它进行展示了。这里对于熟悉Vue的玩家来说,属于0门槛了。

# 绑定事件与响应式

光有了渲染还不行啊,我如果想进行动态交互要咋办?

这里直接就把官方的小例子搬过来吧。

<div v-scope="{ count: 0 }">
  {{ count }}
  <button @click="count++">inc</button>
</div>
1
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>
1
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>

1
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>
1
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>
1
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>
1
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>
1
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()
1
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还是很像的,所以基本上没啥上手门槛,目前应该还是起步阶段,大家有兴趣可以尝尝鲜,还是挺有意思的一个东东。

编辑
上次更新: 2023/11/25, 4:11:00
总结
React之ESR渲染解析

← 总结 React之ESR渲染解析→

最近更新
01
github加速
01-01
02
在线小工具
01-01
03
Lora-Embeddings
11-27
更多文章>
Theme by Vdoing | Copyright © 2020-2024 STDSuperman | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式