跳到主要内容

· 阅读需 1 分钟

什么是 PAG

Portable Animated Graphics 是一套完整的动效工作流解决方案。 提供从 AE (Adobe After Effects) 导出插件,到桌面预览工具,再 到覆盖 iOS,Android,macOS,Windows,Linux 和 Web 等各平 台的渲染 SDK。

信息

目前来看直接引用 js 文件还是很方便的。

React 引入涉及到 libpag.wasm 这个文件,开发模式和生产模式需要对这个文件做配置,否则找不到此文件。

看看这里有特效

官网

· 阅读需 2 分钟

再次搬家

Docusaurus 介绍

让您节省时间、专注于文档编写。您只需采用 MDX 格式编写文档和博文, Docusaurus 就会将它们构建成静态的 HTML 文件供用户访问。 您甚至可以在 Markdown 中嵌入 React 组件,这一切都得益于 MDX。

利用 React 组件可以为您的项目扩展或自定义页面布局。 得益于 Docusaurus 的插件架构,在设计您自己的 网站的同时,仍然能够重用由 Docusaurus 插件所创建的数据。

内置支持本地化翻译功能。可以通过 git、Crowdin 或任何 翻译管理工具来翻译您的文档并能够独立部署。

让用户可以查看您的项目的所有版本的文档。文档版本化功能可以帮 您保持文档与项目版本的同步。

让您的用户能够轻松地查找他们所需要的内容。 我们自豪地支持 Algolia 文档搜索产品。

文档

· 阅读需 4 分钟

好久不见,好忙,前段时间搭了一套electron-vue客户端,就想着用node把后台也一起搞搞,koa+mysql。

mysql 安装

本地开发环境为mac,mysql下载地址,下载安装就可以了。

mysql

通过Navicat连接查看,新建了一个名为electron_vue的数据库。

mysql

koa 安装

可以通过koa-generator脚手架直接安装使用->传送门

脚手架下载后npm install安转依赖模块,安装后目录展示。

koa

npm start启动后,浏览器访问http://localhost:3000/是这个样子的。

koa

koa 连接 mysql

首先执行npm install mysql -S,引入mysql依赖。

根目录下新建config文件夹,文件夹下新建mysql_config.js文件,添加相关配置。

const mysqlConfig = {
host: "localhost",
port: 3306,
user: "root", // mysql用户名
password: "4231", // 自己设置的mysql密码
database: "electron_vue", // mysql数据库名称
};

module.exports = mysqlConfig;

根目录下新建utils文件夹,新建query.jssql.js,分别加入如下代码。

// query.js
const mysql = require("mysql");
const MYSQL_CONFIG = require("../config/mysql_config"); // 数据库配置

// mysql
const pool = mysql.createPool(MYSQL_CONFIG);

// query sql语句入口
const query = (sql, val) => {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
reject(err);
} else {
connection.query(sql, val, (err, fields) => {
if (err) reject(err);
else resolve(fields);
connection.release();
});
}
});
});
};

module.exports = {
query,
};
// sql.js
// 创建数据库
const CREATE_TABLE = `CREATE TABLE IF NOT EXISTS user(
user_id INT(5) NOT NULL AUTO_INCREMENT,
user_name VARCHAR(255) NOT NULL,
user_phone VARCHAR(255) NOT NULL,
PRIMARY KEY (user_id)
);`.replace(/[\r\n]/g, "");

// 查询数据表
const QUERY_TABLE = (tableName) => `SELECT * FROM ${tableName}`;

// 插入数据
const INSERT_TABLE = (tableName, { key, val }) =>
`INSERT INTO ${tableName}(${key}) VALUES (${val})`;

// 更新数据
const UPDATE_TABLE = (tableName, { primaryKey, primaryVal }, { key, value }) =>
`UPDATE ${tableName} SET ${key}=${val} WHERE(${primaryKey}=${primaryVal});`;

// 删除数据
const DELETE_TABLE = (tableName, { primaryKey, primaryVal }) =>
`DELETE FROM user WHERE(${primaryKey}=${primaryVal});`;

module.exports = {
CREATE_TABLE,
INSERT_TABLE,
UPDATE_TABLE,
DELETE_TABLE,
};

routers/index.js中引入,先创建一张表试一下。

const router = require("koa-router")();

const { query } = require("../utils/query");
const {
CREATE_TABLE,
INSERT_TABLE,
UPDATE_TABLE,
DELETE_TABLE,
} = require("../utils/sql");

query(CREATE_TABLE);

// ....省略了

module.exports = router;

启动后台发现报错如下(据说是mysql8客户端错误)

run

需要在mysql中执行此命令:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '此处填写数据库密码!';
FLUSH PRIVILEGES;

我是在Navicat中执行的。

run

在重新启动,会发现数据库中新建了一张名为user的表。

run

至此,基本的koa连接使用mysql就完成了。

ps:

升级了mysql到8之后,发现,koa mysql模块连接不上mysql

报错如下(忘记截图了。。。):

UnhandledPromiseRejectionWarning: Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client

原因是最新的mysql模块并未完全支持MySQL 8的“caching_sha2_password”加密方式,而“caching_sha2_password”在MySQL 8中是默认的加密方式。因此,下面的方式命令是默认已经使用了“caching_sha2_password”加密方式,该账号、密码无法在mysql模块中使用。

需要重新修改mysql root的密码。

mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';

参考地址 koa+mysql 参考地址 mysql8修改加密方式

· 阅读需 1 分钟

React、Vue在接口获取传输数据使用qs进行数据序列化遇到问题,后台查询列表需要拼接带’.‘的参数。

如果参数中存在’.‘需要添加allowDots属性,允许 dots:

qs.stringify(params, {allowDots: true})

qs参考地址 https://github.com/ljharb/qs

标签:

· 阅读需 5 分钟

离线安装还Docker后,在离线安装一个Nginx镜像,实现的方法,首先要在有网的地方将nginx镜像导出,然后在复制到内网环境中安装。

在有网环境中将镜像打包

  • 先去pull一个最新的nginx镜像
  docker pull nginx
  • 然后将镜像导出
  docker save e445ab08b2be -o /Users/zhouyanwei/Desktop/nginx.tar
镜像的ID 保存的本地目录+文件名.tar
  • 然后在本地会有生成一个打包好nginx.tar文件(或者上面自定义的名字)。

在专网环境中安装

  • 把这个压缩包放到内网中执行
  docker load -i nginx.tar
  • 然后docker images 看一下当前的镜像

1

  • 此时 REPOSITORY 和 TAG 都为none

  • 在重新标记下标记一下就好了

  docker tag e445ab08b2be nginx:latest
  • 然后docker images 看一下当前的镜像

2

启动一个ngixn容器

  docker run --name nginx -d -p 8090:80 nginx

第一个 nginx 容器名称。 -d设置容器在在后台一直运行。 -p 端口进行映射,将本地 8090 端口映射到容器内部的 80 端口。

可能会遇见的错误

3

解决办法,是因为selinux开启导致docker run失败。

  vim /etc/selinux/config
  • 把 SELINUX项修改为disabled,然后重启CentOS系统。

  • 重启后在执行启动命令就成功了。

  docker start nginx
  • 此时我们在浏览器中访问当前地址加端口号就能访问成功了。

4

部署 nginx

  • 单单是把nginx启动了并不是我们的最终目的,最起码我们有一个存放文件的地方,还要能修改conf文件,还要能看log吧。

  • 首先创建ngixn目录,在目录下分别创建这三个目录进行存放。

    www: 目录将映射为 nginx 容器配置的虚拟目录。 logs: 目录将映射为 nginx 容器的日志目录。 conf: 目录里的配置文件将映射为 nginx 容器的配置文件。

  mkdir -p ~/nginx/www ~/nginx/logs ~/nginx/conf

此时会在root文件夹下创建nginx文件夹,下面有三个文件夹。其他账户登录要去home文件夹下当前登录的用户文件夹下查找。

  • 然后我们将当前ngixn默认的配置文件拷贝到我没呢新建的nginx/conf文件夹下。
  docker cp 01d9251b8255:/etc/nginx/nginx.conf ~/nginx/conf/
容器id,可使用 docker ps 查看
  • 在重新部署nginx
  docker run -d -p 80:80 --name nginxpro -v ~/nginx/www:/usr/share/nginx/html -v ~/nginx/conf/nginx.conf:/etc/nginx/nginx.conf -v ~/nginx/logs:/var/log/nginx nginx

-p 80:80: 将容器的 80 端口映射到主机的 80 端口。 --name nginxpro:将容器命名为 nginxpro。 -v ~/nginx/www:/usr/share/nginx/html:将我们自己创建的 www 目录挂载到容器的 /usr/share/nginx/html。 -v ~/nginx/conf/nginx.conf:/etc/nginx/nginx.conf:将我们自己创建的 nginx.conf 挂载到容器的 /etc/nginx/nginx.conf。 -v ~/nginx/logs:/var/log/nginx:将我们自己创建的 logs 挂载到容器的 /var/log/nginx。

  • 然后我们进www文件目录创建一个index.html。
  vim ~/nginx/www/index.html

5

12 月 2 日更新。 遇到的问题 如何添加代理

docker中的nginx配置文件不单只是一个nginx.conf

先执行命令进nginx交互模式里看一下nginx的结构

6

要把自己创建的目录和容器对应上才可以。

6

所以还需要重新部署ngixn

docker run -d -p 80:80 --name nginx \
-v ~/nginx/www:/usr/share/nginx/html \
-v ~/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v ~/nginx/conf.d:/etc/nginx/conf.d \
-v ~/nginx/logs:/var/log/nginx nginx

在default.conf重设置代理就可以生效了。

· 阅读需 3 分钟

如何在Linux离线下进行Docker安装。

本地环境

安装步骤

首先把下载好的Docker压缩包放到Linux根目录中

执行解压命令将压缩包进行解压到指定目录

tar zxvf docker-19.03.1.tar
sudo cp docker/* /usr/bin/
  • 小提示

    安装好后不要忘记把根目录的压缩包删除,回到根目录执行命令 rm -f docker-19.03.1.tar 要是可以直接ssh的,可以直接在本地把压缩包解压,然后把docker目录直接拽过去就好。

执行启动命令

sudo dockerd

然后查看一下docker版本号是否有了。 docker -v

将docker注册为service

  • 新建docker.service文件
vim /etc/systemd/system/docker.service
  • 把下面的内容复制进去
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd
ExecReload=/bin/kill -s HUP $MAINPID
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
# Uncomment TasksMax if your systemd version supports it.
# Only systemd 226 and above support this version.
#TasksMax=infinity
TimeoutStartSec=0
# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes
# kill only the docker process, not all processes in the cgroup
KillMode=process
# restart the docker process if it exits prematurely
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s

[Install]
WantedBy=multi-user.target
  • 执行命令

    添加文件权限并启动docker

      chmod +x /etc/systemd/system/docker.service

    重载unit配置文件

      systemctl daemon-reload

    启动Docker

      systemctl start docker

    设置开机自启

      systemctl enable docker.service
  • 常用命令

    重启docker

      systemctl restart docker

    关闭docker

      systemctl stop docker

· 阅读需 4 分钟

好久没有有更新了,换个工作一直忙,今天研究了一下前端测试环境自动化部署,再次记录,方便以后查阅。

安装 Jenkins

第一步就是先安装Jenkins,这里以mac系统为例,实际都是大同小异的。

通过 homebrew 安装 Jenkins, 建议通过命令安装,如果使用安装包安装,会涉及到权限问题服务启动不了。

brew install jenkins

如果没有安装homebrew,请参考这里https://brew.sh/index_zh-cn

安装好了之后执行命令

jenkins

浏览器访问,进入页面稍等一会。

[http://localhost:8080](http://localhost:8080)

因为我在安装的时候忘记截图了,所以就盗用了别人的图。

首先按照他的路径找到密码粘贴进去。

1

然后创建一个用户,方便以后好登陆。

2

在配置最后一个页面就可以了。

3

记得还有一个选择插件的页面,用默认的就好了。

建一个item进行自动化打包

进入jenkins主页面是这样的。

4

点击新建item

5

确定后进入配置页面

General配置

6

源码管理配置

因为我们目前还在使用svn,所以选择SVN,马上要开新项目,要使git了,配置应该不会有太大变化。

7

构建触发器配置

这个就看自己的需要了。

8

构建环境配置

9

构建配置

10 11

注意一下红色提示

此处配置完成后就可以进行build打包了。

12

然后我们进入jenkins目录文件,就可以看见项目和打包好的dist文件夹了。我的目录是

/Users/zhouyanwei/.jenkins/workspace/test

到此已经实现了自动化打包,接下来就是配置如何实现自动化上传到测试服务器上。

publish over ssh 插件进行自动化部署

首先我们先安装插件,因为我已经安装了。

13 14

安装好了之后进行Publish over SSH配置

19

找到 Publish over SSH 项

15

在进行SSH配置的时候,我了解到有两种方法,第一种是使用秘钥进行进行传输,还有一种就是我使用的账号密码进行传输。

16

别忘记保存,应用。

然后我们回到我们的项目配置,进行构建后操作配置

构建后操作配置

17 18

5月30日补充: 最后一步替换后执行操作,使用.代替是不对的,不执行可以不写。

别忘记保存,应用。

然后我们回到项目首页 Build Now 就可以了,完成后就会自动打包进行服务器更新了。

· 阅读需 1 分钟

最简单的双向数据绑定的实现,开始通过使用Object.defineProperty(),到ES6新增的Proxy的实现。

HTML

<div id="app">
<input type="text" id="inputTxt">
<input type="text" id="showTxt">
</div>

Object.defineProperty()

var obj = {};
Object.defineProperty(obj, 'txt', {
get: function () {
return obj;
},
set: function (newValue) {
document.getElementById('inputTxt').value = newValue;
document.getElementById('showTxt').value = newValue;
}
})
document.addEventListener('keydown', function (e) {
obj.txt = e.target.value;
})

Proxy

const obj = {};
const aProxy = new Proxy(obj, {
get: function(target, key, receiver){
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
if(key === 'txt' && value !== undefined) {
document.getElementById('inputTxt').value = value;
document.getElementById('showTxt').value = value;
}
return Reflect.set(target, key, value, receiver);
}
});
document.addEventListener('keydown', function (e) {
aProxy.txt = e.target.value;
})

· 阅读需 6 分钟

闭包与继承组合学习,加深记忆。

闭包

通过一个函数,能够访问另一个函数作用域的变量的函数

function init() {
var name = "Mozilla"; // name 是一个被 init 创建的局部变量
function displayName() { // displayName() 是内部函数,一个闭包
alert(name); // 使用了父函数中声明的变量
}
displayName();
}
init();//Mozilla

function a(x) {
var y = 4;
function b() {
console.log(x+y)
}
return b
}
var c = a(3)
c() // 7

构造函数继承

call apply方式 构造函数绑定

function Animal() {
this.species = '动物'
}
function Cat(name, color) {
Animal.apply(this, arguments)
this.name = name
this.color = color
}
var cat1 = new Cat('大俊', '黑色')
console.log(cat1.species) //动物

使用call和apply借用其他构造函数的成员, 可以解决给父构造函数传递参数的问题, 但是获取不到父构造函数原型上的成员.也不存在共享问题.

prototype模式

function Animal() {
this.species = '动物'
}
function Cat(name, color) {
this.name = name
this.color = color
}
Cat.prototype = new Animal();
Cat.prototype.constructor = Cat
var cat1 = new Cat('大俊', '黑色')
console.log(cat1.species) //动物

问题是不能给父构造函数传递参数, 父子构造函数的原型对象之间有共享问题.

直接继承prototype

function Animal() {}
Animal.prototype.species = '动物'
function Cat(name, color) {
this.name = name
this.color = color
}
Cat.prototype = Animal.prototype
Cat.prototype.constructor = Cat
var cat1 = new Cat('大俊', '黑色')
console.log(cat1.species) //动物
console.log(Animal.prototype.constructor) //Cat

缺点是 Cat.prototype和Animal.prototype,现在指向了同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。 只能继承父构造函数的原型对象上的成员, 不能继承父构造函数的实例对象的成员, 同时父构造函数的原型对象和子构造函数的原型对象上的成员有共享问题

利用空对象作为中介

function Animal() {}
Animal.prototype.species = '动物'
function Cat(name, color) {
this.name = name
this.color = color
}
var F = function () {}
F.prototype = Animal.prototype
Cat.prototype = new F()
Cat.prototype.constructor = Cat
var cat1 = new Cat('大俊', '黑色')
console.log(cat1.species)//动物
//封装
function extend(Child, Parent) {
var F = function () {}
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.uber = Parent.prototype//为子对象设一个uber属性,
// 这个属性直接指向父对象的prototype属性为了实现继承的完备性,纯属备用性质。
}
//使用
extend(Cat, Animal)
var cat1 = new Cat('大俊', '黑色')
console.log(cat1.species)//动物

拷贝继承

function Animal() {}
Animal.prototype.species = '动物'
function Cat(name, color) {
this.name = name
this.color = color
}
function extend2(Child, Parent){
var p = Parent.prototype
var c = Child.prototype
for(var x in p){
c[x] = p[x]
}
c.uber = p
}
extend2(Cat, Animal)
var cat1 = new Cat()
console.log(cat1.species)

拷贝继承的问题,如果继承过来的成员是引用类型的话,那么这个引用类型的成员在父对象和子对象之间是共享的,也就是说修改了之后, 父子对象都会受到影响.

非构造函数继承

object()方法

var Animal = {
species: "动物"
}
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var Cat = object(Animal);
Cat.name = "大俊";
console.log(Cat.species)//动物

浅拷贝

var Animal = {
species: "动物"
}
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
var Cat = extendCopy(Animal);
Cat.name = "大俊";
console.log(Cat)//动物

这样的拷贝有一个问题。那就是,如果父对象的属性等于数组或另一个对象,那么实际上,子对象获得的只是一个内存地址,而不是真正拷贝,因此存在父对象被篡改的可能。
var Animal = {
species: "动物"
}
function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}
Animal.do = ["吃", "喝"];
var Cat = extendCopy(Animal);
Cat.do.push("睡觉");
console.log(Cat.do)//["吃", "喝", "睡觉"]
console.log(Animal.do)//["吃", "喝", "睡觉"]

3、深拷贝

深拷贝,就是能够实现真正意义上的数组和对象的拷贝。递归调用"浅拷贝"就行了。

var Animal = {
species: "动物"
}
function deepCopy(p, c) {
var c = c || {};
for (var i in p) {
if (typeof p[i] === 'object') {
c[i] = (p[i].constructor === Array) ? [] : {};
deepCopy(p[i], c[i]);
} else {
c[i] = p[i];
}
}
return c;
}
Animal.do = ["吃", "喝"];
var Cat = deepCopy(Animal);
Cat.do.push("睡觉");
console.log(Cat.do)//["吃", "喝", "睡觉"]
console.log(Animal.do)//["吃", "喝"]

学习地址 http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html;

· 阅读需 3 分钟

判断类型、构造函数、对象属性等,记录到一起,方便记忆学习。

typeof

操作符返回一个字符串,表示未经计算的操作数的类型,其实就是判断参数是什么类型的实例。

一般返回以下几种结果:"number"、"string"、"boolean"、"object"、"function" 和 "undefined"。

console.log(typeof 123) //number
console.log(typeof (1+2)) //number
console.log(typeof NaN) //number
console.log(typeof Number(6)) //number
console.log(typeof Infinity) //number

console.log(typeof "") //string
console.log(typeof "zyw") //string
console.log(typeof (typeof "zyw")) //string

console.log(typeof true) //boolean
console.log(typeof false) //boolean

console.log(typeof {name:'zyw'}) //object
console.log(typeof ['z', 'y', 'w']) //object
console.log(typeof new Date()) //object

console.log(typeof function(){}) //function

console.log(typeof undefined) //undefined
console.log(typeof zyw) //undefined zyw未定义

instanceof

用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置

注意上面数组和对象返回的都是object,这时就需要使用instanceof来判断了。

console.log(['z', 'y', 'w'] instanceof Array) //true

// 定义构造函数
function C(){}
function D(){}
var o = new C();
o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof D; // false,因为 D.prototype不在o的原型链上
o instanceof Object; // true,因为Object.prototype.isPrototypeOf(o)返回true
C.prototype instanceof Object // true,同上

console.log("zyw" instanceof String) //false
console.log(new String() instanceof String) //true
console.log(new String("zyw") instanceof String) //true
console.log(new Date() instanceof String) //false

console.log(new String("zyw") instanceof Object) //true
console.log({} instanceof Object) //true
console.log(Object.create(null) instanceof Object) //false

console.log(new Date() instanceof Date) //true
console.log(new String() instanceof Date) //false

console.log(new Date() instanceof Object) //true

hasOwnProperty

返回一个布尔值,指示对象自身属性中是否具有指定的属性

o = new Object();
o.prop = 'exists';
function changeO() {
o.newprop = o.prop;
delete o.prop;
}
o.hasOwnProperty('prop'); // 返回 true
changeO();
o.hasOwnProperty('prop'); // 返回 false

Object.prototype.toString.call()

常用于判断浏览器内置对象。

console.log( Object.prototype.toString.call('zyw')) //[object String]
console.log( Object.prototype.toString.call(123)) //[object Number]
console.log( Object.prototype.toString.call(Symbol(123))) //[object Symbol]
console.log( Object.prototype.toString.call(null)) //[object Null]
console.log( Object.prototype.toString.call(undefined)) //[object Undefined]
console.log( Object.prototype.toString.call(function () {})) //[object Function]
console.log( Object.prototype.toString.call({name: 'zyw'})) //[object Object]

Array.isArray()

用来判断对象是否为数组

var arr = ['z', 'y', 'w']
console.log(Array.isArray(arr))//true
var obj = {name: 'zyw'}
console.log(Array.isArray(obj))//false

参考网址:MDN