uni-app 是一个使用 Vue.js 开发所有前端应用的框架。开发者编写一套代码,可发布到 iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台。
详细的 uni-app 官方文档,请翻阅 https://uniapp.dcloud.net.cn/
uni-app 官方推荐使用 HBuilderX 来开发 uni-app 类型的项目。主要好处:
当然,你依然可以根据自己的喜好,选择使用 VS Code、Sublime、记事本… 等自己喜欢的编辑器!
访问 HBuilderX 的官网首页 https://www.dcloud.io/hbuilderx.html
点击首页的 DOWNLOAD 按钮
选择下载 正式版 -> App 开发版
将下载的 zip包 进行解压缩
将解压之后的文件夹,存放到纯英文的目录中(且不能包含括号等特殊字符)
双击 HBuilderX.exe 即可启动 HBuilderX
为了方便编写样式(例如:<style lang=“scss”></style>),建议安装 scss/sass 编译 插件。插件下载地址:
https://ext.dcloud.net.cn/plugin?name=compile-node-sass
操作步骤:工具 -> 预设快捷键方案切换 -> VS Code
{ “editor.caretWidth” : 1, “editor.colorScheme” : “Default”, “editor.wordWrap” : true, “explorer.autoReveal” : true, “explorer.iconTheme” : “hx-file-icons-colorful”, “workbench.colorCustomizations” : { “[Default]” : { “sideBar.background” : “#FDF0ED”, //加深项目管理器颜色 “editor.background” : “#FDF0ED”, //加深编辑区域背景颜色 “toolBar.background” : “#FDF0ED”, “toolBar.hoverBackground” : “#fce5de”, “toolBar.border” : “#fad3c7”, “tab.activeBackground” : “#fad3c7”, //选中时的背景颜色 “tab.activeForeground” : “#242227”, //选中时的前景颜色 “tab.inactiveBackground” : “#FDF0ED”, //未选中时的背景颜色 “tab.inactiveForeground” : “#938D8B”, //未选中时的前景颜色 “tab.hoverBackground” : “#FCE7E2”, “tab.unfocusedHoverBackground” : “#FDF0ED”, //未选中分栏里鼠标滑过未选中标签的背景颜色 “tab.unfocusedActiveForeground” : “#242227”, //未选中分栏里选中标签的前景颜色 “tab.unfocusedInActiveForeground” : “#FDF0ED”, //未选中分栏里选中标签的前景颜色 “editorSuggestWidget.selectedBackground” : “#fad3c7”, //助手弹窗选中条目时背景颜色 “editorSuggestWidget.background” : “#fce5de”, //助手弹窗背景颜色 “editorSuggestWidget.border” : “#fadad1”, //助手弹窗边框颜色 “input.background” : “#fad3c7”, //文本框背景颜色 “inputValidation.infoBackground” : “#FDF0ED”, //下拉框背景颜色 “inputList.hoverBackground” : “#fce5de”, //鼠标滑过item背景颜色 “inputList.border” : “#fce5de”, //下拉框边框颜色 “list.foreground” : “#938D8B”, //前景颜色 “list.highlightForeground” : “#E42E5B”, //高亮时前景颜色 “list.activeSelectionBackground” : “#fad3c7”, //选中条目背景颜色 “list.activeSelectionForeground” : “#E42E5B”, //选中条目前景颜色 “list.hoverBackground” : “#fce5de”, //鼠标滑过背景颜色 “outlineBackground” : “#FDF0ED”, //文档结构背景颜色 “scrollbarSlider.background” : “#fad3c7”, //滚动条背景颜色 “scrollbarSlider.hoverBackground” : “#E42E5B”, //鼠标滑过滚动条背景颜色 “extensionButton.prominentBackground” : “#FDF0ED”, //背景颜色 “extensionButton.prominentForeground” : “#E42E5B”, //前景颜色 “extensionButton.border” : “#fad3c7”, // 边框颜色 “extensionButton.prominentHoverBackground” : “#fad3c7”, //鼠标滑过时的背景颜色 “extensionButton.checkColor” : “#E42E5B”, //选中时的前景颜色 “settings.textInputBorder” : “#fadad1”, //文本框边框颜色 “inputOption.activeBorder” : “#E42E5B”, //文本框有焦点时边框颜色 “settings.dropdownBorder” : “#fadad1”, // combobox下拉列表边框颜色 “settings.dropdownListBorder” : “#fadad1”, //combobox item边框颜色 “imageview.background” : “#FDF0ED”, //浅色方格颜色 “imageview.foreground” : “#fadad1”, //深色方格颜色 “statusBar.background” : “#FDF0ED”, //状态栏背景颜色 “statusBar.foreground” : “#938D8B”, //状态栏前景颜色 “minimap.handle.background” : “#fadad1” //迷你地图滑块背景 } } }
1. 文件 -> 新建 -> 项目
2. 填写项目的基本信息
3. 目录结构
一个 uni-app 项目,默认包含如下目录及文件:
┌─components uni-app组件目录 │ └─comp-a.vue 可复用的a组件 ├─pages 业务页面文件存放的目录 │ ├─index │ │ └─index.vue index页面 │ └─list │ └─list.vue list页面 ├─static 存放应用引用静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此 ├─main.js Vue初始化入口文件 ├─App.vue 应用配置,用来配置小程序的全局样式、生命周期函数等 ├─manifest.json 配置应用名称、appid、logo、版本等打包信息 └─pages.json 配置页面路径、页面窗口样式、tabBar、navigationBar 等页面类信息
1. 填写自己的微信小程序的 AppID:
2. 在 HBuilderX 中,配置“微信开发者工具”的安装路径:
3. 在微信开发者工具中,通过 设置 -> 安全设置 面板,:
4. 在 HBuilderX 中,点击菜单栏中的 运行 -> 运行到小程序模拟器 -> 微信开发者工具(选择第一个,也就是不是运行到页面的那个),将当前 uni-app 项目编译之后,自动运行到微信开发者工具中,从而方便查看项目效果与调试:
5. 初次运行后,可以设置 mainfest.json 文件里面的 源码视图, 代码的最下面有个 setting ,可以在里面添加代码 取消页面上的警告信息
"mp-weixin" : { /* 小程序特有相关 */ "appid" : "wx23419c45073f448f", "setting" : { "urlCheck" : false, "checkSiteMap": false // 这个代码 }, "usingComponents" : true }
1. 本地管理
# 忽略 node_modules 目录 /node_modules // 我这里设置的 uni_modules 根据他的名字设置 /unpackage/dist
git init git add . git commit -m "feat: 新建了一个项目"
2. 把项目托管到码云
在 pages 目录中,创建首页(home)、分类(cate)、购物车(cart)、我的(my) 这 4 个 tabBar 页面。在 HBuilderX 中,可以通过如下的两个步骤,快速新建页面:
将 资料 目录下的 static 文件夹 拷贝一份,替换掉项目根目录中的 static 文件夹
修改项目根目录中的 pages.json 配置文件,新增 tabBar 的配置节点如下:
{ "tabBar": { "selectedColor": "#C00000", // 这个是选中字体 颜色 "list": [ { "pagePath": "pages/home/home", "text": "首页", "iconPath": "static/tab_icons/home.png", "selectedIconPath": "static/tab_icons/home-active.png" }, { "pagePath": "pages/cate/cate", "text": "分类", "iconPath": "static/tab_icons/cate.png", "selectedIconPath": "static/tab_icons/cate-active.png" }, { "pagePath": "pages/cart/cart", "text": "购物车", "iconPath": "static/tab_icons/cart.png", "selectedIconPath": "static/tab_icons/cart-active.png" }, { "pagePath": "pages/my/my", "text": "我的", "iconPath": "static/tab_icons/my.png", "selectedIconPath": "static/tab_icons/my-active.png" } ] } }
在 HBuilderX 中,把 pages 目录下的 index首页文件夹 删除掉
同时,把 page.json 中记录的 index 首页 路径删除掉
为了防止小程序运行失败,在微信开发者工具中,手动删除 pages 目录下的 index 首页文件夹
同时,把 components 目录下的 uni-link 组件文件夹 删除掉 (删除完了以后记得从新渲染)
{ "globalStyle": { "navigationBarTextStyle": "white", // 导航兰 字体颜色 "navigationBarTitleText": "黑马优购", // 文本 "navigationBarBackgroundColor": "#C00000", // 背景色 "backgroundColor": "#FFFFFF" } }
运行如下的命令,基于 master 分支在本地创建 home 子分支,用来开发和 home 首页相关的功能:
git checkout -b home
由于平台的限制,小程序项目中不支持 axios,而且原生的 wx.request() API
功能较为简单,不支持拦截器等全局定制的功能。因此,建议在 uni-app 项目中使用 @escook/request-miniprogram 第三方包发起网络数据请求。
官方文档:https://www.npmjs.com/package/@escook/request-miniprogram
最终,在项目的main.js
入口文件中,通过如下的方式进行配置:
import { $http } from '@escook/request-miniprogram' uni.$http = $http // 配置请求根路径 $http.baseUrl = 'https://www.uinav.com' // 请求开始之前做一些事情 $http.beforeRequest = function (options) { uni.showLoading({ title: '数据加载中...', }) } // 请求完成之后做一些事情 $http.afterRequest = function () { uni.hideLoading() }
1. 请求轮播图的数据
实现步骤:
在 data 中定义轮播图的数组
在 onl oad 生命周期函数中调用获取轮播图数据的方法
在 methods 中定义获取轮播图数据的方法
示例代码:
export default { data() { return { // 1. 轮播图的数据列表,默认为空数组 swiperList: [], } }, onl oad() { // 2. 在小程序页面刚加载的时候,调用获取轮播图数据的方法 this.getSwiperList() }, methods: { // 3. 获取轮播图数据的方法 async getSwiperList() { // 3.1 发起请求 const { data: res } = await uni.$http.get('/api/public/v1/home/swiperdata') // 3.2 请求失败 if (res.meta.status !== 200) { return uni.showToast({ title: '数据请求失败!', duration: 1500, icon: 'none', }) } // 3.3 请求成功,为 data 中的数据赋值 this.swiperList = res.message }, }, }
2. 渲染轮播图的 UI 结构
<template> <view> <!-- 轮播图区域 --> <swiper :indicator-dots="true" :autoplay="true" :interval="3000" :duration="1000" :circular="true"> <!-- 循环渲染轮播图的 item 项 --> <swiper-item v-for="(item, i) in swiperList" :key="i"> <view class="swiper-item"> <!-- 动态绑定图片的 src 属性 --> <image :src="item.image_src"></image> </view> </swiper-item> </swiper> </view> </template> <style lang="scss"> swiper { height: 330rpx; .swiper-item, image { width: 100%; height: 100%; } } </style>
3. 配置小程序分包( 分包可以减少小程序首次启动时的加载时间 )
为此,我们在项目中,把 tabBar 相关的 4 个页面放到主包中,其它页面(例如:商品详情页、商品列表页)放到分包中。在 uni-app 项目中,配置分包的步骤如下:
{ "pages": [ { "path": "pages/home/home", "style": {} }, { "path": "pages/cate/cate", "style": {} }, { "path": "pages/cart/cart", "style": {} }, { "path": "pages/my/my", "style": {} } ], "subPackages": [ { "root": "subpkg", "pages": [] } ] }
填写页面名称--使用scss的页面--在pages.json中注册--创建同名目标(新版好像没有这个)--选择小程序分包(subpkg)--创建
将 <swiper-item></swiper-item> 节点内的 view 组件,改造为 navigator 导航组件,并动态绑定 url 属性 的值。
<swiper-item v-for="(item, i) in swiperList" :key="i"> <navigator class="swiper-item" :url="'/subpkg/goods_detail/goods_detail?goods_id=' + item.goods_id"> <!-- 动态绑定图片的 src 属性 --> <image :src="item.image_src"></image> </navigator> </swiper-item>
当数据请求失败之后,经常需要调用 uni.showToast({ /* 配置对象 */ }) 方法来提示用户。此时,可以在全局封装一个 uni.$showMsg() 方法,来简化 uni.showToast() 方法的调用。具体的改造步骤如下:
// 封装的展示消息提示的方法 uni.$showMsg = function (title = '数据加载失败!', duration = 1500) { uni.showToast({ title, duration, icon: 'none', }) }
async getSwiperList() { const { data: res } = await uni.$http.get('/api/public/v1/home/swiperdata') if (res.meta.status !== 200) return uni.$showMsg() // 这里 this.swiperList = res.message }
实现思路:
export default { data() { return { // 1. 分类导航的数据列表 navList: [], } }, onl oad() { // 2. 在 onl oad 中调用获取数据的方法 this.getNavList() }, methods: { // 3. 在 methods 中定义获取数据的方法 async getNavList() { const { data: res } = await uni.$http.get('/api/public/v1/home/catitems') if (res.meta.status !== 200) return uni.$showMsg() this.navList = res.message }, }, }
定义如下的 UI 结构:
<!-- 分类导航区域 --> <view class="nav-list"> <view class="nav-item" v-for="(item, i) in navList" :key="i"> <image :src="item.image_src" class="nav-img"></image> </view> </view> .nav-list { display: flex; justify-content: space-around; margin: 15px 0; .nav-img { width: 128rpx; height: 140rpx; } }
<view class="nav-item" v-for="(item, i) in navList" :key="i" @click="navClickHandler(item)"> // nav-item 项被点击时候的事件处理函数 navClickHandler(item) { // 判断点击的是哪个 nav if (item.name === '分类') { uni.switchTab({ url: '/pages/cate/cate' }) } }
实现思路:
示例代码如下:
export default { data() { return { // 1. 楼层的数据列表 floorList: [], } }, onl oad() { // 2. 在 onl oad 中调用获取楼层数据的方法 this.getFloorList() }, methods: { // 3. 定义获取楼层列表数据的方法 async getFloorList() { const { data: res } = await uni.$http.get('/api/public/v1/home/floordata') if (res.meta.status !== 200) return uni.$showMsg() this.floorList = res.message }, }, }
<!-- 楼层区域 --> <view class="floor-list"> <!-- 楼层 item 项 --> <view class="floor-item" v-for="(item, i) in floorList" :key="i"> <!-- 楼层标题 --> <image :src="item.floor_title.image_src" class="floor-title"></image> </view> </view> .floor-title { height: 60rpx; width: 100%; display: flex; }
<!-- 楼层图片区域 --> <view class="floor-img-box"> <!-- 左侧大图片的盒子 --> <view class="left-img-box"> <image :src="item.product_list[0].image_src" :style="{width: item.product_list[0].image_width + 'rpx'}" mode="widthFix"></image> </view> <!-- 右侧 4 个小图片的盒子 --> <view class="right-img-box"> <view class="right-img-item" v-for="(item2, i2) in item.product_list" :key="i2" v-if="i2 !== 0"> <image :src="item2.image_src" mode="widthFix" :style="{width: item2.image_width + 'rpx'}"></image> </view> </view> </view> .right-img-box { display: flex; flex-wrap: wrap; justify-content: space-around; } .floor-img-box { display: flex; padding-left: 10rpx; }
// 获取楼层列表数据 async getFloorList() { const { data: res } = await uni.$http.get('/api/public/v1/home/floordata') if (res.meta.status !== 200) return uni.$showMsg() // 通过双层 forEach 循环,处理 URL 地址 res.message.forEach(floor => { floor.product_list.forEach(prod => { prod.url = '/subpkg/goods_list/goods_list?' + prod.navigator_url.split('?')[1] }) }) this.floorList = res.message }
<!-- 楼层图片区域 --> <view class="floor-img-box"> <!-- 左侧大图片的盒子 --> <navigator class="left-img-box" :url="item.product_list[0].url"> <image :src="item.product_list[0].image_src" :style="{width: item.product_list[0].image_width + 'rpx'}" mode="widthFix"></image> </navigator> <!-- 右侧 4 个小图片的盒子 --> <view class="right-img-box"> <navigator class="right-img-item" v-for="(item2, i2) in item.product_list" :key="i2" v-if="i2 !== 0" :url="item2.url"> <image :src="item2.image_src" mode="widthFix" :style="{width: item2.image_width + 'rpx'}"></image> </navigator> </view> </view>
git add . git commit -m "完成了 home 首页的开发" git push -u origin home git checkout master git merge home git branch -d home