代码拉取完成,页面将自动刷新
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/@vue/reactivity@3.0.5/dist/reactivity.global.js"></script>
</head>
<body>
<div id="app"></div>
<div style="font-size: 14px; position:absolute;bottom:20px; left:10px;">
源代码地址:https://gitee.com/xiao_yan_bin/vue_code<br/>
github: https://github.com/xiaoyanbin/vue_code<br/>
vue设计与实现,持续更新,建议点赞收藏,有问题可以在评论区留言
</div>
<script>
const {effect,reactive,shallowReactive,ref,shallowReadonly} = VueReactivity
const bol = ref(false)
// 缓存队列代码
const queue = new Set()
let isFlushing = false
const p = Promise.resolve()
// 调度器的主要函数,用来将一个任务添加到缓冲队列中,并开始刷新队列
function queueJob(job){
if(!queue.has(job)){
queue.add(job)
if(!isFlushing){
isFlushing = true
p.then(() =>{
try{
queue.forEach(job => job())
} finally {
isFlushing = false
queue.clear()
}
})
}
}
}
// 1.定义一个全局变量currentInstance
let currentInstance = null
// 2.定义一个setCurrentInstance方法,用于设置currentInstance
function setCurrentInstance(instance){
currentInstance = instance
}
function createRenderer(options){
const {createElement,insert,remove,unmount,setElementText,patchProp} = options
function patch(n1,n2,container,anchor){
if(n1 && n1.type !==n2.type){
// n1存在,且类型不同,直接替换
unmount(n1)
n1 = null
}
const { type } = n2
if(typeof type === 'string'){
if(!n1){
mountElement(n2,container)
} else {
patchElement(n1,n2)
}
} else if(typeof type === 'object'){
if(!n1){
// 挂载组件
mountComponent(n2,container,anchor)
} else {
// 更新组件
patchComponent(n1,n2,container,anchor)
}
// 处理组件类型
} else {
// 处理其他类型
}
}
function patchComponent(n1,n2,container){
// 获取组件实例,及n1.component, 同时让虚拟节点n2也指向组件节点
const instance = n2.component = n1.component
// 获取当前的porps数据
const { props } = instance
// 调用hasPropsChanged函数,判断props是否发生改变,如没有变化,则不需要更新
if(hasPropsChanged(n1.props,n2.props)){
const [ nextProps, nextAttrs ] = resolveProps(n2.type.props,n2.props)
console.log('新的节点',nextProps,nextAttrs)
for(const key in nextProps){
props[key] = nextProps[key]
}
// 删除不存在的props
for(const key in props){
if(!(key in nextProps)){
delete props[key]
}
}
}
// vnode.component = instance
// 创建渲染响应式上下文
const renderContext = new Proxy(instance,{
get(target,key,receiver){
const { props, state } = target
if(state && key in state){
return Reflect.get(state,key,receiver)
} else if(props && key in props){
return Reflect.get(props,key,reactive)
} else {
return undefined
}
},
set(target,key,value,receiver){
const { props, state } = target
if(state && key in state){
// state[key] = value
Reflect.set(state,key,value,receiver)
} else if(props && key in props){
console.warn(`不允许直接修改实例上的属性${key} props are readonly`)
} else {
console.log('不允许直接修改实例上的属性')
}
}
})
console.log('组件更新')
}
// 判断props是否发生改变
function hasPropsChanged(prevProps,nextProps){
const prevKeys = Object.keys(prevProps)
const nextKeys = Object.keys(nextProps)
if(prevKeys.length !== nextKeys.length){
return true
}
for(let i = 0;i<prevKeys.length;i++){
const key = prevKeys[i]
if(prevProps[key] !== nextProps[key]){
return true
}
}
return false
}
function mountComponent(vnode,container,anchor){
const componentOptions = vnode.type
console.log('组件',componentOptions)
const { render:renders,data, setup, props:propsOption, beforeCreate, created,beforeMount, mounted, beforeUpdate, updated } = componentOptions
beforeCreate && beforeCreate()
const state = reactive(data())
// 调用resolveProps函数,将props转换为响应式对象
const [props, attrs] = resolveProps(propsOption,vnode.props)
const slots = vnode.children || {}
const instance = {
state,
props:shallowReactive(props),
isMounted: false,
subTree: null,
slots,
mounted:[]
}
console.log(slots,'slots')
function emit(event,...payload){
const { props } = instance
// 根据约定事件对事件名称进行处理 change => onChange
const eventName = `on${event[0].toUpperCase()}${event.slice(1)}`
const handler = props[`${eventName}`]
handler && handler(...payload)
}
// setup
const setupContext = { attrs, emit, slots }
setCurrentInstance(instance)
// 处理setup
const setupResult = setup(shallowReadonly(instance.props),setupContext)
setCurrentInstance(null)
let setupState = null
let render = renders
if(typeof setupResult === 'function'){
render = setupResult
} else {
setupState = setupResult
}
console.log(setupState,'setupState')
vnode.component = instance
// 创建渲染响应式上下文
const renderContext = new Proxy(instance,{
get(target,key,receiver){
const { props, state, slots } = target
if(state && key in state){
return Reflect.get(state,key,receiver)
} else if(props && key in props){
return Reflect.get(props,key,reactive)
} else if(setupState && key in setupState){
return Reflect.get(setupState,key,receiver)
} else if(key === '$slots'){
console.log('slots',slots)
return slots
}
else {
return undefined
}
},
set(target,key,value,receiver){
const { props, state } = target
if(state && key in state){
// state[key] = value
Reflect.set(state,key,value,receiver)
} else if(props && key in props){
console.warn(`不允许直接修改实例上的属性${key} props are readonly`)
} else if(setupState && key in setupState){
Reflect.set(setupState,key,value,receiver)
} else {
console.log('不允许直接修改实例上的属性')
}
}
})
// 当组件自身状态发生改变时,对组件进行更新
created && created.call(renderContext)
effect(() => {
const subTree = render.call(renderContext,renderContext)
if(!instance.isMounted){
beforeMount && beforeMount.call(renderContext)
instance.isMounted = true
patch(null,subTree,container,anchor)
instance.mounted && instance.mounted.forEach(hook => hook.call(renderContext))
} else {
beforeUpdate && beforeUpdate.call(renderContext)
patch(instance.subTree,subTree,container)
updated && updated.call(renderContext)
}
instance.subTree = subTree
},{
scheduler:queueJob
})
}
// resolveProps函数,用于将props转换为响应式对象
function resolveProps(options,propsData){
console.log('组件定义的props',options,'传入组件的数据',propsData)
const props = {}
const attrs = {}
for(const key in propsData){
if(key in options || key.startsWith('on')){
props[key] = propsData[key]
} else {
attrs[key] = propsData[key]
}
}
return [props,attrs]
}
// 比较节点
function patchElement(n1,n2){
const el = n2.el = n1.el
const oldProps = n1.props || {}
const newProps = n2.props || {}
for(const key in newProps){
const prevValue = oldProps[key]
const nextValue = newProps[key]
if(prevValue !== nextValue){
patchProp(el,key,prevValue,nextValue)
}
}
for(const key in oldProps){
if(!(key in newProps)){
patchProp(el,key,oldProps[key],null)
}
}
console.log(n1,n2,el,'el')
patchChildren(n1,n2,el)
}
// 比较子节点
function patchChildren(n1,n2,container){
if(typeof n2.children === 'string'){
if(Array.isArray(n1.children)){
unmount(n1)
}
console.log('比较节点',n2.children,n1.children)
if(n2.children !== n1.children){
setElementText(container,n2.children)
}
} else if(Array.isArray(n2.children)){
if(typeof n1.children === 'string'){
setElementText(container,'')
mountElement(n2,container)
} else {
const c1 = n1.children
const c2 = n2.children
const commonLength = Math.min(c1.length,c2.length)
for(let i = 0;i<commonLength;i++){
patch(c1[i],c2[i],container)
}
if(c1.length > c2.length){
c1.slice(c2.length).forEach(child => {
unmount(child)
})
} else if(c1.length < c2.length){
c2.slice(c1.length).forEach(child => {
mountElement(child,container)
})
}
}
}else {
if(Array.isArray(n1.children)){
n1.children.forEach(child => {
unmount(child)
})
} else {
unmount(n1.children)
}
}
}
// 挂载节点
function mountElement(vnode,container){
const el = vnode.el = createElement(vnode.type)
if(typeof vnode.children === 'string'){
setElementText(el,vnode.children)
} else if(Array.isArray(vnode.children)){
console.log(222)
vnode.children.forEach(child => {
patch(null,child,el)
});
}
if(vnode.props){
for(const key in vnode.props){
console.log(key,'key')
patchProp(el,key,null,vnode.props[key])
}
}
insert(el,container,null)
}
function render(vnode,container){
if(vnode){
patch(container._vnode,vnode,container)
} else {
if(container._vnode){
// container.innerHTML = ''
unmount(container._vnode)
}
}
container._vnode = vnode
}
return {
render
}
}
function shouldSetAsProp(el,key,nextValue){
if(key === 'form' && el.tagName === 'INPUT'){
return false
}
return key in el
}
// 将通用的操作
const renderer = createRenderer({
createElement(type){
console.log("创建元素"+type)
return document.createElement(type)
},
insert(child,parent,anchor){
console.log(`${child.outerHTML}插入到${parent.outerHTML}的${anchor}前`)
parent.insertBefore(child,anchor)
},
remove(child){
console.log(`${child}被移除`)
child.parent.removeChild(child)
},
unmount(vnode){
console.log(`${vnode}被卸载`)
const parent = vnode.el.parentNode
if(parent){
parent.removeChild(vnode.el)
}
},
setElementText(el,text){
console.log(`${el}的文本被设置为${text}`)
el.textContent = text
},
patchProp(el,key,preValue,nextValue){
console.log(`el:${el},key:${key},preValue:${preValue},nextValue:${nextValue}`)
if(/^on/.test(key)){
let invokers = el._vei || (el._vei = {})
let invoker = invokers[key]
const eventName = key.slice(2).toLowerCase()
if(nextValue){
if(!invoker){
// 如果没有invoker,则将一个伪造的invoker赋值给el._vei中
// vei是vue event invoker的缩写
invoker = el._vei[key] = (e) => {
// 如果事件发生的时间小于绑定事件的时间,则不执行
if(e.timeStamp <invoker.attached){
return
}
// 当伪造事件处理函数执行时,会调用真正的事件处理函数
if(Array.isArray(invoker.value)){
invoker.value.forEach(fn => {
fn(e)
})
} else {
invoker.value(e)
}
}
// 将真正的事件处理函数赋值给伪造的事件处理函数
invoker.value = nextValue
// 添加 invoker.attached 属性,用于存储事件绑定的时间
invoker.attached = performance.now()
// 将伪造的事件处理函数绑定到元素上
el.addEventListener(eventName,invoker)
} else {
invoker.value = nextValue
}
} else if(invoker){
// 如果nextValue不存在,则移除事件处理函数
el.removeEventListener(eventName,invoker)
el._vei = null
}
// if(preValue){
// el.removeEventListener(eventName,preValue)
// }
// el.addEventListener(eventName,nextValue)
} else if(key === 'class'){
el.className = nextValue
} else if(shouldSetAsProp(el,key,nextValue)){
const type = typeof el[key]
if(type === 'boolean' && nextValue === ''){
el[key] = true
} else {
el[key] = nextValue
}
} else {
el.setAttribute(key,nextValue)
}
}
})
effect(() =>{
const MyComponent = {
name: 'MyComponent',
data(){
return {
count: 'hello word 生命周期'
}
},
props:{
title:String,
},
setup(){
const count1 = ref('我是setup中的数据')
return { count1 }
},
render(){
return {
type:'div',
children:`foo 的值是 ${this.count1.value}${this.title}`
}
}
}
function defineAsyncComponent(loader){
let InnerComp = null
return {
name:'AsyncComponentWrapper',
data(){
return {
}
},
setup(){
const loaded = ref(false)
loader().then(res => {
InnerComp = res
console.log(res,'res')
loaded.value = true
})
const fn = () => {
return loaded.value ? { type: InnerComp} : {type:'Text',children:'loading...'}
}
console.log(fn,'fn')
return fn
}
}
}
// 建立异步组件
const asyncComponent = defineAsyncComponent(() => {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(MyComponent)
},2000)
})
})
renderer.render({type:asyncComponent},document.querySelector('#app'))
})
// // 创建一个渲染器
// const render = createRenderer()
// // 调用render函数渲染该vnode
// renderer.render(vnode,document.querySelector('#app'))
</script>
</body>
</html>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。