小程序-豆瓣电影项目实例
小程序-豆瓣电影项目实例,包含三个功能模块(首页列表、搜索列表、介绍页) ,适合刚入门学习的同学
资料
1. 小程序官方文档
2. 微信web开发者工具下载地址
3. 微信小程序处理方案专辑
4. 豆瓣电影API
效果
豆瓣电影.gif
开始
这里先说明下,此案例使用的是体验模式,未使用AppID,这是由于我们的请求用的是第三方豆瓣API,配置安全域名的话也是没有权限访问,所以我就偷了个懒,直接使用自己写的一个node代理商服务来请求资源。假如使用了AppID的话,那就配置一个自己能访问的域名信息。
打开微信web开发者工具,新建个小程序
新建项目.png
选择体验小程序,确定后小程序自动会帮助我们生成一个项目目录。下面是我的项目的整体结构
// 目录结构.├── common│ ├── images│ ├── filter.wxs // 自己封装的过滤器│ └── head.wxml // 头部模板├── node-proxy // node服务器│ ├── index.js│ ├── package.json├── component // 自己设置公共组件│ └── movies // 电影列表组件目录├── store│ ├── index // 首页:展现正在上映电影列表│ ├── search // 搜索页:展现搜索后电影列表│ └── detail // 电影介绍├── app.js // 注册小程序├── app.json // 小程序全局配置文件├── app.wxss // 全局样式└── project.config.json // 工具配置文件.node服务
在node-proxy目录下创立我们的服务,怎样搭建这里不做多详情,不会的直接拷贝文件后运行命令npm install或者者淘宝的镜像cnpm install就可
// node-proxy/package.json{ "name": "node-proxy", "version": "1.0.0", "description": "豆瓣电影服务器端", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "express": "^4.16.2", "superagent": "^3.8.2" }}// node-proxy/index.js// 引入模块const express = require('express'), request = require('superagent');// 创立APPconst app = express();// 豆瓣电影APIconst host = 'https://api.douban.com/v2';app.all('*', function (req, res, next) { if (!req.get('Origin')) return next(); // use "*" here to accept any origin res.set('Access-Control-Allow-Origin', '*'); res.set('Access-Control-Allow-Methods', 'GET'); res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type'); if ('OPTIONS' == req.method) return res.send(200); next();});// 正在上映 APIapp.get('/movie/in_theaters', function (req, res) { reqHttp(req, res);});// 搜索app.get('/movie/search', function (req, res) { reqHttp(req, res);})// 介绍app.get('/movie/subject/:id', function (req, res) { reqHttp(req, res);})function reqHttp(req, res){ let reqHttp = request.get(host + req.originalUrl); reqHttp.pipe(res); reqHttp.on('end', (err, res) => { });}// 监听app.listen(5200);而后我们通过node index.js 来启动服务
全局配置
我们有三个模块(首页、搜索、介绍页),所以先配置好他们的路由
{ "pages":[ "pages/index/index", "pages/search/search", "pages/detail/detail" ], "window":{ "backgroundTextStyle":"light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "小程序-豆瓣电影", "navigationBarTextStyle":"black" }}首页
首页有两大块:头部和电影列表
头部: 我们把头部设想为一个公共的模板页面来引入
电影列表: 由于首页和搜索页面都需要展现电影列表,那就让它们也共用吧,直接使用小程序的自己设置组件来来实现此功能,关于自己设置组件,请参考官方文档
// pages/index/index.wxml<!-- 模板引入 --><import src="../../common/head.wxml"/><view class="container"> <!-- 使用模板 --> <template is="head"/> <!-- 以下是一个自己设置组件的引用 --> <movies movie-type="in_theaters"></movies></view>这里我们使用了一个组件名为movies的标签,这个名称是怎样来的,我下面讲解
// common/head.wxml<template name="head"> <view class="page-head"> <form class="head-form" bindsubmit="formSubmit"> <input placeholder="输入电影名称" class='head-input' name="searchKey" /> <button formType="submit" class='head-button'>搜索</button> </form> </view></template>头部搜索后bindsubmit="formSubmit"有个事件解决,而后根据条件进入搜索界面
// pages/index/index.jsPage({ data: {}, formSubmit(e){ let _searchKey = e.detail.value.searchKey; // 这里做值校验 if(!_searchKey)return; wx.navigateTo({ url: '/pages/search/search?q=' + _searchKey }) }})现在我们头部功能已经完成,如同漏了点东西,那就是我们的全局样式。代码有点多,我就直接放压缩的了, 要看非压缩的我下面会提供项目完整路径,可下载
// app.wxsspage{height:100%;font-size:28 rpx;line-height:1.6;color:#666;background-color:#f8f8f8}checkbox,radio{margin-right:10 rpx}form{width:100%}.padding-10{padding:10 rpx}.container{min-height:100%;justify-content:space-between;font-size:32 rpx;font-family:-apple-system-font, Helvetica Neue, Helvetica, sans-serif}.page-head{background-color:#fff;border-top:1px solid #e7e7e7;border-bottom:1px solid #e7e7e7;height:80 rpx;width:100%;font-size:28 rpx}.head-form{display:block;padding-top:12 rpx;text-align:center;line-height:56 rpx}.head-form .head-input{display:inline-block;border:1px solid #ccc;background-color:#fff;border-radius:8 rpx;text-align:left;padding:0 10 rpx}.head-form .head-button{display:inline-block;background-color:#fff;border-radius:4 rpx;width:100 rpx;line-height:56 rpx;height:56 rpx;padding:0;margin-left:14 rpx;font-size:28 rpx}下面开始我们的电影列表的自己设置组件
自己设置组件-电影列表
一个自己设置组件由 json wxml wxss js 4个文件组成。有个快捷操作能直接生成这4个文件
自己设置组件.png
要编写一个自己设置组件,首先需要在 json 文件中进行自己设置组件公告(将 component 字段设为 true 可这一组文件设为自己设置组件),假如选用快捷方式生成的文件, 这里已经自动帮我们写好了
// component/movies/movies.json{ "component": true}下面开始编写我们的电影列表组件模板,以及样式。编写组件样式时,需要注意少量问题:
- 组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器。
- 组件和引用组件的页面中使用后代选择器(.a .b)在少量极端情况下会有非预期的体现,如遇,请避免使用。
- 子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其余组件可能导致非预期的情况。
- 继承样式,如 font 、 color ,会从组件外继承到组件内。
- 除继承样式外, app.wxss 中的样式、组件所在页面的的样式对自己设置组件无效。
- 组件可以指定它所在节点的默认样式,使用 :host 选择器
// component/movies/movies.wxml<!-- 组件模板 --><view> <text class="padding-10">{{movieTitle}}</text></view><view class='movie-list'> <view class='movie-item' wx:for="{{movieList}}" wx:for-item="movie"> <navigator url="/pages/detail/detail?id={{movie.id}}"> <view class='item-inner'> <image mode="scaleToFill" src="{{movie.images.medium}}" class='movie-image'/> <view> <text class="movie-name">{{movie.title}}</text> </view> <image src="../../common/images/rating{{movie.rating.stars}}.png" class="bigstar"/> {{movie.rating.average}} </view> </navigator> </view></view>// component/movies/movies.wxss.padding-10{ padding: 10rpx;}.movie-list{ display: flex; flex-wrap: wrap; padding: 5rpx;}.movie-item{ width: 33.333333%; text-align: center; overflow: hidden;}.item-inner{ margin: 5rpx; color: #666; font-size: 28rpx;}.movie-image{ width: 100%; height: 300rpx;}.bigstar{ width: 120rpx; height: 24rpx;}.movie-name{ display: block; font-size: 28rpx; text-align: center; white-space:nowrap; overflow: hidden; text-overflow: ellipsis;}在自己设置组件的 js 文件中,需要使用 Component() 来注册组件,并提供组件的属性定义、内部数据和自己设置方法。在实现交互之前,我们先配置一下请求的全局变量,也就是node服务地址
// app.jsApp({ globalData: { apiUrl: 'http://localhost:5200' }})// component/movies/movies.jsconst app = getApp();Component({ properties: { // 定义组件的对外属性,属性值可以在组件使用时指定 movieType: { type: String, value: 'in_theaters', } }, data: { // 这里是少量组件内部数据 movieTitle: '', movieList: [] }, ready(){ // 验证请求来源 搜索、首页 let _url = 'in_theaters'; // 获取电影列表 this.getMovieList(_url); }, methods: { // 获取电影列表 getMovieList (url) { let _this = this; wx.showLoading(); wx.request({ url: app.globalData.apiUrl + '/movie/' + url, method: 'GET', data: { count: 15 }, success: function (res) { // 此处省略请求能否成功校验 _this.setData({ movieTitle: res.data.title, movieList: res.data.subjects }); wx.hideLoading(); } }) } }})至此,我们还差最关键的一步,在我们需要使用该组件页面的JSON文件中进行申明引入usingComponents
// pages/index/index.json{ "usingComponents": { "movies": "../../component/movies/movies" }}我们上面提到index.wxml有一个movies标签组件是怎样来的问题,它就是这里配置而来
现在我们的首页已经完成,不出意外的话你可以看到如下页面:
首页.png
搜索页
电影列表完成后,我们搜索界面已经非常简单了,在JSON文件中引入申明后页面直接引入组件
// pages/search/search.json{ "navigationBarTitleText": "电影搜索", "usingComponents": { "movies": "../../component/movies/movies" }}// pages/search/search.wxml<view class="container"> <!--以下是对一个自己设置组件的引用 --> <movies movie-type="search"></movies></view>你可能在想,那我们的search.js要不要做什么事呢,很清楚的告诉你, 啥都不用管,让它自生自灭吧。我们只要要自己设置组件movies.js加入一个类型校验就可。
眼尖的你可能发现我们在页面引入组件的时候都有一个movie-type的属性,在自己设置组件movies.js中有一个properties来接收,properties定义组件的对外属性,属性值可以在组件使用时指定
// index.wxml<movies movie-type="in-theaters"></movies>// search.wxml<movies movie-type="search"></movies>Component({ properties: { // 定义组件的对外属性,属性值可以在组件使用时指定 movieType: { type: String, value: 'in_theaters', } }, data:{} ...})所以我们即可以根据MovieType验证它的请求来源,来做相应的请求操作。改动的代码很少,只需在ready方法中加入一个判断就可
// component/movies/movies.jsComponent({ ... ready(){ // 验证请求来源 搜索、首页 let _url = 'in_theaters'; if (this.properties.movieType == 'search') { // 搜索页面请求解决 var _pages = getCurrentPages(), _curPage = _pages[_pages.length - 1]; // 获取到搜索带过来的参数并复制到请求的url接口中 _url = 'search?q=' + _curPage.options.q; } // 获取电影列表 this.getMovieList(_url); } ...})搜索功能也完成,是不是炒鸡简单,输入搜索内容后看下页面效果
电影搜索.png
介绍页
电影介绍只需根据ID查询对应的数据在展现页面,相信大家都知道怎样玩了,那就直接上代码了
// pages/detail/detail.wxml<wxs module="filter" src="../../common/filter.wxs"></wxs><view> <image class="movie-image" mode="scaleToFill" src="{{movieInfo.images.large}}"/></view><view class="padding-10"> <view class="movie-title">{{movieInfo.title}}</view> <view>导演:{{filter.joinName(movieInfo.directors)}}</view> <view>主演:{{filter.joinName(movieInfo.casts)}}</view> <view class="movie-summary">简介{{utils.formatTime()}}</view> <view>{{movieInfo.summary}}</view></view>// pages/detail/detail.wxss.movie-image{ width: 100%; height: 600rpx;}.movie-title{ margin-top: 30rpx; font-size: 40rpx; font-weight: bold; color: #333;}.movie-summary{ border-top: 1rpx solid #eee; margin-top: 20rpx; padding-top: 20rpx; color: #444; font-size: 32rpx;}// pages/detail/detail.json{ "navigationBarTitleText": "电影介绍"}// pages/detail/detail.jsconst app = getApp();Page({ // 组件的初始数据 data: { movieInfo: {} }, // 组件加载事件 onLoad(options){ let _this = this; wx.showLoading(); wx.request({ url: app.globalData.apiUrl + '/movie/subject/' + options.id, method: 'GET', success: function (res) { // 此处省略请求能否成功校验 _this.setData({ movieInfo: res.data }) _this.movieInfo = res.data; wx.hideLoading(); } }) }})PS:在这里我们要注意的一个是我们在detail.wxml文件中使用了过滤器,假如我们有时候需要对少量特殊字段进行解决的时候可以直接使用此方式。在wxs文件中不支持es6语法,否则会编译失败,这个也的注意下,希望后面小程序能处理此问题
// common/filter.wxsvar filter = { joinName: function (arrObj) { var _arrName = []; if (!arrObj)return ''; for (var i = 0; i < arrObj.length; i++) { _arrName.push(arrObj[i].name) } return _arrName.join('、') }}module.exports = { joinName: filter.joinName}效果图
电影介绍.png
完工
好了,一个简单的豆瓣电影小程序功能就开发完成。新手练习可把此项目复制到自己的GitHub上,后在项目基础上加入少量简单的功能来巩固自己,如:下拉刷新、加载更多。在厉害点可加入豆瓣图书的功能,底部有两个tab标签,一个电影一个图书去扩展等等。
源码地址:小程序-豆瓣电影,假如觉得有帮助,可star一下啊
2018.2.6升级 新添加WePY实现
最近发现个非常好用的小程序框架WePY,WePY框架在开发过程中参考了 Vue 等现有框架的少量语法风格和功能特性,对原生小程序的开发模式进行了再次封装,更贴近于 MVVM 架构模式, 并支持ES6/7的少量新特性。
优势.png
源码地址:WePY小程序-豆瓣电影
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 小程序-豆瓣电影项目实例