gitbook/玩转Vue 3全家桶/docs/428106.md
2022-09-03 22:05:03 +08:00

389 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 02 | 上手一个清单应用帮你入门Vue.js
你好,我是大圣,欢迎进入课程导读篇的第二讲。
在上一讲中我带你了解了前端框架的基本发展历史。在为什么要学Vue框架这个问题上相信你现在已经有了自己的答案。那么从今天开始我们正式进入上手学Vue 的阶段。
我们的专栏课程会通过故事的形式展开。故事的主角小圣是一名刚入行的前端工程师在校期间学了点HTML、CSS和JavaScript但是不太懂框架。我是他的经理会手把手教他在Vue.js这个框架里打怪升级。
小圣在学习Vue的过程中碰到的各种问题同样也可能是你会碰到的问题。所以在我带着你一起解决小圣面临的问题的同时你的很多问题也会迎刃而解。
今天是小圣第一天入职他只知道团队的项目是用Vue.js开发的但并不熟悉Vue的具体技术细节所以我决定带他先做一个清单应用先在整体上熟悉这个框架。当然了我在这里带小圣做的清单应用更多的是一种模拟的场景并不需要对号入座到真实的工作场景下。毕竟在真实的工作场景下可能小圣一进公司就上实际的项目了。
如果你已经很熟悉Vue开发了可以直接粗略地把本讲过一遍直奔下一讲。在那里我会带你梳理Vue 3的新特性相信这些新特性会让你对Vue 3产生新的期待。
## 环境准备
小圣领完电脑后首先要做的是安装编辑器和浏览器这个不用多介绍你也能轻松地搞定。我推荐给小圣写代码的编辑器是VS Code调试页面的浏览器是Chrome。有了VS Code和Chrome我们就可以开始后面的工作了。
## 任务分解
如下图所示,小圣要上手开发的应用大概长这个样子:它有一个输入框,供我们输入数据;下方有一个列表,显示着所有我们要做的事情。
在输入框输入内容后,敲下回车,下面就会新增一条数据。对于每个要做的事情,你还可以用复选框标记,标记后文字就会变灰,并带有一个删除的效果,表示这件事情已经做完了。
![图片](https://static001.geekbang.org/resource/image/0c/01/0c58c5219ecb61394110ccb848829c01.png?wh=1526x884)
清单应用虽然看起来简单,不过麻雀虽小,五脏俱全。其实不管入门哪个框架,你都可以写一个清单,来上手体验一下。
不过由于小圣只有简单的jQuery开发经验他在学习Vue的时候首先要做的就是思想的转变。为什么要这么说呢下面我们来对比看看jQuery的开发思路和Vue.js的开发思路有什么不同看完你就会明白我为什么说小圣在学习Vue时首先要做的是转变思路。
比如,我们想做一个输入框,里面输入的任何数据都会在页面上同步显示。
对于这样一个前端的功能jQuery开发的思路是
1. 先找到输入框,给输入框绑定输入事件;
2. 输入的同时,我们获取输入的值;
3. 再找到对应的html标签更新标签的内容。
对应代码大概是这样的:
```plain
<div>
<h2 id="app"></h2>
<input type="text" id="todo-input">
</div>
<script src="jquery.min.js"></script>
<script>
// 找到输入框,监听输入
$('#todo-input').on('input',function(){
let val = $(this).val() // 获取值
$('#app').html(val) // 找到标签,修改内容
})
</script>
```
在实现我们想要的输入框的功能时上述jQuery代码需要先找到输入框然后持续监听输入之后一直等待到输入值被获取最后找到标签所在的前端页面位置进行内容的修改。
上述的jQuery代码其实是jQuery时代的开发逻辑的一个缩影。**而jQuery时代的开发逻辑就是我们先要找到目标元素然后再进行对应的修改**。
学习Vue.js首先就要进行思想的升级也就是说**不要再思考页面的元素怎么操作,而是要思考数据是怎么变化的**。这就意味着我们只需要操作数据至于数据和页面的同步问题Vue会帮我们处理。实际上Vue让前端开发者能够专注数据本身的操作而数据和页面的同步问题则交由Vue来负责。这种机制正是Vue当初受到开发者青睐的一个重要原因。
对于同样的输入框需求Vue的开发思路是我们需要一个数据在输入框的代码和h2标签的代码内使用。我们只需要操作数据然后交给Vue去管理数据和页面的同步就可以了。
在Vue框架下如果你想要页面显示一个数据就要先在代码的data里声明数据在输入框的代码里使用v-model来标记输入框和数据的同步在HTML模板里使用两个花括号标记来显示数据例如{{title}}。对应代码大概是这个样子:
```xml
<div id="app">
<h2>{{title}}</h2>
<input type="text" v-model="title">
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const App = {
data() {
return {
title: "" // 定义一个数据
}
}
}
// 启动应用
Vue.createApp(App).mount('#app')
</script>
```
从这个例子中你就可以看到Vue在开发思路上和jQuery的不同。而我们要做的就是逐渐习惯Vue的这种开发模式。
## 清单页面的渲染
在前端页面我们在输入框输入数据然后输入框下方要有一个列表显示我们所有输入的值。按照Vue的思考方式如果我们想实现这个功能那么我们需要一个数组然后使用v-for这个语法来循环渲染。
先看代码:
```xml
<div id="app">
<h2>{{title}}</h2>
<input type="text" v-model="title">
<ul>
<li v-for="todo in todos">{{todo}}</li>
</ul>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const App = {
data() {
return {
title: "", // 定义一个数据
todos:['吃饭','睡觉']
}
}
}
// 启动应用
Vue.createApp(App).mount('#app')
</script>
```
看上述代码在data中我们再定义一个数据todos输入一个数组。为了方便调试我们先放两个假数据如果我们在标签里直接写{{todos}},就会看到显示的是一个数组,但这个不是我们想要的,我们需要的是显示一个列表。
在Vue中只要是渲染列表我们都是用v-for这个语法而具体到上述代码对v-for语法的使用也即
```xml
<li v-for="todo in todos">{{todo}}</li>
```
上面这行单独抽出来的代码的意思就是我们循环遍历todos这个数据 每一条遍历的结果叫todo然后把这个数据渲染出来这样页面就能显示一个列表了。
![图片](https://static001.geekbang.org/resource/image/b2/28/b2436fe52fec4a2e0b4a13dba97f1728.png?wh=874x436)
## 处理用户交互
在上一步中我们主要考虑的是实现前端页面的一个输入框以及能显示输入值的一个列表的功能。下一步就是让用户敲回车的时候能够让列表新增一条。采用Vue的思维我们需要完成以下这几个步骤
1. 监听用户的输入。在监听中,如果判断到用户的输入是回车的时候,那就执行一个函数。
2. 在执行的这个函数内部把title追加到todos最后面的位置并且清空title。
那么Vue如何实现这一功能呢我们先看实现这一功能后的完整代码
```xml
<div id="app">
<input type="text" v-model="title" @keydown.enter="addTodo">
<ul>
<li v-for="todo in todos">{{todo}}</li>
</ul>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const App = {
data() {
return {
title: "", // 定义一个数据
todos:['吃饭','睡觉']
}
},
methods:{
addTodo(){
this.todos.push(this.title)
this.title = ""
}
}
}
// 启动应用
Vue.createApp(App).mount('#app')
</script>
```
对照上述代码我们来看一下在Vue中监听用户交互的方法。在Vue中我们使用@来标记用户的交互,@click是点击@keydown是键盘敲下所以就像上述代码展示的那样如果只监听回车键那么我们就用@keydown.enter=“addTodo” 。
监听到用户的输入后对于要执行的函数我们新增一个methods配置。在函数内部我们可以在this上直接读到data里的的数据所以我们不需要考虑怎么找到标签只需要进行如下这行潇洒的代码就能让列表自动新增了一条 这就是数据驱动页面的魅力。
```xml
this.todos.push(this.title)
```
## 额外信息的显示
好了,我们现在既实现了一个输入框,以及输入数据后能够新增一条数据的列表的功能,也实现了用户在输入后的交互功能。
下一步,我们想实现标记清单中某一项是否完成的功能。但这却难住了小圣同学,因为从目前的代码设计上来看,我们的输入只能是字符串格式的内容。而我们想要实现的标记功能,却是把列表中的某一项,用灰色的字体背景和中划线来标记,以此表示这一条内容是已经完成的内容。
如果我们想实现这个功能,就需要对数据结构进行一下改造,把内容的数据类型,从简单的字符串类型改为对象。
那么数据结构要怎么改造呢?我们先直接看改造数据结构后的完整代码:
```xml
<ul>
<li v-for="todo in todos">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}"> {{todo.title}}</span>
</li>
</ul>
<script>
const App = {
data() {
return {
title: "", // 定义一个数据
todos:[
{title:'吃饭',done:false},
{title:'睡觉',done:true}
]
}
},
methods:{
addTodo(){
this.todos.push({
title:this.title,
done:false
})
this.title = ""
}
}
}
</script>
<style>
.done{
color:gray;
text-decoration: line-through;
}
</style>
```
结合代码我给你理理整个的改造思路。首先对于todos数组除了title还要加上一个done字段来标记列表中的某一项内容是否完成并且渲染的时候使用todo.title。
在前面的步骤中对于列表中每一项我们是用无序列来表示的。但如果我们想要在列表中实现对某些选项的同时多选那么就需要用到复选框。对于每条信息我们都要加一个复选框所以我们依然使用v-model来绑定这个done字段从而实现数据里能记录用户操作的状态。
我们还需要根据done字段来显示某一行的样式。在Vue中冒号":" 开头的属性是用来传递数据的这里的写法的意思就是根据todo.done来决定是否有done这个class。最后当加上".done"的样式后,下面的左图就是我们想要的效果,而下面的右图则是涉及到".done"的相关代码:
![图片](https://static001.geekbang.org/resource/image/7f/b7/7f73769e14d67yy4104404b7442809b7.png?wh=1884x910)
## 进一步优化
完成前面的步骤以后,现在看起来一个清单应用最基本的功能模块、用户交互、复选框功能都已经实现了。但是为了进一步提升交互,小圣还想要增加两个功能,第一个功能是:在前端页面显示的列表的最下面,显示一下列表项目中没完成的项目的比例;第二个功能是:新增一个清理的按钮,用来删掉已经标记为完成的列表中的一条或多条数据。
那么,对于要增加的第一个功能,也即如何实现在前端页面的列表的最下方,显示一下列表项目中没有完成的项目的比例呢?小圣按照学到的知识,写出了下面的代码:
```xml
<div>
{{todos.filter(v=>!v.done).length}}
/
{{todos.length}}
</div>
```
把这段代码增加到上一步最后的完整代码中,运行代码,从下图所示的前端页面运行时状态中,我们能看到,其中显示的未完成比例的数据也没问题。
![图片](https://static001.geekbang.org/resource/image/d0/97/d02a1fc75d79063f48660f4yy13dd197.png?wh=988x416)
不过从上述代码实现的方式上看代码看起来很丑且性能不好而且需要二次计算的数据这在我们开发的需求中很常见。此外在模板里面写JS看起来代码也很乱。Vue针对这种情况设计了一个功能也就是**计算属性**。
我们看一下采用Vue的计算属性实现的能够支持二次计算的上述功能的实现代码
```xml
<div>
{{active}} / {{all}}
</div>
<script>
computed:{
active(){
return this.todos.filter(v=>!v.done).length
},
all(){
return this.todos.length
}
}
</script>
```
从上面的代码中能看到和之前采用往模板里写JS的办法相比我们新增了一个属性computed。computed属性的配置也即active和all都是函数。这两个函数返回的计算后的值在模板里可以直接当做数据来用这样把JavaScript的计算逻辑依然放在了JavaScript里避免了过于臃肿的模板。
而且computed计算属性还内置了缓存功能如果依赖数据没变化多次使用计算属性会直接返回缓存结果同我们直接写在模板里相比性能也有了提升。
计算属性不仅可以用来返回数据,有些时候我们也需要修改计算属性,比如我让小圣新增一个全选的复选框,要求如下:
1. 全选框在勾选与取消勾选两个状态之间的切换,会把所有清单内的数据都同步勾选。
2. 清单内的项目如果全部选中或者取消,也会修改全选框的状态。
对于新增全选框的功能需要满足上面的两个要求所以全选框这个计算属性就有了修改的需求。这时候computed的配置就不能是函数了要变成一个对象分别实现get和set函数get就是之前的返回值set就是修改计算属性要执行的函数。
我们来看一下computed修改后的代码
```xml
<div>
全选<input type="checkbox" v-model="allDone">
<span> {{active}} / {{all}} </span>
</div>
<script>
computed:{
active(){
return this.todos.filter(v=>!v.done).length
},
all(){
return this.todos.length
},
allDone: {
get: function () {
return this.active === 0
},
set: function (val) {
this.todos.forEach(todo=>{
todo.done = val
});
}
}
}
</script>
```
和没有全选框时的computed属性的配置代码相比上面的代码新增了一个allDone的计算属性页面中直接使用checbox绑定。在allDone的get函数里对于allDone会返回什么值我们只需要判断计算属性active是不是0就可以。
而set函数做到的就是我们在修改allDone也就是前端页面切换全选框的时候直接遍历todos把里面的done字段直接和allDone同步即可。
实现新增一个全选的复选框后的效果是什么样呢?我们一起来看一下:
![图片](https://static001.geekbang.org/resource/image/27/cf/273d37ca7e59a40ac0d5a537203f41cf.gif?wh=542x325)
## 条件渲染
在上面一部分,我们增加了在前端页面的底部显示未完成比例,和增加全选框这两个功能。除此之外,我们还需要新增一个“清理”的按钮,点击之后把已完成的数据删除,功能需求很简单,但是有一个额外的要求,就是列表中没有标记为完成的某一项列表数据时,这个按钮是不显示的。
这种在特定条件下才显示或者隐藏的需求也很常见我们称之为条件渲染。在Vue中我们使用v-if 来实现条件渲染。
老规矩,我们还是先看代码:
```xml
<button v-if="active<all" @click="clear">清理</button>
<script>
methods:{
clear(){
this.todos = this.todos.filter(v=>!v.done)
}
}
</script>
```
通过上述代码我们实现了增加一个清理按钮的功能。当active小于all的时候我们显示清理按钮也就是说v-if后面的值是true的时候显示清理按钮false的时候不显示。“@”符号的作用,我们在前面讲到监听用户交互时,已经拿@keydown为例说明过了这里代码中的@click的作用是绑定点击事件。
我们还可以用v-else配合v-if当todos是空的时候显示一条“暂无数据”的信息具体的实现代码如下
```xml
<ul v-if="todos.length">
<li v-for="todo in todos">
<input type="checkbox" v-model="todo.done">
<span :class="{done:todo.done}"> {{todo.title}}</span>
</li>
</ul>
<div v-else>
暂无数据
</div>
```
当我们实现了清理按钮的功能,并且也实现了列表为空时,能够显示“暂无数据”的信息后,我们看下清单应用的最终效果。
![图片](https://static001.geekbang.org/resource/image/3c/72/3c8ddf81d6b478069d6b1dec7b605572.gif?wh=542x325)
这个需求并没有考虑美观性小圣没写太多CSS主要专注在JS的交互逻辑上。小圣这个需求做完晚上下班的时候跟我分享了一下学习Vue的心得你也可以在评论区分享一下你对Vue的开发的心得我们一起交流。
## 总结
我们来总结一下小圣今天都学到了什么吧。入职第一天小圣首先扭转了之前使用jQuery时的开发思路并且弄明白了jQuery和Vue开发思路的区别。从寻找DOM到数据驱动这是前端开发的一次巨大的变革也是小圣同学的第一个挑战。
其次就是对Vue的入门使用我带你回顾一下今天做的这个清单应用对于这个应用首先我们要有输入框能输入文本并且在输入框下方循环显示清单我们用到了v-modelv-for这些功能。这些v-开头的属性都是Vue自带写法我们通过{{}}包裹的形式显示数据。
然后我们想在用户输入完成后敲击回车新增一条数据,这就用到@开头的几个属性,@keyup和@click都是绑定对应的交互事件。最后通过computed我们能对页面数据的显示进行优化。我们所需要关心的就是数据的变化这种思维方式会贯穿小圣的整个打怪升级之路。
## 思考题
下班前我给小圣布置一个作业,现在所有的操作状态一刷新就都没了,这个问题怎么解决呢?
欢迎在评论区一起讨论,也欢迎你把这篇文章分享给其他人,我们下一讲见。