疑惑之处:
当你使用vue时,看到这样的代码:
<script setup>
import { ref } from 'vue'
const name = ref('张三')
const age = ref(18)
const sex = ref('男')
</script>
<template>
<div>
'我是{{name}},年龄{{age}},性别{{sex}}'
</div>
</template>
<style scoped>
</style>
效果:
想要实现这个对于大家来说,那包是简简单单的呀,那有没有想过底层是如何利用js是如何实现的呢?
原题为:
用一个函数实现,对template中name、age、sex的一一替换。你想到用怎么样的方法吗?
let template = '我是{{name}}, 年龄{{age}},性别{{sex}}'
let person = {
name: '张三',
age: 18,
sex: '男'
}
今天我们就来看看,如何用正则表达式,在JS实现上述效果。
正则表达式
今天的重点不在于正则表达式,但是我们可以稍微来了解一下,它的用法:
概念:
正则表达式(Regular Expression,简称regex或regexp)是一种用于匹配字符串中字符模式的工具。它提供了一种灵活且强大的方式来搜索、编辑或操作文本,以及创建输入验证规则等。正则表达式由一系列字符组成,这些字符定义了一个搜索模式。这个模式可以用来查找、编辑或者操作字符串。
案例:
如何利用正则判断一个手机号是否为11位,且开头为1呢?
let reg=/^1[0-9]{10}/
console.log( reg.test('12345678901'))
- 该正则表述的是一个字符串如果要与这个正则表达式相匹配,它必须以
1开始,后面紧跟着 10 个数字,总共 11 位。你觉得这样写对吗? / /指定一个正则表达式字面量的方式。^1代表的是一定以一开头[0-9]:这是一个字符集,匹配任何一个从0到9的数字。{10}:量化符,表示前面的元素(这里是[0-9]或者\d)必须出现恰好 10 次。结合前面的部分,这就意味着接下来的 10 个字符都必须是数字。- 当在后面加一个a时,匹配仍然是true,这说明该匹配方法是含有即为true,所以我们需要改进一下 :
/^1[0-9]{10}$/加个$,确保字符完全符合正则,以11位结束
console.log( reg.test('12345678901a')) // true
如此一来,也算是入门了
实战:
那么对于 template = '我是{{name}}, 年龄{{age}},性别{{sex}}',这段字符串,我们如何写好正则呢?
要知道我们需要获取{{name}}、{{age}}、{{sex}},中的内容,并且替换掉。我们需要了解一些别的用法。
贪婪匹配
默认情况下,+ 是贪婪的(greedy),这意味着它会尽可能多地匹配字符,直到无法再匹配为止。例如,在字符串 "aaab" 中使用模式 /a+/ 来匹配,它将匹配所有的 'a' 字符,即返回 "aaa",而不是只匹配一个或两个 'a'。
示例
let str = "aaab";
let regGreedy = /a+/;
console.log(regGreedy.test(str)); // true
console.log(str.match(regGreedy)); // [ 'aaa', index: 0, input: 'aaab', groups: undefined ]
console.log(regGreedy.exec(str)); // [ 'aaa', index: 0, input: 'aaab', groups: undefined ]
String.prototype.match()
- 全局匹配 (
g标志) :如果正则表达式包含g(全局)标志,match返回一个数组,其中包含所有匹配的子字符串。如果不含g标志,则只返回第一个匹配项。 - 捕获组:当使用
match且没有g标志时,它会返回一个数组,其第一个元素是整个匹配结果,后面的元素是捕获组的内容(如果有)。如果指定了g标志,则不会返回捕获组的信息。 - 附加信息:返回的数组还包括三个特殊属性:
index(匹配项在原字符串中的索引)、input(原始输入字符串)和groups(命名捕获组的结果,如果有的话)。
RegExp.prototype.exec()
- 全局匹配 (
g标志) :无论是否设置了g标志,exec都只会找到下一个匹配项。如果没有更多匹配项,它将返回null。因此,为了找到所有的匹配项,你需要在一个循环中重复调用exec。 - 捕获组:
exec总是返回一个数组,其中第一个元素是整个匹配结果,后面的元素是捕获组的内容(如果有)。即使有g标志,也是一样的。 - 附加信息:同样地,返回的数组包括
index、input和groups属性。
匹配template:
let template='我是{{name}},年龄{{age}},性别{{sex}}'
let person={
name:'张三',
age:18,
sex:'男'
}
function compile(template,data){
const reg=/\{\{(\w+)\}\}/
const key =reg.exec(template)[1] // name
reg.exec(template)[0] //{{name}}
let value=data[key]
console.log(value) // 输出:张三
}
compile(template,person)
步骤:
正则的写法
- 在正则中单单一个
{代表了其他的含义,想要表示含有{,需要写成\{ \w等价于[a-zA-Z0-9_],即它可以匹配任意的字母、数字或下划线,。()的目的在于RegExp.prototype.exec()该方法的捕获组识别()内的字母, 如'name'
获取键和对应的值:
const key = reg.exec(template)[1]:调用exec来获取匹配的第一个捕获组,即name。let value = data[key]:从data对象中根据key获取相应的值,在这里是'张三'。console.log(value):输出这个值,即'张三'。
我们成功的获取了name键值,接下来就是如何替换上去,并且进行多次匹配的问题了。
解题的三种写法:
循环代替:
let template='我是{{name}},年龄{{age}},性别{{sex}}'
let person={
name:'张三',
age:18,
sex:'男'
}
function compile(template,data){
const reg=/\{\{(\w+)\}\}/
while(reg.test(template)){
let key = reg.exec(template)[1]; // 第二项是分组匹配的内容
let value = data[key];
template = template.replace(reg,value); // 更新
}
return template;
}
console.log(compile(template,person)) //我是张三,年龄18,性别男
- 循环条件为:
template中是否有符合正则的,有就继续循环 - 找到符合正则的,并进行键值对的替换,然后利用
replace的高级用法 replace根据所给的正则,进行相应的替换,这样循环,直到没有{{ }}可替换的- 最后返回
template
升级写法:
function compile(template, data) {
const reg = /{{(\w+)}}/g;
return template.replace(reg, (match, path) => {
console.log(match, path); // 用于调试输出
return path in data ? data[path] : '';
});
}
1.全局匹配
- 正则表达式的全局标志
g:通过使用/g标志,replace方法可以一次性找到并替换所有出现的占位符,而不需要显式地循环和管理状态。这使得代码更加简洁且易于理解。
2. 使用回调函数进行替换
-
replace方法结合回调函数:每次找到一个匹配项时,replace会调用回调函数,并将匹配结果及其捕获组作为参数传递给该函数。这种做法不仅简化了代码逻辑,还提高了可读性。match参数:表示与整个正则表达式相匹配的子字符串(例如{{name}})。path参数:表示第一个捕获组所匹配的文本(例如name),即占位符内的键名。
3. 更健壮的数据查找
path in data检查:确保只有当数据对象中存在对应的键时才进行替换。如果不存在,则返回空字符串(或你可以选择保留原始占位符)。这增加了代码的鲁棒性,避免了潜在的错误。
递归:
if(reg.test(template))
{
const key =reg.exec(template)[1]
const value=person[key]
template=template.replace(reg,value)
return compile(template,person);
}
else{
return template;
}
}
与while方法差不多,大家看一看
总结:
今天,学习了一下正则表达式,还实现了vue中{{}}占位符的底层分析。 尚在学习中,如有问题请提出。