DEV Community

Gil Rudolf Härdi
Gil Rudolf Härdi

Posted on

Code your own vue: methods and events

Hi everyone, It's been a while since the last chapter of 'Code your own vue', in the last post we saw how to do our own lifecycle hooks, today we going to see how to code methods and events.
If you followed atleast the first chapter you can use methods and events in this way

<div id="app">
    <h1>{{ msg }}</h1>
    <button onclick="hi()">Click me</button>
</div>
Enter fullscreen mode Exit fullscreen mode
const vm = new Vue({
    el: "#app",
    data: {
        msg: "Hello",
    },
});

// toggle vm.msg between 'Hello' and "World"
const hi = () => vm.msg = vm.msg === "Hello" ? "World" : "Hello";
Enter fullscreen mode Exit fullscreen mode

But today we going to programm this in the vue way:

<div id="app">
    <h1>{{ msg }}</h1>
    <button v-on:click="hi">Click me</button>
</div>
Enter fullscreen mode Exit fullscreen mode
const vm = new Vue({
    el: "#app",
    data: {
        msg: "Hello",
    },
    methods: {
        hi() {
            this.msg = this.msg === "Hello" ? "World" : "Hello";
        },
    },
});
Enter fullscreen mode Exit fullscreen mode

Implementing methods

First we can define a function that will read the methods and mix all those methods with our vue instance. Like this:

function walkMethods(vue, methods) {
  for (const key in methods) {
    vue[key] = methods[key];
  }
}
Enter fullscreen mode Exit fullscreen mode

Then call the function in the constructor before the created lifecycle hook.

class Vue {
  constructor({ methods }) {
    // Before Create
    walkMethods(this, methods);

    // Create

    // Mount
Enter fullscreen mode Exit fullscreen mode

And now you should be able to call this.[method] in the vm or vm.[method] outside of vue.

Implementing events

Implementing events is more dificult. Javascript Dom cannot get attributes with specials characters like @click or v-on:click. So we need to handle that, for that I decided read the innerHTML and add vue-event=[event] and vue-event-method=[method] as attribute when a @[event] or v-on:[event] is found in a element. Other thing to consider is editing the innerHTML, if we add a event and edit the innerHTML the element will lose all events, for this reason we need to edit the innerHTML before adding any event.

const regex = {
  // regex to get v-on:[event]="[method]" and @[event]="[method]"
  vueOn: /(@|v-on:)\w+="([0-z.?]+)\(?\)?"/,
};

// replace v-on:[event]=[method] to selectionable attributes
function replaceAttributes(el) {
  el.innerHTML = el.innerHTML.replace(
    new RegExp(regex.vueOn, "g"),
    (match) => {
      // get event and method as [event]=[method]
      // from @[event]=[method] or v-on:[event]=[method]
      const attr = /@/.test(match) ? match.slice(1) : match.split(":")[1];
      // get event and method without quotes
      const [ event, method ] = attr.replace(/"/g, "").split("=");
      return `vue-event=${event} vue-event-method=${method}`;
    }
  );

  return el;
}
Enter fullscreen mode Exit fullscreen mode

After that we need a function that read all element with the vue-event attribute, add the event listener and remove all those attributes.

function addEvents(vue) {
  vue.$el.querySelectorAll("[vue-event]").forEach((el) => {
    const event = el.getAttribute("vue-event");
    const method = el.getAttribute("vue-event-method");

    el.addEventListener(event, vue[method].bind(vue.$data));
    clearElement(el, ["vue-event", "vue-event-method"])
  });
}

function clearElement(el, attributes) {
  attributes.forEach(attr => el.removeAttribute(attr));
}
Enter fullscreen mode Exit fullscreen mode

And finally, we need to use those functions on our render function.

const regex = {
  mostach: /\{\{((?:.|\r?\n)+?)\}\}/,
};

function renderVue(vue) {
  const originalTemplate = replaceAttributes(vue.$el.cloneNode(true));

  return () => {
    const { $data } = vue;

    vue.$el.innerHTML = originalTemplate.innerHTML.replace(
      new RegExp(regex.mostach, "g"),
      (_, val) => $data[val.trim()]
    );

    addEvents(vue);
  };
}
Enter fullscreen mode Exit fullscreen mode

Example of the rendering:

<!-- Original -->
<button v-on:click="foo">I'm a button<button>

<!-- After replaceAttributes -->
<button vue-event="click" vue-event-method="foo">I'm a button<button>

<!-- After rendering -->
<button>I'm a button<button>
Enter fullscreen mode Exit fullscreen mode

Conclusion

And we are finally done, adding methods to vue is really easy but code the vue events can be a headache.

You can see more about code your own vue here

Discussion (1)

Collapse
ghaerdi profile image
Gil Rudolf Härdi Author

If you have readed the last two post about this series, just let you know that I did a little update to both post, mostly code.