kan进博客 - 美妙前端
type: channel
link: https://www.kjwoo.cn/category/beautifulFrontEnd.html
items: office转图片或pdf（简易方案）
items: 函数式编程
items: 前端使用代理
items: js计算本地缓存
items: 省份、城市、区/县 关系
items: html中资源文件添加版本号
items: pm2使用
items: 前端项目（nodejs部署Centos7）
items: React 单个数据监控
items: 仓库类封装

office转图片或pdf（简易方案）
type: item
date: 2020-10-11T09:43:56+00:00
description: 安装环境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); }); });

函数式编程
type: item
date: 2020-10-11T09:40:00+00:00
description: 简单的一个拖拽例子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`; });

前端使用代理
type: item
date: 2020-10-11T09:21:19+00:00
description: 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…

js计算本地缓存
type: item
date: 2020-09-28T06:39:00+00:00
description: 主要的思想：获取所有子项长度，进行叠加得出缓存使用的大小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');

省份、城市、区/县 关系
type: item
date: 2020-09-17T07:42:41+00:00
description: export const province = [{ value: 110000, text: '北京', parentId: 0 }, { value: 120000, text: '天津', parentId: 0 }, { value: 130000, text: '河北省', parentId: 0 }, { value: 140000, text: '山西省', parentId: 0 }, { value: 150000, text: '内蒙古自治区', parentId: 0 }, { value: 210000, text: '辽宁省', parentId: 0 }, { value: 220000, text: '吉林省', parentId: 0 }, { value: 230000, text: '黑龙江省', parentId: 0 }, { value: 310000, text: '上海', parentId: 0 }, { value: 320000, text: '江苏省', parentId: 0 }, { value: 330000, text: '浙江省', parentId: 0 }, { value: 340000, text: '安徽省', parentId: 0 }, { value: 350000, text: '福建省', parentId: 0 }, { value: 360000, text: '江西省', parentId: 0 }, { value: 370000, text: '山东省', parentId: 0 }, { value: 410000, text: '河南省', parentId: 0 }, { value: 420000, text: '湖北省', parentId: 0 }, { value: 430000, text: '湖南省', parentId: 0 }, { value: 440000, text: '广东省', parentId: 0 }, { value: 450000, text: '广西壮族自治区', parentId: 0 }, { value: 460000, text: '海南省', parentId: 0 }, { value: 500000, text: '重庆', parentId: 0 }, { value: 510000, text: '四川省', parentId: 0 }, { value: 520000, text: '贵州省', parentId: 0 }, { value: 530000, text: '云南省', parentId: 0 }, { value: 540000, text: '西藏自治区', parentId: 0 }, { value: 610000, text: '陕西省', parentId: 0 }, { value: 620000, text: '甘肃省', parentId: 0 }, { value: 630000, text: '青海省', parentId: 0 }, { value: 640000, text: '宁夏回族自治区', parentId: 0 }, { value: 650000, text: '新疆维吾尔自治区', parentId: 0 }, { value: 710000, text: '台湾省', parentId: 0 }, { value: 810000, text: '香港特别行政区', parentId: 0 }, { value: 820000, text: '澳门特别行政区', parentId: 0 }]; export const city = [{ value: 110100, text: '北京市', parentId: 110000 }, { value: 120100, text: '天津市', parentId: 120000 }, { value: 130100, text: '石家庄市', parentId: 130000 }, { value: 130200, text: '唐山市', parentId: 130000 }, { value: 130300, text: '秦皇岛市', parentId: 130000 }, { value: 130400, text: '邯郸市', parentId: 130000 }, { value: 130500, text: '邢台市', parentId: 130000 }, { value: 130600, text: '保定市', parentId: 130000 }…

html中资源文件添加版本号
type: item
date: 2020-09-14T08:02:00+00:00
description: 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(.*?)sr…

pm2使用
type: item
date: 2020-09-13T00:34:00+00:00
description: 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

前端项目（nodejs部署Centos7）
type: item
date: 2020-06-12T23:18:00+00:00
description: 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/

React 单个数据监控
type: item
date: 2019-11-18T00:08:59+00:00
description: 目的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等

仓库类封装
type: item
date: 2019-10-25T07:56:00+00:00
description: 驱动点一、传统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.metho…