我的git代码: https://github.com/chentianwei411/Typeahead
目标:
建立一个输入关键字得到相关列表的组件,用Vuejs2和Fetch API
思路:
把一个json数据,fetch到本地。然后用户模糊搜索title, 得到相关的结果列表。
[ { "userId": 1, "id": 1, "title": "sunt aut facere repellat provident", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum" }, ...
工具:
使用JSONPlaceholder上的json数据(一个Fake Online REST API用于测试和Prototyping Serving)
使用VueJs2建立一个简单的组件。
不会使用任何库,如Axios ,jQuery, 而使用原生的Fetch API.
使用VueCLI3来搭建手脚架项目。
步骤:
1.安装Vue (参考之前的博客)
yarn global add @vue/cli
vue --version # 查看是否安装好!
vue create type.vue # 新建一个项目,插件选择后,cd type.vue进入。
yarn serve # 执行这条命令,打开热重载模式的网页,具体说明参考之前的博客。
2. 创建组件Typeahead。
在Aup.vue的<template>加上:
<Typeahead source="https://jsonplaceholder.typicode.com/posts" placeholder="What Tv Serie you are looking for..." filter-key="title" :start-at="2" />
设置4个prop特性:
- source接收源数据。
- placeholder设置输入框的默认内容
- filter-key设置搜索的关键字属于source data中的哪部分。
- v-bind:start-at,这个是动态的绑定,用户在input内输入字符的个数最少是多少个才能展开搜索
⚠️:v-bind:start-at="2"告诉Vue,动态赋予一个表达式。虽然2是整数,但其实它是一个表达式。
另外,也可以增加非prop特性:
一个非prop特性是给组件添加一个特性,但在组件的props定义中没有这个特性。
非prop特性会添加到这个组件的根元素上。
3. props接受父组件传入的数据。
数据来源的类型是array对象。
[ { "userId": 1, "id": 1, "title": "sunt aut facere repellat provident", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum" },
...
因此,prop至少有3个:
props: { source: { type: [String, Array], required: true }, filterKey: { type: String, required: true }, startAt: { type: Number, default: 3 },
placeholder: {
type: String,
default: "",
}
4. 然后建立model/state, 即VueJS的data。
此时我们将有2个特性, 给data对象。
<script> export default { data() { return { items: [], query: '' } } } </script>
items数组用来保存从source prop传入的数据。
query是一个空string。它会和input标签绑定。使用v-mode指令。这样它会被实时的更新,无需刷新网页。
5. 在定义完model后,创建一个fetchItems方法来让items数组接收来自source prop的数据。
这个方法放入methods对象中。
<script> export default { methods: { fetchItems() { if ( typeof this.source === 'string' ) { fetch(this.source) .then(stream => stream.json()) .then(data => this.items = data) .catch(error => console.error(error)) } else { this.items = this.source } } } } </script>
这里有一个判断:
因为定义的source prop的type可能是string,也可能是array对象。
如果是string, 则使用Fetch API并把response的数据储存到items数组中。
如果用户手动传入的是array对象,则直接赋值给items.
fetch方法接收URL返回以promise对象。
第一个then把数据转化为json格式。
第二个then,则是涉及到实际的data,从服务器来的数据被分配给组件实例中的itmes数组。
还有catch方法,用于处理任何的错误。
6. 建立好fetchItems方法后,使用mounted 钩子函数,这样当组件挂载时,fetchItems被激发。
7. 现在设置Vue component的template部分。
增加一个input标签,query特性绑定v-model。
当在input中输入一个关键字时,会查找items中每个item对象中的body。并列出搜索到的item。
一个假设,输入框要求用户输入想要查询的文章的部分title,即能显示查询的相关记录。
所以之前用filterKey prop来储存item对象中的property。
8. 使用computed 特性。这是reactive响应式的。
定义一个filtered函数:
- 1. 当字符输入大于startAt prop,则继续,否则退出filtered函数。
- 2.对items的每个item进行验证,使用Array.protptype.filter()方法,这会生成一个新的array,内含符合filter内函数判断为true的item.
-
-
computed: { filtered() { if (this.query.length > this.startAt) { return this.items.filter(item => { // }) } } }
-
-
- 3. filter()内的函数:这里有一个假设,用户搜索的是item中的某个key的值:这里用filterKey来存储这个key。因此使用item.hasOwnProperty(this.filterKey)作为if的条件。
- 如果if false则console.error(`...${...}...`)。输出到控制台某个提示❌信息。
- 4. 最后,我们会检查这个item的filterKey的值,看它是否和用户输入的query互相匹配。
- 这里有2个方法:
- 1. 使用javascript中的正则表达式构建器进行匹配。
- 2. 或者使用indexOf()方法。string.indexOf("xxx")会返回“xxx”所处的位置,一个整数它必大于-1。所以可以使用 string.indexOf("xxx") > -1的方式进行条件判断。
- 另外,取消大小写的判断,用toLowerCase()来转为小写字符。
9. 现在filtered函数会返回符合用户输入条件的items对象集合(数组)。
让我们把这些数据,显示在网页上,修改template模版。添加:
<ul> <li v-for="item in filtered" v-bind:key="item.id"> //... </li> </ul>
为了能够使用过渡动画效果,可以用css自己写,但是有现成的工具,就无需自己再造轮子了(一个梗?)。使用Vuejs自带的<transition-group>,各种效果都可以实现具体看官方文档过渡&动画。
<transition-group name="fade" tag="ul" class="Results">
<li v-for="item in filtered" :key="item">
<span>
<strong>{{ item.title }}</strong> - <small>{{ item.id }}</small><br>
<small>{{ item.body }}</small>
</span>
</li>
</transition-group>
<transition-group>用于多个元素/组件的过渡效果。它渲染真实的DOM元素。
tag属性配置哪个元素应该被渲染,默认<span>.
name属性用于生成*-enter, *-leave-to等6个过渡的类名,默认是v-[enter|leave]-*
在<style>中加上对应的类:
.SearchInput { width: 100%; padding: 1.5em 1em; font-size: 1em; outline: 0; border: 5px solid #41B883; } .Results { margin: 0; padding: 0; text-align: left; position: relative; } .Results li { background: rgba(53, 73, 94, 0.3); margin: 0; padding: 1em; list-style: none; width: 100%; border-bottom: 1px solid #394E62; transition: ease-in-out 0.5s; #一个平滑的过渡设置。 }
#让开始到结束的整个过渡阶段,产生过渡效果。 .fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
#让开始(在元素被插入前生效)和结束(离开过渡被触发后的下一桢生效)不使用透明效果。(2个瞬间状态。) .fade-enter, .fade-leave-to { opacity: 0; }
10.如果filtered函数没返回满足用户输入的条件的结果。
我们需要显示一条提示信息告诉用户没有相关内容。
还是使用computed特性添加一个isEmpty函数:
<script> export default { computed: { isEmpty() { if( typeof this.filtered === 'undefined' ) { return false } else { return this.filtered.length < 1 } } } } </script>
解释:
用户没有输入的时候,this.filtered === "undefined", 不会显示提示信息。
如果用户输入信息后,没有找到记录则this.filtered.length < 1, 返回true,显示提示信息!
然后把这个computed特性中的isEmpty函数集成到template中:我们使用v-show命令来检查我们的。
<p v-show="isEmpty">Sorry, but we can't find any match for given term :( </p>
当有搜索结果后,用户可能做其他操作,比如点击出现的记录的链接,或者做些别的什么,这时程序可以清空<input>输入框。
在methods对象中添加一个reset()函数, this.query = ""
当<input>失去焦点时,激活reset()。用javaScript自带的Onburl event
<input v-model="query" @blur="reset"
最后的操作,把完成的组件集成到应用程序。
打开App.vue文件,这是由VueCLI3自动生成的。
改成这个样子:
<template> <div id="app"> <div class="Wrap text-center"> <h1>Vue Typeahead</h1> <p>Simple VueJS 2 TypeAhead component builded with Fetch Browser API.</p> <!-- Our component--> <typeahead source="https://jsonplaceholder.typicode.com/posts" placeholder="What TV Serie you are looking for..." filter-key="title" :start-at="2" > </typeahead> </div> </div> </template> <script> import Typeahead from './components/Typeahead.vue' export default { name: 'app', components: { Typeahead } } </script> <style> body { margin: 0; padding: 0; font-family: 'Open Sans', Arial, sans-serif; box-sizing: border-box; background: linear-gradient(135deg, #41B883 0%,#354953 100%); background-size: cover; min-height: 100vh; color: #fff; } h1 { font-size: 6vw; } h1, h2, h3, h4 { margin: 0; padding: 0; font-family: 'Lobster', Arial, sans-serif; } .text-center { text-align: center; } .Wrap { max-width: 60vw; margin: 15vh auto; } </style>
#
linear-gradient(方向,开始颜色,结束颜色)是一个设置背景色的函数:用它可以产生不同方向的颜色渐变背景。
- 方向: 定义一个开始点和gradient效果的方向
- 开始颜色, 想渲染的平滑过渡的颜色,可以附加一个百分数,即从什么比例的位置开始过渡。
- 结束颜色, 附加的百分数,决定过渡效果的结束点
总结:
如何创建一个简单的输入查询组件,并使用Fetch Api。其中使用VueJs中的常用功能,如props传入数据,使用computed properties创建,组织你的Vue程序。
附加git commit骚操作
修改了多次git 的readme. 让提交变得有点乱,所以想把所有readme的操作合并成一个commit。
1. git reset Head~3 (回退几步操作)
2.因为修改的代码是正确的所以 git stash
3.打开sourcetree, 树结构一看就明白。
git rebase -i [目标提交点] (即在这个提交点后的提交点重新排序)
4. 然后再git stash pop, 拿出之前放入stash的文件。
5. 最后git commit --amend, ?
6. 附加: 强制push到个人的远程仓库上
git push -f