背景
为适应业务需求,规则说明的固定内容转换为由接口动态获取,这就要求前端对规则说明的渲染改变技术方案~其中使用v-html渲染文本,需要注意的是顺序标签的渲染~
技术点
- Vue渲染文本的方式
- style属性scoped相关
- a标签点击事件
技术点解析
Vue渲染文本的方式
- v-text
- {{}}
- v-html
v-text
- 将元素当成纯文本输出
- 相对比较安全
- 可简写为{{}},支持逻辑运算
- 且不会出现{{}}一闪而过之后消失,重新渲染元素的体验,但是使用起来不如{{}}方便
<p v-text="message"></p>
{{}}
- 将元素当成纯文本输出
- 当页面渲染过大过慢时,会出现{{}}一闪而过之后消失重新渲染元素的体验
<p> {{}} </p>
v-html
- 将元素当成HTML标签解析后输出
- 存在安全性问题
<p v-html="html"></p>
style属性scoped相关
定义
- HTML5中的新属性
- 数据类型为布尔类型
作用
- 实现组件的私有化
- 只作用于当前组件的元素
- 组件之间互不污染,实现模块化
原理
- css带属性选择器
- scoped会在DOM结构及css样式上加上唯一性的标记【data-v-something】属性
<style scoped>
.a >>> .b { /* ... */ }
</style>
将上述代码编译以后如下所示:
.a[data-v-f3f3eg9] .b { /* ... */ }
穿透
任何事物都有两面性,scoped也不例外,其使用起来虽然方便,但也会存在局限性。当需要修改公共组件时,便需要了解穿透的解决方案-深度作用选择器
- >>>
- /deep/ (sass或less) 如果使用/deep/报错或不生效,使用::v-deep
- 使用两个style标签,一个带scoped,一个不带scoped,用以达到修改第三方组件的目的
解决方案
- 使用v-html渲染接口获取的规则说明
- 去掉scoped修饰
- 在添加scoped属性的前提下,使用深度选择器进行样式穿透,成功渲染顺序标签
代码
方案一:v-html + 去掉scoped修饰:这个方法不建议使用,会改变布局,导致组件之间样式冲突
<template>
<div class="rule-main">
<div v-html="integralRule"></div>
</div>
</template>
<script>
export default {
data() {
return {
integralRule: ''
}
},
mounted() {
this.getRule()
},
methods: {
async getRule() {
const res = {
"data": "\n <ol><li>suger and solt</li></ol>"
}
this.integralRule = res.data
}
}
}
</script>
<style lang="less">
.rule-main {
min-height: 100vh;
padding: 10rpx 24rpx 30rpx 48rpx;
color: #333;
background: #f6f6f6;
}
ol, li {
list-style: decimal;
}
</style>
方案二:v-html + scoped修饰 + 深度选择器
<template>
<div class="rule-main">
<div v-html="integralRule"></div>
</div>
</template>
<style lang="less" scoped>
.rule-main {
min-height: 100vh;
padding: 10rpx 24rpx 30rpx 48rpx;
color: #333;
background: #f6f6f6;
}
/deep/ li {
list-style: decimal;
}
</style>
方案三:定义两个style标签,一个含有scoped属性,一个不含有scoped属性,将需要穿透的属性放在不含有scoped属性的style标签中
<style lang="less">
.hight-color {
color: #FF5E29;
}
.font-weight {
font-weight: 500;
}
</style>
<style lang="less" scoped>
.in-box {
margin: .16rem .12rem 0;
padding-bottom: .16rem;
}
方案四:通过给各个组件的第一层标签设置唯一class或者id,使用scss,然后去掉scoped。
注意:需要严格控制class 和 id 的 根命名。保证其唯一性。
UI
扩展
- 微信小程序使用<rich-text nodes="{{desc}}">渲染文本
- Scoped CSS | Vue Loader
<a>标签点击事件
v-html原理
v-html是vue中用来将string形式的html内容按普通HTML插入的命令 - 并且插入的内容不会作为 Vue 模板进行编译 ,继而相关标签的事件则会导致无效
解决方案(使用事件代理解决v-html点击事件无效)
<div @click="eventGet" class="mid-rule" v-html="midRule"></div>
midRule() {
const replaceRuleFCopy = this.activityInfo?.ruleFCopy?.replace('$awardSendTime$', `<span class="hight-color font-weight">${this.activityInfo.awardSendTime || ''}</span>`)
.replace('$goodsSpan$', `<span class="hight-color font-weight">${this.activityInfo.goodsSpan || ''}</span>`)
.replace('$seeGoodsDetail$', `<a style="text-decoration: underline;" @click="jumpGoodsLandingPage">${this.activityInfo.seeGoodsDetail || '查看活动商品>'}</a>`)
return replaceRuleFCopy
}
methods: {
eventGet(e) {
// 在判断事件目标节点的时候,考虑到兼容性应该统一转换成大写或小写进行判断
if (e.target.localName.toLowerCase() === 'a') {
// 通过判端目标节点,在这里对其进行操作
this.jumpGoodsLandingPage() // 调用点击事件方法
}
},
jumpGoodsLandingPage() {
const getQueryString = () => new URLSearchParams(window.location.search).toString()
const urlSearchParams = getQueryString()
const pageUrl = `${this.activityInfo?.packageGoodsLandingPageUrl}?${urlSearchParams}`
window.location.href = pageUrl
}
}