npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@tozd/vue-observer-utils

v0.5.0

Published

Exposing Vue observer internals + extensions

Downloads

77

Readme

@tozd/vue-observer-utils

This NPM package exposes Vue observer internals (code which provides reactivity in Vue). Moreover, it extends the observer with additional API.

Installation

This is a NPM package. You can install it using NPM:

$ npm install @tozd/vue-observer-utils

It requires vue peer dependency:

$ npm install vue

Usage

First, you have to register the package as a Vue plugin:

import Vue from 'vue';
import VueObserverUtils from '@tozd/vue-observer-utils';

Vue.use(VueObserverUtils);

After that, you can access:

For example, to know if you code is being executed in a reactive context you can now check if Vue.util.Dep.target is set. Moreover, Vue.util.Dep.target gives you access to the current reactive context, a Watcher instance.

API

The following are additional API functions available.

Vue.util.nonreactive(f)

Calls function f outside of a reactive context and returns its returned value.

Vue.util.onInvalidate(callback)

Registers callback to run when current reactive context is next invalidated, or runs it immediately if the reactive context is already invalidated. The callback is run exactly once and not upon future invalidations unless onInvalidate is called again after the reactive context becomes valid again.

Registered callback is run also when the reactive context is teared down.

Vue.util.onTeardown(callback)

Registers callback to run when current reactive context is teared down, or runs it immediately if the reactive context is already teared down. The callback is run after any onInvalidate callbacks.

vm.$wait(condition, effect)

Runs condition function repeatedly inside a reactive context until it returns a truthy value, after which effect function is called once provided that value. effect is run exactly once unless $wait is called again.

$wait returns unwait function which you can call to stop waiting. If condition is already satisfied when $wait is called, then effect function is called immediately even before $wait returns. Moreover, $wait returns null in this case.

vm.$await(promise, [options])

Returns undefined until the promise is resolved, after that it returns the resolved value of the promise. It is reactive so the value changes once the promise gets resolved.

Important: $await is meant to be used inside a reactive context which can rerun multiple times. Make sure that you are not creating a new (different) promise for every rerun by mistake, but reuse an existing one.

Available options:

  • key, default promise: an Object to identify the promise by, this is used to remember and retrieve resolved value in when reactive context is rerun
  • forgetRejected, default false: if promise is rejected, forget that it has been run so that if reactive context is rerun, a new attempt at resolving will be made (instead of returning undefined)
  • invalidateRejected, default false: if promise is rejected, invalidate the reactive context to force rerun

Examples

See @tozd/vue-format-time for an example how you can use Vue.util.onInvalidate to implement an efficient time-based reactive transformations.

Alert queue

const component = {
  data() {
    return {
      queue: [],
    };
  },

  created() {
    this.unwait = null;
    this.showNextAlert();
  },
  
  methods: {
    showNextAlert() {
      // Wait for the first next message to be available.
      this.unwait = this.$wait(function () {
        // Messages are enqueued from oldest to newest and "find" searches array elements in
        // same order as well, so the first one which matches is also the oldest one.
        return this.queue.find((element) => element.shown === false);
      }, function (message) {
        this.unwait = null;
        
        message.shown = true;
        
        alert(message.text);
        
        this.showNextAlert();
      });
    },
    
    addAlert(text) {
      this.queue.push({
        text,
        shown: false,
      })
    },
  },
};

For a more feature-full implementation of a notification queue, see @tozd/vue-snackbar-queue.

Reactive fetch

In this example we maintain a simple cache of promises for all fetch requests we made. We assume the mapping does not change: once user's ID is resolved to an username that never changes. In your case you might want to have cache invalidation as well. Moreover, this cache can grow indefinitely even if some values are not used anymore (for example, a particular user is not displayed anymore so we do not need their username). You can use onInvalidate to track when a particular promise is not being used anymore and if it is not used again in a later rerun, you could remove if from the cache (maybe after some expiration time).

// This is shared between all component instances.
const idToUsername = new Map();

const component = {
  data() {
    return {
      users: [
        893,
        991,
        140,
      ],
    };
  },
  
  computed: {
    usernames() {
      // We want to fetch username for each user in a reactive manner.
      return this.users.map((user) => {
        return this.getUsername(user);
      });
    },
  },

  methods: {
    // This method checks if we have already fetched (or are in process
    // of fetching) a username for this user. We use a simple Map in this
    // example to cache fetches made.
    getUsername(id) {
      if (!idToUsername.has(id)) {
        idToUsername.set(id, this.fetchUsername(id));
      }
      
      // It is important that we "$await" same promise so we use a Map
      // as a cache for that. In a way we create a new cache if an argument
      // to the "async" function changes.
      return this.$await(idToUsername.get(id));
    },
    
    // A regular "async" function without any reactivity.
    async fetchUsername(id) {
      const response = await fetch(`https://example.com/api/user/${id}`, {
        mode: 'cors',
        credentials: 'omit',
      });
    
      if (response.status !== 200) {
        throw new Error(`Fetch error: ${response.statusText}`);
      }
    
      const result = await response.json();
      
      return result.username;
    },
  },
};