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中的模板编译的步骤:将template模板转化成ast语法树(拼接字符串),然后通过new Function + with语法,将ast语法树包装成Render函数,然后生成虚拟节点,然后将虚拟节点挂载到dom树上,生成真实DOM.
(1) 将template模板转换成ast语法树 -parserHTML(正则实现)
(2) 对静态语法做静态标记 -markUp
(3) 重新生成代码 生成render函数返回的是虚拟节点
注意:在开发时尽量不要使用template,因为将template转化成render方法,需要在运行时进行编译操作,会有性能损耗,同时引用电邮compiler包的vue体积也会变大。默认.vue文件中的template处理是通过vue-loader(依赖的是vue-template-compiler)来进行处理的而不是通过运行时的编译

使用到的正则
// 匹配属性
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const dynamicArgAttribute = /^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
// 匹配开始标签开始
const startTagOpen = new RegExp(`^<${qnameCapture}`)
// 匹配开始标签闭合
const startTagClose = /^\s*(\/?)>/
// 匹配标签结束
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
const doctype = /^<!DOCTYPE [^>]+>/i

解析开始节点,先匹配到tagName,然后再去循环匹配取的属性的值,然后组装成一个节点

function parseStartTag(){
        let start = html.match(startTagOpen)
        if(start && start.length){
            const match = {
                tagName:start[1],
                attrs: []
            }
            advance(start[0].length)
            let end,attr
        while (!(end = html.match(startTagClose)) && (attr = html.match(attribute))) {
            advance(attr[0].length)
            match.attrs.push({
                name: attr[1],
                value: attr[3]|| attr[4]|| attr[5]
            })
        }
        if(end){
            advance(end[0].length)
        }   
        return match  
        }
        
    }

(1代表dom节点,3代表文本节点)
匹配完开始节点后,把已经匹配过的字符串截取掉,剩余字符串接着匹配,下面可能会出现以下几种情况
(1)接下来匹配就是结束标签
????那就恭喜你啦,这个节点已经完全匹配出来了,只需要把这个节点从我们记录的栈里弹出,形成一个下面的结构就OK啦,然后截取点已经匹配过的字符串,拿剩下的字符串继续进行匹配,直到传入的字符串全部被匹配一遍

 {
        tag: tagName,
        type: 1,
        children: [],
        attrs:attrs,
        parent: null
    }

(2)接下来匹配的还是开始标签
此时我们匹配到的这个开始标签和我们上一个标签匹配到的肯定是父子关系,从栈里取出最上面的一个节点,如果有值的话,就记录为当前节点的父节点,拿出来的节点的子节点为当前匹配的节点,然后截取点已经匹配过的字符串,拿剩下的字符串继续进行匹配,直到传入的字符串全部被匹配一遍

(3)接下来匹配的是一段文本,文字就很简单了,用正则匹配到第一个‘<’也就是下一个dom节点的位置),把匹配后取得的字符串去空格处理后,生成一个下面的文本节点就OK啦,然后截取点已经匹配过的字符串,拿剩下的字符串继续进行匹配,直到传入的字符串全部被匹配一遍

{
  text: text,
  type:3
}

等到传入的字符串全部匹配结束以后,会形成一个js描述的节点之间相互关系的dom树

{ 
     tag: 'div',
     parent: null,
     attrs:[
		{
 			name: 'id',
			value: 'app'
		}
	],
	children:[
		tag: 'span',
		parent:{
 			tag: 'div',
     		parent: null,
     		attrs:[
				{
 					name: 'id',
					value: 'app'
				}
			],
		children:[...]
		}
	]
}

如图所示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ys0T1mrl-1638887538147)(/img/bVcWvuN)]
然后就到了激动人心的时刻啦,我们需要根据这颗描述节点的树提供的内容把他拼接成可以渲染成render函数的虚拟dom,按照tagName,attrs,children的顺序进行处理,最后形成类似下面这样的字符串

_c("div",{id:"app",style:{"width":"20px"}},_c("p",undefined,_v("hello"+_s(name))),_v("hello"))

特别注意的是对于行内样式的处理,要把行内样式的值拼成key-value的形式

if(attr.name === 'style'){
                let obj = {}
                attr.value.split(';').forEach(attrItem => {
                    let [key, value] = attrItem.split(':')
                    obj[key] = value
                })
                attr.value = obj
            }

然后借助new Function + with就可以把拼接好的字符串转换成render函数了

传入el属性
判断是否有render方法
渲染render方法
判断是否有template
渲染template
渲染el里面的内容
判断字符串里是否以箭头开头
使用正则进行匹配
用正则匹配是否是开始标签
用正则分别取出来标签名和属性
将开始标签压入栈内
把已经匹配过的字段截掉重新进行匹配
用正则匹配是否是结束标签
说明是以文本开头然后
从栈里取出来最上面的标签名,形成闭合的节点
添加一个dom节点
添加一个文字节点
生成用js描述dom节点的ast语法树
拼接包装成字符串
通过new Function + with语法生成render函数

(以上是自己学习过程中整理,有错误或者不严谨的地方欢迎指正)

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-08 13:43:31  更:2021-12-08 13:44:44 
 
开发: 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年11日历 -2024/11/24 8:00:05-

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