uni-app使用Vue.js
说明
:uni-app基于Vue 2.0
实现,开发者需注意Vue 1.0 -> 2.0 的使用差异,详见从 Vue 1.x 迁移。
1. 生命周期
uni-app不仅支持应用生命周期和页面生命周期,还支持 vue 实例的如下生命周期函数:
- beforeCreate
- created:建议使用 uni-app 的 onLoad 代替 vue 的 created
- beforeMount
- mounted:建议使用 uni-app 的 onReady代替 vue 的 mounted
- beforeUpdate
- updated
注意
:不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$watch('a', newValue => this.myMethod())。因为箭头函数是和父级上下文绑定在一起的,this 不会是如你做预期的 Vue 实例,且 this.a 或 this.myMethod 也会是未定义的。
2. 模板语法
几乎全支持 Vue官方文档:模板语法,下面讲下不支持的情况。
(1)不支持纯HTML
uni-app的非H5端里所有的 BOM/DOM 都不能用,也就是说 v-html
指令不能用,可以使用 rich-text组件 代替。或者使用三方组件wxparse,在hello uni-app中有示例。
(2)不支持部分复杂的 JavaScript 渲染表达式
非H5端无法支持复杂的 JavaScript 表达式。目前可以使用的有 + - * % ?: ! == === > < [] .,剩下的还待完善。
(3)不支持过滤器
编译到 App 和小程序平台时不支持使用过滤器,可以使用计算属性(computed)提前计算出数据(如果是数组类型,就提前计算整个数组),用以代替过滤器。
备注
: 自HBuilderX 1.7.1起,在微信小程序平台支持使用过滤器。
3. data属性
data 必须声明为返回一个初始数据对象的函数;否则页面关闭时,数据不会自动销毁,再次打开该页面时,会显示上次数据。
//正确用法,使用函数返回对象
data() {
return {
title: 'Hello'
}
}
//错误写法,会导致再次打开页面时,显示上次数据
data: {
title: 'Hello'
}
注意
:在微信小程序端,uni-app 将数据绑定功能委托给Vue,开发者需按Vue 2.0的写法实现数据绑定,不支持微信小程序的数据绑定写法,故如下写法不支持:
<view id="item-{{id}}"></view>
需修改为:
<view v-bind:id="'item-' + id "></view>
4. 计算属性
支持 Vue官方文档:计算属性。
5. 全局变量
实现全局变量的方式需要遵循 Vue 单文件模式的开发规范。详细参考:uni-app全局变量的几种实现方式。
6. 不支持函数
非H5端不支持在 template 内使用 methods 中的函数。
备注
: 自HBuilderX 1.7.1起,在微信小程序平台支持支持在 template 内使用 methods 中的函数。
7. Class与Style绑定
为节约性能,我们将 Class 与 Style 的表达式通过 compiler 硬编码到 uni-app 中。
8. 条件渲染
全支持 Vue官方文档:条件渲染。
9. 列表渲染
全支持vue列表渲染 Vue官方文档:列表渲染。只是需要注意一点,嵌套列表渲染,必须指定不同的索引!需要填写 :key="xx"
。
<!-- 在这种嵌套循环的时候, index 和 itemIndex 这种索引是必须指定,且别名不能相同,正确的写法如下 -->
<template>
<scroll-view v-for="(card, index) in list" :key="index">
<view v-for="(item, itemIndex) in card" :key="itemIndex">
{{item.value}}
</view>
</scroll-view>
</template>
注意
:同级多个 v-for 时 key 的值是不允许重复的,key 的绑定是不支持表达式的,需要按照以下方式处理。
// 错误的写法
<view v-for="(item, index) in list1" :key="index">{{item}}</view>
<view v-for="(item, index) in list2" :key="index">{{item}}</view>
// 正确的写法
<view>
<view v-for="(item, index) in list1" :key="index">{{item}}</view>
</view>
<view>
<view v-for="(item, index) in list2" :key="index">{{item}}</view>
</view>
10. 事件处理器
几乎全支持 Vue官方文档:事件处理器。事件映射表如下所示:
// 事件映射表
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
longtap: 'longtap',
input: 'input',
change: 'change',
submit: 'submit',
blur: 'blur',
focus: 'focus',
reset: 'reset',
confirm: 'confirm',
columnchange: 'columnchange',
linechange: 'linechange',
error: 'error',
scrolltoupper: 'scrolltoupper',
scrolltolower: 'scrolltolower',
scroll: 'scroll'
}
注意
:
(1)事件映射表中没有的原生事件也可以使用,例如map组件的regionchange 事件直接在组件上写成 @regionchange,同时这个事件也非常特殊,它的 event type 有 begin 和 end 两个,导致我们无法在handleProxy 中区分到底是什么事件,所以你在监听此类事件的时候同时监听事件名和事件类型既 <map @regionchange="functionName" @end="functionName" @begin="functionName"><map>。
(2)为兼容各端,事件需使用 v-on 或 @ 的方式绑定,请勿使用小程序端的bind 和 catch 进行事件绑定。
(3)事件修饰符
- .stop:各平台均支持, 使用时会阻止事件冒泡,在非 H5 端同时也会阻止事件的默认行为
- .prevent 仅在 H5 平台支持
- .self:仅在 H5 平台支持
- .once:仅在 H5 平台支持
- .capture:仅在 H5 平台支持
- .passive:仅在 H5 平台支持
(4)若需要禁止蒙版下的页面滚动,可使用 @touchmove.stop.prevent="moveHandle",moveHandle 可以用来处理 touchmove 的事件,也可以是一个空函数。
<view class="mask" @touchmove.stop.prevent="moveHandle"></view>
(5)按键修饰符:uni-app运行在手机端,没有键盘事件,所以不支持按键修饰符。
11. 表单控件绑定
支持 Vue官方文档:表单控件绑定。建议开发过程中直接使用 uni-app:表单组件。用法示例:
(1)H5 的select 标签用 picker 组件进行代替
<template>
<view>
<picker @change="bindPickerChange" :value="index" :range="array">
<view class="picker">
当前选择:{{array[index]}}
</view>
</picker>
</view>
</template>
<script>
export default {
data () {
return {
index: 0,
array: ['A', 'B', 'C']
}
},
methods: {
bindPickerChange (e) {
console.log(e)
}
}
}
</script>
(2)表单元素 radio 用 radio-group 组件进行代替
<template>
<view>
<radio-group class="radio-group" @change="radioChange">
<label class="radio" v-for="(item, index) in items" :key="item.name">
<radio :value="item.name" :checked="item.checked"/> {{item.value}}
</label>
</radio-group>
</view>
</template>
<script>
export default {
data () {
return {
items: [
{name: 'USA', value: '美国'},
{name: 'CHN', value: '中国', checked: 'true'},
{name: 'BRA', value: '巴西'},
{name: 'JPN', value: '日本'},
{name: 'ENG', value: '英国'},
{name: 'TUR', value: '法国'}
]
}
},
methods: {
radioChange (e) {
console.log('radio发生change事件,携带value值为:', e.target.value)
}
}
}
</script>
12. 组件
12.1 Vue组件
组件是整个 Vue.js
中最复杂的部分,支持 Vue官方文档:组件 。有且只能使用单文件组件(.vue 组件)的形式进行支持。其他的诸如:动态组件,自定义 render
,和<script type="text/x-template">
字符串模版等非H5端都不支持。详细的非H5端不支持列表:
(1)暂不支持在组件引用时,在组件上定义 click 等原生事件、v-show(可用 v-if 代替)和 class style 等样式属性(例:<card class="class-name"> </card> 样式是不会生效的)。
Slot(scoped 暂时还没做支持)
(2)动态组件
(3)异步组件
(4)inline-template
(5)X-Templates
(6)keep-alive
(7)transition
(8)class
(9)style
(10)组件里使用 slot 嵌套的其他组件时不支持 v-for
12.2 uni-app组件
uni-app提供了丰富的UI组件,比如: picker
,map
等,需要注意的是原生组件上的事件绑定,需要以 vue
的事件绑定语法来绑定,如 bindchange="eventName"
事件,需要写成 @change="eventName"
。如:
<picker mode="date" :value="date" start="2015-09-01" end="2017-09-01" @change="bindDateChange">
<view class="picker">
当前选择: {{date}}
</view>
</picker>
12.3 全局组件
uni-app 支持配置全局组件,需在 main.js 里进行全局注册,注册后就可在所有页面里使用该组件。注意
:Vue.component 的第一个参数必须是静态的字符串。示例:
(1)main.js 里进行全局注册
import Vue from 'vue'
import pageHead from './components/page-head.vue'
Vue.component('page-head',pageHead)
(2)index.vue 里可直接使用组件
<template>
<view>
<page-head></page-head>
</view>
</template>
12.4 命名限制
在 uni-app 中以下这些作为保留关键字,不可作为组件名。
- a
- canvas
- cell
- content
- countdown
- datepicker
- div
- element
- embed
- header
- image
- img
- indicator
- input
- link
- list
- loading-indicator
- loading
- marquee
- meta
- refresh
- richtext
- script
- scrollable
- scroller
- select
- slider-neighbor
- slider
- slot
- span
- spinner
- style
- svg
- switch
- tabbar
- tabheader
- template
- text
- textarea
- timepicker
- trisition-group
- trisition
- video
- view
- web
备注
:除以上列表中的名称外,标准的 HTML 及 SVG 标签名也不能作为组件名。
13. Q&A
(1) 如何获取上个页面传递的数据
在 onLoad 里得到,onLoad 的参数是其他页面打开当前页面所传递的数据。
(2) 如何设置全局的数据和全局的方法
uni-app内置了 vuex ,在app里的使用,可参考hello-uniapp
的store/index.js
。
//store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {...},
mutations: {...},
actions: {...}
})
export default store
//main.js
...
import store from './store'
Vue.prototype.$store = store
const app = new Vue({
store,...
})
...
//test.vue 使用时:
import {mapState,mapMutations} from 'vuex'
(3)如何捕获 app 的 onError
由于 onError 并不是完整意义的生命周期,所以只提供一个捕获错误的方法,在 app 的根组件上添加名为 onError 的回调函数即可。如下:
export default {
// 只有 app 才会有 onLaunch 的生命周期
onLaunch () {
// ...
},
// 捕获 app error
onError (err) {
console.log(err)
}
}
(4)组件属性设置不生效解决办法
当重复设置某些属性为相同的值时,不会同步到view层。 例如:每次将scroll-view组件的scroll-top属性值设置为0,只有第一次能顺利返回顶部。 这和props的单向数据流特性有关,组件内部scroll-top的实际值改动后,其绑定的属性并不会一同变化。解决办法有两种(以scroll-view组件为例):
- 监听scroll事件,记录组件内部变化的值,在设置新值之前先设置为记录的当前值
<scroll-view :scroll-top="scrollTop" scroll-y="true" @scroll="scroll">
export default {
data() {
return {
scrollTop: 0,
old: {
scrollTop: 0
}
}
},
methods: {
scroll: function(e) {
this.old.scrollTop = e.detail.scrollTop
},
goTop: function(e) {
this.scrollTop = this.old.scrollTop
this.$nextTick(function() {
this.scrollTop = 0
});
}
}
}
- 监听scroll事件,获取组件内部变化的值,实时更新其绑定值
<scroll-view :scroll-top="scrollTop" scroll-y="true" @scroll="scroll">
export default {
data() {
return {
scrollTop: 0,
}
},
methods: {
scroll: function(e) {
this.scrollTop = e.detail.scrollTop
},
goTop: function(e) {
this.scrollTop = 0
}
}
}
备注
:第二种解决方式在某些组件可能造成抖动,推荐第一种解决方式。