单文件使用Vue

入门

单文件使用Vue只需导入cdn即可

引入Vue后会在全局新增一个构造函数Vue(),使用该函数创建实例使用Vue
该函数只接收一个对象类型的参数,这种对象称为配置对象,可以通过修改属性值来配置Vue
例:

<!-- 创建一个容器 -->
<div id="box">
	<h1>{{name}},{{age}}</h1>
</div>
// 创建一个Vue实例
const v=new Vue({
	el:'#box',//指定当前容器为哪个容器服务
	data:{//用于存储数据,供指定容器使用
		name:"Orange",
		age:18
	}
})
    <div id="vm">
        <p>{{number+1}}</p>
        <p>{{ok?'Ture':'False'}}</p>
        <p>{{message.split('')}}</p>
        <p>{{message.split('').reverse()}}</p>
        <p>{{message.split('').reverse().join('')}}</p>
    </div>
    <script>
        new Vue({
            el:'#vm',
            data: {
                number: 9,
                ok: false,
                message: "ABC",
            }
        })
    </script>

但是不能使用JS代码,如if(){} for(){}
模板语法详见单文件使用Vue#模板语法
显示的效果为:

10
False
[ "A", "B", "C" ]
[ "C", "B", "A" ]
CBA
说明:

.split().reverse().join()都是内置方法,输出结果分别为:
[ "A", "B", "C" ] [ "C", "B", "A" ] CBA

模板语法

插值语法

即使用关键字{{}}替换

 <div id="vm">
	<h1>{{name}}</h1>
</div>
<script>
	new Vue({
		el:'#vm',
		data: {
		   name:"Orange"
		}
	})
</script>

除了可以插入data中的数据,还可以插入methods中的方法,相当于亲自调用回调函数

<p id="pid" >{{showName()}}</p>

注意这个和事件绑定的函数调用不同,不加()就是相当于没有调用函数

指令语法

<div id="vm">
	<h1>{{fellows.name}}</h1>
	<h1><a v-bind:href="fellows.url">点击跳转</a></h1>
</div>
<script>
	new Vue({
		el: '#vm',
		data: {
			fellows:{//设计为多级结构有利于避免重复
				name: "Orange",
				age:18,
				url: "https://www.runoob.com/"
			}
		}
	})
</script>

v-bind将引号中的字符转换为语句执行,将执行结果绑定给href
另外v-bind:可以简写为:

总结

插值语法常用于标签体内容
指令语法常用于标签属性,解析标签

数据绑定

单向数据绑定

v-bind可以实现数据绑定,但是只能单向,即标签内容改变不会改变data中的值

<input type="text" v-bind:value="name">

双向数据绑定

v-model可以实现双向绑定,但是不会改变源代码,刷新后又会恢复

<input type="text" v-model:value="name">

v-model但是该指令只能应用于表单类元素上,防止页面原有元素被改变,换言之该指令只能绑定在value这个属性上
由于特定绑定属性的特性,可以简写为:

<input type="text" v-model="name">

MVVM模型

Pasted image 20231206162140.png

<div id="vm">
	<h1>{{fellows.name}}</h1> <!-- 视图 -->
	<h1><a v-bind:href="fellows.url">点击跳转</a></h1>
</div>
<script>
	new Vue({//视图模型
		el: '#vm',
		data: {//模型
			fellows:{
				name: "Orange",
				age:18,
				url: "https://www.runoob.com/"
			}
		}
	})
</script>
Note

  • 关系:
    • 模型通过视图模型实现DOM操作
    • 视图通过视图模型接受模型监听
  • 发现:
    • data中的所有属性最后都出现在vm中
    • vm中的属性及Vue原型中的属性都可以在Vue模板中直接调用

在浏览器的控制台中可以验证,访问vm发现name和age都变成了vm中的属性:
Pasted image 20231206202323.png|300

数据代理

基本数据代理技术

数据代理技术涉及Object的方法defineProperty(),详见[[Object#defineProperty()]]

var obj1 = { x: 100 };
var obj2 = { y: 200 };
Object.defineProperty(obj2, 'x', {
	get() {
		return obj1.x;
	},
	set(value) {
		obj1.x = value;
	},
})

Vue中的数据代理

在Vue中,data中的数据会放入创建的Vue实例中,即#MVVM模型中的vm中,同时还会创建响应的getter和setter函数用于访问和修改数据
Pasted image 20231206202347.png|300
所以通过控制台或脚本可以直接修改data中的属性值
Pasted image 20231206203442.png
创建实例后,Vue会先将data中的数据存入新建对象的 _data 属性中,所以实际上{{_data.name}}也可以正常访问name,但是为了写起来方便,又在外部创建了name和age属性,通过数据代理技术实现对_data中的数据进行访问和修改

事件处理

基本使用

在实例中配置事件的回调函数即可在其控制的容器中使用这个回调函数:

<div id="box">
	<button v-on:click="showInfo">click!</button>
	<button @click="showInfo2(66)">click!</button>
</div>
<script>
	const vm = new Vue({
		el: '#box',
		methods: {
			showInfo() {
				alert("Hello");
			}
			showInfo2(number) {
				alert("Hello ",number);
			}
		}
	})
</script>

事件修饰符

实例:超链接点击时有一个跳转的默认事件一般在方法中定义属性preventDefault()来阻止默认事件

<div id="box">
	<a href="https://zh.wikipedia.org" @click="alert">wikipedia</a>
</div>
<script>
	const vm = new Vue({
		el: '#box',
		methods: {
			alert(e) {
				e.preventDefault();
				alert("Hello");
			}
		}
	})
</script>

更加优雅的方法就是使用事件修饰符prevent

<div id="box">
	<a href="https://zh.wikipedia.org" @click.prevent="alert">wikipedia</a>
</div>
<script>
	const vm = new Vue({
		el: '#box',
		methods: {
			alert(e) {
				alert("Hello");
			}
		}
	})
</script>

Vue中提供了6个事件修饰符:

 <div id="box" @click="alert">
	<button @click.stop="alert">click</button>
 </div>
Note

修饰符可以连续写

键盘事件

实例:下面的代码实现只有按下enter键才显示输入的内容

<div id="box">
	<input type="text" @keyup="show">
	<p id="pid"></p>
</div>
<script>
	const vm = new Vue({
		el: '#box',
		methods: {
			show(e) {
				console.log(e.keyCode)
				if (e.keyCode === 13) {
					var p = document.getElementById('pid');
					p.innerHTML = e.target.value;
				}
			}
		}
	})
</script>

实际上可以使用键盘事件修饰符来代替判断语句:

<input type="text" @keyup.enter="show">

计算属性与监视属性

a## 计算属性
实例:
姓名案例
模糊搜索
列表排序

<p id="pid">{{fullName}}</p>

get()

computed: {
	fullName: {
		get() {
			return this.first + '-' + this.last;
		}
	}
}

相比其他方式,计算属性有一个缓存特性,即若前面已经调用过该方法,后面就不会重复调用,而是直接读取缓存中的内容,如下面的计算属性被调用了4次,但是只有一次get()方法的调用

<p id="pid">{{fullName}}</p>
<p id="pid">{{fullName}}</p>
<p id="pid">{{fullName}}</p>
<p id="pid">{{fullName}}</p>

set()

get()用于属性被调用时的响应,set()用于属性被改变时的响应,不是必须
和[[Object#defineProperty()]]相同,参数是被改变后的值

Warning

get()set()不能写成箭头函数,此时this就是window,无法访问到实例对象

简写:
当确定不会改变计算属性时 ,可以采用简写形式,将计算属性写为一个函数,实现get()的功能

 computed: {
	fullName(){
			return this.first + '-' + this.last;
		}
	}
}

总结

监视属性

实例:
天气案例
模糊搜索

监视属性可以监视所有属性值,如还可以监视计算属性的变化

watch: {
	info: {
		handler(a, b) {
			console.log(a, b);
		}
	}
}

监视多级结构中某个属性的变化

// 内部的写法
watch: {
	'number.a': {
		handler(a, b) {
			console.log('a: ', a, b);
		}
	},
}
// 外部的写法
vm.$watch('number.a', {
	handler(a, b) {
		console.log(a, b);
	}
});

简写:
配置项只需要handler时,可以采用简写写法:

// 内部
watch: {
	isHot(a, d) {
		console.log(a, b);
	}
}
// 外部
vm.$watch('isHot', function (a, b) {
	console.log(a, b);
})

注意不能使用箭头函数,会造成this指向问题

watchcomputed的比较

计算属性可以更加简单的实现属性的修改等操作,而监视属性则可以轻松的实现异步任务,如:
实现延迟1秒输出结果:

// watch实现
handler(n, o) {
	setTimeout(() => {
		this.fullname = n.first + '-' + n.last;
	}, 1000);
}
// computed实现
fullname() {
	setTimeout(() => {
		return this.name.first + '-' + this.name.last;
	}, 1000)
}

事实上计算属性无法实现这个任务,返回值被setTimeout接收,而fullname没有返回值
注意这里的setTimeout()的回调函数一定要写成箭头函数,否则this就会指向window

总结

数据更新检测的原理

监测对象

Vue.set()

详见API—Vue.js—Vue.set()

// 选项式API
Vue.set(target,key,val) 
// 命令式API
vm.$set(target,key,val)
Vue.config.productionTip = false
const vm = new Vue({
	el: '#box',
	data: {
		student: {
			name: "Orange",
		},
	}
});
Vue.set(vm.student, 'sex', 'male');
vm.$set(vm.student, 'sex', 'male');

由于存在数据代理,直接索引vm中的数据即可,无需在_data中寻找

监测数组

和对象属性不同,Vue中的数组属性不依靠get和set函数进行监视,而是监测数组对象push() pop() shift() unshift()等是否被调用,若调用则更新数据

数组方法详见Array对象

Vue使用了包装的技术,实际上在Vue中调用的方法已经不是原生的数组方法,Vue将其进行了包装,以便调用时可以检测到
列表渲染

在上一小节Vue.set()中发现参数还可以传数组和索引,所以实际上也可以使用Vue.set()函数实现数组元素的修改

样式绑定

class 绑定

style 绑定

<div v-bind:style="styleObject"></div>
data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

一道小题:颜色变换

条件渲染

指令

v-ifv-else-if

v-if值也为布尔值

<button @click="n<=3?n++:n=1">我爱你</button>
<h1 v-if="n===1">我</h1>
<h1 v-else-if="n===2">爱</h1>
<h1 v-else-if="n===3">你</h1>
<h1 v-else>haha</h1>

不同点在于不是隐藏样式,而是彻底使其消失

注意

注意该语法不允许中间有其他语句,打断后面的都会失效

v-show

值为布尔值或结果为布尔值的表达式,也可以调用实例中定义的方法

<h1 v-show="false">My name is {{name}} !</h1>

底层是调整了dispaly属性值
在控制台查看时源码变为:

<h1 style="display: none;">My fucking name is Orange !</h1>
建议

若内容需要频繁切换建议使用v-show,存在效率的问题

在需要同时对多个内容进行条件渲染时,若使用div标签统一显隐会破坏原来的css代码结构,此时可以使用<template>标签,特点是解析后就会消失,不会破坏原有css代码的功能,但是只能配合v-show使用

<template>
	<h1>爱</h1>
	<h1>你</h1>
	<h1>我</h1>
	<h1>haha</h1>
</template>

列表渲染

基本列表

v-for

实现循环执行指令
基本语法:形参 in 数据集,数据集的大小即为执行次数
形参有两个,数据和唯一的编号:(Obj,index) in 数据集
下面的示例是遍历数组

<body>
    <div id="box">
        <ul>
            <li v-for="(per,index) in perosnList":key="index">
	            {{per.name}}-{{per.age}}
            </li>
        </ul>
    </div>
    <script>
        Vue.config.productionTip = false
        const vm = new Vue({
            el: '#box',
            data: {
                name: "Orange",
                perosnList: [
                    { id: '001', name: 'Lihua', age: 18 },
                    { id: '002', name: 'Xiaoming', age: 19 },
                    { id: '003', name: 'Laoba', age: 17 }
                ],
            },
        })
    </script>
</body>

另外遍历对象也比较常用

key

一个形象的例子:

<div id="box">
	<h1>Orange</h1>
	<button @click.once="add">click!</button>
	<ul>
		<li v-for="(per,index) in perosnList" key="per.id">
			{{per.name}}-{{per.age}}
			<input type="text">
		</li>
	</ul>
</div>
<script>
	Vue.config.productionTip = false
	const vm = new Vue({
		el: '#box',
		data: {
			name: "Orange",
			perosnList: [
				{ id: '001', name: 'Lihua', age: 18 },
				{ id: '002', name: 'Xiaoming', age: 19 },
				{ id: '003', name: 'Laoba', age: 17 }
			],
		},
		methods: {
			add() {
				var p = { id: '004', name: 'Xiaohaha', age: 2.5 };
				this.perosnList.unshift(p);
			}
		}
	})
</script>

在输入框输入数据后点击按钮添加新的数据
Pasted image 20231214032418.png|300
如果将key值设置为index或不设置key,会发现
Pasted image 20231214032437.png|300
发生了错位

列表过滤

实例见模糊搜索列表排序
实际上就是使用数组和字符串方法操作

收集表单数据

使用v-model实现双向数据绑定来获取表单数据
v-model默认获取value值,对于一些没有value值的输入类型,需要人为指定,如单选框:

性别:
男<input type="radio" name="gender" v-model="userInfo.gender"value="male">
女<input type="radio" name="gender" v-model="userInfo.gender" value="female"> 

复选框也需要相应改变

爱好:
学习<input type="checkbox" v-model="userInfo.hobby" value="study">
打游戏<input type="checkbox" v-model="userInfo.hobby" value="game">
吃饭<input type="checkbox" v-model="userInfo.hobby" value="eat">

相应的在data中需要将hobby设置为数组,因为hobby初始值会改变表单的值

data:{
	userInfo:{
		hobby:[],
	}
},

对于不需要收集值的数据可以不设置value,自动读取true或false

使用按钮也可提交表单,使用时间绑定配置提交相应,并阻止刷新页面的默认行为(prevent)
<form @submit.prevent="demo">

修饰符

.lazy

在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步,可以添加 lazy 修饰符,从而转为在 change 事件之后进行同步:

<!-- 在“change”时而非“input”时更新 -->
<input v-model.lazy="msg"><br>

一般为失去焦点时

.number

如果想自动将用户的输入值转为数值类型,可以给 v-model 添加 number 修饰符:

<input v-model.number="age" type="number"><br>

这通常很有用,因为即使在 type="number" 时,HTML 输入元素的值也总会返回字符串。如果这个值无法被 parseFloat() 解析,则会返回原始的值

通常和表单number类型一起使用,使得只能输入数组并且存储为数字

年龄:<input type="number" v-model.number="userInfo.age"> 

.trim

如果要自动过滤用户输入的首尾空白字符,可以给 v-model 添加 trim 修饰符:

<br><input v-model.trim="msg"><br>

过滤器

实例见格式化时间#单文件使用Vue 过滤器 过滤器 实现

过滤器本质是一个函数

<h1>{{time | timeFormater}}</h1>

|分隔数据和过滤器,过滤器的返回值替换掉整个插值语法的内容,接受的是前方的数据

<body>
    <div id="root">
        <h1>当前时间为:</h1>
        <h1>{{time | timeFormater('YYYY年MM月DD日')|onlyYear}}</h1>
    </div>
    <script>
        const vm=new Vue({
			el:'#root',
            data:{
                time:Date.now()
            },
            filters:{
                timeFormater(value,format){
                    return dayjs(value).format(format);
                },
                onlyYear(value){
                    return value.slice(0,4);
                }
            }
		})
    </script>
</body>

上面的写法是局部过滤器,只能本实例调用,下面给出全局过滤器写法

Vue.filter('onlyYear',function(value){
	return value.slice(0,4);
})

注意全局过滤器需要一个一个定义,并且需要在可能调用该过滤器的实例之前声明

指令

内置指令

Vue2-指令

v-on

单文件使用Vue#事件处理

v-bind

单文件使用Vue#单向数据绑定

v-model

单文件使用Vue#双向数据绑定

v-text

向所在标签插入文本,替换整个标签中的内容,作为文本解析,不会作为vue语法解析

<h1 v-text="name"></h1>

可以实现和插值语法相同的功能,但是不如插值语法灵活

v-html

改变指定标签的innerHTML,作为html文本解析,不会作为vue语法解析

严重注意:v-html有安全性问题

在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击
一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!

v-cloak

不是clock :-(

[v-cloak] {  
	display: none;
}
<div v-cloak>{{ message }}</div>

不会显示,直到编译结束

v-once

<h1 v-once>初始值为:{{n}}</h1>
<h1>现在为:{{n}}</h1>
<button @click="n++">点我n+1</button>

一道小题:优化性能的指令

v-pre

自定义指令

应用见自定义指令

语法

局部定义

在实例对象内部的属性directives中定义
和过滤器的逻辑类似内部注册的指令只能本实例调用

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

全局定义

和过滤器的逻辑类似,在外部定义时为单数,一个一个注册,其后注册的实例均可以调用

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

钩子函数

对象内部的函数称为钩子函数,关于钩子函数有一篇博客写的比较清晰:Vue中的钩子函数,要配合生命周期理解

钩子函数的thiswindow

钩子函数参数

钩子函数均接收如下几个参数:

对象式是最标准的写法,可以照顾到很多细节问题,如何时调用什么函数等
如果实现较为简单的功能,可以使用函数形式

所以如果没有在插入时调用的需求,就可以直接写为函数式