持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
问题
1、为什么setup 不建议使用this
我们在options api 中 经常会使用
<script>
export default {
data() {
return {
count: 0
}
},
updates.
methods: {
increment() {
this.count++// 这里通过this调用data中定义的count
}
},
}
</script>
//而在 composition api中
setup(){
let count = ref(0)
const increment=()=> {
count.value++
}
return {
count,
increment
}
}
setup 发生在 data computed methods 之前。
setup 中应该避免使用 this
源码:runtime-core
文件下-src
文件夹下-component.ts
文件中setupStatefulComponent
方法中,优先调用了 setup
函数。而后面再执行 finishComponentSetup
中 applyOptions
函数中执行了 data
,computed
,methods
等
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean
) {
const Component = instance.type as ComponentOptions
if (__DEV__) {
//篇幅有限省略部分代码
}
// 0. create render proxy property access cache
instance.accessCache = Object.create(null)
// 1. create public instance / render proxy
// also mark it raw so it's never observed
instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
if (__DEV__) {
exposePropsOnRenderContext(instance)
}
// 2. call setup() 调用setup函数
const { setup } = Component
if (setup) {
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
setCurrentInstance(instance)
pauseTracking()
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
resetTracking()
unsetCurrentInstance()
// 判断结果是否是一个Promise
if (isPromise(setupResult)) {
setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
if (isSSR) {
//篇幅有限省略部分代码
} else {
// 调用 handleSetupResult
handleSetupResult(instance, setupResult, isSSR)
}
} else {
// 最后调用 finishComponentSetup
finishComponentSetup(instance, isSSR)
}
}
2、v-for中key的作用
- 1、主要在Vue中虚拟DOM算法,在新旧nodes对比辨识VNodes
- 2、如果不使用key,
patchUnkeyedChildren
,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法; - 3、如果有key,
patchKeyedChildren
,基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素 - 4、源码:
runtime-core-renderer.ts
文件中-在patchChildren
方法中进行判断
const patchChildren: PatchChildrenFn = (
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized = false
) => {
const c1 = n1 && n1.children
const prevShapeFlag = n1 ? n1.shapeFlag : 0
const c2 = n2.children
const { patchFlag, shapeFlag } = n2
// 当有key 执行patchKeyedChildren
if (patchFlag > 0) {
if (patchFlag & PatchFlags.KEYED_FRAGMENT) {
patchKeyedChildren(
c1 as VNode[],
c2 as VNodeArrayChildren,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
return
} else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) {
// unkeyed 执行patchUnkeyedChildren
patchUnkeyedChildren(
c1 as VNode[],
c2 as VNodeArrayChildren,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
return
}
}
}
3、defineProperty 与Proxy 的区别
-
1、 Object.definedProperty 是劫持对象的属性时,如果新增元素:那么Vue2需要再次 调用definedProperty,而 Proxy 劫持的是整个对象,不需要做特殊处理;
-
2、 修改对象的不同:
使用 defineProperty 时,我们修改原来的 obj 对象就可以触发拦截;
而使用 proxy,就必须修改代理对象,即 Proxy 的实例才可以触发拦截;
- 3、Proxy 能观察的类型比 defineProperty 更丰富
has:in操作符的捕获器;
deleteProperty:delete 操作符的捕捉器;
等等其他操作;
-
4、 Proxy 作为新标准将受到浏览器厂商重点持续的性能优化;
-
5、缺点:Proxy 不兼容IE,也没有 polyfill, defineProperty 能支持到IE9
源码:reactivity
文件夹下-src
文件夹-reactive.ts
文件中createReactiveObject
函数中
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {
//篇幅有限省去部分源码
// target already has corresponding Proxy
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
// Vue3-数据劫持使用的Proxy
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy)
return proxy
}
4、虚拟DOM的理解
- 1、对真实的元素节点进行抽象,抽象成VNode(虚拟节点)
因为对于直接操作DOM来说是有很多的限制的,比如diff、clone等等,但是使用JavaScript编程语言来操作这些,就变得非常的简单,所谓的性能更高。
VNode其本质就是一个JavaScript对象,我们可以使用JavaScript来表达非常多的逻辑,而对于DOM本身来说是非常不方便的;
- 2、方便实现跨平台,包括你可以将VNode节点渲染成任意你想要的节点
如渲染在canvas、WebGL、SSR、Native(iOS、Android)上;
并且Vue允许你开发属于自己的渲染器(renderer),在其他的平台上渲染;
const vnode = {
'tag':'div',
'children':'哈哈'
}
/*
通过渲染器
div: document.createElement
iOS: UIButton
Android: Button
*/
虚拟DOM的渲染过程
5、methods方法中 this 指向问题
<script>
export default {
data() {
return {
count: 0
}
},
// Methods are functions that mutate state and trigger updates.
methods: {
increment() {
this.count++
}
},
}
</script>
源码: runtime-core
文件夹下componentOptions.ts
文件 通过遍历 methods方法,ctx[key] = methodHandler.bind(publicThis)
if(methods){
for(const key in methods){
const methodHandler = (methods as MethodOptions)[key]
if(isFunction(methodHandler)){
if(__DEV__){
}else{
ctx[key] = methodHandler.bind(publicThis)
}
}else if(__DEV__){
warn(`Method "${key}" has type "${typeof methodHandler}" in the component definition.` + ``)
}
}
}
Vue3源码阅读流程
理解功能模块的作用
-
1、Compiler模块:编译模板系统;
-
2、Runtime模块:也可以称之为Renderer模块,真正渲染的模块;
-
3、Reactivity模块:响应式系统;比如:data中数据, setup中数据
三大系统协调工作
编译系统
将template模块转为render函数
渲染系统
runtime-dom以及runtime-core一系列业务逻辑的处理
响应式系统
主要进行vnode的diff算法
Vue.createApp(App).mount('#app')
CreateApp-创建流程
step1-创建render
在runtime-dom/src/index.ts文件中
1、通过ensureRenderer()函数创建 renderer
2、在renderer.ts文件中,通过baseCreateRenderer函数的实现体
3、在patch函数(约2千行) 最终返回render,并且返回一个createApp方法
CreateApp-断点调试流程
可以按照此步骤进行断点调试帮助理解
Mount-挂载流程
step1-调用app对象里面的mount方法
这里其实就是调用apiCreateApp.ts文件中createAppAPI函数
1、返回了一个createApp函数
2、在CreateApp函数中定义了一个app对象
3、调用createApp返回的app对象里面的mount方法
step2-执行render函数
通过render函数将根组件挂载到DOM上面
step3-挂载Component
mountComponent函数主要做了三步操作
1、createComponentInstance函数初始化组件实例
const instance: ComponentInternalInstance =
compatMountInstance ||
(initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
2、函数组件初始化过程真正给instance 内部状态赋值的方法,初始化组件内容
setupComponent(instance)
3、监听组件数据的变化,并完成响应式
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
mount的完成流程
debug断点调试mount流程
Compile 过程
step1-template模板转化成render函数
compile的目的是将template模板转化成render函数