Learning Uni-App Quickly

Uni-app是一个开发跨平台应用的前端框架,开发者编写一套代码,就能编译到Android、iOS、H5、微信小程序、百度小程序、支付宝小程序、头条小程序等七个平台。app可以分为两种,web app和移动app(Android和iOS),打算开发Android app,踩了大概一周多的坑,我最终决定使用uni-app,uni-app是前端开发的一个比较容易学习的框架,而且拥有一定的web开发基础知识会更加顺手,因此本文采用了html→css→JavaScript→vue.js→uni-app的学习路线。

首先简单说明一下前端在整个项目中的地位,以下图的搜狗图片网站为例,现在我们开发项目一般都采用前后端分离的思想。所谓前端,简单理解就是用户能看得到的任何东西,比如说这个网站里的搜索框、框的位置颜色大小、网站的背景图、左上角的“新闻”按钮、按钮的位置颜色大小、按钮点击后跳转到哪个页面等等,都属于前端的范畴;所谓后端,可以理解为前端的底层支持,比如说动态数据,为什么“精选推荐”里显示这些文章、为什么“大家在关注”里显示这些链接,都是后端实现的。后端可以分为数据库和数据库以外的部分,数据库负责存储数据,数据库以外的部分负责管理数据(即对数据库表的增删改查)。前端向后端发起一个请求,后端解析这个请求,然后向数据库查询需要的原始数据,再经过后端逻辑的处理,把得到的动态结果返回给前端,再由前端最终展示给客户,这就是我们常说的MVC(Model-View-Controller)模式——模型(数据库)、视图(前端)、控制器(对数据库的处理)分离。因此前端开发就属于整个项目的MVC中V的部分,它要解决两个问题:给用户展示什么、向后端发起什么请求。

Uni-app是一款比较成熟的开发框架,但对完全没有前端开发基础的学习者来说还是不太友好,因此本文虽然着重介绍uni-app的开发,还是选择按照html→css→JavaScript→vue.js→uni-app的路线循序渐进。当然,本文目标还是让读者快速上手Uni-app,所以对前面的四个步骤只是简要介绍,略去了无关主线的部分,下面依次介绍这五种技术。

Html,即超文本标记语言,可以用来编写一个静态网页。使用html,可以规定这个网页的哪个位置有按钮、哪个位置有文本、按钮的颜色是什么、文本的字体是什么等等。这里的静态网页指的是编写出来的网页是固定的,数据是写死的,每次打开这个网页显示的都是同一份内容,同时网页也不会对用户的操作做出任何反应。
Html的基本语法只有一个,就是“<标签名 属性名=属性值 属性名=属性值...>内容”,比如说前面“搜狗图片”的网页,里面的每个按钮、每张图片、每个超链接,都分别是一个标签。Html的一个常见格式如下:

  1. <!DOCTYPE html>
    
  2. <html>
    
  3.     <head>
    
  4.         <title>浏览器标签文字</title>
    
  5.         <meta charset="utf-8" />
    
  6.     </head>
    
  7.     <body bgcolor="#000000" text="#FFFFFF">
    
  8. 	<p>换<br>行</p>
    
  9.         <a href="http://amiya.vip" target="_blank">别点我</a>
    
  10. 	<br/>
    
  11. 	<input type="radio" name="id1" checked />单选1<input type="radio" name="id1" />单选2
    
  12. 	<input type="checkbox" name="id2" />复选1<input type="checkbox" name="id2" />复选2
    
  13. 	<br/><br/>
    
  14. 	<input type="text" value="文本输入" size="30px" maxlength="12" />
    
  15.     </body>
    
  16. </html>

第一行的“”是一个声明,告诉浏览器使用的html版本,初学者可以忽略。网页由html标签包裹,其中有两个子标签head和body。head中定义通用的格式,这里定义了浏览器标签栏显示的文字和字符编码格式;body中定义网页的主体部分,即显示在浏览器页面中的所有组件。初学者可以直接copy以下的html模板:

  1. <!DOCTYPE html>
    
  2. <html>
    
  3.     <head>
    
  4.         <title></title>
    
  5.         <meta charset="utf-8" />
    
  6.     </head>
    
  7.     <body>
    
  8.  
  9.     </body>
    
  10. </html>

最新的html5将一些内容只能为空的标签规定为单标签“<标签名 属性名=属性值 属性名=属性值... />”,并不再允许使用“<标签名 属性名=属性值 属性名=属性值...>内容”,例如换行的br。
这里给出常用的html标签、子标签和属性

CSS,即层叠样式表,简单理解就是重新整合了html代码,把可以重用的部分合并,使代码更简洁优雅。为了做到这一点,CSS引入了一些新的标签和新的属性。CSS引入了id和class两个属性,id唯一标识一个标签,class标识一类标签,CSS主要提供了三种选择标签的方式:用html标签名选中所有同名标签、用class属性值选中一类同值标签、用id属性值选中单个特定标签,CSS还引入了div标签,它没有实际含义,仅作为容器元素,用于把一系列相关的标签嵌套为一个整体统一定义属性,以下代码展示了CSS选择器的使用:

  1. <!DOCTYPE html>
    
  2. <html>
    
  3.     <head>
    
  4.         <title>CSS</title>
    
  5.         <meta charset="utf-8" />
    
  6. 		<style type="text/css">
    
  7.  
  8. 			<!--
    
  9. 				引入外部css文件
    
  10. 				<link rel="stylesheet" type="text/css" href="../../style.css" />
    
  11. 			-->
    
  12.  
  13. 			/* 通用选择 */
    
  14. 			* {
    
  15. 				color:white;
    
  16. 				background-color:black;
    
  17. 			}
    
  18.  
  19. 			/* 按标签名选择 */
    
  20. 			h1 {
    
  21. 				color:red;
    
  22. 			}
    
  23.  
  24. 			/* 同时选择两类标签 */
    
  25. 			h2,h3 {
    
  26. 				color:white;
    
  27. 			}
    
  28.  
  29. 			/* 选择嵌套子元素用 */
    
  30. 			.a h4 {
    
  31. 				color:red;
    
  32. 				font-size:30px;
    
  33. 			}
    
  34.  
  35. 			/* 按id名选择用(#) */
    
  36. 			#id1 {
    
  37. 				color:yellow;
    
  38. 				font-size:18px;
    
  39. 				background-color:green;
    
  40. 			}
    
  41.  
  42. 			/* 按class名选择用(.) */
    
  43. 			.aaa {
    
  44. 				color:green;
    
  45. 			}
    
  46.  
  47. 		</style>
    
  48.     </head>
    
  49.     <body bgcolor="#000000" text="#FFFFFF">
    
  50. 		<div>
    
  51. 			<h1>标题1</h1>
    
  52. 			<h1>标题2</h1>
    
  53. 			<h2>副标题</h2>
    
  54. 			<h3>正文</h3>
    
  55. 			<div class="a">
    
  56. 				<h4>
    
  57. 					h4
    
  58. 					<h5>h5</h5>
    
  59. 				</h4>
    
  60. 			</div>
    
  61. 			<p id="id1">p</p>
    
  62. 			<h6 class="aaa">h6</h6>
    
  63. 			<h6 class="aaa">我也是h6</h6>
    
  64. 		</div>
    
  65.     </body>
    
  66. </html>

因为html标签是嵌套使用的,选择一个标签往往还会选上它的子标签,因此有的标签会被多个选择器选中,这时选择器的优先级服从id>class>html标签、嵌套时内层优先的原则。此外CSS还能引入外部的css文件,外部CSS优先级始终低于本文件中定义的CSS。CSS还提供了超链接选择器,用于定义超链接颜色:a:link(未点击),a:hover(鼠标悬停),a:active(点击时),a:visited(点击过)。
用一句话总结,CSS主要有两个功能:第一,如果一个标签定义许多个属性,可以提取出来定义在标签外,增加代码清晰性和可读性;第二,如果多个标签定义相同的样式,可以提取出来定义在同一处,增加代码可重用性和可维护性,而且多个文件共用的样式还可以单独在一个.css文件中定义。

JavaScript是一种解释型语言,可通过script标签嵌入html代码,作用是修改样式,给网页添加交互、功能。JavaScript的核心概念是事件,所谓事件指的就是用户操作(包括鼠标、键盘等)。JavaScript给控件标签提供了onclick、onmouseover、onmouseout等属性,分别表示点击、鼠标进入、鼠标移出的事件,其属性值是JavaScript函数,JavaScript通过它们监听用户操作来修改样式。下面这段代码展示了JavaScript的基本语法。

  1. <!DOCTYPE html>
    
  2. <html>
    
  3. 	<head>
    
  4.         <title>JS鼠标提示框</title>
    
  5.         <meta charset="utf-8">
    
  6. 		<style type="text/css">
    
  7. 			.div0 {
    
  8. 				float:left;
    
  9. 			}
    
  10. 			#div2 {
    
  11. 				width:200px;
    
  12. 				height:100px;
    
  13. 				background-color:#CCCCCC;
    
  14. 				border:1px solid;
    
  15. 				display:none;
    
  16. 			}
    
  17. 		</style>
    
  18. 		<script>
    
  19. 			window.onload = function()
    
  20. 			{
    
  21. 				var oDiv1 = document.getElementById("div1");
    
  22. 				var oDiv2 = document.getElementById("div2");
    
  23. 				oDiv1.onmouseover = function() {
    
  24. 					oDiv2.style.display = "block";
    
  25. 				}
    
  26. 				oDiv2.onmouseout = function() {
    
  27. 					oDiv2.style.display = "none";
    
  28. 				}
    
  29. 			}
    
  30. 		</script>
    
  31.     </head>
    
  32.     <body>
    
  33. 		<div>
    
  34. 			<div>
    
  35. 				<input type="checkbox" class="div0">
    
  36. 				<div id="div1" class="div0">自动登录</div>
    
  37. 			</div>
    
  38. 			<br>
    
  39. 			<div id="div2" style="display: block;">
    
  40. 				为了您的信息安全,请不要在公共电脑上选择此项
    
  41. 			</div>
    
  42. 		</div>
    
  43. 	</body>
    
  44. </html>

JavaScript,简称js,它的语法和其他编程语言很像,变量是弱类型的,变量声明使用var,支持if、for、while、switch语句。JavaScript还提供了定时器:setInterval(func,time)返回定时器对象,每隔time毫秒调用一次func函数;setTimeout(func,time)返回定时器对象,在time毫秒后调用一次func函数,clearInterval(timer)和clearTimeout(timer)传入定时器并还原。

现在重新整合一下html、css和JavaScript之间的关系,html规定了一个web页面上的所有组件,以及它们的位置和样式,css是对html的优化,html和css共同完成了静态页面的编写,js通过处理用户交互,对不同的交互执行不同的事件,完成对静态页面的样式的修改,使静态页面“运动”起来,就能基本实现我们日常生活中看到的页面效果。html+css+js对整个页面进行了结构(html)、样式(css)和行为(JavaScript)的分离,现在的开发基本都遵从分离原则,分离能让项目不至于混乱,出现问题更容易定位,从前面讲到的整个项目的M-V-C分离,到View中的结构-样式-行为分离,都是分离原则的体现。
那么还要vue.js和uni-app干嘛呢?vue.js和uni-app不是一门新的语言,而是基于html+css+js的框架,什么是框架呢,初学者可以用库类比。库是每个程序员都用过的东西,不论经验丰富与否,比如我们在C++中include的头文件、C++提供的STL标准模板库,就是很常见的库,库可以理解为前人写好的、可供我们按需调用的工具。至于框架,与库很类似,它也是前人写好的、供我们按需使用的,不过不是小工具,而是一整套规则。使用库时,库只占项目的一小部分,我们可以比较随意地使用和替换;而框架不同,一旦我们选定了框架,在开发过程中如果想要替换框架,往往要重构整个项目,因为我们是在框架的基础上写项目、在项目的基础上使用库。
既然框架凌驾于整个项目之上,学习框架就要首先熟悉框架规定好的项目结构。vue.js和uni-app是两个框架,它们的项目结构肯定不同,我之所以把vue.js放在这条路线中,是因为uni-app是基于vue的,完美兼容vue的绝大多数语法,但本文主要是介绍uni-app框架,因此在vue.js中只涉及一些比较重要的语法,而在uni-app中花更多篇幅介绍项目结构(相当于我们只使用了vue的库而非框架的功能)。

使用vue.js,首先需要在head标签中导入网络资源库,代码如下:

  1. <script src="https://unpkg.com/vue/dist/vue.js"></script>

你也可以下载到本地直接导入,当然可以忽略这一步,因为最终使用uni-app的开发工具帮我们完成了这个工作。
Vue.js在html+css的基础上新增了一些属性,包括v-if、v-show、v-for、v-bind、v-model等,还把js的代码重新整合,分门别类放置在data、methods、filters等对象里,如下面的代码所示:

  1. <!DOCTYPE html>
    
  2. <html>
    
  3.     <head>
    
  4.         <title>vue.js</title>
    
  5.         <meta charset="utf-8" />
    
  6. 		<style>
    
  7. 			* {
    
  8. 				margin:0;
    
  9. 				padding:0;
    
  10. 			}
    
  11. 			#div {
    
  12. 				color:#FF0000;
    
  13. 				font-size:320px;
    
  14. 				font-family:Microsoft YaHei;
    
  15. 			}
    
  16. 		</style>
    
  17. 		<script src="https://unpkg.com/vue/dist/vue.js"></script>
    
  18.     </head>
    
  19.     <body>
    
  20. 		<div id="myapp">
    
  21. 			{{ message }}
    
  22. 			<br/>
    
  23. 			<!-- filter用于格式化数据 -->
    
  24. 			{{ message | doubleText }}
    
  25. 			<!-- v-on:被简写作@(v-on:click="btnClick") -->
    
  26. 			<button id="btn" @click="btnClick()">别点我</button>
    
  27. 			<br/><br/>
    
  28. 			<div id="div" v-if="flag">哼</div>
    
  29. 			<ul>
    
  30. 				<li v-for="(item,index) in items">
    
  31. 					{{ item.name }}的价格: {{ item.price }}元
    
  32. 					<button @click="increasePrice(item)">涨价1元</button>
    
  33. 				<li>
    
  34. 			</ul>
    
  35. 		</div>
    
  36. 		<script>
    
  37. 			var myApp = new Vue({
    
  38. 				el:"#myapp",
    
  39. 				data:{
    
  40. 					flag:false,
    
  41. 					message:"Hello Vue.js",
    
  42. 					items:[
    
  43. 						{name:"苹果",price:2},
    
  44. 						{name:"香蕉",price:3},
    
  45. 						{name:"橘子",price:1}
    
  46. 					]
    
  47.  
  48. 				},
    
  49. 				methods:{
    
  50. 					btnClick:function() {
    
  51. 						var oBtn = document.getElementById("btn");
    
  52. 						if(this.message == "Hello Vue.js") {
    
  53. 							this.flag = true;
    
  54. 							this.message = "点我干嘛";
    
  55. 							oBtn.innerHTML = "你再点我一下试试?";
    
  56. 						}
    
  57. 						else {
    
  58. 							this.flag = false;
    
  59. 							this.message = "Hello Vue.js";
    
  60. 							oBtn.innerHTML = "别点我";
    
  61. 						}
    
  62. 					},
    
  63. 					increasePrice:function(item) {
    
  64. 						++item.price;
    
  65. 					}
    
  66. 				},
    
  67. 				filters:{
    
  68. 					doubleText:function(value) {
    
  69. 						return value + " " + value + "!";
    
  70. 					}
    
  71. 				}
    
  72. 			});
    
  73. 		</script>
    
  74.     </body>
    
  75. </html>

注意vue.js中把JavaScript改写在Vue对象里,用来控制el所选中的区域,如上面这段代码el的值是#myapp,说明这个Vue实例是控制id为myapp的整个div。data中声明了程序需要用到的变量;methods中声明了原来的JavaScript函数,处理后端传回的数据并传递给标签(最终展示给用户);filters中的内容也是js函数,不过一般只用于生成数据的格式(同质的数据只要存一份在data里,使用时按需编写filter过滤器,因此filter中的函数逻辑一般比较简单)。
vue.js为每个组件提供了v-if、v-show、v-for的属性,用来控制标签的渲染,还提供了v-on、v-bind、v-model进行数据绑定,也提供了双大括号数据绑定,具体见以上代码。其中v-model是双向数据绑定,所谓数据绑定,指的是js代码中的变量,和html标签中的属性值或内容值进行绑定,当改变其中一个时,另一个自动完成相同改变。
如果没有vue.js,这些数据同步都要由程序员自己实现,但现在只要使用vue.js提供的功能即可。v-on、v-bind、双大括号都是单向绑定,但js函数改变了data里的数据时,被绑定的html标签也会自动更改(如购物app,只要把商品对应的内部变量改了,页面显示的信息就更改了,极大地减少了程序员的工作量)。单向数据绑定一般用在客户不会改变标签的情况下,如果客户能够改变标签内容,而且希望同步到js数据,那就用v-model双向绑定就好了。
总结一下vue.js对我们有用的特性:第一,把js代码分类放置在Vue中,使代码逻辑更加清晰;第二,实现了双向数据绑定,开发者只需要关注业务逻辑本身而不用过多关注数据同步。

Uni-app是在vue.js的基础上的另一个框架,实现了从网页端app到移动端app的衔接,正是因为uni-app框架,使我们能够用web编程的语法,进行android的开发。下载官方提供的HBuilderX开发工具,新建一个默认uni-app项目,如下图所示:


main.js和manifest.json是项目的配置,其中manifest还提供图形化界面配置app的图标、启动图、版本号等,如下图所示:


App.vue中包括整个项目的js代码(包括app加载、隐藏、显示时调用的js函数)和整个项目的css代码(会被加载到每个页面),如下图所示:

uni.scss定义了可能用到的变量,整个项目都可以使用,如下图所示:

pages.json是页面的配置文件,pages数组声明了所有页面,其中pages[0]是默认启动页,globalStyle对象可以设置导航栏、窗口等,此外还可以添加底部导航栏tabBar对象,可选属性详见pages.json开发文档,如下图所示:

pages目录包含多个.vue文件,每个页面都是一个vue组件,即遵循vue单文件组件规范,每个文件包括template、script、和style标签,分别编写html、js、css代码,static目录则存放项目用到的静态文件,如下图所示:

下面介绍uni-app与前述html、css、JavaScript、vue.js需要区分的部分。
首先是尺寸单位,uni-app同样也是使用px和%两种,但px被重写为类似%的百分比布局,它默认宽度屏幕为750px,也就是代码中的mpx会被转义为m÷750×100%。
其次是样式导入,不同于css的link,uni-app使用import,例如:

  1. @import "../../xxx.css";

此外,uni-app的在标签上也有所更改:div、ul、li改成view;span、font改成text;a改成navigator;img改成image;input的type属性改成confirmtype;select改成picker;iframe改成web-view;保留form、button、checkbox、radio、label、textarea、canvas、video;增加scroll-view(滚动容器)、swiper(滑动容器)、icon(图标)、rich-text(富文本,不可执行js,但可渲染各种文字格式和图片)、progress(进度条)、slider(滑块指示器)、switch(开关选择器)、camera(相机)、live-player(直播)、map(地图)、cover-view(容器,可覆盖原生组件),这些标签可以在开发时查阅开发文档

讲完了html→css→js→vue.js→uni-app这条开发手机app的路线,总要介绍一下这条路线在app前端开发的位置,实际上这是一条非原生开发路线。移动app开发可以分为原生开发和非原生开发,原生开发指运用官方提供的原始工具和原始语言进行开发(Android使用Java、iOS使用Objective C),开发周期长、学习曲线陡,但能直接操控硬件,因此性能高、交互体验好,而非原生开发一般是web技术,界面展示使用web技术,开发周期短、容易上手,但性能不如原生开发。

参考资料