跳到主要内容
Vue 核心语法、响应式原理与生命周期实战指南 | 极客日志
JavaScript 大前端 算法
Vue 核心语法、响应式原理与生命周期实战指南 Vue 框架核心语法、响应式原理与生命周期实战。涵盖数据驱动视图、MVVM 思想、插值与指令系统(v-bind/v-model/v-for/v-if)、事件处理与修饰符、计算属性与侦听器、虚拟 DOM 与 Diff 算法、Class/Style 绑定、表单收集及生命周期钩子。深入解析 Object.defineProperty 实现的数据代理与劫持机制,提供手写简易 Vue 框架思路,适合希望掌握 Vue 底层逻辑与高级应用的开发者。
墨染流年 发布于 2026/4/8 更新于 2026/5/24 13 浏览Vue2 简介
数据驱动视图,双向数据绑定。
1.1 数据驱动视图
1.1.1 单向数据绑定
当页面数据发生变化时,页面会自动重新渲染。
1.1.2 双向数据绑定
在填写表单时,双向数据绑定可以辅助开发者在不操作 DOM 的前提下,自动把用户填写的内容同步到数据源中。
1.1.3 MVVM 分层思想
问题:MVVM 模型当中倡导了 Model 和 View 进行了分离,为什么要分离?
将 Model 和 View 分离之后,出现了一个 VM 核心,这个 VM 把所有的脏活累活给做了。也就是说,当 Model 发生改变之后,VM 自动去更新 View;当 View 发生改动之后,VM 自动去更新 Model。再也不需要编写操作 DOM 的 JS 代码了,开发效率提高了很多。
<div id ="app" >
姓名:<input type ="text" v-model ="name" >
</div >
<script >
const vm = new Vue ({
el : '#app' ,
data : {
name : 'zhangsan'
}
})
</script >
第一个 Vue 程序 首先,使用 script 标签引入 vue.js 文件。
<script src ="../js/vue.js" > </script >
<body >
<div id ="app" > </div >
<script >
const myVue = new Vue ({
template : '<h1>Hello World!</h1>'
})
myVue.$mount('#app' )
</script >
</body >
当使用 script 引入 vue.js 之后,Vue 会被注册为一个全局变量。首先必须要 new 一个 Vue 实例。
Vue 构造函数的参数:options options 翻译为多个选项,Vue 框架要求 options 参数必须是一个纯粹的 JS 对象 {}。在当前对象中编写大量的键值对 key:value,每个键值对都是配置项。
Vue 实例挂载 将 Vue 实例挂载到 id=app 的元素位置。
Vue 实例都有一个 $mount() 方法,这个方法的作用是什么?
将 Vue 实例挂载到指定位置,将 Vue 实例编译后的 HTML 代码渲染到页面的指定位置。注意:指定位置的元素被替换。
#app 显然是 ID 选择器。
Template 语句的数据来源 data 模板语句的数据来源主要由 data 选项提供。data 选项的类型是 Object 或 Function。如果 data 是对象的话,对象必须是纯粹的对象(含有零个或多个的 key/value 对)。data 数据如何插入到模板语句当中?使用 {{}},这是 Vue 框架自己搞的一套语法,被称为模板语法中的插值语法。
<body >
<div id ="app" > </div >
<script >
new Vue ({
template : `<h1>{{ name }}{{ releaseTime }}开始学 Vue!! {{lead.age}}的{{lead.name}}也在学习!! 班里还有{{classmates[0].age}}岁的{{classmates[0].name}}和{{classmates[1].age}}岁的{{classmates[1].name}}。</h1>` ,
data : {
name : '张三' ,
releaseTime : '2025 年 8 月 2 日' ,
lead : { name : '高齐强' , age : 40 },
classmates : [
{ name : '李四' , age : 18 },
{ name : '王五' , age : 80 }
]
}
}).$mount('#app' )
</script >
</body >
Template 配置项 template 只能有一个根元素;template 编译后进行渲染时会将挂载位置的元素替换。只要 data 中的数据发生变化,模板语句一定会重新编译。template 后面的代码如果需要换行的话,建议将代码写到 ` 符号当中,不建议使用 + 进行字符串的拼接。将 Vue 实例挂载时,也可以不用 $mount 方法,可以使用 Vue 的 el 配置项。el 配置项主要是用来指定 Vue 实例关联的容器。
<body >
<div id ="app" >
<div >
<h1 > {{msg}}</h1 >
<h2 > {{name}}</h2 >
</div >
</div >
<script >
new Vue({
template: `
<div >
<h1 > {{msg}}</h1 >
<h2 > {{name}}</h2 >
</div >
`,
data: {
msg: "hello world!",
name: "明天不 tm 学了!"
},
el: '#app'
})
</script >
</body >
Vue 实例和容器 Vue 实例和容器的关系是一夫一妻制。一个 Vue 实例只能接管一个容器,不能重复接管。
<body >
<div id ="app" > <h1 > {{msg}}</h1 > </div >
<div id ="app2" > <h1 > {{msg}}</h1 > </div >
<div > <h1 > {{name}}</h1 > </div >
<script >
new Vue ({
el : '#app' ,
data : { msg : 'hell0' }
})
new Vue ({
el : '#app2' ,
data : { name : '张三' }
})
</script >
</body >
Vue 核心语法
插值 {{ }} 语法 主要研究 {{这里可以写什么}}。在 data 中声明的变量、函数等都可以;常量都可以;只要是合法的 JavaScript 表达式,都可以。模板表达式都被放在沙盒中,只能访问全局变量的一个白名单,如 Math 和 Date 等。
new Vue ({
el : '#app' ,
data : {
number : 1 ,
gender : true ,
msg : 'abcdef' ,
sayHello : function ( ) {
console .log ('hello vue!' );
}
}
})
<div >
<h1 > {{msg}}</h1 >
<h1 > {{sayHello()}}</h1 >
<h1 > {{100}}</h1 >
<h1 > {{'hello vue!'}}</h1 >
<h1 > {{1 + 1}}</h1 >
<h1 > {{gender ? '男' : '女'}}</h1 >
<h1 > {{msg.split('').reverse().join('')}}</h1 >
<h1 > {{Date.now()}}</h1 >
<h1 > {{Math.ceil(3.14)}}</h1 >
</div >
Vue 指令
什么是指令?作用是什么?指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
Vue 框架中的所有指令的名字都以'v-'开始。
Vue 框架中所有的指令都是以 HTML 标签的属性形式存在的。
指令的语法规则:<HTML v-指令名:参数="javascript 表达式"></HTML>。
v-once 指令 作用:只渲染元素一次。随后的重新渲染,元素及其所有的子节点将被视为静态内容并跳过。
条件渲染指令
v-if 指令 作用:表达式的执行结果需要是一个布尔类型的数据:true 或者 false。true:这个指令所在的标签,会被渲染到浏览器当中;false:这个指令所在的标签,不会被渲染到浏览器当中。
<div >
<h1 > {{msg}}</h1 >
<h1 v-once > {{msg}}</h1 >
<h1 v-if ="a <= b" > v-if 测试:{{msg}}</h1 >
</div >
<script >
new Vue ({
el : '#app' ,
data : { msg : 'Hello Vue!' , a : 10 , b : 11 }
})
</script >
v-show 指令 v-if 指令会动态地创建或移除 DOM 元素,从而控制元素在页面上的显示与隐藏;v-show 指令会动态为元素添加或移除样式,从而控制元素的显示与隐藏。
如何选择?如果一个元素在页面上被频繁的隐藏和显示,建议使用 v-show,因为此时使用 v-if 开销比较大。v-if 的优点:页面加载速度快,提高了页面的渲染效率。
<div >
<div v-if ="false" > 不会显示</div >
<img :src ="imgPath1" v-if ="counter % 2 === 1" >
<img :src ="imgPath2" v-else >
<span v-if ="temprature <= 10" > 寒冷</span >
<span v-else-if ="temprature <= 25" > 凉爽</span >
<span v-else > 炎热</span >
<div v-show ="false" > 你可以看到我吗?</div >
</div >
<script >
const vm = new Vue ({
el : '#app' ,
data : {
counter : 1 ,
imgPath1 : '../img/1.jpg' ,
imgPath2 : '../img/2.jpg' ,
temprature : 0
}
})
</script >
v-bind 属性绑定指令 可以让 HTML 标签的某个属性的值产生动态的效果。
<HTML v-bind:参数 ="表达式" > </HTML >
<HTML :参数 ="表达式" > </HTML >
什么时候使用插值语法?什么时候使用指令?凡是标签体当中的内容要想动态,需要使用插值语法;只要想让 HTML 标签的属性动态,需要使用指令语法。
<img :src ="imgPath" >
<input type ="text" :value ="username" >
<a :href ="url" > 走起 2</a >
v-model 指令 v-bind 和 v-model 的区别和联系:v-bind 是单向数据绑定(data => 视图),v-model 是双向数据绑定(data <=> 视图)。v-bind 可以使用在任何 HTML 标签当中,v-model 只能使用在表单类元素上,例如 input 标签、select 标签、textarea 标签。
<input type ="text" :value ="name1" >
<input type ="text" v-model ="name2" >
v-model 指令修饰符 包括 .lazy、.number、.trim 等,用于处理特定的输入场景。
事件绑定指令 v-on Vue 事件处理:所有事件所关联的回调函数,需要在 Vue 实例的配置项 methods 中进行定义。v-on 指令也有简写形式,@click 等同于 v-on:click。
<button @click ="sayHi($event, 'jack')" > hi button2</button >
<button @click ="sayWhat" > what button</button >
methods : {
sayHi (event, name ) {
console .log (name, event)
},
sayWhat (event ) {
console .log (event.target .innerText )
}
}
事件修饰符
.stop:停止事件冒泡,等同于 event.stopPropagation()
.prevent:等同于 event.preventDefault() 阻止事件的默认行为
.capture:添加事件监听器时使用事件捕获模式
.self:只有当事件是在该元素本身触发时才执行
.once:事件只发生一次
.passive:无需等待,直接继续执行事件的默认行为
<a href ="https://www.baidu.com" @click.prevent ="yi" > 百度</a >
<div @click.stop ="er" >
<button @click ="yi" > 事件冒泡</button >
</div >
<button @click.once ="yi" > 事件只发生一次</button >
按键修饰符 常用的按键修饰符:.enter, .tab, .delete, .esc, .space, .up, .down, .left, .right。系统修饰键:ctrl、alt、shift、meta。
<input type ="text" @keyup.enter ="getInfo" >
<input type ="text" @keyup.ctrl.i ="getInfo" >
v-for 列表渲染指令 v-for 指令还支持一个可选的第二个参数,即当前项的索引。语法格式为 (item, index) in items。
<ul >
<li v-for ="(vip,index) of vips" > 会员名:{{vip.name}},年龄:{{vip.age}}岁</li >
</ul >
<table >
<tr v-for ="(vip,index) in vips" :key ="vip.id" >
<td > {{index+1}}</td >
<td > {{vip.name}}</td >
<td > {{vip.age}}</td >
<td > <input type ="checkbox" > </td >
</tr >
</table >
虚拟 DOM 与 diff 算法 (:key 属性) v-for 指令所在的标签中,还有一个非常重要的属性::key。如果没有指定 :key 属性,会自动拿 index 作为 key。建议使用对象的 id 作为 key,避免效率低和错乱的问题。
<tr v-for ="(hero,index) in heros" :key ="hero.id" >
<td > {{index+1}}</td >
<td > {{hero.name}}</td >
<td > {{hero.power}}</td >
</tr >
v-text v-html 指令 v-text 指令可以将指令的内容拿出来填充到标签体当中,和 JS 的 innerText 一样,以覆盖的形式进行。即使内容是一段 HTML 代码,这种方式也不会将 HTML 代码解析并执行。v-html 会将内容当做一段 HTML 代码解析并执行。
<h1 v-text ="msg" > test</h1 >
<h1 v-html ="s1" > </h1 >
v-cloak 指令 [v-cloak] { display: none; }。v-cloak 指令使用在标签当中,当 Vue 实例接管之后会删除这个指令,防止闪烁。
自定义指令 在每个 vue 组件中,可以在 directives 节点下声明私有自定义指令。在使用自定义指令时,需要加上 v- 前缀。
directives : {
'text-danger' : function (element, binding ){
element.innerText = binding.value
element.style .color = 'red'
}
}
过滤器 过滤器(Filters)是 vue 为开发者提供的功能,常用于文本的格式化。过滤器可以用在两个地方:插值表达式和 v-bind 属性绑定,过滤器应该被添加在 JavaScript 表达式的尾部。在 Vue3 当中,已经将过滤器语法废弃了。
<h2 > 商品价格:{{price | filterA | filterB(3)}}</h2 >
计算属性 computed 虽然计算属性在声明的时候被定义为方法,但是计算属性的本质是一个属性。使用 Vue 的原有属性,经过一系列的运算/计算,最终得到了一个全新的属性。
反转字符串 methods 实现 methods : {
reverseInfo ( ) {
return this .info .split ('' ).reverse ().join ('' );
}
}
反转字符串计算属性实现 computed : {
reversedInfo : {
get ( ) {
return this .info .split ('' ).reverse ().join ('' )
},
set (val ) {
this .info = val.split ('' ).reverse ().join ('' )
}
}
}
监视属性 watch 监视哪个属性,就把属性放入 watch 中即可。可以监视 Vue 的原有属性。打开页面初始化时,会调用一次 handler 方法。handler 方法的调用时间:当被监视的属性发生变化的时候,handler 就会自动调用一次。
watch : {
number : {
immediate : true ,
handler (newValue, oldValue ) {
console .log (newValue, oldValue)
}
},
a : {
deep : true ,
handler (newValue, oldValue ) {
console .log ('@' )
}
}
}
数据代理
VM(View Model) 通过 Vue 实例都可以访问哪些属性?所有以 $ 开始的属性,可以看做是公开的属性;所有以 _ 开始的属性,可以看做是私有的属性。
Object.defineProperty() 方法 给对象新增属性,或者设置对象原有的属性。配置项包括 value, writable, getter, setter。
数据代理机制 通过访问代理对象的属性来间接访问目标对象的属性。数据代理机制的实现需要依靠:Object.defineProperty() 方法。
Vue 数据代理机制对属性名的要求 Vue 实例不会给以_和$开始的属性名做数据代理。在 Vue 当中,给 data 对象的属性名命名的时候,不能以_或$开始。
手写 Vue 框架数据代理的实现 class Vue {
constructor (options ) {
Object .keys (options.data ).forEach ((propertyName, index ) => {
let firstChar = propertyName.charAt (0 )
if (firstChar != '_' && firstChar != '$' ) {
Object .defineProperty (this , propertyName, {
get ( ) {
return options.data [propertyName]
},
set (val ) {
options.data [propertyName] = val
}
})
}
})
}
}
Vue 框架源代码 创建 Vue 实例时,原始数据会被存储在 vm._data 属性中。vm.$options.data 就是创建该实例时传入的 data 选项。如果 data 是函数,则调用 getData(data, vm) 来获取真正的数据对象 data。
data(函数形式) data 可以是直接的对象,也可以是一个函数。如果是函数的话,必须使用 return 语句返回{}对象。
data ( ) {
return {
msg : 'Hello Zhangsan!'
}
}
数据绑定
Class 绑定
字符串形式 适用场景:如果确定动态绑定的样式个数只有 1 个,但是名字不确定。
<div :class ="c1" > {{msg}}</div >
数组形式 适用场景:当样式的个数不确定,并且样式的名字也不确定的时候,可以采用数组形式。
<div :class ="['active','text-danger']" > {{msg}}</div >
对象形式 适用场景:样式的个数是固定的,样式的名字也是固定的,但是需要动态的决定样式用还是不用。
<div :class ="classObj" > {{msg}}</div >
style 绑定 <div :style ="myStyle" > {{msg}}</div >
<div :style ="{backgroundColor: 'gray'}" > {{msg}}</div >
列表渲染 <li v-for ="(vip,index) of vips" > 会员名:{{vip.name}},年龄:{{vip.age}}岁</li >
列表排序 computed : {
filteredHeros ( ) {
const arr = this .heros .filter ((hero ) => {
return hero.name .indexOf (this .keyword ) >= 0
})
if (this .type === 1 ) {
arr.sort ((a, b ) => { return a.power - b.power })
} else if (this .type === 2 ) {
arr.sort ((a, b ) => { return b.power - a.power })
}
return arr
}
}
表单数据的收集 <form @submit.prevent ="send" >
用户名:<input type ="text" v-model.trim ="user.username" >
年龄:<input type ="number" v-model.number ="user.age" >
性别:男<input type ="radio" name ="gender" value ="1" v-model ="user.gender" >
爱好:旅游<input type ="checkbox" v-model ="user.interest" value ="travel" >
<select v-model ="user.grade" >
<option value ="zk" > 专科</option >
<option value ="bk" > 本科</option >
</select >
<textarea v-model.lazy ="user.introduce" > </textarea >
<button > 注册</button >
</form >
响应式与数据劫持 修改 data 后,页面自动改变/刷新。这就是响应式。Vue 的响应式是如何实现的?数据劫持:Vue 底层使用了 Object.defineProperty,配置了 setter 方法,当去修改属性值时 setter 方法则被自动调用。
数组的响应式处理 控制台修改,页面实时渲染。通过数组的下标去修改数组中的元素,默认情况下是没有添加响应式处理的。怎么解决?使用 vm.$set 或 Vue.set,或者使用 push(), pop(), splice() 等 7 个方法。
vm.users .push ('newUser' )
Vue .set (vm.users , 0 , 'modified' )
vm.users [0 ] = 'newName'
Vue 的生命周期
创建阶段 beforeCreate :实例刚被创建,数据观测和事件配置之前调用。无法访问 data、methods 和 computed 等属性。
created :实例创建完成,数据观测和计算属性等已配置。可以访问数据,但 DOM 尚未生成。
挂载阶段 beforeMount :挂载开始之前调用,模板已编译但未渲染到页面。$el 属性尚未生成。
mounted :实例挂载到 DOM 后调用。可以访问渲染后的 DOM 元素。
更新阶段 beforeUpdate :数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。可以访问当前数据,但 DOM 尚未更新。
updated :数据更改导致的虚拟 DOM 重新渲染和打补丁之后调用。操作更新后的 DOM。
销毁阶段 beforeDestroy :实例销毁之前调用。实例仍然完全可用。清理工作(清除定时器、取消事件监听)。
destroyed :实例销毁后调用。所有绑定已解除,事件监听器已移除。
beforeDestroy ( ) {
clearInterval (this .timer )
window .removeEventListener ('resize' , this .handleResize )
}
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online