Evans

Evans 查看完整档案

杭州编辑陕西科技大学  |  Bioengineer 编辑  |  填写所在公司/组织 blog.8god.me 编辑
编辑

热爱IT技术开发的码农技术控

个人动态

Evans 收藏了文章 · 10月13日

给你的mongodb设置密码吧!

mongodb安装后是无需密码

Mongodb安装后自身是没有密码的,用户连接只需填写id地址,端口号,数据库名称即可

安全问题

只要你服务器的mongodb数据库端口开放,任何人的电脑都可以连接到你的数据库,操作修改你的mongodb数据,本人以前就遭受过入侵,深有感触。如图:黑客盗取你的数据库,然后留下一个邮箱和账号,要求你给比特币才肯归还数据库给你。。。
图片描述

给mongodb加密

如果需要给MongoDB数据库使用安全验证,则需要用--auth开启安全性检查,只有数据库认证的用户才能执行读写操作,开户安全性检查。
第一步:开机先:mongod --dbpath 存放数据库文件夹路径
图片描述

第二步:打开命令行窗口输入mongo,进入mongo环境
图片描述

第三步:切换到 'admin' 数据库 use admin
图片描述

第四步:给admin设置用户密码:
user: 用户名, pwd: 用户密码,roles: 用来设置用户的权限,比如读,读写 等等
db.createUser({user: 'root', pwd: '123456', roles: ['root']})
图片描述
验证是否添加成功,'db.auth(用户名,用户密码)' 这里用db.auth('root', '123456') 如果返回 '1'表示验证成功, 如果是 '0' 表示验证失败...

第5步:刚才是给root设置密码,现在要给特定的每个库设置权限,比如我这里有一个库,库名字叫做Article,这里以Article这个库为例
图片描述

切换到Article数据库,use Article
图片描述

接下来为这个库添加一个用户,并且赋予权限,db.createUser({user:'zwVic',pwd:'adgjmp123',roles: [{role:'readWrite',db:'Article'}]})})
这行代码意思是 创建一个zwStar用户 给予读写权限 db表示该用户操作的数据库名
图片描述
图片描述

OK,一切搞定,重新开机mongodb,MongoDB默认是没有开启访问控制,我们通过--auth参数重启mongod服务。mongod --dbpath 存放数据库文件夹路径 --auth一旦开启了,用户连接mongod必须指定用户名和密码。
图片描述

连接加密数据库

xxx.db('mongodb://your name: your pwd@ ip :27017/Article');
your name:为用户名
your pwd:为密码

总结

通过加密后。连接数据库就需要账号,密码,同时阿里云或者腾讯云上也可以给服务器设置安全组增加安全性,比如27107这个端口只授权给自己访问等等....
文中有什么不对的,希望大家指正哈!
如果觉得本文对你有所帮助,就star一下吧~

查看原文

Evans 收藏了文章 · 10月9日

如何给localStorage设置一个过期时间?

如何给localStorage设置一个有效期

引言

​  这个话题其实在上次分享<小程序填坑记里讲过了>已经讲过(大佬可绕过哦~),但后来群里/评论都有些同学,提到了一些疑问,问能否单独整理一篇更为详细的分享,讲解一下细节和完善提到的不足,如是有了下文👇。 —— 「 用心分享 做有温度的攻城狮,我是首席填坑官——苏南

各位大佬早安,这里是@IT·平头哥联盟,我是首席填坑官∙苏南,用心分享 做有温度的攻城狮。
公众号:honeyBadger8,群:912594095

思考点

​  从我们接触前端起,第一个熟悉的存储相关的Cookie或者来分析我们生活中密切相关的淘宝、物流、闹钟等事物来说起吧,

  • Cookie从你设置的时候,就会给个时间,不设置默认会话结束就过期;
  • 淘宝购物 从你下单付款起,就会给这件货物设置一个收货期限时间,过了这个时间自动认为你收货(即订单结束);
  • 闹钟 你设置的提醒时间,其实也就是它的过期时间;
  • 再比如与您每天切身相关的产品需求,过完需求,你给出的上线时间,也就是这个需求的过期时间;
  • 再通俗点讲,您今年的生日过完到明年生日之间也是相当于设置了有效期时间;
以上种种,我们能得出一个结论任何一件事、一个行为动作,都有一个时间、一个节点,甚至我们可以黑localStorage,就是一个完善的API,为什么不能给一个设置过期的机制,因为sessionStorageCookie并不能满足我们实际的需求。

实现思路

  抱歉,黑localStorage不完善,有点夸张了,综合上述的总结,问题就简单了,给localStorage一个过期时间,一切就都so easy ?到底是不是,来看看具体的实现吧:

简单回顾

//示例一:
localStorage.setItem('test',1234567);
let test = localStorage.getItem('test');
console.log(typeof test, test); 

//示例二:
localStorage['name'] = '苏南';
console.log(localStorage['name']);
/*
输出:
"1234567" ,'苏南',
这里要注意,1234567 存进去时是number 取出来就成string了
*/

重写 set(存入) 方法:

  • 首先有三个参数 key、value、expired ,分别对应 键、值、过期时间,
  • 过期时间的单位可以自由发挥,小时、分钟、天都可以,
  • 注意点:存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify
  • 这个时间如何设置呢?在这个值存入的时候在键(key)的基础上扩展一个字段,如:key+'_expires_',而它的值为当前 时间戳 + expired过期时间
  • 具体来看一下代码

set(key, value, expired) {
    /*
    * set 存储方法
    * @ param {String}     key 键
    * @ param {String}     value 值,
    * @ param {String}     expired 过期时间,以分钟为单位,非必须
    * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095
    */
    let source = this.source;
    source[key] = JSON.stringify(value);
    if (expired){
        source[`${key}__expires__`] = Date.now() + 1000*60*expired
    };
    return value;
}

重写 get(获取) 方法:

  • 获取数据时,先判断之前存储的时间有效期,与当前的时间进行对比;
  • 但存储时expired为非必须参数,所以默认为当前时间+1,即长期有效;
  • 如果存储时有设置过期时间,且在获取的时候发现已经小于当前时间戳,则执行删除操作,并返回空值;
  • 注意点:存储的值可能是数组/对象,取出后不能直接返回,需要转换 JSON.parse
  • 具体来看一下代码
get(key) {
    /*
    * get 获取方法
    * @ param {String}     key 键
    * @ param {String}     expired 存储时为非必须字段,所以有可能取不到,默认为 Date.now+1
    * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095
    */
    const source = this.source,
    expired = source[`${key}__expires__`]||Date.now+1;
    const now = Date.now();

    if ( now >= expired ) {
        this.remove(key);
        return;
    }
    const value = source[key] ? JSON.parse(source[key]) : source[key];
    return value;
}

重写 remove(删除) 方法:

  • 删除操作就简单了,;

remove(key) {
    const data = this.source,
        value = data[key]; //首席填坑官∙苏南的专栏
    delete data[key];
    delete data[`${key}__expires__`];
    return value;
}

优化点:

  • 记得上次有个同学,是这么评论的:「 删除缓存能放到constructor里面执行么,放到get里面 不取就一直在那是不是不太好?」;
  • 所以本次优化做一个初始化删除操作,清除已经过期的数据;
  • 为什么不用for in而是 for ? for in循环遍历对象的属性时,原型链上的所有属性都将被访问,解决方案:使用hasOwnProperty方法过滤或Object.keys会返回自身可枚举属性组成的数组;

class storage {

    constructor(props) {
        this.props = props || {}
        this.source = this.props.source || window.localStorage
        this.initRun();
    }
    initRun(){
        /*
        * set 存储方法
        * @ param {String}     key 键
        * @ param {String}     value 值,存储的值可能是数组/对象,不能直接存储,需要转换 JSON.stringify
        * @ param {String}     expired 过期时间,以分钟为单位
        * @ 由@IT·平头哥联盟-首席填坑官∙苏南 分享,交流:912594095
        */
        const reg = new RegExp("__expires__");
        let data = this.source;
        let list = Object.keys(data);
        if(list.length > 0){
            list.map((key,v)=>{
                if( !reg.test(key )){
                    let now = Date.now();
                    let expires = data[`${key}__expires__`]||Date.now+1;
                    if (now >= expires ) {
                        this.remove(key);
                    };
                };
                return key;
            });
        };
    }
}

总结:

  以上就是今天为大家总结的分享,您GET到了吗?小程序、sessionStorage、localStorage,都适用,做些许调整即可哦,希望今天的分享能给您带来些许成长,如果觉得不错,记得关注下方公众号哦,每周第一时间为您推最新分享👇👇。

专注于前端、测试领域的分享,欢迎关注我们,宝剑锋从磨砺出,梅花香自苦寒来,做有温度的攻城狮!,公众号:honeyBadger8

更多文章:

作者:苏南 - 首席填坑官
链接:https://blog.csdn.net/weixin_...
交流:912594095,公众号:honeyBadger8
本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。
查看原文

Evans 收藏了文章 · 9月29日

Promise 中的三兄弟 .all(), .race(), .allSettled()

阿里云最近在做活动,低至2折,有兴趣可以看看:
https://promotion.aliyun.com/...

为了保证的可读性,本文采用意译而非直译。

从ES6 开始,我们大都使用的是 Promise.all()Promise.race()Promise.allSettled() 提案已经到第4阶段,因此将会成为ECMAScript 2020的一部分。

1.概述

Promise.all<T>(promises: Iterable<Promise<T>>): Promise<Array<T>>

  • Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果

Promise.race<T>(promises: Iterable<Promise<T>>): Promise<T>

  • Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

Promise.allSettled<T>(promises: Iterable<Promise<T>>): Promise<Array<SettlementObject<T>>>

  • Promise.allSettled()方法返回一个promise,该promise在所有给定的promise已被解析或被拒绝后解析,并且每个对象都描述每个promise的结果。

回顾: Promise 状态

给定一个返回Promise的异步操作,以下这些是Promise的可能状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 意味着操作成功完成。
  • rejected: 意味着操作失败。
  • Settled: Promise要么被完成,要么被拒绝。Promise一旦达成,它的状态就不再改变。

clipboard.png

3.什么是组合

又称部分-整体模式,将对象整合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性,它基于两种函数:

  • 基元函数(简短:基元)创建原子块。
  • 组合函数(简称:组合)将原子和/或复合件组合在一起以形成复合件。

对于 JS 的 Promises 来说

  • 基元函数包括:Promise.resolve()Promise.reject()
  • 组合函数:Promise.all(), Promise.race(), Promise.allSettled()

4. Promise.all()

Promise.all()的类型签名:

  • Promise.all<T>(promises: Iterable<Promise<T>>): Promise<Array<T>>

返回情况:

完成(Fulfillment):
如果传入的可迭代对象为空,Promise.all 会同步地返回一个已完成(resolved)状态的promise
如果所有传入的 promise 都变为完成状态,或者传入的可迭代对象内没有 promisePromise.all 返回的 promise 异步地变为完成。
在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)。

失败/拒绝(Rejection):
如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成。

来个例子:

const promises = [
  Promise.resolve('a'),
  Promise.resolve('b'),
  Promise.resolve('c'),
];
Promise.all(promises)
  .then((arr) => assert.deepEqual(
    arr, ['a', 'b', 'c']
  ));

如果其中的一个 promise 被拒绝,那么又是什么情况:

const promises = [
  Promise.resolve('a'),
  Promise.resolve('b'),
  Promise.reject('ERROR'),
];
Promise.all(promises)
  .catch((err) => assert.equal(
    err, 'ERROR'
  ));

下图说明Promise.all()是如何工作的

这需要有数字电路的知识才能看得懂哦

4.1 异步 .map() 与 Promise.all()

数组转换方法,如.map().filter()等,用于同步计算。例如

function timesTwoSync(x) {
  return 2 * x;
}
const arr = [1, 2, 3];
const result = arr.map(timesTwoSync);
assert.deepEqual(result, [2, 4, 6]);

如果.map()的回调是基于Promise的函数会发生什么? 使用这种方式 .map()返回的的结果是一个Promises数组。

Promises数组不是普通代码可以使用的数据,但我们可以通过Promise.all()来解决这个问题:它将Promises数组转换为Promise,并使用一组普通值数组来实现。

function timesTwoAsync(x) {
  return new Promise(resolve => resolve(x * 2));
}
const arr = [1, 2, 3];
const promiseArr = arr.map(timesTwoAsync);
Promise.all(promiseArr)
  .then(result => {
    assert.deepEqual(result, [2, 4, 6]);
  });

更实际工作上关于 .map()示例

接下来,咱们使用.map()Promise.all()Web下载文件。 首先,咱们需要以下帮助函数:

function downloadText(url) {
  return fetch(url)
    .then((response) => { // (A)
      if (!response.ok) { // (B)
        throw new Error(response.statusText);
      }
      return response.text(); // (C)
    });
}

downloadText()使用基于Promise的fetch API 以字符串流的方式下载文件:

  • 首先,它异步检索响应(第A行)。
  • response.ok(B行)检查是否存在“找不到文件”等错误。
  • 如果没有错误,使用.text()(第C行)以字符串的形式取回文件的内容。

在下面的示例中,咱们 下载了两个文件

const urls = [
  'http://example.com/first.txt',
  'http://example.com/second.txt',
];

const promises = urls.map(
  url => downloadText(url));

Promise.all(promises)
  .then(
    (arr) => assert.deepEqual(
      arr, ['First!', 'Second!']
    ));

Promise.all()的一个简版实现

function all(iterable) {
  return new Promise((resolve, reject) => {
    let index = 0;
    for (const promise of iterable) {
      // Capture the current value of `index`
      const currentIndex = index;
      promise.then(
        (value) => {
          if (anErrorOccurred) return;
          result[currentIndex] = value;
          elementCount++;
          if (elementCount === result.length) {
            resolve(result);
          }
        },
        (err) => {
          if (anErrorOccurred) return;
          anErrorOccurred = true;
          reject(err);
        });
      index++;
    }
    if (index === 0) {
      resolve([]);
      return;
    }
    let elementCount = 0;
    let anErrorOccurred = false;
    const result = new Array(index);
  });
}

5. Promise.race()

Promise.race()方法的定义:

Promise.race<T>(promises: Iterable<Promise<T>>): Promise<T>

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。来几个例子,瞧瞧:

const promises = [
  new Promise((resolve, reject) =>
    setTimeout(() => resolve('result'), 100)), // (A)
  new Promise((resolve, reject) =>
    setTimeout(() => reject('ERROR'), 200)), // (B)
];
Promise.race(promises)
  .then((result) => assert.equal( // (C)
    result, 'result'));

在第 A 行,Promise 是完成状态 ,所以 第 C 行会执行(尽管第 B 行被拒绝)。

如果 Promise 被拒绝首先执行,在来看看情况是嘛样的:

const promises = [
  new Promise((resolve, reject) =>
    setTimeout(() => resolve('result'), 200)),
  new Promise((resolve, reject) =>
    setTimeout(() => reject('ERROR'), 100)),
];
Promise.race(promises)
  .then(
    (result) => assert.fail(),
    (err) => assert.equal(
      err, 'ERROR'));

注意,由于 Promse 先被拒绝,所以 Promise.race() 返回的是一个被拒绝的 Promise

这意味着Promise.race([])的结果永远不会完成。

下图演示了Promise.race()的工作原理:

这需要有数字电路的知识才能看得懂哦

Promise.race() 在 Promise 超时下的情况

在本节中,我们将使用Promise.race()来处理超时的 Promise。 以下辅助函数:

function resolveAfter(ms, value=undefined) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(value), ms);
  });
}

resolveAfter() 主要做的是在指定的时间内,返回一个状态为 resolvePromise,值为为传入的 value

调用上面方法:

function timeout(timeoutInMs, promise) {
  return Promise.race([
    promise,
    resolveAfter(timeoutInMs,
      Promise.reject(new Error('Operation timed out'))),
  ]);
}

timeout() 返回一个Promise,该 Promise 的状态取决于传入 promise 状态 。

其中 timeout 函数中的 resolveAfter(timeoutInMs, Promise.reject(new Error('Operation timed out')) ,通过 resolveAfter 定义可知,该结果返回的是一个被拒绝状态的 Promise

再来看看timeout(timeoutInMs, promise)的运行情况。如果传入promise在指定的时间之前状态为完成时,timeout 返回结果就是一个完成状态的 Promise,可以通过.then的第一个回调参数处理返回的结果。

timeout(200, resolveAfter(100, 'Result!'))
  .then(result => assert.equal(result, 'Result!'));

相反,如果是在指定的时间之后完成,刚 timeout 返回结果就是一个拒绝状态的 Promise,从而触发catch方法指定的回调函数。

timeout(100, resolveAfter(2000, 'Result!'))
  .catch(err => assert.deepEqual(err, new Error('Operation timed out')));

重要的是要了解“Promise 超时”的真正含义:

  1. 如果传入入Promise 较到的得到解决,其结果就会给返回的 Promise
  2. 如果没有足够快得到解决,输出的 Promise 的状态为拒绝。

也就是说,超时只会阻止传入的Promise,影响输出 Promise(因为Promise只能解决一次), 但它并没有阻止传入Promise的异步操作。

5.2 Promise.race() 的一个简版实现

以下是 Promise.race()的一个简化实现(它不执行安全检查)

function race(iterable) {
  return new Promise((resolve, reject) => {
    for (const promise of iterable) {
      promise.then(
        (value) => {
          if (settlementOccurred) return;
          settlementOccurred = true;
          resolve(value);
        },
        (err) => {
          if (settlementOccurred) return;
          settlementOccurred = true;
          reject(err);
        });
    }
    let settlementOccurred = false;
  });
}

6.Promise.allSettled()

“Promise.allSettled”这一特性是由Jason WilliamsRobert PamelyMathias Bynens提出。

promise.allsettle()方法的定义:

  • Promise.allSettled<T>(promises: Iterable<Promise<T>>)
    : Promise<Array<SettlementObject<T>>>

它返回一个ArrayPromise,其元素具有以下类型特征:

type SettlementObject<T> = FulfillmentObject<T> | RejectionObject;

interface FulfillmentObject<T> {
  status: 'fulfilled';
  value: T;
}

interface RejectionObject {
  status: 'rejected';
  reason: unknown;
}

Promise.allSettled()方法返回一个promise,该promise在所有给定的promise已被解析或被拒绝后解析,并且每个对象都描述每个promise的结果。

举例说明, 比如各位用户在页面上面同时填了3个独立的表单, 这三个表单分三个接口提交到后端, 三个接口独立, 没有顺序依赖, 这个时候我们需要等到请求全部完成后给与用户提示表单提交的情况

在多个promise同时进行时咱们很快会想到使用Promise.all来进行包装, 但是由于Promise.all的短路特性, 三个提交中若前面任意一个提交失败, 则后面的表单也不会进行提交了, 这就与咱们需求不符合.

Promise.allSettledPromise.all类似, 其参数接受一个Promise的数组, 返回一个新的Promise, 唯一的不同在于, 其不会进行短路, 也就是说当Promise全部处理完成后我们可以拿到每个Promise的状态, 而不管其是否处理成功.

下图说明promise.allsettle()是如何工作的

这需要有数字电路的知识才能看得懂哦

6.1 Promise.allSettled() 例子

这是Promise.allSettled() 使用方式快速演示示例

Promise.allSettled([
  Promise.resolve('a'),
  Promise.reject('b'),
])
.then(arr => assert.deepEqual(arr, [
  { status: 'fulfilled', value:  'a' },
  { status: 'rejected',  reason: 'b' },
]));

6.2 Promise.allSettled() 较复杂点的例子

这个示例类似于.map()Promise.all()示例(我们从其中借用了downloadText()函数):我们下载多个文本文件,这些文件的url存储在一个数组中。但是,这一次,咱们不希望在出现错误时停止,而是希望继续执行。Promise.allSettled()允许咱们这样做:

const urls = [
  'http://example.com/exists.txt',
  'http://example.com/missing.txt',
];

const result = Promise.allSettled(
  urls.map(u => downloadText(u)));
result.then(
  arr => assert.deepEqual(
    arr,
    [
      {
        status: 'fulfilled',
        value: 'Hello!',
      },
      {
        status: 'rejected',
        reason: new Error('Not Found'),
      },
    ]
));

6.3 Promise.allSettled() 的简化实现

这是promise.allsettle()的简化实现(不执行安全检查)

function allSettled(iterable) {
  return new Promise((resolve, reject) => {
    function addElementToResult(i, elem) {
      result[i] = elem;
      elementCount++;
      if (elementCount === result.length) {
        resolve(result);
      }
    }

    let index = 0;
    for (const promise of iterable) {
      // Capture the current value of `index`
      const currentIndex = index;
      promise.then(
        (value) => addElementToResult(
          currentIndex, {
            status: 'fulfilled',
            value
          }),
        (reason) => addElementToResult(
          currentIndex, {
            status: 'rejected',
            reason
          }));
      index++;
    }
    if (index === 0) {
      resolve([]);
      return;
    }
    let elementCount = 0;
    const result = new Array(index);
  });
}

7. 短路特性

Promise.all()romise.race() 都具有 短路特性

  • Promise.all(): 如果参数中 promise 有一个失败(rejected),此实例回调失败(reject)

Promise.race():如果参数中某个promise解决或拒绝,返回的 promise就会解决或拒绝。

8.并发性和 Promise.all()

8.1 顺序执行与并发执行

考虑下面的代码:

asyncFunc1()
  .then(result1 => {
    assert.equal(result1, 'one');
    return asyncFunc2();
  })
  .then(result2 => {
    assert.equal(result2, 'two');
  });

使用.then()顺序执行基于Promise的函数:只有在 asyncFunc1()的结果被解决后才会执行asyncFunc2()

Promise.all() 是并发执行的

Promise.all([asyncFunc1(), asyncFunc2()])
  .then(arr => {
    assert.deepEqual(arr, ['one', 'two']);
  });

9.2 并发技巧:关注操作何时开始

确定并发异步代码的技巧:关注异步操作何时启动,而不是如何处理它们的Promises

例如,下面的每个函数都同时执行asyncFunc1()asyncFunc2(),因为它们几乎同时启动。

function concurrentAll() {
  return Promise.all([asyncFunc1(), asyncFunc2()]);
}

function concurrentThen() {
  const p1 = asyncFunc1();
  const p2 = asyncFunc2();
  return p1.then(r1 => p2.then(r2 => [r1, r2]));
}

另一方面,以下两个函数依次执行asyncFunc1()asyncFunc2(): asyncFunc2()仅在asyncFunc1()的解决之后才调用。

function sequentialThen() {
  return asyncFunc1()
    .then(r1 => asyncFunc2()
      .then(r2 => [r1, r2]));
}

function sequentialAll() {
  const p1 = asyncFunc1();
  const p2 = p1.then(() => asyncFunc2());
  return Promise.all([p1, p2]);
}

9.3 Promise.all() 与 Fork-Join 分治编程

Promise.all() 与并发模式“fork join”松散相关。重温一下咱们前面的一个例子:

Promise.all([
    // (A) fork
    downloadText('http://example.com/first.txt'),
    downloadText('http://example.com/second.txt'),
  ])
  // (B) join
  .then(
    (arr) => assert.deepEqual(
      arr, ['First!', 'Second!']
    ));
  • Fork:在A行中,分割两个异步任务并同时执行它们。
  • Join:在B行中,对每个小任务得到的结果进行汇总。

代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

交流

干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。

https://github.com/qq44924588...

我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,即可看到福利,你懂的。

clipboard.png

查看原文

Evans 关注了专栏 · 9月29日

终身学习者

我要先坚持分享20年,大家来一起见证吧。

关注 40749

Evans 收藏了文章 · 7月16日

Vue组件通信中eventBus的使用

在vue1.0中,组件之间的通信主要通过vm.$dispatch沿着父链向上传播和用vm.$broadcast向下广播来实现。然而在vue2.0中,已经废除了这种用法。

vuex加入后,对组件之间的通信有了更加清晰的操作,对于中大型的项目来说,一开始就把vuex的使用计划在内是明智的选择。

然而在一些小型的项目,或者说像我这样写到一半才发现vue2.0用不了$.broadcast和$dispatch的人来说,就需要一个比较便捷的解决方法。那么,eventBus的作用就体现出来了。

主要是现实途径是在要相互通信的兄弟组件之中,都引入一个新的vue实例,然后通过分别调用这个实例的事件触发和监听来实现通信和参数传递。

这里来看一个简单的例子:

比如,我们这里有三个组件,main.vue、click.vue、show.vue。click和show是父组件main下的兄弟组件,而且click是通过v-for在父组件中遍历在了多个列表项中。这里要实现,click组件中触发点击事件后,由show组件将点击的是哪个dom元素console出来。

首先,我们给click组件添加点击事件

<div class="click" @click.stop.prevent="doClick($event)"></div>  

想要在doClick()方法中,实现对show组件的通信,我们需要新建一个js文件,来创建出我们的eventBus,我们把它命名为bus.js

import Vue from 'vue';  
export default new Vue(); 

这样我们就创建了一个新的vue实例。接下来我们在click组件和show组件中import它。

import Bus from 'common/js/bus.js';  

接下来,我们在doClick方法中,来触发一个事件:

methods: {  
   addCart(event) {  
   Bus.$emit('getTarget', event.target);   
   }  
}  

这里我们在click组件中每次点击,都会在bus中触发这个名为'getTarget'的事件,并将点击事件的event.target顺着事件传递出去。

接着,我们要在show组件中的created()钩子中调用bus监听这个事件,并接收参数:

created() {  
        Bus.$on('getTarget', target => {  
            console.log(target);  
        });  
}  

这样,在每次click组件的点击事件中,就会把event.target传递到show中,并console出来。

所以eventBus的使用还是非常便捷的,但是如果是中大型项目,通信比较复杂,还是建议大家直接使用vuex。

来看一个实际例子:

我们创建了一个selection.vue的下拉框组件,在layout.vue组件中使用了selection.vue组件,我们要实现点击layout.vue组件页面的任意一处(除下拉框组件本身外),都可以将下拉框收起来。首先,新建了一个eventBus.js文件,在里面新建了一个vue的实例赋值给const eventBus,在selection.vue和layout.vue中分别import eventBus from '../../eventBus'和import eventBus from '../eventBus',则eventBus对于selection.vue和layout.vue就是一个全局的vue实例对象,然后通过分别调用eventBus这个实例的事件触发$emit和事件监听$on来实现通信和参数传递。图6,是为了在一个页面中,把selection.vue使用了至少两次,则我们点击任意一个selection.vue的同时,要把其它的selection.vue给收起来,如图7。

图1:
clipboard.png

图2:
clipboard.png

图3:
clipboard.png

图4:
clipboard.png

图5:
clipboard.png

图6:
clipboard.png

图7:
clipboard.png

查看原文

Evans 收藏了文章 · 4月7日

(详解)从浏览器输入 URL 到页面展示过程发生了什么?

引言

对于面试常问的从浏览器输入 URL 到页面展示过程发生了什么?,我想大家都或多或少能说出一二。但是,其实这个问题很有深度,而你是否回答的有深度,在很大程度上会影响到面试官对你的印象。

并且,网上各种资料都是浅尝辄止地讲解这个过程,经常会出现今天看到这个版本,明天看到另一个版本地情况。所以,这次我们就来深入浅出一下这整个过程~

一、Chrome 多进程架构

首先,在开始讲解整个过程前,我们需要认识一下 Chrome 多进程架构。因为,从浏览器输入 URL 到页面渲染的整个过程都是由 Chrome 架构中的各个进程之间的配合完成。

Chrome 的多进程架构:

  • 浏览器进程,它负责用户界面(地址栏、菜单等等)、子进程的管理(例如,进程间通信和数据传递)、存储等等
  • 渲染进程,它负责将接收到的 HTML 文档和 JavaScript 等转化为用户界面
  • 网络进程,它负责网络资源的请求,例如 HTTP请求、WebSocket 模块
  • GPU(图形处理器)进程,它负责对 UI 界面的展示
  • 插件进程,它负责对插件的管理

二、过程详解

2.1 解析输入

发生这个过程的前提,用户在地址栏中输入了 URL,而地址栏会根据用户输入,做出如下判断:

  • 输入的是非 URL 结构的字符串,则会用浏览器默认的搜索引擎搜索该字符串
  • 输入的是 URL 结构字符串,则会构建完整的 URL 结构,浏览器进程会将完整的 URL 通过进程间通信,即 IPC,发送给网络进程

2.2 请求过程

在网络进程接收到 URL 后,并不是马上对指定 URL 进行请求。首先,我们需要进行 DNS 解析域名得到对应的 IP,然后通过 ARP 解析 IP 得到对应的 MACMedia Access Control Address)地址。

域名是我们取代记忆复杂的 IP 的一种解决方案,而 IP 地址才是目标在网络中所被分配的节点。MAC 地址是对应目标网卡所在的固定地址。

1. DNS 解析

DNS 解析域名的过程分为以下几个步骤:

  • 询问浏览器 DNS 缓存
  • 询问本地操作系统 DNS 缓存(即查找本地 host 文件)
  • 询问 ISPInternet Service Provider)互联网服务提供商(例如电信、移动)的 DNS 服务器
  • 询问根服务器,这个过程可以进行递归和迭代两种查找的方式,两者都是先询问顶级域名服务器查找

2. 通信过程

首先,建立 TCP 连接,即三次握手过程

  • 客户端发送标有 SYN 的数据包,表示我将要发送请求。
  • 服务端发送标有 SYN/ACK 的数据包,表示我已经收到通知,告知客户端发送请求。
  • 客户端发送标有 ACK 的数据包,表示我要开始发送请求,准备被接受。

image.png

然后,利用 TCP 通道进行数据传输

  • 服务端接收到数据包,并发送确认数据包已收到的消息到客户端,不断重复这个过程
  • 客户端在发送一个数据包后,未接收到服务端的确定消息,则重新发送该数据包,即 TCP 的重发机制
  • 当接收完所有的数据包后,接收端会按照 TCP 头中的需要进行排序,形成完整的数据

最后,断开 TCP 连接,即四次握手过程

  • 客户端发送请求,申请断开连接,进入等待阶段,此时不会发送数据,但是会继续接收数据。
  • 服务端接收请求后,告知客户端已明白,此时服务端进入等待状态,不会再接收数据,但是会继续发送数据。
  • 客户端收到后,进入下一阶段等待。
  • 服务端发送完剩余的数据后,告知客户端可以断开连接,此时服务端不会发送和接收数据。
  • 客户端收到后,告知服务端我开始断开连接。
  • 服务端收到后,开始断开连接。

image.png

而这整个过程的客户端则是网络进程。并且,在数据传输的过程还可能会发生的重定向的情况,即当网络进程接收到状态码为 3xx 的响应报文,则会根据响应报文首部字段中的 Location 字段的值进行重新向,即会重新发起请求

3. 数据处理

当网络进程接收到的响应报文状态码,进行相应的操作。例如状态码为 200 OK 时,会解析响应报文中的 Content-Type 首部字段,例如我们这个过程 Content-Type 会出现 application/javascripttext/csstext/html,即对应 Javascript 文件、CSS 文件、HTML 文件。

详细的 MIME 类型讲解可以看 MDN

2.3 创建渲染进程

当前需要渲染 HTML 时,则需要创建渲染进程,用于后期渲染 HTML。而对于渲染进程,如果是同一站点是可以共享一个渲染进程,例如 a.abc.comc.abc.com 可以共享一个渲染渲染进程。否则,需要重新创建渲染进程

需要注意的是,同站指的是顶级域名二级域名相等

2.4 开始渲染

在创建完渲染进程后,网络进程会将接收到的 HTML、JavaScript 等数据传递给渲染进程。而在渲染进程接收完数据后,此时用户界面上会发生这几件事:

  • 更新地址栏的安全状态
  • 更新地址栏的 URL
  • 前进后退此时 enable,显示正在加载状态
  • 更新网页

image.png

2.5 渲染过程

大家都知道页面渲染的过程也是面试中单独会考的点,并且时常会由这个点延申出另一个问题,即如何避免回流和重绘。

渲染过程,是整个从理器输入 URL 到页面渲染过程的最后一步。而页面渲染的过程可以分为 9 个步骤:

  • 解析 HTML 生成 DOM
  • 解析 CSS 生成 CSSOM
  • 加载或执行 JavaScript
  • 生成渲染树(Render Tree
  • 布局
  • 分层
  • 生成绘制列表
  • 光栅化
  • 显示

2.5.1 构建 DOM 树

由于网络进程传输给渲染进程的是 HTML 字符串,所以,渲染进程需要将 HTML 字符串转化成 DOM 树。例如:
image.png

需要注意的是这个 DOM 树不同于 Chrome-devtoolElement 选项卡的 DOM 树,它是存在内存中的,用于提供 JavaScriptDOM 的操作。

2.5.2 构建 CSSOM

构建 CSSOM 的过程,即通过解析 CSS 文件、style 标签、行内 style 等,生成 CSSOM。而这个过程会做这几件事:

  • 规范 CSS,即将 color: blue 转化成 color: rgb() 形式,可以理解成类似 ES6ES5 的过程
  • 计算元素样式,例如 CSS 样式会继承父级的样式,如 font-sizecolor 之类的。

image.png

CSS Object Model 是一组允许用 JavaScript 操纵 CSSAPI。详细 API 讲解可以看 MDN

2.5.3 加载 JavaScript

通常情况下,在构建 DOM 树或 CSSOM 的同时,如果也要加载 JavaScript,则会造成前者的构建的暂停。当然,我们可以通过 defersync 来实现异步加载 JavaScript。虽然 defersync 都可以实现异步加载 JavaScript,但是前者是在加载后,等待 CSSOMDOM 树构建完后才执行 JavaScript,而后者是在异步加载完马上执行,即使用 sync 的方式仍然会造成阻塞。

JavaScript 执行的过程,即编译和运行 JavaScript 的过程。由于 JavaScript解释型的语言。所以这个过程会是这样的:

  • 针对每句代码进行分行处理,即 Token
  • 根据 Token,生成 ASTAbstract Sytanx Tree) 抽象语法树和创建上下文
  • 解释器解析和执行 AST,生成字节码。
  • 编译器针对需要反复执行的代码,生成对应的机器码,提高运行效率

2.5.4 生成渲染树(Render Tree)

在有了 DOM 树和 CSSOM 之后,需要将两者结合生成渲染树 Render Tree,并且这个过程会去除掉那些 display: node 的节点。此时,渲染树就具备元素和元素的样式信息。

2.5.5 布局

根据 Render Tree 渲染树,对树中每个节点进行计算,确定每个节点在页面中的宽度、高度和位置。

需要注意的是,第一次确定节点的大小和位置的过程称为布局,而第二次才被称为回流

2.5.6 分层

由于层叠上下文的存在,渲染引擎会为具备层叠上下文的元素创建对应的图层,而诸多图层的叠加就形成了我们看到的一些页面效果。例如,一些 3D 的效果、动画就是基于图层而形成的。

值得一提的是,对于内容溢出存在滚轮的情况也会进行分层

2.5.7 生成绘制列表

对于存在图层的页面部分,需要进行有序的绘制,而对于这个过程,渲染引擎会将一个个图层的绘制拆分成绘制指令,并按照图层绘制顺序形成一个绘制列表。

2.5.8 光栅化

有了绘制列表后,渲染引擎中的合成线程会根据当前视口的大小将图层进行分块处理,然后合成线程会对视口附近的图块生成位图,即光栅化。而渲染进程也维护了一个栅格化的线程池,专门用于将图块转为位图。

栅格化的过程通常会使用 GPU 加速,例如使用 wil-changeopacity,就会通过 GPU 加速显示

2.5.9 显示

当所有的图块都经过栅格化处理后,渲染引擎中的合成线程会生成绘制图块的指令,提交给浏览器进程。然后浏览器进程将页面绘制到内存中。最后将内存绘制结果显示在用户界面上。

而这个整个从生成绘制列表、光栅化、显示的过程,就是我们常说的重绘的过程

结语

整个浏览器输入 URL 到页面渲染的过程涉及到的知识点非常广,如 Chrome 多进程的架构、HTTP 通信过程、浏览器解析 JavaScript 过程、浏览器绘制页面过程以及一些计算机的基础知识等等,并且,这整个过程的分析其实和 Chrome-devtools 密切相关,所以很好的使用 Chrome-devtools 是非常重要的,后续应该会出一篇关于使用 Chrome-devtools 的指南。当然,本篇文章仍然存在诸多不足,欢迎提 issue ~

写作不易,如果你觉得有收获的话,可以帅气三连击!!!
查看原文

Evans 收藏了问题 · 2019-12-16

vue+element得select下拉框手机号中间4位用*号代替。

问题描述

vue+element得select下拉框手机号中间4位用*号代替。
图片描述
图片描述
我是直接在花括号中直接处理呢还是另写个方法呢?谢谢!

Evans 收藏了文章 · 2019-02-26

整理 node-sass 安装失败的原因及解决办法

声明:本文非原创,如有侵权请留言或发邮件告知,作者会立即停止侵权并删除本文。发布此文章主要是希望跟作者遇到同样问题的同学能解决node-sass 安装失败的问题。

npm install 时偶尔遇到报错:没有安装python或node-sass 安装失败的问题,百度之后发现是被墙了,但根据百度的方法换了淘宝镜像和用了vpn都安装失败,最后发现原来是因为没有卸载之前安装失败的包导致的。作者本人最后的解决方案是npm uninstall node-sass,然后使用VPN重新安装了一遍就成功了。不能翻墙的同学请看下文

node-sass 安装失败的原因

npm 安装 node-sass 依赖时,会从 github.com 上下载 .node 文件。由于国内网络环境的问题,这个下载时间可能会很长,甚至导致超时失败。
这是使用 sass 的同学可能都会遇到的郁闷的问题。

解决方案就是使用其他源,或者使用工具下载,然后将安装源指定到本地。

解决方法一:使用淘宝镜像源(推荐)

npm

设置变量 sass_binary_site,指向淘宝镜像地址。示例:

npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

// 也可以设置系统环境变量的方式。示例
// linux、mac 下
SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ npm install node-sass

// window 下
set SASS_BINARY_SITE=https://npm.taobao.org/mirrors/node-sass/ && npm install node-sass

或者设置全局镜像源:

npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/

之后再涉及到 node-sass 的安装时就会从淘宝镜像下载。

yarn

配置下 node-sass 的二进制包镜像地址

yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g

或者设置全局镜像源:

yarn config set registry https://registry.npm.taobao.org -g

解决方法二:使用 cnpm

使用 cnpm 安装 node-sass 会默认从淘宝镜像源下载,也是一个办法:

cnpm install node-sass

解决方法三:创建.npmrc文件

在项目根目录创建.npmrc文件,复制下面代码到该文件。

phantomjs_cdnurl=http://cnpmjs.org/downloads
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
registry=https://registry.npm.taobao.org

保存后 删除之前安装失败的包(第一次安装请跳过此步)

npm uninstall node-sass

重新安装

npm install node-sass

作者后来另一个项目在没有使用VPN的情况下测试此方法,安装时报错 ERR! node-sass@3.8.0 postinstall: `node scripts/build.js 改用方法一成功。

解决方法四:下载 .node 到本地

这里去根据版本号、系统环境,选择下载 .node 文件,然后安装时,指定变量 sass_binary_path,如:

npm i node-sass --sass_binary_path=/Users/lzwme/Downloads/darwin-x64-48_binding.node

安装失败后重新安装问题

之前安装失败,再安装就不去下载了,怎么办呢?那就先卸载再安装:

npm uninstall node-sass
npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

相关错误提示

提示没有安装python、build失败等,如:

gyp ERR! configure error
gyp ERR! stack Error: Can't find Python executable "C:\Users\zhuon\AppData\Local\Programs\Python\Python36\python.EXE", you can set the PYTHON env variable.
gyp ERR! stack     at PythonFinder.failNoPython (G:\Workspace\ManYan\manyan-nav\node_modules\node-gyp\lib\configure.js:483:19)
gyp ERR! stack     at PythonFinder.<anonymous> (G:\Workspace\ManYan\manyan-nav\node_modules\node-gyp\lib\configure.js:508:16)
gyp ERR! stack     at G:\Workspace\ManYan\manyan-nav\node_modules\graceful-fs\polyfills.js:284:29
gyp ERR! stack     at FSReqWrap.oncomplete (fs.js:152:21)
gyp ERR! System Windows_NT 10.0.15063
gyp ERR! command "C:\\dev\\nodejs\\node.exe" "G:\\Workspace\\ManYan\\manyan-nav\\node_modules\\node-gyp\\bin\\node-gyp.js" "rebuild" "--verbose" "--libsass_ext=" "--libsass_cflags=" "--libsass_ldflags="
"--libsass_library="
gyp ERR! cwd G:\Workspace\ManYan\manyan-nav\node_modules\node-sass
gyp ERR! node -v v8.4.0
gyp ERR! node-gyp -v v3.6.2
gyp ERR! not ok
Build failed
npm WARN co-mocha@1.2.0 requires a peer of mocha@>=1.18 <4 but none was installed.
npm WARN egg-restapi-module-tool@1.0.0 No repository field.
npm WARN egg-restapi-module-tool@1.0.0 scripts['server'] should probably be scripts['start'].

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! node-sass@3.8.0 postinstall: `node scripts/build.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the node-sass@3.8.0 postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     D:\nodejs\cache\_logs\2017-09-02T16_06_24_298Z-debug.log
主要转自:志文工作室
其它参考:
https://github.com/lmk123/blo...
https://segmentfault.com/q/10...
查看原文

Evans 收藏了文章 · 2019-02-20

ionic打包报错Execution failed for task ':processDebugResources'

ionic项目打包运行命令 ionic build android的时候报错

  • 报错详情如下
:processDebugResourcesERROR: In <declare-styleable> FontFamilyFont, unable to find attribute android:fontVariationSettings
ERROR: In <declare-styleable> FontFamilyFont, unable to find attribute android:ttcIndex

 FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':processDebugResources'.
> com.android.ide.common.process.ProcessException: Failed to execute aapt


BUILD FAILED

Total time: 27.96 secs
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
Error: cmd: Command failed with exit code 1 Error output:
ERROR: In <declare-styleable> FontFamilyFont, unable to find attribute android:fontVariationSettings
ERROR: In <declare-styleable> FontFamilyFont, unable to find attribute android:ttcIndex


FAILURE: Build failed with an exception.

 * What went wrong:
Execution failed for task ':processDebugResources'.
> com.android.ide.common.process.ProcessException: Failed to execute aapt

 * Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
  • 解决办法:

已经执行过命令ionic cordova platform add android 添加了android平台,
直接打开目录 你的项目工程/platform/build.gradle文件中
找到def addSigningProps(propsFilePath, signingConfig){...}方法
在这个方法之前添加上如下内容

gradle.taskGraph.whenReady { taskGraph ->
    taskGraph.getAllTasks().each() { task ->
        if (task.name == 'validateReleaseSigning' || task.name == 'validateSigningRelease') {
            promptForReleaseKeyPassword()
        }
    }
}

def addSigningProps(propsFilePath, signingConfig) {
...
}
  • 原因分析

compile "com.android.support:support-v4:+" 带+号是指要用最新版本。

force 'com.android.support:support-v4:27.1.0' 添加force强制指定annotations

查看原文

Evans 收藏了文章 · 2018-12-07

阿里云服务器ECS搭建LNMP

购买阿里云服务后搭建服务器

安装 nginx

1  http://nginx.org/en/download.html  下载nginx的最新版

2 解压nginx安装包 tar -xzf nginx-1.9.3.tar.gz

3 进入 nginx-1.9.3

4 安装 zlib库
# sudo apt-get install zlib1g-dev  // Ubuntu
# sudo yum install zlib-devel      // centos

5 安装 PCRE库
# sudo apt-get install libpcre3-dev # Ubuntu
# sudo yum install pcre-devel    # CentOS

6 安装可选的OpenSSL
  # sudo apt-get install openssl
  # sudo apt-get install libssl-dev
   
  # sudo yum install openssl
  # sudo yum install openssl-devel

7 进入<nginx源代码根目录>/src/core,然后用vi打开nginx.h头文件

8 ./configure    // 执行配置命令
  [nginx 被默认安装在 usr/local/nginx 中]

9 执行 make install 

10 执行nginx -t   检查配置文件是正确的

11 进入/usr/local/nginx/sbin目录
 # ./nginx            //启动nginx服务
 # ./nginx -s reload // 重启服务
// 如果发现80端口已经被占用,可能是nginx服务已经启动。
 # pkill nginx      // 杀掉nginx服务。
 # ps -ef | grep nginx     命令查看内存中是否还有nginx进程。

安装 php

1、安装gcc及libxml2
# yum install gcc -y 
# yum install libxml2* -y
2、下载最新PHP官方安装包 http://php.net/downloads.php 
3、解压安装包
# tar zxvf php-5.6.29.tar.gz
4、安装php 
# cd php-5.6.28  
// 最简单的配置,(在配置phpmyadmin时,出现缺少扩展mbstring,mysqli的问题)
# ./configure --prefix=/usr/local/php --enable-fpm
// --prefix  
// 比较全的配置 (但可能缺少库)
# ./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-mysql=mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --with-zlib --with-libxml-dir --with-gd --with-iconv --with-curl --with-png-dir --with-jpeg-dir --enable-sockets --enable-mbstring --enable-inline-optimization --enable-zend-multibyte --with-freetype-dir
// 个人本次使用的配置
# ./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-mysql=mysqlnd --with-mysqli=mysqlnd 

# make && make install  // 编译 安装

5、拷贝生产环境的PHP配置文件
cp /home/develop/php-5.6/php.ini-production /usr/local/php/etc/php.ini
6、拷贝php-fpm配置文件 (如果没有特殊要求,则直接使用默认配置即可)
cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf

7、启动服务
# cd /usr/local/
// 启动nginx服务
# ./nginx/sbin/nginx  
// 启动php
# ./php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf

8、配置php和nginx 及 fastcgi
//打开nginx配置文件
# cd /usr/local
# vi nginx/conf/nginx.conf
其中有一段 修改成 
 location ~ \.php$ {
            root           /mnt/www;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
            include        fastcgi.conf;
        }
// 其中 fastcgi.conf 在 /usr/localnginx/conf/中,安装自带的。
如果出现如下问题,就是没有配置好:
FastCGI sent in stderr: "Primary script unknown" while reading response header from upstream,
 client: 180.167.68.134, server: localhost, request: "GET /index.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "*.*.*.*"

安装 mysql

卸载 mysql

  1. ps -ef|grep mysql 查看安装路径

  2. find / -name mysql 查看相关 文件包

  3. rpm -qa | grep -i mysql 查看相关组件

  4. rpm -ev mysql-community-server-5.6.35-2.el7.x86_64 删除组件

  5. rpm -ev mysql-community-client-5.6.35-2.el7.x86_64

  6. rpm -ev php-mysql-5.4.16-42.el7.x86_64

  7. rpm -ev mysql-community-release-el7-5.noarch

  8. rpm -ev mysql-community-libs-5.6.35-2.el7.x86_64

  9. rpm -e --nodeps mysql-community-libs-5.6.35-2.el7.x86_64 // 强制卸载

  10. rm -rf /var/lib/mysql 删除文件

  11. rm -rf /var/lib/mysql/mysql rm -rf /usr/share/mysql

  12. more /etc/passwd | grep mysql

  13. more /etc/shadow | grep mysql 删除组,用户

  14. more /etc/group | grep mysql 删除组,用户

  15. userdel mysql 删除用户

  16. groupdel mysql 删除组

  17. rpm -qa | grep -i mysql 确认是否卸载

安装 mysql

  1. 下载 MySQL-5.6.35-1.linux_glibc2.5.x86_64.rpm-bundle.tar

  2. 解压 tar -xvf MySQL-5.6.35-1.linux_glibc2.5.x86_64.rpm-bundle.tar

  3. cd MySQL-5.6.35 // 改文件名为 MySQL-5.6.35

  4. yum install MySQL-shared-compat-5.6.35-1.linux_glibc2.5.x86_64.rpm # 安装 RHEL兼容包

  5. yum install MySQL-server-5.6.35-1.linux_glibc2.5.x86_64.rpm # MySQL服务端程序

  6. yum install MySQL-client-5.6.35-1.linux_glibc2.5.x86_64.rpm # MySQL客户端程序

  7. yum install MySQL-devel-5.6.35-1.linux_glibc2.5.x86_64.rpm # MySQL的库和头文件

  8. yum install MySQL-shared-5.6.35-1.linux_glibc2.5.x86_64.rpm # MySQL的共享库

  9. cat /root/.mysql_secret配置MySQL登录密码
    // 获取MySQL安装时生成的随机密码 为 2M*8c

  10. service mysql start # 启动MySQL服务

  11. mysql -uroot -p # 进入MySQL,

  12. 使用之前获取的随机密码

  13. SET PASSWORD FOR 'root'@'localhost' = PASSWORD('mypassword');
    // 在MySQL命令行中设置root账户的密码为mypassword

  14. quit # 退出MySQL命令行

  15. service mysql restart # 重新启动MySQL服务

yum 安装 mysql

  1. yum install libaio # 安装libaio依赖包

  2. wget http://dev.mysql.com/get/mysq...

  3. yum localinstall mysql-community-release-el7-5.noarch.rpm

  4. yum install mysql-community-server

  5. 启动和关闭 MySQL Server

  6. systemctl start mysqld //启动 MySQL Server

  7. systemctl status mysqld // 查看 MySQL Server 状态

  8. systemctl stop mysqld //关闭 MySQL Server

  9. 防火墙设置 firewall-cmd(推荐)centos 7才可以

  10. firewall-cmd --permanent --zone=public --add-port=3306/tcp

  11. firewall-cmd --permanent --zone=public --add-port=3306/udp
    // 就开放了相应的端口。

  12. firewall-cmd --reload //使最新的防火墙设置规则生效

  13. 重置mysql密码

  1、systemctl start mysqld 先启动
  2、mysql_secure_installation  安全设置 (没有密码 按enter 进入)
     Set root password? [Y/n] y        [设置root用户密码]
     Remove anonymous users? [Y/n] y            [删除匿名用户]
     Disallow root login remotely? [Y/n] n      [禁止root远程登录]
     Remove test database and access to it? [Y/n] y   [删除test数据库]
     Reload privilege tables now? [Y/n] y        [刷新权限]
 3、新建数据库
    //登录MYSQL(有ROOT权限)。这里我以ROOT身份登录
     [root@iZ28gvqe4biZ ~]# mysql -u root -p
    //首先为用户创建一个数据库hivemeta
    mysql  > create database hivemeta;
    mysql  > use hivemeta
    //授权hdp用户拥有hivemeta数据库的所有权限。
    mysql  > grant all privileges on *.* to hdp@"%" identified by "hdp" with grant option;
    //刷新系统权限表
    mysql  > flush privileges;
    mysql  > use hivemeta;
    //mysql/hive字符集问题
    mysql  > alter database hivemeta character set latin1;

安装 phpMyAdmin

  1. 下载 phpMyAdmin

  2. tar zxvf php-5.6.5.2.all-language.tar.gz // 解压

  3. 访问 phpMyAdmin/index.php

问题一 添加mbstring扩展

# cd /usr/src/php-5.6.29/ext/mbstring
# /usr/local/php/bin/phpize
# ./configure --with-php-config=/usr/local/php/bin/php-config
# make && make install
# echo ‘extension=mbstring.so' >>/usr/local/php/lib/php.ini
//或者打开 php.ini,直接添加extension=mbstring.so字段
# 重启服务器

问题二 添加 mysqli 扩展

# cd /usr/src/php-5.6.29/ext/mysqli
# /usr/local/php/bin/phpize
# ./configure --with-php-config=/usr/local/php/bin/php-config
# make && make install
# 这样会在 /usr/local/php/lib/php/extensions/no-debug-non-ztszh中生成 mysqli.so文件
# echo ‘extension=mysqli.so' >>/usr/local/php/lib/php.ini
# 或者打开 php.ini,直接添加extension=mysqli.so字段

# 网络上另一种配置是
# ./configure --with-php-config=/usr/local/php/bin/php-config –-with-mysql=/usr/lib64/mysql/
# ./configure --with-php-config=/usr/local/php/bin/php-config --with-mysqli=/usr/lib64/mysql/bin/mysql_config
# 这个是针对mysql5.5之前。其中-with-mysql指的是mysql的安装路径。现在mysql5.6默认的安装在/usr/lib64/mysql/,但是没有mysql_config文件。

错误

打开 phpMyAdmin/index.php出现提示 You are using PHP’s deprecated ‘mysql’ extension,

解决:
在phpmyadmin中config.inc.php修改:
$cfg'Servers'['extension'] = 'mysqli';

执行PHP配置命令./configure 时提示以下错误:

checking for OS + Linux 2.6.32-431.el6.x86_64 x86_64
checking for C compiler ... not found
解决:
yum -y install gcc gcc-c++ autoconf automake make

使用SFTP错误:

Received unexpected end-of-file from SFTP server
解决方法:
vi /etc/ssh/sshd_config
删除Subsystem sftp /usr/libexec/openssh/sftp-server前面的"#",
保存退出,重启SSH
service sshd restart 然后重新连接FTP测试

查看原文

认证与成就

  • 获得 0 次点赞
  • 获得 3 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

注册于 2017-11-04
个人主页被 125 人浏览