IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue异步自定义指令 -> 正文阅读

[JavaScript知识库]Vue异步自定义指令

Vue异步自定义指令

涉及Vue自定义指令生命周期、Vue生命周期、v-html、dom操作、$nextTick等知识点



一、场景描述

提供:后台给你返回一段富文本并包含代码块,代码块用类来唯一标识;
需求:是前端在拿到富文本后在页面进行异步渲染,并给代码高亮显示;

二、思路分析

1.思路再现

思路:可以为接收到的富文本定义一个父容器并给其唯一标识,后端返回数据后v-html渲染,这样可以很轻松地展示富文本内容;但是在做代码高亮的时候有问题了。。。

2.做代码高亮遇到的问题详解

如果文档同步渲染做代码高亮其实是非常容易的,使用highlight.js即可,但是遇到异步创建dom节点后,问题就出现了。。。

问题一:mounted不能保证子组件全部挂载完成?

我一开始是打算在mounted里面请求富文本数据,然后在成功的回调里面获取v-html渲染的富文本内容,再通过给代码节点添加precode标签,但是我发现怎么也拿不到异步dom,心里想着mounted里面不是应该渲染完了嘛?
我以前一直认为父子组件的生命周期是:
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
所以,既然父组件mounted,那一定是子组件挂载完成了呀?那这里怎么说mounted不能保证子组件全部挂载完成?
在这里插入图片描述
然后我去中文版官方文档找到了相应的出处,如下:
在这里插入图片描述

综上可以理解为:如果子组件是一个异步组件,那父组件 mounted 时子组件或许就没有加载完成。

于是我在请求到数据后加了一层$nextTick

 getDocList() {
   docDetail(this.$route.params.id).then((res) => {
     this.docList = res.data;
     this.$nextTick(() => {
       console.log([...document.getElementById('content').getElementsByClassName('code-block')])
     });
   });
 },

下一步我是继续打算在拿到代码节点后获取节点innerTextreplace<pre><code>innerText</code></pre>,但是这种方法且不说可行性,从性能上来讲可谓是无敌拉胯了;


问题二:vue自定义指令还有生命周期?

于是我又开始面向百度编程,查阅到自定义指令v-highlight,给需要高亮的代码块加上该指令,然后获取对其子代码块进行操作。

directives: {
    highlight: {
      inserted(el, bind, vNode, oldvNode) {
        let blocks = el.querySelectorAll(".ql-code-block");
        blocks.forEach((block) => {
          hljs.highlightBlock(block);
        });
      },
    },
  },

一开始我以为这样写是没问题的,但是怎么也不生效,也获取不到dom元素,此时我就纳闷了,再去查资料,看了下自定义指令的声明周期,结果发现:

Vue.directive('focus', {
	bind: function(el){}, //每当指令绑定到元素上,立即执行 bind 函数。注意:绑定时 DOM 树还未被构建。
	inserted: function(el){}, //inserted 表示元素插入到 DOM 中的时候(已经插入),执行 inserted 函数
	updated: function(el){} //当VNode更新时,会执行 updated
	componentUpdate : function(el){} //被绑定的元素所在模板完成一次更新更新周期的时候调用
	unbind: function(el){} //只调用一次,指令与元素解绑的时候调用
})

原来在inserted不适用与同步渲染,于是我把它换成了componentUpdate,让模板在声明周期更新后调用,问题解决。

directives: {
    highlight: {
      componentUpdate(el, bind, vNode, oldvNode) {
        let blocks = el.querySelectorAll(".ql-code-block");
        blocks.forEach((block) => {
          hljs.highlightBlock(block);
        });
      },
    },
  },

三、小的知识点

1、vue中如何使用highlight.js

引入:除了引入Vue-highlight.js文件外,还需要引入样式文件highlight.js。vue-highlight.js只是实现了代码高亮的功能,安装包里是没有css样式文件,因此我们还需要安装一个highlight.js来实现真正的样式。

使用:

<!-- 1.如果你需要高亮的代码是一个变量值,那么你可以这样使用它。 其中 sourcecode 为变量。 -->
<pre v-highlightjs="sourcecode"><code></code></pre>

<!-- 2.如果你需要高亮的代码固定的源代码,那么你可以这样使用它。 -->
<pre v-highlightjs><code>const s = new Date().toString()</code></pre>


2、v-html可能导致的问题

Vue中的v-html指令用以更新元素的innerHTML,其内容按普通HTML插入,不会作为Vue模板进行编译,如果试图使用v-html组合模板,可以重新考虑是否通过使用组件来替代。
描述:

  • 易导致XSS攻击
    v-html指令最终调用的是innerHTML方法将指令的value插入到对应的元素里,这就是容易造成xss攻击漏洞的原因了。Vue在官网对于此也给出了温馨提示,在网站上动态渲染任意HTML是非常危险的,因为容易导致XSS攻击,只在可信内容上使用v-html,永不用在用户提交的内容上。 关于XSS,跨站脚本攻击XSS,是最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。当动态页面中插入的内容含有这些特殊字符如<时,用户浏览器会将其误认为是插入了HTML标签,当这些HTML标签引入了一段JavaScript脚本时,这些脚本程序就将会在用户浏览器中执行。当这些特殊字符不能被动态页面检查或检查出现失误时,就将会产生XSS漏洞。
  • 不作为模板编译
    v-html更新的是直接使用元素的innerHTML方法,内容按普通HTML插入,不会作为Vue模板进行编译,如果试图使用v-html组合模板,可以重新考虑是否通过使用组件来替代。另外后端返回<script>标签中的代码是不会直接执行的,这是浏览器的策略,如果需要的话可以在$nextTick回调中动态创建<script>标签然后src引入代码url即可。
  • scoped样式不能应用
    在单文件组件里,scoped的样式不会应用在v-html内部,因为那部分HTML没有被Vue的模板编译器处理,如果你希望针对v-html的内容设置带作用域的CSS,你可以替换为CSS Modules或用一个额外的全局<style>元素手动设置类似BEM的作用域策略。此外提一下关于样式隔离的话,Shadow DOM也是个不错的解决方案。关于CSS Modules以及BEM命名规范可以参考下面的链接。

3、$nextTick()的使用

(1)NextTick是什么

官方对其的定义

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM

可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新
举例一下:
Html结构

<div id="app"> {{ message }} </div>

构建一个vue实例

const vm = new Vue({
    el: '#app',
    data: {
        message: '原始值'
    }
})

修改message

this.message = '修改后的值1'
this.message = '修改后的值2'
this.message = '修改后的值3'

这时候想获取页面最新的DOM节点,却发现获取到的是旧值

console.log(vm.$el.textContent) // 原始值

这是因为message数据在发现变化的时候,vue并不会立刻去更新Dom,而是将修改数据的操作放在了一个异步操作队列中
如果我们一直修改相同数据,异步操作队列还会进行去重
等待同一事件循环中的所有数据变化完成之后,会将队列中的事件拿来进行处理,进行DOM的更新

  • 为什么要有nexttick?

举个例子

{{num}}
for(let i=0; i<100000; i++){
    num = i
}

如果没有 nextTick 更新机制,那么 num 每次更新值都会触发视图更新(上面这段代码也就是会更新10万次视图),有了nextTick机制,只需要更新一次,所以nextTick本质是一种优化策略

(2)使用场景

如果想要在修改数据后立刻得到更新后的DOM结构,可以使用Vue.nextTick()
第一个参数为:回调函数(可以获取最近的DOM结构)
第二个参数为:执行函数上下文

// 修改数据
vm.message = '修改后的值'
// DOM 还没有更新
console.log(vm.$el.textContent) // 原始的值
Vue.nextTick(function () {
    // DOM 更新了
    console.log(vm.$el.textContent) // 修改后的值
})

组件内使用 vm.$nextTick() 实例方法只需要通过this.$nextTick(),并且回调函数中的 this 将自动绑定到当前的 Vue 实例上

this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
this.$nextTick(function () {
    console.log(this.$el.textContent) // => '修改后的值'
})

$nextTick() 会返回一个 Promise 对象,可以是用async/await完成相同作用的事情

this.message = '修改后的值'
console.log(this.$el.textContent) // => '原始的值'
await this.$nextTick()
console.log(this.$el.textContent) // => '修改后的值'

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:24:08  更:2022-10-17 12:26:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 15:58:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码