What

因为一些不可描述的原因,dropbox能用但是速度感人

现在需要一款能够 在 android+Linux+iPadOS三端做同步的

iPad上的Documents配上ssh可以完成Linux+iPadOS的同步, // android似乎并不行

google drive就和dropbox是同样的问题

baiduYunPan ??? 如果你不怕你的个人资料有一天变成 “根据相关法律法规” 的话 // 不过值得一提的是 这玩意支持linux了哦 虽然目测是个 浏览器壳 当然也就没有自动备份同步功能

然后搜了搜开源的 ownCloud 和 nextCloud ,看介绍基本是一家的

还有人说 用svn/git来搞,你确定喜欢在手机和ipad上输命令?

开干

client就不说了,各个平台下下来安装就行,说说 server

我目前的电脑环境Ubuntu18.04 x86_64 Linux 4.15.0-65-generic , bash 4.4.20 , 15925MiB

警告第一条命令将会进入root,进入后你将有所有权限,请自重

sudo -s
apt install apache2
service apache2 start
apt install mariadb-server
mysql_secure_installation
apt install php libapache2-mod-php php-mysql php-gd php-json php-mysql php-curl php-mbstring php-intl php-imagick php-xml php-zip
apt install phpmyadmin
ln -s /etc/phpmyadmin/apache.conf /etc/apache2/conf-available/phpmyadmin.conf
a2enconf phpmyadmin
a2enmod ssl
a2ensite default-ssl
service apache2 reload
mariadb
MariaDB [(none)]> CREATE DATABASE nextcloud;
MariaDB [(none)]> CREATE USER nextcloud IDENTIFIED BY 'p@ssc0de';
MariaDB [(none)]> grant usage on *.* TO nextcloud@localhost IDENTIFIED BY 'p@ssc0de';
MariaDB [(none)]> GRANT ALL privileges ON nextcloud.* TO nextcloud@localhost;
MariaDB [(none)]> FLUSH PRIVILEGES;

访问 https://localhost/phpmyadmin/

(用户名,密码)=(nextcloud,p@ssc0de)

https://download.nextcloud.com/server/releases/nextcloud-17.0.0.zip下载最新的zip包 解压到 /var/www/html/nextcloud

chown -R www-data:www-data nextcloud/

通过https://localhost/nextcloud即可访问 设置admin 密码,填写刚刚的配置即可

手机连接会无权限,跟着提示修改/var/www/html/nextcloud/config/config.php的配置即可

失败的尝试

下载wget https://raw.githubusercontent.com/nextcloud/vm/master/nextcloud_install_production.sh

然后sudo bash nextcloud_install_production.sh

https://github.com/nextcloud/server 下源码或者下releases

nextCloud 额外的应用

把搜到的release包解压到/var/www/html/nextcloud/apps

index.php/settings/apps中启用

例如

1
2
3
4
5
calendar.tar.gz
checksum.tar.gz
files_readmemd.tar.gz
richdocuments.tar.gz
spreed-7.0.2.tar.gz

参考

https://docs.nextcloud.com/server/17/admin_manual/installation/source_installation.html#example-installation-on-ubuntu-18-04-lts-server

https://github.com/nextcloud/server.git

https://nextcloud.com/install/#instructions-server

https://askubuntu.com/questions/387062/how-to-solve-the-phpmyadmin-not-found-issue-after-upgrading-php-and-apache

https://www.youtube.com/watch?v=QXfsi0pwgYw

总结

现在看来主要问题在于 直接snap的 会和当前本地开得一堆服务或多或少冲突?? 我查运行状态 都是在运行,查端口 没看到监听

另外一个就是 文件夹权限要搞成 www-data:www-data

在一个就是mysql的db和账户创建,只玩过CURD,很久没摸也忘了

客户端口没啥难度

emmmmmmmm 似乎也不能在ipados上搞… 只能网页勉强

Docker

  1. 本地建立文件夹 /data/nextcloudserver/ 给它存东西用
  2. 一条docker命令docker run -d -p 8080:80 -v /data/nextcloudserver/:/var/www/html --name=nextcloudserver nextcloud ,注意docker的习惯冒号左边是宿主机器的东西,右边是容器内的东西,这里映射了端口和磁盘

这样就可以跑了,本地访问都是ui操作,没啥好说的

  1. 配置config/config.php中的trusted_domains (无脑的话root进,或者docker起个busybox连接一下),注意这里的写法是星号来匹配应该192.168.*.*,而不是192.168.0.0/24, 保存就行

或者官方的仓库下

https://github.com/nextcloud/docker/blob/master 文件夹.examples/docker-compose/insecure/mariadb/apache

执行docker-compose up -d

官方仓库自签https 证书

内网要用的话,可以考虑。毕竟没有CA中心,管内网的还是有办法搞你XD

.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm

sed玩得熟的可以sed替换哈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
diff --git a/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/db.env b/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/db.env
index a436605..e9872a4 100644
--- a/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/db.env
+++ b/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/db.env
@@ -1,3 +1,3 @@
-MYSQL_PASSWORD=
+MYSQL_PASSWORD=123
MYSQL_DATABASE=nextcloud
MYSQL_USER=nextcloud
diff --git a/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/docker-compose.yml b/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/docker-compose.yml
index 3d60f7e..0d9fa81 100644
--- a/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/docker-compose.yml
+++ b/.examples/docker-compose/with-nginx-proxy-self-signed-ssl/mariadb/fpm/docker-compose.yml
@@ -8,7 +8,7 @@ services:
volumes:
- db:/var/lib/mysql
environment:
- - MYSQL_ROOT_PASSWORD=
+ - MYSQL_ROOT_PASSWORD=123
env_file:
- db.env

@@ -30,7 +30,7 @@ services:
volumes:
- nextcloud:/var/www/html:ro
environment:
- - VIRTUAL_HOST=
+ - VIRTUAL_HOST=nextcloud.cromarmot.com
depends_on:
- app
networks:
@@ -59,11 +59,11 @@ services:
volumes:
- certs:/certs
environment:
- - SSL_SUBJECT=servhostname.local
- - CA_SUBJECT=my@example.com
- - SSL_KEY=/certs/servhostname.local.key
- - SSL_CSR=/certs/servhostname.local.csr
- - SSL_CERT=/certs/servhostname.local.crt
+ - SSL_SUBJECT=nextcloud.cromarmot.com
+ - CA_SUBJECT=cromarmot@example.com
+ - SSL_KEY=/certs/nextcloud.cromarmot.com.key
+ - SSL_CSR=/certs/nextcloud.cromarmot.com.csr
+ - SSL_CERT=/certs/nextcloud.cromarmot.com.crt
networks:
- proxy-tier

然后你需要在要连接的电脑上把hosts文件增加 ip到nextcloud.cromarmot.com 的映射

执行docker-compose up -d

移除

执行docker-compose down

注意,只会移除container和network,不会移除volume, 如果要移除,要手动移除

What

基本讲就是 throttle(截流)和debounce(去抖)

功能都是为了避免快速反复触发

比如 用户快速点击按钮

假设设置为1s

throttle相当于,执行的最小间隔为1s

debounce也是执行最小间隔为1s,但是假设用户持续的一直快速点击,函数不会执行,需要等到最后一次点击后过1s才执行

直接看lodash的

throttle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function throttle(func, wait, options) {
let leading = true
let trailing = true

if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
if (isObject(options)) {
leading = 'leading' in options ? !!options.leading : leading
trailing = 'trailing' in options ? !!options.trailing : trailing
}
return debounce(func, wait, {
leading,
trailing,
'maxWait': wait
})
}

export default throttle

debounce:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
function debounce(func, wait, options) {
let lastArgs,
lastThis,
maxWait,
result,
timerId,
lastCallTime

let lastInvokeTime = 0
let leading = false
let maxing = false
let trailing = true

// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function')

if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
wait = +wait || 0
if (isObject(options)) {
leading = !!options.leading
maxing = 'maxWait' in options
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
trailing = 'trailing' in options ? !!options.trailing : trailing
}

function invokeFunc(time) {
const args = lastArgs
const thisArg = lastThis

lastArgs = lastThis = undefined
lastInvokeTime = time
result = func.apply(thisArg, args)
return result
}

function startTimer(pendingFunc, wait) {
if (useRAF) {
root.cancelAnimationFrame(timerId)
return root.requestAnimationFrame(pendingFunc)
}
return setTimeout(pendingFunc, wait)
}

function cancelTimer(id) {
if (useRAF) {
return root.cancelAnimationFrame(id)
}
clearTimeout(id)
}

function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time
// Start the timer for the trailing edge.
timerId = startTimer(timerExpired, wait)
// Invoke the leading edge.
return leading ? invokeFunc(time) : result
}

function remainingWait(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
const timeWaiting = wait - timeSinceLastCall

return maxing
? Math.min(timeWaiting, maxWait - timeSinceLastInvoke)
: timeWaiting
}

function shouldInvoke(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime

// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
(timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait))
}

function timerExpired() {
const time = Date.now()
if (shouldInvoke(time)) {
return trailingEdge(time)
}
// Restart the timer.
timerId = startTimer(timerExpired, remainingWait(time))
}

function trailingEdge(time) {
timerId = undefined

// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time)
}
lastArgs = lastThis = undefined
return result
}

function cancel() {
if (timerId !== undefined) {
cancelTimer(timerId)
}
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
}

function flush() {
return timerId === undefined ? result : trailingEdge(Date.now())
}

function pending() {
return timerId !== undefined
}

function debounced(...args) {
const time = Date.now()
const isInvoking = shouldInvoke(time)

lastArgs = args
lastThis = this
lastCallTime = time

if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime)
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = startTimer(timerExpired, wait)
}
return result
}
debounced.cancel = cancel
debounced.flush = flush
debounced.pending = pending
return debounced
}

export default debounce

直接的参数区别

1
2
3
4
5
6
7
8
9
10
throttle

let leading = true
let maxing = true && maxWait
let trailing = true

debounce
let leading = false
let maxing = false
let trailing = true

Vue 有一个官方教程中的使用方法如下

https://cn.vuejs.org/v2/guide/computed.html#%E4%BE%A6%E5%90%AC%E5%99%A8

之前我也写过一个 在methods中 计算出一个函数,但是那样的返回和计算不能使用箭头函数,因为运行函数和执行的this不同,ide也报错,XD 虽然可用

参考

https://github.com/lodash/lodash/blob/master/throttle.js

https://github.com/lodash/lodash/blob/master/debounce.js

What

想看一下 某个包的文件

查看 usb连接/模拟器 的设备 adb devices

连接一个具体的设备 adb -s <设备id> shell

列出手机里的安装包pm list packages 当然 grep命令也可以用

比如你要查看的包为com.xxx.yyy那么 run-as com.xxx.yyy

cd ls grep cat这些命令是有的 并没有vi XD,不过有cat 基本能看文件了

// 搜到了 单个文件 cat 出来的 但没搜到文件夹的

// 也有间接先cat到sdcard再pull 再删sdcard

// 还搜到了一个 用 adb backup 把整个app搞出来,再 转换成tar的 [见下面链接]

// 当然 还有一堆说root的 emmm 告辞

// Stetho?

参考

https://stackoverflow.com/questions/53634246/android-get-all-installed-packages-using-adb

https://gist.github.com/davidnunez/1404789

https://stackoverflow.com/questions/1043322/why-do-i-get-access-denied-to-data-folder-when-using-adb?rq=1

https://stackoverflow.com/questions/7399028/android-adb-permission-denied

https://stackoverflow.com/questions/15558353/how-can-one-pull-the-private-data-of-ones-own-android-app

https://stackoverflow.com/questions/9017073/is-it-possible-to-see-application-data-from-adb-shell-the-same-way-i-see-it-moun/16461386

https://stackoverflow.com/questions/15558353/how-can-one-pull-the-private-data-of-ones-own-android-app/31504263#31504263

What

闪耀暖暖的设计师 材料耗材计算器

基于一个已经有的原生js 进行改造

原来生 http://www.spongem.com/ajglz/tools/evalLevelUpCost.html

重构内容

  1. 首先把这个的源码 搞下来,比较无脑的操作
  2. 然后开个module 配一下routing
  3. 搞个component 然后把函数数据都搬进来
  4. 把相关全局变量 改为 this.
  5. 修改原来的一些$方法 改为原生js,或者改为[(ngModel)]双向绑定,至此基本可以运行。
  6. 解构一下用户输入和计算输出分别叫做Class: CardBasic/Card -> html:Card/CardOut // 好吧 这里变量名有点乱 现在看来
  7. 引入https://material.angular.io的组件,稍微调整画风
  8. 尝试搞一个computed,移除计算按钮, 改为 get XXX的方法

这里遇到问题, 根据调试,页面上的调用会反复调用get方法, 大量重复不必要计算

  1. 解决方案1: 引入Ramda,建立两个cache 分别是计算结果 和 用户输入,用 Ramda的深比较和深拷贝完成少的计算次数

写着还好,行数不多,就能实现,性能也好,但是问题是,相当于你依然要去关心的是,输出值输入值直接的具体关系才能写。

  1. 解决方案2: 引入rxjs,用Observable搞

这里实践上有些问题

  • 双向绑定如何和BehaviorSubject绑定,最后用(ngModelChange)来 单向到 具体字段
  • 能否监听整个Class
1
2
3
4
5
import { of } from 'rxjs';
let a = {x:1,y:2};
let a$ = of(a);
a$.subscribe(console.log);
a.x=3;

例如这样 并不会因为修改了a的子字段而自动发送next

搞了半天最后还是 感觉很 手工写依赖的输入

这一块的实现主要如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 相当于所有用户输入的部分 都会对应一个emitter
private textEmitter: Map<string, BehaviorSubject<string>> = new Map();
// 在初始化时 初始化它们并监听
// initial textEmitter
for (const key in this.card) {
this.textEmitter.set(key, new BehaviorSubject(this.card[key]));
}
this.obAllKeys(this.textEmitter)
.pipe(
map(() => {
// 细粒度触发 全体计算
console.log('fire calc')
return this.cardOut; // 就是最开始写的get方法 触发过程中只 告诉说 '哎!我变了!' 即可
})
).subscribe(this.cardOut$); // 页面通过这个读取 是个 BehaviorSubject

// 做的个功能 监听一个类 的所有字段 对应的 textEmitter的Observable
// 做了一点防抖
obAllKeys(emitter: Map<string, BehaviorSubject<string>>): Observable<string> {
let mergeOb: Observable<string> = new BehaviorSubject('');
emitter.forEach((emitValue, emitKey) => {
mergeOb = merge(mergeOb, // 关于merge的使用考虑 最开始想过使用combineLatest 但是 问题是 我明明可以直接通过 页面双向绑定拿到 用户输入,所以其实只需要一个变更通知即可,再另一个 combineLatest感觉要配上flat 才能 自动 绑定所有key,不然是手写的所有key,而对应的下标再反向生成 对象,表示 是不是有更好的方法 没学到没搜到
emitValue.pipe(
debounceTime(this.debounceInterval),
distinctUntilChanged(),
map((v) => emitKey + v) // 这个是避免 在段时间内 上次状态a, 改b 改a 改回最初的a 这样就不会触发改变
));
});
return mergeOb.pipe(debounceTime(this.debounceInterval)); // 按理说一次只会改动一个 这个是初次进入时 会有多个 当然 combineLatest 不会有这个问题
}

// 主要是和页面上的 输入绑定 具体就是 ngModelChange ,这一块的问题就是 写了 [(ngModel)] 又写了一遍这个绑定 光是key就要写两遍 还是字符串没法保证正确 靠的是下面的 调试强行保证部分
onChange(cardKey: string, value: string) {
console.log(`onCardIdChange: ${cardKey} => ${value}`);
if (this.textEmitter.has(cardKey)) {
this.textEmitter.get(cardKey).next(value);
} else { // 调试用
console.warn('onChange', cardKey, value);
}
}
  1. 最后增加了一点 try catch,因为 不同的卡可选范围并不同,或者用户输入不合法会 爆红=.=只在 两个LevelVal里加了

最后结果

https://cromarmot.github.io/Debuq/#/nikki4/lvlup

如果打开看到404,保持页面,等待一会儿再刷新即可。因为有worker service

TODO

比如搞成动态拉取

计算函数再向纯函数靠拢啊

怎么json 转换 Class 甚至 Map // 搜了一会没搜到简洁的方案

更好的双向绑定+BehaviorSubject 绑定?

是否可能简化某部分代码

输入控制 比如 现在其实可以输入 小数 和 范围以外的数

比如因为还是有不少json 导致 typescript的 类型判断并不完整 虽然

依赖

英文原文

rxjs入门

摘要

如今,越来越多开发者开始学 RxJs, 并跟随最佳实践正确使用它。但是完全不必要,那些所谓的最佳实践,需要学一些新的内容,并且在你的项目中增加额外的代码。

更多的是,使用最佳实践,是冒着创建好的代码库和让你的队友高兴的风险! 🌈

Stop being a gray mass! 打破常规,停止使用最佳实践

下面我将想你介绍,怎么改造那些所谓的最佳实践代码.

  • 不要unsubscribe
  • 嵌套使用Subscribe
  • 不要使用 纯函数
  • 手动subscribe,不要使用 async pipe
  • 向你的服务暴露subjects
  • 始终对子组件传递流
  • 宝石图? 并不适合你
閱讀全文 »

Why

模拟生产环境 尝试 升级 gitlab-ce 从 11.6 到 12.2, 以及尝试模拟之前产生的错误

官方说 先要到11.11.X再到12.X

物料准备

vmware on Ubuntu 18.04 LTS

gitlab-ce-11.6.5-ce.0.el6.x86_64.rpm

gitlab-ce-11.11.0-ce.0.el6.x86_64.rpm

gitlab-ce-12.2.5-ce.0.el7.x86_64.rpm

rhel-server-7.7-x86_64-dvd.iso

Install OS

https://access.redhat.com/downloads/content/69/ver=/rhel---7/7.7/x86_64/product-software

注册 下载 安装

需要注意的是 下4GB左右的 而不是boot.iso

为什么是7 不是8(当前最新)

因为想和某个生产环境保持一致 尝试 模拟某些操作

閱讀全文 »

How

参考

https://www.cnblogs.com/amiezhang/p/11337095.html

上面链接都有讲

按照文件大小排序前100:

git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -100 | awk '{print$1}')"

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch 文件名' --prune-empty --tag-name-filter cat -- --all

git filter-branch --index-filter 让每个提交的文件都复制到索引(.git/index)中

然后运行过滤器命令:git rm --cached --ignore-unmatch 文件名 ,让每个提交都删除掉“文件名”文件

然后--prune-empty把空的提交“修剪”掉

然后--tag-name-filter cat把每个tag保持原名字,指向修改后的对应提交

最后-- --all将所有ref(包括branch、tag)都执行上面的重写

git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin

git reflog expire --expire=now --all

git gc --prune=now

注意

  1. 上面的文件名可以写通配符号

  2. 众所周知只要我加到git中过的,即使把分支删了,也可以从reflog中找到,但这里 有 清除reflog所以 想要这样做的,要么 单独开文件夹搞,要么 确定 reflog都不会再用

  3. 什么时候使用? 使用频率不应高,因为 这种操作无疑是会对 分支重写,也就完全不同的commit hash,所以最后push都会带上–force, 目前能想到场景,比如 不必要的大文件,如不小心提交的下下来的打包后文件,大zip.也有场景 比如 很多分支前 不小心提交了一个涉密的文件。 当然push force以后 所有用仓库的人要想继续用只有去新的分支里搞了,已经被别人下的还会在别人硬盘里

19-08-22 -> 19-09-15

ISBN:9787508672069

Main

回顾历史,发现很多现在有的,都是由各种其它混杂原因的组合

历史解决了三大 饥荒 瘟疫 战争

将可能考虑新的问题 永生 快乐

历史的解决问题的贡献,很多也用于人类升级

閱讀全文 »

依赖版本

element-ui 版本 10592d12ea981912165542920160669fd8874bd9

文档 https://element.eleme.io/#/zh-CN/component/message-box

要解决的问题

像是

1
2
3
4
$msgbox(options)
$alert(message, title, options) 或 $alert(message, options)
$confirm(message, title, options) 或 $confirm(message, options)
$prompt(message, title, options) 或 $prompt(message, options)

这样的api是怎么实现的

代码阅读

大概看一眼怎么搞的

src/index.js 看 这些方法 ,都是来自 import MessageBox from '../packages/message-box/index.js';

1
2
3
4
Vue.prototype.$msgbox = MessageBox;
Vue.prototype.$alert = MessageBox.alert;
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$prompt = MessageBox.prompt;

那么看 packages/message-box/src文件夹

两个文件 main.jsmain.vue

main.vue瞄一眼,都是我们常见的写法

main.js,看这些方法 最后 都是 调用 MessageBox(配置参数)的形式调用

再看const MessageBox = function(options, callback) {的实现

都是 先msgQueue.push(配置参数) + showNextMsg()

msgQueueshowNextMsg,分别是个数组,和从数组中shift() 取出值 进行具体展示执行

然后 看 showNextMsg()

1
2
3
if (!instance) {
initInstance();
}

然后

1
2
3
4
5
6
7
const initInstance = () => {
instance = new MessageBoxConstructor({
el: document.createElement('div')
});

instance.callback = defaultCallback;
};

1
const MessageBoxConstructor = Vue.extend(msgboxVue);

说明了基本这就是 工厂模式+单例模式+Vue.extend来创建弹框单例,所以我们页面上不需要写什么,就能直接调用

然后这些confirm调用过程 也就是对这个单例的参数改动 比如控制样式 数据 显示之类的

总结

  1. 用户调用 $msgbox
  2. 调用到packages/message-box/src/main.jsMessageBox(options,callback)
  3. MessageBox(options,callback)通过 数组 + 数组.shift,依次提取数据展示
  4. 对于展示的实例 是 通过 Vue.extend(packages/message-box/src/main.vue) + 工厂单例来产生的一个实例
  5. 他们 搞了一个element/examples/components/demo-block.vue 可以在markdown写示例代码,并且在网页上查看 可以运行 66666

那常见的问题可能有说,连续调用2此带有回调函数的$msgbox会怎样

我们可以看showNextMsg()实现

1
2
3
4
5
6
7
8
9
10
11
12
13
//得到 instance
if (!instance.visible || instance.closeTimer) {
//...
let oldCb = instance.callback;
instance.callback = (action, instance) => {
oldCb(action, instance);
showNextMsg();
};
//...
Vue.nextTick(() => {
instance.visible = true;
});
//...

意思就是如果我们同步调用两个,

  • 因为我们visible设置为true是 异步里发生的,那么 这两个调用时 visible默认都是false,所以 只会最后一个生效
  • 如果我们异步调用两次msgbox,那么 后一个调用 会在!visible的地方为false,不会直接触发,但是因为设计了msgQueue,因此 会放在数组中,然后看到 上面的callback,会在调用oldCb以后 再调用showNextMsg,所以会在对话框回调以后 再回调 那个时刻,msgQueue里首个 并从数组中移除

总结

总流程实现

也就是方法是src/index.js: Vue.prototype.方法名 = 方法函数 来让全局可用

通过 document.createElement('div') 和 全局变量, 创建绑定维护对应的单例实例.

这样就实现了 任何页面,不需要写组件,就能this.方法名(参数) 来弹窗

适用场景,不需要业务定UI,否则需要页面v-slot之类插入业务内容

参数设计(核心参数与可选参数与回调)

核心参数是函数的直接参数

可选参数组合成一个对象,提供默认值

回调既有成功也有失败,随是promise,总觉得用reject来做取消和关闭其实不太好, 可以全resolve走自定义状态

提供一定的重载实现

内部具体

队列, 同步异步, callback持有

0%