一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
1。 vonde到真是的DOM是如何转变的
graph TD
创建vnode --> 渲染vnode --> 生成DOM
整个组件渲染流程
vnode本质上是用来描述DOM的JavaScript对象,它在Vue.js中可以描述不同类型的节点,如普通元素节点、组件节点等
vue.js 2.x 和vue.3初始化应用程序
// vue2.x初始化应用程序
import Vue from 'vue'
import App from './App'
const app = new Vue({
render:h=>h(App)
})
app.$mount('#app')
//vue3.0初始化应用程序
import {createApp} from 'vue'
import App from './app'
const app = createApp(App)
app.mount('#app')
createApp内部实现
- 整个app对象创建过程中,vue.js利用
闭包合函数柯里化的技巧,很好的实现了参数保留 - 例如,在执行app.mount的时候,
不需要传入渲染器render, 因为在执行createAppAPI的时候炫人气render参数已经被保留下来了
const createApp=((...args)=>{
//创建app对象
const app = ensureRenderer().createApp(...args)
const {mount} = app
//重新mount方法,目的: 既能让用户在使用API时更加灵活,也兼容了vue.js 2.x的写法,比如app.mount的第一个参数就同时支持选择器字符串和DOM对象2种类型
app.mount = (containerOrSelector)=>{
//标准化容器
const container = normalizeContainer(containerOrSelector)
if(!container)
return
const component = app._component
//如组件对象没有定义render函数和template模板,则取容器的innerHTML作为组件模板的内容
if(!isFunction(component) && !component.render && !component.template){
component.template = container.innerHTML
}
//挂载前清空容器内容
container.innerHTML = ''
//真正的挂载
return mount(container)
}
return app
})
//ensureRenderer()用来创建一个渲染器对象===============
//渲染相关的一些配置,如更新属性、操作DOM的方法
const rendererOptions={
patchProp,
...nodeOps
}
let renderer
//延时创建渲染器,当用户只依赖响应式包的时候,可以通过tree-shaking移除核心渲染逻辑相关代码
function ensureRenderer(){
return renderer || (renderer = createRenderer(rendererOptions))
}
function createRenderer(options){
return baseCreateRenderer(options)
}
function baseCreateRenderer(options){
function render(vnode,container){
//组件渲染的核心逻辑
}
return{
render,
createApp:createAppAPI(render)
}
}
function createAppAPI(render){
//createApp接收2个参数:根组件的对象和props
return function createApp(rootComponent,rootProps=null){
const app = {
_component:rootComponent,
_props:rootProps,
// 标准的课跨平台的组件渲染流程
mount(rootContainer){
//创建根组件的vnode
const vnode = createVNode(rootComponent,rootProps)
//利用渲染器渲染vnode
render(vnode,rootContainer)
app._container= rootContainer
return vnode.component.proxy
}
}
return app
}
}
function createVNode(type,props=null,children=null){
if(props){
//处理props相关逻辑,标准化class和style
}
//对vnode类型信息编码
const shapeFlag = isString(type)
? 1 /* ELEMENT*/
:isSuspense(type)
? 128 /*SUSPENSE*/
:isTeleport(type)
? 64 /*TELEPORT*/
:isObject(type)
? 4/*STATEFUL_COMPONENT*/
:isFunction(type)
?2 /*FUNCTION_COMPONENT*/
:0
const vnode = {
type,
props,
shapeFlag,
//其它一些属性
}
//标准化子节点,把不同数据类型的children转成数组或者文本类型
normalizeChildren(vnode,children){
return vnode
}
}
vnode的优势
抽象: 引入vnode,可以把渲染过程抽象化,从而使组件的抽象能力得到提升跨平台:因为patch vnode的过程不同平台可以有自己的实现,基于vnode再做服务器渲染、weex平台、小程序平台的渲染
节点挂载
insert执行是在处理子节点后,所以挂载的顺序是先子节点,后父节点,最终挂载到最外层的容器上
function insert(child,parent,anchor){
if(anchor){
parent.insertBefore(child,anchor)
}else{
parent.appendChild(child)
}
}