# 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)
- 执行顺序
- 先执行请求拦截(特意在拦截中加了一个阻塞5s的await)
- 向后端发送请求
- 触发响应拦截(这里也可能存在等待时间)
- 最后才会执行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后,可正常访问
# 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