uni-app 从0 到 1 制作一个项目,收藏等于学会

5,644 阅读15分钟

uni-app 是使用 vue.js 开发的所有前端应用框架,开发者编写的一套代码,可以发布到 ios、android、web ,以及各种小程序平台。

一、创建uni-app

1、hBuilderX 可视化创建

最便捷的就是使用 hbuilder 可视化创建项目,在点击工具栏里的文件 -> 新建 -> 项目:

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

2、cli 脚手架创建
2.1、使用正式版

vue create -p dcloudio/uni-preset-vue my-project

2.2、使用 alpha 版

vue create -p dcloudio/uni-preset-vue#alpha my-alpha-project

2.3、使用 vue3/vite 版

创建 js 开发的项目

npx degit dcloudio/uni-preset-vue#vite my-vue3-project

创建 typeScript 开发的xia

npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project

二、项目结构

以 uni-app + vue3 + vite + ts 项目为例,项目结构目录如图:

  • index.html 首页入口文件。
  • package.json 项目配置文件。
  • tsconfig.json typescript 配置文件。
  • vite.config.ts vite 的配置文件。

src 存放开发资源文件,基本要做的事情都在这个目录内,里面又包含了几个目录文件:

  • pages 存放所有页面文件,我们创建的页面组件都放入该文件夹。
  • static 存放静态资源的文件夹。
  • App.vue 页面入口文件,所有页面都在 App.vue 下进行切换。
  • env.d.ts 第三方模块的类型声明文件。
  • main.ts 项目入口文件,因为使用 ts 语法,所以后缀是 .ts
  • mainfest.json 应用配置文件,用于指定应用名称、图标、权限等。
  • pages.json 全局配置文件,可以配置页面文件路径、窗口样式、原生的导航栏、底部tab栏等。
  • uni.scss 是uni-app的样式包,在其他文件中可以快速引用样式包内的样式。

三、语法:

为了实现多端兼容,也综合考虑到编译速度,运行性能等因素,uni-app 约定了以下开发规范:

1、页面文件遵循 vue 单文件组件写法

2、所有需要的标签接近小程序的规范

3、API接口靠近微信小程序(路由、上传下载、网络请求)

4、数据绑定及事件绑定处理遵循 vue规范

5、生命周期可以同时兼容小程序和VUE的生命周期

6、样式、为了兼容建议使用弹性布局

四、运行 uni-app 项目

1、运行 web端

2、运行小程序端

2.1、先配置小程序的运行路径:“运行”-->"运行到小程序模拟器"-->将微信开发者工具
的正确路径填入

2.2、如果以上配置仍然无法运行,需要打开微信开发者工具的安全端口,在微信开发者
工具“设置”--> “安全” --> 打开服务器端口

2.3、最后在 hBuilder 里运行到小程序模拟器

3、运行 app 端(android)

3.1、我们运行到的是 mumu 模拟器。需要先设置 abd 路径,选择
hBuilderX/plugins/launcher/tools/adbs/adb.exe 的路径。

3.2、设置 mumu 模拟器的启动端口为 7555

3.3、最后在 hBuilderX 里运行到手机模拟器

五、组件

uni-app 里的组件也是后缀为 .vue 的文件:
4.1、可以沿用 vue 组件使用的步骤,先创建,再注册,最后使用
4.2、也可以直接使用,即不需要导入、注册,直接使用
4.2、或者直接使用 uni-app 的内置组件,这部分组件与微信小程序的组件类似
4.4、如果以上方式还是无法满足,可以使用 uni-app 提供的拓展组件,直接去组件页面导入到项目中,导入的扩展组件会存放在 uni-modules 文件夹,使用时不需要导入、注册,直接使用(注意:有些组件可能依赖其他插件,按照提示安装即可)

六、条件编译

因为 uni-app 需要跨平台,而不同平台下有自己的特殊代码,我们为了进行平台兼容,需要区分不同的平台,进而进行不同的代码 uni-app 借鉴了 c 语言里的 #ifdef 或者 #ifndef 的条件编译语法来实现不同平台的兼容。
写法:** 以 #ifdef 或 # ifndef 加 %platform% 开头,以 #end 结尾
5.1、#ifdef : if defined 仅在某平台存在
5.2、#ifndef : if not defined 除了某平台均存在
5.3、%platform% : 平台名称
使用示例:

<!-- #ifdef APP-PLUS -->
展示在 APP 端的内容
<!-- #endif -->

<!-- #ifndef H5 -->
除了 h5 之外,其他端都展示
<!-- #endif -->

<!-- #ifdef H5 || MP-WEIXIN  -->
展示在 h5 和 微信小程序端
<!-- #endif -->

支持的文件:

  • .vue
  • .js
  • .css
  • pages.json
  • 各预编译语言文件,如:.scss、.less、.stylus、.ts、.pug

static 目录的条件编译:
在 static 文件夹里面再新建对应平台的子文件夹,可以让 uni-app 在编译的时候选择对应的静态资源。

七、网络请求

uni-app 中直接提供了 uni.request 接口,可以直接发送网络请求。

uni.request({
 url: "https://www.example.com/api/user/login",
 method: "POST",
 data: {
  userName: 'test',
 },
 success: (res) => {
  console.log("请求成功", res)
 },
 fail: (err) => {
  console.log("请求失败", err)
 }
})

具体的配置参数可以参考官网:
uniapp.dcloud.net.cn/api/request…

发送网络请求的时候,往往会出现跨域问题,uni-app 官方给出了三种解决方案:

1、使用 HBuilderX 内置浏览器

HBuilderX 的内置浏览器是经过官方处理的,不存在跨域问题,在运行菜单里选择运行到内置浏览器就可以看到。

此方法我们不需要任何处理,但是如果想多看几个浏览器是否兼容的时候,还是会存在跨域问题。

2、使用 proxy 代理

方式1:在 mainfest.json 内配置

// manifest.json
{
    "h5": {
        "devServer": {
            "proxy": {
                "/prefix/api/user/list": {
                    "target": "https://api-remote.xxxx.com",
                    "pathRewrite": {
                        "^/prefix": ""
                    }
                }
            }
        }
    }
}

方式2:vue.config.json 内配置

// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      '/prefix/api/user/list': {
        target: 'https://api-remote.xxxx.com',
        pathRewrite: {
          '^/prefix': ''
        }
      }
    },
  }
}

注意:这两种方式不能同时使用,mainfest.json 会覆盖 vue.config.json 配置。

如果使用的是 vite + uni-app时,需要配置的 vite.config.json 与 vue.config.json 写法上有一点点区别:

vite.config.json

import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
export default defineConfig({
 plugins: [
  uni(),
 ],
 server: {
  proxy: {
	 '/api': {
	  target: "https://test.com",
	 	rewrite: (path) => path.replace(/^/api/, '/'),
	 	changeOrigin: true,
	 	secure: false
	 }
	},
 }
})

八、nvue (native vue)介绍

因为 uni-app 需要提供多端打包,所以其提供了两种渲染引擎,即基于前端webview渲染和基于 weex 的原生渲染,只有在 app 端 uni-app 才会根据页面的后缀不同,启用不同的渲染方式进行页面内容的渲染。

nvue 虽然也可以输出 H5 或者小程序端,但是因为其 css 写法极其受限,所以如果将来我们在用 uni-app 开发时,不需要兼容 app 端,就不需要使用 nvue。但也不意味着如果需要兼容 APP 端,所有页面都要创建成 nvue 形式,我们可以根据具体情况选择某些页面为 nvue。vue 和 nvue 文件可以混用,首页或者内容多的页面设置为 nvue,其他页面使用 vue 文件。

如果一个页面路由下同时有 vue 页面和 nvue 页面,即出现同名的 vue 和 nvue 文件。那么在 App 端,会仅使用 nvue 页面,同名的 vue 文件将不会被编译到 App 端。而在非 App 端,会优先使用 vue 页面。
weex 编译模式和 uni-app 编译模式,可以通过配置设置:

manifest.json

{
  // App平台特有配置
  "app-plus": {
    "nvueCompiler":"uni-app" || "weex" //是否启用 uni-app 模式
  }
}

uni-app默认内置集成原生模块,BindingX,animation, DOM.addRule 。通过 uni.requireNativePlugin 引入 App 原生插件,常见的原生插件:

  • dom
  • addRule
  • scrollToElement
  • getComponentRect
  • animation

九、生命周期

uni-app 的生命周期可以分为三类:

  • 应用程序的生命周期
  • 页面的生命周期
  • 组件的生命周期

1、应用程序生命周期

应用程序的生命周期,一般我们直接使用,不会声明,应用程序的生命周期只能写到 App.vue 里,在其他页面里监听无效。

App.vue 为 uni-app 的主组件,是入口文件,所有的页面切换均在 App.vue 下进行,其本身不是页面,所以在该文件中没有 template 模板,不能编写视图。一般情况下,我们会在 uni-app 的 App.vue 文件里进行如下操作:

  • 调用应用程序级别的生命周期函数
  • 配置全局的 CSS 样式
  • 设置全局需要存储的 globalData,然后可以在任意的位置通过 getApp() 获取到uni-app 小程序实例,进而拿到 globalData
onLaunch: function() {
  //当 uni-app 程序初始化完成时触发,在整个程序运行期间只执行一次
 console.log('App Launch')
},
  
onShow: function() {
 // 当 uni-app 启动,从后台进入前台时触发 
 console.log('App Show')
},
onHide: function() {
 // 当 uni-app 从前台进入到后台时触发
 console.log('App Hide')
}

2、页面的生命周期

页面的生命周期,参考微信小程序的生命周期函数,其特点有:

2.1、页面初始化时,首先触发的是 onLoad、onShow、onReady 三个生命周期函数

2.2、这三个生命周期函数里面均可获得 data 内的数据

2.3、通过 tabbar 切换页面,不管是小程序还是H5、APP端,页面都不会被销毁,只会触发 onHide ,该现象遵循微信小程序

2.4、通过 navigator 进入详情页,依然遵循微信小程序,即当前页触发 onHide,详情页触发 onLoad、onShow、onReady,从详情页返回时,详情页触发 onUnload,页面被销毁。

<script setup>
	import {
		onLoad,
		onShow,
		onReady,
    onHide,
    onUnload
	} from "@dcloudio/uni-app"
	onLoad(() => {
		console.log("onload被执行")
	})
	onShow(() => {
		console.log("onShow被执行")
	})
	onReady(() => {
		console.log("onReady被执行")
	})
</script>

3、组件的生命周期

组件的生命周期,参考 vue 的标准生命周期,可自行查看 vue2 和 vue3 的生命周期函数不同。

3.1、当组件所在的页面被加载时,先触发页面的 onLoad、onShow ,然后再执行组件的 beforeCreate、created、beforeMount、mounted ,最后触发页面的 onReady 函数。

3.2、当组件所在的页面被销毁时,先触发页面的 onUnload ,之后触发组件的 beforeDestory 和 destroyed ,最后一级页面才触发 onShow

总结:

page页面建议使用微信小程序的生命周期函数

component 组件建议使用vue的生命周期函数

不建议混用

十、第三方授权登录

常见的三方登录有:

  • 微信授权登录
  • QQ 授权登录
  • 新浪微博登录
  • facebook 登录
  • 账号密码或验证码登录等等

三方授权登录是如何实现的呢?以微信授权登录为例:

APP 微信授权登录需要在微信公众平台申请,注册开发者账号,获得相应的 AppId 和 AppSecret ,在 hBuilderX 的 mainfest.json 配置微信登录的 AppId ,另外需要跟小程序、公众号授权账号互通的话,也需要在微信开放平台申请。

微信开放平台:
open.weixin.qq.com/

App 端登录相关的 SDK 需要在 mainfest 中配置:

  1. 打开 manifest.json -> App模块配置,勾选 OAuth(登陆鉴权)。查看到登陆鉴权。在说明中有蓝色链接,其中包括向微信、QQ、微博等平台申请 sdk 的链接。
  2. 向微信、QQ、微博等平台申请到sdk的信息后,回填到 manifest.json 里。
  3. 这些配置需要打包生效,真机运行仍然是HBuilder基座的设置,可使用自定义基座包。离线打包请参考离线打包文档在原生工程中配置。
  4. 配置并打包后,通过 uni.getProvider() 可以得到配置的结果列表,注意这里返回的是 manifest.json 配置的,与手机端是否安装微信、QQ、微博无关。

如果手机端未安装QQ、微信、微博 调用时会启动这些平台的wap页面登陆,如果已安装相应客户端,会启动它们的客户端登陆

以下是用户授权登录的方法:

function userLogin(type) {
 // 获取服务供应商
 uni.getProvider({
  service: 'oauth',
   success: (res) => {
    // 判断是否存在供应商,存在时 发起授权请求
    if (res.provider.indexOf(type) != -1) {
        uni.login({
         provider: type,
         success: (res) => {
            //获取用户信息
            uni.getUserInfo({
                provider: type,
                success: (info) => {
                 //用户信息处理
                 console.log("info", info)
                }
             })
           },
           fail: (err) => {
             uni.showToast({
           	title:"登录失败",
                duration:3000
               })
            }
	})
    }
  }
 })
}

十一、第三方框架使用

目前支持 vue3 的框架比较多,此处我们以 uView 为例:

支持Vue3 的 uView 框架,官网地址:
vkuviewdoc.fsq.pub/components/…

1、安装:

npm i vk-uview-ui

2、在 main.js 中全局引入:

// 引入 uView
import uView from 'vk-uview-ui';

import { createSSRApp } from 'vue'
export function createApp() {
  const app  = createSSRApp(App)
  
  // 使用 uView UI
  app.use(uView)
  
  return { app }
}

3、引入全局样式

在 app.vue 入口文件内加入全局样式:

@import "vk-uview-ui/index.scss";

4、引入全局 scss 变量文件, uni.scss 文件内加入

@import "vk-uview-ui/theme.scss";

5、pages.json 文件中写入 easycom 规则(插件市场导入方式不需要)

"easycom": {
 "autoscan": true,
 "custom": {
  "^u-(.*)": "vk-uview-ui/components/u-$1/u-$1.vue"
 }
}

此时就能够正常使用了。该框架能够满足移动端常见的布局组件,如果我们需要做可视化数据展示的时候,就有些不能满足需求了,在 PC 端,我们经常使用的是 Echarts,但是由于 echart 涉及大量 dom 操作,无法跨端使用,如果需要考虑小程序就不能使用 Echarts 了。

DCloud 插件市场提供了很多类似的框架,可以自行选择一个符合自己项目需求的。

地址:
ext.dcloud.net.cn/search?q=%E…

推荐使用全端可用的 uChart ,提供了丰富多样的图表。

uCharts是一款基于canvas API开发的适用于所有前端应用的图表库,开发者编写一套代码,可运行到 Web、iOS、Android(基于 uni-app / taro )、以及各种小程序(微信/支付宝/百度/头条/飞书/QQ/快手/钉钉/淘宝/京东/360)、快应用等更多支持 canvas API 的平台。

1、使用 npm 安装,或只获取 u-charts.js 或 u-charts.min.js 单文件。

npm i @qiun/ucharts

2、使用时引入 uCharts

import uCharts from "@qiun/ucharts"

绘制一个雷达图,如图所示:

代码示例:

<template>
 <view>
  <canvas canvas-id="ss" id="ss" class="charts" @touchend="tap" />
 </view>
</template>

<script setup>
 import uCharts from "@qiun/ucharts"
 var uChartsInstance = {};
 import { ref, onMounted } from "vue"
 let cWidth = ref(750)
 let cHeight = ref(500)
 onMounted(() => {
  //这里的 750 对应 css .charts 的 width
  cWidth.value = uni.upx2px(750);
  //这里的 500 对应 css .charts 的 height
  cHeight.value = uni.upx2px(500);
  getServerData();
 })
 function getServerData() {
  //模拟从服务器获取数据时的延时
  setTimeout(() => {
   //模拟服务器返回数据,如果数据格式和标准格式不同,需自行按下面的格式拼接
	let res = {
	 categories: ["维度1", "维度2", "维度3", "维度4", "维度5", "维度6"],
	 series: [{
		 name: "成交量1",
		 data: [90, 110, 165, 195, 187, 172]
	  },
		{
			name: "成交量2",
			data: [190, 210, 105, 35, 27, 102]
		}
	]
 };
 drawCharts('ss', res);
 }, 500);
}

 function drawCharts(id, data) {
  const ctx = uni.createCanvasContext(id, this);
  uChartsInstance[id] = new uCharts({
   type: "radar",
   context: ctx,
   width: cWidth.value,
   height: cHeight.value,
   categories: data.categories,
   series: data.series,
   animation: true,
   background: "#FFFFFF",
   color: ["#1890FF", "#91CB74"],
	 padding: [5, 5, 5, 5],
	 dataLabel: false,
	 legend: {
	  show: false,
	  position: "right",
	  lineHeight: 25
	 },
	 extra: {
		 radar: {
			gridColor: "rgba(0,0,0,0.1)",
			gridCount: 3,
			opacity: 0.6,
			max: 200
		 }
		}
	});
}
 function tap(e) {
	uChartsInstance[e.target.id].touchLegend(e);
	uChartsInstance[e.target.id].showToolTip(e);
 }
</script>

<style scoped>
	.charts {
		width: 100%;
		height: 500rpx;
	}
</style>

十二、打包到各端

1、 H5、WEB 端打包

1.1、通过 “发行 -> 网站PC WEB 或手机 H5,点击“发行”。

1.2、如果直接失败,可能是因为没有 uni-app 的 appid,需要对应项目的mainfest.json文件里的基础配置,去设置 uni-app 的 appid。

1.3、设置完毕 appid之后可以打包成功,打包成功的包会存在当前项目的 unpackage 文件夹里的 dist 文件夹里。

1.4、注意:默认打包完毕的项目需要部署到 web 服务器使用,不要使用资源管理器直接打开,除非进行相对路劲配置。

1.5、如果需要配置相对路径,还需要去当前项目的 mainfest.json 文件里的 H5 选项卡里的配置路径里,设置为”./“

1.6、H5 选项里除了可以配置基础路径之外,还可以配置项目标题,路由模式、https 协议设置、端口号打包的摇树优化、地图配置等。

2、Android 端 apk 包

2.1、通过发行 -> 原生 APP 云打包。

2.2、Android 需要开发者证书,我们可以点击“如何生成证书”,地址为:
ask.dcloud.net.cn/article/357… keystore 的文件)

2.3、可以选择打开渠道包(例如:华为、小米、VIVO等),也可以只打通用包 .android 的安装包后缀为 apk

2.4、根据需要决定勾选代码混淆,是否选择广告推送。

2.5、打包需要个人登录 hBuilderX 账号需要验证,默认会提示勾选 x86cpu,我们需要在 mainfest.json 文件里的常用其他配置,勾选 x86;默认会提示需要遵循中国的消费者隐私协议,需要在 mainfest.json 文件里源码视图里的 app-plus 里增加 privacy 字段。

"app-plus" : {
  "privacy" : {
    "prompt" : "template",
     "template" : {
       "title" : "",
       "message" : "",
       "buttonAccept" : "我知道了",
       "buttonRefuse" : "暂不注册"
       }
     },
  "usingComponents" : true,
}

3、小程序端

打包微信小程序之前,需要先去微信公众平台注册账号,申请一个小程序,然后在开发设置里找打小程序的 appId。

3.1、通过发行 -> 小程序-微信

3.2、需要填写用的小程序 appId

3.3、也可以设置高级选项,即 mainfest.json 的微信小程序配置选项卡里进行高级的打包配置,如 ES6 转 ES5 ,代码样式补全,代码自动压缩,检查安全域名,还可以配置微信小程序的位置接口。

3.4、最终打包完成的微信小程序包会存储在当前项目的 unpackage 文件夹里的 dist 文件夹内,包名叫 mp-weixin 。

3.5、打包完成后微信小程序需要通过微信开发者工具将小程序上传到微信公众平台,在版本管理可查看上传的小程序。

4、基于uni-app的微信小程序分包

分包的原因:当小程序的项目比较大时,项目的页面过多或者图片等静态资源过多时,小程序的单包大小限制为 2M ,如果超过时,需要考虑分包。

微信小程序的分包标准:不超过 16 个分包,单个包或者主包大小不超过 2M ,总体积一共不能超过20M。

分包的基本规范:

  • 主包里面一般存储 tabbar 或者即时展示的 page ,主包默认存在,即 pages 文件夹。
  • 分包存储详情页或者更深的页面,注意 tabbar 对应的页面不可以打包到分包里。

分包基本步骤:

4.1、在开发启动之前设计好分包的数量,以及子包名与放置 page 和对应的 static 静态资源。

4.2、子包的 page 不要再 pages 里注册了,需要使用 subpackages 字段进行注册。

4.3、subpackages 是一个数组,每个子包的配置信息都是一个对象,每个对象里都有两个字段:root 表示子包的相对路径,pages 子包的页面配置信息,可以参考主包的 pages 配置,注意:子包的 path 路径是相对于 root 进行配置的。

"subPackages": [
 {
  "root": "pageA",
  "pages": [{
   "path": "detail/index",
   "style": {
    "navigationBarTitleText": "pageA"
		}
	}]
 }
]

4.4、通过 preloadRule 进行分包预加载设置,该字段值为一个对象,key 是每个分包的页面路径,value 是一个对象,对象里有两个字段:network 表示什么网络下可以预加载分包,有 all 、wifi。packages 表示进入页面后预下载分包的 root 或 name,APP 表示主包。

4.5、开启分包优化,需要在对应平台的配置下添加"optimization":{"subPackages":true}开启分包优化,在 mainfest.json 源码视图内,找到 “mp-weixin”,添加 optimaztion 。

"mp-weixin": {
		"appid": "************5843",
		"optimization": {
			"subPackages": true
		}
}
  • 目前只支持mp-weixin、mp-qq、mp-baidu、mp-toutiao的分包优化。