Vue基础篇--模板语法
Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。它支持的所有兼容 ECMAScript 5 的浏览器。
最简单的结构
在页面中引入vue.js,可以使用官方CDN,可以下载到本地引入
<script src="./lib/Vue.js"></script>
引入该包之后,就可以在环境中使用Vue这个的全局对象,下面将这个对象实例化,并指定了部分属性
el:
指定该Vue对象控制dom树的根节点,通过 id 选择器或类选择器均可指定,#app表示`id=app1`这个dom节点下的所有节点都属于该vue对象作用范围。data: 定义一些
属性数据。methods:
定义一些函数操作,比如一些点击事件的事件函数。
<script src="./lib/Vue.js"></script> <div id=app1 v-on:click="clickToGetinfo"> {{ msg }} </div> <div id=app2> {{ msg }} </div> <script> var vm = new Vue({ el:"#app1", data:{ msg:"abc" }, methods:{ clickToGetinfo: function(){}, } }) </script>
{{ msg }} 是插值表达式语法,msg会被解析为一个变量名,实际上是 {{ vue.data.msg }}的简写,所以它对应了data中的msg: "abc"这个值,页面中{{ msg }}将会被渲染为对应的值abc,但由于 el指定了id=app1范围内的节点,所以只有第一个 {{ msg }}会显示为"abc"
通过v-on指令,为标签绑定了一个点击事件函数ClickToGetinfo,点击触发时,会执行定义在methods中的对应函数。如果函数需要参数,直接使用调用的方式指定即可:v-on:click="clickToGetinfo('123')"
插值语法
语法规则是使用双大括号,内部是一个表达式,例如 {{ msg }},{{ item.value }}
,{{ 'abc ' + o }} 这样的表达式,在页面中展示的也是这个表达式最后的结果。
指令
基本指令
官网api:https://cn.vuejs.org/v2/api/#v-html
- v-text:渲染指定表达式的值,按照原字符串的形式进行渲染 "<span v-text="msg"></span>"
- v-html:渲染指定的表达式的值,这个值会按照html 的语法进行渲染,而不是直接直接输出字符串。
- v-pre:默认情况下,{{ msg }} 会被作为一个插值表达式解析,使用该指令将会避免解析, {{ msg }} 会作为一个字符串直接展示。
- v-cloak:标签内容只有在变量渲染后才会展示到到页面中。用户不会看到标签中表达式被替换的短暂过程
- v-once:该标签中的表达式只会在第一次时候渲染,之后不随data的改变而改变。
<span v-text="msg"></span> <span v-html="msg"></span> <span v-pre>{{ 插值语法不起作用,相当于一个原始的标签 }}</span> <div v-cloak> {{ msg}} </div><span v-once > 第一次渲染msg的值后,不会随msg值得改变而改变{{msg}}</span>
- v-if:if 后对应的值为 true 时候,才会在页面中创建该标签。
- v-else:前面一定是带有 if 或者 else-if 指令的标签,当以上的条件都不满足时候,将会渲染这个标签。
- v-else-if:前面一定有 if ,else-if 的 值为 true 时,才会渲染该标签。
- v-show:指定渲染的条件,如果条件为true,会在页面中的显示,否则会给改标签添加 css 中的 dispaly,隐藏这个标签。
v-if 和 v-show 的区别是,v-if 只会创建满足条件的标签,而不满足条件的标签不会被创建。使用 v-show,所有的标签都会被创建,只是将不满足条件的标签使用了display属性隐藏而没有展示。
因此:当需要根据条件渲染标签时候,使用 if 反复的创建会消耗cpu的性能,使用 show 会将所有组件全部渲染而消耗一部分内存,根据业务实际情况考虑即可。
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div>
- v-for:循环创建的多个标签,需要为每个项提供一个唯一标识 key,作为标签的身份识别,从而实现重用和重新排序现有元素。下面是几种遍历用法
// 遍历数字 <p v-for="count in 10" :key="count"> {{count}} </p> // 遍历1-10,count从1 开始,会出现10个p标签 // 遍历数组 <p v-for="(val, index) in items" :key="val"> {{val}}为值,{{index}} 为索引 </p> <p v-for="(item, index) in items" :key="item.id"> {{item.attr}}为值,{{index}} 为索引</p> // 遍历对象 <p v-for="(val, key, index) in object" :key="key"> {{val}}为值,{{key}}为key, {{index}} 为索引</p> // v-for 可以和if 共同使用, 并且for 的优先级比if 更高,也就是先遍历 数组,其中满足 if 条件的项才会被渲染。 <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo }} </li> // 当在组件上使用 v-for 时,因为组件有自己的作用域,所以在组件标签使用的变量,需要使用v-bind绑定,同时还要再组件 // 内部定义 props数组才能接收对应的变量值。 <ul> <li is="todo-item" // is=todo-item 指定这个li 其实是一个 <todo-item> 组件 v-for="(todo, index) in todos" v-bind:key="todo.id" // 组件todo-item的props中没有定义key变量,组件无法使用该值 v-bind:title="todo.title" // 而props中定义了title变量 v-on:remove="todos.splice(index, 1)" ></li> </ul> <script> // 组件对象 Vue.component('todo-item', { template: '\ <li>\ {{ title }}\ <button v-on:click="$emit(\'remove\')">Remove</button>\ </li>\ ', props: ['title'] // 内部使用了外部的 title 变量,需要使用props 定义来自外部 }) var vm = new Vue({ el: '#todo-list-example', data: { todos: [ { id: 1, title: 'Do the dishes',}, { id: 2, title: 'Take out the trash',}, { id: 3, title: 'Mow the lawn'} ], }, )} </script>
- v-bind:为标签属性绑定一个动态值,这个动态值可以随着data中数据的改变而改变,方便动态的修改标签内容。
<!-- 绑定一个 attribute --> <img v-bind:src="imageSrc"> <img :src="imageSrc"> <img :src="'/path/to/images/' + fileName"> <!-- 绑定一个全是 attribute 的对象 --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div> <!-- 通过 prop 修饰符 绑定变量text,展示text 的内容,类似 v-text="text" 的使用 --> <div v-bind:text-content.prop="text"></div> <!-- 为组件绑定一个 prop属性。“prop”必须在 my-component 中声明,组件内部才可以使用这个变量的值。--> <my-component :prop="someThing"></my-component> <!-- 通过 $props 将父组件的 props 一起传给子组件,类似传入一个对象的方式--> <child-component v-bind="$props"></child-component> // $props 表示父组件的 this.$props 属性值 <script> var vm = Vue({ data: { imageSrc:"", otherProp:"" fileName:"", text:"" someProp: "",someThing }, }) </script>
- v-on:为一个标签绑定事件监听函数,需要使用v-on 指令,指定事件的类型和处理函数即可
<a v-on:click.prevent="function(){}">
。 - v-model:实现数据的双向绑定,v-model关联的变量,标签中的值 和 data 中的值是双向绑定的,一方会随着另一方进行改变。通常用来获取页面中输入框内部用户输入的数据。
class 和 style 绑定
指定标签的class 和 style 内联样式也是可以通过v-bind 绑定Vue对象中的数据,从而使用Vue中的属性动态赋值。
class 语法
1. 最原始的方式,未使用 v-bind <p class="class1 class2 class3"> 类名为 class1,class2, class3 2. 数组的方式 <p :class="[c1, c2, c3]"> // c1, c2, c3 是变量名, 他们对应了真实的类名的字符串 "class1" "class2" "class3" <p :class="[{c1:true}, c2, {c3:flag}]"> // 某些类名根据条件判断使用 <p :class="[flag? c1:"" , c2, c3]"> data:{ c1: "class1", c2: "class2", c3: "class3", flag:true } 3. 使用对象的语法 <p :class="{class1:flag1, class2:flag2, class3:flag}"> // key就是类名,不会作为变量编译,即使没有加 引号,valuea才会当作变量
// Vue对象中data 的值 data:{ flag1:true, flag2:true, flag3:true }
style
style 内联样式的指定方式是在对应的 样式名 指定对应的值即可,样式的值可以使用变量或者是表达式来指定。
这是原始的样子 <div style="{ color: 'red', fontSize:'20px'}"></div> 1. 对象的指定方式:对象的指定方式就是每个样式的值可以使用变量或者表达式替代 <div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div> // value 是变量或者表达式 data: { activeColor: 'red', fontSize: 30 } // 也可以这样 <div v-bind:style="styleObject"></div> // 变量指定为一个对象,直接将对象全部替换过来 data: { styleObject: { color: 'red', fontSize: '13px' } } 2. 数组的指定方式:数组主要是解决多个样式对象的合并 <div v-bind:style="[baseStyles, overridingStyles]"></div> data: { baseStyles:{ color: 'red', fontSize: '13px' }, overridingStyles:{ width:"500px", height: "600px" } }
v-on 事件监听
通过对标签绑定事件,当事件触发,将会执行对应的事件函数。
事件修饰符
事件修饰符是进一步指定事件的触发状态,常用的有
.stop
- 调用event.stopPropagation()
。事件触发该标签后不会向外继续冒泡触发外部标签的事件。.prevent
- 调用event.preventDefault()
。阻止默认的事件行为,只会调用我们指定的事件处理函数。.capture
- 添加事件侦听器时使用 capture 模式,开启在捕获过程的事件监听,默认只会在冒泡过程监听。.self
- 只当事件是从侦听器绑定的元素本身触发时才触发回调函数。.left
- (2.2.0) 只当点击鼠标左键时触发。.right
- (2.2.0) 只当点击鼠标右键时触发。.middle
- (2.2.0) 只当点击鼠标中键时触发。
.{keyCode | keyAlias}
- 只当事件是从特定键触发时才触发回调,也就是绑定指定按键去触发事件。.native
- 监听组件根元素的原生事件。.once
- 该标签只触发一次回调事件。.passive
- (2.3.0) 以{ passive: true }
模式添加侦听器
多个标签嵌套使用时候,标签的触发事件有一个传递的过程,分为捕获和冒泡两种模式,冒泡:从目标元素(事件触发元素)向上层传递事件,直到顶层的window对象。捕获过程是事件从顶层元素向下层传递,直到目标元素(事件触发元素)终止。上述的修饰符中可以使用stop,prevent,self等修饰符控制事件传递的行为。
语法
1. 指定事件类型,绑定一个处理函数 <button v-on:click="doThis"> </button> // doThis 是一个变量 <button @click="doThis"> </button> // 缩写 <button v-on:click="doThat('hello', $event)"> </button> // 内联调用,可以方便传参 2. 使用修饰符 <button @click.stop="doThis"></button> <!-- 停止冒泡 --> <button @click.prevent="doThis"></button> <!-- 阻止默认行为,没有表达式 --> <button @click.stop.prevent="doThis"></button> <!-- 串联修饰符, 有执行顺序不同 --> <input @keyup.enter="onEnter"> <!-- 按键抬起事件,.enter 表示指定的回车键,特殊的按键有按键名 --> <input @keyup.13="onEnter"> <!-- 如果没有按键名,可以使用按键码指定 --> 3. 使用对象语法监听多个事件 <button v-on="{ mousedown: doThis, mouseup: doThat }"></button> var vm = Vue({ methods:{ // 定义事件处理函数 doThis(){}, doThat(params1, params2){}, onEnter(){} } })
自定义按键修饰符
Vue中提供的按键修饰符有
.enter
.tab
.delete
(捕获 “删除” 和 “退格” 键).esc
.space
.up
.down
.left
.right
.ctrl
.alt
.shift
.meta
除了这些,我们也可以自己定义系统没有提供的按键,例如为F2按键定义一个修饰符,只需要在vue.config.keycodes中定义一个属性,属性值为F2按键的键码113。
// 1.0版本中设置的方式,Vue.direative("on").keyCodes.f2 = 113 Vue.config.keyCodes.f2 = 113 // 定义了 113 这个码值的变量为 f2 这样可以使用键值修饰修饰符 f2 <input @keyup.f2="onEnter"> // 这样就可以使用f2 这个修饰符号为 input 标签定义一个F2 按键对应按键抬起事件
v-model 双向数据绑定
双向数据绑定是 标签值 和 data中的数据值是双向绑定的。data数据改变会改变标签渲染值,人为的修改标签中的值也会自动修改data中的值。v-model指令适合与和用于做交互的标签,例如文本输入控件,单选或多选控件等。
input输入框
v-model其实是一个语法糖,它的背后本质上是包含两个操作:
- 1.v-bind绑定一个value属性
- 2.v-on指令给当前元素绑定input事件,每次输入完成,获取input中的value,更新data中的 value
redio单选框
选中一个redio 时,会使用被选中的项 的 value 属性的值,赋值给 v-mode绑定的变量,从而改变data中的 变量值。需要互斥的多个单选按钮绑定同一个变量即可,则始终只会触发一个。如果两个单选按钮不互斥,则绑定不同的变量。
<div id="app"> <input type="radio" id="man" value="男" v-model="sex">男 <input type="radio" id="woman" value="女" v-model="sex">女 </div> <script src="../js/vue.js"> </script> <script> const app = new Vue({ el: '#app', data: { sex: '男' } }) </script>
checkbox的使用
单个勾选框:选中isagree 被指定为true,未选中指定为false
<!-- checkbox 单选框 --> <label for="agree"> <input type="checkbox" id="agree" v-model="isagree">同意协议 <h1> 你的选择是:{{isagree}} </h1> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { isagree: false } }) </script>
多个勾选框:被选中项的value,在data中使用一个数组保存。
<div id="app"> <!-- checkbox 多选框 --> <input type="checkbox" value="red" v-model="color">红 <input type="checkbox" value="yellow" v-model="color">黄 <input type="checkbox" value="green" v-model="color">绿 <input type="checkbox" value="blue" v-model="color">蓝 </div> <script src="../js/vue.js"></script> <script> var app = new Vue({ el: '#app', data: { color: ["red", "blue"] // 储存被选中的内容 } }) </script> </body>
v-model 的修饰符
.lazy修饰符(v-model.lazy=):
- v-model默认是在input事件中同步输入框的数据的, 一旦有数据发生改变对应的data中的数据就会自动发生改变。
- lazy修饰符可以让数据在 input标签 失去焦点或者回车时才会更新,而不是实时更新,这样更节约性能。
number修饰符(v-model.number=):
- 默认情况下,在输入框中无论我们输入的是字母还是数字,都会被当做字符串类型进行处理。如果我们希望处理的是数字类型,那么最好直接将内容当做数字处理。
- number修饰符可以让在输入框中输入的内容自动转成数字类型
trim修饰符(v-model.trim=):
- 如果输入的内容首尾有很多空格,使用 trim修饰符可以自动去除内容左右两边的空格。
自定义指令
当标签使用我们自定义的指令后,这个指令会获取这个标签对象,并在这个标签从生成到插入节点再到销毁的过程中绑定一些执行函数,去执行一些操作。这也是自定义指令的常用方式,例如我们可以定义一个指令 v-focus,这个指令的作用为,被该指令指定的标签,在页面完全加载完成后会获取到焦点。
自定义指令原理
自定义指令提供了几个生命周期函数,标签执行一下操作的过程中会触发这些操作函数来操作标签,从而实现我们自定义指令期望的功能
// 全局定义一个指令,所有Vue对均可使用 Vue.direactive("focus", {} ) - 参数1:指令名字, 定义时不需要添加v-前缀,调用时必须添加前缀 - 参数2:一个对象,对象中有多个函数,这些函数在标签生命周期的特定时刻。 { bind:function(el){}, // 指令绑定到DOM上时触发,bind不是插入到dom中,只触发一次, 参数 el 为被绑定的dom元素对象 inserted: function(el){}, // 当被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。 updated: function(el){}, // vNode 被更新的时候执行,可以执行多次。 componentUpdated: function(el){}, //指令所在组件的 VNode 及其子 VNode 全部更新后调用。 unbind:function(el){}, //只调用一次,指令与元素解绑时调用。 } // 局部定义 var vm = New Vue({ el:, methods:{}, data:{}, directive:{ focus:{ inserted:function(el){}, bind:function(el){} } },
})
指令钩子函数的参数
指令通常有两种使用方式,传值和不传值,例如下面的两种自定义指令用法,v-focus为不传值,v-color="red"为传值。
<input v-focus v-color="'red'">
每个钩子函数都可以定义形参来接收参数值,我们可以在 钩子函数中使用4个形参去接收被注入钩子函数的四个参数。被注入的四个参数值分别为以下的数据。
el:指令所绑定的元素,可以用来直接操作 DOM 。
binding:一个对象,包含以下 property:
name:指令名,不包括 v- 前缀。
value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。
oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
使用参数
通常情况下,我们只需要前两个参数即可获得基本的数据,第一参数为该标签的dom对象,第二次参数为一个结构体,从结构体中可以获得各个指令参数的各种信息,按照需求获取使用即可。下面的例子中只是打印了四个参数。
<p v-color="'red'"> <p> var vm = new Vue({ el: directive:{ color:{ bind: function(el, binding, vnode, oldVnode){ console.log(el) console.log(binding) console.log(vnode) console.log(oldVnode) } } } })
通常情况下,我们只需要前两个参数即可获得基本的数据,第一参数为该标签的dom对象,第二次参数为一个结构体,内部包含了标签中传入的值,以及原始表达式,以及其他参数信息。我们想要获取v-color上的 'red',直接从第二个参数获取即可
<p v-color="'red'" > <p> var vm = new Vue({ el: directive:{ color:{ bind: function(el, binding, vnode, oldVnode){ console.log(binding.value) // 获取 v-color="'red'" 中的 'red' console.log(binding.arg) // 获取 v-color:foo 中的参数 "foo" } } } })
如果这个指令的钩子函数只有 bind 和 update方法,可以直接简写方法函数,不需要指定bind这个操作的名字。
特殊属性
我们在一个标签中可以定义几个特殊的属性。key属性,ref属性, is属性
<p key="k1" ref="name1" is="my-component"> item1 </p> <p key="k2" ref="name2"> item2 </p> <p key="k3" ref="name3"> item3 </p>
key
key
这个特殊的属性主要是用来 服务于 Vue 虚拟 DOM 算法,key 是一个虚拟dom节点的唯一标识,在Vue中,为了最大的节约资源,提高性能和响应速度,Vue 会使用一种 "最大限度减少动态元素" 并尽可能的尝试 "就地修改和复用相同类型元素" 的算法。没有key 对这个元素进行唯一标识,在渲染一个相同类型的元素时,将会复用这个元素,这样可以避免新创建一个 vnode 对象,节约性能,但是在某些情况下复用组件可能出现问题,造成渲染错误。
举例说明:以下会根据 flag 的 真假值选择渲染 login 或者 reg 中的其中一个标签
<input v-if="flag" value="" id='login'> <input v-else value="" id='reg'>
当我们选择 login组件,并在输入框中输入内容,然后通过改变flag 为 false 切换到 reg 组件时候,组件中的内容没有消失,并且如果继续输入,我们会发现两个组件的内容始终保持一致,像是一个组件。而按照一般思路,reg和login是两个input对象,会各自保存各自被输入信息,这样的现象不符合预期。
原因就是Vue复用了组件。当需要渲染一个input 时候,Vue并没有立刻创建一个新的标签,而是发现有一个闲置的 input 没有被使用,于是继续使用这个input组件,所以无论展示的login还是reg组件,都是同一个组件在反复复用。
当指定了一个key 唯一标识一个组件,就不会出现类似的情况,当我们从 login 切换到 reg 时候,由于两个input 的key值不同,所以Vue不会复用这个标签,而当从reg再次切换会login时,会发现已经存在一个login组件,于是复用它。
key 属性在 v-for 指令中常常使用,为了使其每一项都有自己的唯一标识,从而避免标签复用而产生错误。如果是在组件中,那么所有的 v-for 都必须加上 key属性,每项使用一个唯一的值来标识自己。
因此,有相同父元素的子元素必须有独特的 key。重复的 key 或者未指定 key 都可能会造成渲染错误。
ref
ref 属性可以将该标签收集到 Vue对象中的$refs对象中,从而更好的获取这个对应的标签进行操作,避免使用原始的选择器获取标签。
<p key="k1" ref="name1" is="my-component"> item1 </p> <p key="k2" ref="name2"> item2 </p> <p key="k3" ref="name3"> item3 </p> <script> var vm = Vue({ el:"", methods:{ getref(){ console.log(this.$refs) // 输出该对象则可以看到所有ref收集的标签,输出一个对象,key为ref定义时的名字,value为标签对象
console.log(this.$refs.name1)
} } }) </script>
is
is 属性可以为该标签指定一个组件名,该标签将会被渲染成该组件。可以通过动态切换这个组件名的方式,实现组件动态切换。以下两种方式等价
<div class="contain"> <my-comp> </my-comp> </div>
<div class="contain">
<div is="'my-comp'"> </div> ///该div 会被渲染为 my-comp标签
</div>
过滤器 filter
使用过滤器
过滤器就是一个函数,将某个值处理后,输出过滤后 return 返回的结果。
在插值表达式中使用过滤器的语法为 {{ name | filter_name}}
, 也可以连续使用多个过滤器 {{ name | substring("hello") | filter2() }}
,过滤器 也可以直接传参,但是过滤器的第一个形参接收的值始终是被过滤的元素本身,传入的参数只能从第二个参数开始计算。
定义过滤器
过滤器就是一个函数。有两种定义的方式:全局定义和局部定义
全局定义:定义在Vue实例对象之外,所有的Vue实例都可以使用
<div id='app1'> <div> {{ val | filter_name1 }} </div> <div> {{ val | filter_name2("k-on") }} </div> </div> <div id='app2'> <div> {{ val | filter_name1 }} </div> <div> {{ val | filter_name2("k-on") }} </div> </div> // 全局定义的过滤器,直接在Vue类上定义,全局所有的Vue对象都可以使用 Vue.filter("filter_name1", (val) => { return "k-on" + val } ) Vue.filter("filter_name2", (val, prefix) => {return prefix + val})
vm1 = Vue({
el:"#app1"
})
vm2 = Vue({
el: "#app2"
})
局部定义:某个Vue实例对象中定义,只有该Vue对象自己可以访问到并使用。
<div id="app">
<p> {{ data | filter_name1 }} </p> <p> {{ data | filter_name2("k-on-")}} </p> </div> // 私有的过滤器,在 Vue 对象内部定义。 var vm = new Vue({ el:"#app", filters:{ filter_name1: (data)=>{ return "k-on-" + data }, filter_name2: (data, prefix){ // 带参数的过滤器,第一形参是被过滤对象,我们传入的参数从第二个位置开始接收 return prefix + data } } })
如果私有过滤器和全局的过滤器名字重复,优先使用自己的过滤器。