简介:这份资源是尚硅谷张天禹Vue2.0与Vue3.0入门到精通教程的个人整理学习笔记,面向正在自学前端框架的初学者与进阶开发者,帮助解决听课记不住、知识点零散、复习无抓手的问题。压缩包内共1个docx文档,约10.38MB,内容按教程章节系统编排,从Vue简介、基本用法、模板语法、数据绑定、el与data写法、MVVM模型、数据代理到事件处理等均有覆盖,并配有目录便于快速定位。笔记对插值语法与指令语法、js表达式与js语句的区分、Object.defineProperty等易混点做了梳理,适合边看视频边对照复习,也可作为面试前的知识点速查材料。目前已有192人学习,适合希望用一份结构化文档串联Vue2与Vue3核心概念的前端学习者参考使用。
1. 从一份 Word 笔记说起:Vue2 到 Vue3 的完整学习路径到底长什么样
很多人学 Vue 的路径是碎的:今天看一段视频懂了v-if,明天搜一篇博客搞明白了watch,后天遇到组件通信又得重新翻文档。知识点之间没有连成线,结果就是每个 API 都见过,但真拿到一个 TodoList 或者后台管理页面,还是不知道从哪下手。这份尚硅谷张天禹 Vue2.0 + Vue3.0 教程的学习笔记,本质上就是把这条线给你画出来了——从最基础的插值语法一路推到 Vuex、路由、组件化编程,每个阶段都有对应的代码示例和原理拆解。
它适合两类人:一类是刚接触 Vue、需要一套有顺序的笔记跟着敲的入门者;另一类是用过 Vue 但基础不牢、想回头把响应式原理、生命周期、组件通信这些硬骨头啃一遍的开发者。笔记以 Word 形式整理,目录结构跟视频教程对齐,方便按章节检索。下面我从这份笔记的实际内容出发,把 Vue2 的核心机制和工程化实践拆开讲,重点放在那些看视频容易忽略、但写代码一定会踩的参数和边界上。
2. 模板语法与数据绑定:从插值到 MVVM 的落地细节
2.1 插值语法和指令语法的分工
Vue 的模板语法分两块:插值语法{{ }}用于标签体内容,指令语法v-xxx用于标签属性。这个分工看起来简单,但新手最容易犯的错是在{{ }}里写语句而不是表达式。比如{{ if (ok) return 'yes' }}这种写法直接报错,因为插值里只能放表达式——能产生一个值的式子。常见的表达式包括三元运算、方法调用、属性访问,而if、for、变量声明这些语句不行。
指令语法这边,v-bind负责把属性变成动态的,简写是冒号:。比如<a :href="url">里的url会去data里找。这里有个细节:v-bind绑定的值如果写成字符串字面量:href="'https://xxx'",Vue 会把它当表达式求值,结果是字符串本身;如果写成href="https://xxx"不带冒号,那就是普通 HTML 属性,Vue 不管。这个区别在绑定动态路径时特别关键。
2.2 el 和 data 的两种写法与重要原则
el挂载有两种写法。第一种是直接写选择器el: '#app',Vue 内部用document.querySelector去找;第二种是el: document.getElementById('app'),直接传 DOM 元素。第二种在需要动态挂载或者挂载到非标准选择器时更稳。
data也有两种写法:对象式和函数式。在根实例里两种都行,但在组件里必须用函数式,否则多个组件实例会共享同一份数据对象,改一个全变。这个坑在非单文件组件里特别常见——你写data: { count: 0 },页面上两个组件实例的count会互相干扰。函数式返回一个新对象,每个实例拿到独立的数据副本。
提示:判断用哪种写法有个简单原则——只要这个配置项最终会变成组件的一部分,就用函数式。根实例的
el和data可以偷懒,组件里不行。
2.3 MVVM 模型在代码里的映射
MVVM 不是抽象概念,在 Vue 代码里能一一对应。View 是模板部分,Model 是data里的数据,ViewModel 就是 Vue 实例本身。Vue 实例通过Object.defineProperty把data里的属性代理到实例上,所以你才能用this.count直接访问data.count。这个代理机制是响应式的基础,也是后面数据劫持和依赖收集的起点。
理解这一点之后,this.$data和this.count的关系就清楚了:前者是原始数据对象,后者是代理后的属性。你在方法里改this.count,Vue 能监测到;改this.$data.count也能监测到,因为最终都落到同一个属性上。但如果你在data里没声明某个属性,后面直接this.newProp = xxx,Vue 是监测不到的——这个坑在 Vue2 里没有后悔药,必须用Vue.set或者提前声明。
3. 计算属性、侦听器与生命周期:响应式系统的三个关键接口
3.1 computed 和 watch 的选型逻辑
computed和watch都能响应数据变化,但适用场景不同。computed适合“由已有数据推导出新数据”的场景,比如全名 = 姓 + 名,或者过滤后的列表。它内部有缓存机制,依赖不变时不会重新计算。watch适合“数据变化后执行副作用”的场景,比如搜索框输入后发请求、数据变化后操作 DOM 或者 localStorage。
一个常见的误用是用watch去实现computed能做的事。比如监听firstName和lastName,在回调里手动拼fullName——这完全可以用computed一行搞定,而且不用处理两个监听器的执行顺序问题。反过来,用computed去发请求也不合适,因为computed应该是纯函数,不应该有副作用。
watch的配置项里有两个容易忽略的参数:immediate和deep。immediate: true让监听器在初始化时就执行一次,适合需要根据初始值发请求的场景。deep: true用于监听对象内部属性的变化,但要注意它只能监测到已有属性的变化,新增属性还是得用Vue.set。
3.2 生命周期的四个阶段和八个钩子
Vue2 的生命周期分四个阶段:创建、挂载、更新、销毁。每个阶段前后各一个钩子,总共八个。创建阶段有beforeCreate和created,挂载阶段有beforeMount和mounted,更新阶段有beforeUpdate和updated,销毁阶段有beforeDestroy和destroyed。
实际开发中常用的就四个:created里发初始请求、mounted里操作 DOM 或初始化第三方库、beforeDestroy里清理定时器和事件监听、updated里做依赖 DOM 的更新操作。beforeCreate和beforeMount用得少,因为这两个阶段数据还没挂载或者 DOM 还没渲染,能做的事有限。
注意:
mounted不保证所有子组件都挂载完成。如果需要在所有子组件渲染完后操作 DOM,得用$nextTick或者把逻辑放到父组件的mounted里配合ref使用。
3.3 数据监测的边界:数组和对象的不同处理
Vue2 的响应式对数组和对象的处理不一样。对象是通过Object.defineProperty递归劫持每个属性,所以新增属性监测不到。数组则是重写了七个变更方法:push、pop、shift、unshift、splice、sort、reverse。调用这些方法会触发视图更新,但直接通过索引赋值arr[0] = xxx或者修改length不会触发。
这个差异导致一个经典问题:v-for渲染的列表,你通过索引改某一项,页面不更新。解决办法是用Vue.set(arr, index, newVal)或者用splice。在 Vue3 里这个问题被 Proxy 解决了,但 Vue2 项目里还是得按这个规则来。
4. 组件化编程:从非单文件到 CLI 脚手架的工程化跃迁
4.1 非单文件组件的三个注意点
非单文件组件用Vue.extend创建,然后注册到父组件的components选项里。这里有三个坑:第一,组件名如果用驼峰,在模板里得写成短横线,比如MyComponent要写成<my-component>;第二,template里必须有一个根元素,多个根节点会报错;第三,组件的data必须是函数,原因前面说过。
组件的嵌套关系通过components选项建立,但要注意注册是局部的——子组件在哪个父组件里注册,就只能在该父组件的模板里用。全局注册用Vue.component,但全局注册的组件多了之后不好追踪来源,一般只在基础组件库里用。
4.2 VueComponent 和内置关系
每个组件本质上是一个VueComponent构造函数,它是Vue.extend的返回值。VueComponent的原型对象上有一个__proto__指向Vue的原型对象,这就是为什么组件里能访问到Vue原型上的方法,比如$mount、$watch。这个内置关系让组件实例既能用自己的方法,又能继承 Vue 的能力。
理解这个关系对调试有帮助:当你在组件里调用this.$emit或者this.$nextTick时,这些方法其实来自Vue.prototype。如果某个方法在组件里找不到,可以顺着原型链往上查。
4.3 CLI 脚手架初始化和 render 函数
用 Vue CLI 初始化项目,核心命令是vue create 项目名。初始化完成后,main.js里有一个render函数,它替代了template配置。为什么需要render?因为 Vue CLI 默认引入的是运行时版本的 Vue,不带模板编译器。template需要编译器把它转成render函数,运行时版本没有这个能力,所以直接写render: h => h(App)。
如果非要用template,可以在vue.config.js里配置runtimeCompiler: true,但这样会引入编译器,打包体积变大。常见做法是接受render函数,或者用单文件组件的<template>标签——单文件组件在构建时由vue-loader处理,不依赖运行时编译器。
4.4 props、mixin 和 plugin 的工程化用法
props是父传子的标准方式,声明时可以指定类型、默认值和必填。类型检查在开发环境会报警告,但不会阻止渲染。默认值如果是对象或数组,必须用工厂函数返回,否则多个组件实例会共享同一个引用。
mixin用于抽离多个组件的公共逻辑,比如公共的data、methods、生命周期钩子。局部混入在组件里用mixins: [myMixin],全局混入用Vue.mixin(myMixin)。全局混入会影响所有组件,包括第三方组件,所以一般只在需要给所有组件注入公共方法时用,比如权限校验。
plugin插件通过Vue.use安装,本质是调用插件的install方法。插件里可以注册全局组件、添加全局指令、挂载原型方法。常见的vue-router和vuex都是插件,安装时会往Vue.prototype上挂$router和$store。
5. 避坑与排查:Vue2 开发中五个高频翻车现场
5.1 数组索引赋值不更新视图
现象:v-for渲染的列表,通过this.list[0] = newValue修改某一项,数据变了但页面没反应。
原因:Vue2 的响应式对数组的索引赋值没有拦截,只重写了七个变更方法。
解决:用this.$set(this.list, 0, newValue)或者this.list.splice(0, 1, newValue)。如果是在 Vuex 的 mutation 里,同样要用Vue.set。
5.2 对象新增属性不触发响应
现象:this.obj.newKey = 'value'之后,模板里{{ obj.newKey }}不更新。
原因:Object.defineProperty只能在初始化时劫持已有属性,新增属性不在劫持范围内。
解决:用this.$set(this.obj, 'newKey', 'value'),或者提前在data里声明newKey: ''。Vue3 用 Proxy 解决了这个问题,但 Vue2 项目里必须按这个规则来。
5.3 watch 监听对象内部变化失效
现象:watch里监听一个对象,对象内部属性变了但监听器不触发。
原因:默认情况下watch只监听对象引用的变化,不监听内部属性。
解决:加deep: true。但要注意deep会递归遍历对象,性能开销大。如果只需要监听某个具体属性,可以用字符串路径'obj.key'或者计算属性返回该属性。
5.4 组件自定义事件解绑遗漏
现象:组件销毁后,父组件还在接收子组件的事件,或者事件回调里访问已销毁组件的数据导致报错。
原因:$on注册的自定义事件没有在beforeDestroy里用$off解绑。
解决:在子组件的beforeDestroy里调用this.$off('eventName'),或者用this.$off()解绑所有。如果用全局事件总线,组件销毁时一定要解绑,否则总线上的事件会越积越多。
5.5 路由守卫里 next 的误用
现象:路由跳转时页面卡住,或者守卫逻辑执行了但没跳转。
原因:next函数没有调用,或者调用方式不对。next()放行,next(false)中断,next('/path')跳转到新路径,next(error)把错误传给onError。
解决:确保每个守卫分支都有next调用。在beforeEach里做异步校验时,注意next要在异步回调里调用,不能同步调用后又在异步里调一次。
6. 从 TodoList 到全局事件总线:一个案例串起组件通信的四种方式
TodoList 这个案例在笔记里反复出现,每次都用新的通信方式重构一遍。第一版用props实现父子通信,第二版用自定义事件实现子传父,第三版用全局事件总线实现兄弟组件通信,第四版用消息订阅与发布解耦。这个递进过程本身就是组件通信的选型指南。
props适合父传子,数据流向清晰,但层级深了之后要逐层传递,麻烦。自定义事件适合子传父,$emit触发,$on监听,但只能父子之间用。全局事件总线GlobalEventBus把Vue.prototype.$bus指向一个 Vue 实例,所有组件都能通过$bus.$emit和$bus.$on通信,适合兄弟组件或者跨层级组件。消息订阅与发布用第三方库(比如pubsub-js)实现,和事件总线思路类似,但解耦更彻底,适合大型项目。
选型原则很简单:能用props就用props,父子通信优先用自定义事件,兄弟组件用事件总线,跨模块通信用 Vuex。不要一上来就上 Vuex,小项目里 Vuex 的模板代码比业务代码还多。
$nextTick在这个案例里也有典型用法:修改数据后要立刻操作 DOM,但 Vue 的更新是异步的,直接操作拿到的是旧 DOM。this.$nextTick(callback)把回调推迟到 DOM 更新之后执行,确保拿到的是最新 DOM。这个技巧在弹窗、输入框聚焦、滚动到底部这些场景里很常用。
slot插槽则是组件复用的关键。默认插槽让父组件往子组件里塞内容,具名插槽让内容塞到指定位置,作用域插槽让子组件把数据传给父组件的插槽内容。TodoList 的底部统计栏用默认插槽,列表项用作用域插槽把todo对象传给父组件决定怎么渲染。这三种插槽的写法在笔记里都有完整示例,照着敲一遍基本就能掌握。
过渡与动画部分,<transition>包裹的元素在插入、更新、移除时会自动应用 CSS 类名。v-enter、v-enter-active、v-enter-to控制进入过程,v-leave、v-leave-active、v-leave-to控制离开过程。配合animate.css或者自定义关键帧,能给 TodoList 的增删加动效。注意<transition>只能包一个根元素,多个元素要用<transition-group>并指定key。
从那以后我每次写组件通信,都会先画一张数据流向图,标清楚谁传谁、传什么、什么时候传,再决定用哪种方式。这个习惯帮我省掉了大量返工时间。希望帮到你。
本文还有配套的精品资源,点击获取