什么是 PAG
Portable Animated Graphics 是一套完整的动效工作流解决方案。 提供从 AE (Adobe After Effects) 导出插件,到桌面预览工具,再 到覆盖 iOS,Android,macOS,Windows,Linux 和 Web 等各平 台的渲染 SDK。
目前来看直接引用 js 文件还是很方便的。
React 引入涉及到 libpag.wasm 这个文件,开发模式和生产模式需要对这个文件做配置,否则找不到此文件。
Docusaurus 介绍
让您节省时间、专注于文档编写。您只需采用 MDX 格式编写文档和博文, Docusaurus 就会将它们构建成静态的 HTML 文件供用户访问。 您甚至可以在 Markdown 中嵌入 React 组件,这一切都得益于 MDX。
利用 React 组件可以为您的项目扩展或自定义页面布局。 得益于 Docusaurus 的插件架构,在设计您自己的 网站的同时,仍然能够重用由 Docusaurus 插件所创建的数据。
内置支持本地化翻译功能。可以通过 git、Crowdin 或任何 翻译管理工具来翻译您的文档并能够独立部署。
让用户可以查看您的项目的所有版本的文档。文档版本化功能可以帮 您保持文档与项目版本的同步。
让您的用户能够轻松地查找他们所需要的内容。 我们自豪地支持 Algolia 文档搜索产品。
好久不见,好忙,前段时间搭了一套electron-vue客户端,就想着用node把后台也一起搞搞,koa+mysql。
本地开发环境为mac,mysql下载地址,下载安装就可以了。
通过Navicat连接查看,新建了一个名为electron_vue的数据库。
可以通过koa-generator脚手架直接安装使用->传送门
脚手架下载后npm install
安转依赖模块,安装后目录展示。
npm start
启动后,浏览器访问http://localhost:3000/
是这个样子的。
首先执行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.js
及sql.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客户端错误)
需要在mysql中执行此命令:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '此处填写数据库密码!';
FLUSH PRIVILEGES;
我是在Navicat中执行的。
在重新启动,会发现数据库中新建了一张名为user
的表。
至此,基本的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';
React、Vue在接口获取传输数据使用qs进行数据序列化遇到问题,后台查询列表需要拼接带’.‘的参数。
如果参数中存在’.‘需要添加allowDots属性,允许 dots:
qs.stringify(params, {allowDots: true})
离线安装还Docker后,在离线安装一个Nginx镜像,实现的方法,首先要在有网的地方将nginx镜像导出,然后在复制到内网环境中安装。
docker pull nginx
docker save e445ab08b2be -o /Users/zhouyanwei/Desktop/nginx.tar
镜像的ID 保存的本地目录+文件名.tar
docker load -i nginx.tar
此时 REPOSITORY 和 TAG 都为none
在重新标记下标记一下就好了
docker tag e445ab08b2be nginx:latest
docker run --name nginx -d -p 8090:80 nginx
第一个 nginx 容器名称。 -d设置容器在在后台一直运行。 -p 端口进行映射,将本地 8090 端口映射到容器内部的 80 端口。
可能会遇见的错误
解决办法,是因为selinux开启导致docker run失败。
vim /etc/selinux/config
把 SELINUX项修改为disabled,然后重启CentOS系统。
重启后在执行启动命令就成功了。
docker start nginx
单单是把nginx启动了并不是我们的最终目的,最起码我们有一个存放文件的地方,还要能修改conf文件,还要能看log吧。
首先创建ngixn目录,在目录下分别创建这三个目录进行存放。
www: 目录将映射为 nginx 容器配置的虚拟目录。 logs: 目录将映射为 nginx 容器的日志目录。 conf: 目录里的配置文件将映射为 nginx 容器的配置文件。
mkdir -p ~/nginx/www ~/nginx/logs ~/nginx/conf
此时会在root文件夹下创建nginx文件夹,下面有三个文件夹。其他账户登录要去home文件夹下当前登录的用户文件夹下查找。
docker cp 01d9251b8255:/etc/nginx/nginx.conf ~/nginx/conf/
容器id,可使用 docker ps 查看
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。
vim ~/nginx/www/index.html
12 月 2 日更新。 遇到的问题 如何添加代理
docker中的nginx配置文件不单只是一个nginx.conf
先执行命令进nginx交互模式里看一下nginx的结构
要把自己创建的目录和容器对应上才可以。
所以还需要重新部署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重设置代理就可以生效了。
如何在Linux离线下进行Docker安装。
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
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
好久没有有更新了,换个工作一直忙,今天研究了一下前端测试环境自动化部署,再次记录,方便以后查阅。
第一步就是先安装Jenkins,这里以mac系统为例,实际都是大同小异的。
通过 homebrew 安装 Jenkins, 建议通过命令安装,如果使用安装包安装,会涉及到权限问题服务启动不了。
brew install jenkins
如果没有安装homebrew,请参考这里https://brew.sh/index_zh-cn
安装好了之后执行命令
jenkins
浏览器访问,进入页面稍等一会。
[http://localhost:8080](http://localhost:8080)
因为我在安装的时候忘记截图了,所以就盗用了别人的图。
首先按照他的路径找到密码粘贴进去。
然后创建一个用户,方便以后好登陆。
在配置最后一个页面就可以了。
记得还有一个选择插件的页面,用默认的就好了。
进入jenkins主页面是这样的。
点击新建item
确定后进入配置页面
General配置
源码管理配置
因为我们目前还在使用svn,所以选择SVN,马上要开新项目,要使git了,配置应该不会有太大变化。
构建触发器配置
这个就看自己的需要了。
构建环境配置
构建配置
注意一下红色提示
此处配置完成后就可以进行build打包了。
然后我们进入jenkins目录文件,就可以看见项目和打包好的dist文件夹了。我的目录是
/Users/zhouyanwei/.jenkins/workspace/test
到此已经实现了自动化打包,接下来就是配置如何实现自动化上传到测试服务器上。
首先我们先安装插件,因为我已经安装了。
安装好了之后进行Publish over SSH配置
找到 Publish over SSH 项
在进行SSH配置的时候,我了解到有两种方法,第一种是使用秘钥进行进行传输,还有一种就是我使用的账号密码进行传输。
别忘记保存,应用。
然后我们回到我们的项目配置,进行构建后操作配置
构建后操作配置
5月30日补充: 最后一步替换后执行操作,使用.代替是不对的,不执行可以不写。
别忘记保存,应用。
然后我们回到项目首页 Build Now 就可以了,完成后就会自动打包进行服务器更新了。
最简单的双向数据绑定的实现,开始通过使用Object.defineProperty(),到ES6新增的Proxy的实现。
HTML
<div id="app">
<input type="text" id="inputTxt">
<input type="text" id="showTxt">
</div>
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;
})
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;
})
闭包与继承组合学习,加深记忆。
通过一个函数,能够访问另一个函数作用域的变量的函数
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
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借用其他构造函数的成员, 可以解决给父构造函数传递参数的问题, 但是获取不到父构造函数原型上的成员.也不存在共享问题.
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) //动物
问题是不能给父构造函数传递参数, 父子构造函数的原型对象之间有共享问题.
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)
拷贝继承的问题,如果继承过来的成员是引用类型的话,那么这个引用类型的成员在父对象和子对象之间是共享的,也就是说修改了之后, 父子对象都会受到影响.
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)//["吃", "喝", "睡觉"]
深拷贝,就是能够实现真正意义上的数组和对象的拷贝。递归调用"浅拷贝"就行了。
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;
判断类型、构造函数、对象属性等,记录到一起,方便记忆学习。
操作符返回一个字符串,表示未经计算的操作数的类型,其实就是判断参数是什么类型的实例。
一般返回以下几种结果:"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未定义
用于测试构造函数的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
返回一个布尔值,指示对象自身属性中是否具有指定的属性
o = new Object();
o.prop = 'exists';
function changeO() {
o.newprop = o.prop;
delete o.prop;
}
o.hasOwnProperty('prop'); // 返回 true
changeO();
o.hasOwnProperty('prop'); // 返回 false
常用于判断浏览器内置对象。
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]
用来判断对象是否为数组
var arr = ['z', 'y', 'w']
console.log(Array.isArray(arr))//true
var obj = {name: 'zyw'}
console.log(Array.isArray(obj))//false
参考网址:MDN