拷贝上一节写好的 工程文件到新的项目目录下,按照目录结构存放这些文件。
后面所有编写的代码都会针对 App.vue文件来进行编辑。 组织好以后,打开命令行工具,执行 cnpm i
安装已经在package.json文件中声明的包文件到 node_modules 文件夹中
src/componetns/App.vue文件内容,
<template>
<div>
div>
template>
<script>
export default {
data() {
return {
}
}
}
script>
<style scoped>
style>
src/main.js文件内容
import Vue from 'vue/dist/vue.js'
import App from './components/App.vue'
Vue.component("App",App)
new Vue({
el: '#app',
template: ' ',
})
index.tpl 文件内容:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Vue入门title>
head>
<body>
<div id="app">div>
body>
html>
Vue模板中代码都是符合 HTML规范的,但是在Vue的底层实现上Vue 将模板编译成虚拟 DOM 渲染函数。也就是说template中看起来像DOM元素的标签会被Vue编译成虚拟DOM对象,然后Vue会把这些虚拟DOM渲染成真实的DOM交给浏览器。
所以应该正确看待tmplate中的HTML代码,它们实际上是会被Vue处理的虚拟DOM,只不过看起来像HTML。
Vue中的数据绑定就是将 Vue中 的data,methods等与模板中的虚拟DOM进行绑定。模板中无非就是在两个地方需要进行绑定:
绑定就是将Vue模型中的对象绑定到 标签属性和标签里面的文本上。
使用 {{ JavaScript表达式 }}
的语法, 前面一直在使用这种方式, 这种语法只能绑定标签文本内容的值,不能用在标签的属性上,要想在标签属性上使用绑定,则应该使用 Vue指令进行绑定。 {{ }} 输出的时候会对字符串进行原样输出。 如果要输出一些HTML标签字符串在页面上显示,就不能用 {{ }}的方式,需要用到 指令(Directives) v-html 来输出
在这种绑定中Vue完全支持JavaScript表达式,如:(来源于官方文档 https://cn.vuejs.org/v2/guide/syntax.html)
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
比如要将data中的变量绑定到 div的id属性上,此时需要用到 v-bind 指令,如果要为标签绑定事件处理函数,则需要用到v-on指令。
下面的例子中为div标签绑定了id属性和 click事件,当点击div之后执行函数,渲染一段html文本到div上,浏览器会解析HTML代码
src/components/App.vue
<template>
<div>
<div v-bind:id="idShowText">
{{ msgText }}
div>
<div v-bind:id="idShowHTML" v-on:click="showMsg">
<p v-html="msgHTML" />
div>
div>
template>
<script>
export default {
data() {
return {
idShowText: "idShowText",//给div的id属性的值
idShowHTML: "idShowHTML",
msgText: '这是一段文本字符串
包含的HTML标签会原样显示出来',
msgHTML: '点击这个标签后显示的字符串包含HTML,这个HTML会被浏览器解析'
};
},
methods: {
showMsg(){
this.msgHTML = ' 这是一个Span标签' //修改msgHTML变量的内容
}
}
};
script>
<style scoped>
#idShowText,#idShowHTML{
width: 500px;
height: 50px;
border: 1px solid red
}
#idShowHTML {
cursor: pointer;
}
style>
npm run dev
启动服务,效果如下:
点击下面的那个div,发现输出的 标签被浏览器给解析了,使用 {{ }} 语法的原样输出来了。
标签的一般属性绑定使用 v-bind: 属性名,事件绑定用 v-on : 事件名
事件名称为 标准DOM事件名称去掉on之后的字符串。如 onclick ==> click
上面的 v-bind:id=“idShowText” 可以简写为 :id= “idShowText” , v-on:click=“showMsg” 可以简写为
@click=“showMsg”
后面双引号,当然也可以是单引号中的内容不要当成字符串,它实际上还是JavaScript表达式。注意v-html是没有简写的,因为它根本就不是在绑定属性,只是占用了标签属性的位置
<template>
<div>
<div :id="idShowText">
{{ msgText }}
div>
<div :id="idShowHTML" @click="showMsg">
<p v-html="msgHTML" />
div>
div>
template>
指令是模板语法的第二个主要概念。指令Directives是带有v-
前缀的特殊属性。指令的属性值是单个的JavaScript表达式(v-for)除外,而且必须添加到一个Vue模板元素才能生效。它的职责是当表达式的值发生改变时,来更新DOM元素。
官方文档 https://cn.vuejs.org/v2/api/#%E6%8C%87%E4%BB%A4 列出了Vue中能够使用的所有指令。
这里只对条件判断和循环做介绍,其它指令等用到了再做详细介绍
条件判断需要用到 v-if v-else v-else-if ,这与JavaScript语言中的条件判断类似。指令必须要作用到元素上才能生效
下面案例的功能是:点击按钮后,启动定时器,定时器每个1秒产生一个随机数,页面上根据随机数的范围显示不同的内容。
src/components/App.vue
<template>
<div>
<button @click="startTimer">启动定时器button>
<button @click="stopTimer">停止定时器button>
<div>
<template v-if="radomValue<=0.3">
随机值为: {{ radomValue }}
<div>
<span style="color: green">安全span>
div>
template>
<template v-else-if="radomValue>0.3 && radomValue<=0.6">
随机值为: {{ radomValue }}
<div>
<span style="color: orange">警告span>
div>
template>
<template v-else>
随机值为: {{ radomValue }}
<div>
<span style="color: red">危险span>
div>
template>
div>
div>
template>
<script>
let timer=null; //保存定时器
export default {
data() {
return {
radomValue: 0,
};
},
methods: {
startTimer(){
this.radomValue = Math.random() //产生一个随机数
timer=window.setTimeout(this.startTimer,1000) //1秒后再次执行startTimer
},
stopTimer(){
if(timer){
window.clearTimeout(timer) //停止定时器
timer=null //将定时器对象置为空
}
}
}
};
script>
v-for 主要用来对数组进行循环遍历。下面演示的效果为 先显示学生列表,点击按钮后按照年龄进行排序。
点击升序后,按照升序排列
点击降序后,按照降序排列
src/components/App.vue
<template>
<div>
<button @click="sort"> {{ order? '升序':'降序'}} button>
<ul>
<li v-for="(item,index) in students" :key="item.id">
{{index}}
{{item.name}}
{{item.age}}
li>
ul>
div>
template>
<script>
let students = [
{id: 1,name: '张三', age : 20},
{id: 2,name: '李四', age : 19},
{id: 3,name: '王五', age : 23},
{id: 4,name: '赵六', age : 18},
]
export default {
data() {
return {
order : true ,// true为升序,false为降序
students
}
},
methods: {
sort(){
this.students.sort((a,b)=> {
if(this.order){
return a.age-b.age
}else{
return b.age-a.age
}
})
this.order=!this.order
}
}
}
script>
可以看到,只要更改了data中的数据,页面会自动更新渲染。v-for 可以遍历一个数组,也可以遍历一个对象。后面有一个特殊的属性 :key ,这个属性是用来标识虚拟DOM元素的。我们知道这里的元素会被Vue 编译成虚拟DOM,有了 key属性后可以唯一的标识这个属性,当数据发生变化的时候,Vue会重新渲染DOM,有了DOM的唯一标识后,当数据发生变化,Vue在处理这些虚拟DOM的时候,可以很方便的找到元素。更新已渲染过的元素列表时,它默认用“就地复用”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
对于数组,以下的方法执行后,会更改原有数组对象的内容,此时视图界面会重新渲染。
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
数组的以下方法执行后会返回一个新的数组,原有数组对象内容不会发生变化,界面不会引发界面元素的更新。
这种情况下,只需要将返回的新的数组赋值到为data中替换原有数组即可引发视图页面的更新。
详细说明请看官方文档 https://cn.vuejs.org/v2/guide/list.html#%E6%95%B0%E7%BB%84%E6%9B%B4%E6%96%B0%E6%A3%80%E6%B5%8B
Vue在检测到数组变化时,并不是直接重新渲染整个列表,而是最大化复用DOM元素。替换的数组中,含有相同元素的项不会被重新渲染,所以当用新数组替换旧数组的时候,不用担心性能问题。这也是为什么 v-for 循环的时候总是要设置一个 :key
的原因所在。
以下数组的变动,Vue无法检测到,不会触发视图更新
- 通过索引直接为数组赋值。比如:this.students[0]={ … }。
- 修改数组的长度,如: this.students.length=1
要想更改数组元素引发视图更新,可以使用Vue对象提供的静态set方法,比如:
Vue.set(this.students,0,{...})
但是上面的 src/components/App.vue组件中因为没有import Vue 对象,所以无法直接使用Vue对象。 但是App.vue是一个组件,组件实例化之后实际上是一个 Vue的实例对象。实例对象提供了 s e t 方 法 ( 注 意 前 面 有 一 个 set方法(注意前面有一个 set方法(注意前面有一个符号,参看官方文档 https://cn.vuejs.org/v2/api/#%E5%AE%9E%E4%BE%8B%E6%96%B9%E6%B3%95-%E6%95%B0%E6%8D%AE)
也可以使用数组对象的splice方法来解决。 splice是从数组中添加或删除项目,然后返回被删除的项目或者添加过的项目。而且该方法会改变原数组
let students = [
{id: 1,name: '张三', age : 20},
{id: 2,name: '李四', age : 19},
{id: 3,name: '王五', age : 23},
{id: 4,name: '赵六', age : 18},
]
let s=students.splice(0,2) //第一个参数表示开始索引,第二个参数表示个数。其含义就是从students数组中删除2个元素(第二个参数指定),从索引为0 的位置开始(第一个参数指定)。操作后返回的s为一个新的数据,就是被删掉的数组('张三'和'李四')。students数组剩下 '王五'和'赵六'两个元素,王五的索引为0,赵六的索引为1
/*
s中的内容
0: {id: 1, name: "张三", age: 20}
1: {id: 2, name: "李四", age: 19}
students中的内容
0: {id: 3, name: "王五", age: 23}
1: {id: 4, name: "赵六", age: 18}
*/
//从索引0开始,删除1个元素,然后插入后面的第三个参数和第四个参数,第二个参数后面都表示要插入的数据,可以有多个
s= students.splice(0,1,{id:3,name:'王5',age:24},{id:5,name:'孙七',age:28})
/*
s中的内容
0: {id: 3, name: "王五", age: 23}
students中的内容
0: {id: 3, name: "王5", age: 24}
1: {id: 5, name: "孙七", age: 28}
2: {id: 4, name: "赵六", age: 18}
*/
对于v-on:xxx=“yyy” 指令,前面已经使用过了,也可以简写成 @xxx=‘yyy’ ,yyy可以有如下写法:
总之,将yyy看成是JavaScript代码即可。
如果希望在事件处理函数中能访问到事件对象该如何操作?Vue提供了一个特殊的变量$event,这个变量用于访问原生DOM事件对象,可以在调用事件处理函数的时候,作为参数传递进去,比如:
<button @click="startTimer($event)" />
methods:{
startTimer(event){
....
}
}
当一个事件发生时,调用指定的事件处理函数,将事件对象$event传入事件处理函数,此时如果希望阻止DOM元素默认行为,就需要调用 event.preventDefault() 来处理,比如 连接 ,当发生click事件后,a元素默认行为就是跳转到 href指定的地址上,要阻止默认事件发生,就需要这样来写:
<A href='http://www.163.com' @click="handle($event)" />
methods:{
handle(event){
event.preventDefault() //阻止默认行为
}
}
Vue中类似于对事件对象的方法调用,可以使用修饰符来处理。其格式为: @xxx.zzz=‘yyy’
xxx为事件名称
zzz为修饰符,可选的值有:
yyy为事件处理函数。
使用修饰符的时候,事件处理函数不是必须的,也就是说 yyy部分可以省略。
修饰符可以串联,但是使用的时候要注意串联的顺序,要知道修饰符其实相当于调用event对象的方法。比如:
<A href='http://www.163.com' @click.stop.prevent />
在监听键盘事件的时候,键盘上的每一个键都有一个keyCode(一个数字)比如:
<input v-on:keyup.13="handle">
为了在必要的情况下支持旧浏览器,Vue 提供了绝大多数常用的按键码的别名:
.enter
.tab
.delete
(捕获“删除”和“退格”键).esc
.space
.up
.down
.left
.right
官方文档: https://cn.vuejs.org/v2/guide/events.html#%E6%8C%89%E9%94%AE%E7%A0%81
每个元素上都有class属性和style属性,可以使用 v-bind来绑定:
<div v-bind:class='' v-bind:style=''>div>
也可以简写为:
<div :class='' :style=''>div>
:class和 :style绑定的时候,可以绑定对象和数组。
<div :class="{ active: isActive, 'text-danger': hasError }">div>
<div class='myClass' :class="{ active: isActive, 'text-danger': hasError }">div>
也可以直接指定一个对象:
<div :class="classObject">div>
data: {
classObject: {active: true,'text-danger': false}
}
也可以为 :class指定一个数组,数组的元素所代表的就是 class类名
<div :class="[activeClass, errorClass]">div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
数组中的元素其实也可以是一个对象,这是对象与数组混合的写法
<div v-bind:class="[{ active: isActive }, errorClass]">div>
data: {
isActive: true,
errorClass: 'text-danger'
}
style绑定与class绑定类似,都可以使用对象和数组的语法。是对象的时候有一些小小的区别:
<div :style="{ color: activeColor, fontSize: fontSize + 'px' }">div>
data: {
activeColor: 'red',
fontSize: 30
}
直接绑定一个对象:
<div :style="styleObject">div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
数组:
<div :style="[baseStyles, overridingStyles]">div>
baseStyles与overridingStyles 是对象。对象中包含css属性。
Vue中表单输入元素可以使用v-model 将 data中的数据与输入元素进行双向绑定。所谓双向绑定就是当表单输入元素(如文本框 input type=text )的value与 data中的数据进行绑定,这个绑定是双向的。一方的改变,另外一方会跟着改变。
下面来做一个实验. 文本框绑定msg变量,msg变量中有默认值。 input框用 v-model让它与msg绑定。 下面用 {{ msg}} 输出 msg变量中的值。 当按钮按下的时候,修改msg的值。
<template>
<div>
这里输入的值与msg模型数据进行了双向绑定 :<input type="text" v-model="msg">
<br />
<div>
msg变量的值: {{ msg}}
div>
<br />
<button @click="msg='我是被按钮修改过的msg值'">修改msg的值button>
div>
template>
<script>
export default {
data() {
return {
msg:'msg默认值'
}
}
};
script>
当修改文本框的值后,发现下面 用 {{ msg }} 输出的值也跟着变了,当点击按钮后, 文本框中的值和 {{ msg }} 显示的也是一致的。
change
事件进行同步:
<input v-model.lazy="msg" >
<input v-model.lazy="msg" >
有时需要对data中定义的属性做一些加工处理的时候,当然可以在 methods中定义函数来对data中的数据进行加工,显示的时候调用函数来显示最终的结果。这种方式虽然可以,但是当数据发生变化的时候,这个函数就不会自动调用最终达到重新渲染的效果了。
此时可以通过定义计算属性来解决。计算属性用起来像属性,但实际上它是一个函数。
<template>
<div>
<button @click="chageTime">显示时间button>
<div> {{now}} div>
div>
template>
<script>
export default {
data() {
return {
time: new Date()
}
},
methods:{
chageTime(){
this.time=new Date()
window.setTimeout(this.chageTime,1000)
}
},
computed: {
now :function(){
return `${this.time.getHours()}:${this.time.getMinutes()}:${this.time.getSeconds()}`
}
},
};
script>
now 就是一个计算属性,放在 computed对象中,它本质上是一个函数,但是绑定模板的时候可以当属性用。
now这个计算属性关联了属性 time,当单机按钮后, changeTime每隔一秒更改time的值,可以看到计算属性now被调用,视图被重新渲染了。
虽然计算属性在大多数情况下都能满足需要,但有时也需要一个自定义的侦听器来监听data中属性的变化。 Vue 通过 watch
选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
src/components/App.vue
<template>
<div>
<button @click="chageTime">显示时间button>
<div> {{now}} div>
div>
template>
<script>
export default {
data() {
return {
time: new Date()
}
},
methods:{
chageTime(){
this.time=new Date()
window.setTimeout(this.chageTime,1000)
}
},
computed: {
now :function(){
return `${this.time.getHours()}:${this.time.getMinutes()}:${this.time.getSeconds()}`
}
},
watch: {
//监听data 中的time属性变化
time: function(newTime, oldTime){
console.log(`newTime : ${newTime}, oldTime: ${oldTime}`)
}
}
};
script>
可以看到watch 中定义的 time与data 中的定义的time是一致的,表示 要监听 data中的time。 watch中的time是一个函数,这个函数有两个参数,一个是变化后的值,一个是变化前的值。
运行后可以看到,只要time值发生变化,就会新旧值都会通过 console 输出