项目立项要做什么?

需要确定范围

  • B端和编辑器,H5,管理平台

ssr适合toC(追求极致的性能),不适合toB,所以架构需要考虑成本,用最简单的方案

  • 独立组件库
    编辑器和h5展示用到的组件是一样的,需要复用
    确定组件数据结构(使用vnode来表示元素,使用数组index标称图层)

  • 数据流转流程

技术方案文档

  • 随性写,就单纯思考要怎么做
  • 可以尝试写一部分代码,捋一捋思路
    • 如果想明白了,写代码花不了太多时间
    • 如果写不出来,说明思路还存在问题
  1. 需求
  2. 范围(整体设计,架构设计,不需要细节)
  3. 模块设计
    • 模块的拆分和关系图,结果
    • 模块的关键功能,职责等
  4. 特殊的模块重点说明
    • 组件库,独立第三方,同时用于编辑器和h5
    • 自研统计服务,为何自研
  5. 作品的数据结构
    1. vuex store的结构,解释
    2. 数据流转关系图
  6. 扩展性保证
    1. 扩展组件
    2. 数据结构层面
    3. 扩展编辑器的功能,组件移仓,锁定
    4. 扩展页面配置
    5. 扩展分享信息
  7. 开发提效
    1. 脚手架
    2. 组件平台
  8. 运维保障
    1. 线上服务和运维服务
    2. 安全
    3. 监控和报警
    4. 服务扩展性:流量大

zsh命令行工具安装教程

安装

1
$ sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

安装插件

命令检查高亮插件

1
2
3
4
5
6
7
8
9
10

$ cd ~/.oh-my-zsh/custom/plugins/

$ git clone https://github.com/zsh-users/zsh-syntax-highlighting.git

$ vim ~/.zshrc

# 然后在plugin添加 plugins=(git vscode zsh-syntax-highlighting autojump zsh-autosuggestions sudo last-working-dir web-search)
# 主题修改 ZSH_THEME="agnoster"
$ source ~/.zshrc

安装字体

1
2
3
4
5
6
7
8
9
10
11
$ pip3 install powerline-status

$ git clone https://github.com/powerline/fonts.git --depth=1

# 安装
$ cd fonts
$ ./install.sh

# 安装完删除下载的原始包
$ cd ..
$ rm -rf fonts

安装item2

选择 iTerm2->Preferences->Profiles->Text->勾选Use a different font for non-ASCII text->选择字体Meslo

vscode 中修改编辑器主题,终端字体及颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- settings.json -->

"workbench.colorTheme": "Quiet Light",
"terminal.integrated.fontFamily": "Meslo LG L for Powerline",
"workbench.colorCustomizations": {
// "terminal.background": "#dfdfdf",
// "terminal.foreground": "#dddad6",
"terminal.ansiBlack": "#fcfcfc",
"terminal.ansiBrightBlack": "#323232",
"terminal.ansiBrightBlue": "#0D6678",
"terminal.ansiBrightCyan": "#8BA59B",
"terminal.ansiBrightGreen": "#237e02",
"terminal.ansiBrightMagenta": "#8F4673",
"terminal.ansiBrightRed": "#d55a49",
"terminal.ansiBrightWhite": "#d2c78a",
"terminal.ansiBrightYellow": "#dfaf3e",
"terminal.ansiCyan": "#67ac92",
"terminal.ansiMagenta": "#8F4673",
"terminal.ansiRed": "#cb5849",
"terminal.ansiWhite": "#c2924f",
"terminal.ansiYellow": "#d2a53e",
"terminal.ansiGreen": "#48b122",
"terminal.ansiBlue": "#238cc4"
}

zsh导致的nvm不可用问题

修改 ~/.bash_profile 添加一下内容

1
2
3
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion

取消显示终端里面的设备名

找到zsh当前主题文件的路径,例如: /Users/attacki/.oh-my-zsh/themes/agnoster.zsh-theme

修改主题文件里面的字符模板,字符模板里的 @%m,就可以取消显示设备号

1
2
3
4
5
prompt_context() {
if [[ "$USERNAME" != "$DEFAULT_USER" || -n "$SSH_CLIENT" ]]; then
prompt_segment black default "%(!.%{%F{yellow}%}.)%n"
fi
}

vue2响应式原理

vue2响应式

vue响应式的根本原理就是发布订阅模式,分别需要观察者和订阅者。

1
2
3
4
5
6
7
8
9
var vm = new MVVM({
el:'#app',
data:{
message:{
greet: 'hello everyone!~',
tip: 'good job!~'
}
}
})

简易vue入口

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
class MVVM {
constructor(options) {
this.$el = options.el
this.$data = options.data
if (this.$el) {
// 数据劫持 把对象的所有属性都改为 get和set方法
new Observer(this.$data)
this.proxyData(this.$data)
// 用数据和元素进行编译
new Compiler(this.$el, this)
} else {
console.log('没能选中要挂载的dom元素(根节点)')
}
}

element(node) {
// 根据用户传入内容,获取dom元素
return node.nodeType == 1 ? node : document.querySelector(node)
}

proxyData(data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
get() {
return data[key]
},
set(newValue) {
data[key] = newValue
}
})
})
}
}

观察者

观察者的主要工作,就是监听虚拟dom节点数据变化,当数据变化执行对应的方法,其中还包含对新值和老值进行对比,发生变化,就通知订阅者。

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
class Observer {
constructor(data) {
this.observer(data)
}

observer(data) {
// 将data数据的属性改成set和get的形式
if (!data || typeof data !== 'object') {
return
}
// 要将数据一一劫持,获取key和value
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key]) // 劫持
this.observer(data[key]) //深度递归劫持
})
}

defineReactive(obj, key, value) {
let that = this;
let dep = new Dep() //每个变化的数据 都会对应一个数组,这个数组是存放所有更新的操作
Object.defineProperty(obj, key, {
enumerable: true, //数据可枚举,for循环可循环出这个值
configurable: true,
get() {
// 当取值时调用的方法
Dep.target && dep.addSub(Dep.target)
return value;
},
set(newValue) {
//当给data属性中设置值时 更改获取的属性的值
if (newValue != value) {
// 这里 this不是实例
that.observer(newValue) //如果是对象继续劫持
value = newValue
dep.notify() //通知所有人 数据更新
}
}
})
}
}

class Watcher {
constructor(vm, expr, cb) {
this.vm = vm
this.expr = expr
this.cb = cb
// 获取旧值
this.value = this.get()
}

getVal(vm, expr) {
// message.a.b.c.d
expr = expr.split('.')
return expr.reduce((prev, next) => {
return prev[next]
}, vm.$data)
}

get() {
Dep.target = this
let value = this.getVal(this.vm, this.expr)
Dep.target = null
return value
}

update() {
let newVal = this.getVal(this.vm, this.expr)
let oldVal = this.value
if (newVal != oldVal) {
this.cb(newVal) // 对应watch的回调函数
}
}
}

订阅者

订阅者是属于对象的,每个对象只有一个订阅者。
对象的订阅者会收集观察者(在解析dom中vue语法的时候,调用被监听的对象属性就会生成观察者,也就被当前对象的订阅者进行收集),在观察者发出通知的时候进行更新,但是这里有个问题就是更新对象的某一个属性,所有的观察者都会打补丁,当然这只是简易实现,没有性能优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Dep {
constructor() {
// 订阅的数组
this.subs = []
}

addSub(watcher) {
this.subs.push(watcher)
}

notify() {
this.subs.forEach(watcher => watcher.update())
}
}

补丁工具箱

主要提供针对vue语法糖的识别解析的一些工具函数

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
const CompileUtil = {
getVal(vm, expr) {
// message.a.b.c.d
let props = expr.split('.')
let value = props.reduce((prev, next) => {
return prev[next]
}, vm.$data)
return value
},

getTextVal(vm, expr) {
// {{message.greet}}{{message.tip}}
return expr.replace(/\{\{([^}]+)\}\}/g, (...args) => {
return this.getVal(vm, args[1])
})
// 返回的内容是拼接好的
// hello everyone!~good job!~
},

setVal(vm, expr, value) {
expr = expr.split('.')
// 收敛
return expr.reduce((prev, next, currentIndex) => {
if (currentIndex == expr.length - 1) {
return prev[next] = value;
}
return prev[next]
}, vm.$data)
},

text(node, vm, expr) { //处理文本
let updateFn = this.updater['textUpdater']
let value = this.getTextVal(vm, expr)
expr.replace(/\{\{([^}]+)\}\}/g, (...args) => {
new Watcher(vm, args[1], () => {
// 如果数据变化了,文本节点需要重新获取依赖的属性更新文本中的内容
updateFn && updateFn(node, this.getTextVal(vm, expr))
})
})
updateFn && updateFn(node, value)
},

model(node, vm, expr) {
// 将值赋给对应的元素
// 添加监控 数据变化 调用cb
let updateFn = this.updater['modelUpdater']
// 这里应该加一个监控 数据变化 应该调用这个watch的callback
new Watcher(vm, expr, (newVal) => {
// 当值变化后会调用cb 将新的值传递过来
updateFn && updateFn(node, this.getVal(vm, expr))
})
node.addEventListener('input', (e) => {
let newValue = e.target.value
this.setVal(vm, expr, newValue)
})
updateFn && updateFn(node, this.getVal(vm, expr))
},

updater: {
// 文本更新
textUpdater(node, value) {
node.textContent = value
},
// 输入框更新
modelUpdater(node, value) {
node.value = value
}
}
}

编译器

编译器的任务就是渲染了,拿着虚拟dom的补丁列表,渲染到页面当中。

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
class Compiler {
constructor(el, vm) {
this.el = this.isElementNode(el) ? el : document.querySelector(el)
this.vm = vm

if (this.el) {
// 如果可以获取到挂载元素,开始编译
// 1. 获取node节点,对节点属性进行遍历,转移到文档碎片
let fragment = this.node2Fragment(this.el)
// 2. 根据vm.$data对节点属性进行编译
this.compile(fragment)
// 3. 将编译完成的文档碎片重新插入根节点
this.el.appendChild(fragment)
}
}

isElementNode(node) {
return node.nodeType === 1;
}

node2Fragment(el) {
/* 创建文档碎片 */
let fragment = document.createDocumentFragment()
let firstChild
while (firstChild = el.firstChild) {
fragment.appendChild(firstChild)
}
return fragment //内存中的节点
}

isDirective(attr) {
/* 判断是否是指令 */
return attr.includes('v-')
}


compile(fragment) {
// 执行编译,需要递归
let childNodes = fragment.childNodes
Array.from(childNodes).forEach(node => {
if (this.isElementNode(node)) {
// 这里需要编译元素节点
this.compileElement(node)
// 是元素节点,还需要继续深入的检查
this.compile(node)
} else {
// 文本节点
var reg = /\{\{([^}]+)\}\}/g;
if (reg.test(node.textContent)) {
// 如果文本节点有内容就编译
this.compileText(node)
}
}
})
}

compileElement(node) {
let attrs = node.attributes
Array.from(attrs).forEach(attr => {
let attrName = attr.name
// 判断属性是否包含"v-"
if (this.isDirective(attrName)) {
// 取到对应的值放到节点中
let expr = attr.value
let [, type] = attrName.split('-')
// node this.vm.$data // v-model v-text v-html
CompileUtil[type](node, this.vm, expr)
}
})
}

compileText(node) {
// 带{{b.a.c}}
let expr = node.textContent //取文本中的内容
let reg = /\{\{([^}]+)\}\}/g //{{c}}{{b}}{{a}}
if (reg.test(expr)) {
// node this.vm.$data
CompileUtil['text'](node, this.vm, expr)
}
}
}

windows的软件

01、浏览器
RC Firefox

Firefox 官方网站:https://www.mozilla.org/zh-CN/firefox

Firefox 定制版:https://www.runningcheese.com/firefox

RC Chrome

Chrome 官方网站:https://www.google.com/chrome

Chrome 定制版:[https://www.runningcheese.com/chrome]

02、输入法
搜狗输入法

官方网站:https://pinyin.sogou.com

去广告版:https://www.423down.com/587.html

分流下载:见文章开头

皮肤推荐:

1080P 屏幕:https://pinyin.sogou.com/skins/detail/view/info/346153 (安然·简单白)

2K以上屏幕:https://pinyin.sogou.com/skins/detail/view/info/608863 (轻·感)

03、看图软件
IrfanView

官方网站:https://www.irfanview.com

奶酪配置:见文章开头

04、截图工具
FastStone

官方网站:https://www.faststone.org/FSCaptureDetail.htm

汉化版:[http://blog.sina.com.cn/s/blog_89a729a40102wjwk.html]

05、GIF 截图
Screen2Gif

官方网站:[https://www.screentogif.com]

06、视频录制
Bandicam:

官方网站:https://www.bandicam.com

补丁版本:https://www.423down.com/2119.html

Camtasia Studio:

官方网站:https://www.techsmith.com/video-editor.html

补丁版本:https://www.zdfans.com/html/26997.html

07、音乐播放器
网易云音乐

官方网站:https://music.163.com/#/download

Listen 1

官方网站:http://listen1.github.io/listen1

MPC-HC 播放器

官方网站:https://mpc-hc.org

08、视频播放器
PotPalyer:

官方网站:https://potplayer.daum.net/?lang=zh_CN

奶酪配置:见文章开头

09、下载工具
IDM

官方网站:http://www.internetdownloadmanager.com

补丁版本:https://www.423down.com/575.html

Annie

官方网站:https://github.com/iawia002/annie

奶酪配置:见文章开头

BT 下载:

迅雷X

官方网站:https://www.xunlei.com

去广告版:https://www.423down.com/9092.html

qBittorrent

官方网站:https://www.qbittorrent.org

分流下载:见文章开头

10、通讯工具
TIM

官方网站:https://office.qq.com/

UWP版QQ:[https://www.microsoft.com/zh-cn/p/qq/9wzdncrfj1ps]

11、办公软件
Microsoft Office

官方网站:https://www.microsoft.com/zh-cn/microsoft-365/microsoft-office

激活工具:KMS Toolshttps://kms-auto.site

分流下载:https://www.jb51.net/softs/630030.html

Office Tab

官方网站:https://www.extendoffice.com/product/office-tab.html

补丁版本:[https://www.jb51.net/softs/683089.html]

12、解压压缩
WinRAR

官方网站:http://www.winrar.com.cn

去广告版:https://www.423down.com/778.html

7zip

官方网站:https://www.7-zip.org

奶酪配置:见文章开头

13、远程协助
ToDesk

官方网站:https://www.todesk.com/index.html

14、安全软件
火绒安全

官方网站:[https://www.huorong.cn]

15、卸载软件
Geek Uninstaller

官方网站:https://geekuninstaller.com

中文汉化:见文章开头

16、待办清单
Microsoft To Do

官方网站:[https://www.microsoft.com/zh-cn/p/microsoft-to-do-lists-tasks-reminders]

17、邮件管理
网易邮箱大师

官方网站:[https://mail.163.com/dashi]

18、词典
欧陆词典

官方网站:http://www.eudic.net/v4/en/app/eudic/

奶酪在用词典:[https://pan.baidu.com/s/4dI83y1N]

19、笔记
Notepad2

官方网站:https://github.com/zufuliu/notepad2

汉化版本:http://blog.sina.com.cn/s/blog_89a729a40102zkxk.html

Typora

官方网站:[https://www.typora.io]

20、云笔记
印象笔记

官方网站:[https://www.yinxiang.com]

21、剪贴板
Ditto

官方网站:https://ditto-cp.sourceforge.io

汉化版本:[http://blog.sina.com.cn/s/blog_89a729a40102xyoc.html]

22、重命名
Renamer

官方网站:Renamer

23、文本识别
天若OCR

官方网站:https://tianruoocr.cn

开源版本:https://github.com/AnyListen/tianruoocr/releases

ABBYY FineReader

官方网站:https://www.abbyy.com/en-eu/finereader

补丁版本:[https://www.zdfans.com/html/48146.html]

24、思维导图
Xmind

官方网站:https://www.xmind.cn/download/

补丁版本:https://www.423down.com/9212.html

Draw.io

官方网站:https://www.diagrams.net

下载地址:[https://github.com/jgraph/drawio-desktop/releases]

25、PDF 阅读
BookxNote

官方网站:http://www.bookxnote.com/

26、电子书阅读
Neat Reader :https://www.neat-reader.cn/

BookxNote:http://www.bookxnote.com/

更新:

电子书阅读器,改推:京东读书。

大部分格式都支持,阅读体验也不错

https://cread.jd.com/custom/custom_pcDownload.action

27、文件同步
微云

官方网站:https://www.weiyun.com/

28、文件分享
1、速度快 + 文件大 = 奶牛快传https://cowtransfer.com/

2、速度快 + 保存久 = 蓝奏云https://www.lanzou.com

3、保存久 + 文件大 = 百度云[https://pan.baidu.com]

29、护眼
护眼啦

官方网站:http://443w.com/?hu/

分流下载:见文章开头

30、影视设计
Adobe Creative Suite

官方网站:https://www.adobe.com

补丁版本:[https://www.423down.com/?s=adobe]

31、视频转码
FFMpeg

官方网站:http://ffmpeg.org/

命令详解:https://blog.csdn.net/xiaojun111111/article/details/44618351

格式工厂

官方网站:http://www.pcfreetime.com/formatfactory/CN/index.html

去广告版:https://www.423down.com/1072.html

小丸工具箱

官方网站:[https://maruko.appinn.me/]

32、编程开发
Notepad2

官方网站:https://github.com/zufuliu/notepad2

汉化版本:http://blog.sina.com.cn/s/blog_89a729a40102zkxk.html

Sublime Text 3

官方网站:https://www.sublimetext.com/3

汉化补丁:[https://www.423down.com/4966.html]

33、虚拟机
VirtualBox

官方网站:[https://www.virtualbox.org]

34、文件管理
QTTabBar

官方网站:http://qttabbar.wikidot.com

奶酪配置:见文章开头

35、文件搜索
Listary

官方网站:https://www.listary.com/download

Everything

官方网站:[https://www.voidtools.com]

36、文件预览
QuickLook

官方网站:https://pooi.moe/QuickLook/

37、鼠标手势
Mouseinc

官方网站:https://shuax.com/project/mouseinc

奶酪配置:见文章开头

38、快捷键

HotkeyP

官方网站:http://petr.lastovicka.sweb.cz/others.html#hotkey

奶酪配置:见文章开头

39、桌面

奶酪推荐:不推荐使用任何桌面软件。

桌面美化:StartIsBack + TrueLaunchBar。

40、快捷启动
系统自带磁贴

nodejs简易服务器

简单需求使用nodejs直接创建一个简易服务器

创建node服务器

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
const http = require('http')
const fs = require('fs')
const querystring = require('querystring')
const server = http.createServer()
const router = require('./router')

const Route = new router({
"/signTransaction": { method:'post' },
"/getAllsign": { method:'get' },
})

server.on('request',function(req,res){
try {
var param = ''
req.on('data', function (chunk) {
param += chunk;
});
req.on('end', function () {
param = querystring.parse(param);
var request = {
url: req.url,
method: req.method,
param: param
}
// 交由router处理响应
Route.judgeUrl(request, res);
});
} catch (error) {
console.log(error)
}
})

process.on('uncaughtException', function (err) {
//打印出错误
console.log(err);
//打印出错误的调用栈方便调试
console.log(err.stack);
});

server.listen(8888, function(){
console.log('running on port 8888....')
})

因为post请求的参数是放在请求体里面,所以需要对请求进行监听并收集chunk数据,直到请求结束再重新解析处理

路由文件

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
const fs = require('fs')
const path = require('path')
const transaction = require('./Transaction') // 获取转换数据接口

class router{
constructor(urls){
this.urlMap = this.urls;
this.urls = Object.keys(this.urls);
}

judgeUrl(req, res){
const routers = Object.keys(this.urls);
if(this.routers.indexOf(req.url) < 0){
res.writeHead(200, {'Content-Type':'text/plain; charset=utf-8'})
.end('Api URL is not correct,please check again ')
return
} else if(this.urlMap[req.url].method !== req.method){
res.writeHead(401, {'Content-Type':'text/plain; charset=utf-8'})
.end(`Request type error, please use ${this.urlMap[req.url].method} instead `)
return
} else {
switch(req.url){
case '/signTransaction':
this.signTransaction(req, res);
break;
case '/getAllsign':
this.signTransaction(req, res);
break;
}
}
}

signTransaction(req, res){
res.writeHead(200,{'Content-Type':'application/json; charset=utf-8'})
var trans = JSON.parse(req.param.trans)
try {
var transHash = transaction(req.param.key)(trans)
} catch (error) {
res.end(JSON.stringify(error))
return
}
res.end(JSON.stringify(transHash))
}

async getAllsign(req, res){
res.writeHead(200,{'Content-Type':'application/json; charset=utf-8'})
try {
var data = await fetchAllsign();
} catch (error) {
res.end(JSON.stringify(error))
return
}
res.end(JSON.stringify(data))
}
}
module.exports = router