核心知识

​上一篇文章​​探讨了petite-vue核心知识,本文实践一下这些知识。

petite-vue特点

​petite-vue​​ is an alternative distribution of Vue optimized for progressive enhancement.

petite-vue的主要特点是​​渐进增强​​​,什么是​​渐进增强​​呢?

尤大新活petite-vue实践_获取焦点image-20210724220557315

渐进增强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。

尤大新活petite-vue实践_获取焦点_02image-20210724220841196

优雅降级(Graceful Degradation):一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行 hack 使其可以在低版本浏览器上正常浏览。

案例体验

TodoMVC淋漓尽致展现了这一点,一开始尽快实现最小的、简单的功能,再一点点增加功能,增强用户体验。

Step01:初始化

尤大新活petite-vue实践_数据_03image-20210725174929068

首先是初始化,我们使用了如下知识点:

  • 模块方式引入:​​import {createApp} from 'https://unpkg.com/petite-vue?module'​
  • 根作用域
  • 明确的挂载目标
  • v-cloak指令应用
<style>
/* v-cloak应用 */
[v-cloak] {
display: none;
}
</style>

<script type="module">
// 模块方式引入
import { createApp } from 'https://unpkg.com/petite-vue?module'

// 根作用域
createApp({
todos: [
{ id: 1, title: 'petite-vue', completed: false },
{ id: 2, title: 'Alpine', completed: false },
{ id: 3, title: 'svelte', completed: false },
],
}).mount('#app') // 明确挂载目标
</script>

<!-- v-clock应用 -->
<div id="app" v-cloak>
<ul class="todo-list">
<!-- v-clock应用 -->
<li v-for="todo in todos" class="todo">
{{ todo.title }}
</li>
</ul>
</div>

Step02:数据修改和持久化

尤大新活petite-vue实践_作用域_04Jul-25-2021 17-52-14

下面维护todo列表数据,我们希望:

  • 双击待办事项可以切换为编辑状态,这个通过动态class实现
<li v-for="todo in todos" class="todo" 
:class="{ completed: todo.completed, editing: todo === editedTodo }">
  • 切换为编辑状态,input可以自动获取焦点
  • 修改数据之后,自动持久化到localStorage中

我们想要维护todo列表,就要在根作用域中添加若干方法,不同于vue中要放入​​methods​​,它们存在于选项的根上:

<script type="module">
createApp({
editedTodo: null,
removeTodo(todo) {
this.todos.splice(this.todos.indexOf(todo), 1)
},

editTodo(todo) {
this.beforeEditCache = todo.title
this.editedTodo = todo
},

doneEdit(todo) {
if (!this.editedTodo) {
return
}
this.editedTodo = null
todo.title = todo.title.trim()
if (!todo.title) {
this.removeTodo(todo)
}
},

cancelEdit(todo) {
this.editedTodo = null
todo.title = this.beforeEditCache
},
}).mount('#app')
</script>

视图中可以直接使用它们:

<li v-for="todo in todos" class="todo" 
:class="{ completed: todo.completed,
editing: todo === editedTodo }">
<div class="view">
<input class="toggle" type="checkbox"
v-model="todo.completed" />
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
<button class="destroy" @click="removeTodo(todo)">X</button>
</div>
<!--副作用的应用-->
<input class="edit" type="text" v-model="todo.title"
@blur="doneEdit(todo)" @keyup.enter="doneEdit(todo)"
@keyup.escape="cancelEdit(todo)" />
</li>

接下来是保存功能的实现,这里利用了​​v-effect​​指令,我们该如何理解它呢?看看下面数据保存功能:

根节点上面添加​​v-effect​​​,则​​save​​​方法中的响应式数据就会和​​save​​​之间建立依赖关系,数据变化,则​​save​​再次执行。

<!-- 注意save()后面的小括号不能删除!!! -->
<div id="app" v-effect="save()">
......
</div>

​save​​将todos存入localStorage,从而实现数据持久化:

<script type="module">
createApp({
save() {
localStorage.setItem('todos', JSON.stringify(this.todos))
},
}).mount('#app')
</script>

另一个​​v-effect​​用例是编辑输入框自动获取焦点,可以发现能够很好代替指令完成任务:

<input class="edit" v-effect="if(todo === editedTodo) $el.focus();" />

Step03:过滤

最后我们希望能够对todo进行过滤:

尤大新活petite-vue实践_数据_05Jul-25-2021 18-01-22

这里实现一个简易路由,hash变更时修改过滤级别:

setupRouting() {
const onHashChange = () => {
const visibility = window.location.hash.substr(2)

if (['all', 'active', 'completed'].includes(visibility)) {
this.visibility = visibility
} else {
window.location.hash = ''
this.visibility = 'all'
}
}
window.addEventListener('hashchange', onHashChange)
onHashChange()
},

视图中通过​​mounted​​钩子安装这个路由:

<div id="app" @mounted="setupRouting">

再利用​​get方法​​计算出过滤结果,从而实现过滤功能:

get filteredTodos() {
switch (this.visibility) {
case 'all': return this.todos
case 'active': return this.todos.filter(todo => !todo.completed)
case 'completed': return this.todos.filter(todo => todo.completed)
}
},

视图中修改为这个过滤结果,这类似于​​计算属性​​​,注意添加​​key​

<li v-for="todo in filteredTodos" :key="todo.id"></li>