# 2019年10月技术日常

# 2019/10/31 周四

# HTML注释的重要性

当看别人代码时会有大用,

<!-- 每个功能模块前必须加注释 -->
...

<!-- 如果涉及隐藏的必须加end_其他可不加end_start -->
...
<!-- 如果涉及隐藏的必须加end_其他可不加end_end -->

# 2019/10/30 周三

# svn拉取代码

# checkout svn,注意如果密码错误,会不提示重新输入,如果403forbidden,就是没权限
svn checkout http://仓库地址 --username=用户名

# 如果mac没有装xcode,怎么安装svn

最简单的方法:安装idea或webstorm等工具,在里面chenckout svn项目,会提示安装,按照提示来即可。

# 2019/10/28 周一

# nginx默认超时时间为60s

需要注意,前端就算超时设置为2分,但如果前端代码部署到了nginx上,也会导致1分钟超时

location / {
  root d:/test/;
  fastcgi_connect_timeout 600; # set timeout
  fastcgi_send_timeout 600; 
  fastcgi_read_timeout 600;
}

# 常用的组件函数整理

  • 发布订阅模式代码
  • 文件大小默认为B,转为合适的函数
  • 当前时间获取函数

# JS到底是解释型语言还是编译型语言

Is JavaScript really interpreted or compiled language ? https://segmentfault.com/a/1190000013126460

Js是一种解释型语言,令人困惑的地方:

  • —般解释型语言是逐行解释执行的,为什么JS会有変曩提升(hoisting)的能力?
  • 执行JS时会用到JIT, JIT(just in time compilers 及时编译)会做代码优化(同时也会创建代码的编译版本),解释型语言无法做到这些

# 变量提升问题

在函数作用域内的任何变量声明都会被提升到顶部,且值为undefined,JS处理声明语句的过程:

  • 一旦v8引擎进入一个执行具体代码的执行上下文(函数),会对代码进行词法分析或分词(lexing and tokenizing the code), 会将代码切分为原子性的令牌(atomic token) , 比 如foo = 10
  • 在分析完当前作用域后,它会将翻译后的版本解析为AST(抽象语法树)
  • 每次遇到声明都会将其发送到作用域,并创建绑定,每次声明都会为变量分配内存,只是分配内存,并不会通过修改源代码来将变最声明语句提升,在JS中分配内存,意味着将变量默认设置为undefined
  • 在这之后,引擎每次遇到赋值或者取值,都会通过作用域(scope)查找绑定。如果当前作用域中没有找到,就接着向上级作用域中查找,直到找到为止
  • 接着引擎生成CPU可执行的机器码
  • 最后,代码执行完毕

# JIT是什么

JS start out slow, but then got faster thanks to something colled the JIT, but how does the JiT work ? 通俗一点说:浏览器在解释执行JS时,如果遇到某些语句多次执行,会将对应的语句编译,并存储。下次再执行相同的语句时,不用再重新编译,而是直接执行之前存储的该语句编译的版本。当然里面不止这么简单,还有很多优化, 详情參考: A crash course in just-in-time (JIT) compilers

# 总结

  • JS需要有JS引擎解析才能执行。这是解释型语需要的,编译型语言程序你能直接运行。
  • 变量提升只是JS解释器处理事情的方式导致的,
  • JIT 是唯一一点可以对JS是否是解释型语言提出疑问的理由。但JIT不是完整的编译器,它仅在执行前编译,且JIT只是Mozilla 和 Google开发人员为了提升浏览器性能才引入的,JS或TC39从没有强制要求使用JIT, 综上:JS是解释型语言或混合型语言(编译型型和解释型的混合),而不是编译型语亩。

# 2019/10/26 周六

# 原生JS实现遮罩动画

只需引入一个JS,即可载入该动画,demo地址: https://github.com/zuoxiaobai/fedemo/tree/master/src/DebugDemo/%E9%81%AE%E7%BD%A9%E5%8A%A8%E7%94%BB%E6%95%88%E6%9E%9C

# 2019/10/24 周四

# element percentage大于100

element UI报错 custom validator check failed for prop "percentage",进度大于100或出错

# 2019/10/23 周三

# node res大文件字符串时内存溢出

想把buffer数据转为stirng,设置为json格式传到前端,但如文件过大,直接就崩了。

# 2019/10/18 周五

# 骨架屏研究

  • 一种自动化生成骨架屏的方案 https://blog.csdn.net/sinat_17775997/article/details/83443744
  • 教你实现超流行的骨架屏预加载动态效果 http://www.dxcu.com/news/show-531569.html
  • repeating-linear-gradient 线性渐变研究TODO
<head>
  <style>
    .fast-loading {
      height: 20px;
      margin: 10px 0;
      width: 200px;
      background-color: rgb(245, 245, 245);
      background-image: repeating-linear-gradient(90deg, #eee, #f5f5f5 100%);

      animation-name: fastLoading;
      animation-timing-function: linear;
      animation-duration: 1s;
      animation-iteration-count: infinite;
    }
    @keyframes fastLoading {
      from {
        background-position: 0 0;
      }
      to {
        background-position: 100px 0;
      }
    }
    .w100 { width: 100% }
    .w80 { width: 80% }
    .w60 { width: 60% }
    .w40 { width: 50% }
    .w30 { width: 30% }
  </style>
</head>
<body>
  <div style="width: 50%;margin: 50px auto;">
    <div class="fast-loading"></div>
    <div class="fast-loading w40"></div>
    <div class="fast-loading w80"></div>
    <div class="fast-loading w60"></div>
    <div class="fast-loading w30"></div>
    <div class="fast-loading w30"></div>
    <div class="fast-loading w50"></div>
    <div class="fast-loading w60"></div>
  </div>
</body>

# 下载文件带进度显示

  • 本地搭建node服务,模拟download接口
// index.js   
// 运行:在控制台 node index.js
const http = require("http");
const fs = require("fs");

const app = http.createServer((req, res) => {
  const { method, url } = req;
  if (method === "GET" && url === "/api/download") {        
    fs.readFile("./file.pdf", (err, data) => {
        // 这里以pdf为例子
        res.setHeader("Content-Type", "application/pdf");
        const fileName = encodeURI('中文')
        res.setHeader('Content-Disposition' ,`attachment; filename="${fileName}.pdf"`)
        res.end(data);
    });
  }
})
app.listen(3000)
  • 从后端接收文件数据时,进度显示
<!-- 导出按钮 -->
<div class="title-right">
  <a href="javascript:void(0)" 
    :class="{ 'disabled-a': isShowProgress }" 
    @click="exportExecl">
    全部导出
  </a>
</div>

<!-- 显示下载进度组件封装 -->
<download-progress
  :progressEvent="progressEvent"
  :isShowProgress="isShowProgress"
  :cancelDownload="handleWhenPorgresss('end')"
></download-progress>

<script>
import DownloadProgress from './DownloadProgress'
import axios from 'axios'

export default {
  components: { DownloadProgress },
  data() {
    return {
      progressEvent: {}, // 下载进度
      isShowProgress: false, // 是否显示progress面板, disabled导出按钮
      axiosCancelTokenSource: '', // axios取消请求
    }
  },

  methods: {
    onDonloadProgress(progressEvent) {
      cosnole.log(progressEvent)
      this.progressEvent = progressEvent
    },

    async exportExecl() {
      if (this.isShowProgess) {
        console.log('导出中,点击无效')
        return
      }

      try {
        this.handleWhenProgress('start')

        let axiosConfig = {
          onDonloadProgress: this.onDownloadProgess.bind(this),
          cancelToken: this.axiosCancelTokenSource.token,
          timeout: 120000
        }
        let data = await xxx.getFileData(null, axiosConfig)
        console.log(data) 
        // blobdata,在axios配置中 responseType: 'blob' 就会返回Blob类型数据
        // 就是是json格式数据,也会被转Blob,TODO 当为JSON数据时特殊处理

        let fileType = "文件的MIME类型"

        if (data.type !=== fileType) {
          throw new Error("服务器返回数据类型异常")
        }

        this.handleWhenProgress('end')

        // 下载
        this.downloadFile(data, fileType, '文件名')
      } catch(e) {
        console.log('导出发生了异常', e)
        axios.isCancel(e) ? console.log('请求已取消') : this.$message.error(e.message)
        this.handleWhenProgress('end')
      }
    },

    hanldeWhenProgress(state) {
      if (state === 'start') {
        this.showProgress = true

        // 创建axios cancelToken
        this.axiosCanelTokenSource = axios.CancelToken.source() 
      } else {
        // 取消axios请求
        this.axiosCancelTokenSource.cancal('请求取消')

        // 初始化
        Object.assign(this, {
          isShowProgress: false,
          progress: {}
        })
      }
    }
  }
}
</script>

<style lang="less“>
  .title-right {
    a {
      color: rgb(36, 156, 211);
      text-decoration: none;
      &:hover {
        text-decoration: underline;
      }
    }
    .disabled-a {
      color: #888;
      &:hover {
        cursor: wait;
        text-decoration: none;
      }
    }
  }
</style>
  • 文件数据接收完成后,使用axios配合Vue下载文件
// data 后端返回的文件数据
function downloadFile(data, fileType, fileName) {
  // window.open(dataUrl)

  // fileType 文件的MIME类型
  // 参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types
  const blobData = new Blob([data], {
    type: fileType 
  })
  console.log(blobData) // 检查数据是否正常

  //如果是IE,特殊处理,防止IE下提提示 "拒绝访问"
  if (window.navigator.msSaveBlob) {
    try {
      window.navigator.msSaveBlob(blobData, fileName, + '.xlsx')
    } catch(e) {
      console.log('msSaveBlob异常', e)
    }
    return
  }

  // 创建下载链接,并触发下载
  // https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a#浏览器兼容性
  // <a> download attribute not support IE, iOS safari
  const dataUrl = window.URL.createObjectUrl(blobData)
  const downloadElement = document.createElement('a')
  downloadElement.href = dataUrl
  downloadElement.download = fileName // download文件名

  // 触发点击,下载
  document.body.appendChild(downloadElement)
  documentElement.click()

  // 移除辅助下载DOM及对象URL
  document.body.removeChild(downloadElement)
  window.URL.revokeObjectURL(dataUrl)
}

# 2019/10/17 周四

# 纯前端实现execl文件导出

js-xlsx,github: https://github.com/SheetJS/js-xlsx

# eslint禁止在return中使用赋值

为什么会有这个限制?

function doSomething() {
  return foo = bar + 2
} 

官方的解释是:对于上面的代码,很难说明return的意图

  • 该函数返回的结果是bar + 2 为什么要赋值给foo
  • 目的还可能是比较运算符,如 ==

存在歧义,因此最好不要在return语句中使用赋值操作, 解决办法

  • 鼠标移动到错误的位置,知道出现快速修复的按钮,Disabled no-return-assgin for this line,就会添加异常注释,// eslint-disabled-next-line on-return-assign
  • 为了增强代码可读性有些自动修复去掉的括号可以加上,在配合上面的注释即可让eslint忽略

# 2019/10/16 周三

# /deep/ 样式

深度选择器,Vue单文件组件中scope样式,对子组件会不生效。如果想让某些样式在子组件里面生效,可以使用/deep/

<style lang="less" scoped>
/deep/ .el-checkbox {
  min-width: 180px;
  margin-bottom: 4px;
}  
</style>

# 2019/10/15 周二

# 滚动条消失的问题

和高度设置有关,如果,注意用 min-height: calc(100vh - top高度)

# 遍历对象优雅写法

let obj = {
  a: 1,
  b: 2
}
Object.keys(obj).forEach(key => {
  cosnole.log(key, obj[key])
})

# 2019/10/12 周六

# #ffffff 与 #fff 的区别

3位是6位的缩写,比如#ccc就是#cccccc的缩写。并不是所有的都可以缩写,必须符合一定的格式。注意:与移动端原生交互时,颜色不要使用缩写,安卓可能会显示异常。

// 缩写都是以每两位为缩写的单位
// #abc  => #aabbcc
// #1D2  => #11DD22 

# eslint保存时自动fix

vscode默认的autofix只能fix .js的文件,无法fix .vue的文件,加入下面的配置即可

// config
{
  "edit.formatOnSave": false,   // 取消自带fix,使用eslint自动保存fix
  "eslint.autoFixOnSave": true, // 每次保存的时候将代码按eslint格式进行修复
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    {
      "language": "vue",
      "autoFix": true
    },
    "html"
  ]
}

# 全局修改el菜单样式不影响其他

.vue单文件组件style元素加上scope后,当前页面修改el-tree的默认样式无效需要去掉scope,将样式暴露到全局,但对全局可能有影响,解决方法是最外层使用特殊的class包裹

<style lang="less">
  .root-menu-left {
    /* el样式修改 */  
  }
</style>

# axios请求拦截

  • 请求拦截 axios.interceptors.request.use(resolve func, reject func)
  • 响应拦截 axios.interceptors.response.use(resolve func, reject func)
  • 执行顺序
      1. 先执行请求拦截(特意在拦截中加了一个阻塞5s的await)
      1. 向后端发送请求
      1. 触发响应拦截(这里也可能存在等待时间)
      1. 最后才会执行axios请求then后面的内容
<!-- demo -->
<body>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
  <script>
    // Add a request interceptor
    axios.interceptors.request.use(async function (config) {
      // Do something before request is sent
      console.log('request 拦截: ', config)

      // 打印内容格式如下:
      // {
      //   "url": "https://zuo11.com/getList?num=5&start=5",
      //   "data": undefined
      //   "method": "get",
      //   "headers": {
      //     "common": {
      //       "Accept": "application/json, text/plain, */*"
      //     }
      //   },
      //   "timeout": 0,
      //   "xsrfCookieName": "XSRF-TOKEN",
      //   "xsrfHeaderName": "X-XSRF-TOKEN",
      //   "maxContentLength": -1
      // }
      
      // 为所有请求加一个时间戳参数
      config.url  += (config.url.includes('?') ? '&' : '?')  + 't=' + (+new Date())
      // Request URL: https://zuo11.com/getList?num=5&start=5&t=1575620590972

      await new Promise((resolve, reject) => {
        console.log('开始等待中...')
        setTimeout(()=> {
          resolve('结束等待')
        }, 5000)
      })

      return config; // 用来请求的参数
    }, function (error) {
      // Do something with request error
      return Promise.reject(error);
    });

    // Add a response interceptor
    axios.interceptors.response.use(function (response) {
      // Any status code that lie within the range of 2xx cause this function to trigger
      // Do something with response data
      console.log('响应拦截', response)

      // 如果身份校验失败,返回登录页
      response.data.code === 111  && (window.location.href = response.data)

      return response.data // 过滤掉除data参数外的其它参数,响应接收到的值。
      // return response;
    }, function (error) {
      // Any status codes that falls outside the range of 2xx cause this function to trigger
      // Do something with response error
      return Promise.reject(error);
    });

    axios.get('https://zuo11.com/getList?num=5&start=5').then((res) => {
      console.log('请求成功,', res)
    }, (err)=> {
      console.log('请求发生了错误,', err)
    })
  </script>
</body>

参考文档: https://github.com/axios/axios#interceptors

# 2019/10/11 周五

# eltree 懒加载问题

懒加载每次加载数据都是从后台搜索而来,对于复杂的逻辑,建议用文字来整理,按步骤分解,不管逻辑多复杂,条理都会很清晰。

# eltree highlight 属性

加上后,当前选中的背景色会稍微深一点

# v-cloak 指令

防止由于网络原因vue.js未渲染时,页面显示 的问题

// 当编译完成后,v-cloak属性会被自动移除。
[v-cloak] {
  display: none;
}
<div v-cloak>
  {{message/}}
</div>

# 2019/10/10 周四

# URLSearchParams() 查询字符串处理

https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams

var searchParams = new URLSearchParams()
searchParams.append('a', 1212)
searchParams.append('b', 'xxx')
searchParams.toString()   // "a=1212&b=xxxx"

// 结合fromEntries函数,将查询字符串转对象
// https://www.yuque.com/guoqzuo/js_es6/rxu7ms#e6a375d4
Object.fromEntries(new URLSearchParams('foo=bar&baz=qux'))

# 前端ajax请求时,设置Cookie请求头无效。

W3c规定,当请求的header匹配以下不安全的字符时,将被终止

...
Cookie
Host
Referer
User-Agent
...

# elementUI 全局触发消息

// $message(), $alet()
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI)

// 注意:引入方式不是 .use
Vue.prototype.$message = ElementUI.Message
Vue.prototype.$alert = ElementUI.MessageBox.alert

this.$message({
  type: 'error', // warning
  message: '这是一条消息' 
})

this.$message.error(e.message)

# 2019/10/09 周三

# Vue中img的src是动态参数时不显示

解决方法:使用require来加载图片, 参考 Vue中img的src是动态渲染时不显示 (opens new window)

# 滚动相关问题

进入页面后,计算某个id的offsetTop,再通过设置documet.documentElement.scrollTop滚动到该位置。这里滚动不了是因为高度是异步加载数据后才计算出来,需要在加载数据成功后,再执行,并使用 this.$nextTick(()=> {})。这里有耦合,可以用到设计模式中的发布订阅模式,mounted钩子函数订阅数据请求成功的消息,当接收该消息时执行dom操作。在异步数据请求ok后,发布数据请求成功的消息。另外这里是否不用操作dom,直接使用hash来滚动?直接使用element.scrollIntoView()方法?

# window.scroll(x-coord, y-coord)

滚动到指定位置

# window.scrollTo()

// https://developer.mozilla.org/zh-CN/docs/Web/API/Window/scrollTo
window.scrollTo(x-coord,y-coord )
window.scrollTo(options)
// * x-coord 是文档中的横轴坐标。
// * y-coord 是文档中的纵轴坐标。
// * options 是一个包含三个属性的对象:
//  1. top 等同于  y-coord
//  2. left 等同于  x-coord
//  3. behavior  类型String,表示滚动行为,支持参数 smooth(平滑滚动),instant(瞬间滚动),默认值auto,实测效果等同于instant

window.scrollTo( 0, 1000 );

// 设置滚动行为改为平滑的滚动
window.scrollTo({ 
    top: 1000, 
    behavior: "smooth" 
});

# window.scrollBy() 相对于当前位置

window.scrollBy(x-coord, y-coord);
window.scrollBy(options)
// * x是水平滚动的偏移量,单位:像素。
// * Y 是垂直滚动的偏移量,单位:像素。
// 正数坐标会朝页面的右下方滚动,负数坐标会滚向页面的左上方。

window.scrollBy(0, window.innerHeight);  // 向下滚动 一页(浏览器可视高度)

# Element.scrollTop

Element.scrollTop 属性可以获取或设置一个元素的内容垂直滚动的像素数。返回文档在垂直方向已滚动的像素值。

window.scrollY
document.documentElement.scrollTop = 100 页面滚动

# Element.scrollIntoView()

参数是一个布尔值,默认为true,滚动到元素位置

document.getElementById('注意').scrollIntoView(true)
// https://developer.mozilla.org/zh-CN/docs/Web/API/Element/scrollIntoView#%E7%A4%BA%E4%BE%8B

# 使用hash

url设置hash,可以滚动到对应的id位置

# vue vue-touer 滚动

创建Router实例时,可以提供scrollBehavior方法,来设置对应的滚动效果。参考: vue-router滚动行为 (opens new window)

# 2019/10/08 周二

# macOS 安装nginx

解决方法:brew install nginx, 需要注意xcode不要随意卸载,不然会出各种奇怪的问题: 包括svn问题、nginx安装问题等。

# brew install nginx 安装后部分log
==> nginx
Docroot is: /usr/local/var/www

The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so that
nginx can run without sudo.

nginx will load all files in /usr/local/etc/nginx/servers/.

To have launchd start nginx now and restart at login:
  brew services start nginx
Or, if you do not want or need a background service you can just run: nginx
brew install nginx

# 运行nginx,出现端口占用的情况,可能已经启动过nginx了
kevindeMacBook-Air:~ kevin$ sudo nginx
Password:
nginx: [emerg] bind() to 0.0.0.0:8080 failed (48: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:8080 failed (48: Address already in use)

# 如果出现上面端口被占用的情况,也可以查看端口占用情况
kevindeMacBook-Air:~ kevin$ lsof -i :8080
COMMAND   PID  USER   FD   TYPE            DEVICE SIZE/OFF NODE NAME
nginx   42869 kevin    6u  IPv4 0x97257f79dc0dbd3      0t0  TCP *:http-alt (LISTEN)
nginx   42870 kevin    6u  IPv4 0x97257f79dc0dbd3      0t0  TCP *:http-alt (LISTEN)

# 解除端口占用
kevindeMacBook-Air:~ kevin$ sudo kill -9 42869
kevindeMacBook-Air:~ kevin$ sudo kill -9 42870

# 修改端口为81
# 我本地的80端口之前调试过php,开启了apache服务占用了,127.0.0.1/1.php可以正常访问php项目
kevindeMacBook-Air:~ kevin$ vi /usr/local/etc/nginx/nginx.conf

# http://127.0.0.1:81/ 修改为81后,可正常访问

mac安装nginx_1.png

# text-overflow: ellipsis

文本溢出处理,在HTML5权威指南这本书里是没有讲到这个知识点

div.test {
  text-overflow: ellipsis;
}

/* 需要结合下面的三个属使用 */
{
  white-space: nowrap;  /* 不换行 */
  overflow: hidden;     /* 溢出内容隐藏 */
  width: 20em;          /* 指定宽度 */
}

# 2019/09/30 周一

# npm run 端口被占用

npm run dev退出后依旧占用端口,vscode的console,有时候可能没关闭就开了新的terminal,把vscode整体退出,在打开就可以了,可以不依赖vscode的终端,使用系统自带的terminal

# mac 查看端口占用情况:
lsof -i :7000
sudo kill -9 716
# -9后面加一个空格,然后加上占用端口的进程PID,就可以杀掉占用端口的进程。最后重启terminal就ok。
# Mac 查看端口占用情况及杀死进程 https://www.jianshu.com/p/9216b6127a82

相关问题:vue-cli3运行本地项目后,端口不随设置的随便变化 (opens new window)

上次更新: 2020/10/29 22:59:19