在上一章中,我们完成了 热搜首页 的开发,虽然经历了 ”千辛万苦“ ,但是对大家来说,应该也是收获满满。
那么在这一章节,我们将会进入新的篇章,来到 文章搜索 页面的开发。那么在 文章搜索 的页面开发中,我们又会经历哪些 奇奇怪怪的 bug
,又将会获得哪些新的收获呢?
让我们一起期待吧!
在开发文章搜索页面之前,先来分析一下这个搜索页面都有哪些内容……
整个搜索页面分为三块大的内容:
首页 -> 点击搜索框 -> 慕课搜索页面 -> 聚焦「搜索框」 -> 搜索历史页面 -> 往输入框输入内容或不输入内容,回车 -> 搜索结果页面
placeholder
索引item
的展示分为三类
以上这些内容,就是我们整个文章搜索页面之中所具备的一些能力,接下来的开发就是围绕这三大块来进行开发!
创建文章搜索页面,即创建search-blog
页面
但在创建这个页面的时候,我们将使用「分包」这个概念来创建我们的页面
💡:什么是分包?
分包 指的是将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载
简单来说,就是把一个完整的项目拆成了不同的几个子项目,或者你可以认为我们将一个大的 JS 文件按照模块拆成了几个小的 JS 文件 -> 当我们去构建我们这个项目的时候,就可以把这些功能打包成不同的分包,也就是所谓的模块 -> 当用户使用时就可以进行按需加载了
把「小程序」看做是一个大的 JS 文件,把「子包」看做是小的模块,那么「啥是分包」就很好理解了
💡:分包的好处
100 k
),现在把它拆成十个小模块(假设每个模块是10 k
),可以看到 10 k
的下载速度显然要比100 k
的快!微信小程序提供了分包的能力,而 uni-app 也对分包的功能进行了支持!
在 uni-app 中如何实现分包功能 ?
pages.json
,新建 subPackages
节点 -> 是一个数组,数组中每一个对象元素都是一个分包root
:分包包名name
:分包别名pages
:分包下的关于别名的页面路径和窗口表现
path
:分包下的页面路径style
:页面的样式path
自动创建页面
subpkg
目录
pages
目录 -> 存放当前所有的页面
search-blog.vue
-> 如果你对 uni-app 不爽,那你可以在 VS Code 里边通过之前安装的插件来新建页面创建的
search-blog.vue
是子页面 -> 这个文件在subpkg/pages/search-blog/
里边,我们得手动创建它 -> 你用 HbuilderX 创建这个页面时,会有不好的体验
💡:如何验证我们当前的分包已经实现了?
微笑小程序开发者功能 -> 详情 -> 本地代码有:主包 + /subpkg/
(分包)这两个包 -> 这就证明我们的分包已经创建完成了
如果没生效,请注意是不是 uni-app 的编译问题!
我看了这个项目最终完成时的分包代码:
这样岂不是只分了一个包?
👇:有了这个分包页面后,就可以来开发这个search-blog
页面了
定位到首页,完成页面跳转
hot.vue
-> my-search
组件的包裹器view
添加点击事件 -> @click="onToSearch"
view
-> 触发onToSearch
-> 使用uni.navigateTo({})
方法完成页面跳转 -> 页面路径在分包路径下:'/subpkg/pages/search-blog/search-blog'
search-blog
页面渲染搜索框
my-search
组件目前值拥有一个按钮,还没有输入的能力 -> 我们需要赋予它新能力(下一节再赋予,这节只是让它渲染出来)效果:
我们希望通过my-search
组件来渲染我们的搜索框,但是就目前的这个my-search
组件,它并不具备搜索能力
💡:如何给my-search
赋予搜索的能力?
对于 uni-app 来说,它提供了一个uni-search-bar
搜索栏组件
文档:uni-search-bar 搜索栏 - DCloud 插件市场
这个搜索栏组件具备的功能:
从文档里边我们可以看到它的:
icon
)我们之前创建这个项目的时候,选择了uni-ui
,所以这个搜索栏组件已经被安装了
因此,我们可以直接使用它
总之,我们可以利用它来完成我们的搜索功能
💡:实现逻辑
为了好调试(不要每次都在
hot
页面点击然后进入页面),请修改编译模式,把search-blog
页面作为启动页面
my-search.vue
uni-search-bar
组件标签isShowInput
这个props
config
这个props
(有图标、背景色、边框等样式的配置) -> 让父组件指定my-search
的样式uni-search-bar
传递参数,如radius
、bgColor
等
uni-icons
组件,图标类型是clear
my-search-bar
添加宽度100%
样式value
:uni-search-bar
不使用双向数据绑定,因为会修改父组件传递过来的数据 -> 毕竟我们定义了一个value
props -> 所以使用单向绑定效果:
💡:uni-icons
的效果
💡:什么时候触发某个事件?
confirm
在输入框内回车就会触发
💡:让my-search
组件具备非常强大的搜索输入框能力
my-search
组件输入内容 -> 触发onInput
事件 -> 把这个事件的发生通知给父组件,并且把这次输入的内容传给父组件
父组件在使用my-search
组件时添加v-model
注意,可以使用这个v-model
是有条件的(vue 中的基本知识):
v-model
是 vue 中完成双向数据绑定的指令,如果说该指令想要应用到组件的绑定中,那么需要遵守以下条件:
my-search
组件中接收到的值必须以value
命名value
时,必须要发送一个叫做input
的事件满足以上两点,父组件就可以通过v-model
指令把值直接传递给value
这个props
了
这个代码的主要功能就是把子组件触发的事件抛给父组件这一层去处理 -> 关键点:父子组件的双向数据绑定!
💡:占位内容居中展示?
设计稿是居左展示
这个功能是无法通过uni-search-bar
进行配置的,毕竟它没有提供可以让文本居中这样的一个属性
当组件无法提供给我们想要的能力的时候,那我们只能去修改这个组件了 -> 也就是修改uni-search-bar
的源代码
.uni-searchbar__box {
/* 处理初始 searchbar 位置 */
/* 默认值是 center */
justify-content: start;
}
至此,我们就完成了对整个搜索框的赋能,我们给my-search
组件赋予了全新的能力:搜索输入框
本质就是间接给uni-search-bar
组件添加非常多的配置项,以及对应的回调方法
并且,我们也可以通过v-model
来完成父组件与子组件中绑定数据的双向绑定:
父组件search-blog
:<my-search v-model="searchVal"></my-search>
子组件my-search
:value
+ this.$emit('input',val)
需要发送请求 -> 看「返回默认搜索内容」这个接口
api/search.js
-> 和搜索相关的接口都在这儿
getDefaultText
函数search-blog.vue
里边定义loadDefaultText
函数 -> 用来加载数据 -> 在created
里边调用创建我们开头所说的三个组件:
毕竟我们整个文章搜索页面,说白了就是由这三个组件来组成的
目前这个代码的效果有个小问题:
为啥会抖动?
默认情况下是有5px
,当你输入时,text
变成了input
,这5px
就消失了
💡:创建三个组件
search-hot-list
:搜索列表search-history
:搜索历史search-result-list
:搜索结果在search-blog
页面里边渲染这三个基本组件
正常情况下,这三个组件可不会一同展示!
👇:控制这个三个组件的展示!
按照不同的业务逻辑,分别展示这三个组件:
item
或者用户点击搜索历史 item
或者输入框回车或输入内容后回车,就会展示「搜索结果」页面组件的展示逻辑搞明白后,就得通过代码来实现这个展示逻辑了!
💡:实现过程
showType
来决定展示哪个组件
0
、1
、2
表示吗? -> 这很不清晰,得用常量HOT_LIST
、 SEARCH_HISTORY
、SEARCH_RESULT
HOT_LIST
template
里边,只可以访问data
中定义的数据)
data
showType
的值 -> 也就是那几个事件要干的事儿
clear
事件 -> 此时展示搜索结果,你点击清空按钮,也得返回搜索历史👇:实现这三个组件
分析热搜列表的业务:
如何实现? -> 获取热搜列表数据 -> 看接口文档
效果:
组件在被
v-if
切换时,是会销毁组件的! -> 所以这个请求也会重新发起 -> 毕竟又重新渲染组件了!
👇:把获取到的数据进行一个基本的展示
search-hot-title
search-hot-item
hot-ranking
组件hot-icon
-> 只展示前三个效果:
👇:对热搜列表中,关于item
项的点击事件
点击 item 项会有一个搜索效果 -> 相当于是你往搜索输入框输入你所点击的这个 item 的标题
实现:
search-blog
去处理
onSearch
事件给父组件 -> 参数是这个 item 的标题onSearch
事件 -> 直接触发了那个onSearchConfirm
回调效果:
至此,这个热搜列表就已经完成了 -> 热搜列表是「文章搜索」中最简单的一个内容,只需要获取数据,然后展示数据,处理一下 item 事件就可以了
👇:处理搜索历史的业务逻辑
换一种套路来写这个组件 -> 分成两步来实现这个「搜索历史」:
分析完成后的结果:
实现:
search-history
:
search-history-title-box
search-history-title
×
按钮,展示「辣鸡箱」
search-history-box
效果:
至此,搜索历史的 HTML 就完成了,整个 HTML 非常简单,没有涉及到任何的业务逻辑
👇:把 HTML 对应的 CSS 搞定,再去看剩下的复杂内容
实现:
效果:
👇:完成搜索历史的业务逻辑 -> 会面临很多困难
完成搜索历史的切换逻辑
方案:把搜索历史保存到本地,而不是发送请求
💡:怎样的数据算是搜索历史数据?
当用户对该数据进行了搜索之后,那么该数据就算是搜索历史的数据了!
💡:实现
onSearchConfirm
方法 -> 调用saveSearchData
(保存这个输入内容到搜索历史数据中)效果:
👇:实现searchData
中的删除操作
删除分为两种:
uni.showModal
:显示模态弹窗,可以只有一个确定按钮,也可以同时有确定和取消按钮。类似于一个 API 整合了 html 中:alert、confirm。
实现过程:
onClearAll
onHistoryItemClick
onClearAll
的逻辑:
uni.showModal({})
removeAllSearchData
通知 -> 切换回「垃圾箱」onHistoryItemClick
的逻辑:
×
」按钮 -> 抛出removeSearchData
通知,传当前被点击的item
索引removeSearchData
事件 -> 从历史数据里边删除这条removeAllSearchData
事件 -> 清空历史数据效果:
💡:抛出一个通知,会先执行这个通知所对应的事件处理函数
到目前,我们已经完成了 搜索历史 的展示和删除的功能,但是还有一个 数据持久化 的功能未实现
未实现的功能我们先不着急,我们先回头看一下我们现在的代码
在现在的代码中,我们在
search-blog
中通过searchData
保存了所有的搜索历史数据而在真正的搜索历史页面中,反而是通过
props
接收了 父组件传递过来的数据把 添加、删除搜索历史的功能,都放到了
search-blog
页面里进行了实现,反而把 删除的激活 操作放到了search-history
组件中
这样的一系列操作,我们光描述都要花费上 一分钟 的时间,更不用说让别人去读你的代码了
如果我们在这样的代码基础之上,再去实现 数据持久化 的功能,那么咱们的代码就会的更加复杂,难以理解了
所以说,我们现在迫切需要做一件事情,那就是:让 search-blog
和 search-history
解耦,让 searchData
和 组件 解耦 !
那么这个事情,我们怎么做呢?
这里请允许我先卖一个关子
欲知后事如何,请见下一章《全局状态管理》
本章节中,我们完成了 部分 的文章搜索功能
search-blog
页面search-hot-list
search-history
search-result-list
search-blog
和 search-history
、searchData
和 组件 之间(产生了)强耦合 (关系)那我们该如何解决这个「强耦合」问题呢?
想要解决这个问题,那么我们需要用到一个新的东西,叫做 《全局状态管理工具》,那么这个东西怎么用呢?它有什么样的价值呢?
我们下一章再见!