【vue3】template内容编译生成的js代码,及render函数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta
      charset="utf-8"
      name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"/>
      <style>
         .red{color:red}
      </style>

 <!--
   <script src="https://unpkg.com/vue@3.4.30/dist/vue.global.js"></script>
-->
   <script src="./vue/vuetest.global.js"></script>
  </head>
  <body>
      <div id="app">
          
      </div>
  </body>   
   
  <script  type="module"> 
    var person={
       name:"shengbinqian",
       age:10,
       techs:{
          name:"java",
          desc:{data:"java高手"}
       }
    };
    var refdata03=Vue.reactive(person);

    window.refdata=refdata03;
    var refdata1=Vue.ref(13);
    window.refdata1=refdata1;

    const app = Vue.createApp({
         name:"root-component",
         props: {///这个定义 会赋值给组件实例instance中的propsOptions(属性的定义,数据类型,是否必填等)
            rootprops1: String,
         },
         renderTracked:function(e){
            console.log("------------renderTracked,参数e: "+e);
         },
         data:function(){
            return {
                  msg:'hello vue3',
                  userlist:[
                        {"name":"xujingliang","password":"123456"},
                        {"name":"xujingliang","password":"123456"},
                        {"name":"xujingliang","password":"123456"}
                     ]
                   }
         },
         methods: {
            dealChange:function(str){
               alert("子组件发射了chanage事件: "+str);
            },
            dealPutinfo:function(str){
               alert("子组件发射了putinfo事件: "+str);
            }
            
         },
         beforeCreate:function(){console.log("root-beforeCreate");},
         setup:function(props,context){
            ///执行setup函数时, 组件已经实例化了, 此时可以访问到实例对象
             // context中有 props, attrs, slots, emit 和 expose
            console.log("root-setup");
            ///返回的对象第一层key 会被扩展到instance.ctx中
            return {setupData:{data1:"data1",data2:"data2"}};
         },
         ///用到了template 需要引入的vue中有 编译模块, 或者在构建阶段完成编译
         //emit的原理: 
         //在父组件在使用子组件是:会通过v-on 绑定事件属性,在生成父组件vnode时,碰到子组件使用时,会解析子组件 上的各种属性定义
         // 把他们传入到 子组件实例中的 props[change] 或者 动态props[change]中,同时父组件中的定义的dealChange这个函数也会被传入进去(v-on:chanage='dealChange')
         //这里就是change函数
         //然后当子组件 里面通过emit触发一个事件时: 就会去遍历子组件的props属性中的所有信息,找到onChange 他们对应一个函数dealChange
         //这些信息具体是在哪里解析的呢: 在执行父组件的render函数
         template:`<div>
                     <div><span> 发发的方法<div><p>毒贩夫妇</p></div></span></div>
                     <div id="root-chidren-div-id" class="root-chidren-div-class" style="color: blue;">{{msg}}</div>
                     <test-component v-on:chanage='dealChange'  @putinfo="dealPutinfo">
                        <template v-slot:default><div>我是父组件的插槽内容<p>测试锻炼</p></div></template>
                     </test-component>
                   </div>`,
      },{rootprops1:"rootmsg1",rootprops2:"rootmsg2"});
      //
      var testComponet={
         name:"test-component",
         props: {
            testpmsg1: String,
            onPutinfo:Function
         },
         data:function(){
            return {
               cmsg:"shengbinqian",
               cdata:"zhongguoren"
            }
         },
         methods: {
            testdata1:function(str){
               console.log("testdata-"+str);
               this.$emit("chanage","你好我是子组件信息chanage");
            },
            testdata2:function(str){
               this.$emit("putinfo","你好我是子组件信息putinfo");
            }
         },
         template:`<div class='border:solid 1px red'>
                     <div>我是子组件testComponent---{{cmsg}}</div> 
                     <div><slot><span>我是默认内容</span></slot></div>
                     <div>{{cdata}}</div>
                     <div><button @click="testdata1" >chanageEmit</button></div> 
                     <div><button @click="testdata2" >putinfoEmit</button></div> 
                  </div>`,
         setup:function(){
            console.log("testComponet-setup");
         }
      };
      ///会在app的上下文对象APPContext中的components中保存testComponet
      app.component("test-component",testComponet);
      app.mount('#app');


  </script>      
</html>

父组件templdate 编译生成的render函数如下:

(function anonymous(
) {
const _Vue = Vue
  const { createVNode: _createVNode, createElementVNode: _createElementVNode, createTextVNode: _createTextVNode } = _Vue
  
const _hoisted_1 = /*#__PURE__*/_createElementVNode("div", null, [
  /*#__PURE__*/_createElementVNode("span", null, [
    /*#__PURE__*/_createTextVNode(" 发发的方法"),
    /*#__PURE__*/_createElementVNode("div", null, [
      /*#__PURE__*/_createElementVNode("p", null, "毒贩夫妇")
    ])
  ])
], -1 /* HOISTED */)
const _hoisted_2 = {
  id: "root-chidren-div-id",
  class: "root-chidren-div-class",
  style: {"color":"blue"}
}
const _hoisted_3 = /*#__PURE__*/_createElementVNode("div", null, [
  /*#__PURE__*/_createTextVNode("我是父组件的插槽内容"),
  /*#__PURE__*/_createElementVNode("p", null, "测试锻炼")
], -1 /* HOISTED */)

return function render(_ctx, _cache) {
  with (_ctx) {
    const { createElementVNode: _createElementVNode, createTextVNode: _createTextVNode, toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
  
    const _component_test_component = _resolveComponent("test-component")
  
    return (_openBlock(), _createElementBlock("div", null, [
      _hoisted_1,
      _createElementVNode("div", _hoisted_2, _toDisplayString(msg), 1 /* TEXT */),
      _createVNode(_component_test_component, {
        onChanage: dealChange,
        onPutinfo: dealPutinfo
      }, {
       ///这个内容是作为子节点参数传入的,也是父组件中传给子组件的slot,就是一个函数,返回一个包括slot中内容的vnode节点对象
        default: _withCtx(() => [
          _hoisted_3
        ]),
        _: 1 /* STABLE */
      }, 8 /* PROPS */, ["onChanage", "onPutinfo"])
    ]))
  }
}
})

总结1:组件中几个重要的对象说明

1、父组件的type,subtree、vnode

总结2

组件通过templdate内容生成的渲染函数render 就是把纯字符串全部转化成vnode节点数据,一个标签对应一个vnode,然后vnode也有各种属性对应标签上的内容。 然后挂载时,如果vue 发现单当前的 vnode是一个普通的html标签类型(vnode.type类决定) 就直接容调用渲染器的hostCreateElement等创建dom的方法,得到对应vnode的el对象(真实dom),然后再遍历子节点,如果子节点的type不是普通的html标签类型,是一个对象的话,那就是组件,然后要是根据子节点中的type对象创建 子节点的中间态vnode 和子组件的instance对象,然后也开始调用子节点的render方法,得到子节点对应的可以挂载的vnode(subtree),然后要开始挂载,然后循环工作。

总结3

组件创建的主题过程

1、首先有组件的描述对象。
  数据结构是:
    {
        data:function(){return {}},
        beforeCreate:function(){}
        props:{
            msg:String,
            info:String
        }
        methods:{

            dealclick:function(){},
            buttonReactive:function(){}
        }
        setup(){

        },
        template:"<div>dfdfdf<div><childrenComponent><templdate v-slot:body>我是父组件传给子组件的body</template></childrenComponent></div></div>"
        ...
    }
2、然后vue根据上面的组件描述对象创建组件的中间态vnode对象,也称initVnode
  const vnode = createVNode(rootComponent, rootProps);
  这个vnode中有个属性type属性对应的就是描述对象

3、然后vue根据中间态的vnode创建组件的实例对象instance,实例对象中有一个属性type 也是指向组件的描述对象
  同时instance中也有中间态的vnode引用

4、然后对组件对象instance开始各种初始化,初始化props,slot,调用setup方法,并把数据暴露到instance.ctx中,它是组件渲染的上下文数据对象

5、然后处理setup方法的返回结构,把它的返回结果也暴露到instance.ctx上。以便在组件渲染时使用

6、然后调用组件的渲染方法开始渲染

7、执行挂载任务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值