celsius的个人博客

星星之火可以燎原

0%

记录一下自己的学习历程
首先购买一台阿里云轻量应用服务器,之所以用这个是因为直接就搭建好了 node 的相关环境,到手即用

之后在阿里云控制台就能看到服务器的相关信息,比如公网 ip 地址之类的,记得改密码,然后使用 XShell 就能连接上服务器使用命令行来操作,使用 XFtp 来上传文件到服务器,这里没什么说的哈,都是基本操作。
接下来先部署一下自己的静态网站,将你的网页文件上传到/var/www/文件夹下(比如 vue 项目就是 build 后将 dist 文件夹内的东西传上去),然后配置一下 nginx(阿里云轻量应用服务器自带了 nginx)
nginx 的配置文件默认位置是/usr/local/nginx/conf/nginx.conf

使用 vim 或者记事本打开编辑 http 下的 server 里的参数,接着重启一下 nginx 就生效了,然后用浏览器打开 ip 地址就能看到
接下来就来到了接口的开发,首先我们在本地新建一个文件夹,起名叫 api,
进去之后我们打开命令行窗口

1
npm init

配置一下相关信息
然后装上 express 框架并初始化一个项目

1
2
3
4
$ npm install express --save
$ npm install express-generator -g
$ express
$ npm install

然后就会生成这样的一个结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

.
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade

这里具体的代码实现就不说了,先说一下怎么在服务器上跑起来
把 api 文件夹上传到服务器随便哪个目录(node_modules 文件夹别一起上传了)
然后使用 XShell cd 到这个文件夹使用 pm2 启动(pm2 可以在你关闭命令行窗口后保护进程运行,也是阿里云轻量应用服务器自带的)

1
2
npm install
pm2 start ./bin/www

接着去阿里云控制台防火墙放开一下 3000 端口
在这里插入图片描述
然后我们用 ip 地址+3000,比如 47.96.109.208:3000 打开就能看到
在这里插入图片描述
成功!
后面就是在这个模板里修改代码来做接口啦

废话不多说,能实现导出为 excel 的库也不少,这次我用的是 xlsx.full.min.js(git 地址:https://github.com/SheetJS/sheetjs)
在 utils 里简单封装一下

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
import * as XLSX from "xlsx";
/**
* 字符串转字符流
*/
const s2ab = (s: string = "") => {
//字符串转字符流
let buf = new ArrayBuffer(s.length);
let view = new Uint8Array(buf);
for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
return buf;
};
/**
* 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]
*/
const getCharCol = (n: number) => {
let s = "",
m = 0;
while (n > 0) {
m = (n % 26) + 1;
s = String.fromCharCode(m + 64) + s;
n = (n - m) / 26;
}
return s;
};
/**
* 将json转换为excel格式流并使用a标签点击下载
*/
const downloadExl = (json: any, type: any) => {
let tmpdata: any = json[0];
json.unshift({});
let keyMap: string[] = []; //获取keys
//keyMap =Object.keys(json[0]);
for (let k in tmpdata) {
keyMap.push(k);
json[0][k] = k;
}
tmpdata = [];
json
.map((v: { [x: string]: any }, i: number) =>
keyMap.map((k, j) =>
Object.assign(
{},
{
v: v[k],
position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1),
}
)
)
)
.reduce((prev: string | any[], next: any) => prev.concat(next))
.forEach(
(v: { position: string | number; v: any }, i: any) =>
(tmpdata[v.position] = {
v: v.v,
})
);
let outputPos = Object.keys(tmpdata); //设置区域,比如表格从A1到D10
let tmpWB = {
SheetNames: ["mySheet"], //保存的表标题
Sheets: {
mySheet: Object.assign(
{},
tmpdata, //内容
{
"!ref": outputPos[0] + ":" + outputPos[outputPos.length - 1], //设置填充区域
}
),
},
};
let tmpDown: any = "";
tmpDown = new Blob(
[
s2ab(
XLSX.write(
tmpWB,
{
bookType: "xlsx",
bookSST: false,
type: "binary",
} //这里的数据是用来定义导出的格式类型
)
),
],
{
type: "",
}
); //创建二进制对象写入转换好的字节流
let href = URL.createObjectURL(tmpDown); //创建对象超链接
let _a = document.createElement("a");
_a.setAttribute("href", href);
_a.setAttribute("download", "这里是excel文件名.xlsx");
document.body.appendChild(_a);
_a.click();
setTimeout(() => {
//延时释放
URL.revokeObjectURL(tmpDown); //用URL.revokeObjectURL()来释放这个object URL
}, 100);
};
export { downloadExl };

在页面使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { downloadExl } from "@/utils/util";
let json = [
{
提交时间: "2021-05-19 14:57:59",
用户名称: "张大毛",
编号: "1",
更新时间: "2021-05-19 14:57:59",
},
{
提交时间: "2021-05-17 11:32:48",
用户名称: "王德发",
编号: "2",
更新时间: "2021-05-17 11:32:48",
},
];
downloadExl(json);

然后就会直接调起下载
冷吃兔
打开看看
可以,完美
可以,非常完美
各位看官点个赞啦

Git的用途

  1. 托管代码到远程,分布式托管,避免本机磁盘损坏造成不可挽回的局面。
  2. 版本控制,可以发布多个版本并且实现在各个版本之间来回穿梭(实现原理:文件快照,每个版本都会有一个文件快照,比直接备份文件快速便捷。因此,Git仓库又被称为版本库)。
  3. 团队协作,强大的分支功能,可以快速实现团队协作

Git代码托管平台

  1. github: https://github.com

    github是全球最大的开源社交编程及代码托管网站,也被称为全球最大同性交友网(gayhub)👨‍❤️‍💋‍👨👨‍❤️‍💋‍👨👨‍❤️‍💋‍👨👨‍❤️‍💋‍👨👨‍❤️‍💋‍👨👨‍❤️‍👨👨‍❤️‍👨👨‍❤️‍👨👨‍❤️‍👨👨‍❤️‍👨

  2. coding: https://dev.tencent.com

    与腾讯云达成战略合作,基于云计算技术的软件开发平台,集项目管理、代码托管、运行空间、质量控制为一体。

  3. gitee: https://gitee.com

    码云(gitee.com)是 OSCHINA.NET 推出的代码托管平台,支持 Git 和 SVN,提供免费的私有仓库托管。

Git使用步骤

本次以gitee为例做讲解,读者也可以选择github或者coding,使用步骤一致,步骤中使用 [] 中括号括起来的部分需要替换成你自己相应的内容,不需要保留中括号。

  1. 第一次安装git的时候要执行1-4步,否则从第5步开始

安装git工具,安装完成之后鼠标右键只要出现 git bash here 菜单即说明安装成功。

windows系统下载链接

MAC下载链接

  1. 注册gitee的账号(或其他平台账号),修改个人空间地址,绑定邮箱。

  2. 全局配置用户名和邮箱

    git config --global user.name[你的码云账号]

    git config --global user.email [你的码云验证邮箱]

  3. 配置密钥对:生成公钥和私钥,用于上传代码时的安全验证

    git bash里执行命令ssh-keygen 一路回车,就可以生成密钥对,默认密钥对是存放在(/c/Users/[主机用户名]/.ssh/) 。这个目录下有两个文件, .pub就是公钥,另外一个是私钥,这两个文件千万不要动!!!

    到线上(gitee或其他平台)打开设置->安全设置->ssh公钥,把本地的公钥文件全选复制进来,输入登录密码,就配置成功了。

  4. 第一次创建项目的时候执行5-7步,否则从第8步开始

    创建本地仓库

    在本地创建一个项目文件夹,项目代码都在这个文件夹里,执行 git init 初始化一个本地git仓库,这个时候项目里会多出一个.git目录(这个目录默认是隐藏的,这里就是用来存放文件快照的地方),这个目录千万不要动!!!

  5. 创建一个线上仓库

    登录gitee,新建仓库,输入项目名称,选择私有或者公开源代码(私有在加入合作者之前就只能你自己能查看,公开就意味着开源),下面的选框一个都不要勾(初始化的不是文件都来自于本地仓库,线上仓库不需要任何文件),最后点击创建就ok了。

  6. 将本地仓库和线上仓库建立关联:git remote add origin [线上仓库的SSH地址]

    如果在执行这句话的时候报错:fatal: remote origin already exists.

    那么就先执行 git remote rm origin

    再重新执行 git remote add origin [线上仓库的SSH地址]

  7. 代码添加到暂存区 git add -A (也可以 git add [文件名] 来单独添加某一个文件)

  8. 代码提交到本地仓库 git commit -m '[说明本次提交所做的操作,越详细越好]'

  9. 代码推送到远程 git push origin master

版本管理

  1. 把已经放在暂存区的内容在拉回到工作区

    1
    2
    3
    4
    5
    6
    7
    8
    # 拉回暂存区的 index.txt 文件
    git reset HEAD -- index.txt

    # 拉回暂存区的 ceshi 文件夹
    git reset HEAD -- ceshi/

    # 拉回暂存区的 所有文件
    git reset HEAD -- .
  2. 历史版本回退

    1
    2
    # 查看历史版本
    git log

git版本信息git版本信息

这里commit后面的字符串即为版本号,我们可以使用 git reset --hard 版本编号 进行历史回退

1
2
3
4
5
# 回退到第一次提交的版本
git reset --hard ce0c17f7a703c6847552c7aaab6becea6f0197f2

# 回退到第二次提交的版本
git reset --hard abb2c4f12566440e04bc166c3285f855a37a3bb2

Git的用途

git分支,就是我们自己把我们的整个文件夹分成一个一个独立的区域,比如我在开发 登录 功能的时候,可以放在 login 分支下进行开发;开发 列表 功能的时候,可以放在 list 分支下进行开发,大家互不干扰,每一个功能都是一个独立的功能分支,这样开发就会好很多。

git在初始化的时候,会自动生成一个分支,叫做 master,是表示主要分支的意思,我们就可以自己基于master开辟出很多独立分支

  1. 开辟一个分支使用 git branch 分支名称 指令

    1
    2
    # 开辟一个 login 分支
    $ git branch login
  2. 查看一下当前分支情况

    1
    2
    # 查看当前分支情况
    $ git branch

    [查看分支情况查看分

    支情况

    已经可以看到,当前有两个分支了,一个是 master,一个是 login。前面有个 * 号,并且有高亮显示的,表示你当前所处的分支。

    我们对 登录 功能的开发要移动到 login 分支去完成,此时可以切换到其他分支,使用 git checkout 分支名称

    1
    2
    # 切换到 login 分支
    $ git checkout login

    然后我们在整个login分支上进行 登录 功能的开发。开发完毕以后,我们就在当前分支上进行提交,提交以后我们再把分支切换回master发现 master 上面还是最初始的状态,而 login 分支上有我们新写的 登录 功能的代码。我们按照分支把所有功能都开发完毕了以后,需要把所有代码都合并到 master 主分支上。

    git 的合并分支,只能是把别的分支的内容合并到自己的分支上

    使用的指令是 git merge

    1
    2
    3
    4
    5
    # 切换到 master 分支
    $ git checkout master

    # 把 login 的内容合并到自己的分支
    $ git merge login

    这个时候,我们刚才在 login 上开发的东西就都来到了 master 主分支上,如果是有多个分支的话,那么所有的最后都合并到 master 分支上的时候,我们的主分支上就有完整网站的所有页面,各个分支上都是单独的页面和功能。这个时候我们开辟的分支就没有什么用了,就可以删除分支了

    1. 先切换到别的分支

    2. 使用指令 git branch -d 分支名称 来删除

    3. ```base

      先切换到别的分支

      $ git checkout master

      删除 login 分支

      $ git branch -d login

      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

      ### 常用的分支命名

      在使用分支的时候我们的分支命名也要尽量规范一些,我们有一些分支名称大家都默认是有特殊意义的,比如我们之前的写的`login`分支就是不规范的分支名称。

      常见的分支名称

      1. master:主分支,永远只存储一个可以稳定运行的版本,不能在这个分支上直接开发
      2. develop(dev): 主要开发分支,主要用于所用功能开发的代码合并,记录一个个的完整版本
      - 包含测试版本和稳定版本
      - 不要在这个分支上进行开发
      3. feature-xxx(feat-xxx):功能开发分支,从`dev`创建的分支主要用作某一个功能的开发,以自己功能来命名就行,例如 `feat-login` / `feat-list`,开发完毕后合并到 `dev` 分支上
      4. feat-xxx-fix: 某一分支出现`bug`以后,在当前分支下开启一个`fix`分支,比如登录功能有bug,可以基于`feat-login`开辟一个新的分支`feat-login-fix`,解决完 `bug` 以后,合并到当前功能分支上,如果是功能分支已经合并之后发现 `bug` 可以直接在 `develop` 上开启分支,修复完成之后合并到 `dev` 分支上
      5. hotfix-xxx: 用于紧急`bug`修复,直接在 `master` 分支上开启,修复完成之后合并回 `master`

      ### 冲突解决

      git冲突是指在我们的上传过程中,本地的版本和远程的版本不一致导致的,这个时候只要先使用 `git pull` 拉取回来,让本地和远程保持一致,然后再从新上传就好了,但是 `git pull` 相对不安全,因为会自动和本地内容合并,我们也可以选择使用 `git fetch`

      ```base
      # 使用 fetch 获取远程最新信息并开辟一个临时分支
      $ git fetch origin master:tmp

      # 将当前分支和临时分支的内容进行对比
      $ git diff tmp

      # 再选择合并分支内容
      $ git merge tmp

附录1:Git常见命令

  • git init 初始化仓库
  • git config 配置用户信息
  • git remote add origin [线上仓库地址] 新增远程仓库的关联
  • git remote rm origin 删除远程仓库的关联
  • git add 添加到暂存区
  • git commit -m '[本次提交的备注信息]' 代码提交(每一次commit都会有一个新的版本号)
  • git push origin [分支名] 推送到远程仓库
  • git status 查看当前仓库的状态
  • git log 查看日志(每一个commit在这里都能查看到,而且commit后面的随机字符串就是版本号),按字母q 退出log
  • git reset --hard [要回退的版本号] 回退到之前的某一个版本
  • git clone [线上仓库地址] 把线上仓库代码克隆到本地
  • git pull origin [分支名] 在已有的仓库基础上拉取某分支最新的线上代码,拉取之后直接合并
  • git fetch origin [分支名] 在已有的仓库基础上拉取某分支最新的线上代码,拉取之后由用户决定是否合并
  • git branch 查看分支
  • git branch newBranch 基于当前分支创建newBranch分支
  • git branch -d myBranch 删除myBranch分支
  • git diff tmp 查看当前分支和tmp分支的区别
  • git merge tmptmp分支合并到当前分支

附录2:使用Git时候的一些注意事项

  • 一个本地仓库对应一个远程仓库
  • 远程代码和本地代码要保持统一
  • .git 文件不能嵌套(仓库不能嵌套)

附录3:使用Git提交时的备注信息

用于说明 commit 的类别,只允许使用下面7个标识

  1. feat:新功能(feature)
  2. fix:修补bug
  3. docs:文档(documentation)
  4. style: 格式(不影响代码运行的变动)
  5. refactor:重构(即不是新增功能,也不是修改bug的代码变动)
  6. test:增加测试
  7. chore:构建过程或辅助工具的变动

在uniapp中使用svga,我是只需要编译为微信小程序和h5,其他端的暂时没研究。完整代码在最后
首先是微信小程序,从https://github.com/svga/svgaplayer-weapp下载svgaplayer.weapp.js,按照教程引入即可

1
2
3
4
5
6
7
8
<template>
<canvas
type="2d"
style="width: 300px; height:300px;background-color: #000;"
id="myCanvas"
canvas-id="myCanvas"
></canvas>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
const SVGA = require('../../utils/svga.weapp.js')
async onLoad() {
const player = new SVGA.Player('#myCanvas')
const parser = new SVGA.Parser('#myCanvas')
parser.load(
'https://qiniu.taktak.tv/spring-headlines2022/inlive.svga',
function(value: any) {
player.setVideoItem(value)
player.startAnimation()
}
)
}

接着是h5,从https://github.com/svga/SVGAPlayer-Web下载svga.min.js,按照教程引入,运行,发现报错
在这里插入图片描述
看报错原因我们能发现,getContext这是canvas的一个api,而这个报错应该是因为没有通过#myCanvas获取到对应的canvas dom节点,我们点开Elements就会发现这是因为uniapp在编译h5的时候,会把canvas节点上的id属性给弄到uni-canvas这个节点上,这也许是为了重写多端的canvas方法
在这里插入图片描述
但是这样的话我们就没办法拿到真正的canvas节点了啊,这咋办。只能直接上暴力美学,咱们直接用js插入一个canvas dom 进去,下面附全部代码

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<view id="main">
<!-- #ifndef H5 -->
<canvas
type="2d"
style="width: 300px; height:300px;background-color: #000;"
id="myCanvas"
canvas-id="myCanvas"
ref="myCanvas"
></canvas>
<!-- #endif -->
</view>
</template>
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
let SVGA: any = ''
// #ifndef H5
SVGA = require('../../utils/svga.weapp.js')
// #endif
// #ifdef H5
SVGA = require('../../utils/svga.min.js')
// #endif
async onLoad() {
//#ifndef H5
const player = new SVGA.Player('#myCanvas')
const parser = new SVGA.Parser('#myCanvas')
setTimeout(() => {
parser.load(
'https://qiniu.taktak.tv/spring-headlines2022/inlive.svga',
function(value: any) {
player.setVideoItem(value)
player.startAnimation()
}
)
}, 0)
//#endif
//#ifdef H5
setTimeout(() => {
const canvasList: any = document.getElementById('main')
const canvas = document.createElement('canvas')
canvas.width = 300
canvas.height = 300
canvas.style.backgroundColor = '#000'
canvas.id = 'myCanvas'
canvasList.appendChild(canvas)
setTimeout(() => {
const player = new SVGA.Player('#myCanvas')
const parser = new SVGA.Parser('#myCanvas')
parser.load(
'https://qiniu.taktak.tv/spring-headlines2022/inlive.svga',
function(value: any) {
player.setVideoItem(value)
player.startAnimation()
}
)
}, 0)
}, 0)
//#endif
}

在 ts 环境中引入 js 文件有 import 方式和 require 方式,import 方式需要写个声明,require 则不需要,不过 eslint 会报错,可以在 eslinttrc.js 中加入

1
'@typescript-eslint/no-var-requires': 'off'

来关闭验证
这都没啥问题,然后重新编译一下,诶,怎么卡在一个 node_modules 里的一个依赖这卡了五六分钟。我这引入第三方 js 跟依赖也没关系啊。
然后百度+谷歌,并没有找到解决方案
最后我就打开了这个 js 文件,是一个压缩后的 js 文件,使用 prettier 反压缩之后准备看看是不是哪里写的有问题,接着神奇的事发生了,编译直接通过,十分之丝滑……
目前没找到仅仅一个压缩会阻塞编译的原因,希望有大佬知道的可以留言告知,谢谢~

  1. 在开发中遇到了需要监听页面隐藏展示(比如切到后台又切回来),这个时候我立马想到onShow生命周期,不过反应过来这个是微信小程序的。如果想在h5里面监听的话,只需要加一个监听器

    1
    2
    3
    4
    5
    6
    7
    document.addEventListener('visibilitychange', async () => {
    if (document.visibilityState == 'visible') {
    //页面展示
    } eles {
    //页面隐藏
    }
    })

    解决了这个问题,又来了一个新的问题,比如我开发的A页面,从A页面跳转到B页面,再从B页面返回到A页面,这个使用上面所说的visibilitychange是监听不到的,因为本质上讲A页面并没有被隐藏,这里不展开细说,只说怎么解决,那就是用另一个事件监听 popstate

    1
    2
    3
    window.addEventListener('popstate', async () => {
    //监听到页面返回
    })

    好的,又把这个问题解决了,但是出现了一个新的问题,那就是如果我使用了vue-router,在单页面应用里进行路由跳转,因为链接的变化,路由跳转也会被认为是页面之间的切换,因此也会触发popstate这个监听。
    但这显然不是我想要的效果,因此我只好又拿出了另外一个事件监听: pageshow
    eg: 这里使用popstate也可以完成需求,用全局参数记录一下是否路由跳转就行

    1
    2
    3
    4
    5
    6
    window.addEventListener('pageshow', async () => {
    //监听到页面展示
    if (StoreModule.loadEnd) {//loadEnd为true即为页面加载完成
    //to do something
    }
    })

    pageshow,字面上就能看出是页面展示的意思,因此页面的最开始加载和从其他页面返回才会触发,而hash路由跳转的时候并不会触发,再用一个vuex保存一个页面是否加载完成的参数来判断,就能完美处理页面返回的监听处理逻辑了。