官网
Vue:一套用于构建用户界面的 渐进式 JavaScript框架
渐进式:vue可以自底向上逐层的应用
简单应用:只需要一个轻量小巧的核心库
复杂应用:可以引入各式各样的vue插件
声明式编码:给一个声明,它就自己去完成所有命令
命令式编码:一步一步听指挥去执行命令
如果之前有虚拟DOM,现在有新的虚拟DOM,会把新的与旧的比较(DIff算法),如果有相同的就复用虚拟DOM
vue文件引入
关掉Vue控制台提示
- 想让Vue工作,就必须创建一个Vue实例,且要传入一个配置对象;
- root容器里的代码依然符合html规范,只不过混入了一些特殊的Vue语法;
- root容器里的代码被称为Vue模板;
- 先有的容器,然后再有Vue实例
容器的两个作用:
- Vue实例和容器是一一对应的;
- 真实开发中只有一个Vue实例,并且会配合着组件一起使用;
- 插值语法 {{xxx}}中的xxx要写js表达式,且xxx可以自动读取到data中的所有属性
- 一旦data中的数据发生改变,那么页面中用到该数据的地方也会自动更新
区分JS表达式 和 JS代码(语句)
表达式:一个表达式会产生一个值
,可以放在任何一个需要值的地方
(1). a
(2). a+b
(3). demo(1)
(4). x === y ? ‘a’ : ‘b’
JS代码(语句): 能控制代码能否执行
(1). if(){}
(2). for(){}
表达式是特殊的JS语句
容器里面的代码就是模板语法
功能:用于解析标签体内容。
写法:{{xxx}}
,xxx是 js表达式,且可以直接读取到data
中的所有属性。
功能:用于解析标签(包括:标签属性、标签体内容、绑定事件…)
备注:Vue中有很多的指令,且形式都是:v-???
,例如v-if
举例:v-bind:hred="xxx"
或 简写为 :href="xxx"
,xxx同样要写js表达式,且可以直接读取到data中的所有属性。
<body>
<!-- 准备好一个容器 -->
<div id="root">
<h1>插值语法</h1>
<h3>你好,{{name}}</h3>
<hr />
<h1>指令语法</h1>
<!-- v-bind: 可以给标签里的任何一个标签属性动态的绑定一个值 -->
<a v-bind:href="bili.url.toUpperCase()">{{bili.name}}</a>
<a :href="bili.url">bilibili2</a>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
name: 'jack',
bili: {
name: "bilibili",
url: 'https://www.bilibili.com/'
}
}
})
</script>
</body>
Vue中有2种数据绑定的方式
v-bind
):数据只能从data流向页面。v-model
):数据不仅能从data流向页面,还可以从页面流向data。备注
双向绑定一般都应用在表单元素上(如:input
、select
等)
v-model:value
可以简写为 v-model
,因为v-model默认收集的就是value值
<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">
<!-- 如下代码是错误的,因为v-model 只能应用在表单类元素(输入类元素)上 (有value值的) -->
<!-- <h2 v-model:x="name">你好啊</h2> -->
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: "#root",
data: {
name: "尚硅谷"
}
})
</script>
</body>
vm.$mount(‘#root’)
指定el的值(mount—挂载) // el 的两种写法
const vm = new Vue({
// el: '#root', // 第一种写法
data: {
name: 'Vue'
}
})
console.log(vm);
// 可以回头在指定 进行关联,第二种更加灵活
setTimeout(() => {
vm.$mount('#root'); // 第二种写法
}, 1000);
一个重要的原则:
只要是Vue所管理的函数,一定不要写箭头函数,一旦写了箭头函数,this就不再是Vue实例了,而是window
// data 的两种写法
new Vue({
el: '#root',
// data的第一种写法:对象式
// data: {
// name: 'Vue'
// }
// data的第二种写法:函数式
data() { // ES6中简写函数
console.log('@@', this); // 此处的this是vue实例对象
return {
name: 'haha'
}
}
})
观察发现
data中所有的属性,最后都出现在了vm身上
vm身上所有的属性 及 Vue原型上所有属性,在Vue模板中都可以直接访问
参数 | 作用 |
---|---|
value | 设置属性值 |
enumerable | 控制属性是否可以枚举(遍历),默认值是false(不遍历) |
writable | 控制属性是否可以被修改,默认值是false(不被修改) |
configurable | 控制属性是否可以被删除,默认值是false |
get函数 | 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值 |
set函数 | 当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值 |
let number = 18;
let person = {
name: '张三',
sex: '男'
}
//
Object.defineProperty(person, 'age', {
value: 18,
enumerable: true, // 控制属性是否可以枚举(遍历),默认值是false
writable: true, // 控制属性是否可以被修改,默认值是false
configurable: true,// 控制属性是否可以被删除,默认值是false
// 当有人读取person的age属性时,get函数(getter)就会被调用,且返回值就是age的值
get: function () {
return number;
},
// 当有人修改person的age属性时,set函数(setter)就会被调用,且会收到修改的具体值
set(value) {
console.log('有人修改了age属性,且值是' + value);
number = value;
}
})
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
<!-- 通过操作obj2.x来对obj.x进行修改 -->
<script>
let obj = {
x: 100
}
let obj2 = {
y: 200
}
// 给obj2设置属性x
Object.defineProperty(obj2, 'x', {
// 当有人读取obj2的x属性时,返回obj.x
get() {
return obj.x;
},
// 当有人要修改obj2的x属性时,修改的值为value,让obj.x=value
set(value) {
obj.x = value;
}
})
</script>
vm
对象 代理 vm._data
对象 中的属性操作(读/写)Object.defineProperty
把data对象中所有属性都添加到vm上getter/setter
getter/setter
内部去操作(读/写)data中对应的属性。理解:
vm._data
和 我们传递的data
是同一个 一旦data中的数据发生改变,那么页面中用到该数据的地方会自动更新
- 使用
v-on:xxx
或@xxx
绑定事件,其中xxx是事件名;
比如或者
- 事件的回调需要配置在methods对象中,最终会在vm上;
- methods中配置的函数,不要用箭头函数!否则this就不是vm了而是window;
- methods中配置的函数,都是被Vue所管理的函数,this的指向是vm 或 组件实例对象;
@click="demo"
和@click="demo($ event)"
效果一致,前者默认参数就是事件对象,后者要用$event才能生成事件对象,并且可以传多个参数;- method上面的方法最终也会出现在vm上面,但它并没有作数据代理,因为没有必要,数据代理是可以修改数据的,函数是个固定的,只需要来调用就行,不用修改函数
<!-- 准备好一个容器 -->
<div id="root">
<h2>欢迎来到{{name}}学习</h2>
<!-- 当你点击这个按钮的时候,去找名为showInfo的函数去调用 -->
<button v-on:click="showInfo1">点我提示信息1(不传参)</button>
<!-- v-on:click简写形式,进行传参,然后用$event进行占位 -->
<button @click="showInfo2(66,$event)">点我提示信息2(传参)</button>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
name: 'bilibili'
},
methods: { // 配置事件回调
showInfo1(event) {
alert('同学你好!')
console.log(event.target); //发生事件的事件目标
console.log(this); // 此处的this 是vm(Vue实例对象)
},
showInfo2(number, event) {
console.log(number);
console.log(event);
}
}
})
</script>
prevent
阻止默认行为,相当于e.preventDefault()
<a href="http://www.bilibili.com" @click.prevent="showInfo">点我提示信息</a>
这里阻止了跳转至网页
stop
,相当于e.stopPropagation()
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">点我提示信息</button>
</div>
这里原来点击button后会弹出第一个弹框,然后会冒泡到div上,弹出第二个弹框,而现在阻止了冒泡
once
<button @click.once="showInfo">点我提示信息</button>
这里只点击第一次会有弹框,后面的再点击就不会有弹框了
capture
,因为默认是冒泡模式,从里到外,捕获模式是从外到里 <div class="box1" @click.capture="showMsg(1)">
div1
<div class="box2" @click="showMsg(2)">
div2
</div>
</div>
这里原来是冒泡模式,点击box2的时候会先输出2,然后输出1。然而现在是先输出1,再输出2
self
:只有event.target是当前操作的元素时才触发事件,也能阻止冒泡 <div class="demo1" @click.self="showInfo">
<button @click="showInfo">点我提示信息</button>
</div>
passive
:事件的默认行为立即执行,无需等待事件回调执行完毕 <!-- @scroll 是给滚动条加滚动事件 @wheel是鼠标滚轮滚动事件 -->
<ul @wheel.passive="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
...
demo() { // 会先进行demo函数里面的事件,然后再进行原来的默认滚动事件,所以加上了passive,优先滚动
for (let i = 0; i < 1000; i++) {
console.log('@');
}
}
scroll:鼠标、键盘、滚轮都可以触发,但滚到底部就不会在触发(默认=>回调)
wheel:只有滚轮可以触发,滚到底部依然可以触发(回调=>默认)
Vue中常用的按键别名
回车 => enter
删除 => delete(捕获“删除(delete)”和“退格(BackSpace)”键)
退出 => esc
空格 => space
换行 => tab(特殊,必须配合 keydown 去使用,因为keyup时会切位)
上 => up
下 => down
左 => left
右 => right
<input type="text" placeholder="按下回车提示输入" @keyup.enter="showInfo">
<input type="text" placeholder="按下删除提示输入" @keyup.delete="showInfo">
<input type="text" placeholder="按下退出提示输入" @keyup.esc="showInfo">
key
值去绑定,但注意要转为kebab-case
(短横线命名),比如切换大小写,键盘上是 CapsLock
,但是我们要写成 @keyup.caps-lock
<input type="text" placeholder="按下切换大小写提示输入" @keyup.caps-lock="showInfo">
(1) 配合keyup使用:按下修饰键的同时,再按下其他键,随后释放其它键,事件才被触发。(此时的e.key值是其它键,而不是系统修饰键)
(2) 配合keydown使用:正常触发事件。
<input type="text" placeholder="按下ctrl提示输入" @keyup.ctrl="showInfo">
keyCode
去指定具体的按键(不推荐,因为不同的键盘编码可能会不统一,尽量用键名) <input type="text" placeholder="按下回车提示输入" @keyup.13="showInfo">
Vue.config.keyCodes.自定义别名 = 键码
,可以去指定按键别名 Vue.config.keyCodes.huiche = 13; // 定义了一个别名回车
@click.prevent.stop
先阻止默认行为,再阻止冒泡
<div class="demo1" @click="showInfo">
<a href="http://www.bilibili.com" @click.prevent.stop="showInfo">点我提示信息</a>
</div>
<!-- 按 ctrl + y 才触发(系统修饰键 + 键名) -->
<input type="text" placeholder="按下回车提示输入" @keyup.ctrl.y="showInfo">
<!-- 准备好一个容器 -->
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"><br /><br />
全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
}
})
</script>
methods实现:
这里是在插值语法里面调用了fullName()
函数
methods函数出现几次就调用几次,效率比较低。
<!-- 准备好一个容器 -->
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"><br /><br />
<!-- 插值语法里面调用函数 -->
全名:<span>{{fullName()}}</span>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
methods: {
fullName() {
return this.firstName + '-' + this.lastName; // this指向vm
}
}
})
</script>
计算属性实现:
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"><br /><br />
全名:<span>{{fullName}}</span>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: { // 计算属性
fullName: {
// get的作用:当有人读取fullName时,get就会被调用,且返回值作为 fullName的值
// get 什么时候调用?1.初次读取fullName时。2.所依赖的数据发生变化时
get() { // get中的this指向也是 vm
console.log('get被调用了');
return this.firstName + '-' + this.lastName;
},
// set的作用:当有人修改fullName的时候,set会被调用
set(value) {
console.log('set', value);
const arr = value.split('-'); // 把字符串拆成数组,用-分隔的
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
1.定义:要用的属性不存在,要通过已有属性计算得来
2.原理:底层借助了Object.defineproperty
方法提供的getter
和setter
3.get的作用:当有人读取fullName
时,get就会被调用,且返回值作为fullName
的值
4.get函数什么时候执行?
(1) 初次读取时会执行一次
(2)所依赖的数据发生变化时会再次被调用
5. 优势:与methods
实现相比,内部有缓存机制(可以复用),效率更高,调试方便
6. 备注:
(1)计算属性最终会出现在vm
上,直接读取使用即可
(2)如果计算属性要被修改,那必须写set
函数去响应修改,且set
中要引起计算时依赖的数据发生改变
(3)这里fullName
后面不用加(),因为fullName
是一个计算属性,不是函数
只考虑读取,不考虑修改的时候才使用简写形式
每次的时候都要问一下是data中的数据,还是methods里的方法,还是computed中的计算属性
<div id="root">
姓:<input type="text" v-model="firstName"> <br /><br />
名:<input type="text" v-model="lastName"><br /><br />
全名:<span>{{fullName}}</span>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: { // 计算属性
fullName() {
console.log('get被调用了');
return this.firstName + '-' + this.lastName;
}
}
})
</script>
<!-- 准备好一个容器 -->
<div id="root">
<h2>今天天气很{{info}}</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
}
},
})
</script>
handler
自动调用,进行相关操作;监视的两种写法
1.new Vue时传入watch配置;
2.通过vm.$watch
监视;
// 写法一
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);
}
}
}
})
const vm = new Vue({
el: '#root',
data: {
isHot: true
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
}
},
})
// 写法二
vm.$watch('isHot', {
immediate: true, // 初始化时让handler调用一下
// handler什么时候调用?当 isHot 发生改变时
handler(newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
}
})
明确要监视哪些属性的时候用第一个。
创建实例的时候不知道要监视谁,后续根据用户的一些行为然后才知道要监视哪个属性就用第二个)
属性 | 描述 |
---|---|
immediate (立即的) | 初始化时让handlder调用一下 |
handler(newVal, oldVal) | 当监视的属性发生改变的时候调用,参数可以拿到改变前后的值 |
deep | 深度监听,可以监测多层级数据的改变 |
1.Vue中的
watch
默认不监测对象内部值的变化(监测一层)
2.配置deep:true
可以监测对象内部值变化(监测多层)
备注:
(1)Vue自身可以监测对象内部值的改变,但Vue提供的watch
默认不可以!
(2)使用watch时根据数据的具体结构,决定是否采用深度监视,默认是不开启深度监视
配置项只有
handler
的时候才可以简写
第一种简写
const vm = new Vue({
el: '#root',
data: {
isHot: true,
},
computed: {
info() {
return this.isHot ? '炎热' : '凉爽'
}
},
methods: {
changeWeather() {
this.isHot = !this.isHot;
}
},
// 第一种简写
watch: {
// 简写
isHot(newValue, oldValue) {
// immediate: true, // 初始化时让handler调用一下
// deep: true, // 深度监视
console.log('isHot被修改了', newValue, oldValue);
},
}
})
第二种简写
// 第二种简写
vm.$watch('isHot', function (newValue, oldValue) {
console.log('isHot被修改了', newValue, oldValue);
})
watch与computed并不冲突
computed
能完成的功能,watch
都能完成。watch
能完成的功能,computed
不一定能完成,例如:watch可以进行异步操作。(定时器)两个重要的小原则:
- 所被Vue管理的函数,最好写成普通函数,这样this的指向才是 vm 或 组件实例对象
- 所有不被Vue所管理的函数(定时器的回调函数,ajax的回调函数,promise的回调函数等),最好写成箭头函数,这样this的指向才是 vm 或 组件实例对象。
// 用 watch实现姓名案例
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三',
fullName: '张-三',
},
watch: {
firstName(newValue) {
setTimeout(() => {
this.fullName = newValue + '-' + this.lastName;
}, 1000);
},
lastName(newValue) {
this.fullName = this.firstName + '-' + newValue;
}
}
})
// 用 computed实现姓名案例
const vm = new Vue({
el: '#root',
data: {
firstName: '张',
lastName: '三'
},
computed: { // 计算属性
fullName() {
console.log('get被调用了');
return this.firstName + '-' + this.lastName;
}
}
})
写法:class="xxx"
xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定。
对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
<div id="root">
<!-- 绑定class样式---字符串写法,适用于:样式的类名不确定,需要动态指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div><br /><br />
<!-- 绑定class样式---数组写法,适用于:要绑定的样式个数不确定,名字也不确定 -->
<div class="basic" :class="classArr">{{name}}</div><br /><br />
<!-- 绑定class样式---对象写法,适用于:要绑定的样式个数确定,名字也确定,但要动态决定用不用 -->
<div class="basic" :class="classObj">{{name}}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
name: 'bilibili',
mood: 'normal',
classArr: ['bili1', 'bili2', 'bili3'],
classObj: {
bili1: false,
bili2: false
}
},
methods: {
changeMood() {
const arr = ['happy', 'sad', 'normal'];
// 随机生成0,1,2三个数字
// Math.random() 可以随机生成0~1(包括0,不包括1)
// *3 就可以生成0~3(不包括3)
// Math.floor() 向下取整
const index = Math.floor(Math.random() * 3);
this.mood = arr[index];
}
}
})
写法:
:style="{fontSize: xxx}"
其中xxx是动态值。(注意样式名得是小驼峰)
:style="[a,b]"
其中a、b是样式对象。
<div id="root">
<!-- 绑定style样式--对象写法 -->
<div class="basic" :style="styleObj">{{name}}</div><br><br>
<!-- 绑定style样式--数组写法 -->
<div class="basic" :style="styleArr">{{name}}</div>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
new Vue({
el: '#root',
data: {
name: 'bilibili',
styleObj: {
fontSize: '40px',
color: 'red',
},
styleArr: [
{
fontSize: '40px',
color: 'blue',
},
{
backgroundColor: 'pink'
}
]
},
})
</script>
v-if
写法:
(1)v-if=“表达式”
(2)v-else-if=“表达式”
(3)v-else=“表达式”
适用于:切换频率较低的场景。
特点:不展示的DOM元素直接被移除。
注意:v-if
可以和:v-else-if
、v-else
一起使用,但要求结构不能被“打断”。
v-show
写法:v-show=“表达式”
适用于:切换频率较高的场景。
特点:不展示的DOM元素未被移除,仅仅是使用样式隐藏掉
备注
使用 v-if 的时,元素可能无法获取到,而使用 v-show 一定可以获取到。
template只能配合 v-if 使用,不能用 v-show,后者会直接显示。
<!-- 使用v-show做条件渲染 相当于 display:none-->
<h2 v-show="false">欢迎来到{{name}}学习</h2>
<!-- 使用v-if做条件渲染 消失的彻底-->
<h2 v-if="false">欢迎来到{{name}}学习</h2>
<div id="root">
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
<!-- 需求:n为多少,展示哪个div -->
<!-- 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>HAHA</div>
<!-- v-if与template的配合使用 -->
<template v-if="n===1">
<h2>你好</h2>
<h2>哔哩哔哩</h2>
<h2>AHAHAHA</h2>
</template>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
new Vue({
el: '#root',
data: {
name: 'bilibi',
n: 0
}
})
</script>
v-for 指令:
1.用于展示列表数据
2. 语法:v-for=“(item, index) in xxx” :key=“yyy”
3.可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<ul>
<li v-for="(p,index) in persons" :key="p.index">
{{p.name}}-{{p.age}}
</li>
</ul>
<!-- 遍历对象 -->
<h2>汽车信息</h2>
<ul>
<li v-for="(value,key) in car" :key="key">
{{key}}-{{value}}
</li>
</ul>
<!-- 遍历字符串 -->
<h2>测试遍历字符串</h2>
<ul>
<li v-for="(char,index) in str" :key="index">
{{index}}-{{char}}
</li>
</ul>
<!-- 遍历指定次数 -->
<h2>测试遍历字符串</h2>
<ul>
<li v-for="(number,index) in 10" :key="index">
{{number}}-{{index}}
</li>
</ul>
</div>
<script>
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: 'Harry Potter'
}
})
</script>
面试题:react、vue中的key有什么作用?(key的内部原理)
使用key的原因是vue在模板解析的时候会有一个
diff
算法,新旧虚拟dom会根据key值进行对比,如果虚拟DOM中的key所对应的内容相同,就直接复用旧的真实DOM,不同则会新生成一个真实dom,这样是比较影响效率的,给key一个唯一的id就是为了避免出现这种问题。
虚拟DOM中key的作用
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据【新数据】生成【新的虚拟DOM】, 随后Vue进行【新虚拟DOM】与【旧虚拟DOM】的差异比较,比较规则如下:
对比规则:
(1)旧虚拟DOM中找到了与新虚拟DOM相同的key:
①若虚拟DOM中内容没变, 直接使用之前的真实DOM!(复用)
②若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM。
(2)旧虚拟DOM中未找到与新虚拟DOM相同的key
①创建新的真实DOM,随后渲染到到页面。*
用index作为key可能会引发的问题
(1)若对数据进行:逆序添加、逆序删除等破坏顺序操作:
会产生没有必要的真实DOM更新 => 界面效果没问题, 但效率低。
(2)如果结构中还包含输入类的DOM:
会产生错误DOM更新 => 界面有问题。
开发中如何选择key?
(1)最好使用每条数据的唯一标识作为key
, 比如id、手机号、身份证号、学号等唯一值。
(2)如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。
<div id="root">
<!-- 遍历数组 -->
<h2>人员列表</h2>
<button @click.once="add">点我添加老刘</button>
<ul>
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}
<input type="text">
</li>
</ul>
</div>
<script>
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 },
],
},
methods: {
add() {
const p = { id: '004', name: '老刘', age: 40 }
this.persons.unshift(p); // 往persons前面加上p
}
}
})
</script>
过程
- 收集用户的输入
- 将输入的数据进行匹配
收集用户的输入
用
v-model="keyWord"
对input框进行双向绑定,当输入框的value值发生变化时,Vue中的keyWord会有value值
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字进行模糊搜索" v-model="keyWord">
<ul>
<li v-for="(p,index) in filPersons" :key="p.id">
{{p.name}}-{{p.age}}--{{p.sex}}
</li>
</ul>
</div>
用watch实现列表过滤
// 用 watch实现
new Vue({
el: '#root',
data: {
keyWord: '', // keyword是input里面value的数据(v-model:value双向绑定,绑定的是value)
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 19, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '004', name: '温兆伦', age: 25, sex: '男' },
],
filPersons: [],
},
// 监视keyword
watch: {
// 只要keyword一改变,就会执行watch
keyWord: {
immediate: true, // 初始化时让handlder调用一下
handler(val) {// val 是input输入框里面现在的值
// 使用 filter进行筛选
this.filPersons = this.persons.filter(p => { // p是当前项的值,这里是每一个对象
// 数组索引方法,如果不包含就返回-1 ,筛选数组里面包含空字符串
return p.name.indexOf(val) !== -1; // 如果返回true就包含
})
}
}
}
})
用computed计算属性实现列表过滤
// 用computed实现
new Vue({
el: '#root',
data: {
keyWord: '', // keyword是input里面value的数据(v-model:value双向绑定,绑定的是value)
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 19, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '004', name: '温兆伦', age: 25, sex: '男' },
],
},
computed: {
filPersons() {
return this.persons.filter(p => {
return p.name.indexOf(this.keyWord) !== -1;
})
}
}
})
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字进行模糊搜索" v-model="keyWord">
<button @click="sortType=2">年龄升序</button>
<button @click="sortType=1">年龄降序</button>
<button @click="sortType=0">原顺序</button>
<ul>
<li v-for="(p,index) in filPersons" :key="p.id">
{{p.name}}-{{p.age}}--{{p.sex}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false
// 用computed实现
new Vue({
el: '#root',
data: {
// 收集用户想要对年龄进行升序降序还是原顺序
sortType: 0,//0 原顺序,1降序,2升序
keyWord: '', // keyword是input里面value的数据(v-model:value双向绑定,绑定的是value)
persons: [
{ id: '001', name: '马冬梅', age: 18, sex: '女' },
{ id: '002', name: '周冬雨', age: 29, sex: '女' },
{ id: '003', name: '周杰伦', age: 20, sex: '男' },
{ id: '004', name: '温兆伦', age: 25, sex: '男' },
],
},
computed: {
filPersons() {
// 先过滤然后排序,最后return返回
const arr = this.persons.filter(p => {
return p.name.indexOf(this.keyWord) !== -1;
})
// 判断一下是否需要排序
if (this.sortType) {
arr.sort((p1, p2) => {
// p1和p2是一个个对象,排序是让年龄进行排序
// 当sortType为1降序的时候,让p2的年龄-p1的年龄
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age;
})
}
return arr;
}
}
})
</script>
vue会监视data中所有层次的数据。
如何监测对象中的数据?
通过setter
实现监视,且要在new Vue时就传入要监测的数据(在data中)。
(1)对象中后追加的属性,Vue默认不做响应式处理
(2)如需给后添加的属性做响应式,请使用如下API:
Vue.set(target, propertyName/index/key, value)
或
vm.$set(target, propertyName/index/key, value)
如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1)调用原生对应的方法对数组进行更新。比如:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)重新解析模板,进而更新页面。
在Vue修改数组中的某个元素一定要用如下方法:
(1)使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
(2)Vue.set() 或 vm.$set()
特别注意:Vue.set()
和 vm.$set()
不能给vm 或 vm的根数据对象 (vm._data
)添加属性!!!
数据劫持:vue把data里所有属性都变成getter、setter形式的这种行为
- Vue是通过
getter
和setter
进行数据监测的,这个getter和setter和普通的getter和setter不一样,只要它们监测到数据修改,就会重新解析模板,所以这个getter和setter也称之为响应式的getter和setter。- 数组元素是没有响应式的getter和setter服务的,所以你给数组元素赋值Vue是监测不到的,页面不会发生变化
- 如果数组的元素是一个对象,那么这个对象是一定有响应式的getter和setter服务的
- 普通方法添加的属性是没有getter和setter的,而
Vue.set
或vm.$set
添加的属性是有getter和setter的,也就是说它们是响应式的。
参数 | 描述 |
---|---|
target | 在哪里添加(注:不能是vue实例或data) |
key | 要添加属性名/索引 |
val | 要添加属性值 |
<div id="root">
<h1>学生信息</h1>
<button @click="student.age++">年龄+1岁</button><br />
<button @click="addSex">添加性别属性,默认值:男</button><br />
<button @click="student.sex = '未知' ">修改性别</button><br />
<button @click="addFriend">在列表首位添加一个朋友</button><br />
<button @click="updateFirstFriend">修改第一个朋友的名字为:张三</button><br />
<button @click="addHobby">添加一个爱好</button><br />
<button @click="updateHobby">修改第一个爱好为:开车</button><br />
<h2>姓名:{{student.name}}</h2>
<h2>年龄:{{student.age}}</h2>
<h2 v-if="student.sex">性别:{{student.sex}}</h2>
<h2>爱好</h2>
<ul>
<li v-for="(h,index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h2>我的朋友们</h2>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}---{{f.age}}岁
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
student: {
name: 'tom',
age: 18,
hobby: ['唱', '跳', 'RAP'],
friends: [
{ name: 'jerry', age: 35 },
{ name: 'TONY', age: 32 }
]
}
},
methods: {
addSex() { // 添加性别属性,默认值为 男
// Vue.set(this.student, 'sex', '男');
this.$set(this.student, 'sex', '男')
},
addFriend() { // unshift()是在数组前面添加
this.student.friends.unshift({ name: 'POTTER', age: 18 })
},
updateFirstFriend() {
this.student.friends[0].name = '张三';
},
addHobby() {
this.student.hobby.push('画画');
},
updateHobby() {
// this.student.hobby.splice(0, 1, '开车');
Vue.set(this.student.hobby, 0, '开车');
}
},
})
</script>
若:,则v-model收集的是value值,用户输入的就是value值。
若:,单选框(name要相同),则v-model收集的是value值,且要给标签配置value值。
若:,在select身上绑定v-model,且v-model的初始值就是select的默认选项
若: 多选框
备注:关于 v-model 的修饰符
<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="sing">
跳<input type="checkbox" v-model="userInfo.hobby" value="dance">
RAP<input type="checkbox" v-model="userInfo.hobby" value="RAP">
<br /><br />
所属校区:
<select v-model="userInfo.city">
<option value="">请选择校区</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
</select>
<br /><br />
其他信息:
<textarea v-model.lazy="userInfo.other"></textarea>
<br /><br />
<input type="checkbox" v-model="userInfo.agree">阅读并接受<a href="http://www.bilibili/com">《用户协议》</a>
<br /><br />
<button>提交</button>
</form>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el: '#root',
data: {
userInfo: {
account: '',
password: '',
age: '',
sex: '',
hobby: [],
city: '',
other: '',
agree: '',
}
},
methods: {
demo() {
// 转换成JSON格式
console.log(JSON.stringify(this.userInfo));
}
}
})
</script>
定义
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理,复杂还得用computed、methods)。
语法
注册过滤器:全局注册Vue.filter(name,callback)
或 局部注册new Vue{filters:{}}
使用过滤器:{{ xxx | 过滤器名}}
或 v-bind:属性 = “xxx | 过滤器名”
备注
|
前面的值,手动传入的参数从第二个参数开始接收 <!-- 准备好一个容器 -->
<div id="root">
<h2>显示格式化后的时间</h2>
<!-- 计算属性实现 -->
<h3>现在是:{{fmtTime}}</h3>
<!-- methods实现 -->
<h3>现在是:{{getFmtTime()}}</h3>
<!-- 过滤器实现 -->
<h3>现在是:{{time | timeFormater}}</h3>
<!-- 过滤器的传参 -->
<h3>现在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
</div>
<div id="root2">
<h2>{{msg |mySlice}}</h2>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
// 全局过滤器,名字为mySlice
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
new Vue({
el: '#root',
data: {
time: 165754658459
},
computed: {
fmtTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
methods: {
getFmtTime() {
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss')
}
},
// 局部过滤器
filters: {
// str = 'YYYY-MM-DD HH:mm:ss' ES6写法,如果没有传参str,就使用这个默认的,如果传参了就使用传参的
timeFormater(value, str = 'YYYY-MM-DD HH:mm:ss') { // str 是传参生成的模板
return dayjs(value).format(str)
},
}
})
new Vue({
el: '#root2',
data: {
msg: 'hahahha'
}
})
引入 day.min.js
Day.js 是一个轻量的处理时间和日期的 JavaScript 库
之前学过的指令:
v-bind
: 单向绑定解析表达式, 可简写为 :xxx
v-model
: 双向数据绑定
v-for
: 遍历数组/对象/字符串
v-on
: 绑定事件监听, 可简写为@
v-if
: 条件渲染(动态控制节点是否存存在)
v-else
: 条件渲染(动态控制节点是否存存在)
v-show
: 条件渲染 (动态控制节点是否展示)
作用
向其所在的节点中渲染文本内容。
与插值语法的区别
v-text
会替换 掉节点中的内容,插值语法{{xx}}
则不会
v-text
不能识别html结构。
<div id="root">
<div>holo,{{name}}</div>
<div v-text="name">holo</div>
<div v-text="str"></div>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'bilibili',
str: '你好哈
'
}
})
</script>
作用
向指定节点中渲染包含html结构的内容。
与插值语法的区别
v-html
会替换掉节点中所有的内容,插值语法{{xx}}
则不会。
v-html
可以识别html结构。
严重注意:v-html有安全性问题!!!!
<div id="root">
<div>holo,{{name}}</div>
<div v-html="str"></div>
<div v-html="str2"></div>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'bilibili',
str: '你好哈
',
// 注意!安全性问题,下面这种会知道你的cookie信息
// str2: '兄弟我找到你想看的资源了'
}
})
</script>
v-cloak
属性。这里引用的vue.js文件有5s的延迟,显示器上会先显示出
holo,{{name}}
,然后过了5s后会显示holo,bilibili
vue中有个特使指令v-cloak
,Vue实例创建完毕并接管容器后,会删掉v-cloak
属性,所以搭配CSS3的属性选择器,当v-cloak
还存在的时候,就隐藏起来含有v-cloak
的元素
<style>
[v-cloak] {
display: none;
}
</style>
<body>
<div id="root">
<div v-cloak>holo,{{name}}</div>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
name: 'bilibili',
}
})
</script>
v-once
所在结构的更新,可以用于优化性能。 <div id="root">
<h2 v-once>初始化的n值是:{{n}}</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
<script>
Vue.config.productionTip = false; // 阻止vue在启动时生成生产提示
new Vue({
el: '#root',
data: {
n: 1,
}
})
</script>
<div id="root">
<h2 v-pre>vue其实很简单</h2>
<h2>当前的n值是:{{n}}</h2>
<button @click="n++">点我n+1</button>
</div>
new Vue({
directives{指令名:回调函数}
})
// 或
new Vue({
directives:{指令名:配置对象}
})
Vue.directive(指令名,配置对象)
// 或
Vue.directive(指令名,回调函数)
v-
,短横杠命名用字符串,使用时要加v-
;kebab-case
(user-name)命名方式,不要用camelCase
(驼峰法)命名。 <!--
需求1:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍
需求2:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
-->
<div id="root">
<h2>当前的n值是:<span v-text="n"></span></h2>
<h2>放大十倍后的n值是:<span v-big="n"></span></h2>
<!-- <h2>放大十倍后的n值是:<span v-big-number="n"></span></h2> -->
<button @click="n++">点我n+1</button>
<hr>
<input type="text" v-fbind:value="n">
</div>
<div id="root2">
<input type="text" v-fbind:value="x">
</div>
<script>
Vue.config.productionTip = false
// 全局指令
Vue.directive('fbind', {
bind(element, binding) {
console.log('fbind', this); // 所有指令相关的this指向window
// 指令与元素成功绑定时(一上来)
element.value = binding.value;
},
inserted(element, binding) {
// 指令所在元素被插入页面时调用
element.focus();
},
update(element, binding) {
// 指令所在的模板被重新解析时
element.value = binding.value;
element.focus();
}
})
new Vue({
el: '#root',
data: {
n: 1
},
// 局部指令
directives: {
// big函数何时会被调用?1.指令与元素成功绑定时(一上来) 2.指令所在的模板被重新解析时
big(element, binding) {
console.log(element, binding);
console.log('big', this); // 所有指令相关的this指向window
element.innerText = binding.value * 10;
},
/* 可以这样写
'big-number'(element, binding) {
console.log(element, binding);
element.innerText = binding.value * 10;
},
*/
}
})
new Vue({
el: 'root2',
data: {
x: 1
}
})
</script>
又名:生命周期回调函数、生命周期函数、生命周期钩子。
是什么:Vue在关键时刻帮我们调用的一些特殊名称的函数。
生命周期函数的名字不可更改,但函数的具体内容是程序员根据需求编写的。
生命周期函数中的this指向是vm 或 组件实例对象。
<div id="root">
<h2 :style="{opacity: opa}">欢迎学习Vue</h2>
<h2 v-if="a">你好啊</h2>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
a: false,
opa: 1,
},
methods: {
},
// vue完成模板的解析,并把初始的真实的dom元素放入页面后(挂载完毕)调入mounted
mounted() {
setInterval(() => {
this.opa -= 0.01;
if (this.opa <= 0) this.opa = 1;
}, 16);
}
})
/* 通过外部的定时器实现(不推荐)
setInterval(() => {
vm.opa -= 0.01;
if (vm.opa <= 0) vm.opa = 1;
}, 16);
*/
</script>
mounted
(挂载): 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等【初始化操作】。beforeDestroy
(销毁之前): 清除定时器、解绑自定义事件、取消订阅消息等【收尾工作】。beforeDestroy
操作数据,因为即便操作数据,也不会再触发更新流程了。 <div id="root">
<h2 :style="{opacity: opa}">欢迎学习Vue</h2>
<button @click="opa=1">透明度设置为1</button>
<!-- 彻底销毁 -->
<button @click="stop">点我停止变换</button>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
opa: 1,
},
methods: {
stop() {
this.$destroy()
}
},
// vue完成模板的解析,并把初始的真实的dom元素放入页面后(挂载完毕)调入mounted
mounted() {
this.timer = setInterval(() => {
console.log('setInterval');
this.opa -= 0.01;
if (this.opa <= 0) this.opa = 1;
}, 16);
},
beforeDestroy() {
console.log('vm没了');
clearInterval(this.timer)
},
})
</script>