# uni-app
安装 HBuilderX (https://dcloud.io/hbuilderx.html) 点击 APP 开发版
安装 微信开发者工具 (https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html) 选择稳定版
微信开发者工具第一次需要填路径 —— 打开文件位置,然后将位置输入到里边。且要将微信开发者工具的设置 —— 安全 —— 服务端口 开启
用 HBuilderX 的运行选项卡选来选择运行到哪里
# 文件
- pages:用来存放 uni-app 的所有页面
- static:用来存放静态资源
- unpackage:用来存放最后打包输出的文件
- App.vue:项目的根组件
- main.js:项目的人口文件
- manifest.json:用来配置一些应用的一些打包的东西 (配置打包之后的一些配置文件)
- pages.json:用来设置整个项目页面的文件的存放路径以及窗口的外观
- uni-scss:常用样式的变量
# 开发规范
- 页面文件遵循 Vue 单文件组件规范
- 组件标签靠近小程序规范
- 接口能力 靠近微信小程序规范,但需将前缀 wx 替换为 uni
- 数据绑定及事件处理同 Vue.js 规范,同时补充了 App 及页面的生命周期
- 为兼容多端运行,建议使用 flex 布局进行开发
# 简单操作案例
-
App.vue
<script> export default { onLaunch: function () { console.log("App Launch"); }, onShow: function () { console.log("App Show"); }, onHide: function () { console.log("App Hide"); }, }; </script> <style lang="scss"> /* 全局样式 */ @import url("./static/fonts/iconfont.css"); /* iconfont 最好使用 ~@/static/fonts/ 这种绝对路径的方式,去 iconfont.css 中修改 */ .box1 { background-color: pink; } </style> -
pages.json
{ "pages": [ //pages数组中第一项表示应用启动页 { "path": "pages/home/home" }, { "path": "pages/uni-style/uni-style", "style": { "navigationBarTitleText": "样式" } }, { "path": "pages/message/message", "style": { // 页面级别的配置,会将全局的配置替换掉 "navigationBarTitleText": "信息页", "navigationBarBackgroundColor": "#9dc1fa", // 导航栏背景色 // 为 h5设置特定样式 "h5": { "pullToRefresh": { "color": "#3F536E" } } } }, { "path": "pages/index/index" }, { "path": "pages/contact/contact", "style": { "navigationBarTitleText": "联系我们" } }, { "path": "pages/detail/detail", "style": { "navigationBarTitleText": "详情页" } } ], "globalStyle": { // 全局的配置 "navigationBarTextStyle": "white", "navigationBarTitleText": "毛茸茸测试", "navigationBarBackgroundColor": "#87CEFA", // 导航栏背景色 "backgroundColor": "#778899", // 下拉时的背景色 "enablePullDownRefresh": true, //是否可以下拉 "backgroundTextStyle": "light" // 将loading变成亮色 // onReachBottomDistance // 用来设置底部距离页面多远时触发上拉触底的事件,单位只支持px }, "tabBar": { "color": "#A0522D", // tab上的文字默认颜色 "selectedColor": "#AEEEEE", // tab上的文字选中时的颜色 "backgroundColor": "#FFFFFF", // tabBar 的背景色 "borderStyle": "white", //tabbar 上边框的颜色,可选值 black/white "position": "bottom", //可选值 bottom、top(仅支持微信小程序) "list": [ { "text": "首页", // tab 上按钮文字 "pagePath": "pages/index/index", // 页面路径,必须先在 pages 中定义 "iconPath": "static/tabs/home.png", // 未选中的图片路径 "selectedIconPath": "static/tabs/home-active.png" // 选中的图片路径 }, { "text": "信息", "pagePath": "pages/message/message", "iconPath": "static/tabs/message.png", "selectedIconPath": "static/tabs/message-active.png" }, { "text": "我们", "pagePath": "pages/contact/contact", "iconPath": "static/tabs/contact.png", "selectedIconPath": "static/tabs/contact-active.png" } ] }, // 启动模式配置,仅开发期间生效,用于模拟直达页面的场景,如:小程序转发后,用户点击所打开的页面。 "condition": { "current": 0, "list": [ { "name": "详情页", "path": "pages/detail/detail", "query": "id=80" } ] } } -
contact.vue
<template> <view>联系我们</view> </template> -
detail.vue
<template> <!-- view 组件相当于 div --> <view> <view> <!-- text 组件 --> <text>不可选</text> </view> <view> <text selectable>这个是可选</text> <!-- 长按选中 --> </view> <view> <text selectable space="emsp" style="font-size: 16px;" >这个 是可选</text > </view> <view> <text>&</text> <!-- 小程序和浏览器默认是能够解码的 --> </view> <view class="box2" hover-class="box2-active"> <!-- hover-class 指定按下去的样式类 --> <!-- hover-stop-propagation 阻止事件冒泡 --> <!-- hover-start-time 按住后多久出现点击态,单位毫秒 --> <!-- hover-stay-time 手指松开后点击态保留时间 --> <view class="box" :hover-start-time="2000" :hover-stay-time="2000" hover-class="box-active" hover-stop-propagation >大盒子</view > </view> <button type="primary" size="mini">按钮</button> <!-- plain 镂空 --> <button type="primary" plain>按钮</button> <button type="primary" loading>loading</button> <!-- image 组件 --> <!-- mode 图片裁剪、缩放的模式 --> <!-- aspectFit 图片的长边能完全显示出来 --> <!-- aspectFill 只保证图片的短边能完全显示出来 --> <image src="../../static/logo.png" mode="aspectFit"></image> </view> </template> <script></script> <style> .box { width: 100px; height: 100px; background-color: orange; } .box-active { background-color: pink; } .box2 { width: 200px; height: 200px; background-color: blue; } .box2-active { background-color: yellow; } </style> -
home.vue
<template> <view> <view>基本数据绑定</view> <view>{{msg}}</view> <view>{{'你好'+'世界'}}</view> <view>{{1+1}}</view> <view>{{flag?'真的':'假的'}}</view> <image :src="imgUrl" mode=""></image> <view v-for="(item,index) in arr" :key="index"> 序号:{{item.id}} 标题——{{item.title}}, 使用时间——{{item.age}} </view> <button type="primary" @click="handleClick(100,$event)">按钮</button> </view> </template> <script> export default { data() { return { msg: "hello", flag: true, imgUrl: "https://img2.baidu.com/it/u=1205915504,3217808836&fm=26&fmt=auto&gp=0.jpg", arr: [ { title: "iphone 10", age: "1年", id: 1, }, { title: "iphone 11", age: "2年", id: 2, }, { title: "iphone 12", age: "3年", id: 3, }, { title: "iphone 13", age: "4年", id: 4, }, ], }; }, methods: { handleClick(num, e) { console.log(num, e); }, }, }; </script> -
index.vue
<template> <view class="content"> <image class="logo" src="/static/logo.png"></image> <view class="text-area"> <text class="title">{{title}}</text> </view> </view> </template> <script> export default { data() { return { title: "Hello", }; }, onLoad(options) { console.log("页面加载了", options); }, onShow() { console.log("页面显示"); }, onReady() { console.log("页面初次渲染"); }, onHide() { console.log("页面隐藏了"); }, methods: {}, }; </script> <style> .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> -
message.vue
<template> <view>hello-uni</view> </template> -
uni-style.vue
<template> <view> <view> 样式的学习 </view> <view class="box1"> 测试文字 <text>13</text> </view> <view class="iconfont icon-tupian"> 字体图标 </view> </view> </template> <script></script> <style lang="scss"> @import url("./a.css"); .box1 { /* rpx 以750宽的屏幕为基准 */ /* page 相当于 body节点 */ width: 375rpx; height: 375rpx; /* 局部样式 */ background-color: $global-color; text { color: $uni-color-success; } } </style>
# 生命周期
# 应用的生命周期
应用的生命周期要定义在 App.vue 中
- onLaunch 当 uni-app 初始化完成时触发 (全局只触发一次)
- onShow 当 uni-app 启动,或从后台进入前台显示时触发
- onHide 当 uni-app 从前台进入后台 (隐藏时触发)
- onError 当 uni-app 报错时触发
# 页面的生命周期
-
onLoad 监听页面加载,其参数为上个页面传递的参数,参数类型为 Object (用于页面传参)
-
onShow 监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面
切换 tabBar 只会触发 onShow 不会触发 onLoad -
onReady 监听页面初次渲染完成
-
onHide 监听页面隐藏
-
onUnload 监听页面卸载 (销毁)
-
onPullDownRefresh 监听用户下拉动作,一般用于下拉刷新
<template> <view> <view> 列表页 </view> <view v-for="item in list"> {{item}} </view> <button type="default" @click="pullDown()">下拉刷新</button> </view> </template> <script> export default { data() { return { list: ["前端", "java", "ui", "测试", "大数据"], }; }, // 监听下拉刷新 onPullDownRefresh() { setTimeout(() => { this.list = ["前端", "java", "ui"]; uni.stopPullDownRefresh(); //关闭当前页面的下拉刷新 }, 1000); }, methods: { pullDown() { // 下拉刷新开启方式有两种 // 1. 在 pages.json 中设置 enablePullDownRefresh 为 true // 2. uni.startPullDownRefresh() uni.startPullDownRefresh(); }, }, }; </script> -
onReachBottom 页面滚动到底部的事件(不是 scroll-view 滚到底),常用于下拉下一页数据。具体见下方注意事项
<template> <view> <view v-for="item in list" class="box-item"> {{item}} </view> </view> </template> <script> export default { data() { return { list: [ "前端", "java", "ui", "测试", "大数据", "前端", "java", "ui", "测试", "大数据", ], }; }, // 在 pages.json 中设置 onReachBottomDistance 的值(数字) // 监听页面触底 onReachBottom() { console.log("页面触底了"); this.list = [ ...this.list, ...[ "前端", "java", "ui", "测试", "大数据", "前端", "java", "ui", "测试", "大数据", ], ]; }, }; </script> <style> .box-item { height: 100px; line-height: 100px; } </style>
# 请求
使用 uni.request (obj) 进行网络请求
需要注意的是:在小程序中网络相关的 API 在使用前需要配置域名白名单
一般在 onLoad 中请求数据
也可以使用 async 和 await
-
案例 List.vue
<template> <view> <button type="default" @click="get">get请求</button> </view> </template> <script> export default { methods: { get() { uni.request({ url: "https://netease-cloud-music-api-1.vercel.app/search", data: { keywords: "海阔天空", }, success(e) { // 小程序如果请求不成功去 小程序的详情——本地设置——不校验合法域名关掉 console.log(e); }, }); }, }, }; </script> -
封装 uni.request ()
-
util 文件夹下 api.js
const BASE_URL = "http://localhost:8080"; export const myRequest = (options) => { return new Promise((resolve, reject) => { uni.request({ url: BASE_URL + options.url, method: options.method || "GEt", data: options.data || {}, success: (res) => { if (res.data.status !== 0) { return uni.showToast({ title: "获取数据失败", }); } resolve(res); }, fail: (err) => { uni.showToast({ title: "请求接口失败", }); reject(err); }, }); }); }; -
封装完之后去 main.js
import { myRequest } from "./util/api.js"; Vue.prototype.$myRequest = myRequest; -
使用
async getSwipers(){ const res = await this.$myRequest({ url:'/api/getlunbo' }) this.swipers = res.data.message }
-
# 数据缓存
带 sync 的是同步的,不带 sync 的是不同步的
uni.setStorage (OBJECT):存储缓存
uni.setStorageSync (KEY,DATA):存储缓存
uni.getStorage (OBJECT):获取缓存
uni.getStorageSync (KEY):获取缓存
uni.removeStorage (OBJECT):删除缓存
uni.removeStorageSync (KEY):删除缓存
uni.clearStorage ():清除本地所有缓存,禁用
-
案例
<template> <view> <button type="default" @click="get">get请求</button> <button type="default" @click="setStorage">存储数据</button> <button type="default" @click="getStorage">获取数据</button> <button type="default" @click="removeStorage">移除数据</button> </view> </template> <script> export default { methods: { setStorage() { /* uni.setStorage({ key: "id", data: 80, success() { console.log("存储成功"); }, }); */ uni.setStorageSync("id", 100); }, getStorage() { /* uni.getStorage({ key: "id", success(res) { console.log(res.data); }, }); */ const res = uni.getStorageSync("id"); console.log(res); //100 }, removeStorage() { /* uni.removeStorage({ key: "id", success() { console.log("删除成功"); }, }); */ uni.removeStorageSync("id"); }, }, }; </script>
# 上传图片和预览
uni.chooseImage (OBJECT):从本地相册选择图片或使用相机拍照。
uni.previewImage (OBJECT):预览图片。
-
案例
<template> <view> <button type="default" @click="chooseImg">上传图片</button> <image v-for="item in imgArr" :src="item" @click="previewImg(item)" ></image> </view> </template> <script> export default { data() { return { imgArr: [], }; }, methods: { chooseImg() { uni.chooseImage({ count: 5, // 最多可以选择的图片张数,默认9 success: (res) => { // console.log(res) //成功则返回图片的本地文件路径列表 tempFilePaths this.imgArr = res.tempFilePaths; }, }); }, previewImg(imgSrc) { uni.previewImage({ current: imgSrc, //用来设置当前要看见的图片 urls: this.imgArr, loop: true, //是否可循环预览,只支持 App indicator: "number", // 图片指示器样式,只支持 App }); }, }, }; </script>
# 条件编译跨段兼容
(https://uniapp.dcloud.io/platform?id = 跨端兼容)
写法:先注释,然后以 #ifdef 加 平台 开头,以 #endif 结尾。
-
案例
<template> <view> <!-- #ifdef H5 --> <view>我希望在h5页面看见</view> <!-- #endif --> <!-- #ifdef MP-WEIXIN --> <view>我希望在微信小程序看见</view> <!-- #endif --> </view> </template> <script> export default { onLoad() { // #ifdef H5 console.log("h5输出"); // #endif // #ifdef MP-WEIXIN console.log("微信小程序输出"); // #endif }, }; </script> <style> /* #ifdef H5 */ view { color: hotpink; } /* #endif */ /* #ifdef MP-WEIXIN */ view { color: orange; } /* #endif */ </style>
# 导航跳转和传参
# 声明式跳转
navigator 组件
-
案例
<template> <view> 声明式跳转 <!-- 传参 --> <navigator url="../detail/detail?id=80&age=19">跳转至详情页</navigator> <!-- open-type:跳转方式 --> <!-- switchTab:跳转到 tabBar 页面 --> <navigator url="../message/message" open-type="switchTab" >跳转至信息页</navigator > <!-- redirect:对当前页面进行关闭,然后再跳转到下一个页面 --> <navigator url="../detail/detail" open-type="redirect" >跳转至详情页</navigator > </view> </template>
# 编程式跳转
-
案例
<template> <view> <!-- 编程式跳转 --> <button type="default" @click="goDetail">跳转至详情页</button> <button type="default" @click="goMessage">跳转至信息页</button> <button type="default" @click="redirectDetail"> 跳转至详情页并关闭当前页 </button> </view> </template> <script> export default { methods: { goDetail() { uni.navigateTo({ // url:'../detail/detail', // 传参 url: "../detail/detail?id=80&age=19", }); }, goMessage() { // uni.switchTab 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面 uni.switchTab({ url: "../message/message", }); }, redirectDetail() { // uni.redirectTo 关闭当前页面,跳转到应用内的某个页面 uni.redirectTo({ url: "../detail/detail", }); }, }, }; </script>
# 接收参数
-
案例
<script> export default { // 通过 onLoad 接受上个页面传递过来的参数 onLoad(options) { console.log(options); }, }; </script>
# 组件的创建使用和组件的生命周期
创建,使用以及生命周期和 Vue 相同,但是 beforeUpdate 和 updated 仅 H5 支持
# 组件通信
父传子:父组件传递 props,子组件用 props 接收
子传父:通过自定事件传递 this.$emit (‘名字’, 数据),父组件在引用的子标签上用 @名字 =‘函数’ 接收
兄弟组件之间:
uni.on (eventName,callback) 注册全局的自定义事件
-
案例
-
a.vue
<template> <view> 这是A组件<button @click="addNum">修改b组件</button> </view> </template> <script> export default { name: "a", data() { return {}; }, methods: { addNum() { uni.$emit("updateNum", 10); }, }, }; </script> -
b.vue
<template> <view> 这是b组件的数据{{num}} </view> </template> <script> export default { name: "b", data() { return { num: 0, }; }, created() { uni.$on("updateNum", (num) => { this.num += num; }); }, }; </script> <style></style>
-
# 杂项
uni.showToast():显示消息提示框
<swiper indicator-dots circular></swiper> 视图容器
<swiper-item></swiper-item>:仅可放置在 <swiper> 组件中,宽高自动设置为100%。注意:宽高100%是相对于其父组件
map 地图组件
uni.makePhoneCall() 拨打电话
<scroll-view></scroll-view> 可滚动的视图区域:比如电商的分类
<rich-text> 富文本组件,可以解析 HTML
editor 富文本编辑器
editorContext 富文本API,对富文本进行各种操作,需要先定义一个方法(method),然后触发并执行对应的API命令
uni.showLoading()
uni.showModal(OBJECT) 显示模态弹窗,可以只有一个确定按钮,也可以同时有确定和取消按钮。类似于一个API整合了 html 中:alert、confirm。
要去设置一个APPID(微信公众平台-登录-选择一个测试的-将APPID复制到manifest.json中微信小程序配置中)
如果在真机中运行不了,有可能是域名的问题,去微信公众平台-服务器域名中添加请求的主域名
uni.uploadFile(OBJECT) 上传文件
uni.uploadFile({
url: `${this.baseURL}api/article/uploadimgs`,
filePath: this.imgArr[0],
name: "file",
formData: {
token: this.token
},
success: (result) => {
console.log(JSON.parse(result.data).data)
this.avatar = JSON.parse(result.data).data
}
})
uni.makePhoneCall(OBJECT) 拨打电话
# uView UI 框架
-
安装
// 如果您的项目是HX创建的,根目录又没有package.json文件的话,请先执行如下命令: npm init -y // 安装 npm install uview-ui