渐进式 JavaScript 框架,用来动态构建用户界面
特点:
借鉴 angular 的 模板 和 数据绑定 技术
借鉴 react 的 组件化 和 虚拟DOM 技术
本地引入
CDN 引入
概念性的问题:
el: 又称挂载点,可认为是 element 的简写,创建一个 vue实例 得知道是在哪一块元素上创建 Vue实例 ,对哪一块视图进行操作
var vm = new Vue({
el: '#root',
data: {
msg: '123'
},
methods: {
test() {
}
}
})
data 与 el 的2种写法
{{js 表达式}} 用于解析标签体内容
<div id="test">
<input type="text" v-model="msg"><br>
<p>Hello {{msg.toUpperCase()}} p>
div>
<script src="CDN 引入或者本地引入vue.js">script>
<script>
const vm = new Vue({ // 配置对象 options
// 配置选项(option)
el: '#test', // element: 指定用vue来管理页面中的哪个标签区域
data: { // 数据(model)
msg: 'World'
}
})
script>
M: 模型(Model) :data中的数据
V: 视图(View) :模板代码 (不是静态页面) (两个语法:指令,大括号表达式)
VM: viewModel: 视图模型 (Vue的实例)
MVVM 本质上是 MVC (Model-View- Controller)的改进版。即模型-视图-视图模型。
模型 model 指的是后端传递的数据,视图 view 指的是所看到的页面。
视图模型 viewModel 是 mvvm 模式的核心,它是连接 view 和 model 的桥梁。它有两个方向:
将模型转化成视图,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定
将视图转化成模型,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听
这两个方向都实现的,我们称之为数据的双向绑定
插值语法: {{xxx}} 用于解析标签体的内容, 可以向页面输出数据
指令语法: v- 开头的自定义标签属性
v-bind: 数据绑定 (指定变化的属性值)
v-bind:xxx='yy' // yy 会被作为表达式解析
// 简写
:xxx='yy'
特点: 数据只能从 data 流向页面, 是单向数据绑定
v-model: 双向的数据绑定
数据能从 data 流向页面, 还能从页面流向 data
只能应用于表单类元素 (输入类元素) 上
收集表单数据:
若:<input type="text"/>,则v-model默认收集的是value值,用户输入的就是value值。
若:<input type="password"/>,则v-model默认收集的是value值,用户输入的就是value值。
若:<input type="radio"/>,则v-model默认收集的是value值,因为此类型无法输入内容,则无法通过输入得到value值,所以要给标签手动添加value值。
若:<input type="checkbox"/>
1.没有配置input的value属性,那么默认读取的的就是checked是否被勾选(勾选 or 未勾选,是布尔值)
2.配置input的value属性:
(1)v-model的初始值是非数组,那么收集的就是checked(勾选 or 未勾选,是布尔值)
(2)v-model的初始值是数组,那么收集的的就是value组成的数组
v-model 的三个修饰符
lazy:失去焦点再收集数据
number:输入字符串转为有效的数字
trim:输入首尾空格过滤
v-model:value='yy'
// 简写 因为 v-model 默认收集的就是 value 值
v-model='yy'
v-text: 向所在节点渲染文本内容
和插值语法的区别: v-text 会替换节点的内容, 插值语法不会
v-html: 像节点中渲染包含 html 结构的内容
和插值语法的区别:
<body>
<div id='app'>
<h2>1. 大括号表达式h2>
<p>{{msg}}p>
<p>{{msg.toUpperCase()}}p>
<p v-html="msg">p>
<p v-text="msg">p>
<p v-text="msg.toUpperCase()">p>
<h2>2. 指令一: 强制数据绑定h2>
<img src="imgUrl" alt="Vue">
<img v-bind:src="imgUrl" alt="Vue">
<img :src="imgUrl" alt="Vue">
<h2>3. 指令二: 绑定事件监听h2>
<button v-on:click="test1">test1button>
<button @click="test1">test1button>
<button @click="test2('abc')">test2button>
<button @click="test2(msg)">test2button>
div>
<script src="../js/vue.js">script>
<script>
new Vue({
el: '#app',
data: {
msg: 'I Will Back!',
imgUrl: "https://cn.vuejs.org/images/logo.png"
},
methods: {
test1() {
alert('heheh');
},
test2(content){
alert(content);
}
}
})
script>
body>
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给对象添加属性
Object.defineProperty(person, 'age', {
//里面三个参数(对象,属性名,options配置对象{})
// value: 18,
// enumerable: true, //控制属性是否可以枚举(遍历),默认值是false
// writable:true, //控制属性是否可以被修改,默认值是false
// configurable:true //控制属性是否可以被删除,默认值是false
//当有人读取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>
v-on: 绑定事件监听
绑定指定事件名的回调函数
v-on:click='xxx'
v-on:keyup='xxx(参数)'
v-on:keyup.enter='xxx'
// 简写
@click='xxx'
@keyup='xxx'
@keyup.enter='xxx'
Vue 中常用的按键别名:
回车 => enter
删除 => delete (捕获“删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (特殊,必须配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
系统修饰键(用法特殊):tab、ctrl、alt、shift、meta(win)
(1) 配合 keyup 使用: 按下修饰键的同时,再按下其他键,随后释放其他键,事件才被触发
(2) 配合 keydown 使用: 正常触发事件
<div id="root">
<h2>欢迎来到{{name}}学习h2>
<input type="text" placeholder="输入" @keydown.huiche="showInfo">
div>
只要 data 中的数据发生改变, Vue 会重新解析模板
{{方法名}}
来显示计算的结果原理:底层借助了 Objcet.defineproperty
方法提供的 getter
和 setter
get 函数什么时候执行?
(1) 初次读取时会执行一次。
(2) 当依赖的数据发生改变时会被再次调用。
优势:与 methods 实现相比,内部有缓存机制, 效率更高,调试方便。
备注:
//完整写法
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
}
通过 vm 的 $watch() 或 watch 配置来监视指定的属性 (可以监视计算属性中的属性)
属性变化时, 回调函数自动调用, 在函数内部进行计算
监视的属性必须存在,才能进行监视
两种写法:
new Vue 时传入 watch 配置
watch: {
isHot: {
immediate: true, //初始化时让 handler 调用一下
//当 isHot 发生改变时, 调用 handler
handler(newValue, oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
}
通过 vm.$watch 监视
vm.$watch('isHot',{
immediate: true, //初始化时让 handler 调用一下
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
深度监视
监视某个 number 的 a 属性
'numbers.a':{
deep:true,
handler(){
console.log('numbers改变了')
}
}
(1) Vue 中的 watch 默认不监测对象内部值的改变 (一层)
(2) 配置 deep:true 可以监测对象内部值改变 (多层)
备注:
(1) Vue 自身可以监测对象内部值的改变,但 Vue 提供的 watch 默认不可以!
(2) 使用 watch 时根据数据的具体结构,决定是否采用深度监视
numbers:{
deep:true,
handler(){
console.log('numbers改变了')
}
}
一般写法
isHot:{
// immediate:true, //初始化时让 handler 调用一下
// deep:true,//深度监视
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
简写
//简写
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
重要原则
样例代码
<body>
<div id="demo">
姓:<input type="text" placeholder="First Name" v-model="firstName"><br>
名:<input type="text" placeholder="Last Name" v-model="lastName"><br>
姓名1(单向):<input type="text" placeholder="Full Name1" v-model="fullName1"><br>
姓名2(单向):<input type="text" placeholder="Full Name2" v-model="fullName2"><br>
姓名3(双向):<input type="text" placeholder="Full Name3" v-model="fullName3"><br>
<p>{{fullName1}}p>
<p>{{fullName1}}p>
<p>{{fullName1}}p>
div>
<script src="../js/vue.js">script>
<script>
const vm = new Vue({
el: '#demo',
data: {
firstName: 'A',
lastName: 'B',
fullName2: 'A B'
},
computed: {
// 什么时候执行:初始化显示 / 相关的data属性数据发生改变
// 做什么的: 计算并返回当前属性的值
fullName1() { //计算属性中的一个方法,方法的返回值作为属性 (回调函数)
console.log('fullName1()') // 必然会掉用
return this.firstName + ' ' + this.lastName;
},
fullName3: { //不是函数是一个对象,里面有两个方法
get() {
// 回调函数 : 1. 你定义的 2. 你没有调用 3. 但他最终执行了
// 1. 什么时候调用? 2. 用来做什么
// 回调函数 当需要读取当前属性值时回调 根据相关的数据计算并返回当前属性的值
return this.firstName + ' ' + this.lastName;
},
set(value) {
// 回调函数 监视当前属性值的变化 当属性值发生改变时回调 更新相关的属性数据
const names = value.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
}
},
watch: { //配置监视
firstName: function(value){ // firstName 发生了变化
console.log(this); //就是vm对象
this.fullName2 = value + ' ' + this.lastName;
}
}
});
vm.$watch('lastName', function(value) {
//更新fullName2
this.fullName2 = this.firstName + ' ' + value;
})
script>
1. class 样式
写法 :class="xxx" xxx可以是字符串、对象、数组。
字符串写法适用于:类名不确定,要动态获取。
数组写法适用于:要绑定多个样式,个数不确定,名字也不确定。
对象写法适用于:要绑定多个样式,个数确定,名字也确定,但不确定用不用。
2. style 样式
:style="{fontSize: xxx}"其中xxx是动态值。
:style="[a,b]"其中a、b是样式对象。
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>绑定样式title>
<style>
.basic {
width: 400px;
height: 100px;
border: 1px solid black;
}
.happy {
border: 4px solid red;
;
background-color: rgba(255, 255, 0, 0.644);
background: linear-gradient(30deg, yellow, pink, orange, yellow);
}
.sad {
border: 4px dashed rgb(2, 197, 2);
background-color: gray;
}
.normal {
background-color: skyblue;
}
.atguigu1 {
background-color: yellowgreen;
}
.atguigu2 {
font-size: 30px;
text-shadow: 2px 2px 10px red;
}
.atguigu3 {
border-radius: 20px;
}
style>
<script type="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root">
<div class="basic" :class="mood" @click="changeMood">{{name}}div> <br /><br />
<div class="basic" :class="classArr">{{name}}div> <br /><br />
<div class="basic" :class="classObj">{{name}}div> <br /><br />
<div class="basic" :style="styleObj">{{name}}div> <br /><br />
<div class="basic" :style="styleArr">{{name}}div>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false
const vm = new Vue({
el: '#root',
data: {
name: '我要进大厂',
mood: 'normal',
classArr: ['atguigu1', 'atguigu2', 'atguigu3'],
classObj: {
atguigu1: true,
atguigu2: false,
},
styleObj: {
fontSize: '40px',
color: 'red',
},
styleObj2: {
backgroundColor: 'orange'
},
styleArr: [
{
fontSize: '40px',
color: 'blue',
},
{
backgroundColor: 'gray'
}
]
},
methods: {
changeMood() {
//随机切换心情
const arr = ['happy', 'sad', 'normal']
const index = Math.floor(Math.random() * 3)
//Math.random() 返回 0 ~ 1 之间的随机数,包含 0 不包含 1。
//Math.floor(x) 对 x 进行下舍入,即向下取整。
this.mood = arr[index]
}
},
})
script>
html>
v-if
v-else-if
v-else
适用于: 切换频率较低的场景
特点: 不展示的 DOM 元素直接被移除
v-show
适用于: 切换频率较高的场景。
特点: 不展示的 DOM 元素未被移除,仅仅是使用样式隐藏掉
注: v-if 的时候,元素可能无法获取到,而使用 v-show 一定可以获取到
比较 v-if 和 v-show
// v-if 和 template 配合使用
<template v-if="n === 1">
<h2>hih2>
<h2>hih2>
template>
v-for
v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名(形参)v-for
还支持一个可选的第二个参数,即当前项的索引。of
替代 in
作为分隔符,因为它更接近 JavaScript 迭代器的语法key 的原理
有相同父元素的子元素必须有独特的 key, 重复的 key 会造成渲染错误
react、vue中的key有什么作用? (key的内部原理)
虚拟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 是没有问题的。
3.如果不写 key,则默认为 index
用 computed 实现 (推荐)
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>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="(p,index) of filPerons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
li>
ul>
div>
<script type="text/javascript">
Vue.config.productionTip = false
// 用 computed 实现
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
]
},
computed: {
filPerons() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
script>
html>
用 watch 实现
//将下面代码放入上方代码块中
new Vue({
el: '#root',
data: {
keyWord: '',
persons: [
{ id: '001', name: '马冬梅', age: 19, sex: '女' },
{ id: '002', name: '周冬雨', age: 20, sex: '女' },
{ id: '003', name: '周杰伦', age: 21, sex: '男' },
{ id: '004', name: '温兆伦', age: 22, sex: '男' }
],
filPerons: []
},
watch: {
keyWord: {
immediate: true,
// 初始化的时候让handler调用一下,因为开始filPerons: []为空数组,
// 页面上什么内容都不显示,而恰巧handler(val)获取的值为空,
// 而所有字符串第0位不仅包含自己第一个字符,也包含着一个空字符,p.name.indexOf('')为0
// 所以handler(val)函数判断成功,页面显示内容
handler(val) {
this.filPerons = this.persons.filter((p) => {
return p.name.indexOf(val) !== -1
// 判断p.name中是否包含有val值,如果不等于-1,则说明包含
// filter返回一个全新的数组,原数组不变
})
}
}
}
})
列表排序与列表过滤一般是连在一起使用的
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>
<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) of filPerons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
<input type="text">
li>
ul>
div>
<script type="text/javascript">
Vue.config.productionTip = false
new Vue({
el: '#root',
data: {
keyWord: '',
sortType: 0, //0原顺序 1降序 2升序
persons: [
{ id: '001', name: '马冬梅', age: 30, sex: '女' },
{ id: '002', name: '周冬雨', age: 31, sex: '女' },
{ id: '003', name: '周杰伦', age: 18, sex: '男' },
{ id: '004', name: '温兆伦', age: 19, sex: '男' }
]
},
computed: {
filPerons() {
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
//判断一下是否需要排序
if (this.sortType !== 0) {
arr.sort((p1, p2) => {
return this.sortType === 1 ? p2.age - p1.age : p1.age - p2.age
})
}
return arr
}
}
})
script>
html>
更新时的问题
为什么 this.person[0] = {id:'001', name:'马老师', age:50, sex:'男'}
Vue 没有检测到改变 ?
Vue监视数据的原理:
array.splice(index, howmany, item1, ....., itemX)
// index 必须, 整数,指定在什么位置添加/删除项目,使用负值指定从数组末尾开始的位置
// howmany 可选, 要删除的项目数, 如果设置为 0,则不会删除任何项目
// 可选, 要添加到数组中的新项目
对要显示的数据进行特定格式化后再显示(适用于一些简单逻辑的处理)
语法:
备注:
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>过滤器title>
<script type="text/javascript" src="../js/vue.js">script>
<script src="https://cdn.bootcdn.net/ajax/libs/dayjs/1.10.6/dayjs.min.js">script>
head>
<body>
<div id="root">
<h2>显示格式化后的时间h2>
<h3>计算属性实现:{{fmtTime}}h3>
<h3>methods实现:{{getFmtTime()}}h3>
<h3>过滤器实现:{{time | timeFormater}}h3> //将time当参数传给timeFormater
<h3>过滤器实现(传参):{{time | timeFormater('YYYY_MM_DD') | mySlice}}h3>
<h3 :x="msg | mySlice">{{msg}}h3>
div>
<div id="root2">
<h2>{{msg | mySlice}}h2>
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false
//全局过滤器,必须在new Vue({})之前
Vue.filter('mySlice', function (value) {
return value.slice(0, 4)
})
new Vue({
el: '#root',
data: {
time: 1621561377603, //时间戳
msg: '你好,尚硅谷'
},
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: {
// timeFormater(value) {
// return dayjs(value).format('YYYY年MM月DD日 HH:mm:ss')
// }
// }
//局部过滤器
filters: {
//如果不传参数,则使用默认参数'YYYY年MM月DD日 HH:mm:ss',如果传参数,则使用
//传入的参数'YYYY_MM_DD'
timeFormater(value, str = 'YYYY年MM月DD日 HH:mm:ss') {
// console.log('@',value)
return dayjs(value).format(str)
}
}
})
new Vue({
el: '#root2',
data: {
msg: 'hello,atguigu!'
}
})
script>
html>
v-cloak 指令
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令title>
<style>
[v-cloak] {
display: none;
}
style>
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">
new Vue({
el: '#root',
data: {
name: '尚硅谷'
}
})
script>
html>
v-once
<div id="root">
<h2 v-once>初始化的n值是:{{n}}h2>
<h2>当前的n值是:{{n}}h2>
<button @click="n++">点我n+1button>
div>
v-pre
用于vue性能优化
<div id="root">
<h2 v-pre>Vue其实很简单h2>
<h2 >当前的n值是:{{n}}h2>
<button @click="n++">点我n+1button>
div>
需求1:定义一个 v-big 指令,和 v-text 功能类似,但会把绑定的数值放大 10 倍
需求2:定义一个 v-fbind 指令,和 v-bind 功能类似,但可以让其所绑定的 input 元素默认获取焦点
语法:
// 局部指令
new Vue({
directives: {
指令名: 配置对象
}
})
new Vue({
directives: {
指令名: 回调函数
}
})
// 全局指令
Vue.directive(指令名, 配置对象)
Vue.directive(指令名, 回调函数)
二、配置对象中常用的3个回调:
(1) bind: 指令与元素成功绑定时调用。
(2) inserted: 指令所在元素被插入页面时调用。
(3) update: 指令所在模板结构被重新解析时调用。
三、备注:
1. 指令定义时不加 v-,但使用时要加 v-
2. 指令名如果是多个单词,要使用 kebab-case 命名方式,别忘了加"", 不要用 camelCase 命名
3. 所有指令相关的 this 都是 window
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>{{name}}h2>
<h2>当前的n值是:<span v-text="n">span> h2>
<h2>放大10倍后的n值是:<span v-big="n">span> h2>
<button @click="n++">点我n+1button>
<hr />
<input type="text" v-fbind:value="n">
div>
body>
<script type="text/javascript">
Vue.config.productionTip = false
//定义全局指令
/* Vue.directive('fbind',{
//指令与元素成功绑定时(一上来)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入页面时
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析时
update(element,binding){
element.value = binding.value
}
})
Vue.directive('big',function(element, binding) { //两个参数:当前DOM元素(span),本次绑定的所有信息
console.log(element, binding)
// console.log('big', this)
//注意此处的this是window
element.innerText = binding.value * 10
},)
*/
new Vue({
el: '#root',
data: {
name: '尚硅谷',
n: 1
},
directives: {
//指令名如果是多个单词,别忘了加"",方法内部放的是"key",value值,大部分情况""可省略,但加了-之后引号必须带。
// 全写是: 'big-number':function(element,binding){}
/* 'big-number'(element,binding){
// console.log('big')
element.innerText = binding.value * 10
}, */
// big函数何时会被调用?1.指令与元素成功绑定时(一上来)。2.指令所在的模板被重新解析时。
big(element, binding) { //两个参数:当前DOM元素(span),本次绑定的所有信息
console.log(element, binding)
// console.log('big', this)
//注意此处的this是window
element.innerText = binding.value * 10
},
fbind: {
//指令与元素成功绑定时(一上来)调用
bind(element, binding) {//两个参数:当前DOM元素(input),本次绑定的所有信息
element.value = binding.value
},
//指令所在元素被插入页面时调用
inserted(element, binding) {
element.focus()
// input输入框自动获取焦点,这句代码必须放在将input放入页面的后面
},
//指令所在的模板被重新解析时调用
update(element, binding) {
element.value = binding.value
element.focus()
}
}
}
})
script>
html>
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 :style="{opacity:opacity}">欢迎学习Vueh2>
<button @click="opacity = 1">透明度设置为1button>
<button @click="stop">点我停止变换button>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
data: {
opacity: 1
},
methods: {
stop() {
this.$destroy()
}
},
// Vue完成模板的解析并把初始的真实DOM元素放入页面后(挂载完毕)调用mounted(挂载),只是初始化的时候调用了一次,后续数据更改重新解析模板时不会再次调用
mounted() {
console.log('mounted', this)
this.timer = setInterval(() => {
// 这里箭头函数无this,自动寻找上下文this,找到了mounted()的this,这个this指向vm
console.log('setInterval')
this.opacity -= 0.01
if (this.opacity <= 0) this.opacity = 1
}, 16)
},
beforeDestroy() {
console.log('vm即将被销毁')
clearInterval(this.timer)
// 清除定时器
},
})
//通过外部的定时器实现(不推荐)
// setInterval(() => {
// vm.opacity -= 0.01
// if (vm.opacity <= 0) { vm.opacity = 1 }
// }, 16)
script>
html>
生命周期流程
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>分析生命周期title>
<script type="text/javascript" src="../js/vue.js">script>
head>
<body>
<div id="root" :x="n">
<h2 v-text="n">h2>
<h2>当前的n值是:{{n}}h2>
<button @click="add">点我n+1button>
<button @click="bye">点我销毁vmbutton>
div>
body>
<script type="text/javascript">
const vm = new Vue({
el: '#root',
// template: `
//
// 当前的n值是:{{n}}
//
//
// `,
//使用template模板,容器内就不用放入内容了,不过template模板解析的时候会将外面的root容器给覆盖掉。
// 而且template模板只能有一个根元素,所以必须用div 将h2与button包裹起来,否则报错
data: {
n: 1
},
methods: {
add() {
console.log('add')
this.n++
},
bye() {
console.log('bye')
this.$destroy()
// 调用销毁函数,但之前的工作成果还在,只是以后不能管理了
}
},
watch: {
n() {
console.log('n变了')
}
},
beforeCreate() { //看图:这里是指数据代理和数据监测创建之前,不是vm
console.log('beforeCreate')
// console.log(this);
// debugger
// 此时打开控制台可以看到data中无数据,无methods方法
},
created() {
console.log('created')
// console.log(this);
// debugger
// 此时打开控制台可以看到data中有数据,有add,bye方法
},
beforeMount() {
console.log('beforeMount')
// console.log(this);
// debugger
// 元素都加载完成(未经编译),但还没有挂载上去,HTML的body结构里呈现的依然是模板
// 在这里面操作DOM白操作,最终会被虚拟dom转换成的真实dom覆盖掉
},
mounted() {
console.log('mounted')
// console.log(this);
// debugger
// 元素都加载完成(编译完成),已经挂载上去了,HTML的body结构里呈现的你想让他呈现的样子
// 在这里面操作DOM有效,但不推荐
},
beforeUpdate() {
console.log('beforeUpdate')
// console.log(this.n);
// debugger
// 更新数据时调用,数据为新的,但页面还是旧的,尚未更新
},
updated() {
console.log('updated')
// console.log(this.n);
// debugger
// 数据为新的,但页面也是新的,数据与页面保持同步
},
beforeDestroy() {
console.log('beforeDestroy')
// console.log(this.n);
// this.add()
// debugger
//销毁阶段触发
// 点击销毁vm,能打印出n,调用了add方法,但页面不再更新,即到了这个阶段,
//能够访问到数据,调用方法, 但所有对数据的修改不会再触发更新了。
// 此时vm中的data methods 指令等都处于可用状态,马上要执行销毁过程,
// 一般在此阶段:关闭定时器,取消订阅消息,解绑自定义事件等收尾操作
},
destroyed() {
//销毁阶段触发
console.log('destroyed')
},
})
// vm.$mount("#root")
script>
html>
常用的生命周期钩子:
1. mounted: 发送ajax请求、启动定时器、绑定自定义事件、订阅消息等 (初始化操作)
2. beforeDestroy: 清除定时器、解绑自定义事件、取消订阅消息等 (收尾工作)
关于销毁 Vue 实例
1. 销毁后借助 Vue 开发者工具看不到任何信息。
2. 销毁后自定义事件会失效,但原生 DOM 事件依然有效。
3. 一般不会在 beforeDestroy 操作数据,因为即便操作数据,也不会再触发更新流程了
vm的一生(vm的生命周期)∶
将要创建===>调用beforeCreate函数 创建完毕===>调用created函数。
将要挂载===>调用beforeMount函数
(重要)挂载完毕===>调用mounted函数 ============>【重要的钩子】
将要更新===>调用beforeUpdate函数
更新完毕===>调用updated函数
(重要)将要销毁===>调用beforeDestroy函数 ========>【重要的钩子】
销毁完毕===>调用destroyed函数