启动项目
在学习了微信小程序之后,我使用wxml页面可以做出唐诗的内容了。结合wxss,我可以定义好看的布局。在小程序需要备案时,因为唐诗需要资质,而我个人无法满足这些条件,所以我需要开发新的项目。恰好最近在为我的技术笔记发愁。我希望有一个可以展示我笔记内容的工具。而visit 就是一个用于展示和搜索文章的小程序工具。
项目需求
我的最初想法非常简单,1,可以更新文章内容而不需要升级小程序。2,需要在小程序端进行展示。3,能够根据标题或者关键字进行搜索文章内容。
技术选型
- 小程序使用原生开发
- 界面 UI 使用 WeUI
- 内容渲染使用Markdown
知识储备
需要你了解微信小程序开发基础知识,Markdown内容编写,以及towxml库。
完成首页页面
首页页面非常简单,一个标题描述,用来解释小程序干什么?一个搜索框用来搜索内容,三个快捷按钮用于快速查找内容。
写首页的时候,需要用到的技术要点:
- 搜索框、按钮、列表、文本
- 界面交互
- 网络请求、数据存储
在微信开发者工具,app.json 文件中配置首页,配置后会自动生成首页模板页面。
"pages": [
"pages/index/index"
],
在 pages/index/index.wxml 文件中,编写首页内容。包含标题,描述,搜索框,以及快捷按钮。
<view class="page">
<view class="page__hd">
<view class="page__title">使用说明</view>
<view class="page__desc">收集了很多经典的代码片段和库,是学习编程的好工具。可通过关键字查找内容,用于解决开发中遇到的问题。</view>
</view>
<view class="page__bd page__bd_spacing">
<!-- 搜索框-->
<view class="weui-search-bar {{inputShowed ? 'weui-search-bar_focusing' : ''}}" id="searchBar">
<form class="weui-search-bar__form">
<view class="weui-search-bar__box">
<i class="weui-icon-search"></i>
<input type="text" confirm-type="search" class="weui-search-bar__input" placeholder="请输入您要查找的内容" value="{{inputVal}}" focus="{{inputShowed}}" bindinput="inputTyping" bindconfirm="search" />
</view>
<label class="weui-search-bar__label" bindtap="showInput">
<i class="weui-icon-search"></i>
<span class="weui-search-bar__text">搜索</span>
</label>
</form>
<view class="weui-search-bar__cancel-btn" bindtap="hideInput">取消</view>
</view>
<!-- 查询快捷按钮-->
<view class="weui-flex">
<view class="weui-flex__item">
<view class="placeholder" id="btnHot" bindtap="bindBtn" hover-class="placeholder-hover">最火</view>
</view>
<view class="weui-flex__item">
<view class="placeholder" id="btnNew" bindtap="bindBtn" hover-class="placeholder-hover">最新</view>
</view>
<view class="weui-flex__item">
<view class="placeholder" id="btnCold" bindtap="bindBtn" hover-class="placeholder-hover">最冷</view>
</view>
</view>
<!-- 内容-->
</view>
</view>
当使用模拟器预览时,发现首页内容是正确的,但是样式非常丑, 我使用 WeUI 来美化一下,使用weui是因为虽然它不是最美,但它的兼容性和稳定性最好,如果自己写wxss,需要考虑各种机型和设备。
从 Github 搜索 weui-wxss,下载后,将 weui.xss 中的内容复制到项目中的 app.wxss 中。复制后,这个 weui 样式就在整个项目中生效。如果要某个页面单独调整样式,可以在页面中 wxss 中单独修改样式。
首页数据交互
在画完页面之后,需要进行互动,小程序中和数据进行交互需要写JS文件。在JS文件中设置数据和方法,可以和页面进行互动。
根据首页页面实际情况,搜索框,快捷按钮都需要互动。先说搜索框,搜索框需要记录用户输入的内容,当按钮点击搜索,或者回车时,还需要根据用户输入的内容,去后台请求数据。
在 index.js 文件中,我们添加数据字段:
data: {
artList: [],
inputShowed: false,
inputVal: "",
keyword: "",
},
它们分别表示输入值,关键字,文章内容。还需要添加方法:
showInput: function () {
this.setData({
inputShowed: true
});
},
hideInput: function () {
this.setData({
inputVal: "",
inputShowed: false,
keyword: "",
artList: [],
});
},
inputTyping: function (e) {
this.setData({
inputVal: e.detail.value
});
},
search(e) {
var keyword = this.data.inputVal.toLowerCase()
this.setData({
keyword: keyword,
op: 1,
})
this.searchArt(1, keyword)
},
其中,inputTyping 和 search 方法是记录搜索内容,并向后台请求数据,其它两个方法是为了美化搜索框的效果。
当后台返回数据后,会赋值给 artList,它是一个数组列表,我们循环读取渲染它的内容。在 index.wxml 中的代码如下:
<!-- 内容-->
<!--循环输出-->
<view class="weui-cells">
<block wx:for="{{artList}}" wx:key="uuid" >
<view aria-labelledby="js_cell_l1_bd " aria-describedby="js_cell_l1_note" class="weui-cell weui-cell_access" hover-class="weui-cell_active" bindtap="jump" data-idx="{{index}}" data-guid="{{item.uuid}}" data-stars="{{item.views}}">
<view class="weui-cell__bd" id="js_cell_l1_bd" aria-hidden="true">
<view>{{item.title}}</view>
</view>
<view class="weui-cell__ft weui-cell__ft_in-access" aria-hidden="true">
<text wx:if="{{ item.lockState == 1 }}" id="js_cell_l1_note" aria-label=",锁" class="weui-badge weui-badge_dot">锁</text>
<text wx:elif="{{ item.createTime > yestTime }}" id="js_cell_l1_note" aria-label=",新" class="weui-badge weui-badge_dot">新</text>
<!-- <text wx:elif="{{ item.updateTime > item.createdTime }}" id="js_cell_l1_note" aria-label=",有更新" class="weui-badge weui-badge_dot">修</text> -->
<text wx:elif="{{ item.views > 50 }}" id="js_cell_l1_note" aria-label=",火" class="weui-badge weui-badge_dot">热</text>
<text wx:elif="{{ item.views > 500 }}" id="js_cell_l1_note" aria-label=",非常火" class="weui-badge weui-badge_dot">爆</text>
<text wx:elif="{{ item.views > 5000 }}" id="js_cell_l1_note" aria-label=",超级火" class="weui-badge weui-badge_dot">燃</text>
</view>
</view>
</block>
</view>
<!--循环输出-->
为了更好的体验,可在列表中根据浏览次数,显示不同的修饰词。让用户更好的了解到那篇文章质量更高,更受欢迎。点击列表后,会跳转到文章详情。通过 jump 方法实现。当在视图上需要更多的参数时,可以使用 data-格式,这样会传给 js 具体值,在请求后台数据章节会用到。
首页中还有 3 个快捷按钮,分别为最新、最火、最冷。根据文章的浏览量来排序展示。按钮本身有点击属性,当点击按钮时,将根据按钮设置的参数去后台查询对应的数据。这三个按钮都使用同一个查询方法,为了区分三个按钮,可以使用 view 的 id 属性。代码如下:
bindBtn: function (e) {
let btnId = e.target.id;
let qtype = 1;
switch (btnId) {
case "btnHot":
// 按浏览次数倒序排序,再按创建升序
qtype = 1;
break;
case "btnNew":
// 按创建倒序排序
qtype = 2;
break;
case "btnCold":
// 按浏览次数升序排序,再按创建升序
qtype = 3;
break;
default:
break;
}
this.setData({
artList: [],
qtype: qtype,
op: 2,
})
this.getArtList(1, qtype)
},
使用网络请求后台数据
如果只有静态页面,会缺少一些灵气。现在网络无处不在,掌握网络请求,是一项必须的技能。
小程序 HTTP 网络开发非常简单。小程序官方提供了 HTTP 的三个请求 API,分别是网络请求 request,上传文件 uploadfile,下载文件 downloadfile。
可使用这三个 API,去实现我们的需求。在使用本地开发者工具开发小程序时,默认不要勾选检验域名,方便我们调试。当上线的时候,我们需要将后台的域名配置到小程序后台域名白名单中。
核心是请求文章列表。我设计了两个接口,一个接口根据关键字查询文章列表,一个接口根据快接按钮属性查询文章列表。
1,根据按钮属性查询文章列表代码如下:
// 根据类型查询
getArtList: function (pageNo, qtype) {
const that = this
that.loading = true
wx.showLoading({
title: '加载中...',
mask: true,
})
if (pageNo === 1) {
that.setData({
artList: [],
})
}
httpGet('/artl', {
'pageNo': pageNo,
'qtype': qtype,
}).then((res) => {
that.loading = false
wx.hideLoading()
const result = res.data;
if (result.code == 1) {
const articles = result.data;
that.setData({
page: pageNo, //当前的页号
pages: result.count, //总页数
artList: that.data.artList.concat(articles)
})
} else {
wx.showToast({
title: '没有找到记录',
})
}
}).catch((err) => {
that.loading = false
wx.hideLoading()
wx.showToast({
title: '网络异常请重试',
})
})
},
2,根据关键字查询文章列表代码如下:
searchArt: function (pageNo, keyword) {
const that = this
that.loading = true
wx.showLoading({
title: '加载中...',
mask: true,
})
if (pageNo === 1) {
that.setData({
artList: [],
})
}
httpGet('/sart', {
'pageNo': pageNo,
'keyword': keyword,
}).then((res) => {
that.loading = false
wx.hideLoading()
const result = res.data;
if (result.code == 1) {
const articles = result.data;
that.setData({
page: pageNo, //当前的页号
pages: result.count, //总页数
artList: that.data.artList.concat(articles)
})
} else {
wx.showToast({
title: '没有找到记录',
})
}
}).catch((err) => {
that.loading = false
wx.hideLoading()
wx.showToast({
title: '网络异常请重试',
})
})
},
两个方法大体框架差不多,后台接口名不一样。在这里,我多说两句,这两个接口可以合并成一个接口,有好处也有坏处。好处是减少接口的数量,坏处是减少了灵活性,耦合性太强,修改一个接口内容会影响到另一个接口。所以大家可以根据实际需求来设计。我经历的好多接口都是合并之后,在新增需求后又再次拆开,然后继续优化时会再次合并。
网络请求这块知识点不多,大多时间都是和后台的调试。这次没有用到文件上传和文件下载。这两个 API 也非常的简单。我的另一个小程序【豆子工具】中有用到,可以看看它的项目。
当获取到数据后,赋值给 artList,小程序会自动渲染数据到页面。另外两个数据 page 和 pages 是为了分页使用。
that.setData({
page: pageNo, //当前的页号
pages: result.count, //总页数
artList: that.data.artList.concat(articles)
})
当数据渲染到首页后,我们就可以看到文章列表,当点击文章列表中的某个文章项时,会跳转到文章详情。这次我们使用小程序解析 Markdown 格式内容。
解析 Markdown 文件
当我们使用网络请求后台数据后,赋值给 artList,这样首页页面就展示了文章列表。当我们点击文章列表中的某项时,会再次请求后台,获取文章的详情。文章的详情是 Markdown 数据。
在小程序中,无法直接展示 Markdown 数据,我们需要将 Markdown 转换为 WXML,网上的大神很多,有多款 Markdown 转 WXML 库,我使用的是 towxml 库。我使用Markdown主要是因为更新内容不用升级小程序,这是动态渲染的最大优点。当然它还有其它一些优势。
1,按照 towxml 库的说明文档,我们将编译后的 towxml 复制到我们的项目中,然后在 app.js 文件中引入这个组件。
App({
towxml: require('./towxml/index'),
2,按照首页的步骤,我们添加文章详情页面。
"pages/article/index"
在 index.wxml 文件中,我们添加页面布局,这个很简单,显示渲染后的内容即可。
<!--pages/article/index.wxml-->
<view class="page">
<!--使用towxml-->
<towxml nodes="{{article}}" />
</view>
3,注意,我们还需要在 index.json 中添加组件引入。
{
"usingComponents": {
"towxml": "../../towxml/towxml"
}
}
4,在 js 文件中,我们需要调用 Markdown 数据。
onLoad(options) {
const _ts = this;
wx.showLoading({
title: '加载中',
})
httpGet('/artd', {
uuid: options.guid,
}).then((res) => {
const result = res.data;
if (result.code == 1) {
let content = result.data;
let obj = app.towxml(content, 'markdown', {
theme: 'light',
events: {
tap: (e) => {
console.log('tap', e);
}
}
});
_ts.setData({
article: obj,
});
wx.hideLoading({
success: (res) => { },
})
} else {
wx.hideLoading()
}
}).catch((err) => {
console.log(err);
wx.hideLoading()
wx.showToast({
title: '网络异常请重试',
})
})
},
我们在 onLoad 中加载函数,这样页面启动就会自动网络请求,然后渲染 Markdown 数据。在请求数据时,我们注意到 guid,这个参数是上一个页面即首页,点击文章列表项带过来的数据。它就是上节中,在 view 中设置 data-属性,可以将页面参数传给 JS。
首页跳转到详情页方法如下:
jump: function (e) {
const guid = e.currentTarget.dataset.guid
// 调整到文章页面
wx.navigateTo({
url: '../article/index?guid=' + guid,
})
},
现在,关于文章展示和搜索的功能就完成了。