• 大前端 - UniAPP


    当前单纯的Web 端开发已经慢慢无法满足我们的业务需求了,微信小程序、安卓 APP、IOS APP,甚至是 Windows 桌面端开发都成了我们需要掌握的技能。

    所以呢,市面上各种「跨平台」开发解决方案层出不穷,比较有代表性的就是: UniAPP 、 Flutter、 React Native、Taro、Weex等等。

    UniAPP 是基于「 Vue + 微信小程序 」语言体系,开发人员学习成本低上手快,同时随着如今 UniAPP 生态也逐步趋于成熟。所以,基于 UniAPP 开发多端项目,已经是很多中小型企业常用的技术解决方案。

    那么,我们就从 UniAPP 基础开始,对照企业级实践标准,从零到一,一步一步打造一个 多端(微信小程序 + H5 + 安卓 APP + IOS APP)的社区论坛类项目。

    UniAPP 快速入门学习

    一、UniAPP 介绍

    (1)什么是 UniAPP ?

    uni-app 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到 iOS、Android、H5,以及各种小程序(微信/支付宝/百度/头条/ QQ /钉钉)等多个平台,方便开发者快速交付,不需要转换开发思维,不需要更改开发习惯。

    (2)为什么要选择 UniAPP ?

    1. 开发者/案例数量更多

    几十万应用、uni 统计月活12亿、70+ 微信 / QQ群

    1. 平台能力不受限

    在跨端的同时,通过条件编译 + 平台特有 API 调用,可以优雅的为某平台写个性化代码,调用专有能力而不影响其他平台

    1. 性能体验优秀

    加载新页面速度更快、自动 diff 更新数据,App 端支持原生渲染支撑更流畅的用户体验,小程序端的性能优于市场其他框架

    1. 周边生态丰富

    插件市场数千款插件,支持 NPM、支持小程序组件和SDK,微信生态的各种 sdk 可直接用于跨平台 APP

    1. 学习成本低

    基于通用的前端技术栈,采用 vue 语法+微信小程序 api,无额外学习成本

    (3)UniAPP 功能框架

    请添加图片描述

    (4)UniAPP 开发环境搭建

    1. 下载开发工具 HBuilderX

    HBuilderX 是通用的前端开发工具,但为uni-app做了特别强化。

    下载 App 开发版,可开箱即用;如下载标准版,在运行或发行uni-app时,会提示安装uni-app插件,插件下载完成后方可使用

    1. 创建 uni-app 项目

    选择uni-app类型,输入工程名,选择模板,点击创建,即可成功创建。

    uni-app自带的模板有 Hello uni-app ,是官方的组件和API示例。

    还有一个重要模板是 uni ui项目模板,日常开发推荐使用该模板,已内置大量常用组件。

    1. 运行 uni-app

    主要包括:浏览器运行、真机运行、小程序运行等

    1. 发布 uni-app

    主要包括:云端原生 APP 、离线原生 APP、H5、各种小程序

    二、UniAPP 初始化相关配置

    (1)工程目录结构

    ┌─components            uni-app组件目录
    │  └─comp-a.vue        可复用的a组件
    ├─hybrid                存放本地网页的目录(自建)
    ├─platforms             存放各平台专用页面的目录(自建)
    ├─pages                 业务页面文件存放的目录
    │  ├─index
    │  │  └─index.vue      index页面
    │  └─list
    │     └─list.vue        list页面
    ├─static                存放应用引用静态资源(如图片、视频等)的目录,注意:静态资源只能存放于此
    ├─wxcomponents          存放小程序组件的目录(自建)
    ├─common                公共资源(自建)
    ├─api                   请求封装(自建)
    ├─store                 状态管理(自建)
    ├─main.js               Vue初始化入口文件
    ├─App.vue               应用配置,用来配置App全局样式以及监听 应用生命周期
    ├─manifest.json         配置应用名称、appid、logo、版本等打包信息
    └─pages.json            配置页面路由、导航条、选项卡等页面类信息
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    提示

    • static下目录的 js 文件不会被 webpack 编译,里面如果有 es6 的代码,不经过转换直接运行,在手机设备上会报错。
    • 所以less、scss等资源同样不要放在 static 目录下,建议这些公共的资源放在 common目录下

    (2)应用配置 manifest.json

    manifest.json 文件是应用的配置文件,用于指定应用的名称、图标、权限等,我们也可以在这里为 Vue 为H5 设置跨域拦截处理器

    (3)编译配置 vue.config.js

    vue.config.js 是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被自动加载,一般用于配置 webpack 等编译选项。官方文档

    (4)全局配置 page.json

    pages.json 文件用来对 uni-app 进行全局配置,决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等。它类似微信小程序中app.json页面管理部分。

    官方文档

    属性类型必填描述
    globalStyleObject设置默认页面的窗口表现
    pagesObject Array设置页面路径及窗口表现
    easycomObject组件自动引入规则
    tabBarObject设置底部 tab 的表现
    conditionObject启动模式配置
    subPackagesObject Array分包加载配置
    preloadRuleObject分包预下载规则

    (5)全局样式 uni.scss

    uni.scss文件的用途是为了方便整体控制应用的风格。比如按钮颜色、边框风格,uni.scss文件里预置了一批scss变量预置。官方文档

    uni-app 官方扩展插件(uni ui)及 插件市场 上很多三方插件均使用了这些样式变量,如果你是插件开发者,建议你使用 scss 预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App。

    uni.scss是一个特殊文件,在代码中无需 import 这个文件即可在scss代码中使用这里的样式变量。uni-app的编译器在webpack配置中特殊处理了这个 uni.scss,使得每个 scss 文件都被注入这个uni.scss,达到全局可用的效果。如果开发者想要less、stylus的全局使用,需要在vue.config.js中自行配置webpack策略。

    (6)主组件 App.vue

    App.vue是uni-app的主组件,所有页面都是在App.vue下进行切换的,是页面入口文件。但App.vue本身不是页面,这里不能编写视图元素。

    这个文件的作用包括:调用应用生命周期函数、配置全局样式、配置全局的存储globalData

    应用生命周期仅可在App.vue中监听,在页面监听无效。

    (7)入口文件 main.js

    main.js是uni-app的入口文件,主要作用是初始化vue实例、定义全局组件、使用需要的插件如vuex。

    官方文档

    (8)UniAPP 开发规范及资源路径

    1. 开发规范约定
    • 页面文件向导 Vue单文件组件(SFC)规范
    • 组件标签靠近小程序规范,详见 uni-app 组件规范
    • 互连能力(JS API)靠近微信小程序规范,但需要将替换替换 wx 为 uni ,详见uni-app接口规范
    • 数据绑定及事件处理同 Vue.js 规范,同时补充了 App 和页面的生命周期
    • 为兼容多端运行,建议使用 flex 布局进行开发
    1. 资源路径说明

    template 内约会静态资源,如 image,video 等标签的 src 属性时,可以使用相对路径或绝对路径,形式如下:

    <!-- 绝对路径,/static指根目录下的static目录,在cli项目中/static指src目录下的static目录 -->
    <image class="logo" src="/static/logo.png"></image>
    <image class="logo" src="@/static/logo.png"></image>
    <!-- 相对路径 -->
    <image class="logo" src="../../static/logo.png"></image>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意

    • @ 初始的绝对路径以及相对路径会通过 base64 转换规则校验
    • 约会的静态资源在非 h5 平台,均不转为 base64
    • H5平台,小于4kb的资源会被转换成base64,其余不转

    js 文件或 script 标签内,可以使用相对路径和绝对路径,形式如下:

    // 绝对路径,@指向项目根目录,在cli项目中@指向src目录
    import add from '@/common/add.js'
    // 相对路径
    import add from '../../common/add.js'
    
    • 1
    • 2
    • 3
    • 4

    css 文件或 style 标签内,可以使用相对路径和绝对路径,形式如下:

    /* 绝对路径 */
    @import url('/common/uni.css');
    @import url('@/common/uni.css');
    /* 相对路径 */
    @import url('../../common/uni.css');
    
    • 1
    • 2
    • 3
    • 4
    • 5

    css 文件或 style 标签内引用的图片路径,可以使用相对路径也可以使用绝对路径,形式如下:

    /* 绝对路径 */
    background-image: url(/static/logo.png);
    background-image: url(@/static/logo.png);
    /* 相对路径 */
    background-image: url(../../static/logo.png);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    三、UniAPP 生命周期

    学习一个工具的目的核心是什么?是为了解决核心业务逻辑问题,业务逻辑很多时候简单的解释一句话:“在合适的时机干合适的事情”,OK!什么是合适的时机呢?简单的说,页面运行过程中,各个阶段的回调函数就是页面中的时机,我们也叫这个为“生命周期钩子函数”,当然,业务中我们也会写到很多「回调」的逻辑,这些回调其实也是咱们自定义的时机,UniAPP 的生命周期钩子函数回调函数有哪些呢?我们来理解一下!

    uni-app 完整支持 Vue 实例的生命周期,同时还新增 应用生命周期页面生命周期

    (1)应用生命周期

    在整个应用启动,运行的时候执行的钩子函数。

    函数名说明
    onLaunchuni-app 初始化完成时触发(全局只触发一次)
    onShowuni-app 启动,或从后台进入前台显示
    onHideuni-app 从前台进入后台
    onErroruni-app 报错时触发
    onUniNViewMessagenvue 页面发送的数据进行监听,可参考 nvue 向 vue 通讯
    onUnhandledRejection对未处理的 Promise 拒绝事件监听函数(2.8.1+)
    onPageNotFound页面不存在监听函数
    onThemeChange监听系统主题变化

    (2)页面生命周期

    函数名说明
    onLoad监听页面加载,其参数为上个页面传递的数据,参数类型为Object(用于页面传参),参考示例
    onShow监听页面显示。页面每次出现在屏幕上都触发,包括从下级页面点返回露出当前页面
    onReady监听页面初次渲染完成。注意如果渲染速度快,会在页面进入动画完成前触发
    onHide监听页面隐藏
    onUnload监听页面卸载
    onResize监听窗口尺寸变化
    onPullDownRefresh监听用户下拉动作,一般用于下拉刷新,参考示例
    onReachBottom页面滚动到底部的事件(不是scroll-view滚到底),常用于下拉下一页数据。具体见下方注意事项
    onTabItemTap点击 tab 时触发,参数为Object,具体见下方注意事项
    onShareAppMessage用户点击右上角分享
    onPageScroll监听页面滚动,参数为Object
    onNavigationBarButtonTap监听原生标题栏按钮点击事件,参数为Object
    onBackPress监听页面返回
    onNavigationBarSearchInputChanged监听原生标题栏搜索输入框输入内容变化事件
    onNavigationBarSearchInputConfirmed监听原生标题栏搜索输入框搜索事件,用户点击软键盘上的“搜索”按钮时触发。
    onNavigationBarSearchInputClicked监听原生标题栏搜索输入框点击事件
    onShareTimeline监听用户点击右上角转发到朋友圈
    onAddToFavorites监听用户点击右上角收藏

    四、UniAPP 路由配置及页面跳转

    (1)路由配置

    uni-app 页面路由全部交给框架统一管理,开发者需要在pages.json里配置每个路由页面的路径及页面样式(类似小程序在 app.json 中配置页面路由)。

    "pages": [
    		{
    			"path": "pages/index",
    			"style": {
    				"navigationBarTitleText": "路由配置",
    				"navigationBarBackgroundColor": "#FFFFFF",
    				"navigationBarTextStyle": "black",
    				"backgroundColor": "#FFFFFF",
    				"enablePullDownRefresh": true
    			}
    		},
    		{
    			"path": "pages/user",
    			"style": {
    				"navigationBarTitleText": "路由配置",
    				"navigationBarBackgroundColor": "#FFFFFF",
    				"navigationBarTextStyle": "black",
    				"backgroundColor": "#FFFFFF",
    				"enablePullDownRefresh": true
    			}
    		}
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    (2)路由跳转

    uni-app 有两种页面路由跳转方式:使用navigator组件跳转(标签式导航)、调用API跳转(编程式导航)

    框架以栈的形式管理当前所有页面, 当发生路由切换的时候,页面栈的表现如下:

    路由方式页面栈表现触发时机
    初始化新页面入栈uni-app 打开的第一个页面
    打开新页面新页面入栈调用 API uni.navigateTo
    使用组件 <navigator open-type="navigate" />
    页面重定向当前页面出栈,新页面入栈调用 API uni.redirectTo
    使用组件
    页面返回页面不断出栈,直到目标返回页调用 API uni.navigateBack
    使用组件 、
    用户按左上角返回按钮、安卓用户点击物理back按键
    Tab 切换页面全部出栈,只留下新的 Tab 页面调用 API uni.switchTab
    使用组件 、
    用户切换 Tab
    重加载页面全部出栈,只留下新的页面调用 API uni.reLaunch
    使用组件

    (3)获取当前页面栈

    getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。

    注意: getCurrentPages() 仅用于展示页面栈的情况,请勿修改页面栈,以免造成页面状态错误。

    (4)路由传参与接收

    说明:页面生命周期的 onLoad()监听页面加载,其参数为上个页面传递的数据,如:

    //页面跳转并传递参数
    uni.navigateTo({
        url: 'page2?name=liy&message=Hello'
    });
    
    • 1
    • 2
    • 3
    • 4

    url为将要跳转的页面路径 ,路径后可以带参数。参数与路径之间使用?分隔,参数键与参数值用=相连,不同参数用&分隔。如 ‘path?key1=value2&key2=value2’,path为下一个页面的路径,下一个页面的onLoad函数可得到传递的参数。

    // 页面 2 接收参数
    onLoad: function (option) { //option为object类型,会序列化上个页面传递的参数
    	console.log(option.name); //打印出上个页面传递的参数。
    	console.log(option.message); //打印出上个页面传递的参数。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:url 有长度限制,太长的字符串会传递失败,并且不规范的字符格式也可能导致传递失败,所以对于复杂参数建议使用 encodeURI、decodeURI 进行处理后传递

    (5)小程序路由分包配置

    因小程序有体积和资源加载限制,各家小程序平台提供了分包方式,优化小程序的下载和启动速度。

    所谓的主包,即放置默认启动页面及 TabBar 页面,而分包则是根据 pages.json 的配置进行划分。

    在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,会把对应分包自动下载下来,下载完成后再进行展示,此时终端界面会有等待提示。

    "subPackages": [
      	{
    			"root": "news",
    			"pages": [{
    					"path": "index",
    					"style": {
    						"navigationBarTitleText": "新闻中心",
    						"navigationBarBackgroundColor": "#FFFFFF",
    						"navigationBarTextStyle": "black",
    						"backgroundColor": "#FFFFFF"
    					}
    				}
    			]
    		}
      	... 
    ]// 预下载分包设置
    "preloadRule": {
    		"pages/index": {
    			"network": "all",
    			"packages": ["activities"]
    		}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    五、UniAPP 常用组件简介

    uni-app 为开发者提供了一系列基础组件,类似 HTML 里的基础标签元素,但 uni-app 的组件与 HTML 不同,而是与小程序相同,更适合手机端使用。

    虽然不推荐使用 HTML 标签,但实际上如果开发者写了div等标签,在编译到非H5平台时也会被编译器转换为 view 标签,类似的还有 spantextanavigator等,包括 css 里的元素选择器也会转,但为了管理方便、策略统一,新写代码时仍然建议使用view等组件。

    开发者可以通过组合这些基础组件进行快速开发, 基于内置的基础组件,可以开发各种扩展组件,组件规范与vue组件相同。

    案例:知心姐姐布局实现

    六、UniAPP 常用 API 简介

    uni-app的 js 代码,h5 端运行于浏览器中,非 h5 端 Android 平台运行在 v8 引擎中,iOS 平台运行在 iOS 自带的 jscore 引擎中。所以,uni-app的 jsAPI 由标准 ECMAScript 的 js API 和 uni 扩展 API 这两部分组成。

    ECMAScript 由 Ecma 国际管理,是基础 js 语法。浏览器基于标准 js 扩充了window、document 等 js API;Node.js 基于标准 js 扩充了 fs 等模块;小程序也基于标准 js 扩展了各种 wx.xx、my.xx、swan.xx 的 API。

    标准 ecmascript 的 API 非常多,比如:console、settimeout等等。

    非 H5 端,虽然不支持 window、document、navigator 等浏览器的 js API,但也支持标准 ECMAScript。

    开发者不要把浏览器里的 js 等价于标准 js。

    所以 uni-app 的非 H5 端,一样支持标准 js,支持 if、for 等语法,支持字符串、数组、时间等变量及各种处理方法,仅仅是不支持浏览器专用对象。

    案例:知心姐姐聊天功能

    subpages/chat/chat.vue

    <template>
    	<view class="container">
    
    		<!-- 聊天列表 -->
    		<view class="chat-body">
    			<block v-for="(item, index) in chatList" :key="index">
    				<view class="chat-one" v-if="!item.isMe">
    					<image class="chat-face" src="@/static/faces/1-thump.jpg" />
    					<view class="chat-box">
    						<view class="chat-sender">知心姐姐</view>
    						<view class="chat-content" v-if="item.type === 'txt'">{{item.content}}</view>
    						<image class="chat-img" v-if="item.type === 'img'" :src="item.content" mode='widthFix' />
    					</view>
    				</view>
    				<view v-else class="chat-one chat-one-mine">
    					<view class="chat-box">
    						<view class="chat-content" v-if="item.type === 'txt'">{{item.content}}</view>
    						<image class="chat-img" v-if="item.type === 'img'" :src="item.content" mode='widthFix' />
    					</view>
    					<image class="chat-face" src="@/static/faces/6-thump.jpg" />
    				</view>
    			</block>
    
    		</view>
    		<!-- 聊天输入 -->
    		<view class="chat-footer">
    			<input class="msg-input" type="text" cursor-spacing="16" v-model="myInput"/>
    			<image class="img-chose" src="@/static/img.png" @click="choseImgAndSend"/>
    			<view class="send-btn" @click="sendMsg">发送</view>
    		</view>
    	</view>
    </template>
    
    <script>
    	export default {
    		data() {
    			return {
    				// 保存聊天的内容
    				chatList:[{
    					isMe: false,
    					type: 'txt',
    					content: '你好,我是可爱的知心姐姐,请问你想和我聊什么呢?'
    				},{
    					isMe: false,
    					type: 'img',
    					content: '/static/imgs/1.jpg'
    				},{
    					isMe: true,
    					type: 'txt',
    					content: '哇,你真漂亮'
    				},{
    					isMe: true,
    					type: 'img',
    					content: '/static/imgs/2.jpg'
    				}],
    				myInput:''
    			}
    		},
    		onShow(){
    			if(!!uni.getStorageSync('chatList')){
    				this.chatList = JSON.parse(uni.getStorageSync('chatList'))
    				uni.pageScrollTo({
    					scrollTop: 999999,
    					duration: 0
    				})
    			}
    		},
    		methods:{
    			choseImgAndSend(){
            // 选择图片功能
    				uni.chooseImage({
    					count: 1,
    					sizeType: ['original', 'compressed'],
    					sourceType: ['album', 'camera'],
    					success: res => {
    						console.log(res.tempFilePaths[0])
    
    						let senMsg = {
    							isMe: true,
    							type: 'img',
    							content: res.tempFilePaths[0]
    						}
    						this.chatList.push(senMsg)
    
    						let resMsg = {
    							isMe: false,
    							type: 'img',
    							content: res.tempFilePaths[0]
    						}
    						this.chatList.push(resMsg)
    
                // 聊天记录下拉到底部
    						uni.pageScrollTo({
    							scrollTop: 999999,
    							duration: 0
    						})
    
    						uni.setStorageSync('chatList', JSON.stringify(this.chatList))
    					}
    				})
    			},
    			sendMsg(){
    				if(!this.myInput) return
    				let senMsg = {
    					isMe: true,
    					type: 'txt',
    					content: this.myInput
    				}
    				this.chatList.push(senMsg)
    
    				let resMsg = {
    					isMe: false,
    					type: 'txt',
    					content: this.myInput
    				}
    				this.chatList.push(resMsg)
    
    				this.myInput = ''
    
    				uni.pageScrollTo({
    					scrollTop: 999999,
    					duration: 0
    				})
            // 联调记录保存到localStorage
    				uni.setStorageSync('chatList', JSON.stringify(this.chatList))
    			}
    		}
    	}
    </script>
    
    
    <style lang="scss" scoped>
    	.container {
    		background-color: #f1f1f1;
    		min-height: 100vh;
    	}
    
    	.chat-body {
    		padding-bottom: 178upx;
    
    		.chat-time {
    			text-align: center;
    			color: #888;
    			padding: 40upx 0 0;
    		}
    
    		.chat-one {
    			display: flex;
    			flex-direction: row;
    			flex-wrap: wrap;
    			justify-content: flex-start;
    			align-items: flex-start;
    			padding: 20upx 0;
    		}
    
    		.chat-one-begin {
    			padding: 40upx 0 0;
    		}
    
    		.chat-one-mine {
    			justify-content: flex-end;
    		}
    
    		.chat-face {
    			width: 84upx;
    			height: 84upx;
    			border-radius: 10upx;
    			margin-left: 40upx;
    		}
    
    		.chat-one-mine .chat-face {
    			margin-left: 0;
    			margin-right: 40upx;
    		}
    
    		.chat-box {
    			max-width: calc(100% - 290upx);
    			margin-left: 20upx;
    			font-size: 30upx;
    		}
    
    		.chat-one-mine .chat-box {
    			margin-right: 20upx;
    		}
    
    		.chat-sender {
    			color: #888;
    			font-size: 28upx;
    			margin-top: -8upx;
    			margin-bottom: 10upx;
    		}
    
    		.chat-content {
    			background-color: #fff;
    			border-radius: 5px;
    			padding: 10px;
    
    			.micon {
    				margin-right: 20upx;
    				color: #666;
    			}
    		}
    
    		.chat-img {
    			float: left;
    			max-width: 60%;
    			border-radius: 5px;
    		}
    
    		.chat-one-mine .chat-img {
    			float: right;
    		}
    	}
    
    	.chat-footer {
    		width: 670upx;
    		padding: 0 40upx;
    		height: 120upx;
    		position: fixed;
    		bottom: 0;
    		left: 0;
    		background-color: #f1f1f1;
    		display: flex;
    		flex-direction: row;
    		flex-wrap: wrap;
    		justify-content: space-between;
    		align-items: center;
    		align-content: center;
    		border-top: 1upx solid #ddd;
    
    		.msg-input {
    			background-color: #fff;
    			width: calc(100% - 300upx);
    			height: 70upx;
    			line-height: 70upx;
    			font-size: 30upx;
    			border-radius: 10upx;
    			padding: 0 20upx;
    		}
    
    		.img-chose{
    			height: 70upx;
    			width: 70upx;
    		}
    
    		.send-btn {
    			height: 60upx;
    			line-height: 60upx;
    			width: 120upx;
    			text-align: center;
    			background-color: green;
    			color: #FFFFFF;
    			border-radius: 12upx;
    		}
    
    	}
    </style>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258

    pages.json

    {
    	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
    		{
    			"path": "pages/index/index",
    			"style": {
    				"navigationBarTitleText": "uni-app"
    			}
    		}
    	    ,{
                "path" : "pages/404/404",
                "style" :                                                                                    
                {
                    "navigationBarTitleText": "",
                    "enablePullDownRefresh": false
                }
                
            }
            ,{
                "path" : "pages/list/list",
                "style" :                                                                                    
                {
                    "navigationBarTitleText": "",
                    "enablePullDownRefresh": false
                }
                
            }
            ,{
                "path" : "pages/me/me",
                "style" :                                                                                    
                {
                    "navigationBarTitleText": "",
                    "enablePullDownRefresh": false
                }
                
            }
            ,{
                "path" : "pages/finder/finder",
                "style" :                                                                                    
                {
                    "navigationBarTitleText": "",
                    "enablePullDownRefresh": false
                }
                
            }
        ],
        // 配置的分包代码
    	"subPackages": [{
    		"root": "subpages",
    		"pages": [{
                        "path" : "chat/chat",
                        "style": {
    		                    "navigationBarTitleText": "",
    		                    "enablePullDownRefresh": false
    		                }
                    }
                ]
    	}],
    	"globalStyle": {
    		"navigationBarTextStyle": "black",
    		"navigationBarTitleText": "uni-app",
    		"navigationBarBackgroundColor": "#F8F8F8",
    		"backgroundColor": "#F8F8F8"
    	},
    	"tabBar": {
    		"color": "#7A7E83",
    		"selectedColor": "#3cc51f",
    		"borderStyle": "black",
    		"backgroundColor": "#ffffff",
    		"list": [{
    			"pagePath": "pages/index/index",
    			"iconPath": "static/tabbar-icons/wx.png",
    			"selectedIconPath": "static/tabbar-icons/wx_s.png",
    			"text": "微信"
    		}, {
    			"pagePath": "pages/list/list",
    			"iconPath": "static/tabbar-icons/list.png",
    			"selectedIconPath": "static/tabbar-icons/list_s.png",
    			"text": "通讯录"
    		}, {
    			"pagePath": "pages/finder/finder",
    			"iconPath": "static/tabbar-icons/find.png",
    			"selectedIconPath": "static/tabbar-icons/find_s.png",
    			"text": "发现"
    		}, {
    			"pagePath": "pages/me/me",
    			"iconPath": "static/tabbar-icons/me.png",
    			"selectedIconPath": "static/tabbar-icons/me_s.png",
    			"text": "我"
    		}]
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    七、UniAPP 自定义组件与通信

    (1)自定义组件概念

    组件是 vue 技术中非常重要的部分,组件使得与ui相关的轮子可以方便的制造和共享,进而使得vue使用者的开发效率大幅提升,在项目的component目录下存放组件,uni-app 只支持 vue 单文件组件(.vue 组件)

    组件可以使用「全局注册」和「页面引入」两种方式进行使用,使用分为三步:

    导入 import xxx from 'xxx'

    注册 Vue.use('xx',xx) components:{ xxx }

    使用 <xx />

    (2)父子组件通信

    1. 父组件通过自定义属性向子组件传递数据

    2. 子组件通过 props 接收父组件传递的数据

    1. 父组件通过自定义事件标签向子组件传递事件
    2. 子组件通过触发父组件定义事件方式修改父组件数据

    (3)slot 数据分发与作用域插槽

    1. 父组件通过调用子组件内部嵌套 html 内容作为slot分发给子组件
    2. 子组件通过在 slot 标签上添加属性,向父组件通信数据,作用域插槽

    (4)全局事件定义及通信

    1. 在整个应用的任何地方均可以使用uni.$on创建一个全局事件
    2. 在整个应用的任何地方也均可以使用 uni.$emit 来触发全局事件,实现多组件见的数据通信

    八、UniAPP Vuex 状态管理

    1. 概念

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

    1. 应用场景

    Vue多个组件之间需要共享数据或状态。

    1. 关键规则
    • State:存储状态数据
    • Getter:从状态数据派生数据,相当于 State 的计算属性
    • Mutation:存储用于同步更改状态数据的方法,默认传入的参数为 state
    • Action:存储用于异步更改状态数据,但不是直接更改,而是通过触发 Mutation 方法实现,默认参数为context
    • Module:Vuex 模块化
    1. 交互关系
      请添加图片描述

    2. 使用方式

    import {
    		mapState,
    		mapActions
    } from 'vuex'
    export default {
    		computed: {
    			...mapState(['loginState', 'userInfo'])
    		},
    		methods: {
    			...mapActions(['userLoginAction', 'userLogoutAction']),
    		}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 体验案例:模拟用户登陆逻辑实现

    注意:配合使用 Storage 来实现刷新页面后状态持续保持的业务需求

    九、运行环境判断与跨端兼容

    (1)开发环境和生产环境

    uni-app 可通过 process.env.NODE_ENV 判断当前环境是开发环境还是生产环境,一般用于连接测试服务器或生产服务器的动态切换。

    在HBuilderX 中,点击「运行」编译出来的代码是开发环境,点击「发行」编译出来的代码是生产环境

    在任意地方都可以这样判断
    if(process.env.NODE_ENV === 'development'){
        console.log('开发环境')
    }else{
        console.log('生产环境')
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    (2)判断平台

    平台判断有2种场景,一种是在编译期判断,一种是在运行期判断。

    编译期判断编译期判断,即条件编译,不同平台在编译出包后已经是不同的代码,

    // #ifdef H5
     alert("只有h5平台才有alert方法")
    // #endif
    // 如上代码只会编译到H5的发行包里,其他平台的包不会包含如上代码。
    
    • 1
    • 2
    • 3
    • 4

    运行期判断 运行期判断是指代码已经打入包中,仍然需要在运行期判断平台,此时可使用 uni.getSystemInfoSync().platform 判断客户端环境是 Android、iOS 还是小程序开发工具

    switch(uni.getSystemInfoSync().platform){
       case 'android':
          console.log('运行Android上')
          break;
       case 'ios':
          console.log('运行iOS上')
          break;
       default:
          console.log('运行在开发者工具上')
          break;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    (3)跨端兼容

    uni-app 已将常用的组件、JS API 封装到框架中,开发者按照 uni-app 规范开发即可保证多平台兼容,大部分业务均可直接满足,但每个平台有自己的一些特性,因此会存在一些无法跨平台的情况。

    • 大量写 if else,会造成代码执行性能低下和管理混乱。
    • 编译到不同的工程后二次修改,会让后续升级变的很麻烦。

    在 C 语言中,通过 #ifdef、#ifndef 的方式,为 windows、mac 等不同 os 编译不同的代码。 uni-app 参考这个思路,为 uni-app 提供了条件编译手段,在一个工程里优雅的完成了平台个性化实现。

    条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。

    **写法:**以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。

    • \#ifdef:if defined 仅在某平台存在
    • \#ifndef:if not defined 除了某平台均存在
    • %PLATFORM%:平台名称

    %PLATFORM% 可取值如下:

    平台
    APP-PLUSApp
    APP-PLUS-NVUEApp nvue
    H5H5
    MP-WEIXIN微信小程序
    MP-ALIPAY支付宝小程序
    MP-BAIDU百度小程序
    MP-TOUTIAO字节跳动小程序
    MP-QQQQ小程序
    MP-360360小程序
    MP微信小程序/支付宝小程序/百度小程序/字节跳动小程序/QQ小程序/360小程序
    QUICKAPP-WEBVIEW快应用通用(包含联盟、华为)
    QUICKAPP-WEBVIEW-UNION快应用联盟
    QUICKAPP-WEBVIEW-HUAWEI快应用华为
  • 相关阅读:
    VT-VSPA1-508-1X/V0替代型模拟放大器
    杰理之关于提升 BLE 传输速率说明【篇】
    Spring源码分析-扩展点-配置文件自定义标签
    freeRTOS学习day4-中断使用消息队列
    0X JavaSE-- 并发编程()
    【读点论文】CMT: Convolutional Neural Networks Meet Vision Transformers
    算法基础 1.4 高精度 (加减乘除)
    [echarts] 图表网站 ppchart made a pie
    tcp/ip:记一次完整的数据包传输过程
    MySQL高可用九种方案
  • 原文地址:https://blog.csdn.net/weixin_38245947/article/details/125471681