下面就详细讲解一下“Vue3 源码导读(推荐)”的完整攻略。
概述
在Vue.js开发过程中,我们都知道Vue.js是一个非常好用的MVVM框架,而Vue.js3的发布也备受关注。Vue.js 3.0采用完全重写的方式,核心代码相比2.x版本变化较大,提高了性能。
导读
为了能够更好地学习Vue.js 3.0,我们需要先了解Vue.js 3.0的源码结构和架构。Vue.js 3.0代码采用了TypeScript编写,源码的目录结构也有所变动,因此阅读源码时需要先掌握TypeScript。以下是一个寻找Vue.js 3.0源码的示例。
git clone https://github.com/vuejs/vue-next.git
cd vue-next
yarn
目录结构
Vue.js 3.0的源码目录结构如下:
├── benchmark 性能测试代码
├── dist 打包后的代码
│ ├── runtime-core 运行时核心
│ ├── runtime-dom 运行时DOM
│ ├── compiler-core 编译器核心
│ ├── compiler-dom 编译器DOM
│ ├── server-renderer 服务端渲染
│ ├── size-plugin 代码包大小检测插件
│ └── vue.global.d.ts Vue.js 的类型定义
├── packages 包
│ ├── compiler-core Vue.js编译器核心
│ ├── compiler-dom Vue.js编译器DOM
│ ├── reactivity Vue.js响应式系统
│ ├── runtime-core Vue.js运行时核心
│ ├── runtime-dom Vue.js运行时DOM
│ ├── server-renderer Vue.js服务端渲染
│ ├── shared 组件定义相关的工具方法
│ ├── size-check 代码包大小检测插件
│ ├── template-explorer 用于学习 Vue.js 模板的交互式工具
│ ├── vue Vue.js 库的入口文件
│ └── vue-router-next 用于 Vue.js 3 的路由库
├── scripts 脚本
│ ├── benchmarks 相应的性能测试相关脚本
│ ├── build 构建配置文件
│ ├── ci 持续集成脚本
│ ├── release 发布相关脚本
│ └── size-check 代码包大小检测相关脚本
├── tests 测试
│ ├── e2e e2e 测试
│ ├── test 单元测试
│ └── test-utils 测试工具
├── types TypeScript类型定义
└── packages.json 包管理文件
其中,runtime-core
表示运行时核心,runtime-dom
表示运行时DOM。compiler-core
表示编译器核心,compiler-dom
表示编译器DOM。server-renderer
表示服务端渲染。
示例
以下是一个 runtime-core
的示例:
// 创建VNode
export function createVNode(type: VNodeTypes, props: (Data & VNodeProps) | null = null, children: unknown = null, patchFlag: number = 0, dynamicProps: string[] | null = null, isBlockNode = false): VNode {
if (!type || type === NULL_DYNAMIC_COMPONENT) {
if (__DEV__ && !type) {
warn(`Invalid vnode type when creating vnode: ${type}.`)
}
type = Comment // 当类型为空或者是一个空动态组件时,设置类型为Comment组件
}
if (isVNode(type)) {
// clone vnode
return cloneVNode(type, props, children)
}
// class component normalization.
if (isFunction(type) && '__vccOpts' in type) {
type = type.__vccOpts
}
// class & style normalization.
if (props) {
// for reactive or proxy objects, we need to clone it to enable mutation.
if (isProxy(props) || InternalObjectKey in props) {
props = extend({}, props)
}
let { class: klass, style } = props
if (klass && !isString(klass)) {
props.class = normalizeClass(klass)
}
if (isObject(style)) {
// reactive or proxy objects need to be cloned since they are likely to be
// mutated
if (isProxy(style) && !isArray(style)) {
style = extend({}, style)
}
props.style = normalizeStyle(style)
}
}
// encode content if any.
if (isString(children)) {
children = escapeHtml(children)
}
let shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: isSuspense(type)
? ShapeFlags.SUSPENSE
: isTeleport(type)
? ShapeFlags.TELEPORT
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: 0
// 设置是否为block节点
if (isBlockNode) {
shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
}
// 设置 children 数组
if (isArray(children)) {
shapeFlag |= ShapeFlags.ARRAY_CHILDREN
} else {
// only allow array children or no children
children = normalizeChildren(children)
}
return {
__v_isVNode: true,
__v_skip: true,
type,
props,
key: (props && normalizeKey(props)) || null,
ref: (props && normalizeRef(props)) || null,
children,
component: null,
suspense: null,
dirs: null,
// 设计的优化: fast-path flags
shapeFlag,
// 性能优化所用,避免不必要的children数组,如果没有动态props,则静态的走cache,动态的不走
patchFlag,
dynamicProps,
// 针对组件节点上下文,initial为false代表是异步节点
isBlock: isBlockNode,
// 是否为cloned节点,即是否为克隆节点,只有teleport的时候才会设置为克隆节点
// 详情请看 createTeleport 相关源码实现
isCloned: false,
// 是否链接其他vnode,详情请看 createEditorBlock 相关源码实现
isReplaced: false,
// el 用来记录对应节点 mounted 后对应的真实 DOM,详情请看mount 源码实现
el: null,
// anchor 用来记录组件节点的插入位置, 用于需要在该组件前插入DOM时
anchor: null
}
}
以上代码是 runtime-core
中创建 VNode 的部分源码,主要分为以下几个部分:
- 对类型
type
校验,当类型为空或者是一个空动态组件时,设置类型为 Comment 组件。 - 对 children 进行处理,如果为普通字符串则进行编码。
- 设置组件的 ShapeFlags,其中 isBlockNode 为是否为 block 节点。
- 判断 children 类型,分为数组和非数组两个情况。
- 返回 VNode 对象。
以上是 runtime-core
源码的一个示例。
另一个示例是在 reactivity
模块中实现双向绑定的部分。
// reactive/index.ts
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive<T extends object>(target: T): T {
if (target && (target as Target)[ReactiveFlags.REACTIVE]) { // 如果target已经是一个Proxy中
// target is already a Proxy, return it.
return target
}
// target 是不可扩展的对象,则不进行代理
if (!isObject(target)) {
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
const observed = createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers
)
return observed
}
该示例展示了从函数调用开始到返回的过程,主要实现了以下几个部分:
- 判断 target 是否已经是响应式对象,如果是,则直接返回 target。
- 判断 target 是否可扩展,不可扩展则直接返回 target。
- 调用
createReactiveObject
生成代理对象 observed 并返回。
以上是 reactivity
模块源码的一个示例。
总结
Vue.js 3.0 的源码是一个非常庞大的生态系统,了解其源码架构和目录结构并进行源码阅读对于学习 Vue.js 3.0 有很大的帮助。同时,Vue.js 3.0 的 TypeScript 实现也给学习 TypeScript 的同学提供了一个学习的机会,加深 TypeScript 的理解。学习 Vue.js 3.0 的源码需要一定的 TypeScript 基础和耐心,相信通过阅读 Vue.js 3.0 的源码,一定会对 Vue.js 3.0 框架有更深入的理解和认识。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue3 源码导读(推荐) - Python技术站