vue源码解析之(第二步 模板编译)

#### 解析模板 转化为 字符串DOM结构的过程



  
  
  Document


  
{{name}}
{{age}}

下面我们主要用来解析模板,同时将DOM结构的模板通过outerHTML的方法,转义为字符串进行编译。
 

import {observe} from './observe/index' // 这里先引入后期再下面要创建的方法
 
export const initMiXin = (Vue){
    
    Vue.prototype._init(options) {  // 这里接收的是src/index.js中传递的参数
        
        const vm = this // 这里的this是Vue
    
        vm.$options = options // 把数据挂载在vm.$options的属性上面
 
        initState(vm) // 初始化数据
 
    } 
 
    
}
 
function initState(vm){
    
   const ops =  vm.$options
 
    if(ops.data){  // 如果有data这个属性
    
        initData(vm) // 初始化Data
 
    }

    // 这里增加处理模板的思路
    
    if(ops.el){  ++++++++++++ 处理el模板的数据
    
        vm.$mount(ops.el) // 将el进行挂载

    }
 
}


Vue.prototype.$mount = function (el) { +++++ 原型上创建$mount的方法 进行模板数据的处理
    
    const vm = this // this执行创建Vue构造函数的实例对象
    
    el= document.queryselector(el) // 获取元素

    let ops = vm.$options

    if(!ops.render) {  // 不存在render渲染模板

        let template;

        if(!ops.template && el){  // options参数中只存在 el 

            template = el.outerHTML // outerHTML 这个方法是将DOM节点转化为了字符串来显示
        }else { 

            if(el) {
        
                template = ops.template // 这是由template渲染的模板

            }

        }

        if(template) { 

            const render = compileToFunction(template) // 对字符串的模板进行编译

            ops.render = render // 往$options上面挂载一个render方法

         }
     }
      // 这里最终可以获得ops.render的方法
       
    

}
 
function initData(vm){ 
    
    let data = vm.$options.data 
 
    data = typeof data === 'function'? data() : data // 判断data类型如果是函数的话就执行
 
    vm._data= data //再往Vue上面挂载一个_data的属性
 
    observe(data) // 这里对数据进行劫持
}
####模板转化为ast语法树 虚拟DOM 真实DOM
// 标签名 a-aaa
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;  
// 命名空间标签 aa:aa-xxx
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// 开始标签-捕获标签名
const startTagOpen = new RegExp(`^<${qnameCapture}`); 
// 结束标签-匹配标签结尾的 
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配属性 const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/; // 匹配标签结束的 > const startTagClose = /^\s*(\/?)>/; // 匹配 {{ }} 表达式 const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; // 在这里 我们可以接受到字符串的DOM结构 然后进行编译 export const compileToFunction(template){ // 这里主要是转template转化为ast语法数 let ast = parseHTML(template) // 通过parseHTML方法转为ast语法数 // 然后生成render方法 返回值是虚拟DOM } // parseHTML方法创建ast语法树 function parseHTML(html) { // 第三步 创建删除html字符串的方法 advance(num){ html = html.substring(num) // 给删除" // 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] || sttr[5]} ) } if(end) { advance(end[0].length) } return match } return false // 如果不是开始标签就停止 } // 第一步 循环html字符串DOM结构 while(html){ let textEnd = html.indexof('<') // 如果索引是0 则说明是个标签 if(textEnd == 0) { // 索引为0 说明开始标签为 < // 这里的返回值已经是 {tagName:'div', attrs:{id:app}} const startTagMatch = parseStartTag() if(startTagMatch){continue} // 上面的结果执行完有返回值 然后继续下面的执行 let endTagMatch = html.match(endTag) // 寻找结束标签 if(endTagMatch){ advance(endTagMatch[0].length) // 清除结束标签 continue } } if(textEnd > 0) { // 索引大于0 的 let text = html.substring(0,textEnd) // 解析文本内容 中间内容为文本内容 if(text){ chars(text) // 处理文本标签**** 我们下面继续 advance(text.length) // 清除文本内容 } } } }

 转化为ast语法树

// 此代码依旧在compile/index.js 文件中执行

function parseHTML(html){

    const ELEMENR_TYPE = 1

    const TEXT_TYPE = 3

    const stack = [] // 存放元素

    let currentParent; // 指向栈中的最后一个
    
    let root;

    // 最终转化为ast语法树

    function createASTElement(tag,attrs){

        return { 

            tag,

            type:ELEMENT_TYPE,

            children:[],

            attrs,

            parent:null
        }

    }

    function start(tag,attrs){ // 开始

        let node = createASTElement(tag,attrs) // 创建一个ast节点

        if(!root){

            root = node // 如果为空则为数的跟节点
        }

        if(currentparent){

            node.parent = currentparent

            currentparent.children.push(node) 

        }
        
        stack.push(node) // 存放元素里面
        
        currentParent = node  // 栈中的最后一个元素
    }


    function chars(text){ // 处理文本

        text = text.replace(/\s/g,'') // 去掉空格的

        text && currentParent.children.push({

            type:TEXT_TYPE,
            
            text,

            parent:currentParent

        })

    }


    function end(tag){ // 结束

        let node =  stack.pop() // 删除最后一个元素

        currentParent = stack[stack.length-1] // 最后一个元素

    }


    return root // 最后将ast语法树返回出去

    
}


 

你可能感兴趣的:(vue.js,前端,javascript)