JavaScript入门 异步组件/自定义指令/自定义插件/模态框-Teleport传送门/开发模式框架/响应式原理/虚拟DOM/重绘重排/vue 赋值异步问题/nextTikct Day05

异步组件


大型项目中,我们可能需要拆分应用为更小的块,并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent 方法来实现此功能。

同步组件

从当前文件定义的

异步组件

组件由异步获取,可能是从网络或是客户端获取,不是当前文件定义的

Suspense 内置组件

配合异步组件使用,在异步组件加载完成前显示自定义内容

		<div id="app">
            <com-a></com-a>
            <Suspense>
                <my-com></my-com>
                <template #fallback>
                    加载中...
                </template>
            </Suspense>
        </div>

      const {defineAsyncComponent } = Vue
      import {defineAsyncComponent} from 'Vue'
            // 定义异步组件
            const MyCom = defineAsyncComponent(()=>{
                return new Promise((resolve,reject)=>{
                    // 从服务端获取异步组件ComB
                    setTimeout(()=>{
                        resolve(ComB)
                    },3000)
                })
            })
            //定义组件 服务端返回的异步组件
            const ComB = {
                template:` <p>异步组件</p>`
            }

 自定义指令


内置指令

v-开头的特殊属性

<input v-model="message" >

v-show    v-bind   v-on     v-for    v-if

自定义指令

全局自定义指令

app.directive()

   <div id="app">
        <p v-color="'red'">元素1</p> <!--这里'red'用字符串,如果不用引号red就在下面根data()处设置-->
    </div>
    <script>
        const {createApp} =Vue
         app.directives('color',{//全局指令
             mounted(el,binding){   //binding的值是'red'
                 el.style.color = binding.value
             }
         })
        const app = createApp({
            data(){
                return {
                    red:'blue' //如果上面的red没引号 就是将这里的blue传过去
                }
            },
        }).mount('#app')
    </script>

局部自定义指令

directives

   <div id="app">
        <p v-color="'red'">元素1</p> <!--这里'red'用字符串,如果不用引号red就在下面根data()处设置-->
    </div>
    <script>
        const {createApp} =Vue
        const app = createApp({
            //局部指令
            directives:{
                //指令名称
                color:{
                     //el 使用指令的元素节点
                    mounted(el,binding){//binding的值是'red'
                        el.style.color = binding.value
                    }
                },
            },
            data(){
                return {
                    red:'blue' //如果上面的red没引号 就是将这里的blue传过去
                }
            },
        }).mount('#app')
    </script>

自定义插件


插件

为vue提供全局功能的技术

vueRouter(实现路由)    vuex(实现状态管理)

定义

包含install方法的一个对象

const aplugin = { //插件名名
install(app,options){ //app是整个应用实例 //options选项 注册插件时传入的可以是对象函数
    //定义全局功能
    // 例如全局指令:全局组件 全局指令
    // 例如全局属性方法 $emit()
    }
}

注册

app.use(aplugin,{/*options*/})  //aplugin是插件名

事例

		<div id="app">
            <p v-color="red">元素一</p>
            <button-counter></button-counter>
        </div>
		<script>
			const { createApp } = Vue
			//定义插件
			const myPlugin = {
				install(app, options) {
                    app.directive('color',{
                        mounted(el,binding) {
                            //接收颜色值,设置给节点对象
                            let color = binding.value //颜色值
                            console.log('color ',color);
                            el.style.color = color  //设置给节点对象
                        },
                    })
			const app = createApp({
				data() {
					return {
                        red:'red'
                    }
				}
			})           
            // 注册插件
            app.use(myPlugin,{
                name:'插件'
            })
            app.mount('#app')
		</script>

Teleport传送门


是一个内置组件,它可以将一个组件内部的一部分模板“传送”到该组件的 DOM 结构外层的位置去。常见例子:全屏的模态框

<Teleport> 接收一个 to prop 来指定传送的目标。to 的值可以是一个 CSS 选择器字符串,也可以是一个 DOM 元素对象。这段代码的作用就是告诉 Vue“把以下模板片段传送到 body 标签下”

		<div id="app">           
			<button @click="onOpen">打开模态框</button>
			<teleport to="body">
				<modal v-if="isShow" @close-modal="onClose">
					<template #content>
						<div>这是模态框内容</div>
					</template>
				</modal>
			</teleport>
		</div>

  过滤器 filter(V2x了解)、混入mixins


全局过滤器

Vue.filter("过滤器名称", (形参=管道符前面的数据,形参=想要传入的数据...)=>{
                //业务
                return 结果
           })

 局部过滤器

          filter:{
                data(){},
                methods:{},
                filter:{
                    '过滤器名称'(参数){
                        return 结果
                    }
                }
            }

使用

{{ 参数 | 过滤器名称 }}

混入mixins(vue3有更佳的组合式api)

 模块混入,组合成一个

        <div id="app">
            <mixin-com></mixin-com>
            <child></child>
        </div>
			const { createApp } = Vue
            const MixinCom = {
                template:'<button @click="add">{{count}}加一</button>',
                data() {
                    return {
                        count: 10,
                    };
                },
                methods: {
                    add(){
                        this.count++
                    }
                },
            }
            const Child = {
                mixins:[MixinCom], //混入组件 混入后加减操作的是同一个值 方法也是一个道理
                template:`<div>
                            <button @click="minus">{{count}} 减一</button>
                            <button @click="add">{{count}} 加一</button>
                        </div>`,
                methods:{
                    minus(){
                        this.count--
                    }
                }
            }

开发模式框架[面试理论]


在长期开发过程中总结出来的一种开发框架,也就是代码架构的写法

MVC

jsp + javabean + servlet

视图     模型          控制器

Model 数据模型

        负责提供数据,用于数据存储及业务逻辑,在后端里充当连接数据库,获取数据的作用

View 视图模型

        负责界面显示,用于与用户实现交互的页面

Contorller 控制器

        product(接收请求) -> model product(让模型数据和视图进行渲染)

                                          view productView(渲染后响应给用户)

MVP

Model 数据模型

        负责提供数据,用于数据存储及业务逻辑,在后端里充当连接数据库,获取数据的作用

View 视图模型

        负责界面显示,用于与用户实现交互的页面

Present 表示层

        用于连接M层、V层,完成M与V的交互,还可以进行业务逻辑处理。这里M的数据不会影响到V层。

 MVC和MVP的区别

用户请求MVC里直接到的Contorller 控制器层,MVP到的View层

MVPPresent层Model和View层进行隔离相互不通讯,用Present层进行交互

MVVM

Model 数据模型

        负责提供数据,用于数据存储及业务逻辑,在后端里充当连接数据库,获取数据的作用

View 视图模型

        负责界面显示,用于与用户实现交互的页面

VM -viewModel 

将Model和View进行关联的纽带

Model 数据        <-  VM -viewModel  ->       View 视图

Model 数据 变化可以通过VM让View自动更新,View变化同理

 双向绑定原理/响应式原理[面试]


采用“数据劫持”结合“发布者-订阅者”模式的方式,
通过“Object.defineProperty()”方法来劫持各个属性的setter,getter,
数据变动时发布消息给订阅者,触发相应的监听回调。

Vue3 Proxy代理

Vue2 Object.defindPropery() 数据劫持

 虚拟DOM/重排重绘[面试]


虚拟DOM

h函数是创建虚拟dom的函数,虚拟dom本质上就是一个普通的JS对象,用于描述视图的界面结构,将真实DOM节点用js对象构造出来,即模拟真实的DOM生成的一个js对象

在vue中,每个组件都有一个render函数,每个render函数都会返回一个虚拟dom树,这也就意味着每个组件都对应一棵虚拟DOM树。

为什么需要虚拟DOM?

vue在渲染时,使用虚拟dom来替代真实dom,主要为解决渲染效率的问题 

在vue中,渲染视图会调用render函数,这种渲染不仅发生在组件创建时,同时发生在视图依赖的数据更新时。如果在渲染时,直接使用真实DOM,由于真实DOM的创建、更新、插入等操作会引起重排(回流)、重绘带来大量的性能损耗,从而就会极大的降低渲染效率。 

重排重绘 

重绘

CSS 样式改变(例如:visibility,背景色的改变),使浏览器需要根据新的属性进行绘制。 

重排

对DOM的修改引发了DOM几何元素的变化,如宽高变化等,渲染树需要重新计算,重新生成布局,重新排列元素

重绘不一定导致重排,但重排一定会导致重绘:

单单改变元素的外观,不会引起网页重新生成布局,但当浏览器完成重排之后,将会重新绘制受到此次重排影响的部分。比如改变元素高度,这个元素乃至周边dom都需要重新绘制。

浏览器渲染引擎工作流程

浏览器渲染引擎工作时,会先解析HTML然后生成DOM树,与此同时,渲染引擎也会用CSS解析器解析CSS文档构建CSSOM树。
接下来,DOM树和CSSOM树关联起来构成渲染树(RenderTree)然后浏览器按照渲染树进行布局(Layout),最后一步通过绘制显示出整个页面。 

vue 赋值异步问题/nextTikct


vue框架监听响应数据变化更新界面是一个异步过程

赋值异步--赋值不会异步,异步是指赋值和页面的渲染是异步

如果不是渲染异步,那么页面上会执行3次渲染,但页面上是同时更新渲染,原因是:

           为了提升页面渲染的性能,对赋值做了一个合并渲染处理--它使用的原理是防抖的原理

           当赋值以后会起一个定时器,如果在定时器时间内有执行第二次赋值,那么会清除第一次定时器,重新起一个新的定时器, 依此类推,到最后一次赋值,然后再执行渲染

           即防抖执行最后一次,节流执行第一次

 解决赋值异步问题?

console.log('当前节点宽度:', this.$refs.node.clientWidth)

可以使用异步来实现问题解决

定时器--setTimeout、setInterval、Promise、nextTick、async、requestAnimationFrame

以上的解决方案渲染出结果优先级为:Promise、nextTick、requestAnimationFrame、setTimeout、setInterval

组件的 $nextTick(cb) 方法,会把 cb 回调推迟到下一个 DOM 更新周期之后执行。

$nextTick返回的是promise

通俗的理解是:等组件的DOM 更新完成之后,再执行 cb 回调函数。从而能保证 cb 回调函数可以操作到最新的 DOM 元素。

 //回调写法
 this.$nextTick(() => {
     console.log('nextTick 当前节点宽度:', this.$refs.node.clientWidth)
 })
 // Promise写法
this.$nextTick().then(() => {
    console.log('nextTick 当前节点宽度:', this.$refs.node.clientWidth)
 })

<template>
    <div>
        <h4>vue 赋值异步问题</h4>
        <p> 第一个值:{{value1}}
            <span class="box" ref='node' :style="{width: value1 + 'px'}"></span>
        </p>
        <p> 第二个值:{{value2}}</p>
        <p>第三个值:{{value3}} </p>
        <p><button @click="changeValue">change value</button>/p>
    </div>
</template>
<script>
export default {
    data() {
        return {
            value1: 10,
            value2: 1,
            value3: 2
        }
    },
    methods: {
        async changeValue() {
            setTimeout(() => {
                console.log('setTimeout 当前节点宽度:', this.$refs.node.clientWidth)
            })
            this.value1 += 1
            this.value2 += 1
            this.value3 += 1
            Promise.resolve().then(() => {
                console.log('Promise 当前节点宽度:', this.$refs.node.clientWidth)
            })
            let timer = setInterval(() => {
                clearInterval(timer)
                console.log('setInterval 当前节点宽度:', this.$refs.node.clientWidth)
            })
            requestAnimationFrame(() => {
                console.log('requestAnimationFrame 当前节点宽度:', this.$refs.node.clientWidth)
            })
            this.getNodeWidth()
            // 因为这里只有resolve返回,所以可以使用async+await来实现
            await this.$nextTick()
            console.log('nextTick 当前节点宽度:', this.$refs.node.clientWidth)
        },
        async getNodeWidth() {
            console.log('async 当前节点宽度:', this.$refs.node.clientWidth)
        }
    },
    updated() {
        console.log('------------------- updated')
    }
}

		<div id="app">
			<p ref="pref">{{count}}</p>
			<button @click="plus">加一</button>
		</div>
		<script>
			const { createApp } = Vue
			createApp({
				data() {
					return {
						count: 0,
					}
				},
				methods: {
					plus() {
						this.count++
						console.log('this.count  : ', this.count)//2
						// vue框架监听响应数据变化, 更新界面是一个异步过程
						// 通过节点获取内容
						const pEle =  document.querySelector('p') 
						const pEle = this.$refs.pref
						console.log('pEle.innerHTML  :',pEle.innerHTML);//1
						this.$nextTick(() => {
							const pEle = this.$refs.pref
							console.log('pEle.innerHTML  :', pEle.innerHTML)//2
						})
					},
				},
			}).mount('#app')
		</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值