-
拿到模板
let tmpNode = document.querySelector('#root');
-
拿到数据
let data = { name:'张三', age:'24' }
-
将数据与模板结合
使用递归实现 匹配 {{ xxx }} 的正则 let reg = /\{\{(.+?)\}}/g; // . 匹配除换行符以外的其它任意字符, () 组的意思,g 全局匹配 function compiler(template,data) { // 取出子节点 let childNodes = template.childNodes; for(let i = 0;i<childNodes.length;i++) { // 获取节点的类型 let type = childNodes[i].nodeType; // 1 元素节点 3 文本节点 if(type === 3) { // 文本节点 let txt = childNodes[i].nodeValue; txt.replace(reg,(_,a)=> { console.log(_,a); // {{name}} {{age}} let key = a.trim(); // 去除 a 左右的空格 childNodes[i].nodeValue = data[key]; }) } else if(type === 1) { // 元素节点,继续递归,获取元素节点中的文本节点 compiler(childNodes[i],data); } } }
-
放到页面中
let generate = tmpNode.cloneNode(true); /** 执行这一步的原因: 1、直接把页面中的内容给替换了,并没有保留原模版 2、直接替换会影响后期数据的双向绑定 */ /** cloneNode() 1、指定节点的精确拷贝,拷贝所有的属性和值 2、如果参数为 true 递归复制节点的子节点,false(默认) 只复制当前节点 */ compiler(generate,data); /** 放到页面当中 root.parentNode ==> 获取到的是body中的标签 replaceChild ==> 用新节点替换某个子节点,注意只是替换,并不是把原来节点中的内容全部覆盖 */ root.parentNode.replaceChild(generate,root);
-
完整的代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>仿照vue实现</title> </head> <body> <!-- 编写页面模板 --> <div>123</div> <div id="root"> <div> <div> <span>{{name}}</span> </div> <p>{{age}}</p> </div> <p>{{ name }}</p> <p>{{ age }}</p> </div> <script> // 1 拿到模板 let tmpNode = document.querySelector('#root'); let reg = /\{\{(.+?)\}}/g; // 2 拿到数据 let data = { name:'张三', age:'24' } // 3 将数据放到模板中(递归) // template 指DOM元素 function compiler(template,data) { // 取出子节点 let childNodes = template.childNodes; for(let i = 0;i<childNodes.length;i++) { // 获取节点的类型 let type = childNodes[i].nodeType; // 1 元素节点 3 文本节点 if(type === 3) { // 文本节点 let txt = childNodes[i].nodeValue; txt.replace(reg,(_,a)=> { console.log(_,a); // {{name}} {{age}} let key = a.trim(); // 去除 a 左右的空格 childNodes[i].nodeValue = data[key]; }) } else if(type === 1) { // 元素节点,继续递归,获取元素节点中的文本节点 compiler(childNodes[i],data); } } } let generate = tmpNode.cloneNode(true); console.log('1',tmpNode,generate); compiler(generate,data); console.log('2',generate); // 4 放到页面当中 root.parentNode.replaceChild(generate,root); </script> </body> </html>
-
上面代码存在的问题
-
Vue使用的是虚拟DOM,上面的操作使用的是真实的DOM元素
将在下一篇文章将,将真实DOM转换为虚拟DOM
-
只考虑了单属性 {{ name }}, 没有考虑多属性 {{ name.firstName.secName }}
function getValueByPath(obj,path) { // obj ==> data , path ==> {{ data.name.firstName.txt }} // data.name.firstName.txt let res = obj; let paths = path.split('.'); // data.name.firstName.txt 分割为数组 let props; /* shift、unshift、push - unshift:向数组的开头添加一个或更多个元素,并返回新的长度 arr.unshift($1,$2,$3....) - push: 向数组的尾部添加一个或更多个元素 - shift:把数组的第一个元素从其中删除,并返回第一个元素的值 */ while(props = paths.shift()) { // 数组.shift() 得到的是数组中的第一项 res = res[props] } return res; }
-
代码太凌乱没有整合
-
-
关键操作方法介绍
-
正则
-
.
表示匹配除换行符以外的其它任意字符, -
()
组的意思 -
g
全局匹配 -
replace()
-
let reg = /\{\{(.+?)\}}/g; let str = 'aa{{123}}b{{456}}c{789}' str.replace(reg,(_)=> { // 回调函数中的第一个参数表示:正则匹配到的字符串 2:在使用组 匹配时,组匹配到的值 3:匹配值在原字符串中的索引 4:原字符串 })
-
-
-
节点类型 nodeType
- 1 => 元素节点
- 3 => 文本节点
-
cloneNode()
- 指定节点的精确拷贝,拷贝所有的属性和值
- 如果参数为 true 递归复制节点的子节点,false(默认) 只复制当前节点
-
以上是分析vue源码中学到的,不喜勿喷😭,感觉不错🙂,点赞👍,收藏♥