HTML Attributes 定义在 HTML 标签上的属性
DOM properties,dom 元素上的属性,很多都相似,但是类在 html 使 class,dom 上是 className
<input value="foo">
<!-- el.value就是输入框现在的值,但是el.getAttribute('value')始终是foo不会变,等同于el.defaultValue -->
<input type="foo">
<!-- html属性会校验修正不合法的值 -->
<!-- console.log(el.type) 显示为text -->
HTML 属性的作用是设置与之对应的 dom 属性初始值
<button disabled>Button</button>
const button = {
type:'button',
props:{
disabled:''
}
}
<button :disabled="false">Button</button>
const button = {
type:'button',
props:{
disabled:false
}
}
如果当成htm属性,注意html上的值全部会被当成字符串
el.setAttribute('disabled',false)
等价 el.setAttribute('disabled','false')
按钮会被禁用
function mountElement ( vnode , container ) {
const el = document . createElement ( vnode . type ) ;
if ( vnode . props ) {
for ( const key in vnode . props ) {
const value = vnode . props [ key ] ;
if ( key in el ) {
if ( typeof value === "boolean" && value === "" ) {
el [ key ] = true ;
} else {
el [ key ] = value ;
}
} else {
el . setAttribute ( key , value ) ;
}
}
}
}
设置 class 三种方式,el.setAttribute 性能最优
setAttribute
el.className
el.classList
没有调用 beforeUnmount、unmounted 等声明周期函数
没有调用自定义指令的卸载钩子
不会移除 dom 上的时间处理函数
function mountElement ( vnode , container ) {
const el = ( vnode . el = document . createElement ( vnode . type ) ) ;
}
function render ( vnode , container ) {
if ( vnode ) {
patch ( container . _vnode , vnode , container ) ;
} else {
if ( container . _vnode ) {
unmount ( vnode ) ;
}
}
}
function unmount ( vnode ) {
const el = vnode . el ;
const parent = el . parent ;
if ( parent ) parent . removeChild ( el ) ;
}
function patch(n1,n2,container) {
// type都不一致了,没有打补丁的必要
if(n1&& n1.type !== n2.type) {
unmount(n1)
n1 = null
}
const {type} = n2
if(type=== 'string') {
if(!n1) {
mountElement(n2,container)
} else{
patchElement(n1,n2)
}
// 对象,表明是组件
} else if(type === 'object') {
}
}
function patchProps ( el , key , prevValue , nextValue ) {
if ( / ^ o n / . test ( key ) ) {
let invoker = el . _vei ;
const name = key . slice ( 2 ) . toLowerCase ( ) ;
if ( nextValue ) {
if ( ! invoker ) {
invoker = el . _vei = ( e ) => {
invoker . value ( e ) ;
} ;
invoker . value = nextValue ;
el . addEventListener ( name , invoker ) ;
} else {
invoker . value = nextValue ;
}
} else if ( invoker ) {
el . removeEventListener ( name , invoker ) ;
}
}
// ...
}
let invokers = el . _vei || ( el . _vei = { } ) ;
const name = key . slice ( 2 ) . toLowerCase ( ) ;
const invoker = invokers [ name ] ;
let invoker = ( el . _vei = ( e ) => {
if ( Array . isArray ( invoker . value ) ) {
invoker . value . forEach ( ( fn ) => fn ( e ) ) ;
}
} ) ;
if ( ! invoker ) {
invoker = el . _vei = ( e ) => {
if ( e . timeStamp < el . attached ) return ;
invoker . value ( e ) ;
} ;
invoker . value = nextValue ;
el . addEventListener ( name , invoker ) ;
el . attached = performance . now ( ) ;
} else {
invoker . value = nextValue ;
}
function patchElement ( n1 , n2 ) {
const el = ( n2 . el = n1 . el ) ;
const oldProps = n1 . props ;
const newProps = n2 . props ;
// 更新props
for ( const key of newProps ) {
if ( newProps [ key ] !== oldProps [ key ] ) {
patchProps ( el , key , oldProps [ key ] , newProps [ key ] ) ;
}
}
for ( const key of oldProps ) {
if ( ! ( key in newProps ) ) {
patchProps ( el , key , oldProps [ key ] , null ) ;
}
}
patchChildren ( n1 , n2 , el ) ;
}
function patchChildren ( n1 , n2 , container ) {
if ( typeof n2 . children === "string" ) {
if ( Array . isArray ( n1 . children ) ) {
n1 . children . forEach ( ( c ) => unmount ( c ) ) ;
}
setElementText ( container , n2 . children ) ;
} else if ( Array . isArray ( n2 . children ) ) {
//diff算法核心,子节点都是一组子节点
// 这里用简易算法
if ( Array . isArray ( n1 . children ) ) {
n1 . children . forEach ( ( c ) => unmount ( c ) ) ;
n2 . children . forEach ( ( c ) => patch ( null , c , container ) ) ;
} else {
//旧子节点为空或字符串
setElementText ( container , "" ) ;
n2 . children . forEach ( ( c ) => patch ( null , c , container ) ) ;
}
} else {
// 新子节点不存在
if ( Array . isArray ( n1 . children ) ) {
n1 . children . forEach ( ( c ) => unmount ( c ) ) ;
} else if ( typeof n1 . children === "string" ) {
setElementText ( container , "" ) ;
}
}
}
const Text = Symbol ( ) ;
const newVnode = {
type : Text ,
children : "我是注释内容" ,
} ;
function patch ( n1 , n2 , patch ) {
const { type } = n2 ;
if ( type === Text ) {
if ( ! n1 ) {
const el = ( n2 . el = document . createTextNode ( n2 . children ) ) ;
insert ( el , container ) ;
} else {
const el = ( n2 . el = n1 . el ) ;
el . nodeValue = n2 . children ;
}
}
}
const Fragment = Symbol ( ) ;
const vnode = {
type : Fragment ,
children : [
{ type : "li" , children : "text 1" } ,
{ type : "li" , children : "text 2" } ,
] ,
} ;
function patch ( n1 , n2 , container ) {
///
if ( ( type = Fragment ) ) {
if ( ! n1 ) {
n2 . children . forEach ( ( c ) => patch ( null , c , container ) ) ;
} else {
patchChildren ( n1 , n2 , container ) ;
}
}
}