vue中的compiler

1、generate.js 

const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; //{{aaa}}

export function generate(el) {
  console.log("-----------", el);

  //遍历树   将树拼接成字符串
  let children = genChildren(el);
  let code = `_c("${el.tag}",${
    el.attrs.length ? genProps(el.attrs) : "undefined"
  }${children ? `,${children}` : ""})`;

  return code;

  console.log(code);
}

function genProps(attrs) {
  let str = "";
  for (let i = 0; i < attrs.length; i++) {
    let attr = attrs[i];
    if (attr.name === "style") {
      //color:red;background:blue;
      let styleObj = {};
      arr.value.replace(/([^;:]+)\:([^;:]+)/g, function () {
        console.log(arguments[1], arguments[2]);
        styleObj[arguments[1]] = arguments[2];
      });
      attr.value = styleObj;
    }

    str += `${arr.name}:${JSON.stringify(arr.value)},`; //stringify 是为了添加双引号的
  }

  return `${str.slice(0, -1)}`;
}

// html 字符串  转化成字符串 c_('div',{id:'app',a:1},'hello');

function genChildren(el) {
  let children = el.children;
  if (children) {
    return children.map((c) => gen(c)).join(",");
  }
  return false;
}

function gen(el) {
  if (el.type == 1) {
    //element =1   text=3;
    return generate(el);
  } else {
    let text = el.text;

    if (defaultTagRE.test(text)) {
      return `_v('${text}')`;
    } else {
      //
      let tokens = [];

      let match;
      let lastIndex = (defaultTagRE.lastIndex = 0); //css-loader 原理一样 就是一个字符串拼接
      while ((match = defaultTagRE.exec(text))) {
        let index = match.index; //开始索引
        if (index > lastIndex) {
          tokens.push(JSON.stringify(text.slice(lastIndex, index)));
        }
        tokens.push(`_s(${match[1].trim()})`);  //JSON.stringify()

        let lastIndex = index + match[0].length;
      }

      if (lastIndex < text.length) {
        tokens.push(JSON.stringify(text.slice(lastIndex)));
      }

      return `_v(${tokens.join("+")})`;
    }
  }
}

2、index.js

import { generate } from './generate';
import {parserHTML} from './parser';

export function compileToFunction(template) {
  let root=parserHTML(template); //解析成html

  /* render(){
    return _c('div',{id:'app',a:1},'hello');
  }
 */
  //
  
  let code=generate(root);


}

//htmlparser2 开始标签  结束标签  文本标签

// ast 语法层面的描述  (js  css  html)  vdom (dom节点而已) 


// html -> ast(语法不存在的不能描述,只能描述存在的语法) ->render 函数 虚拟dom  (增加额外属性)->生成真实dom

3、init.js

import { compileToFunction } from ".";

Vue.prototype.$mount = function (el) {
  const vm = this;
  const options = vm.$options;
  el = document.querySelector(el);

  if (!options.render) {
    let template = options.template;
    if (!template && el) {
      template = el.outerHTML;
      let render = compileToFunction(template);
      options.render = render;
    }
  }
  console.log(options.render);   //调用render方法  渲染成真实dom  替换掉页面的内容

  mountComponent(vm,el);   //组件的挂载流程

};

 4、lifecycle.js


export function mountComponent(vm,el){
    console.log(vm,el);

    // 更新函数 数据变化后  会再次调用此函数
    let updateComponent=()=>{

        // 调用render函数 生成虚拟dom 

        // 用虚拟dom 生成真实dom

        vm._update(vm._render());

        // 后续更新可以调用updateComponent方法;


    }

    updateComponent();
}

export function lifecycleMixin(Vue){
    Vue.prototype._update=function(vnode){
        console.log('update',vnode);

        const vm=this;
        let render=vm.$options.render;   // 就是我们解析出来的render方法,同时也有可能是用户写的。
        let vnode=render.call(vm);

        return vnode;
    }
}

5、parser.js


const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; //标签名
const qnameCaptrue = `((?:${ncname}\\:)?${ncname})`; //用来获取标签名 match后的索引为1的  <aa:xxx></aa:xxx>
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 匹配开始标签
const endTag = new RegExp(`^\\/${qnameCaptrue}[^>]*>`); // 匹配闭合标签
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+)+|'([^']*)'+|([^\s"'=<>`]+)))?/;
// 匹配属性  a=b  a="b"   a='b'

const startTagClose = /^\s*(\/?)>/; //   />
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g; //{{aaa}}

// 分组  ()
// 可有可无 ?

let r = "<xxxxx>".match(new RegExp(qnameCaptrue));
console.log(r);

function createAstElement(tagName, attrs) {
  return {
    tag: tagName,
    type: 1,
    children: [],
    parent: null,
    attrs,
  }
}

let root = null;
let stack = [];

function start(tagName, attributes) {
  let parent = stack[stack.length - 1];
  let element = createAstElement(tagName, attributes);
  if (!root) {
    root = element;
  }
  if (parent) {
    element.parent = parent;
    parent.children.push(element);
  }
  stack.push(element);

  console.log("start", tagName, attributes);
}
function end(tagName) {
  let last = stack.pop();
  if (last.tag !== tagName) {
    throw new Error("标签有误");
  }
  console.log("end", tagName);
}

function chars(text) {
  text = text.replace(/\s/g, "");
  let parent = stack[stack.length - 1];
  if (text) {
    parent.children.push({
      type: 3,
      text,
    });
  }
  console.log("charts", text);
}

export function parserHTML() {
  function advance(len) {
    html = html.substring(len);
  }
  function parserStartTag(html) {
    const start = html.match(startTagOpen);
    if (start) {
      const match = {
        tagName: start[1],
        attrs: [],
      };
      advance(start[0].length);
      let end;
      let attr;
      console.log(html);
      // 如果没有遇到标签结尾,就不停的解析
      while (
        !(end == html.match(startTagClose)) &&
        attr == html.match(attribute)
      ) {
        console.log(attr);
        match.attrs.push({
          name: attr[1],
          value: attr[3] || attr[4] || attr[5],
        });
        advance(attr[0].length);
      }
      if (end) {
        advance(end[0].length);
      }
      return match;
    }
    return false;
  }

  while (html) {
    //看要解析的内容是否存在,如果存在就不停的解析
    let textEnd = html.indexOf("<"); //当前解析的开头
    if (textEnd == 0) {
      const startTagMatch = parserStartTag();
      if (startTagMatch) {
        start(startTagMatch.tagName, startTagMatch.attrs);
        continue;
      }
      const endTagMatch = html.match(endTag);
      if (endTagMatch) {
        end(endTagMatch[1]);
        advance(endTagMatch[0].length);
        continue;
      }
    }

    let text;
    if (textEnd >= 0) {
      html = html.substring(0, textEnd);
    }
    if (text) {
      chars(text);
      advance(text.length);
    }
  }

  return root;
}




// 看一下用户是否传入了render
/***
 * render 
 * 没传入  可能传入的是template   template也没有传入  就是外层的div了
 * 将我们的html  =》 词法解析 (开始标签 结束标签 属性  文本)
 * 
 * ast语法树  用来描述html
 * 
 */

 export function compileToFunction(template){
     let root=parserHTML(template);

     let code=generate(root);

     let render=new Function(`with(this){return ${code}}`);   //code 中会用到数据  数据在vm上

     console.log(render.toString());

     return render;
     /* render.call(vm); */

     // html =>ast (只能描述语法  语法不存在的属性无法描述 ) =>render 函数  => 虚拟dom  (增加额外的属性) => 生成真实dom 

     // render 函数 + (width +new Function) =>虚拟dom  (增加额外的属性) => 生成真实dom
 }

 /* let vm={arr:1}
 with(this){
     console.log(arr);
 }; */

6、render.js

export function renderMixin(Vue) {
  Vue.prototype._c = function (tag, data, ...children) {
    //createElement
    return createElement(this, ...arguments);
    console.log(tag, data, ...children);
  };

  Vue.prototype._v = function (text) {
    //createTextElement
    return createTextElement(this, text);
    console.log(text);
  };

  Vue.prototype._s = function (val) {
    //stringify
    return JSON.stringify(val);
  };

  Vue.prototype._render = function () {
    const vm = this;
    let render = vm.$options.render;

    let vnode = render.call(vm);
    console.log("render");
  };
}

 7、state.js

function proxy(vm, source, key) {
  console.log(vm, source, key);
  Object.defineProperty(vm, source, {
    get() {
      return vm[source][key];
    },
    set(newValue) {
      vm[source][key] = newValue;
    },
  });
}

function initData(vm) {
  let data = vm.$options.data;

  data = vm._data = isFunction(data) ? data.call(vm) : data;

  // 用户去vm.xxx => vm._data.xxx
  for (let key in data) {
    proxy(vm, "_data", key);
  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

vues

刚好遇见你

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值