大纲目录
- 第1章 课程导学
- 第2章 倘想达到最高处,就要从低处开始 (uni-app 基础)
- 第3章 千里之行,始于足下 (基础配置)
- 第4章 工欲善其事,必先利其器 (uniCloud 的基础用法)
- 第5章 扬帆起航,胜利在向你招手 (首页功能模块)
- 第6章 做事是否快捷,不在一时奋发,而在能否持久(搜索页功能模块)
- 第7章 锲而不舍,金石可镂(标签页功能模块)
- 第8章 坚持就是胜利,坚持才会有所成就(详情页功能模块)
- 第9章 关注页功能模块
- 第10章 个人信息页功能模块
- 第11章 积少成多,走向完善(项目优化与平台兼容)
- 第12章 最后的冲刺,成功就在眼前(项目发行与打包)
- 第13章 常常是最后一把钥匙打开了门(课程总结与展望)
第1章 课程导学
第2章 倘想达到最高处,就要从低处开始 (uni-app 基础)
2-3 uni-app 核心知识点概览
1 条件编译
条件编译写法 | 说明 |
---|---|
#ifdef APP-PLUS 需条件编译的代码 #endif | 仅出现在 App 平台下的代码 |
#ifndef H5 需条件编译的代码 #endif | 除了 H5 平台,其他平台均存在的代码 |
ifdef H5 || MP-WEIXIN 需条件编译的代码 #endif | 在 H5 平台或微信小程序平台存在的代码(这里只有|| , 不可能出现&& , 因为没有交集) |
2 App 端的 Nvue 开发
3 HTML5+
2-4 搭建uni-app开发环境
1 安装并运行HbuiderX
2 使用 vue-cli 的方式运行项目
- 检查 node.js 版本
node -v
#v14.15.4
npm install -g cnpm --registry=https://registry.npm.taobao.org
设置镜像然后再npm install -g @vue/cli
装到全局的环境中- 自己出现了报错的情况,需要先运行
sudo chown -R $USER /usr/local
之后再安装就好了. - 检查
vue-cli
是否安装vue --version
#2.9.6
- 进入目录
vue create -p dcloudio/uni-preset-vue test-uniapp
2-5 语法速通-模板语法与数据绑定
{{title}}
绑定变量
v-bind:class="className"
绑定class,可以简写为:class="className"
v-on:click="open"
绑定事件,可以简写为@click="open"
动态数据绑定
this.title = "hello"
这个和小程序中不同. 小程序中是this.setData({xx: 'XX'})
2-6 语法速通-条件判断
①
v-if
条件判断, 为真的时候,才渲染. 为false
则不渲染.
<view v-if="true">hello uni-app</view>
②
v-if
绑定变量, 为false
和0
的时候,不渲染
<view v-if="show">hello uni-app</view>
...
data() {
return {
show: false
}
},
③
v-if
中可以接表达式例如v-if="show === 'hello'"
④
三元表达式
{{show ? 'hello' : 'world'}}
⑤
v-if
v-else-if
v-else
, 根据参数来识别,这2个view要紧挨着.v-else-if
可以省略.
<view v-if="show === 1">uni-app</view>
<view v-else-if="show === 2">vue</view>
<view v-else></view>
2-7 语法速通-列表渲染
①
v-for
通过一个数组来渲染我们的列表
<view v-for="(item,index) in arr" >{{item}}</view>
...
<script>
return {
arr : ['uni-app', 'vue', 'html']
}
</script>
②
v-for
加上索引项index
写法如下
<view v-for="(item,index) in arr" >{{(index+1) + ' ' +item}}</view>
...
<script>
return {
arr : ['uni-app', 'vue', 'html']
}
</script>
③
v-for
循环打印对象
<view v-for="(key,value) in arr" >{{key + ' ' +value}}</view>
...
<script>
return {
arr: {
name: 'LiMing',
age: 18,
type: 'eat'
}
}
</script>
2-8 语法速通-基础组件
view
text
image
…
双向绑定
< input v-model="vlue" />
2-9 语法速通-自定义组件
① 在根目录中建立
components
文件夹, 再建立一个/btn/btn.vue
btn.vue
源码如下,可以自己定义样式,行为.
<template>
<view class="btn-box" @click="clickBox">
点击
</view>
</template>
<script>
export default {
methods: {
clickBox() {
console.log('click');
}
}
}
</script>
<style>
.btn-box {
width: 200px;
height: 100px;
text-align: center;
line-height: 100px;
}
</style>
在需要的地方,进入,然后注册.
components
注册组件,然后就可以直接使用了.
<template>
<view class="content">
<btn></btn>
</view>
</template>
<script>
import btn from '@/components/btn/btn.vue'
export default {
components: {
btn
},
}
</script>
② 给组件传值 关键词
props
, 然后标注参数类型.
- index.vue 中
<btn color="red"></btn>
- 组件中,用
<template>
<view class="btn-box" :style="{color: color}">
点击
</view>
</template>
<script>
export default {
props: {
color: {
type: String,
default: '#000'
}
}
}
</script>
③ 页面上拿到组件中的返回值
index.vue
中注册监听事件
<btn color="red" @change="change"></btn>
...
methods: {
change(e) {
console.log("我是页面的事件,我返回了" + e);
}
}
- 组件中,通过
$emit()
来返回. 接受2个参数, 第一个是index.vue
的监听函数,第二个是参数
methods: {
clickBox() {
console.log('click');
this.$emit('change', this.color)
}
}
- 如上2步,组件中,通过
$emit
把this.color
传给index.vue
的change
事件. 而change
中的e
就是组件中返回来的this.color
④ 组件插槽
<slot></slot>
- 在
index.vue
中,使用<btn color="red" @change="change">主页中的内容</btn>
这样的写法, 希望把文字也插入到组件中. - 在 组价中, 使用插槽
<slot></slot>
的方式来获取内容.
2-10 语法速通-api与条件编译
文档:
https://uniapp.dcloud.io/api/README
① api的一个演示
<script>
export default {
onLoad() {
console.log(11);
uni.getSystemInfo({
success(res) {
console.log("success", res);
},
fail(err) {
console.log("error", err);
},
complete(res) {
console.log("不管成功失败都会返回: ", res);
}
})
},
}
</script>
② 条件编译 用
#ifdef 平台标识符
来让代码在规定的平台里面编译.可以加上||
(或者).
<template>
<view class="content">
<!-- #ifdef H5 || APP-PLUS -->
<button>我是一个按钮</button>
<!-- #endif -->
</view>
</template>
#ifndef
加了一个n
表示在后面的平台上面,不显示.
条件编译写法 | 说明 |
---|---|
#ifdef APP-PLUS 需条件编译的代码 #endif | 仅出现在 App 平台下的代码 |
#ifndef H5 需条件编译的代码 #endif | 除了 H5 平台,其他平台均存在的代码 |
ifdef H5 || MP-WEIXIN 需条件编译的代码 #endif | 在 H5 平台或微信小程序平台存在的代码(这里只有|| , 不可能出现&& , 因为没有交集) |
文档
https://uniapp.dcloud.io/platform?id=%e8%b7%a8%e7%ab%af%e5%85%bc%e5%ae%b9
③ 在
style
中使用 条件编译
<style>
/* #ifdef H5 */
...
/* #endif */
</style>
④ 在
style
中,page
表示这个页面
⑤ 尺寸单位
px
%
rpx
rem
vh
vw
⑥ 引入外部 css 文件
@import '@/static/index.css'
2-11 生命周期概述
文档:
https://uniapp.dcloud.io/collocation/frame/lifecycle
① 生命周期的分类
-
应用生命周期, 在
App.vue
文件中onLaunch
应用初始化完成触发一次,全局只触发一次
onShow
应用启动的时候, 或者从后台进入前台会触发
onHide
应用从前台进入后台触发 -
页面生命周期
onLoad
监听页面加载
onShow
监听页面显示,每次切换到这个页面,都会执行
onReady
监听页面初次渲染完成 (如果渲染速度快, 会在页面进入动画完成前触发)
onHide
监听页面隐藏
onUnload
监听页面卸载,页面关闭 -
组件生命周期
beforeCreate
在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用
created
实例创建完成之后立即调用, 挂载阶段还没有开始
mounted
挂载到实例上去之后调用
destroyed
vue 实例销毁后调用 -
大概顺序
App Launch
>App Show
>component beforeCreate
>component created
>page onload
>page onshow
>component mounted
>page onready
第3章 千里之行,始于足下 (基础配置)
3-1 uni-app 项目配置
① 微信小程序
微信小程序 -> 设置 -> 安全设置 -> 服务端口 -> 开启
HBuilderX -> 配置 -> 运行配置 -> 把微信开发者工具路径 配置 起来
② app真机,模拟器
连接安卓设备
- 打开usb调试
连接ios设备
- 运行 -> 选择 ios 设备
ios 模拟器
- 打开 xcode
- 偏好配置 -> 上面倒数第三个 Components -> 选择版本(例如 IOS 12.4 Simulator) 下载 -> 右下角 安装
- 重启 HBuilderX
- 运行 -> 模拟器
③ h5, 如果直接运行到浏览器失效的话,可以按照下面操作
- 设置 -> 运行配置 -> 浏览器运行配置 把相关浏览器路径配置好
3-2 目录结构概述
components
自定义组件的目录,自己创建的pages
页面存放目录static
静态文件资源目录unpackage
编译后的文件存放目录utils
公用的工具类,自己创建的common
公用的文件, 自己创建的App.vue
项目启动页main.js
应用入口,绑定全局变量,引用一些第三方库等manifest.json
项目配置page.json
页面配置,页面地址,页面名称等 文档:https://uniapp.dcloud.io/collocation/pages
uni.scss
scss配置
3-3 配置项目底部选项卡 - tabbar 配置
pages.json
中的globalStyle
中的配置,是所有页面的通用配置.pages.json
中的pages
下面的配置,可以配置 单独终端(例如 微信小程序,h5,app等)的配置, 如下
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app",
"app-plus":{
},
"mp-weixin":{
},
"h5":{
}
}
}
],
"globalStyle": {
...
}
}
tabbar
文档https://uniapp.dcloud.io/collocation/pages?id=tabbar
, 写法如下,注意图片的路径写法.
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
...
],
"globalStyle": {
...
},
"tabBar": {
"color": "#666",
"selectedColor": "#ff5a5f",
"borderStyle": "black",
"list": [{
"pagePath": "pages/index/index",
"text": "首页",
// 本地图片, 大小 40kb, 尺寸建议 81*81px
"iconPath": "static/home.png",
"selectedIconPath": "static/home-active.png"
}, {
"pagePath": "pages/about/about",
"text": "关于",
// 本地图片, 大小 40kb, 尺寸建议 81*81px
"iconPath": "static/follow.png",
"selectedIconPath": "static/follow-active.png"
}, {
"pagePath": "pages/my/my",
"text": "首页",
// 本地图片, 大小 40kb, 尺寸建议 81*81px
"iconPath": "static/my.png",
"selectedIconPath": "static/my-active.png"
}]
}
}
tabbar
会缓存每个页面,所以onLoad
只会隐藏,不会重新加载.tabbar
中有一个新的生命周期onTabItemTap(e)
, tabbar 点击就会触发, 这个e
返回例如{index: 0, text: "首页", pagePath: "pages/index/index"}
3-4 在uni-app中如何使用sass
- 如果第一次使用,需要安装sass插件
scss/sass编译
- sass 写法
<style lang="scss">
, 支持嵌套
写法,如下例
<style lang="scss">
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
.title {
font-size: 36rpx;
color: #8f8f94;
}
}
}
</style>
- 使用变量
<style lang="scss">
$width: 200rpx;
...
.content{
width: $width;
...
</style>
- 使用父级
&.box
, 不能有空格,要接在一起写.
<style lang="scss">
$width: 200rpx;
.content {
display: flex;
// & 父级
&.box {
border:1px solid red;
}
</style>
第4章 工欲善其事,必先利其器 (uniCloud 的基础用法)
4-1 认识 uniCloud开发
① 云函数 示例如下
'use strict';
exports.main = async (event, context) => {
// event 为客户端上传的参数
console.log('event:' + event)
// 返回数据给客户端
return event
};
② 云数据库 示例如下
'use strict'
//获取数据库引用
const db = uniCloud.database()
const collection = db.collection('user');
// 伪代码
exports.main = async (event, context) => {
//event 为客户端上传的参数
console.log('event: ' + event)
//将token写入数据库
collection.where({
name: event.data.name,
password: event.data.password
})
.update({
token: event.token,
token_time: event.timestamp
});
// 获取用户信息
const user_info = await collection.where({
name: event.data.name
}).get();
// 返回数据给客户端
return {
code: 200,
msg: '登录成功',
data: user_info.data
}
};
③ 云存储和CND
4-2 HBuilderX 中配置 uniCloud 环境
- 新建项目的时候, 勾选下方的,
启动uniCloud(全端可用的云开发,使用js开发服务器逻辑.)
- 之后 绑定我们的云空间
- 点击
manifest.json
文件,查看`uni-app应用标识(AppID), 如果为空要重新获取. - 在
uniCloud
文件夹下新建cloudfunctions
文件夹, 这个文件夹是存放云函数的, 可以点击新建云函数, 之后点击新建的云函数目录, 选择上传部署
. 就可以上传到后台.
4-3 使用 uniCloud web 控制台
- 右击
uniCloud
文件夹, 选择打开 uniCloud Web控制台...
, 就可以进入云开发控制台
4-4 开始使用云函数
①
云函数
运行在云端(服务器端)的函数
'use strict';
exports.main = async (event, context) => {
//event为客户端上传的参数
console.log('event : ', event)
//返回数据给客户端
return event
};
event
为客户端上传的参数content
包含了调用信息和运行状态, 获取每次调用的 上下文
②
uniCloud.callFunction
调用我们的云函数
...
methods: {
open() {
// 执行云函数
uniCloud.callFunction({
name: "get_list", //云函数的名字
data: { // 参数
name: 'Liming',
age: 18
},
success(res) {
console.log(res)
},
fail(err) {
}
})
}
...
③ 在本地的
云函数
中如下return
返回,这个返回就是上面的success(res)
中的res
的值
'use strict';
exports.main = async (event, context) => {
//event为客户端上传的参数
console.log('event : ', event)
//返回数据给客户端
return {
content: '成功'
}
};
④ 注意: 如果修改了 本地的云函数,
一定要上传部署
,不然是不生效的.
4-5 云数据库的添加和删除
云数据库
的添加和删除, 只能在云函数
中运行
① 云函数中如何连接数据库
'use strict';
// 连接到数据库
const db = uniCloud.database()
exports.main = async (event, context) => {
// 连接到 `user` 表
const collection = db.collection('user')
//返回数据给客户端
return {}
};
const collection = db.collection('user')
// 连接到数据库const collection = db.collection('user')
// 连接到user
表
② 添加数据
add
方法
- 添加1条数据
let res = await collection.add({
name: 'uni-app'
})
- 添加多条数据
let res = await collection.add([
{
name: 'vue'
},
{
name: 'html',
type: '前端'
}
])
③ 删除数据
doc
选择数据 然后remove
删除
let res = await collection.doc('60092bbc6cb4fb0001f96b05').remove()
4-6 数据库的更新和查找
① 记录更新
doc
选择数据,update
或者set
2个方法使用是一样的,用来更新数据
//删除数据
let res = await collection.doc('60092bbc6cb4fb0001f96b04').update({
name: 'html'
})
②
update
和set
d的区别
update
只能更新存在的记录.set
如果记录存在就更新, 如果不存在就添加.
③ 查找数据
get
where
- 查询一条数据,
doc
是查询条件,get
获取数据(并不是获取一条数据,是根据条件来获取,没有写条件就是获取全部)
let res = await collection.doc('60091e078976a900010cf515').get()
doc
只能根据id
来查询,where
可以根据条件来查询
let res = await collection.where({
name: 'vue-test'
}).get()
④ 返回数据
//返回数据给客户端
return {
code: 200,
msg: '查询成功',
data: res.data
}
⑤ 如何根据前端提交的参数来获取数据?
event
就是前端提交过来的数据,就可以根据这个数据来做筛选, 如下
let res = await collection.where({
name: event.name
}).get()
4-7 使用云储存上传文件
① 上传文件
uniCloud.uploadFile
methods: {
open() {
// 图片上传apis
uni.chooseImage({
count: 1,
success(res) {
const tempFilePath = res.tempFilePaths[0]
console.log(res)
uniCloud.uploadFile({
filePath: tempFilePath,
cloudPath: res.tempFiles[0].name,
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
}
})
},
fail(err) {
console.log(err);
}
})
}
}
- 注意上面的
cloudPath
-使用阿里云时,cloudPath为云端文件名,请勿使用非法字符
- 文档:
https://uniapp.dcloud.io/uniCloud/storage?id=uploadfile
② 删除文件
uniCloud.deleteFile
阿里云不支持此API,前端运行此API会报权限错误
uniCloud.deleteFile({
fileList:['59a0e604-79b9-4d58-9aae-53cc4eda7db5'],
success(res) {
console.log(res);
},
fail(err) {
console.log(err);
}
})
第5章 扬帆起航,胜利在向你招手 (首页功能模块)
5-1 项目初始化
①
db_init.json初始化数据库
位置:uniCloud
->database
->db_init.json
如果没有就自己新建
文档:https://uniapp.dcloud.io/uniCloud/hellodb?id=db-init
{
"list": { // 集合(表名)
"data": [ // 数据
{
"name": "tom"
},
{
"name": "liming"
}
]
}
}
- 写好之后,右键
初始化云数据库
, 控制台就可以看到这个数据已经同步到后台了 - 项目中, 提供了 这个文件,复制过来,然后重新
初始化云数据库
, 同步到后台.
5-2 自定义导航栏
1 取消原来的导航栏, 选用自定义导航栏
"navigationStyle":"custom",
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/tabbar/index/index",
"style": {
"navigationStyle":"custom",
...
}
}
...
2 把导航栏作为一个新的自定义组件来操作, 在根目录下新建
components
, 然后再新建组件并创建同名目录navbar
3 在需要调用的页面,
import
导入,然后在components
中注册,之后就可以使用了. 但是在uni-app
中有一个简单的方法easyCom
easyCom
是局部引入, 在需要调用的文件中, 直接使用<narbar></narbar>
即可.
<template>
<view class="content">
<!-- 自定义导航栏组件 -->
<navbar></navbar>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello'
}
},
}
</script>
- 如上代码, 并没有导入
navbar
,也没有在components
中注册,但是由于easyCom
的存在, 则可以直接使用.不会报错.
5-3 导航栏适配小程序
① 顶部状态栏高度的动态赋值
<view :style="{height: statusBarHeight+'px;'}"></view>
...
<script>
//获取手机系统信息
const info = uni.getSystemInfoSync();
//设置状态栏高度
this.statusBarHeight = info.statusBarHeight;
</script>
② 适配小程序和h5的解决方案
<template>
<view class="navbar">
<view class="navbar-fixed">
<!-- 状态栏 -->
<view :style="{height: statusBarHeight+'px'}"></view>
<!-- 导航栏内容 -->
<view class="navbar-content" :style="{height:navBarHeight+'px',width:windowWidth+'px'}">
<view class="navbar-search">
<view class="navbar-search_icon">
<uni-icons type="search" size="16" color="#999"></uni-icons>
</view>
<view class="navbar-search_text">uni-app, vue</view>
</view>
</view>
</view>
<view :style="{height: statusBarHeight+navBarHeight+'px'}"></view>
</view>
</template>
<script>
export default {
data() {
return {
statusBarHeight: 20, //顶部状态栏的高度
navBarHeight: 40,
windowWidth: 375
};
},
created() {
//获取手机系统信息
const info = uni.getSystemInfoSync();
//设置状态栏高度
this.statusBarHeight = info.statusBarHeight;
this.windowWidth = info.windowWidth
// h5 app mp-alipay
// #ifndef H5 || APP-PLUS || MP-ALIPAY
//获取微信右上角胶囊的位置
const menuButtonInfo = uni.getMenuButtonBoundingClientRect()
console.log(menuButtonInfo)
// (胶囊底部高度 - 状态栏高度) + (胶囊顶部高度 - 状态栏内的高度) = 导航栏的高度
this.navBarHeight = (menuButtonInfo.bottom - info.statusBarHeight) + (menuButtonInfo.top - info.statusBarHeight)
this.windowWidth = menuButtonInfo.left
// #endif
}
}
</script>
<style lang="scss">
.navbar {
.navbar-fixed {
position: fixed;
top: 0;
left: 0;
z-index: 99;
width: 100%;
background-color: $mk-base-color;
.navbar-content {
display: flex;
justify-content: center;
align-items: center;
height: 45px;
padding: 0 15px;
box-sizing: border-box;
.navbar-search {
display: flex;
align-items: center;
padding: 0 10px;
width: 100%;
height: 30px;
border-radius: 30px;
background-color: #fff;
.navbar-search_icon {
margin-right: 10px;
}
.navbar-search_text {
font-size: 12px;
color: #999;
}
}
}
}
}
</style>
- 小程序
- h5
5-4 使用字体图标
插件市场搜索
icons
然后下载到本地
-
引用
<uni-icons type="search" size="16" color="#999"></uni-icons>
-
文档:
https://hellouniapp.dcloud.net.cn/pages/extUI/icons/icons
5-5 选项卡展示
- 选项卡还是以
组件
的形式来写 scss
伪类写法
.tab-icons{
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 45px;
&::after{
content: '';
position: absolute;
top: 12px;
bottom: 12px;
left: 0;
width: 1px;
background-color: #ddd;
}
5-6 选项卡数据初始化
- 调用云函数, 且获取到返回信息. 注意这里的
const { result } = res;
和.then() 写法
methods: {
getLabel() {
// 调用云函数
uniCloud.callFunction({
name: 'get_label',
}).then(res => {
const { result } = res;
this.tabList = result.data;
})
}
}
- 常用写法
// 调用云函数
uniCloud.callFunction({
name: 'get_label',
success(res) {
console.log(res.result);
},
fail(err) {
}
});
大概步骤
- 在
父页面
中, 获取数据, 然后赋给组件<tab :list="tabList"></tab>
- 在
组件
中, 通过props
注册一下. 这里是Array类型
<script>
export default {
props: {
list: {
type: Array,
default () {
return []
}
}
},
...
- 之后,
组件
中就可以使用list
来做操作了.
5-7 封装数据请求
- 在根目录的
common
文件夹下面新建api
文件夹,再新建index.js
, 在其中封装一个 Promise(), 如下
const get_label = (data) => {
return new Promise((reslove, reject) => {
uniCloud.callFunction({
name: 'get_label',
data
}).then(res => {
if (res.code === 200) {
// .then
reslove(res.result);
} else {
// catch
reject(res.result);
}
}).catch(err => {
reject(err);
})
})
}
export default {
get_label
}
- 在
main.js
中,import
然后再赋值, 如下
import api from './common/api'
...
Vue.prototype.$api = api
- 在需要的地方调用这个方法
methods: {
getLabel() {
this.$api.get_label({
name: 'get_label'
}).then(res => {
const {
data
} = res;
this.tabList = data;
})
}
}
- 优化代码, 在
common/api
下新建list.js
文件, 把之前的 代码拷贝进入, 再export
出来
export const get_label = (data) => {
return new Promise((reslove, reject) => {
uniCloud.callFunction({
name: 'get_label',
data
}).then(res => {
if (res.result.code === 200) {
// .then
reslove(res.result);
} else {
// catch
reject(res.result);
}
}).catch(err => {
reject(err);
})
})
}
- 在
common/api
的index.js
中,修改代码如下, 这样每个代码分开管理.
import {get_label} from './list.js'
export default {
get_label
}
- 继续优化, 因为每个请求都要写
return Promise..
, 可以在common
文件夹下面 新进一个http.js
export default function $http(options) {
const {
url,
data
} = options
return new Promise((reslove, reject) => {
uniCloud.callFunction({
name: url,
data
}).then(res => {
if (res.result.code === 200) {
// .then
reslove(res.result);
} else {
// catch
reject(res.result);
}
}).catch(err => {
reject(err);
})
})
}
- 将
common/api/list.js
中的内容优化如下
import $http from '../http.js'
export const get_label = (data) => {
return $http({
url: 'get_label',
data
})
}
export const get_list = (data) => {
return $http({
url: 'get_list',
data
})
}
- 如上,
list.js
优化完毕, 但是在其中每加入一个函数,都需要在index.js
中导入,然后注册. 如下,可以利用require.context
方法,实现自动导入, 之后, 在list.js
中写入新的函数,就不需要在index.js
中注册了, 已经自动帮我们注册了.之后直接写函数,直接调用即可.
//批量导出文件
const requireApi = require.context(
// api 目录的相对路径
'.',
//是否查询子目录
false,
//查询文件的一个后缀
/.js$/
)
let module = {};
requireApi.keys().forEach((key, index) => {
if (key === './index.js') return;
console.log(key);
Object.assign(module, requireApi(key));
})
export default module
5-8 选项卡切换
-
加一个点击事件,绑定一个class
:class="{active:activeIndex === index}" @click="clickTab(item,index)"
-
把事件返回给 父页面
methods: {
// 点击导航栏高亮
clickTab(item, index) {
this.activeIndex = index
//把事件传递到父页面去
this.$emit('tab', {
data: item,
index: index
})
}
}
- <tab :list=“tabList” @tab=“tab”> 绑定事件
tab
是自定义事件 - 在
methods
里面注册一下
tab(data, index) {
console.log(data, index);
}
5-9 基础卡片视图实现
- 以组件的新式来实现,
5-10 更多卡片视图实现
- 在组件中,分别写3种模式(列表,大图,多图).
- 以插槽的形式,让父页面绝对调用谁
5-11 实现内容切换
- 拆分组件,例如
list
组件, 如下
<swiper-item>
<list-scroll class="list-scroll">
<list-card mode="base"></list-card>
<list-card mode="image"></list-card>
<list-card mode="column"></list-card>
<list-card mode="column"></list-card>
<list-card mode="column"></list-card>
<list-card mode="column"></list-card>
</list-scroll>
</swiper-item>
- 在同级目录新建
list-item.vue
, 把中间的部分拿出来
<template>
<list-scroll class="list-scroll">
<list-card mode="base"></list-card>
<list-card mode="image"></list-card>
<list-card mode="column"></list-card>
<list-card mode="column"></list-card>
<list-card mode="column"></list-card>
<list-card mode="column"></list-card>
</list-scroll>
</template>
<script>
</script>
<style lang="scss">
.list-scroll {
height: 100%;
}
</style>
- 因为这个不符合
easyCom
的规范,所以需要重新导入,然后注册一下.如下
<template>
<swiper class="home-swiper">
<swiper-item>
<list-item></list-item>
</swiper-item>
</swiper>
</template>
<script>
import listItem from './list-item.vue'
export default {
components: {
listItem
},
...
5-12 选项卡与内容联动
5-13 内容卡片数据初始化
5-14 切换选项卡懒加载数据
- 懒加载: 之前是每个页面刷新的时候, 获取数据,直接改变
data
的值,这样就会出现闪烁的情况. 解决方法,就是将每一页的数据,放到缓存数据中,例如数组中. 把每一页的数据加到这个数组中,则可以保证,加载的时候,不会出现闪烁的情况.
5-15 -1 上拉加载更多(上)
加载插件 loadmore
-LoadMore 加载更多 DCloudDCloud出品
- 文档:
https://ext.dcloud.net.cn/plugin?id=29
5-16 -2 上拉加载更多(下)
- 数据更新的操作
this.$set()
this.$forceUpdate()
this.$set(this.load, current, oldLoad)
// 强制渲染
this.$forceUpdate()
5-17 -1 收藏按钮实现(上 )
- 阻止事件冒泡
@click.native.stop
5-18 -2 收藏按钮实现(下)
- 加载框
uni.showLoading()
- 关闭加载框
uni.showLoading()
- 提示信息
uni.showToast({
title: this.like ? '收藏成功' : '取消收藏',
icon: 'none'
})
第6章 做事是否快捷,不在一时奋发,而在能否持久(搜索页功能模块)
6-1 搜索页导航栏修改
- 跳转事件
uni.navigateTo({
url: '/pages/home-search/home-search'
})
6-2 使用vuex 管理历史记录
- 在目录下新建一个目录
store
- 在
store
目录下,新建index.js
- 输入下面代码
// vuex 状态管理
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state:{
}
})
export default store
- 在
main.js
中注册一下
...
import store from './store'
...
const app = new Vue({
store,
...App
})
6-3 -1 搜索逻辑实现(上 )
6-4 -2 搜索逻辑实现(下)
- 加载组件
uni-load-more
<uni-load-more v-if="loading" status="loading" iconType="snow"></uni-load-more>
// 执行
this.loading = true
//关闭
this.loading = false
6-5 搜索历史数据持久化
- 返回上一页
uni.navigateBack()
- 回到 tab 页
uni.switchTab({
url: '/pages/tabbar/index/index'
})
vuex
并不是持久化的存储,这里改造下vuex
中的代码
historyLists: uni.getStorageSync("__hostory")
获取本地缓存uni.setStorageSync('__hostory', list)
设置本地缓存uni.removeStorageSync('__hostory')
清空本地缓存
// vuex 状态管理
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
// 数据源
state: {
historyLists: uni.getStorageSync("__hostory") || []
},
//可以改变数据源中的数据
mutations: {
SET_HISTORY_LISTS(state, history) {
state.historyLists = history
},
CLEAR_HISTORY(state, history){
state.historyLists = []
}
},
actions: {
set_history({commit, state}, history) {
let list = state.historyLists
list.unshift(history)
uni.setStorageSync('__hostory', list)
commit('SET_HISTORY_LISTS', list)
},
clear_history({commit}){
uni.removeStorageSync('__hostory')
commit('CLEAR_HISTORY')
}
}
})
export default store
第7章 锲而不舍,金石可镂(标签页功能模块)
7-1 标签管理页布局样式
7-2 标签页数据处理
7-3 编辑标签页
7-4 保存标签页数据
7-5 使用自定义事件同步数据
- 我们在内页修改了数据,之后返回上一页,此时,上一页数据应该要变化,但是实际上没有变化.
- 此时我们需要在内容修改数据之后, 要通知上一页帮我们主动会重新渲染. 此时需要用到自定义事件,
uni.$emit
- 自定义事件,只能在打开的页面触发
步骤
- 在内容调用
uni.$emit('事件名', 参数[可省略])
- 在调用页面的
onLoad()
中监听uni.$on('事件名', 参数[可省略])
uni.$on('labelChange', (res) => {
this.getLabel();
})
- 在内容页中, 在
onLoad
中接受, 再将字符串通过JSON.parse(str)
解析成对象
onLoad(query) {
console.log(JSON.parse(query.params));
},
第8章 坚持就是胜利,坚持才会有所成就(详情页功能模块)
8-1 详情页页面展示
8-2 内容预加载
- 当加载内容页面的时候,如果所有的数据都要立刻请求,再加载,则有一个空白的时候.
- 如上,我们可以在列表中,将一些数据带过去.达到
预加载
的效果.
步骤
- 在列表页中, 通过url的参数传过去,注意参数的个数, 这里是把 对象通过
JSON.stringify(object)
转为 字符串
const params = {
_id: item._id,
title: item.title,
create_time: item.create_time,
thumbs_up_count: item.thumbs_up_count,
borwse_count: item.borwse_count
}
//...
uni.na
vigateTo({
url: '/pages/home-detail/home-detail?params=' + JSON.stringify(params)
})
8-3 详情页面数据初始化
8-4 富文本渲染
- 组件
gaoyia-parse
,拷贝到本地的components
目录下,注意:这个插件需要手动导入并注册!
import uParse from '@/components/gaoyia-parse/parse.vue'
<script>
import uParse from '@/components/gaoyia-parse/parse.vue'
export default {
components: {
uParse
},
//...
- 使用
<u-parse :content="fromData.content" :noData="noData"></u-parse>
- 为了美观, 在
App.vue
中引入css
<style>
/*每个页面公共css */
@import 'components/gaoyia-parse/parse.css';
</style>
8-5 发布窗口展示
- 弹窗的插件
uni-popup
, 下载下来,还有一个依赖的组件uni-transition
- 文档:
https://ext.dcloud.net.cn/plugin?id=329
使用步骤
- 编辑如下
<uni-popup ref="popup" type="bottom">
<view class="popup-wrap">
弹出层
</view>
</uni-popup>
-
打开
this.$refs.popup.open()
注意, 如果一进入页面就要打开, 这个事件不可以直接放在onLoad
中,因为onLoad
时候,这个组件还没有渲染, 可以放在onReady
中. -
控制点击后面蒙版是否关闭弹窗
:maskClick="false"
<uni-popup ref="popup" type="bottom" :maskClick="false">
8-6 评论内容实现(1)
8-7 评论内容实现(2)
8-8 评论内容实现(3)
8-9 评论内容实现(4)
- 递归组件,自己调动自己
步骤
- 先
import
导入自身import commentsBox from '@/components/comments-box/comments-box.vue'
- 后 注册一下
<script>
import commentsBox from '@/components/comments-box/comments-box.vue'
export default {
name: "comments-box",
components: {
commentsBox
},
//...
- 如上, 递归组件, 需要加上
name
例如name: "comments-box",
8-10 评论内容实现 (5)
8-11 评论内容实现(6)
8-11 评论内容实现(6)
8-12 关注作者(上)
8-13 关注作者(下)
8-14 文章的收藏与点赞(上)
8-15 文章的收藏和点赞(下)
8-16 评论列表(上)
- 页面上拉触底事件的处理函数,页面的生命周期函数
onReachBottom
8-16 评论列表(上)
8-17 评论列表(下)
- 自己建立一个工具类
- 在根目录下建一个
utils
的文件夹, 然后新建一个index.js
文件 - 在
index.js
中输入
// 时间格式化
export const parseTime = (time) => {
console.log(time)
}
- 在引用的页面中,导入这个文件
import {parseTime} from '@/utils/index.js'
- 在页面中加一个过滤器
filters
filters: {
formatTime(time) {
return parseTime(time)
}
},
- 在页面中使用
<view>{{comments.create_time | formatTime}}</view>
过滤器使用, 如上
comments.create_time
是formatTime
的一个参数, 如果formatTime
有多个参数, 直接在后面写上,
例如<view>{{comments.create_time | formatTime(a, b)}}</view>
, 这里的a
是第二个参数,b
是第三个参数.
第9章 关注页功能模块
9-1 关注页导航栏实现
9-2 收藏文章内容实现
9-3 收藏与首页内容关联
9-4 关注作者页面实现
9-5 同步关注作者页面数据
第10章 个人信息页功能模块
10-1 个人中心页面实现
10-2 个人中心数据处理
- 使用
vuex
的记录
- 添加一个
userinfo
用来保存用户信息的记录. - 在
store/index.js
中, 添加如下代码
// vuex 状态管理
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
// 数据源
state: {
userinfo: uni.getStorageSync('USERINFO') || {}
},
//可以改变数据源中的数据
mutations: {
SET_USER_INFO(state, userinfo) {
state.userinfo = userinfo
}
},
actions: {
set_userinfo({commit}, userinfo) {
uni.setStorageSync('USERINFO', userinfo)
commit('SET_USER_INFO', userinfo)
}
}
})
export default store
- 在
app.vue
中调用,一开始获取到用户的信息,然后赋值给vuex
onLaunch: function() {
console.log('App Launch')
this.$api.get_user({
user_id: '600966e08976a900010da78d'
}).then(res => {
const {
data
} = res
this.$store.dispatch('set_userinfo', data)
})
},
- 在其他页面中,调用这个用户信息, 先导入, 后面在
computed
中声明, 在其他地方就可以调用this.userinfo
了, 如下
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
}
},
computed: {
...mapState(['userinfo'])
},
onLoad() {
console.log(this.userinfo);
},
methods: {
}
}
</script>
- 之前,我们在
common/https.js
中用一个默认的id来获取数据,现在有了vuex
中的数据, 则需要将https.js
中的代码修改一下,如下, 先导入, 再使用
import store from '../store/index.js'
export default function $http(options) {
const {
url,
data
} = options
const dataObj = {
user_id: store.state.userinfo._id,
...data
}
//...
10-3 我的文章实现
10-4 问题反馈页面实现
10-5 反馈图片选择
10-6 上传图片
第11章 积少成多,走向完善(项目优化与平台兼容)
11-1 微信小程序优化与兼容
描述: 在
app.vue
中,异步
获取到了用户信息,然后放到vuex
中,但是加载到首页的时候,在首页的onload
中,我们就已经用到了用户信息,但是由于异步
, 数据此时可能还没有传递过来.这个问题,要用过监听vuex
中的userinfo
, 让首页在识别到的情况下,在做操作.
步骤
- 在首页 引入
vuex
import { mapState } from 'vuex'
- 在页面中的
computed
中注册一下
computed: {
...mapState(['userinfo'])
},
- 监听一下
useinfo
的变化, 如果发生了变化,说明已经获取到了,没有变化则没有获取到
watch: {
userinfo(newVal) {
// 获取 tab 导航栏的信息
this.getLabel();
}
},