分类 美妙前端 下的文章

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 自启动

安装pm2

npm 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 install
pm2 set pm2-logrotate-ext:retain 7 //保存7个文件
pm2 set pm2-logrotate-ext:max_size 100M //每个备份文件大小为100M

Linux 执行shell 执行npm的权限问题

linux下遇到权限问题请执行: npm i --unsafe -perm

node升级

清除npm缓存,执行命令

npm cache clean -f

n模块是专门用来管理nodejs的版本,安装n模块

npm install -g n

更新升级node版本

 n stable // 把当前系统的 Node 更新成最新的 “稳定版本”
 n lts // 长期支持版
 n latest // 最新版
 n 10.14.2 // 指定安装版本

查看升级后的node版本

node -v

mac系统中vscode有更新权限

sudo chown $USER ~/Library/Caches/com.microsoft.VSCode.ShipIt/

目的

1、实现对单个状态进行监控
2、多个组件的状态共享
3、异步处理业务逻辑

使用的类库说明

1、为了实现多个组件的状态共享, 可以使用react1.6.8之后的hooks中提供的Provider或者redux
2、为了实现业务异步处理,并且数据的操作与组件业务逻辑分离。

1) 无法使用react1.6.8提供的hooks,虽然hooks在业务与数据混合的时候可以实现异步,但是在业务与数据分离的情况下,hooks是有心无力的。
2) 使用redux的时候,有一个中间件 redux-thunk,可以进行在数据层异步处理

3、为了实现状态监控

1) 使用redux中subscribe进行监控,但是这是监控所有state变化的,颗粒太大了,如果需要监听一个值,那么需要层层对比。
2) 使用react1.6.8之后的useEffect,可以对单个状态进行改变

4、为了与react进行结合使用,需要使用到react-redux
5、使用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);

概述

从PPT在线编辑1.0版本逐步过渡到2.0版本,其中经历版本如下:

一、1.1动画版本
二、1.2形状和组合版本
三、1.3 GGB与支持其他资源插入PPT版本
四、2.0自研渲染引擎版本。 

目标

一、1.1动画版本

1、实现动画可以编辑
2、支持插入动画
注:支持大部分基本动画

二、1.2形状和组合版本

1、实现形状可以编辑
2、组合操作插入
3、背景修改与设置

三、1.3 GGB与支持其他资源插入PPT版本

1、GGB插入PPT
2、题目优化
3、其他资源支持

四、2.0 自研PPT渲染引擎版本

1、使用SPR数据使用自研PPT渲染引擎展示
2、支持在线编辑PPT
3、支持创建PPT
4、完全翻译SPR展示

大体工作计划(时间颗粒为周)

一、1.1动画版本(5周开发)

1)SPR动画结构研究与标准动画数据结构规划
2)SVG编辑器开发与标准动画数据结构确定
3)标准动画数据结构与SPR渲染引擎交互(修改SPR中动画)
4)添加动画交互制作(基本一个动画一天)
5)动画属性栏优化、API联调、课堂端联调、代码优化、压缩

二、1.2形状和组合版本(2周开发)

1)SVG形状开发,插入与修改
2)组合(对象)模式设计,快捷键支持,组合属性栏调整

三、1.3 GGB与支持其他资源插入PPT版本(2周开发)

1)GGB插入PPT模式、GGB属性栏与交互
2)题库优化、GGB动画支持与其他资源插入支持,快捷键支持、性能优化

四、2.0 自研PPT渲染引擎版本(4周开发)

1)架构选型、调试模式建立、工具类封装
2)自研PPT渲染引擎开发
3)SPR操作迁移到自研
4)快捷键支持、交互优化、代码优化、打包,渲染端(课堂端)兼容多种模式

估计前端开发总时长:5 + 2 + 2 + 4 = 13周

Node环境

npm 命令

配置淘宝镜像: npm config set registry https://registry.npm.taobao.org

yarn 命令

配置淘宝镜像:yarn config set registry http://registry.npm.taobao.org/

webpack4

使用webpack4的时候,需要一起安装webpack-cli

webpack-dev-server

启动一个web服务进行开发

这是npm下载包的目录

npm config set cache "D:DevelopNPMCache"
npm config set prefix "D:DevelopNPMPrefix"
npm install -g cnpm --registry=https://registry.npm.taobao.org

设置全局包命令环境变量:D:DevelopNPMPrefix