Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
<meta name="description" content="compile base 4 - traverse node and directive">
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div id="app">
    <p>{{message}}</p>
    <p>{{frames.mvc}}</p>
    <p>{{frames.mvvm}}</p>
    <p v-text="content"></p>
  </div>
  
  <button id="changeBtn">change</button>
</body>
</html>
 
const TOKEN_REG = /\{\{(.+?)\}\}/g
function parseTemplate (template) {
  let tokens = []
  let lastIndex = TOKEN_REG.lastIndex = 0
  let match
  
  while(match = TOKEN_REG.exec(template)) {
    index = match.index
    
    if (index > lastIndex) {
      tokens.push({
        value: template.slice(lastIndex, index)
      })
    }
    
    value = match[1]
    
    tokens.push({
      tag: true,
      value: value.trim()
    })
    lastIndex = index + match[0].length
  }
  
  if (lastIndex < template.length) {
    tokens.push({
      value: template.slice(lastIndex)
    })
  }
  
  return tokens
}
const compileEngine = {
  compile (el) {
    let childLinkFn = this.compileNodeList(el.childNodes)
    return function (data, el) {
      let childNodes = [].slice.apply(el.childNodes)
      childLinkFn(data, childNodes)
    }
  },
  compileNodeList (nodeList) {
    let linkFns = []
    
    for (let i = 0, l = nodeList.length; i < l; i++) {
      let node = nodeList[i]
      let linkFn = this.compileNode(node)
      let childLinkFn
      
      if (node.hasChildNodes()) {
        childLinkFn = this.compileNodeList(node.childNodes)
      }
      linkFns.push(linkFn, childLinkFn)
    }
    return function childLinkFn (data, nodes) {
      let node
      let nodeLinkFn
      let childrenLinkFn
      
      for (let i = 0, n = 0, l = linkFns.length; i < l; n++) {
        node = nodes[n]
        
        nodeLinkFn = linkFns[i++]
        childrenLinkFn = linkFns[i++]
        
        let childNodes = [].slice.apply(node.childNodes)
        
        if (nodeLinkFn) {
          nodeLinkFn(data, node)
        }
        if (childrenLinkFn) {
          childrenLinkFn(data, childNodes)
        }
      }
    }
  },
  compileNode (node) {
    let type = node.nodeType
    
    if (type === 3) {
      return this.compileTextNode(node)
    } else if (type === 1) {
      return this.compileElementNode(node)
    }
    else {
      return null
    }
  },
  compileTextNode (node) {
    let template = node.data
    let tokens = parseTemplate(template)
    
    return function (data, el) {
      let result = tokens.map((token) => {
        if (token.tag) {
          let funcBody = `return $scope.${token.value}`
          let getter = new Function('$scope', funcBody)
          return getter(data)
        }
        return token.value
      }).join('')
      
      el.data = result
    }
  },
  
  compileElementNode (node) {
    return function (data, el) {
      let exp = el.getAttribute('v-text')
      
      if (exp) {
        let funcBody = `return $scope.${exp}`
        let getter = new Function('$scope', funcBody)
        el.innerText = getter(data)
      }
    }
  }
}
class Vue {
  constructor (config) {
    this.config = config
    this.linker = compileEngine.compile(config.el)
    this.init()
  }
  
  init () {
    this.render()
  }
  
  render () {
    let data = this.config.data
    this.linker(this.config.data, this.config.el)
  }
}
let vm = new Vue({
  el: app,
  data: {
    message: 'Hello Vue.js!',
    frames: {
      mvc: 'backbone',
      mvvm: 'vue'
    },
    content: 'content'
  }
})
changeBtn.addEventListener('click', handleChange)
function handleChange () {
  vm.config.data.message = 'Hello world!'
  vm.config.data.frames.mvc = 'spine'
    vm.config.data.content = 'content2'
  vm.render()
}
Output 300px

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
ningshenpro
0viewers