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

@~crazy/watcher

v3.2.6

Published

对象观察者

Downloads

49

Readme

Watcher

数据观察者

安装

npm i @~crazy/watcher -S

使用

import { Watcher, addWatch, protect, readonly } from '@~crazy/watcher';

// 原始数据
const originalData = {
	width: 100,
	height: 100,
	x: 0,
	y: 0,
	style: {
		color: '#000',
		borderWidth: 2,
		borderColor: '#DDD',
	},
	classList: ['box', 'container'],
};

// 创建 Watcher
const data = new Watcher(originalData, {
	// 添加全局观察, 因为是全局,所以 record 为 null
	default: (record, bubble) => {
		console.log(bubble);
	},
});

data.x = 10;
/**
 * 输出
 {
	timestamp: 1657704905426,
	type: 'Update',
	path: ['x'],
	key: 'x',
	isNew: false,
	hasChange: true,
	originValue: 0,
	oldValue: 0,
	value: 10,
}
 **/

观察属性

// 观察 style.color
data.addWatch('style.color', (record) => {
	console.log(record.path);
});
data.style.color = '#F00'; // 输出 ['style', 'color']

// 立即触发 style.color 的观察
data.addWatch('style.color', {
	handler(record) {
		console.log(record.path); // 输出 ['style', 'color']
	},
	immediate: true,
});

深观察

// 深观察 style
data.addWatch('style', {
	handler(record, bubble) {
		// 表示当前 style 的数据记录,
        // 因为子孙属性改变时 style 也改变了,
        // 此时的 record 会包含一个 bubble 属性指向发生变化的源
		console.log(record);
		// 平时 bubble 为 null, 
        // 当子孙属性发生变化时,
        // 如果祖先使用了深观察,
        // 那就会开始向上冒泡,bubble 表示真正修改的数据记录
		console.log(bubble);
	},
	deep: true,
});
data.style.color = '#FF0';
/**
 * 输出
    record
    {
        "timestamp": 1657706407402,
        "type": "Add",
        "path": ["style"],
        "key": "style",
        "isNew": false,
        "hasChange": true,
        "originValue": {
            "color": "#FF0",
            "borderWidth": 2,
            "borderColor": "#DDD"
        },
        "value": {
            "color": "#FF0",
            "borderWidth": 2,
            "borderColor": "#DDD"
        },
        "bubble": {
            "timestamp": 1657706407403,
            "type": "Update",
            "path": [
                "style",
                "color"
            ],
            "key": "color",
            "isNew": false,
            "hasChange": true,
            "originValue": "#000",
            "oldValue": "#F00",
            "value": "#FF0"
        }
    }
    bubble
    {
        "timestamp": 1657706407403,
        "type": "Update",
        "path": ["style", "color"],
        "key": "color",
        "isNew": false,
        "hasChange": true,
        "originValue": "#000",
        "oldValue": "#F00",
        "value": "#FF0"
    }
 **/

观察数组

addWatch(data, 'classList', (record) => {
	console.log(record);
});
// 对数组内元素进行修改
data.classList[0] = 'newBox';
/**
 * 输出
    {
        "timestamp": 1657707309892,
        "type": "Update",
        "path": ["classList", "0"],
        "key": "0",
        "isNew": false,
        "hasChange": true,
        "originValue": "box",
        "oldValue": "box",
        "value": "newBox"
    }
 **/

使用代理方式观察属性

/**
 * 使用代理方式添加 style.color 的观察
 *
 * 因为 data.addWatch 函数是 Watcher 的实例函数,
 * 而 data.style 是一个 ProxyObject (代理对象),无法通过 data.style.addWatch 添加观察
 * 所以需要使用代理方式为其添加观察
 * 
 * Watcher.prototype.addWatch 是根据值路径去解析的
 * 代理方式的好处是在代码执行时通常获取到代理对象时无法知道路径,
 * 使用代理方式可以忽略路径直接观察
 **/
addWatch(data.style, 'color', {
	handler(record) {
		console.log(record.value); // 输出 #F00
	},
	immediate: true,
});

保护&只读

// 保护 height 属性,保护后无法修改属性的值,
// 只能通过函数返回的 setter 函数进行设置
const setter = protect(data, 'height');
data.height = 200; // 输出: Assignment to protection variable 'height'

// 通过 setter 修改只读属性
setter((proxy) => {
	proxy.height = 200;
});
console.log(data.height); // 输出: 200

// 设置 style.borderWidth 为只读
readonly(data.style, 'borderWidth');
// 尝试修改
data.style.borderWidth = 10; 
// 输出: Assignment to readonly variable 'borderWidth'
</script>