Code Monkey home page Code Monkey logo

blog's Introduction

blog's People

Contributors

lzcrwxl avatar

Stargazers

reake avatar  avatar wangshaoli avatar

Watchers

James Cloos avatar

blog's Issues

scss高级语法之遍历数组

首先声明数组

  $colors:((color:#065c6d),
      (color:#57469f),
      (color: #9b4c42),
      (color:#941d39),
      );

其次遍历

@for $i from 1 through length($colors) {
        .item#{$i} {
          background: url('../images/static_img/cosmetic-pb-#{$i}.png') no-repeat center;
          -webkit-background-size: cover;
          -moz-background-size: cover;
          background-size: cover;
          $item: nth($colors, $i);
          h6 {
            color: map-get($item, color);
          }
        }
      }

最终生成的代码

.cosmetics-page .cosmetics-problems .problems-list .item1 {
  background: url("../images/static_img/cosmetic-pb-1.png") no-repeat center;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  background-size: cover
}

.cosmetics-page .cosmetics-problems .problems-list .item1 h6 {
  color: #065c6d
}

.cosmetics-page .cosmetics-problems .problems-list .item2 {
  background: url("../images/static_img/cosmetic-pb-2.png") no-repeat center;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  background-size: cover
}

.cosmetics-page .cosmetics-problems .problems-list .item2 h6 {
  color: #57469f
}

.cosmetics-page .cosmetics-problems .problems-list .item3 {
  background: url("../images/static_img/cosmetic-pb-3.png") no-repeat center;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  background-size: cover
}

.cosmetics-page .cosmetics-problems .problems-list .item3 h6 {
  color: #9b4c42
}

.cosmetics-page .cosmetics-problems .problems-list .item4 {
  background: url("../images/static_img/cosmetic-pb-4.png") no-repeat center;
  -webkit-background-size: cover;
  -moz-background-size: cover;
  background-size: cover
}

.cosmetics-page .cosmetics-problems .problems-list .item4 h6 {
  color: #941d39
}

浏览器如何调用摄像头

浏览器调用摄像头:
参考文章:https://juejin.im/entry/5aef11e4f265da0b752804b3

需要开启ngrok,

image

报错:NotSupportedError Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).

新发布的Webkit内核的浏览器(chrome谷歌浏览器、QQ浏览器)控制台会有这个提示,基于安全隐私问题,现在Webkit内核的浏览器共享视频、语音、经纬度坐标等必须通过https形式访问!也就是说须将http访问形式改造成https

如果是vue cli 开启dev 访问的话,
在需要在webpack.dev.conf.js里添加配置
disableHostCheck: true,,
否则会报错: Invalid Host header

如何手机访问局域网网站

前提

先关闭电脑的防火墙,将手机和电脑设置在同一局域网下

ipconfig:
IPv4 地址 . . . . . . . . . . . . : 192.168.0.61

vue 配置

// config/index.js
dev: {
.....
    // Paths
    host: '0.0.0.0', // can be overwritten by process.env.HOST
    port: 9528, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined

......

手机通过电脑的ip地址加端口号就能访问

192.168.0.61:9528

PHPStudy是一款轻量级PHP服务器,搭建环境迅速。但是与XAMPP之类服务器不同的是,PHPStudy默认只有本机才能设置域名、访问网站。需要更改vhost.conf中的文件,才可以使得内网可以访问。

  1. 打开PHPStudy设置,通过“打开配置文件”,打开vhosts-conf这个文件,打开,找到如下代码:
<VirtualHost *:80>
    DocumentRoot "D:\joinbe\liuyizhai\public"
    ServerName liuyizhai.local
    ServerAlias phpStudy.net

其中。test.com指的是你设置的本机访问的域名。这里没有设置局域网访问,所以局域网访问过去,看到的是类似FTP列表的形式。

将这段代码改成:

<VirtualHost *:80>
<VirtualHost *:80>
    DocumentRoot "D:\joinbe\liuyizhai\public"
    ServerName 192.168.0.54 // 本机的ip地址
    ServerAlias phpStudy.net
  1. 打开 其他选项菜单->打开hosts 设置 成本机的ip地址:

image

装饰函数实现插件式的表单验证

前沿

在一个web项目中,可能存在非常多的表单,比如登录校验,代码如下:

<html>

<body>
  用户名:<input type="text" id="username">
  密码:<input type="password" id="password">
  <input type="button" value="提交" id="submitBtn">
</body>
<script>
  var username = document.getElementById('username'),
    password = document.getElementById('password'),
    submitBtn = document.getElementById('submitBtn');

  var formSubmit = () => {
    if (username.value === '') {
      console.log(username)
      return alert('用户名不能为空')
    }
    if (password.value === '') {
      return alert('密码不能为空')
    }
    var param = {
      username:username.value,
      password:password.value
    }
    console.log(param)
    ajax('http://xxx.com',param)
  }
  submitBtn.onclick = () => formSubmit()
</script>
</html>

formSubmit 函数在此处承担了两个职责,除了提交ajax请求之外,还要验证用户输入的合法性。这种代码一来会造成函数臃肿,职责混乱,二来谈不上任何可复用性。

装饰模式重构

接下来,我们尝试着用装饰函数的方法改造一下:

<html>

<body>
  用户名:<input type="text" id="username">
  密码:<input type="password" id="password">
  <input type="button" value="提交" id="submitBtn">
</body>
<script>
  Function.prototype.before = function (beforefn) {
    var _self = this
    return function () {
      if (beforefn.apply(this, arguments) === false) {
        return
      }
      return _self.apply(this, arguments)
    }
  }
  var username = document.getElementById('username'),
    password = document.getElementById('password'),
    submitBtn = document.getElementById('submitBtn');


  var validata =  ()=> {
    if (username.value === '') {
       alert('用户名不能为空')
       return false
    }
    if (password.value === '') {
     alert('密码不能为空')
     return false
    }
  }

  var formSubmit = () => {
    var param = {
      username:username.value,
      password:password.value
    }
    ajax('http://xxx.com',param)
  }
  formSubmit = formSubmit.before(validata)
  submitBtn.onclick = () => formSubmit()
</script>
</html>

这段代码使得validata和formSubmit 完全分离开来,它们不再有任何的耦合关系,validata可以成为一个即插即用的函数,这有利于分开维护这两个函数。
值得注意的事,因为函数通过Function.prototype.before被装饰之后,返回的实际上是一个新的函数,如果在原函数上保存了一些属性,那么这些属性会丢失。
另外,这种装饰方法也叠加了函数的作用域,如果装饰的链条过长,性能上也会受一些影响
代码如下:

var func = function(){
  alert(1)
}
func.a = "a"

func = func.before(function(){
  alert(2)
})

alert(func.a) // 输出:undefined

策略模式重构

再次改造,使用策略模式解决校验规则
代码如下:

<html>
<body>
  用户名:<input type="text" id="username">
  密码:<input type="password" id="password">
  <input type="button" value="提交" id="submitBtn">
  <p id="warn"></p>

</body>
<script>
    var username = document.getElementById('username'),
    password = document.getElementById('password'),
    warn = document.getElementById('warn'),
    submitBtn = document.getElementById('submitBtn');

  Function.prototype.before = function (beforefn) {
    var _self = this
    return function () {
      if (beforefn.apply(this, arguments) === false) {
        return
      }
      return _self.apply(this, arguments)
    }
  }
  // 策略模式
  var vldStrategy = { //策略对象-验证规则
    isNonEmpty(value, errorMsg) { //输入不为空
      if (value === '') {
        return errorMsg
      }
    }
  }


  class Validator {
    constructor() {
      this.rules = [] // 数组用于存放负责验证的函数
    }
    add(domNode, ruleArr) { //添加验证规则
      var self = this
      for (var i = 0, rule; rule = ruleArr[i++];) {
        (function () {
          var strategyArr = rule.strategy.split(':'),
            errorMsg = rule.errorMsg
          self.rules.push(function () {
            var tempArr = strategyArr.concat()
            var ruleName = tempArr.shift()
            tempArr.unshift(domNode.value)
            tempArr.push(errorMsg)
            return vldStrategy[ruleName].apply(domNode, tempArr)
          })
        })(rule)
      }
      return this
    }
    start() {
      for (let i = 0, vldFn; vldFn = this.rules[i++];) {
        console.log(vldFn)
        var msg = vldFn()
        if (msg) {
          warn.textContent = msg;
          return false;
        }
      }
    }
  }

  let vld = new Validator()
  vld.add(username, [{         // 这边不能直接传 username.value,不然始终获取的是第一次加载的value值
    strategy: 'isNonEmpty',
    errorMsg:'账号不能为空'
  }])

  var formSubmit = () => {
    var param = {
      username: username.value,
      password: password.value
    }
    console.log(param)
    ajax('http://xxx.com', param)
    return warn.textContent
  }
  formSubmit = formSubmit.before(vld.start.bind(vld))
  submitBtn.onclick = () => formSubmit()
</script>

</html>

vue-cli3.0 环境变量配置

比如之前在 prod.env.js 中我们会如下配置

'use strict'
module.exports = {
  NODE_ENV: '"production"'
}

在新版 Vue CLI 的项目中,我们一般在项目根目录放置几个文件:

.env.development
.env.production

符合官方文档中提到的如下模式:

.env.[mode] # 只在指定的模式中被载入

里面的内容不是之前的那种对象了,只是键=值

VUE_APP_API_PREFIX=http://****

这里要注意了:只有以 VUE_APP_ 开头的才可以使用

每日一道面试题(前端)

每日一道面试题

开篇

从今天开始,每日一道面试题,帮助自己提高和巩固知识,所有的题目都是网上找的,答案也是仁者见智的回答,废话不多说,直接上题:

1、简述JavaScript的几种数据类型?(2019-1-24)

最新的 ECMAScript 标准定义了 7 种数据类型:
6种原始类型:Undefined、Null、Boolean、Number、String 、Symbol(ECMAScript 6 新定义)  和1种复杂数据类型:Object

2、跨域问题,谁限制的跨域,怎么解决

浏览器的同源策略导致了跨域
用于隔离潜在恶意文件的重要安全机制
- jsonp ,允许 script 加载第三方资源
- nginx 反向代理(nginx 服务内部配置 Access-Control-Allow-Origin *)
- cors 前后端协作设置请求头部,Access-Control-Allow-Origin 等头部信息
- iframe 嵌套通讯,postmessage

3、JavaScript 如何实现继承?

(1)构造继承
(2)原型继承
(3)实例继承
(4)拷贝继承
//原型prototype机制或apply和call方法去实现较简单,建议使用构造函数与原型混合方式。
function Parent() { 
  this.name = 'song';
}
function Child() {
  this.age = 28;
}
Child.prototype = new Parent(); //通过原型,继承了Parent
var demo = new Child();
alert(demo.age);
alert(demo.name); //得到被继承的属性

4、vue事件修饰符先后顺序的区别

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self 会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

5、重绘和回流

- 重绘:当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
- 回流:当Render Tree(DOM)中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
- 回流要比重绘消耗性能开支更大。
- 回流必将引起重绘,重绘不一定会引起回流。

6、Javascript有哪些内置对象?

只有Math和Global(在浏览器环境中,Global就是Window)

7、一行代码实现数组去重

[...new Set([1,2,3,1,'a',1,'a'])]

8、浏览器页面有哪三层构成,分别是什么,作用是什么

构成:结构层,表示层,行为层

作用:HTML 实现页面结构,css实现表示,javascript实现交互

9、JS中使用typeof能得到哪些类型

值类型:变量本身就是含有赋予给它的数值的,它的变量本身及保存的数据都存储在栈的内存块当中

引用类型:引用类型当然是分配到堆上的对象或者数据变量,根据官方一点的解释就是引用类型的变量只包括对其所表示的数据引用

对象、数组、函数
无限扩展属性,可能出现内存占用比较大的情况

typeof abc      //"undefined"
typeof NaN      //"number"
typeof null     //"object"
typeof console.log //"function"

typeof只能区分值类型,对引用类型无能为力,只能区分函数function
NaN表示特殊的非数字值,null是空指针,并没有指向任何一个地址
typeof能区分的五种基本类型:string、boolean、number、undefined、symbol和函数function

10、内存泄漏

  • 意外的全局变量: 无法被回收
  • 定时器: 未被正确关闭,导致所引用的外部变量无法被释放
  • 事件监听: 没有正确销毁 (低版本浏览器可能出现)
  • 闭包: 会导致父级中的变量无法被释放
  • dom 引用: dom 元素被删除时,内存中的引用未被正确清空

可用 chrome 中的 timeline 进行内存标记,可视化查看内存的变化情况,找出异常点。

11、TCP三次握手和4次挥手

三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。
四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

12、for循环console.log

for (var i = 1; i <= 5; i++) {
  setTimeout(function () {
    console.log(i)
  },0)
}

会打印出5个6,这是why

因为 for 循环会先执行完(同步优先于异步优先于回调),这时五个 setTimeout 的回调全部塞入了事件队列中,然后 1 秒后一起执行了。
为什么不是 1 2 3 4 5 ,问题出在作用域上。

为 setTimeout 的 console.log(i); 的i是 var 定义的,所以是函数级的作用域,不属于 for 循环体,属于 global。
等到 for 循环结束,i 已经等于 5 了,这个时候再执行 setTimeout 的五个回调函数(参考上面对事件机制的阐述),里面的 console.log(i);
的 i 去向上找作用域,只能找到 global下 的 i,即 5。所以输出都是 5。

办法1:人为给 console.log(i); 创造作用域,保存i的值。

for(vari = 0; i < 5; i++) { 
  (function(i){   //立刻执行函数
    setTimeout(function (){
      console.log(i);
     },1000);
  })(i);
}

这里用到立刻执行函数。这样 console.log(i); 中的i就保存在每一次循环生成的立刻执行函数中的作用域里了。
办法2

for (let i = 0; i < 5; i++) {   //let 代替 var
  setTimeout(function (){
    console.log(i);
   },1000);
}

let 为代码块的作用域,所以每一次 for 循环,console.log(i); 都引用到 for 代码块作用域下的i,因为这样被引用,所以 for 循环结束后,这些作用域在 setTimeout 未执行前都不会被释放。

13、Javascript怎么比较两个数组是否相同?
Javascript不能直接用==或者===来判断两个数组是否相等,无论是相等还是全等都不行,以下两行JS代码都会返回false

 alert([]==[]); //false
 alert([]===[]); //false

要判断JS中的两个数组是否相同,需要先将数组转换为字符串,再作比较。以下两行代码将返回true

alert([].toString()== [].toString()); //true
alert([].toString()===[].toString()); //true

14、从输入url到页面加载完成发生了什么?

   1、浏览器的地址栏输入URL并按下回车。

  2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
  3、DNS解析URL对应的IP。
  4、根据IP建立TCP连接(三次握手)。
  5、HTTP发起请求。
  6、服务器处理请求,浏览器接收HTTP响应。
  7、渲染页面,构建DOM树。
  8、关闭TCP连接(四次挥手)。

15、为何Vue避免 v-if 和 v-for 用在一起
原因:v-for比v-if优先,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。

  <ul>
<li
  v-for="user in users"
  v-if="user.isActive"
  :key="user.id"
>
  {{ user.name }}
</li>
  </ul>

如上情况,即使100个user中之需要使用一个数据,也会循环整个数组。
正确:

computed: {
activeUsers: function () {
return this.users.filter(function (user) {
  return user.isActive
})
}
}
<ul>
<li
  v-for="user in activeUsers"
  :key="user.id"
>
{{ user.name }}
</li>
</ul>

16、JS中document和window的区别

Document 对象
每个载入浏览器的 HTML 文档都会成为 Document 对象。Document 对象是 Window 对象的一部分,可通过 window.document 属性对其进行访问。

Window 对象
Window 对象表示浏览器中打开的窗口。

17、js 判断一个 object 对象是否为空
判断一个对象是否为空对象,本文给出三种判断方法:
1.最常见的思路,for...in... 遍历属性,为真则为“非空数组”;否则为“空数组”

for (var i in obj) { // 如果不为空,则会执行到这一步,返回true
    return true
}
return false // 如果为空,返回false

2.通过 JSON 自带的 stringify() 方法来判断:
JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串。

if (JSON.stringify(data) === '{}') {
    return false // 如果为空,返回false
}
return true // 如果不为空,则会执行到这一步,返回true

这里需要注意为什么不用 toString(),因为它返回的不是我们需要的。

var a = {}
a.toString() // "[object Object]"

3.ES6 新增的方法 Object.keys():
Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组。
如果我们的对象为空,他会返回一个空数组,如下:

var a = {}
Object.keys(a) // []

我们可以依靠Object.keys()这个方法通过判断它的长度来知道它是否为空。

if (Object.keys(object).length === 0) {
    return false // 如果为空,返回false
}
return true // 如果不为空,则会执行到这一步,返回true

18、 setTimeout倒计时为什么会出现误差?

setTimeout() 只是将事件插入了“任务队列”,必须等当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码消耗时间很长,也有可能要等很久,所以并没办法保证回调函数一定会在 setTimeout() 指定的时间执行。所以, setTimeout() 的第二个参数表示的是最少时间,并非是确切时间。
HTML5标准规定了 setTimeout() 的第二个参数的最小值不得小于4毫秒,如果低于这个值,则默认是4毫秒。在此之前。老版本的浏览器都将最短时间设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常是间隔16毫秒执行。这时使用 requestAnimationFrame() 的效果要好于 setTimeout();

19、keep-alive的组件 的生命周期钩子函数

当引入keep-alive 的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。

vue单页面seo另一种方式---prerender+metaInfo

前言

目前vue项目做seo主要有两种方式,一种是ssr,也就是服务端渲染,现有的框架如nuxt.js,还有一种是做预渲染prerender,在页面比较少的情况下,预渲染可以极大的提高网页访问速度。而且配合一些meat插件,完全可以满足SEO需求。

预渲染为SEO提供了另一种可能,简单的来说,预渲染就是当vue-cli构建的项目进行npm run build 的时候,会按照路由的层级进行动态渲染出对应的html文件。

这里要用到两个依赖:

  1. 使用 vue-meta-info 动态设置title/keword/description 、
    具体用法参见文档:https://github.com/muwoo/vue-meta-info

  2. 如果需要根据路由生成纯静态资源,使用 prerender-spa-plugin 可以轻松的添加预渲染,
    官方文档:https://github.com/chrisvfritz/prerender-spa-plugin

npm i vue-meta-info prerender-spa-plugin --save

prerender-spa-plugin的使用,基与vue-cli配置:

// webpack.prod.conf.js
const path = require('path')
const PrerenderSpaPlugin = require('prerender-spa-plugin')

module.exports = {
  // ...
  plugins: [
    new PrerenderSpaPlugin(
      // 编译后的html需要存放的路径
      path.join(__dirname, '../dist'),
      // 列出哪些路由需要预渲染,不能太多,太多会报Unable to prerender all routes! 
       ['/index','/warehouse-service','/ec-service','/tm-service','/partner','/about-us']
    )
  ]
}

最终会生成类似于这样的目录结构
image

vue-meta-info的使用

  1. 全局引入 vue-meta-info
// main.js
import Vue from 'vue'
import MetaInfo from 'vue-meta-info'

Vue.use(MetaInfo)
  1. 组件内静态使用 metaInfo
<template>
  ...
</template>

<script>
  export default {
    metaInfo: {
      title: 'My Example App', // set a title
      meta: [{                 // set meta
        name: 'keyWords',
        content: 'My Example App'
      }]
      link: [{                 // set link
        rel: 'asstes',
        href: 'https://assets-cdn.github.com/'
      }]
    }
  }
</script>
  1. 如果你的 title 或者 meta 是异步加载的,那么你可能需要这样使用
<template>
  ...
</template>

<script>
  export default {
    name: 'async',
    metaInfo () {
      return {
        title: this.pageName
      }
    },
    data () {
      return {
        pageName: 'loading'
      }
    },
    mounted () {
      setTimeout(() => {
        this.pageName = 'async'
      }, 2000)
    }
  }
</script>

最后

prerender+metaInfo
这种方式基本可以解决seo,优点是代码入侵最低,开发成本最小,但也有缺点:

  • /user/:id ,像这样的动态路由不合适
  • 做不到渲染太多页面,可能会导致编译时间过长报错

如何及时发现线上bug

前沿

Fundebug帮助我们解决什么问题?

  • js运行、资源加载、http请求错误实时邮件报警
  • 错误源码调用栈定位
  • 用户行为记录
  • 细化适用前端各种框架
  • 业务主流程监控上报自定义扩展

起步走

  1. 前往 https://fundebug.com/,用公司邮箱注册,告知加入的团队相应项目。
  2. 回到自己的项目,安装
    npm install fundebug-javascript fundebug-cli --save

最后

fundebug是收费版,体验版只能用14天
国外的免费版的Sentry
在Sentry上新建一个账号,再新建一个组织,一个组织可以邀请多人参与到其中来。然后创建一个新的项目,选中JavaScript项目。左下角填写项目名然后创建项目。

高阶函数

高阶函数是指至少满足下列条件之一的函数:

  • 函数可以作为参数被传递
  • 函数可以作为返回值输出
  1. 函数作为参数传递

回调函数

在异步请求中,在ajax请求返回之后做是,但又不知道请求返回的确切时间,最常见的方法是把callback 函数当作参数传入发起ajax请求方法中,待请求完成之后执行callback函数

var getInfo = function(userId,callback){
  $.ajax('http://xxx.com?userInfo'+userId,function(data){
    if(typeof callback === 'function'){
      callback(data)
    }
  })
}

getInfo('22222',function(data){
  alert(data)
})

函数作为返回值输出

判断函数的类型:

var Type = {}

for(var i =0,type;type = ['String','Array','Number'][i++];){
  (function(type){
    Type['is'+type] = function(obj){
      return Object.prototype.toString.call(obj)=== '[object '+ type +']'
    }
  })(type)
}

console.log(Type.isArray([])) // true
console.log(Type.isString([])) // false
console.log(Type.isArray('dsss')) //false

linux下安装node,mongodb,nginx

安装node

  1. 去官网下载和自己系统匹配的文件:
#建立安装包文件夹
mkdir package
#下载
cd package
wget  https://npm.taobao.org/mirrors/node/v8.9.3/node-v8.9.3-linux-x64.tar.xz 
  1. 解压到根目录
xz-d node-v8.9.3-linux-x64.tar.xz/tar -xzvf node-v8.9.3-linux-x64.tar.xz
tar -xvf node-v8.9.3-linux-x64.tar -C /    
  1. 建立软连接
#node
ln -s /node-v8.9.3-linux-x64/bin/node    /usr/local/bin/node
#node查看版本
node -v 
#npm
ln -s /node-v8.9.3-linux-x64/bin/npm   /usr/local/bin/npm

安装mongodb

  1. 通过xshell打开ftp上传安装包:mongodb-linux-x86_64-amazon-3.6.2.tgz
  2. 解压到根目录
    tar -zxvf mongodb-linux-x86_64-amazon-3.6.2.tgz -C /
  3. 创建mongodb 文件夹
mkdir mongodb
#文件移动到指定目录
mv mongodb-linux-x86_64-amazon-3.6.2 mongodb

#创建文件夹
cd mongodb/
mkdir data
mkdir logs
cd logs/
#创建日志文件
touch mongo.log
#创建配置文件
cd ..
mkdir etc
cd etc/
#编辑文件
vi mongo.conf
按i进行编辑
内容如下
dbpath=/mongodb/data
logpath=/mongodb/logs/mongo.log
logappend=true
journal=true
quiet=true
port=27017
bind_ip_all=true    #允许所有ip地址访问,安全隐患

#保存退出
esc -> shift+:->wq

#建立软连接:
ln -s /mongodb/bin/mongod   /usr/local/bin/mongod

ln -s /mongodb/bin/mongo /usr/local/bin/mongo
#还有一种方案
我将其安装在了 /mongodb 这个目录,同时,我将 MongoDB 的 bin 目录加入了PATH变量中:
vi ~/.bash_profile

加入如下一行:
export PATH="/mongodb/bin/:$PATH"

保存之后,运行:
source ~/.bash_profile

这个时候,我就可以直接在任何地方启动 MongoDB 或者进行相关的数据库操作了,使用 mongod 命名启动服务器:
#建立链接
mongod -f /mongodb/etc/mongo.conf 

安装cnpm

建议使用淘宝镜像安装,否则会很慢的
npm install -g cnpm --registry=https://registry.npm.taobao.org
安装完成后也要创建一个软链接
ln -s /node-v8.9.4-linux-x64/bin/cnpm /usr/local/bin/cnpm

安装pm2

npm install pm2 -g
建立软连接
ln -s /node-v8.9.4-linux-x64/bin/pm2 /usr/local/bin/pm2

启动app
pm2 start app.js

#注意linux不同版本 防护墙命令也不相同 centos7版如下:
1.查看已开放的端口(默认不开放任何端口)
firewall-cmd --list-ports

1.1开启防火墙
systemctl start firewalld
2.开启80端口
firewall-cmd --zone=public(作用域) --add-port=80/tcp(端口和访问类型) --permanent(永久生效)
firewall-cmd --zone=public --add-port=80/tcp --permanent
3.重启防火墙
firewall-cmd --reload

安装nginx

1.检查并安装所需的依赖软件
    1).gcc:nginx编译依赖gcc环境
      安装命令:yum install gcc-c++
    2).pcre:(Perl Compatible Regular Expressions)是一个Perl库,包括 perl 兼容的正则表达式库。nginx的http模块使用pcre来解析正则表达式.
      安装命令:yum install -y pcre pcre-devel
    3).zlib:该库提供了很多种压缩和解压缩的方式,nginx使用zlib对http包的内容进行gzip。
      安装命令:yum install -y zlib zlib-devel
    4).openssl:一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。nginx不仅支持http协议,还支持https(即在ssl协议上传输http).
      安装命令:yum install -y openssl openssl-devel
  2.下载nginx源码包
    下载命令:wget http://nginx.org/download/nginx-1.12.0.tar.gz
  3.解压缩源码包并进入
    1).解压缩:tar -zxvf nginx-1.12.0.tar.gz
    2).进入解压缩后文件夹:cd nginx-1.12.0
  4.配置编译参数(可以使用./configure --help查询详细参数)

./configure

 5.编译并安装
    命令:make && make install
    可以进入/usr/local/nginx查看文件是否存在conf、sbin、html文件夹,若存在则安装成功
  6.启动nginx
    1).进入安装目录
      cd /usr/local/nginx/sbin/
    2).启动
      ./nginx

 4).查看是否启动:ps -ef | grep nginx
      如果有master和worker两个进程证明启动成功
  注意:执行./nginx启动nginx,这里可以-c指定加载的nginx配置文件,如下:
        ./nginx -c /usr/local/nginx/conf/nginx.conf
      如果不指定-c,nginx在启动时默认加载conf/nginx.conf文件,此文件的地址也可以在编译安装nginx时指定./configure的参数(--conf-path= 指向配置文件(nginx.conf))
  7.停止
    1).暴利kill(不推荐使用)
      kill -9 processId
    2).快速停止
      cd /usr/local/nginx/sbin && ./nginx -s stop
      此方式相当于先查出nginx进程id再使用kill命令强制杀掉进程
    3).完整停止(建议使用)
      cd /usr/local/nginx/sbin && ./nginx -s quit
      此方式停止步骤是待nginx进程处理任务完毕进行停止
  8.重启及重新加载配置
    1.先停止再启动(建议使用)
      ./nginx -s quit && ./nginx
    2.重新加载配置文件
      ./nginx -s reload
  9.测试
    nginx安装成功,启动nginx,即可通过ip地址来访问nginx:

nginx开机自启动
即在rc.local增加启动代码就可以了。
vi /etc/rc.local
增加一行 /usr/local/nginx/sbin/nginx
设置执行权限:
chmod 755 rc.local

如何判断一个数组之最优方案

伪数组

JS没有真正的数组。

typeof 运算符不能辨别数组和对象。要判断一个值是否为数组,你还需要检查他的constructor属性:

var arr =[]
function isArray(arr){
  if(arr&&typeof arr ==='object'&& arr.constructor ===Array){
    console.log("它是一个数组") // arr 是一个数组
  }
}
isArray(arr)

上面的监测对于在不同帧或窗口创建的数组将会给出false。当数组有可能在其他的帧中被创建时,下面的检测更靠谱:

var arr =[]
function isArray(arr){
  if(Object.prototype.toString.apply(arr)==='[object Array]'){
    console.log("它是一个数组")
  }
}
isArray(arr)

arguments数组不是一个数组,它只是一个有着length成员属性的对象。上面的检测会分辨出arguments并不是一个数组

对于学基础还是学框架的个人理解

前言

是这样的,这个问题好像每过几年都会被人提及,特别是前端领域,变化特别快。有人说程序员疲于学习新的框架,到时候框架不流行了等于白学,还有人说学习流行框架为了赶潮流,其他公司都在用框架,你不用显得很落伍,对于这个问题,我曾经也苦恼,看过各种讨论的文章,我自己也思索过,这里说下自己的心得体会。

先说说现状,目前来说前端主流框架就三种:angular、vue、react。在没框架之前我们用的是jq,框架出来就就近几年,从我的经验来讲:

  • 从公司的角度出发:小公司要会框架的,大公司要基础好的。因为小公司相对人少,框架对于他们来说降低了时间成本和人力成本。而大公司首先他们有自己的框架,其次,每个人写的是比较细节的东西,如果只是会框架会造成很多细节上的问题。
  • 从个人发展来说,我认为先是打基础、把基础打好、再学框架,不需要太多,一般1个就够用了,然后继续进阶高阶的算法、函数等等。因为说到底,框架的运用只是一个熟练度的问题,如果在基础打好的前提下,它能帮你找到工作。再往后,如果要提升自己,那要了解框架背后的原理,说到底,程序是你逻辑思维的表达,如果思路清楚了,甚至语言都是相同的。

如果你只是刚入行不到2年的前端小白,快速学习框架便于你找到更好的工作,如果2年以上了,那基础是更好的学习方向

以上是我个人的一些浅薄观点

vue 的keep-alive include无效

keep-alive内置组件的include不生效,是需要在对应的组件中设置好name属性,并且是需要配置路由的vue组件都需要声明,不声明的组件默认都生效缓存,另外 注意不是路由里面的name属性

view.vue:


 <keep-alive include="test">
            <router-view />
          </keep-alive>

test.vue

export default {
  name: "test",
  data() {}
}

浏览器如何渲染页面

话不多说,一起来看:

  1. 浏览器通过 HTMLParser 根据深度遍历的原则把 HTML 解析成 DOM Tree。
  2. 浏览器通过 CSSParser 将 CSS 解析成 CSS Rule Tree(CSSOM Tree)。
  3. 浏览器将 JavaScript 通过 DOM API 或者 CSSOM API 将 JS 代码解析并应用到布局中,按要求呈现响应的结果。
  4. 根据 DOM 树和 CSSOM 树来构造 render Tree。
  5. layout:重排(也可以叫回流),当 render tree 中任一节点的几何尺寸发生改变,render tree 就会重新布局,重新来计算所有节点在屏幕的位置。
  6. repaint:重绘,当 render tree 中任一元素样式属性(几何尺寸没改变)发生改变时,render tree 都会重新画,比如字体颜色,背景等变化。
  7. paint:遍历 render tree,并调动硬件图形 API 来绘制每个节点。

avatar

传递对象参数代替过长的参数列表

前言

一个函数有可能接收多个参数,而参数的数量越多,函数就越难理解和使用。使用该函数的人首先得搞明白全部参数的含义,在使用时,还要小心翼翼,以免少传某个参数或者把两个参数搞反了位置,代码如下:

var getData = function(page,pageSize,totalCount,id){
  console.log("page="+page)
  console.log("pageSize="+page)
  console.log("totalCount="+page)
  console.log("id="+page)
}

getData(1,10,30,25) // 这边如果想新增加一个参数就会涉及许多代码修改

这时我们可以把参数都放入一个对象内,然后把该对象传入getData函数,getData函数需要的数据可以自行从该对象里获取。现在不需要关心参数的数量和顺序,只要保证参数对应的key值不变就可以了:

var getData = function({page,totalCount,pageSize,id}){  //对象解构
  console.log("page="+page)
  console.log("pageSize="+pageSize)
  console.log("totalCount="+totalCount)
  console.log("id="+id)
}

getData({
  page:1,
  pageSize:3,
  totalCount:30,
  id:25
})

React 应用中使用装饰器

使用create-react-app创建项目后执行

npm run eject //把project的相关配置信息以及依赖信息弹射出来

安装babel插件

npm install --save-dev babel-plugin-transform-decorators-legacy

修改package.json文件的babel配置项

"babel": { "plugins": [ "transform-decorators-legacy" ], "presets": [ "react-app" ] },

如何使用

@connect

如果报错:
Error: The ‘decorators’ plugin requires a ‘decoratorsBeforeExport’ option, whose value must be a boolean. If you are migrating from Babylon/Babel 6 or want to use the old decorators proposal, you should use the ‘decorators-legacy’ plugin instead of ‘decorators’.
解决方法:

cnpm install babel-plugin-transform-decorators-legacy  --save-dev
cnpm install  @babel/plugin-proposal-decorators --save-dev

然后修改babel部分

// .babelrc
{
  "presets": [
    "react-app",
    "@babel/preset-env"
  ],
  "plugins": [
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    [
      "@babel/plugin-proposal-class-properties",
      {
        "loose": true
      }
    ]
  ]
}

关于babel,@这个符号,这个是只有babel7才特有的
重启项目,就OK了

react-router 4.0 以上可选参数配置之填坑

低版本和高版本配置不一样,官方文档上也没有看到高版本的写法,是我搜索其他人的代码时发现的。。。

低版本

<Route path="/search/:category(/:keyword)" component={Search}/>

高版本

<Route path="/search/:category/:keyword?" component={Search}/>

兼容多种模块(AMD&CMD&NODE&浏览器) 规范

以下代码演示如何将hello()方法定义到不同的运行环境中,它能够兼容Node、AMD、
CMD以及常见的浏览器环境中:

// demo17.js
(function(name,definition){
  // 检测上下文环境是否为AMD&CMD
  var hasDefine = typeof define ==='function',
  // 检测上下文环境是否为NODE
    hasExports = typeof module !=='undefined'&& module.exports
  if(hasDefine){
    // AMD 或CMD 环境
    define(definition)
  }else if(hasExports){
    // 普通node 模块
    module.exports = definition()
  }else{
    // 浏览器环境
    window[name] = definition()
  }
})('hello',function(){
  var hello = function(){
    console.log('hello')
  }
  return hello;
})

```  html
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<script src="demo17.js"></script>
<script>
// 浏览器环境
  this.hello() // 输出hello
</script>
</html>
// node 环境
var hello = require('./demo17')
hello()  // 输出hello

create-react-app设置proxy反向代理不起作用

[email protected] 更高的版本里有两种方式设置代理:

  • 如果proxy的值是字符串,直接在package.json里设置:
//package.json

  "proxy": "http://localhost:4000",
  • 如果proxy的值是一个json,则需要做如下修改:
  1. npm install http-proxy-middleware
  2. src文件夹根目录下创建 setupProxy.js 文件,配置如下:
const proxy = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(proxy('/api', { target: 'http://localhost:4000/' }));
  app.use(proxy('/*.svg', { target: 'http://localhost:4000/' }));
};

vue打包后ios环境长按无法识别二维码

现象

vue打包生成微信内嵌的h5页面,某个页面存在一个二维码,在安卓手机上可以长按识别二维码,但是ios不行。

解决方案

1.不要让图片压缩成base64格式
2.需要将vue-router的mode:history去掉就好了(默认使用的是hash模式的router),原因不明

函数声明提升与变量声明提升

函数的执行顺序(声明式函数、赋值型函数、匿名函数、自执行函数)
声明式函数和赋值型函数的区别在于:在js的预编译期间,声明式函数会被提前提取出来,然后才会按照顺序执行JS代码

	//函数声明
  a();//a
	function a() {
		console.log("a");
	}

    //函数赋值
	b();//Uncaught TypeError: b is not a function
	var b = function() {
		console.log("b");
	}

· 预编译期间:对当前js文件中的所有 声明变量 和 函数进行处理,但是需要注意:1 ,此时处理的只是声明式函数,2,变量也只是进行了声明但是没有进行初始化和赋值。

接下来,我们结合上面分析的来进行示例

    f();//输出函数声明2
	function f(){
		console.log("输出函数声明1")
	}
	function f(){
		console.log("输出函数声明2")
	}

结论1 :都是函数声明的情况下,后面的函数会层叠前面的函数

函数声明提升>形参>变量声明提升
当同时存在函数声明和变量声明时,来看这个例子

getName();  //1
var getName = function () {
    console.log(2);
}

function getName() {
    console.log(1);
}
getName();  //2

当存在函数声明和变量声明时,函数声明在前,变量声明在后,所以上述代码是这样的

//预编译阶段
//使用函数声明创建的函数函数声明提升
var getName;
getName = function () {
    console.log(1);
};
//变量声明提升,因为之前已经声明过getName,所以再次声明var会被忽略
var getName;

//执行阶段
getName();  //1
getName = function () {
    console.log(2);
};
getName();  //2

函数表达式最大的问题,在于js会将此代码拆分为两行代码分别执行。
例如下代码:

console.log(x);//输出:function x(){}
var x=1;
function x(){}

实际执行的代码为,先将 var x=1 拆分为 var x; 和 x = 1; 两行,再将 var x; 和 function x(){} 两行提升至最上方变成:

var x;
function x(){}
console.log(x);
x=1;

所以最终函数声明的x覆盖了变量声明的x,log输出为x函数。

Windows下如何安装&部署Jenkins

windows下安装并配置Jenkins

 Jenkins是一个基于java的持续集成工具,开源项目。用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。我在安装时会记录下步骤及遇到的问题

一、下载Jenkins:

下载地址:https://www.jenkins.io/zh/download/

如下图:选择稳定版

image-20201120093306026

二、安装Jenkins

直接双击jenkins.msi文件,一直next,傻瓜式安装

注意:这边会提示需要win10账号密码,如果是win10家庭版网上没找到解决方案,所以先选第一个

image-20201120094112883

到这一步如果没有安装Java环境的注意安装指定的版本,下载链接:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html,这里我先安装下

image-20201120095846917

浏览器会自动打开localhost:8080, 按照页面提示输入密码

安装推荐插件,安装完毕创建账号,至此安装完毕

注意:安装插件失败:系统管理-->管理插件

选择【高级】选项卡

替换最下方【升级站点】中的URL

http://updates.jenkins-ci.org/update-center.json

替换为

http://mirror.esuni.jp/jenkins/updates/update-center.json

如下图:【详细的Jenkins的镜像地址查询:http://mirrors.jenkins-ci.org/status.html】

image-20201120110600799

三、Jenkins实现自动化部署

实现的目标是:我们的本地项目发起一个git提交后,剩下的单元测试, 打包构建,代码部署,邮件提醒等,我们会全部自动化完成部署。

3.1. 准备

首先我们随便准备一个项目(我这边是使用webpack搭建的vue项目了),在git仓库中新建一个项目,然后把该本地项目提交到github上去。

比如我这边项目如下所示:

img

3.2. 在jenkins中绑定github

我们现在要实现这么一个功能,当我们在本地项目往github远程仓库push我们的代码的时候,jenkins就能知道我们提交了代码,要实现这么一个功能的基本原理是:在远程仓库上需要配置一个Jenkins服务的接口地址,当本地向远程仓库发起push时候,远程仓库会向配置的Jenkins服务器的接口地址发起一个带参数的请求,当jenkins收到后开始工作。

配置步骤如下:

Github配置

  1. 在Github上获取访问token的值,需要一个对项目有写权限的账户。

如果要实现自动构建的话,Jenkins需要获得远程代码仓库Github的读取权。

点击右上角的 Github --> setting --> Developer settting --> Personal access tokens -> 点击Generate new token 填写如下所示的内容:

img

创建成功后,会生成一个token的值如下:

img

先保存好这个token,后面会使用到。

2) 用Ngrok实现内网穿透

Ngrok的用途是:将内网的IP映射成对外可访问的域名。

国内Ngrok官网可以了解下:https://www.ngrok.cc/

我们需要注册一个账号,注册完成后,我们可以直接添加隧道。如何开通隧道,可以看这篇文章(https://www.sunnyos.com/article-show-67.html)

开通完成后,我们变成如下这个样子了,如下图所示:

img

现在我们需要点击上图的客户端下载,下载自己系统对应的客户端。然后压缩后,文件夹内会有一个 叫 sunny 这个bat文件,我们进入下载目录的文件夹下,使用linux移动命令把该批处理文件移动到 /usr/local/bin 目录下即可;命令如下所示:

$ sudo mv sunny /usr/local/bin 

然后我们进入 /usr/local/bin 目录下,运行命令: ./sunny clientid 隧道id; 即可,隧道id就是我们刚刚生成的。比如如下所示:
img

运行该命令后,我们回车可以看到如下信息了,如下图所示:

img

因此我们现在访问:http://kongzhi.free.idcfengye.com 就可以访问对应的本地服务 http://127.0.0.1:8080/ 服务了。如下所示:

img

3)Github webhooks设置

进入Github上指定的项目 --> setting -> WebHooks --> add webhook, 如下图所示:

img

完成后,点击下面 add webhooks 按钮,即可创建完成,如下图所示:

img

Jenkins的github配置

4)安装Github Plugin

点击 Manage Jenkins -> Manage Plugins -> 可选插件, 如下图所示:

img

点击下面的 "直接安装" 按钮, Jenkins会自动帮我们解决插件的依赖。如下图所示:

img

5)配置Github Plugin

Manage Jenkins -> Configure System -> Github -> 点击添加Github服务器, 如下图所示:

img

API URL 默认为:https://api.github.com,我们不动它,然后凭据点击 Add添加,如下图所示:

img

如上,我们之前已经生成了token之后。在如上配置中,我们页面中的类型选择 Secret text,在Secret中黏贴Github生成的Token,id和描述可以随便写,或不填。

最后点击添加按钮后,会回到主页面,在凭据中选择我们刚才创建的凭据,然后我们点击 "链接测试"。如果没有成功的话,则需要返回重新操作以上步骤,如下所示,我们只要点击保存完成配置即可,如下图所示:
img

到这里,基础配置已经完成了。

3.3. Jenkins中创建任务

  1. 点击创建一个新任务,如下图所示:

img

输入一个任务名称,比如叫 testJekins, 然后选择第一项即可。如下图所示:

img

因此准备工作已经完成了,我们输入任务名称后,选择第一项后,点击底部的确定按钮,就可以进入下一步的页面了,如下图所示:

img

如上选择Github,填写项目的URL,该地址是我们Github上的主页地址,不是github资源地址。

2). 源码管理配置

打开刚刚我们创建的任务,切换到 "源码管理" tab项,然后在左侧选择 Git,然后我们把我们的github项目中的仓库地址,添加配置登录名和密码(点击添加会弹出一个框,我们默认操作就行),及分支。如下图所示:
img

3). 构建触发器

img

4). 构建环境

img

其他暂时不用配置。配置完成后,我们再回到列表页面,看到基本的效果如下图所示,现在我们在本地项目代码再push一下,看是否发生了变化。

img

如上我们可以看到,我们每次代码push的时候,Jenkins会自动帮我们构建一次,但是它有几秒钟的延迟,如果我们立即push完成后,刷新页面后,并没有立即看到构建完成,而要过几秒钟再看下就可以了。

我们也可以看看控制台输出的信息也可以看得到如下信息,代表构建成功了。如下:

img

我们也可以看得到,我们的testJenkins项目中也有我们github上的项目中的所有文件,如下所示:

img

老生常谈之JS中的apply()方法

首先还是拿w3c里面的例子:

   function add(a,b)  
    {  
        alert(a+b);  
    }  
    function sub(a,b)  
    {  
        alert(a-b);  
    }  
    add.call(sub,3,1); 

这个例子中的意思就是用 add 来替换 sub,add.call(sub,3,1) == add(3,1) ,所以运行结果为:alert(4); // 注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。(其还可以看成是sub继承了add这个方法并调用执行)

具体详情参考w3cschool

js里应用AOP实例(运用到apply方法)

比如页面中有个登录button,点击这个button会弹出登录浮层,与此同时要进行数据上报,来统计有多少用户点击了这个登录button

<!DOCTYPE html>
<html lang="en">
<body>
  <button tag="login" id="button">点击打开登录浮层</button>
</body>
<script>
Function.prototype.after = function(afterfn){
  let _self = this // 这里的this 指代showLogin这个方法
  return function(){
    let ret = _self.apply(this,arguments) // 这里的this指代button这个元素,apply 把button替换为showLogin方法并执行
    afterfn.apply(this,arguments) // 同理 button替换为log方法并执行
    return ret   
  }
}

let showLogin = function(){
  console.log('打开登录浮层')
}

let log = function(){
  console.log('上报标签为:'+this.getAttribute('tag'))
}

showLogin = showLogin.after(log) // 
/* 这里的showLogin 已经变成 function(){
    let ret = _self.apply(this,arguments) 
    afterfn.apply(this,arguments) 
    return ret   
  } */
document.getElementById('button').onclick = showLogin
</script>
</html>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.