第三章-vue3的设计思路

71 阅读1分钟

一、声明式地描述UI

  • 声明式:
<h1 @click=handler>click Me</h1>
  • 渲染函数(返回虚拟DOM, 组件的渲染内容)
export default {
   render() {
      return {
        tag: "h1",
        props: { onClick: handler },
        children: "click Me",
      }
   }
}
  • 渲染函数 + h(h一个辅助创建虚拟DOM的工具函数)

h(type, props, children)

export default {
   render() {
      return h("h1", {onClick: handler}, "click Me")
   }
}

二、初识渲染器

渲染器(renderer): 虚拟DOM →真实DOM

function renderer(vnode, container) {
  const el = document.createElement(vnode.tag);
  for(let key in vnode.props) {
     if(/^on/.test(key)){
        el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key])
     }
  }
  if(typeof vnode.children === "string") {
    el.appendChild(document.createTextNode(vnode.children))
  } else if (Array.isArray(vnode.children)) {
    vnode.children.forEach(child => renderer(child, el))
  }
  container.appendChild(el);
}
let vnode = {
  tag: "h1",
  props: { onClick: () => alert("hello world") },
  children: "click Me",
}

renderer(vnode, document.body)

三、组件的本质

组件就是一组DOM元素的封装(可以是一个返回虚拟DOM的函数, 也可能是一个包含render的对象)

const MyComponent = {
  render () {
    return  {
      tag: "h1",
      props: { onClick: () => alert("hello world") },
      children: "click Me",
    }
  }
}

const vnode = {
  tag: MyComponent
}

function mountElement(vnode, container) {
  const el = document.createElement(vnode.tag);
  for(let key in vnode.props) {
    if(/^on/.test(key)){
      el.addEventListener(key.substr(2).toLowerCase(), vnode.props[key])
    }
  }
  if(typeof vnode.children === "string") {
    el.appendChild(document.createTextNode(vnode.children))
  } else if (Array.isArray(vnode.children)) {
    vnode.children.forEach(child => renderer(child, el))
  }
  container.appendChild(el);
}

function  mountComponent(vnode, container) {
  const subtree = vnode.tag.render();
  renderer(subtree, container)
}

function renderer(vnode, container) {
  if(typeof vnode.tag === "string") {
    mountElement (vnode, container)
  } else if(typeof vnode.tag === "object") {
    mountComponent(vnode, container);
  }
}

renderer(vnode, document.body)

四、模板的工作原理

编译器(compiler): 模板 (template) → 虚拟DOM

01 <template>
02   <div @click="handler">
03     click me
04   </div>
05 </template>
06
07 <script>
08 export default {
09   data() {/* ... */},
10   methods: {
11     handler: () => {/* ... */}
12   }
13 }
14 </script>

编译成

01 export default {
02   data() {/* ... */},
03   methods: {
04     handler: () => {/* ... */}
05   },
06   render() {
07     return h('div', { onClick: handler }, 'click me')
08   }
09 }

五、vue.js时是各个模块组成的有机整体

模板 (template)→ 编译 → 虚拟DOM → 渲染 → 真实的DOM

例子: 编译器能够识别模板中的动态属性和静态属性,生成虚拟DOM的时候附带信息, 使得渲染器能够减少寻找变更点的工作量

模板: 
<div id="foo"  :class=cls></div>
编译结果: 
render() {
 return {
   tag: "div",
   props: {
     id: "foo",
     class: cls
   },
   patchFlags: 1 //假设数字 1 代表 class是动态的
 }
}