模版语法
插值
- 数据绑定最常见的形式就是文本插值,{
- v-once,一次性插值
- v-html,输出真的html
- v-bind指令
- 动态地绑定一个或多个属性,或一个组件
prop
到表达式。 - 可以缩写为:
- 在绑定
prop
时,prop
必须在子组件中声明。 - 可以用修饰符指定不同的绑定类型。
- 动态地绑定一个或多个属性,或一个组件
- JS表达式
- 三元
- 算术
- 复杂的函数运算
指令
- 带有
v-
前缀的特殊属性 - v-bind
- v-on
- 它用于监听 DOM 事件
- 可以缩写为
@
,v-on:click
->@click
- v-once
- 只渲染元素和组件一次。
- 添加了
v-once
能保证节点只渲染一次,但是并不一定能优化渲染性能,反而可能会拖慢客户端复用节点时的比对效率。
- v-html
- 内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。
- App端和H5端支持
v-html
,微信小程序会被转为rich-text
- data属性
- 必须声明为返回一个初始数据对象的函数
JavaScript//正确用法,使用函数返回对象 data() { return { title: 'Hello' } }
- 两种绑定方式:,属性区域通过:属性名称
Class 与 Style 绑定
- 对象语法
- 可以传给 v-bind:class 一个对象,实现动态地切换 class。
- 数组语法
- 可以把一个数组传给 v-bind:class,以应用一个 class 列表。
:class="[activeClass,errorClass]"
- 以:style=""这样的方式设置px像素值,其值为实际像素,不会被编译器转换。
- 小程序端不支持
classObject
和styleObject
语法。
条件渲染
- v-if和v-else
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回truthy
值的时候被渲染。 使用v-else
指令来表示 v-if 的“else 块”。
- 条件渲染分组
- 因为
v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 标签元素当做不可见的包裹元素,并在上面使用 v-if。最终的渲染结果将不包含该 元素。
- 因为
- v-show
- 带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的
CSS
属性的display
。
- 带有 v-show 的元素始终会被渲染并保留在 DOM 中。v-show 只是简单地切换元素的
- v-if 和 v-show 区别
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-show
不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换,来控制元素的显示和隐藏。- 如果需要非常频繁地切换,则使用 v-show 较好。
- 如果在运行时条件很少改变,则使用 v-if 较好。
- 不推荐同时使用
v-if
和v-for
。
- 不推荐同时使用
- 当
v-if
与v-for
一起使用时,v-for
具有比v-if
更高的优先级。
- 当
列表渲染
- 在v-for里使用数组
v-for
指令需要使用item in items
形式的特殊语法,其中items
是源数据数组,而item
则是被迭代的数组元素的别名。v-for="(item, index) in items"
- 在v-for里使用对象
- 可以用 v-for 来遍历一个对象的
property
。 v-for="(value, name, index) in object"
- 第一个参数
value
是被迭代的对象元素的属性值。- 第二个参数为property
名称 (也就是键名)。- 第三个参数作为索引。
- 可以用 v-for 来遍历一个对象的
- 列表渲染分组
- 可以利用带有
v-for
的template
来循环渲染一段包含多个元素的内容。
- 可以利用带有
- key
- 在H5平台 item 从 1 开始
- 在组件上使用 v-for
- 当在组件上使用 v-for 时,key是必须的。
事件处理
- 监听事件
- 可以用@事件监听 DOM 事件,并在触发时运行一些
JavaScript
代码。
- 可以用@事件监听 DOM 事件,并在触发时运行一些
- 事件处理方法
- 在
methods
对象中定义方法 event
是原生 DOM 事件
- 在
- 内联处理器中的方法
- 事件修饰符
.stop
: 各平台均支持, 使用时会阻止事件冒泡,在非 H5 端同时也会阻止事件的默认行为.native
: 监听原生事件,各平台均支持.prevent
: 仅在 H5 平台支持.capture
: 仅在 H5 平台支持.self
: 仅在 H5 平台支持.once
: 仅在 H5 平台支持.passive
: 仅在 H5 平台支持
- 事件映射表
JavaScript
// 事件映射表,左侧为 WEB 事件,右侧为 ``uni-app`` 对应事件
{
click: 'tap',
touchstart: 'touchstart',
touchmove: 'touchmove',
touchcancel: 'touchcancel',
touchend: 'touchend',
tap: 'tap',
longtap: 'longtap', //推荐使用longpress代替
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'
}
表单控件绑定
- v-model
- 可以用 v-model 指令在表单
input
、textarea
及select
元素上创建双向数据绑定。
- 可以用 v-model 指令在表单
- uni-app表单组件
组件基础
自定义组件
html
<button-counter title="title:"@clicknow="clicknow">
</button-counter>
<script type="text/javascript">
Vue.component('button-counter',{
props:['title'],
data:function(){
return{
count:0
}
},
template:'<div><h1>1111</h1><button v-on:click="clickfun">{{title}}:{{count}}times</button></div>',
methods:{
clickfun:function(){
this.count++;
this.$emit('clicknow',this.count)
}
}
})
</script>
计算属性和侦听器
- 计算属性computed
- 每一个计算属性都包含一个
getter
和一个setter
,默认是利用getter
来读取。
- 每一个计算属性都包含一个
- 计算属性的getter
- 在模板中绑定表达式是非常便利的,但是它们实际上只用于简单的操作。在模板中放入太多的逻辑会让模板过重且难以维护。
html<view> {{ message.split('').reverse().join('') }} </view>
- 对于任何复杂逻辑,你都应当使用计算属性。
- 计算属性的 setter
- 需要时也可以提供一个
setter
函数, 当手动修改计算属性的值时,就会触发setter
函数,执行一些自定义的操作。
html<template> <view> <view>{{ fullName }}</view> </view> </template> <script> export default { data() { return { firstName: 'Foo', lastName: 'Bar' } }, computed: { fullName: { // getter get(){ return this.firstName + ' ' + this.lastName }, // setter set(newValue){ var names = newValue.split(' ') this.firstName = names[0] this.lastName = names[names.length - 1] } } } } </script> //再运行 `fullName = 'John Doe'` 时,`setter` 会被调用,`firstName` 和 `lastName` 也会相应地被更新。
- 需要时也可以提供一个
- getter与setter区别
- get:通过设置get方法可以得到fullName的新值。
- set:通过set的方法,设置一个值(newValue)来改变fullName相关联的值,引起fullName重新的计算,相应的页面上fullName也会发生改变成新的内容。
计算属性缓存vs方法
html
<template>
<view>
<view>Reversed message: "{{ reversedMessage() }}"</view>
</view>
</template>
<script>
export default {
data() {
return {
message: 'Hello'
}
},
methods: {
reversedMessage(){
return this.message.split('').reverse().join('')
}
}
}
</script>
可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。 只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 message
还没有发生改变,多次访问 reversedMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。 每当触发重新渲染时,调用方法将总会再次执行函数。 我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代。
计算属性vs侦听属性
Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:侦听属性。当有一些数据需要随着其它数据变动而变动时,你很容易滥用 watch
。然而,通常更好的做法是使用计算属性而不是命令式的 watch
回调。
- 侦听器watch
- 类型:
interface Watch { [key: string]: string | Function | Object | Array }
- 一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项对象。Vue实例将会在实例化时调用
$watch()
,遍历watch
对象的每一个property
。
- 类型:
html
<script>
export default {
data() {
return {
a: 1,
b: 2,
c: 3,
d: 4,
e: {
f: {
g: 5
}
}
}
},
watch: {
a: function(val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
// 方法名
b: 'someMethod',
// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
c: {
handler: function(val, oldVal) { /* ... */ },
deep: true
},
// 该回调将会在侦听开始之后被立即调用
d: {
handler: 'someMethod',
immediate: true
},
// 你可以传入回调数组,它们会被逐一调用
e: [
'handle1',
function handle2(val, oldVal) { /* ... */ },
{
handler: function handle3(val, oldVal) { /* ... */ },
/* ... */
}
],
// watch vm.e.f's value: {g: 5}
'e.f': function(val, oldVal) { /* ... */ }
}
}
</script>
组件
- 组件是视图层的基本组成单元
- 组件是一个单独且可复用的功能模块的封装
- 一个组件包括开始标签和结束标签,标签上可以写属性,并对属性赋值。内容写在两个标签内。
- 根节点为
<template>
,这个<template>
下只能且必须有一个根<view>
组件。这是vue单文件组件规范。 - 一个组件的 data 选项必须是一个函数。
- 根节点为
- 基础组件是内置在uni-app框架中的,包括view、text、input、button、video等几十个基础组件,列表详见:uni-app基础组件
- 实际开发中会有很多封装的组件。比如我们需要一个五角星点击评分的组件,在DCloud的插件市场里可以获取到:https://ext.dcloud.net.cn/plugin?id=33
- 优势:
- 可以将组件进行任意次数的复用。
- 合理的划分组件,有助于提高应用性能。
- 代码更加方便组织和管理,并且扩展性也更强,便于多人协同开发。
- 组件化开发能大幅度提高应用开发效率、测试性、复用性等。
- 注册
- 在注册一个组件的时候,始终要起一个名字
- 使用kebab-case:当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
<my-component-name>
。 - 使用PascalCase:当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。 也就是说
<my-component-name>
和<MyComponentName>
都是可接受的。
- 使用kebab-case:当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如
- 在注册一个组件的时候,始终要起一个名字
- 全局注册
uni-app
支持配置全局组件,需在main.js
里进行全局注册,注册后就可在所有页面里使用该组件。- 注意:
- Vue.component 的第一个参数必须是静态的字符串。
- nvue 页面暂不支持全局组件。
main.js
里进行全局导入和注册JavaScriptimport Vue from 'vue' import pageHead from './components/page-head.vue' Vue.component('page-head',pageHead)
index.vue
里可直接使用组件html<template> <view> <page-head></page-head> </view> </template>
- 局部注册
- 局部注册之前,在需要引用该组件的页面,导入你想使用的组件。
- 页面引入组件方式
- 传统vue规范:在 index.vue 页面中,通过
import
方式引入组件 ,在components
选项中定义你想要使用的组件。
html对于<!-- 在index.vue引入 uni-badge 组件--> <template> <view> <uni-badge text="1"></uni-badge><!-- 3.使用组件 --> </view> </template> <script> import uniBadge from '@/components/uni-badge/uni-badge.vue';//1.导入组件(这步属于传统vue规范,但在uni-app的easycom下可以省略这步) export default { components:{uniBadge }//2.注册组件(这步属于传统vue规范,但在uni-app的easycom下可以省略这步) } </script>
components
对象中的每个 property 来说,其 property 名就是自定义元素的名字,其 property 值就是这个组件的选项对象。 2. 通过uni-app的easycom:将组件引入精简为一步。只要组件安装在项目的components
目录下,并符合components/组件名称/组件名称.vue
目录结构。就可以不用引用、注册,直接在页面中使用。htmleasycom是自动开启的,不需要手动开启,有需求时可以在<!-- 在index.vue引入 uni-badge 组件--> <template> <view> <uni-badge text="1"></uni-badge><!-- 3.使用组件 --> </view> </template> <script> // 这里不用import引入,也不需要在components内注册uni-badge组件。template里就可以直接用 export default { data() { return { } } } </script>
pages.json
的easycom
节点进行个性化设置,详见。 不管components目录下安装了多少组件,easycom打包后会自动剔除没有使用的组件,对组件库的使用尤为友好。 组件是vue
技术中非常重要的部分,组件使得与ui相关的轮子可以方便的制造和共享,进而使得vue
使用者的开发效率大幅提升。 - 传统vue规范:在 index.vue 页面中,通过
props
props
可以是数组或对象,用于接收来自父组件的数据。props
可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。
选项 | 类型 | 说明 |
---|---|---|
type | String 、 Number 、Boolean 、Array 、 Object 、Date 、Function 、Symbol ,任何自定义构造函数、或上述内容组成的数组 | 会检查一个 prop 是否是给定的类型,否则抛出警告 |
default | any | 为该 prop 指定一个默认值。如果该 prop 没有被传入,则换做用这个值。对象或数组的默认值必须从一个工厂函数返回。 |
required | boolean | 定义该 prop 是否是必填项 |
validator | Function | 自定义验证函数会将该 prop 的值作为唯一的参数代入。在非生产环境下,如果该函数返回一个 false 的值 (也就是验证失败),一个控制台警告将会被抛出 |
html
//子组件
<template>
<view>
<!-- 我是子组件componentA -->
<view>{{age}}</view>
</view>
</template>
<script>
export default {
props: {
// 检测类型 + 其他验证
age: {
type: Number,
default: 0,
required: true,
validator: function(value) {
return value >= 0
}
}
}
}
</script>
//父组件
<template>
<view>
<!-- 我是父组件 -->
<componentA :age="10"></componentA>
</view>
</template>
- 传递静态或动态的Prop
- 可以给
prop
传入一个静态的值:
html<blog-post title="My journey with Vue"></blog-post>
- 可以通过
v-bind
动态赋值
html<!-- 动态赋予一个变量的值 --> <blog-post v-bind:title="post.title"></blog-post> <!-- 包含该 prop 没有值的情况在内,都意味着 `true`。--> <blog-post is-published></blog-post> <blog-post v-bind:is-published="post.isPublished"></blog-post>
- 传入一个对象的所有
property
(微信小程序暂不支持该用法,即:<blog-post v-bind="post"(错误)>)
- 想要将一个对象的所有
property
都作为prop
传入,你可以使用不带参数的v-bind
(取代 v-bind:prop-name)。例如,对于一个给定的对象post
- 可以给
- 单向数据流 所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。 这个
prop
用来传递一个初始值;这个子组件接下来希望将其作为一个本地的prop
数据来使用。在这种情况下,最好定义一个本地的data property
并将这个prop
用作其初始值 这个prop
以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个prop
的值来定义一个计算属性
ref
被用来给元素或子组件注册引用信息,引用信息将会注册在父组件的 $refs
对象上。