安装环境shell脚本（centos7）#!/bin/bash #suppor china yum install wqy-microhei-fonts wqy-zenhei-fonts #office to pdf yum install unoconv -y #pdf to slide image yum install ImageMagick -y使用node构造核心类officeCovert.jsconst unoconv = require('awesome-unoconv'); const fs = require('fs'); const child_process = require("child_process"); class OfficeCovert{ //file to pdf file2pdf(sourceFile, generateFile){ return unoconv.convert(sourceFile, generateFile); } //file to image list file2images(file, path, prefix = '', suffix ='', ext = 'png'){ return new Promise((resolve, reject)=> { if(fs.existsSync(file)){ this.rm(path); this.mkdir(path); child_process.exec(`convert ${file} ${path}/${prefix}%d${suffix}.${ext} `, (...args) =>{ if(args && args[0] === null){ resolve(path); }else{ reject(...args); } }, err => { reject(err); }); }else{ reject('file not found'); } }); } //delete file or path rm(path){ let files = []; if(fs.existsSync(path)){ if(fs.statSync(path).isDirectory()){ files = fs.readdirSync(path); files.forEach((file, index)=> { let curPath = path + '/' + file; if(fs.statSync(curPath).isDirectory()){ this.rm(curPath); }else{ fs.unlinkSync(curPath); } }); }else{ fs.unlinkSync(path); } } } //create path mkdir(path){ if(!fs.existsSync(path)){ fs.mkdir(path,{ recursive: true }, res=> { if(res){ console.warn(res); } }); } } } module.exports = OfficeCovert;入口文件index.jsconst {resolve} = require('path'); const OfficeCovert = require('./officeCovert'); //定义需要转换的文件 const pptFile = resolve('./office/test2.pptx'); //生成pdf名称 const pdfFile = resolve('./output/test2.ppf'); //设置图片目录 const imgFile = resolve('./output/test2/tst'); const oc = new OfficeCovert(); //geneate pdf oc.file2pdf(pptFile, pdfFile).then(gFile =>{ //generate image oc.file2images(gFile, imgFile, 'test11', 'mytest', 'png').then(path=> { console.log('generate image success !!!!', path); //clean file oc.rm(gFile); }).catch((...args) => { console.log(args); }); });
简单的一个拖拽例子index.html rxjs实现拖拽 rx-drag.jsconst { fromEvent, operators: { map, takeUntil, concatAll, withLatestFrom } } = rxjs; const target = document.getElementById("drag"); const mouseDown = fromEvent(target, "mousedown"); const mouseMove = fromEvent(document, "mousemove"); const mouseUp = fromEvent(document, "mouseup"); mouseDown .pipe( map(e => { const { left, top } = e.target.getBoundingClientRect(); const clickOffsetX = e.clientX - left; const clickOffsetY = e.clientY - top; return { clickOffsetX, clickOffsetY }; }), map(({ clickOffsetX, clickOffsetY }) => { return mouseMove.pipe( takeUntil(mouseUp), map(moveEvent => ({ x: moveEvent.clientX - clickOffsetX, y: moveEvent.clientY - clickOffsetY })) ); }), concatAll() ) .subscribe(({ x, y }) => { target.style.left = `${x}px`; target.style.top = `${y}px`; });
react中在src目录下新建文件setupProxy.jsconst {createProxyMiddleware} = require('http-proxy-middleware') //代理设置 module.exports = function (app) { app.use(createProxyMiddleware("/api", { target: 'http://localhost:3300', secure: false, changeOrigin: true, pathRewrite: { "^/api": "/test" } })); }vue项目中在config/index.js的dev节点下新增代理信息 //设置代理 proxyTable: { '/api': { target: 'http://localhost:3300', secure: false, changeOrigin: true, pathRewrite: { "^/api": "/test" } } }建立专门的代理项目(egg项目举例)config.jsconst host = 'http://test.tms.loji.com'; //公共头部 var commonHeaders = { 'content-type': 'application/json', Cookie: '登录之后cookie' }; module.exports = { host, commonHeaders }home.js'use strict'; const Controller = require('egg').Controller; const {host, commonHeaders} = require('../../config'); class HomeController extends Controller { getHeaderAndData(ctx){ let data = ctx.request.body; let contentType = ctx.headers['content-type']; let headers = JSON.parse(JSON.stringify(commonHeaders)); if(!/json/.test(contentType)){ headers['content-type'] = contentType; }else{ data = JSON.stringify(data); } return {data, headers} } //匹配所有路由（作为代理处理） async allRoute(){ const {ctx} = this; const {data = null, headers} = this.getHeaderAndData(ctx); let response = await ctx.curl(host + ctx.originalUrl, { method: ctx.method, data, headers }); try{ ctx.body = JSON.parse(response.data); }catch(e){ console.error('解析json失败！！！', response); ctx.body = { status: 0, msg: '解析json错误' } } } } module.exports = HomeController;route.js'use strict'; module.exports = app => { const { router, controller } = app; router.all('*', controller.home.allRoute); }; egg配置（与代理无关）'use strict'; const path = require('path'); module.exports = appInfo => { const config = exports = {}; //跨域允许 config.cors = { origin: '*', allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH' }; //使用ejs模板 config.view = { mapping: { '.html': 'ejs', }, }; //禁止表单报错 config.security = { csrf: { enable: false, }, }; //规定静态资源路劲 config.static = { prefix:'/', dir: [path.join(appInfo.baseDir, 'app/public')] } //设置系统加密串 config.keys = appInfo.name + '_1592967337826_1169'; //使用的中间件 config.middleware = []; //自定义配置 const userConfig = { // myAppName: 'egg', }; return { ...config, ...userConfig, }; };
主要的思想：获取所有子项长度，进行叠加得出缓存使用的大小function getStorageSize(storage){ if(typeof storage !== 'object' || storage === null){ throw new Error('计算的缓存不合法'); }else{ var size = 0; for(item in storage) { if(storage.hasOwnProperty(item)) { size += storage.getItem(item).length; } } return Number((size / 1024).toFixed(2)); } } var localSize = getStorageSize(window.localStorage); console.log('localStorage当前大小', localSize, 'kb'); var sessionSize =getStorageSize(window.sessionStorage); console.log('sessionStorage当前大小', sessionSize, 'kb');
node脚本(使用git里面的提交号作为文件的版本号)给html中的js、css添加版本号，解决浏览器缓存引起的问题const fs = require("fs"); const { join, resolve } = require('path'); const execSync = require('child_process').execSync; const { mergeUrl } = require('./url.utils'); const findPath = resolve('./dist/template'); const rootPath = resolve('./'); //文件版本记录 const fileVersionNote = {}; const toComPath = path => path.replace(/\\/g, '/'); //获取版本号 const getVersion = (dir, fileName) => { let dirPathPart = toComPath(dir).split('/'); dirPathPart.pop(); let dirPath = dirPathPart.join('/'); fileName = fileName.replace(/^\s*(.*?)\s*$/, '$1'); if (!/^\//.test(fileName)) { fileName = join(dirPath, fileName).replace(rootPath, ''); }else{ fileName = 'dist' + fileName; } return getGitCommitVersion(toComPath(fileName).replace(/^\/(.*?)$/, '$1')); } //获取git文件版本号 const getGitCommitVersion = (file) => { let gv = null; if(/\?/.test(file)){ file = file.replace(/^(.*?)\?.*?$/, '$1'); } let noteV = fileVersionNote[file] if (typeof noteV !== 'string' || noteV === '') { if (fs.existsSync(file)) { try { gv = execSync(`git log ${file} | grep commit | awk 'NR==1' | awk -F ' ' '{print $2}'`); gv = gv.toString(); } catch (error) { } } if (typeof gv !== 'string' || gv.length < 10) { console.log('版本号获取错误', file); noteV = ''; }else{ noteV = gv.substr(0, 7); console.log('新文件git提交号：', file, noteV); } fileVersionNote[file] = noteV; } return noteV; } //读取所有的html function findHtmlAndAddVersion(dir) { if (fs.existsSync(dir)) { fs.stat(dir, (err, stat) => { if (err || !stat) { return console.error('读取文件状态错误', err); } else { if (stat.isFile()) { //只是读取html文件 if (/\.htm(l){0,1}$/i.test(dir)) { fs.readFile(dir, 'utf8', (err, data) => { if (err) { return console.error('读取文件错误', err); } else { let html = data.replace(/<link(.*?)href\s*=\s*([\'\"])(.*?)\2(.*?)>/gi, ($0, $1, $2, $3, $4, $5) => { if (/^\s*http[s]{0,1}:\/\//i.test($3)) { return $0; } else { return `<link${$1}href="${mergeUrl($3, { _gv: getVersion(dir, $3) })}"${$4}>`; } }).replace(/<script(.*?)src\s*=\s*([\'\"])(.*?)\2(.*?)>(.*?)<\/script>/gi, ($0, $1, $2, $3, $4, $5) => { if (/^\s*http[s]{0,1}:\/\//i.test($3)) { return $0; } else { return `<script${$1}src="${mergeUrl($3, { _gv: getVersion(dir, $3) })}"${$4}>${$5} `; } }); fs.writeFileSync(dir, html, function (err) { if (err) { return console.error('文件写入失败', err); } }); } }); } } else if (stat.isDirectory()) { fs.readdir(dir, function (err, files) { if (err || !Array.isArray(files)) { return console.error('读取文件夹错误', err); } else { files.forEach(file => { findHtmlAndAddVersion(join(dir, file)); }); } }); } else { console.error('文件类型识别错误', stat); } } }); } else { console.error('文件夹不存在！！！', dir); } } //添加版本号 findHtmlAndAddVersion(findPath);**注： 这里需要在linux或者在git
pm2概述内建负载均衡（使用Node cluster 集群模块）后台运行0秒停机重载(维护升级的时候不需要停机).具有Ubuntu和CentOS 的启动脚本停止不稳定的进程（避免无限循环）控制台检测提供 HTTP API远程控制和实时的接口API ( Nodejs 9.模块,允许和PM2进程管理器交互 )pm2命令npm install pm2 -g # 命令行安装 pm2 pm2 start app.js -i 4 #后台运行pm2，启动4个app.js # 也可以把'max' 参数传递给 start # 正确的进程数目依赖于Cpu的核心数目 pm2 start app.js --name my-api # 命名进程 pm2 list # 显示所有进程状态 pm2 monit # 监视所有进程 pm2 logs # 显示所有进程日志 pm2 stop all # 停止所有进程 pm2 restart all # 重启所有进程 pm2 reload all # 0秒停机重载进程 (用于 NETWORKED 进程) pm2 stop 0 # 停止指定的进程 pm2 restart 0 # 重启指定的进程 pm2 startup # 产生 init 脚本 保持进程活着 pm2 web # 运行健壮的 computer API endpoint (http://localhost:9615) pm2 delete 0 # 杀死指定的进程 pm2 delete all # 杀死全部进程pm2目录卸载rm /usr/local/bin/pm2 rm -r /usr/local/lib/node_modules rm -r /root/.pm2/ `` ### pm2 自启动安装pm2npm install pm2 -g保存当前进程状态pm2 save生成开机自启动服务pm2 startup启用开机自启:systemctl enable pm2-root ### pm2 运行npm命令 ### 简单用法 1. npm run dev 2. pm2 start npm -- run dev 以上使用是等效的 #### 监听并重新命名运行（--watch监听代码变化，--name 重命令任务名称，-- run后面跟脚本名字） pm2 start npm --watch --name nickname -- run dev ### 手动删除pm2日志pm2 flush### 自动删除pm2日志pm2 install pm2-logrotate // 注意是pm2 install而不是npm installpm2 set pm2-logrotate-ext:retain 7 //保存7个文件pm2 set pm2-logrotate-ext:max_size 100M //每个备份文件大小为100M
Linux 执行shell 执行npm的权限问题linux下遇到权限问题请执行： npm i --unsafe -permnode升级清除npm缓存，执行命令npm cache clean -fn模块是专门用来管理nodejs的版本，安装n模块npm install -g n更新升级node版本 n stable // 把当前系统的 Node 更新成最新的 “稳定版本” n lts // 长期支持版 n latest // 最新版 n 10.14.2 // 指定安装版本查看升级后的node版本node -vmac系统中vscode有更新权限sudo chown $USER ~/Library/Caches/com.microsoft.VSCode.ShipIt/
目的1、实现对单个状态进行监控2、多个组件的状态共享3、异步处理业务逻辑使用的类库说明1、为了实现多个组件的状态共享， 可以使用react1.6.8之后的hooks中提供的Provider或者redux2、为了实现业务异步处理，并且数据的操作与组件业务逻辑分离。1) 无法使用react1.6.8提供的hooks，虽然hooks在业务与数据混合的时候可以实现异步，但是在业务与数据分离的情况下，hooks是有心无力的。2) 使用redux的时候，有一个中间件 redux-thunk，可以进行在数据层异步处理3、为了实现状态监控1) 使用redux中subscribe进行监控，但是这是监控所有state变化的，颗粒太大了，如果需要监听一个值，那么需要层层对比。2) 使用react1.6.8之后的useEffect，可以对单个状态进行改变4、为了与react进行结合使用，需要使用到react-redux5、使用react中的hooks模式，需要使用的函数式声明，而不能使用class式声明方案决定1、使用redux + react-redux + redux-thunk + react中的hooks特性进行整合2、整合的重点在于，在监听对组件中，不能使用class声明，必须使用函数式声明3、为了使用redux，也就是说，有一定程度上，抛弃了react中hooks的一些特性，比如： useReducer、useContext等
驱动点一、传统H5（高频更新）项目中变量监控、跨浏览器监控是很有必要的二、保存的数据需要在仓库中处理之后再返回三、变量实时保存到localStorage或sessionStorage仓库核心代码如下//仓库类，设置数据、变量监控、localStorage监控，自定义钩子函数 var myStore = { _options: { error: null,//错误回调 watch: {},//监听变量的变化（数组、数字、字符串） }, //定义仓库核心 _store: function(options){ var data = { _options: { error: null,//错误回调 watch: {},//监听的对象 methods: {},//方法 initData: {},//初始化的值 static: {},//静态数据区 watchStatic: {},//监听静态变量（不要与静态区键值相同，否则出现数据丢失问题） }, _d: {},//保存运行的数据 _wd: {},//监控数据的值 _sd: {},//静态数据区（跨页面访问） //设置值 _set: function(key, value){ var self = data; if(typeof self._sd[key] !== 'undefined'){//静态数据区 self._sd[key] = value; localStorage.setItem(key, JSON.stringify(value)); }else{ self._d[key] = value; } }, //设置多个值 _sets: function(obj){ var self = data; if(typeof obj === "object" && obj !== null){ for(var i in obj) { self._set(i, obj[i]); } }else{ console.error('yexStore的sets使用方法不正确，需要一个对象做为参数'); } return self; }, //获取值 _get: function(key){ var self = data; if(typeof self._sd[key] !== 'undefined'){//静态数据区 var returnData = localStorage.getItem(key); try { returnData = JSON.parse(returnData); } catch (error) {} return returnData; }else if(typeof self._wd[key] !== 'undefined'){ return self._wd[key]; }else{ return self._d[key]; } }, //合并对象，并监听对象 _mergeOptions: function(options){ var self = this; //设置初始化数据 if(typeof options.initData === 'object' && options.initData !== null ){ Object.assign(self._d, options.initData); delete options.initData; } //静态数据区 if(typeof options.static === 'object' && options.static !== null){ Object.assign(self._sd, options.static); //读取静态数据区在sessionStore的值 var item = null;//子项 for(var i in self._sd){ item = self._sd[i]; if(typeof item === 'undefined' || item === null){ item = localStorage.getItem(i); try { self._sd[i] = JSON.parse(item); } catch (e) {} }else{ localStorage.setItem(i, item); } } delete options.static; } //处理方法合并 if(typeof options.methods === 'object' && options.methods !== null){ var item = null;//临时数据 for(var i in options.methods){ var item = options.methods[i]; if(typeof item === 'function'){ self[i] = item; } } delete options.methods; } //合并其他选项 Object.assign(self._options, options); //处理监听区数据 var watch = self._options.watch; if(typeof watch === 'object' && watch !== null){ for(var i in watch){ self._watchData(i, watch[i]); } } //启动静态监控（监听storage事件） var watchStatic = self._options.watchStatic; for(var i in watchStatic){ localStorage.removeItem(i);//移除了key才能再次触发消息 } window.addEventListener('storage', function(e){ var key = e.key; var watchStatic = self._options.watchStatic; if(watchStatic && typeof watchStatic[key] === 'function'){ var value = e.newValue; if(value !== null){ try{value = JSON.parse(value);}catch(e){}; watchStatic[key](value); } localStorage.removeItem(key);//移除了key才能再次触发消息 } }); }, _watchData: function(attribute, setFunc){ if(typeof attribute !== 'string' || attribute.replace(/\s*/g,'') === ''){ return; } var self = this; var obj = self._d; try{ self._wd[attribute] = self._d[attribute];//初始化 Object.defineProperty(obj, attribute, { set: function(newValue) { self._wd[attribute] = newValue; setFunc(newValue);//设置值监听 } }); }catch(e){ console.log('监听的值重复'); } return self; }, _initData: function(){ }, init: function(options){ var self = this; self._mergeOptions(options); self._initData(); return self; }, };//所有的数据 this.dataObj = data.init(Object.assign({}, options)); //需要对扩展方法进行外层暴露 if(typeof options.methods === 'object' && options.methods !== null){ var item = null;//临时数据 for(var i in options.methods){ var item = options.methods[i]; if(typeof item === 'function'){ this[i] = this.dataObj[i]; } } } this.set = this.dataObj._set; this.sets = this.dataObj._sets; this.get = this.dataObj._get; }, _storeObj: null,//实例化的仓库对象 //设置仓库的数据 set: function(key, value){ var self = yexStore; self._storeObj.set(key, value); return self; }, sets: function(obj){ var self = this; self._storeObj.sets(obj); return self; }, //获取仓库的数据 get: function(key){ var self = this; return self._storeObj.get(key); }, //运行方法 runFunc: function (name, options) { var self = yexStore; var func = self._storeObj[name]; if(typeof func !== 'undefined'){ func(options); }else{ var eFunc = self._options.error; if(typeof eFunc === "function"){ eFunc('“' + name + '”方法不存在！！！'); } } }, //初始化数据 _initOptions: function(options){ var self = this; //暴露库里面的方法（作为调用句柄） if(typeof options.methods === 'object' && options.methods !== null){ var item = null;//临时数据 for(var i in options.methods){ var item = options.methods[i]; if(typeof item === 'function'){ self[i] = self._storeObj[i]; } } } }, //初始化仓库 init: function(options){ var self = this; self._storeObj = new self._store(Object.assign({},options)); self._initOptions(options);//初始化配置项，需要动态对函数的名字进行定义，一定需要放在最后 return self; } };使用方式//初始化仓库 var store = myStore.init({ initData: { sysRunStartTime: 0,//系统开始运行时间 classId: 0,//使用的班级id lessonId: 1,//使用的课程id pageIndex: null//页面记录 }, watch: { //页面索引（0首页、1点评页、2作业布置页、3授课、4点名页） pageIndex: function (index) { var self = store; console.log('监听到页面变化', pageIndex); } }, watchStatic: { pageChange: function(value){ console.log('监听到localStroage变化', value); } }, static: { token: null, host: config.host,//配置请求的地址 //静态区监控对象-不是为了存储数据而是为了监控数据（指明存储类型为静态） pageChange: null }, methods: { //添加页面变化监控 addPageListen: function (callback) { var self = this; var name = 'pageListenList'; if (typeof self[name] !== 'object' || !Array.isArray(self[name])) { self[name] = []; } self[name].push(callback); return self; } } });触发点 //触发localStrage监控（特定版本有效-比如： chrome 69） store.set('pageChange', 1); //触发变量监听(通用) store.set('pageIndex', 1);