Vue页面模板和render函数和生命周期

本文详细介绍了Vue中模板(template)和渲染函数(render)的使用,包括模板属性的独立定义页面结构,以及渲染函数的最高优先级和覆盖template属性。内容涵盖了Vue实例的生命周期,虚拟DOM的概念和作用,以及如何利用Vue的钩子函数进行数据和DOM的管理。此外,还讨论了Vue的模板渲染性能优化,如模板优先级和虚拟DOM的diff算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

页面模板和render函数
Vue对象构建时需要为对象指定页面构成结构,该构成结构称之为模板
在基本构成vue对象时通过 mount(el) 指定构成的页面模板,同时指定页面位置
1、模板属性 (template)
Vue实例添加 template 属性,可以独立定义页面构成模板
template 构成的模板 最终会替换到 mount(el) 的指向位置
示例:
StringDOMHTML标签的字符串定义方式
StringElHTML元素选择器

 // 单一职能原则 => 代码中一个定义只用于描述一个功能

        //             => 一个文件的代码组成只用于解决一类问题

        //      优点:文件功能独立相互关联引用,但互不干扰,可以快速替换和修改、可以快速复用

        //      缺点:调用链长,中途接收开发困难

        // vue的容器构建时,页面定义的代码不是最终呈现的DOM

        /*

        Vue实例添加 template 属性,可以独立定义页面构成模板

        vue容器构建步骤:

            1.读取容器代码结构 => template

            2.渲染代码结构为真实响应式DOM => render

            3.将真实响应式DOM写入到页面对应的位置进行展示 => mount(el)

        template 构成的模板 最终会替换到 mount(el) 指向元素的内容

        示例:

            Vue.createApp({,

                template:StringDOM | StringEl

            }).mount("#app")

            + StringDOM:HTML标签的字符串定义方式

            + StringEl:HTML元素选择器

 <div id="app">
        <h1>测试template优先级</h1>
    </div>

    <hr>
    <!-- 模板第一种定义方案 -->
    <!-- <div id="root" style="display: none;">
        <h5>页面root模板-div</h5>
        <p>msg:{{ msg }}</p>
        <input type="text" v-model="msg">
    </div> -->
    <!-- 模板第二种定义方案 -->
    <!-- <template id="root">
        <h5>页面root模板-template</h5>
        <p>msg:{{ msg }}</p>
        <input type="text" v-model="msg">
    </template> -->
    <!-- 模板第三种定义方案 -->
    <script type="text/x-template" id="root">
        <h5>页面root模板-script</h5>
        <p>msg:{{ msg }}</p>
        <input type="text" v-model="msg">
    </script>

    <script type="module">
        // 单一职能原则 => 代码中一个定义只用于描述一个功能
        //             => 一个文件的代码组成只用于解决一类问题
        //      优点:文件功能独立相互关联引用,但互不干扰,可以快速替换和修改、可以快速复用
        //      缺点:调用链长,中途接收开发困难

        // vue的容器构建时,页面定义的代码不是最终呈现的DOM

        /*
        Vue实例添加 template 属性,可以独立定义页面构成模板
        vue容器构建步骤:
            1.读取容器代码结构 => template 
            2.渲染代码结构为真实响应式DOM => render
            3.将真实响应式DOM写入到页面对应的位置进行展示 => mount(el)
        template 构成的模板 最终会替换到 mount(el) 指向元素的内容

        示例:
            Vue.createApp({,
                template:StringDOM | StringEl
            }).mount("#app")
            + StringDOM:HTML标签的字符串定义方式
            + StringEl:HTML元素选择器

        */
        import { createApp } from "../assets/vue/3.0/vue.esm-browser.js";
        createApp({
            // template:`
            //     <p>msg:{{ msg }}</p>
            //     <input type="text" v-model="msg">
            // `,
            template:"#root",
            data(){
                return {
                    msg:"msg测试数据"
                }
            }
        }).mount("#app")
    </script>

2、模板渲染函数(render

Vue实例添加 render 属性函数,可以独立定义JS模板构成函数
render 构成的模板具有==最高优先权,
最终会替换到 mount(el) 的指向位置
==,且template属性失效
示例:
render 属性取值为一个函数,该函数返回构成的模板
Vue.h 为渲染函数,该参数可用于以方法方式构建页面模板
  <div id="app"></div>

    <script>
        // 1、提供页面结构 => template 
        let htmlTemplate = `
            <p>msg:{{ msg }}</p>
            <input type="text" v-model="msg">
        `;
        let data = {
            msg:"测试msg"
        }
        
        // 1.1 字符串到dom的转换
        let domBox = document.createElement("div");
        domBox.innerHTML = htmlTemplate;
        // console.log(domBox);
        let list = domBox.childNodes
        console.log(list);
        // JS 内存dom虚拟容器盒子
        let tempBox = document.createDocumentFragment();
        list.forEach(item=>{
            // console.log(item,item.cloneNode(true));

            // 2、渲染节点  => render
            //          递归函数 => vue的语法解析 => 响应式关联
            let cloneNode = item.cloneNode(true)
            let text = cloneNode.textContent;
            let flag = text.match(/{{\s*([^\s]*)\s*}}/);
            if(flag!=null){
                cloneNode.textContent = text.replace(/{{\s*([^\s]*)\s*}}/,data[flag[1]])
            }
            tempBox.appendChild( cloneNode )
        })

        // 3、把转换的DOM节点渲染到页面  => mount(el)
        let htmlBox = document.querySelector("#app")
        // console.log(htmlBox);
        htmlBox.appendChild(tempBox)
    </script>

3、虚拟DOM

Vue.createApp({,
template:StringDOM | StringEl
}).mount("#app")
Vue.createApp({
data:{
title:"标题"
},
render: function () {
return Vue.h('h4', 'render'+this.title);
}
}).mount("#app")Vue引入了虚拟DOMDIFF算法提示渲染性能
虚拟DOM的存在就是为了提高程序运行性能;
虚拟DOM实际就是一个存在于内存中的数据对象,可以用于描述一个DOM
结构
浏览器对于DOM元素的直接操作效率较低,但对于JS的内存数据操作效率较
浏览器对于DOM的操作是非智能操作,每一次的调用都会重新渲染该DOM
对象 (回流、重绘)
JS数据变量的赋值,可以有效确定值是否真的发生了变化,如果是才会修改
内存结果
diff 算法
内存中的数据变量发生变化,页面DOM元素需要进行更新
<body>
    <div id="app">

    </div>

    <script type="module">
        import { createApp,h,createVNode } from "../assets/vue/3.0/vue.esm-browser.js";
        createApp({
            data(){
                return {
                    msg:"测试MSG变量关联"
                }
            },
            render(vm) {
                console.log(vm,this);
                // console.log(h);
                // h 是vue在render函数中提供的用于完成响应式关联的DOM构建工具
                // return "<h4>测试数据</h4>"
                // return h("h4","render函数模板:"+vm.msg)
                return h("div",[
                    "容器",
                    h("h4","render函数模板:"+vm.msg),
                    h("input",{ type:"text",value:vm.msg,onInput:(event)=>{ vm.msg = event.target.value } })
                ])
            },
        }).mount("#app")
    </script>

 

 

生命周期生命周期:一段代码从 创建 到 销毁的 完成过程
钩子函数: Vue对外提供的在一些特点代码执行段上的回调函数
Vue.createApp({
// 在实例初始化之后,数据观测 和 事件配置之前被调用。
beforeCreate:function(){},
// 在实例创建完成后被立即调用
created:function(){},
// 在挂载开始之前被调用:相关的 render 函数首次被调用。
beforeMount:function(){},
// el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子
mounted:function(){},
// 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现
有的 DOM
beforeUpdate:function(){},
// 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
updated:function(){},
// 实例销毁之前调用。在这一步,实例仍然完全可用。
beforeUnmount:function(){},
// Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有
的事件监听器会被移除
unmounted:function(){},
// 特殊生命周期监听函数
renderTracked:function({key, target, type}) {},
renderTriggered:function({key, target, type}) {},
});
生命周期流程

  <div id="app">
        <div>
            <p ref="pDom">msg:{{ msg }}</p>
            <input type="text" v-model="msg">
            <p>info:{{ info }}</p>
            <input type="text" v-model="info">
            <br><br>
            <input type="button" value="销毁" @click="unmountCallback()">
        </div>
    </div>

    <script type="module">
        import { createApp } from "../assets/vue/3.0/vue.esm-browser.js";
        let app = createApp({
            data(){
                return {
                    msg:"msg数据",
                    info:"info数据"
                }
            },
            methods: {
                unmountCallback(){
                    app.unmount()
                }
            },
            beforeCreate() {
                console.group("beforeCreate=====>")
                console.log("vue实例构建完成,vue环境构建完成");
                console.log("$data:",this.$data); //  {}
                console.log("msg:",this.msg); // undefined
                console.groupEnd();
            },
            // 应用:在vue项目运行时用于初始化数据的请求(ajax数据加载)-最早的初始化数据加载生命周期钩子函数
            // 特殊vue运行环境(SSR):服务端渲染初始数据的加载最好是在created生命周期中完成
            //          ==> SSR 可以提供更好的 SEO(搜索引擎优化-就是爬虫-不执行css和js)
            //          ==> SSR 首屏(第一个页面) 效率根高 (一个优秀的知识宣传网站不超过8屏[8个屏幕大小])
            created() {
                console.group("created=====>")
                console.log("运行依赖的加载,响应式功能的构成");
                console.log("$data:",this.$data); //  Proxy {msg: 'msg数据'}
                console.log("msg:",this.msg); // 'msg数据'
                console.groupEnd();
            },
            beforeMount() {
                console.group("beforeMount=====>")
                console.log("加载和编译模板结构,生成虚拟DOM树(JS对象)=>绑定响应数据=>引入diff算法");
                console.log("$el:",this.$el); // null
                console.log("$refs.pDom:",this.$refs.pDom); // undefined
                console.groupEnd();
            },
            // 应用:需要基于页面元素进行初始化构建的相关操作,都会在mounted进行调用
            // 该构造函数是vue自动化执行的初始方法中的最后一个,且上述生命周期构子函数只会执行一次
            mounted() {
                console.group("mounted=====>")
                console.log("基于虚拟节点(vnode)构建的带有响应式功能的真实DOM树,已完成创建和页面写入");
                console.log("$el:",this.$el); // 页面最终容器DOM
                console.log("$refs.pDom:",this.$refs.pDom); // 页面最终的元素
                console.groupEnd();
            },
            // 被动监控变量更新后的 vnode操作和DOM更新
            beforeUpdate() {
                console.group("beforeUpdate=====>")
                console.log("数据仓库变量已被修改,DOM尚未更新");
                console.log("$data:",this.$data);
                console.log("$refs.pDom.textContent:",this.$refs.pDom.textContent);
                console.groupEnd();
            },
            updated() {
                console.group("updated=====>")
                console.log("基于diff算法的vnode更新完成,同时DOM渲染完成");
                console.log("$data:",this.$data);
                console.log("$refs.pDom.textContent:",this.$refs.pDom.textContent);
                console.groupEnd();
            },
            // vue3的销毁构造函数
            beforeUnmount() {
                console.group("beforeUnmount=====>")
                console.log("开始销毁");
                console.groupEnd();
            },
            unmounted() {
                console.group("unmounted=====>")
                console.log("销毁完成");
                console.groupEnd();
            },
            // vue2的销毁构造函数
            // beforeDestroy() {    
            // },
            // destroyed() {
            // },
        });

        // setTimeout(() => {
        //     app.mount("#app")
        // }, 5000);
        app.mount("#app")
    </script>
 <div id="app">
        <div class="swiper-container" ref="loop">
            <div class="swiper-wrapper">
                <div class="swiper-slide">slider1</div>
                <div class="swiper-slide">slider2</div>
                <div class="swiper-slide">slider3</div>
            </div>
        </div>
        <hr>
        <pre>请求结果:{{ result }}</pre>
    </div>

    <script type="module">
        import { createApp } from "../assets/vue/3.0/vue.esm-browser.js";
        createApp({
            data(){
                return {
                    result:{}
                }
            },
            methods: {
                initSwiper(){
                    new Swiper(this.$refs.loop, {
                        autoplay: 5000,//可选选项,自动滑动
                    })
                },
                loadInitData(){
                    axios.get("http://127.0.0.1:3000/public/user")
                        .then(( response )=>{
                            console.log( response.data );
                            this.result = response.data
                        })
                        .catch(()=>{
                            alert("请求失败")
                        })
                }
            },
            created() {
                this.loadInitData();
            },
            mounted() {
                // 不要在生命周期构子函数中定义大量的逻辑执行代码
                // 以方法方式分解功能,通过实例进行调用
                // new Swiper(this.$refs.loop, {
                //     autoplay: 5000,//可选选项,自动滑动
                // })
                // this.loadInitData();
                this.initSwiper();
            },
        }).mount("#app")
    </script>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值