Vue3 的页面渲染流程

#vue3

目录

R1

==init → mounted → updated → unmountd==

  • 初始化
    • 创建应用实例
    • 初始化响应式系统
    • 编译模板
      • 也可以没有
  • 挂载
    • 创建虚拟 DOM
    • 渲染真实 DOM
    • ==建立响应式联系==
  • 更新
    • ==触发响应式更新==
    • diff 算法比较
    • 最小化 DOM 操作
  • 卸载
    • 清理副作用
    • 移除事件监听
    • 删除 DOM节点

1. 初始化阶段

// 创建应用实例
const app = createApp({
  setup() {
    const count = ref(0)
    return { count }
  },
  template: '<div>{{ count }}</div>'
})

// 挂载应用
app.mount('#app')

1.1. 创建应用实例

// 简化的createApp实现
function createApp(rootComponent) {
  const app = {
    mount(selector) {
      // 创建根组件的 vnode
      const vnode = createVNode(rootComponent)
      
      // 获取容器元素
      const container = document.querySelector(selector)
      
      // 渲染vnode
      render(vnode, container)
    }
  }
  return app
}

1.2. 编译模板

// template 会被编译成 render 函数
function render() {
  return createVNode('div', null, [ctx.count])
}

1.3. 创建虚拟DOM

function createVNode(type, props, children) {
  return {
    type,
    props,
    children,
    el: null,
    shapeFlag: getShapeFlag(type)
  }
}

2. 响应式系统初始化

// 创建响应式对象
const state = reactive({
  count: 0
})

// 创建计算属性
const double = computed(() => state.count * 2)

// 创建副作用
watchEffect(() => {
  console.log('count changed:', state.count)
})

3. 挂载过程

function mount(vnode, container) {
  // 创建DOM元素
  const el = document.createElement(vnode.type)
  
  // 处理props
  if (vnode.props) {
    for (const key in vnode.props) {
      patchProp(el, key, null, vnode.props[key])
    }
  }
  
  // 处理children
  if (vnode.children) {
    mountChildren(vnode.children, el)
  }
  
  // 插入到容器
  container.appendChild(el)
  
  // 保存真实DOM引用
  vnode.el = el
}

4. 更新流程

function patch(n1, n2, container) {
  if (n1 && !isSameVNodeType(n1, n2)) {
    unmount(n1)
    n1 = null
  }
  
  const { type } = n2
  
  if (typeof type === 'string') {
    // 处理普通元素
    processElement(n1, n2, container)
  } else if (typeof type === 'object') {
    // 处理组件
    processComponent(n1, n2, container)
  }
}

5. diff 算法

function patchChildren(n1, n2, container) {
  const c1 = n1.children
  const c2 = n2.children
  
  // 新旧子节点的处理
  if (typeof c2 === 'string') {
    // 文本节点的处理
    if (Array.isArray(c1)) {
      unmountChildren(c1)
    }
    if (c1 !== c2) {
      container.textContent = c2
    }
  } else if (Array.isArray(c2)) {
    // 数组节点的处理
    if (Array.isArray(c1)) {
      // 双端diff算法
      patchKeyedChildren(c1, c2, container)
    } else {
      container.textContent = ''
      mountChildren(c2, container)
    }
  }
}

6. 完整的渲染示例

// 组件定义
const MyComponent = {
  setup() {
    const count = ref(0)
    
    const increment = () => {
      count.value++
    }
    
    // 监听变化
    watch(count, (newVal, oldVal) => {
      console.log(`Count changed from ${oldVal} to ${newVal}`)
    })
    
    return {
      count,
      increment
    }
  },
  
  template: `
    <div>
      <p>Count: {{ count }}</p>
      <button @click="increment">Increment</button>
    </div>
  `
}

// 创建应用并挂载
const app = createApp(MyComponent)
app.mount('#app')

7. 生命周期钩子的执行顺序

const MyComponent = {
  setup() {
    onBeforeMount(() => {
      console.log('Before Mount')
    })
    
    onMounted(() => {
      console.log('Mounted')
    })
    
    onBeforeUpdate(() => {
      console.log('Before Update')
    })
    
    onUpdated(() => {
      console.log('Updated')
    })
    
    return {}
  }
}

8. 异步组件的渲染

const AsyncComponent = defineAsyncComponent({
  loader: () => import('./components/MyComponent.vue'),
  loadingComponent: LoadingComponent,
  errorComponent: ErrorComponent,
  delay: 200,
  timeout: 3000
})

9. 性能优化相关

  • 使用v-memo优化列表渲染
  • 使用shallowRef/shallowReactive优化大数据
  • 使用v-once优化静态内容
// 1. 使用v-memo优化列表渲染
<template>
  <div v-for="item in list" :key="item.id" v-memo="[item.id, item.name]">
    {{ item.name }}
  </div>
</template>

// 2. 使用shallowRef/shallowReactive优化大数据
const state = shallowRef({
  deepNestedData: {...}
})

// 3. 使用v-once优化静态内容
<template>
  <div v-once>
    {{ staticContent }}
  </div>
</template>

10. 错误处理

app.config.errorHandler = (err, vm, info) => {
  // 处理渲染过程中的错误
  console.error('Render Error:', err)
  console.log('Error Component:', vm)
  console.log('Error Info:', info)
}