大纲目录

      • 第1章 课程导学
      • 第2章 倘想达到最高处,就要从低处开始 (uni-app 基础)
        • 2-3 uni-app 核心知识点概览
        • 2-4 搭建uni-app开发环境
        • 2-5 语法速通-模板语法与数据绑定
        • 2-6 语法速通-条件判断
        • 2-7 语法速通-列表渲染
        • 2-8 语法速通-基础组件
        • 2-9 语法速通-自定义组件
        • 2-10 语法速通-api与条件编译
        • 2-11 生命周期概述
      • 第3章 千里之行,始于足下 (基础配置)
        • 3-1 uni-app 项目配置
        • 3-2 目录结构概述
        • 3-3 配置项目底部选项卡 – tabbar 配置
        • 3-4 在uni-app中如何使用sass
      • 第4章 工欲善其事,必先利其器 (uniCloud 的基础用法)
        • 4-1 认识 uniCloud开发
        • 4-2 HBuilderX 中配置 uniCloud 环境
        • 4-3 使用 uniCloud web 控制台
        • 4-4 开始使用云函数
        • 4-5 云数据库的添加和删除
        • 4-6 数据库的更新和查找
        • 4-7 使用云储存上传文件
      • 第5章 扬帆起航,胜利在向你招手 (首页功能模块)
        • 5-1 项目初始化
        • 5-2 自定义导航栏
        • 5-3 导航栏适配小程序
        • 5-4 使用字体图标
        • 5-5 选项卡展示
        • 5-6 选项卡数据初始化
        • 5-7 封装数据请求
        • 5-8 选项卡切换
        • 5-9 基础卡片视图实现
        • 5-10 更多卡片视图实现
        • 5-11 实现内容切换
        • 5-12 选项卡与内容联动
        • 5-13 内容卡片数据初始化
        • 5-14 切换选项卡懒加载数据
        • 5-15 -1 上拉加载更多(上)
        • 5-16 -2 上拉加载更多(下)
        • 5-17 -1 收藏按钮实现(上 )
        • 5-18 -2 收藏按钮实现(下)
      • 第6章 做事是否快捷,不在一时奋发,而在能否持久(搜索页功能模块)
        • 6-1 搜索页导航栏修改
        • 6-2 使用vuex 管理历史记录
        • 6-3 -1 搜索逻辑实现(上 )
        • 6-4 -2 搜索逻辑实现(下)
        • 6-5 搜索历史数据持久化
      • 第7章 锲而不舍,金石可镂(标签页功能模块)
        • 7-1 标签管理页布局样式
        • 7-2 标签页数据处理
        • 7-3 编辑标签页
        • 7-4 保存标签页数据
        • 7-5 使用自定义事件同步数据
      • 第8章 坚持就是胜利,坚持才会有所成就(详情页功能模块)
        • 8-1 详情页页面展示
        • 8-2 内容预加载
        • 8-3 详情页面数据初始化
        • 8-4 富文本渲染
        • 8-5 发布窗口展示
        • 8-6 评论内容实现(1)
        • 8-7 评论内容实现(2)
        • 8-8 评论内容实现(3)
        • 8-9 评论内容实现(4)
        • 8-10 评论内容实现 (5)
        • 8-11 评论内容实现(6)
        • 8-11 评论内容实现(6)
        • 8-12 关注作者(上)
        • 8-13 关注作者(下)
        • 8-14 文章的收藏与点赞(上)
        • 8-15 文章的收藏和点赞(下)
        • 8-16 评论列表(上)
        • 8-16 评论列表(上)
        • 8-17 评论列表(下)
      • 第9章 关注页功能模块
        • 9-1 关注页导航栏实现
        • 9-2 收藏文章内容实现
        • 9-3 收藏与首页内容关联
        • 9-4 关注作者页面实现
        • 9-5 同步关注作者页面数据
      • 第10章 个人信息页功能模块
        • 10-1 个人中心页面实现
        • 10-2 个人中心数据处理
        • 10-3 我的文章实现
        • 10-4 问题反馈页面实现
        • 10-5 反馈图片选择
        • 10-6 上传图片
      • 第11章 积少成多,走向完善(项目优化与平台兼容)
        • 11-1 微信小程序优化与兼容
        • 11-2 支付宝小程序优化与兼容
        • 11-3 其他平台优化与兼容
      • 第12章 最后的冲刺,成功就在眼前(项目发行与打包)
        • 12-1 h5端发行打包
        • 12-2 小程序端发行上传
        • 12-3 App 端发行打包
      • 第13章 常常是最后一把钥匙打开了门(课程总结与展望)
        • 13-1 课程回顾与总结

第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 的方式运行项目

  1. 检查 node.js 版本 node -v # v14.15.4
  2. npm install -g cnpm --registry=https://registry.npm.taobao.org 设置镜像然后再 npm install -g @vue/cli 装到全局的环境中
  3. 自己出现了报错的情况,需要先运行 sudo chown -R $USER /usr/local 之后再安装就好了.
  4. 检查 vue-cli 是否安装 vue --version # 2.9.6
  5. 进入目录 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 绑定变量, 为false0的时候,不渲染

<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>

③ 页面上拿到组件中的返回值

  1. index.vue 中注册监听事件
<btn color="red" @change="change"></btn>
...
methods: {change(e) {console.log("我是页面的事件,我返回了" + e);}
}
  1. 组件中,通过 $emit() 来返回. 接受2个参数, 第一个是 index.vue的监听函数,第二个是参数
methods: {clickBox() {console.log('click');this.$emit('change', this.color)}
}
  1. 如上2步,组件中,通过 $emitthis.color 传给 index.vuechange 事件. 而 change 中的 e 就是组件中返回来的 this.color

④ 组件插槽 <slot></slot>

  1. index.vue 中,使用 <btn color="red" @change="change">主页中的内容</btn> 这样的写法, 希望把文字也插入到组件中.
  2. 在 组价中, 使用插槽 <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

① 生命周期的分类

  1. 应用生命周期, 在 App.vue 文件中

    onLaunch 应用初始化完成触发一次, 全局只触发一次
    onShow 应用启动的时候, 或者从后台进入前台会触发
    onHide 应用从前台进入后台触发

  2. 页面生命周期

    onLoad 监听页面加载
    onShow 监听页面显示,每次切换到这个页面,都会执行
    onReady 监听页面初次渲染完成 (如果渲染速度快, 会在页面进入动画完成前触发)
    onHide 监听页面隐藏
    onUnload 监听页面卸载,页面关闭

  3. 组件生命周期

    beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用
    created 实例创建完成之后立即调用, 挂载阶段还没有开始
    mounted 挂载到实例上去之后调用
    destroyed vue 实例销毁后调用

  4. 大概顺序 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开发

uni-app笔记-编程知识网

① 云函数 示例如下

'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'
})

updateset 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() {// 图片上传apisuni.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>
  • 小程序
    uni-app笔记-编程知识网
  • h5
    uni-app笔记-编程知识网

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) {}
});

大概步骤

  1. 父页面中, 获取数据, 然后赋给组件 <tab :list="tabList"></tab>
  2. 组件中, 通过 props 注册一下. 这里是 Array类型
<script>export default {props: {list: {type: Array,default () {return []}}},
...
  1. 之后, 组件 中就可以使用 list来做操作了.

5-7 封装数据请求

  1. 在根目录的 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) {// .thenreslove(res.result);} else {// catchreject(res.result);}}).catch(err => {reject(err);})})}export default {get_label
}
  1. main.js 中, import 然后再赋值, 如下
import api from './common/api'
...
Vue.prototype.$api = api
  1. 在需要的地方调用这个方法
methods: {getLabel() {this.$api.get_label({name: 'get_label'}).then(res => {const {data} = res;this.tabList = data;})}
}
  1. 优化代码, 在 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) {// .thenreslove(res.result);} else {// catchreject(res.result);}}).catch(err => {reject(err);})})}
  1. common/apiindex.js 中,修改代码如下, 这样每个代码分开管理.
import {get_label} from './list.js'export default {get_label
}
  1. 继续优化, 因为每个请求都要写 return Promise.., 可以在 common 文件夹下面 新进一个 http.js
export default function $http(options) {const {url,data} = optionsreturn new Promise((reslove, reject) => {uniCloud.callFunction({name: url,data}).then(res => {if (res.result.code === 200) {// .thenreslove(res.result);} else {// catchreject(res.result);}}).catch(err => {reject(err);})})
}
  1. 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})
}
  1. 如上, 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 选项卡切换

  1. 加一个点击事件,绑定一个class :class="{active:activeIndex === index}" @click="clickTab(item,index)"

  2. 把事件返回给 父页面

methods: {// 点击导航栏高亮clickTab(item, index) {this.activeIndex = index//把事件传递到父页面去this.$emit('tab', {data: item,index: index})}
}
  1. <tab :list=“tabList” @tab=“tab”> 绑定事件 tab是自定义事件
  2. methods里面注册一下
tab(data, index) {console.log(data, index);
}

5-9 基础卡片视图实现

  • 以组件的新式来实现,

5-10 更多卡片视图实现

  • 在组件中,分别写3种模式(列表,大图,多图).
  • 以插槽的形式,让父页面绝对调用谁

5-11 实现内容切换

  1. 拆分组件,例如 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>
  1. 在同级目录新建 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>
  1. 因为这个不符合 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 上拉加载更多(上)

  • 加载插件 loadmoreLoadMore 加载更多 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 搜索页导航栏修改

  1. 跳转事件
uni.navigateTo({url: '/pages/home-search/home-search'
})

6-2 使用vuex 管理历史记录

  1. 在目录下新建一个目录 store
  2. store 目录下,新建 index.js
  3. 输入下面代码
// vuex 状态管理
import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)const store = new Vuex.Store({state:{}
})export default store
  1. 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 搜索历史数据持久化

  1. 返回上一页 uni.navigateBack()
  2. 回到 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.historyListslist.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
  • 自定义事件,只能在打开的页面触发

步骤

  1. 在内容调用 uni.$emit('事件名', 参数[可省略])
  2. 在调用页面的 onLoad()中监听 uni.$on('事件名', 参数[可省略])
uni.$on('labelChange', (res) => {this.getLabel();
})
  1. 在内容页中, 在onLoad中接受, 再将字符串通过 JSON.parse(str) 解析成对象
onLoad(query) {console.log(JSON.parse(query.params));
},

第8章 坚持就是胜利,坚持才会有所成就(详情页功能模块)

8-1 详情页页面展示

8-2 内容预加载

  • 当加载内容页面的时候,如果所有的数据都要立刻请求,再加载,则有一个空白的时候.
  • 如上,我们可以在列表中,将一些数据带过去.达到预加载的效果.

步骤

  1. 在列表页中, 通过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 目录下, 注意:这个插件需要手动导入并注册!
  1. import uParse from '@/components/gaoyia-parse/parse.vue'
<script>import uParse from '@/components/gaoyia-parse/parse.vue'export default {components: {uParse},
//...
  1. 使用 <u-parse :content="fromData.content" :noData="noData"></u-parse>
  2. 为了美观, 在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

使用步骤

  1. 编辑如下
<uni-popup ref="popup" type="bottom"><view class="popup-wrap">弹出层</view>
</uni-popup>
  1. 打开 this.$refs.popup.open() 注意, 如果一进入页面就要打开, 这个事件不可以直接放在 onLoad 中,因为 onLoad时候,这个组件还没有渲染, 可以放在 onReady 中.

  2. 控制点击后面蒙版是否关闭弹窗 :maskClick="false"

<uni-popup ref="popup" type="bottom" :maskClick="false">

8-6 评论内容实现(1)

8-7 评论内容实现(2)

8-8 评论内容实现(3)

8-9 评论内容实现(4)

  • 递归组件,自己调动自己

步骤

  1. import 导入自身 import commentsBox from '@/components/comments-box/comments-box.vue'
  2. 后 注册一下
<script>import commentsBox from '@/components/comments-box/comments-box.vue'export default {name: "comments-box",components: {commentsBox},
//...
  1. 如上, 递归组件, 需要加上 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 评论列表(下)

  • 自己建立一个工具类
  1. 在根目录下建一个 utils 的文件夹, 然后新建一个 index.js 文件
  2. index.js 中输入
// 时间格式化
export const parseTime = (time) => {console.log(time)
}
  1. 在引用的页面中,导入这个文件
import {parseTime} from '@/utils/index.js'
  1. 在页面中加一个过滤器 filters
filters: {formatTime(time) {return parseTime(time)}
},
  1. 在页面中使用 <view>{{comments.create_time | formatTime}}</view>

过滤器使用, 如上 comments.create_timeformatTime 的一个参数, 如果 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 的记录
  1. 添加一个 userinfo 用来保存用户信息的记录.
  2. 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
  1. app.vue 中调用,一开始获取到用户的信息,然后赋值给 vuex
onLaunch: function() {console.log('App Launch')this.$api.get_user({user_id: '600966e08976a900010da78d'}).then(res => {const {data} = resthis.$store.dispatch('set_userinfo', data)})
},
  1. 在其他页面中,调用这个用户信息, 先导入, 后面在 computed 中声明, 在其他地方就可以调用 this.userinfo了, 如下
<script>import { mapState } from 'vuex'export default {data() {return {}},computed: {...mapState(['userinfo'])},onLoad() {console.log(this.userinfo);},methods: {}}
</script>
  1. 之前,我们在 common/https.js 中用一个默认的id来获取数据,现在有了 vuex 中的数据, 则需要将 https.js 中的代码修改一下,如下, 先导入, 再使用
import store from '../store/index.js'
export default function $http(options) {const {url,data} = optionsconst 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 , 让首页在识别到的情况下,在做操作.

步骤

  1. 在首页 引入 vuex
import { mapState } from 'vuex'
  1. 在页面中的 computed 中注册一下
computed: {...mapState(['userinfo'])
},
  1. 监听一下useinfo的变化, 如果发生了变化,说明已经获取到了,没有变化则没有获取到
watch: {userinfo(newVal) {// 获取 tab 导航栏的信息this.getLabel();}
},

11-2 支付宝小程序优化与兼容

11-3 其他平台优化与兼容


第12章 最后的冲刺,成功就在眼前(项目发行与打包)

12-1 h5端发行打包

12-2 小程序端发行上传

12-3 App 端发行打包


第13章 常常是最后一把钥匙打开了门(课程总结与展望)

13-1 课程回顾与总结