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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mm-works/p000005

v1.0.202004272011

Published

hack

Downloads

54

Readme

示例

https://mm-works.github.io/

1. vscode扩展

以下操作说明均以安装mmstudio扩展为前提.

2. 项目调试

2.1. 启动调试

启动项目调试的命令为npm t,简单的,可以通过alt+d快速打开终端并启动调试

2.2. 停止调试

找到启动命令的终端,按下ctrl+c停止命令,注意windows上经常会出现无法停止的情况,这种情况下,杀掉该终端即可。

2.3. 重启调试

停止调试,再次启动调试即可

需要重启调试的情况:

  1. 第一次添加某个公共的客户端原子操作后
  2. 第一次添加某个公共控件后
  3. 添加自定义路由之后
  4. 添加前置路由过滤器之后

3. 页面初始化

3.1. 服务端渲染和浏览器端渲染的差别

比如我们要访问一个页面,这个页面初始化需要呈现出来:"render-demo",它的html片段见pages/render.html

如果是以上的html片段保存成一个文件render.html,直接在浏览器打开就能正确呈现出来。但是它是完全静态的,我们试着把它进行动态化

我们需要先简单说明一下这个页面呈现的的详细过程

  1. 浏览器打开这个文件,读取文件内容,即以上代码,实际上它是一段文本
  2. 浏览器尝试解析这段文本,识别里面的各个标签,像html,body,h1,h2,分析它们的结构。
  3. 浏览器将这些标签用正确的方式进行处理,其中会把h1h2用不同的字号以及合适的字体通过像素显示到显示设备上。
  4. 如果标签中有<script>标签,也会将其里面的脚本使用js引擎(chrome中为v8)执行。
  5. 在脚本执行过程中,如果脚本有对dom进行操作,页面会重新渲染

基于以上步骤,我们有两种不同的处理方式

  1. 如果我们在1和2之间将整个页面的文本处理好,浏览器在3就能正确解析并显示出来。
  2. 如果我们在4进行处理,同样在5时也能正确呈现出我们想要的效果

以上的两种不同的处理方式,第一种就是“服务端渲染”的思路,第二种就是“浏览器端”处理的逻辑

3.1.1. 在实际项目中应该选用哪种方式

  1. 服务端渲染任务在客户端进行,不占用服务器cpu资源
  2. 服务端渲染可以使用服务器缓存,大并发站点节省服务器cpu资源
  3. 综上,使用哪种方式需要综合评定,一般业务型站点,如无特殊需求,建议选择开发效率高,成本低的一种,无须理论拘泥。

3.2. 浏览器端渲染

相对来讲,这种方式较容易被开发人员理解和接受,其过程就是取数,然后渲染页面,如果页面有条件,则使用该条件查询和排序。以下为实战的操作顺序

  1. 新建页面(alt+p)pg001

  2. 因页面较为简单,作为示例,我们将其内容划分为一个组件

  3. 添加一个响应a001

  4. 将其设置为初始化事件,在s.ts中添加初始化事件:

    'mm-events-init': 'a001'
  5. 将tpl中的部分内容生成渲染块,具体操作为:选中<div><h1>mm</h1></h2>studio</h2></div>,按下快捷键(alt+x)即可自动生成p01.tpl。注意渲染原子操作使用的是一个非常优秀的dot模板引擎

  6. 在响应a001.ts中引用渲染原子操作。alt+t a,选择页面操作类回车,再次选择渲染原子操作即可插入代码模板,填入相应参数值就可以,或者在alt+t a直接输入原子操作编号即可快速定位至原子操作,回车插入代码

  7. 渲染的原子操作第二个参数为需要渲染的数据,通常这里会调用一个自定义的服务。我们来快速创建一个服务alt+s

  8. 为了演示方便,这里我直接返回一个数组,但也模拟了排序条件改变,完整的服务代码见pg001/zj-001/s001.ts

  9. 我们在组件的初始化响应中调用该服务

    // 调用nodejs服务
    const r1583660193 = await (() => {
    	const service_name = 'pg001/zj-001/s001';	// 服务名称
    	const msg = { sort: r1583724505 };	// 参数
    	return aw4<string[]>(service_name, msg);
    })();
  10. 渲染的第三个参数我们通过另外一个原子操作获得

    const r1583724505 = (() => {
    	const key = 'sort';	// url参数名
    	const default_value = 'asc';	// 默认值,如果不希望使用默认值,可以删除该参数或者传入空值
    	return aw3(mm, key, default_value);
    })();
  11. 将以上步骤中服务返回的结果作为渲染的第二个参数,p01作为渲染的第三个参数,填入渲染的实参。注意渲染原子操作使用的是一个非常优秀的dot模板引擎

    // html渲染
    (() => {
    	const data = r1583660193;	// 要渲染的数据
    	const position = 'inner';	// inner 替换全部子结点 after 当前结点之后 before 当前结点之前 firstin 第一个子结点之前 lastin 最后一个子结点之后
    	return aw5(mm, data, p01, 'p01', position);
    })();
  12. 经过以上步骤,我们就完成了浏览器端的渲染,再复杂一些,可以使用原子操作把渲染的条件放到url参数中,渲染时取出。逻辑比较简单这里不作解释。

3.3. 服务端渲染

服务端的渲染整个思路较长,并且参数修改往往还会牵涉到一个页面生命周期迭代的问题。上面浏览器端的例子最后提到的条件放在url参数中即为这种方式的一个引子,如果各位能够理解,那么服务端渲染就好理解得多。

必须强调,虽然在调试时为了调试方便,服务端的部分代码是在浏览器中运行。但是实际运行环境中,一定一定不要在服务端的代码中使用任何浏览器特性的东西,比如alert,比如window,webStorage关键字等等,原子操作已经做过过滤,使用vscode扩展自动添加的代码不会存在这种问题,不能使用的原子操作是无法选择的。但如果存在大量开发人员手动添加的代码的情况,一定一定要保证这一点,或者出现某个页面本地调试正常,部署后无法正常加载的问题,也可以从这个思路进行排查问题。

步骤:

  1. 同浏览器端渲染1,2步。
  2. 添加服务端初始化响应,具体操作为:在组件的n.ts文件中按下快捷键alt+a创建响应,注意该操作一个组件只能操作一次,多次操作生成的多个响应并非系统问题,多余的响应也不会执行,请注意。设计如此,非bug。
  3. 创建tpl,步骤同浏览器端原子操作
  4. 添加一个服务s001.ts,方法和内容参见浏览器端原子操作
  5. 在响应na001.ts中添加服务调用并渲染,内容见pg002/zj-001/na001.ts

这个时候启动调试(alt+m d)页面应该就可以看到效果了(如果之前开启过调试,也许需要先手动关闭,具体操作为在终端界面按下ctrl+d)

4. 原子操作

在响应,服务,项目级原子操作中,均可引用原子操作。

4.1. 分类

原子操作分为通用型原子操作和项目级原子操作,通用型原子操作可以在任何项目中使用,其实现要求比较高。项目级原子操作则只要该项目可用即可。

4.2. 创建方法

通过vscode命令mmstudio: Add new atom创建。

4.3. 通用型原子操作

注意事项:

  1. 最好先确定好原子操作的名称,因为它将展示给所有开发人员,尽可能将其描述精确、简练。
  2. 希望加入团队的人员请联系我.我将非常乐意接受社区的贡献。
  3. 原子操作要有单元测试,否则有可能将无法通过审核合并。

4.4. 项目级原子操作

可以在项目中实现某个通用操作,其代码实现制作为一个原子操作index.ts(目前限定其为某一个函数),并将其插入代码模板写入use.snippet。也可以将项目中常用的几个原子操作的使用编写为一个原子操作,在项目中快速引用。

4.5. snippet的写法

  1. 普通的文本按原样插入到使用位置
  2. $为一个特殊字符,如果要在代码模板中输入一个字符$,必须加上转义符\,即\$方可。
  3. 如果$它后跟一个数字,如$1,$2,则表示一个停止符,在插入原子操作时通过tab键可以按$后的数字切换光标位置。
  4. $后如果后跟大括号,如${1},${2},其作用同$1,$2
  5. $后大括号中数字后如跟:,如${1:val},则:后为默认插入内容,当光标停留时默认内容将被选中,且可修改
  6. $后大括号中数字后如跟|,需要保证大括号结束前也要有一个|,如${1:|a,b,c|},则|之间的内容当光标停留时将按,分隔,作为枚举列表供选择,注意应当按实际使用场景调整顺序。
  7. $CURRENT_SECONDS_UNIX为一个数字值,通常它是唯一的,可以用它作为变量名

5. 控件

在tpl中可引用控件,在项目中可以调用控件。

5.1. 创建方法

通过vscode命令mmstudio: Add new widgets创建。

5.2. 通用型控件

注意事项同通用型原子操作

5.3. 项目级控件

可以在项目中实现某个通用控件,控件用到一些封装方法和web组件及shadowdom的技术,不过其基础代码已添加,开发人员按照示例的方法添加自己的实现代码就好。并需其插入代码模板写入use.snippet。也可以将项目中固定搭配的控件合并为一个,在项目中快速引用。

注意:

引用控件时请先选定要插入控件的位置,这样插入的代码模板才不会乱。

5.4. 控件方法的调用

因为typescript类型的关系,如果要使用控件mm-000001,需在客户端响应中手动引入控件类型

import Widget1 from '@mmstudio/ww000001';
// 引入项目内控件
import Widget2 from '../../widgets/pw001';

const w1 = document.querySelector<Widget1>('#widgetid1')!;
w1.method01('foo', 'bar');

const w2 = document.querySelector<Widget2>('#widgetid2')!;
w2.method01('foo', 'bar');

6. 文件转换

6.1. 转pdf

使用服务端渲染的方法制作页面即可,然后将页面后缀(html)修改为pdf即可查看效果(如果是开发阶段,需要修改页面的端口为8889)

6.2. 转word文档

使用服务端渲染的方法制作页面,然后,然后将页面后缀(html)修改为xlsx即可查看效果(如果是开发阶段,需要修改页面的端口为8889)

6.3. 转xlsx表格

使用服务端渲染的方法制作页面,注意必须将页面使用table渲染,然后,然后将页面后缀(html)修改为xlsx即可查看效果(如果是开发阶段,需要修改页面的端口为8889),这是一种相对简单的做法,无法满足复杂表格(如带有公式等高级功能的表格)

7. 文件服务

通常使用控件和原子操作来完成。以下为简单介绍。

7.1. 配置

需要配置一个文件数据库

mm.json

{
	"minio": {
		"endPoint": "127.0.0.1",
		"port": 9000,
		"accessKey": "mmstudio",
		"secretKey": "Mmstudio111111",
		"useSSL": false,
		"region": "cn-north-1",
		"partSize": 5242880
	}
}

同时需要启动一个文件数据库minio

docker-compose安置

[sudo] docker-compose -f db.yaml up
version: '3.7'

services:
  minio:
    image: minio/minio
    container_name: minio
    command: server /data
    volumes:
      - /home/taoqf/data/minio:/data
    ports:
      - 9000:9000
    environment:
      MINIO_ACCESS_KEY: mmstudio
      MINIO_SECRET_KEY: Mmstudio123

7.2. 上传

当前项目页面地址/fsweb/upload,支持一次上传多个文件

7.3. 下载

当前项目页面地址/fsweb/getfile?id=xxx,如果是图片,不添加download参数直接展示在页面展示,如果要下载,请添加 download 参数,download参数支持以下几种形态.

  1. /fsweb/getfile?id=xxx&downlaod
  2. /fsweb/getfile?id=xxx&downlaod=false
  3. /fsweb/getfile?id=xxx&downlaod=abc.jpe

如果一次性下载多个文件,请使用 /fsweb/getfile?id=xxx,yyy,zzz

7.4. 上传office文档,转换为ppt和图片

当前项目页面地址/fsweb/upload-office,支持选择性转换为图片。

7.5. 重新上传

当前项目页面地址/fsweb/reupload?id=xxx

7.6. 删除

当前项目页面地址/fsweb/delfile?id=xxx

8. 获取ip

在任意一个服务(s000.ts)中,可以通过msg.realip获得客户端ip。注意,如果使用了反向代理,请设置请求头x-real-ipx-forwarded-for,如果反向代理设置了其它的ip(比如真实情况的反向代理为第三方),一般也可以通过msg.headers.xxx获取。

9. 自定义路由

在某些情况下,比如支付的回调等,我们需要第三方服务回调提供一些路由。

9.1. 添加通用服务

首先必须要先添加一个服务,由该服务响应该路由的请求,但也有可能是可能是某个组件下已完成的服务,这里以新建通用服务为例说明新建通用服务的方法。

因为我们希望把通用的服务单独列出目录来区分,比如希望通用的服务都放在src/services目录下以方便管理。

因为新建服务的逻辑是需要打开一个可编辑文件,这样新建的服务会位于该可编辑文件相同的目录下。所以我们新建第一个服务时会用到一些技巧。

  1. 我们先在vscode中新建一个文件src/services/mm

    touch src/services/mm
  2. 点击打开这个文件.

  3. alt+s添加服务

  4. 删除第一次添加的文件

    rm src/services/mm
  5. 编写服务逻辑

9.2. 添加路由

  1. alt+r添加路由
  2. 选中服务services/s001
  3. 使用get请求访问该路由(也有可能是post,put,delete,all,根据第三方服务情况而定)
  4. 重启项目调试
  5. curl http://localhost:8889/r001即可触发调用服务

9.3. 添加附加数据

有时候,一个服务的逻辑几乎能被多个路由调用,又有一些细微的差别,这个时候,为了项目维护方便,通常不建议复制并修改原服务。我们有两种方法来实现:

  1. 添加项目级服务端原子操作,将服务的逻辑封装起来,暴躁出某个参数,在不同服务中调用该原子操作。
  2. 在路由定义中附加上该参数,将多个路由同时关联同一个服务,在服务中通过msg.foo获取到参数进行逻辑判定处理。

路由定义中附加参数的方法

在mm.json中找到routers下的多个路由,分别为它们附加参数(如foo)

{
	"routers": [
		{
			"method": "get",
			"service": "searvices/s001",
			"url": "/r001",
			"data: {
				"foo": "bar1"
			}
		},
		{
			"method": "get",
			"service": "searvices/s001",
			"url": "/r002",
			"data: {
				"foo": "bar2"
			}
		}
	]
}

10. 前置路由过滤器

前置路由过滤器将提前将某个请求进行处理,其操作类似于添加路由。以下列出不同点:

  1. 前置路由过滤器对应的服务返回的结果中如果有data,该请求将提前返回,后续路由逻辑全部跳过,但可以设置cookie,和响应头header。

  2. 返回结果中可以有重定向redirect,如果有,请求也请提前返回,后续路由逻辑全部跳过,可同时设置cookie和响应头

  3. 前置路由示波器通常使用通配符作为url,以下几种示例,都是正确的写法

    {
    	"filters": [
    		{
    			"method": "get",
    			"service": "searvices/s001",
    			"url": "/*",
    			"data: {}
    		},
    		{
    			"method": "get",
    			"service": "searvices/s001",
    			"url": "/*.html",
    			"data: {}
    		},
    		{
    			"method": "get",
    			"service": "searvices/s001",
    			"url": "/mmstudio",
    			"data: {}
    		{
    			"method": "get",
    			"service": "searvices/s001",
    			"url": "/m?studio",
    			"data: {}
    		{
    			"method": "get",
    			"service": "searvices/s001",
    			"url": "/m+studio",
    			"data: {}
    		{
    			"method": "get",
    			"service": "searvices/s001",
    			"url": "/mm(studio)?",
    			"data: {}
    		}
    	]
    }

11. 负载均衡

通常的nginx的反向代理,负载均衡就可以满足绝大多数大型应用(体量小的应用不用担心负载问题)

12. 两个单例服务

有些服务不能启动多个实例,所以需要独立部署为单例。这类服务在负荷大(非常大)时,将会拖慢整个应用的速度,所以在业务设计时,需要非常注意。

原理上来讲,这类服务是不可能通过增加结点实现的,除非修改设计,比如不在必要的时候不生成唯一的编码,使用uuid替代。

12.1. 编码服务

详见调用编码服务原子操作

12.2. 定时任务

使用vscode扩展添加定时任务配置。

服务详情见定时任务服务

13. 第三方库的引用

使用第三方库的时候,由于第三方库的代码质量我们无法保证,所以可能会有以下问题,列出以待查阅分析:

13.1. 添加依赖

  1. 服务端原子操作如果引用第三方包,将务必将包依赖添加到package.json中的dependencies中。
  2. 客户端原子操作如果引用第三方包,将务必将包依赖添加到package.json中的devDependencies中。
  3. 控件如果引用第三方包,将务必将包依赖添加到package.json中的devDependencies中。

13.2. 全局引用

客户端原子操作或控件引用的第三包如果有全局引用,最常见的有jquery,这一类引用在使用时通常会有两种问题:

  1. 需要在页面中通过script全局引入js文件
  2. 打包时多个对jquery依赖的控件有可能会冲突,造成找不到jquery对象的问题
  3. ts定义问题,使用全局还是模块引用需要权衡,多数国内jquery依赖的包质量不高,非常乱。

这里只指出解决问题的线索,具体问题请自行解决。

  1. 根目录下的gulpfile.js,本地调试及打包相关
  2. n.ts,打包相关

13.3. amd

虽然一些脚手架工具已经可以比较方便进行页面调试了,但是依然,会存在各种各样的奇怪问题,项目开发人员技术水平不过硬时,出现问题很难自行解决。并且通常地,当项目比较大时,执行速度过慢,从而导致开发效率降低。所以我们在开发过程中使用的是amd的加载方式,这种技术相对成熟,各种工具和第三方包的支持也比较好,万一出现问题也相对容易解决。但这会产生一些问题,即有部分第三方包并不提供amd的版本,虽然在打包时理论上可以使用,但无法开发调试。这就需要项目使用人员去开源贡献或是自行解决(配置amd.json文件,同时项目下添加amd的版本以供开发时使用,将commonjs版本修改umd版本并不复杂,通常只需要添加头尾即可)。amd.json的格式参看amd加载器中的相关说明。

13.4. TypeScript定义

有些第三方类库可能会缺少ts定义,如果是比较流行的类库,试试到@types下找一找。示例(xxx为类库名称):

yarn add --dev @types/xxx

如果没有,可以到开源社区贡献。

13.5. 简单粗野的做法

如果不想贡献,还要使用第三方类库,在许可允许的范围内(哈哈),可以源码级引入,修改为ts,去掉全局依赖。当然,这种作法我个人并不推荐,但是当今国内开发的圈子里,这样做的不在少数,在项目工期比较紧且项目开发人员普遍素质不很高的情况下,这确实是项目开发成本比较低的一种方法,祝好运!