为什么建议你读一读 vue 源码?
做了多年的前端,感觉每天都在重复的调接口。技术栈学了好多,但水平感觉没有涨多少,对很多问题也是一知半解。
如果你也有这样的困惑,那读源码可能是最好的突破口。
所以我今年也是立了个flag,希望可以多看一些源码。
看源码,肯定得从最熟悉的 vue 开始了。
vue源码内容还是挺多的,而且代码需要考虑各种边界情况,所以阅读起来还是有一定难度,所以本人在肝了3个月后,准备来谈谈对vue3的理解,希望可以梳理出一条清晰的逻辑线。
文章为系列文章,最终会实现一个最简单的todo,大概长这样。
<header>
<input type="text" :value="todo" @input="input"></input>
</header>
<main>
<button @click="add">add todo</button>
<ul>
<li v-for="item in list" :key="item">
{{ item.name }}
<button @click="remove(item.id)">remove</button>
</li>
</ul>
</main>
const { createApp } = Vue
const APP = {
data() {
return {
list: [{
name: "吃饭",
id: 1
}],
todo: ""
}
},
methods: {
add() {
this.list.push({
name: this.todo,
id: new Date().getTime()
})
this.todo = ''
},
input(e) {
this.todo = e.target.value
},
remove(id) {
const index = this.list.findIndex(item => item.id == id);
this.list.splice(index, 1)
}
},
mounted() {
this.todo = 'init todo'
},
}
const app = createApp(APP)
app.mount('#app')
虽然代码很简单,但是可以把vue的整个核心逻辑走一遍,包括响应式数据,运行时,模板编译,diff比对,指令解析等
代码地址为github.com/yeshaojun/v…, 后续文章会单独开一个分支出来,不同的文章对应不同的分支。
vue3有哪些变化
在开始之前,先看看 vue3 的一些变化,为什么会有这些变化?
1.对 ts 的支持
现在 ts 越来越普及,基本上已经成为前端的标配了。
而用过 vue2 的应该都知道,vue2 对 ts 的支持并不友好。当然这并不是因为 vue2 的类型管理是用 Flow.js 写的,而是因为 vue2 的实例化是通过 new 方法来创建的,数据绑定在 this 中,ts 很难对 this 中的静态方法做类型推导。
那就又有一个问题了,为什么 this 很难做类型推导呢?
这就涉及 tree shaking 的原理了,感兴趣的可以看这篇文blog.csdn.net/qq_34629352…
我截取了其中一个回答,意思就是javascript动态语言的特性使得静态分析比较困难
那 vue3 是怎么做的呢?
import { createApp } from 'vue';
createApp({
template: "<div>hello world</div>"
}).mount('#app')
细心的你应该也已经发现了,vue3 实例化的方式,是通过函数的方式创建的,而函数对 ts 的支持是很好的。
2.响应式实现方式改变了
vue 的响应式数据是一大特色,而 vue2 的响应式是基于 Object.defineProperty()这个 api 实现的,有很多缺陷,无法监听数组,也无法监听删除事件。基于这个缺陷,在 vue2 中做了很多判断以及兼容。(比如对数组操作的方法扩展等)
而在 vue3 中,则改成了 proxy,实现了真正的代理,并提升了性能。
3.Composition API 组合语法(这个特性非常棒)
vue2采用的Option API对于代码量大的情况下并不友好,会有反复横跳的问题,而且代码很难复用。而如果使用mixins,会有变量重复,数据来源不清晰等问题。
有了Composition API, 就可以方便的写hook了,这将大大的提高开发效率。
举个简单的例子,api请求。
在vue2中,写法大概是这样的。
this.loading = true
api().then(res =>{
this.result = res
}).error(err => {
this.error = err
}).finally(() =>{
this.loading = false
})
使用hook,则只需要一行代码。
const { data, loading, error } = useFetch()
4.vue3更快更高效了
在vue的编译阶段,以及dom树比对阶段都做了大量的优化,使vue3速度更快。最主要的就是补丁标记和动态属性记录,除此之外还有事件缓存,使用微任务更新组件等
具体的可以查看官方对渲染的说明,还提供了在线体验的地址。cn.vuejs.org/guide/extra…
5.扩展性更好了
vue中是使用虚拟节点, 也就是一段js来表示dom节点的。
既然是js,就可以编译成不同的语法,而不仅仅只是dom。
在vue3中,所有跟平台的操作,都进行了拆包,那就是拆包,使用最近流行的 monorepo 管理方式,响应式、编译和运行时全部独立了。
这一点从vue3的代码结构中,也很容易看出来。
剩下的一些vue3新特性,可以查看官方的vue迁移文档说明 v3-migration.vuejs.org/zh/
如何开始调试源码
想要调试源码,肯定要先下载源码。
不知道怎么找的,可以直接打开vue3官网,点击右上角的github连接就行。
vue3的源码是core这个仓库,找到之后就是下载,安装依赖。
1. git clone https://github.com/vuejs/core
2. cd core
3.pnpm i
4.将package.json中的dev改为"dev": "node scripts/dev.js --sourcemap",
5.pnpm run dev
看源码肯定需要断点调试,这个大家应该都会。(老手可以跳过了,新手还是简单提醒一下)
当前执行函数,当前脚本中的变量可以在作用域中查看,如果有些变量,作用域中没有,可以在监视中自行添加。
设置完断点之后,可在右侧断点位置切换与取消。
还有一个常用的地方是调用栈,特别是分支多的时候,追踪函数执行非常的方便,可以直接点击列表查看调用关系。
在packages/vue/examples中有很多例子,接下来就开始愉快的调试源码吧。