1. 面试类
1.1 长列表页优化性能
Object.freeze 冻结list、分页
1.2 浏览器工作原理 url
1.读取缓存:
搜索自身的 DNS 缓存。(如果 DNS 缓存中找到IP 地址就跳过了接下来查找 IP 地址步骤,直接访问该 IP 地址。)
2.DNS 解析:将域名解析成 IP 地址
3.TCP 连接:TCP 三次握手,简易描述三次握手
客户端:服务端你在么?
服务端:客户端我在,你要连接我么?
客户端:是的服务端,我要链接。
连接打通,可以开始请求来
4.发送 HTTP 请求
5.服务器处理请求并返回 HTTP 报文
6.浏览器解析渲染页面
7.断开连接:TCP 四次挥手
关于第六步浏览器解析渲染页面又可以聊聊如果返回的是html页面
根据 HTML 解析出 DOM 树
根据 CSS 解析生成 CSS 规则树
结合 DOM 树和 CSS 规则树,生成渲染树
根据渲染树计算每一个节点的信息
根据计算好的信息绘制页面
1.3 http缓存分类
Http缓存可以分为两大类,强制缓存(也称强缓存)和协商缓存。两类缓存规则不同,强制缓存在缓存数据未失效的情况下,不需要再和服务器发生交互;而协商缓存,顾名思义,需要进行比较判断是否可以使用缓存。
两类缓存规则可以同时存在,强制缓存优先级高于协商缓存,也就是说,当执行强制缓存的规则时,如果缓存生效,直接使用缓存,不再执行协商缓存规则。
强缓存:强制缓存分为两种情况,Expires和Cache-Control
Expires基本被淘汰
Cache-control的值有:
1.max-age 设置强缓存时间
2.public 表示可以被浏览器和代理能走缓存
3.immutable 设置浏览器点击刷新按钮也走强缓存
4.no-cache 不走强缓存,但会走协商缓存
5.no-store 不走强缓存,也不走协商缓存
协商缓存:有etag和last-modified设置
怎么设置协商缓存?
response header里面的设置
etag: '5c20abbd-e2e8'
last-modified: Mon, 24 Dec 2018 09:49:49 GMT
Etag: 每个文件有一个,改动文件了就变了,就是个文件hash,每个文件唯一,就像用webpack打包的时候,每个资源都会有这个东西,如: app.js打包后变为 app.c20abbde.js,加个唯一hash,也是为了解决缓存问题。
last-modified:文件的修改时间,精确到秒
也就是说,每次请求返回来 response header 中的 etag和 last-modified,在下次请求时在 request header 就把这两个带上,服务端把你带过来的标识进行对比,然后判断资源是否更改了,如果更改就直接返回新的资源,和更新对应的response header的标识etag、last-modified。如果资源没有变,那就不变etag、last-modified,这时候对客户端来说,每次请求都是要进行协商缓存了,即:
发请求-->看资源是否过期-->过期-->请求服务器-->服务器对比资源是否真的过期-->没过期-->返回304状态码-->客户端用缓存的老资源
参考: www.jianshu.com/p/9c95db596…
1.4 js加载方式(js的循环机制)
js加载分为同步和异步
js是单线程程序,一般情况下代码会一步一步执行,如果遇到错误,write,alert则会阻塞运行。
异步情况其实是把一部分代码放入任务队列中,让它延后执行,执行完主线程,任务队列的任务添加 到主线程加载执行,往复循环,这也是js的循环机制
异步执行顺序: 微任务:promist、宏任务:setinterval、 settimeout
1.5 node的异步回调
1.6 项目中遇到的坑
1、 在vue中,v-model.trim写在文本域中,按回车不会换行,因为trim把换行当成了空格
2、 在使用element-ui form表单时,遇到多个form,form里input按回车会触发表单提交。从而使页面会刷新 (在el-form中加入@submit.native.prevent)
3、 element ui upload终止上传 return false
4、 element ui 下拉框不随着页面滚动而移动问题popper-append-to-body = "false" 加这个属性,加了下拉框就不会随页面滚动而滚动了。要改其样式就不能加scoped了
5、 webpack用url-loader处理图片路径时,build打包后static里的图片加载不出来(在url-loader里的include里只加了’src’,而static与src同级,应把static也包含在内解析)
6、 IE兼容问题:
-
new Date(“2019-02-22”)转换失败,需要换成斜杠格式的日期,如:var t = new Date (“2019/02/22”)
-
this.$route.query 如果参数太长,会导致ie页面奔溃,样式全部坍塌
3. elementUI的下拉框,分页器,联级选择器在ie中点击后会出现光标闪动(在main.js中注册element时在这三个组件的生命周期brforeMount里设置属性setAttribute('unselectable', 'on'))
7、 windows和linux构建差异,windows会兼容文件的大小写,而linux严格区分大小写,导致在服务器找不到文件,构建失败
8、 node_modules版本问题
有次element-ui 的表格显示不出来,以为是包有冲突,去调试各种包都没效果,然后猜测是element-ui版本问题,重新cnpm i了无数次表格也没出来,看了下package.json里element-ui版本是^2.12.0,是项目的版本,然后去node_modules里看_element-ui和element-ui包的版本,是2.13.0,原来是package.json里^会固定大版本更新中版本,所以每次install时,都会下载1.13.0,换成写死版本或者用~固定中版本,更新小版本就解决了
9、 使用tinymce富文本中得插入代码,cdn引入prism让代码高亮显示,js代码不生效,用npm下载prismjs,在.babelrc文件中配置prism才生效
1.7 性能优化
1.7.1 基础优化
1.js,css,html,图片等压缩
2.js置底,css放入head标签中
3.小图片使用data:url,精灵图等
4.减少闭包使用,减少全局变量,代码规范,标签语义化
5.减少http请求,利用浏览器缓存
6.按钮防抖,滚动节流,窗口缩放节流等
7.减少dom操作
8.js库按需引入
9.循环加key,不适用index作为key
10.减少dom重排/回流 重绘(重排/回流是删除dom,改变dom结构时;重绘是css样式改变时;重排/回流会引起重绘)
1.7.2 Vue优化
1.vue的v-if,v-show合理使用
2.keep-alive合理使用
3.路由懒加载,组件懒加载
4.引入vue骨架
5.computed的使用,具有缓存功能
6.$nextTick的使用,会把频繁任务推到事件循环的异步队列里,最后只会执行一次dom更新操作
1.7.3 React优化
1.setState的使用,异步回掉,最后只会执行一次state更新操作
2.shouldComponentsUpdate控制不相关props,返回false,让子组件不更新
3.render不执行state更新操作,会造成死循环
4.不在jsx中绑定this,要在constructor里绑定this
5.循坏加唯一key值
1.7.4 webpack优化
1、 devtool:eval 速度更快,缺点是不能正确显示console.log的打印行数
2、 DllPlugin, HappyPack,OptimizeCSSPlugin等
1.7.5 服务端优化
1.express托管dist文件时启用gzip中间件,压缩代码
const compression = require("compression")
//启用gzip中间件,在托管之前
app.use(compression())
2.使用强制缓存和协商缓存
3.减少不必要的返回字段。如列表不返回博客内容
1.8 如何劫持 XMLHttpRequest 的 send() 方法,调用他的时候,把参数输出到控制台
xhr.onreadystatechange = function(){
if(xhr.status == 200 && xhr.readyState == 4){
console.log(xhr.responseText);
}
1.9 xss 攻击的原理和预防
1.10 数组去重
· var arr = ["1", "2", "2", "3"]
· (1) 利用es6 Set和扩展运算符
· [...new Set(arr)]
· (2) 排序后判断前后两个相不相等,注意排序参数ab问题,需把数组第一个的值push进去
· function unique (arr) {
· arr = arr.sort();
· var array = [arr[0]];
· arr.sort((a, b) => {
· if (a !== b) {
· array.push(a);
· }
· });
· return array;
· }
· (3)利用对象key不重复特性
· function unique (arr) {
· var obj = {};
· arr.forEach(ele => { obj[ele] = undefined; });
· return Object.keys(obj);
· }
· (4)filter循环,利用indexOf第二个参数,每次从第一个开始找,不重复的才返回
· function unique (arr) {
· return arr.filter(function (item, index, arr) {
· // 当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
· return arr.indexOf(item, 0) === index;
· });
· }
· (5)利用hasOwnProperty
· function unique (arr) {
· var obj = {};
· return arr.filter(function (item, index, arr) {
· return obj.hasOwnProperty(typeof item + item) ? false : (obj[typeof item + item] = true);
· });
· }
· (6) 利用reduce和includes
· function unique (arr) {
· return arr.reduce((prev, cur) => {
· return prev.includes(cur) ? [...prev] : [...prev, cur];
· }, []);
· }
1.11 手写promise
1.12 手写new
// 实现类似new的函数,接受一个构造函数作为第一个参数
function create () {
let obj = {}
// 获取构造函数
let Constru = [].shift.call(arguments)
obj.proto = Constru.prototype
// 其他参数
let result = Constru.apply(obj, arguments)
// 判断apply的返回值是考虑构造函数中会有返回值,这时result就为构造函数的返回值
return result instanceof Object ? result : obj
}
1.13 手写call、apply、bind
1.13.1 Call
Function.prototype.myCall = function (content) {
const fn = Symbol();
content[fn] = this;
content[fn](…Array.prototype.slice.call(arguments, 1))
delete content[fn]
}
1.13.2 Apply
Function.prototype.myApply = function (content) {
let fn = Symbol();
let param = arguments[1] || [];
content[fn] = this;
contentfn;
delete content[fn];
};
1.13.3 Bind:
简单版
Function.prototype.myBind = function (content) {
let that = this;
let args = Array.prototype.slice.call(arguments, 1);
return function () {
return that.apply(content, args.concat(Array.prototype.slice.call(arguments)));
};
};
完整版
Function.prototype.bind = function(context) {
var that = this;
var args = Array.prototype.slice.call(arguments, 1);
var bound = function() {
if(this instanceof bound) { // 如果是作为构造函数,那么第一个参数 context 就不需要了
return that.apply(this, args.concat(Array.prototype.slice.call(arguments)));
} else {
return that.apply(context, args.concat(Array.prototype.slice.call(arguments))) ;
}
}
// 维护原型关系
if(this.prototype) {
bound.prototype = this.prototype;
}
1.14 手写防抖、节流
export let debounce = function (fn, time = 2000) {
let timeout = null;
return function () {
let arg = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.apply(this, arg);
}, time);
};
};
export let throttle = function (fn, time = 2000) {
let canRun = true;
return function () {
let this_ = this;
let arg = arguments;
if (!canRun) return;
canRun = false;
setTimeout(() => {
canRun = true;
fn.apply(this_, arg);
}, time);
};
};
1.15 手写深拷贝
export let deepClone = function (tar) {
if (!tar || typeof (tar) !== 'object') {
return tar;
}
let current = (tar instanceof Array ? [] : {});
for (const key of Object.keys(tar)) {
current[key] = deepClone(tar[key]);
}
return current;
};
1.16 手写reduce
值得注意:如果有初始值的话,回调函数就会从数组的第0项开始执行,也就是会执行arr.length次;
但是如果没有初始值的话,会默认取数组的第0项为初始值,回调函数会从数组的第1项开始执行,也就是会执行arr.length - 1次
Array.prototype.MyReduce = function (fn, initialValue) {
var arr = Array.prototype.slice.call(this);
var pre, startIndex;
pre = initialValue ? initialValue : arr[0];
startIndex = initialValue ? 0 : 1;
for (var i = startIndex; i < arr.length; i++) {
pre = fn.call(null, pre, arr[i], i, this)
}
return pre
}
1.17 Css新增特性
1.18 http与https区别
1.19 什么是MVVM
MVVM 是Model-View-ViewModel 的缩写,它是一种基于前端开发的架构模式,其核心是提供对``View ``和`` ViewModel ``的双向数据绑定,这使得ViewModel 的状态改变可以自动传递给 View,即所谓的数据双向绑定。
1.20 Redux,react-redux,redux-thunk,redux-saga都是什么
1.redux是react得状态管理器。解决层层组件传值问题,属于单项数据流,视图components调用action到store,store接受reducer,reducer返回改变后state给store,store再改变components视图。
2.react-redux是为了在react中更加方便使用redux。用provider包裹最外层组件,把store传入,再去用使用store组件中用connect连接。传递两个参数:mapStateToProps,mapDispatchToProps.
3.redux-thunk是redux的中间件。使得action可以返回对象或一个函数。函数接收一个dispatch参数,可派发action。异步请求可写在actino中间件中
4.redux-saga是redux的中间件。可拦截action。在saga文件中处理副作用函数。众多辅助函数可供使用:put, call, takeEvery.
1.21 React面试常见问题
1.21.1 什么是React?React有什么特点?
React 是一个用于构建用户界面的JavaScript 库。React主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
React 特点
1.声明式设计 −React采用声明范式,可以轻松描述应用。
2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
3.灵活 −React可以与已知的库或框架很好地配合。
4.JSX − JSX是 JavaScript语法的扩展。React开发不一定使用 JSX,但我们建议使用它。
5.组件 − 通过 React构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
1.21.2 react中的性能优化有哪些方式?
1)react通过操纵虚拟DOM,不进行节点操作,最大限度的减少与真实DOM的交互,这样可以提高性能;
2)shouldComponentUpdate,在这个生命周期内,我们可以进行新旧两个state和props的对比,如果数据没有发生变化,这个函数就会返回false,这样后面的生命周期就不会执行,render函数也不会重新渲染,这样也可以提升性能;
3)immutable,immutable的特点就是不可修改,改变它的任何数据时,都会重新生成一个新的对象,修改只会在新生成的对象上修改,原数据不会发生改变,这样就可以避免把所有节点都复制一遍,降低性能损耗;immutable的实现原理就是数据结构共享;
1.21.3 react中如何修改state中的数据?this.setState是同步的还是异步的?this.setState中的第二个参数的作用是什么?为什么是异步的?
1)通过this.setState来修改state中的数据;
2)this.setState是异步的;
3)其中有两个参数,第一个参数是一个对象或者是一个函数(必须返回一个对象),
函数中的第一个值为(prevState),第二个参数是(prevProps)
1 例:
this.setState((prevState,prevProps)=>({ }))
4)为什么是异步的,一位state可以批量执行,也就是说当多个setState一起同时执行时会被合并,提高DOM的渲染效率;
5)this.setState什么时候是同步的?原生js绑定的事件,setTimeout/setInterval等,(就是不受react机制控制时)
6)this.setState本身其实是一个同步的,异步不是因为本身的运行机制或者代码,而是因为他所在的合成事件和钩子函数的调用顺序在更新之前,导致函数内没法立即拿到更新后的值,形成了所谓的异步,可以通过第二个参数中的callback拿到更新后的结果;
1.21.4 你了解 Virtual DOM 吗?解释一下它的工作原理。
虚拟dom是先将真实的DOM抽象成一个JavaScript对象,也就是虚拟DOM。虚拟dom采用diff算法,能够高效得对比前后差异。然后更新差异后得dom。
1.21.5 与 ES5 相比,React 的 ES6 语法有何不同?
Es6采用import引入方式,结构赋值等快速定义变量,class语法,calss继承等快速创建组件。Es6语法简介高效
1.21.6 区分有状态和无状态组件
有状态组件是继承react.components创建得组件,具有生命周期等一些函数
无状态组件 它是一种函数式组件,没有state, 接收Props,渲染DOM,而不关注其他逻辑
无状态组件性能更高
1.21.7 React中的合成事件是什么?
合成事件是围绕浏览器原生事件充当跨浏览器包装器的对象。它们将不同浏览器的行为合并为一个 API。这样做是为了确保事件在不同浏览器中显示一致的属性
1.21.8 react的受控组件和不受控组件是什么
1.21.9 如何给非控组件设置默认的值?
1.21.10 为什么说React中的props是只读的
需符合单项数据流模式
1.21.11 super()和super(props)有什么区别?
在JavaScript中,super指的是父类构造函数。在调用父构造函数之前,不能在构造函数中使用this
2. 类似苹果高斯模糊遮罩效果
outline: 9999px solid rgba(0,0,0,.5); 设置这个盒子外的遮罩范围
backdrop-filter: blur(5px) 高斯模糊
3. 判断复杂数据类型
Object.prototype.toString.call({}) // "[object Object]"
Function.prototype.call.bind(Object.prototype.toString())和上面的等价,许多源码都会用这个代替上面的方法,优点是不会覆盖自定义在原型上的call方法
4. vue $refs 获取元素距离顶部的距离
this.$refs.divRef.getBoundingClientRect().top
5. vue强制刷新页面,可代替this.$set()
this.$forceUpdate()
6. git tag: vs code git本地tag和远程tag对不上 报错:(would clobber existing tag)
用git ls-remote -t 查看远程tags
git tag -l查看本地tag
然后用 git tag -d xxx删除本地tag
最后远程拉取远程tags git fetch origin --prune-tags
参考地址: www.cnblogs.com/little-ab/p…
更简便方法:git tag –d xxx 再 git pull
7. 创建tag: git tag xxx , git push origin xxx
8. git reset版本回退
git reset --hard c4718176a1de899bc754bde40c0fd3de081ec93a
git push –f
9. git reset --soft HEAD^ 或 git reset --soft HEAD~1撤回上一次commit操作
10. git merge [branch name] --no-commit,为了防止合并失败并不自动提交,能够给使用者一个机会在提交前审视和修改合并结果
git merge [branch name] --no-commit
11. git rebase
git rebase操作实际上是将当前执行rebase分支的所有基于原分支提交点之后的commit打散成一个一个的patch,并重新生成一个新的commit hash值,再次基于原分支目前最新的commit点上进行提交,并不根据两个分支上实际的每次提交的时间点排序,rebase完成后,切到基分支进行合并另一个分支时也不会生成一个新的commit点,可以保持整个分支树的完美线性
另外值得一提的是,当我们开发一个功能时,可能会在本地有无数次commit,而你实际上在你的master分支上只想显示每一个功能测试完成后的一次完整提交记录就好了,其他的提交记录并不想将来全部保留在你的master分支上,那么rebase将会是一个好的选择,他可以在rebase时将本地多次的commit合并成一个commit,还可以修改commit的描述等
11.1 git cherry-pick 命令的作用,就是将指定的提交(commit)应用于其他分支。
12. element ui 表单重置
this.$refs.form.resetFields();
// 单独验证某一个form-item
this.$refs.form.validateField('point')
分解入参,变成数组
Array.prototype.slice.call(arguments) (element ui 每个组件也是有生命周期的,全局改动时可利用生命周期改动,如ElementUI.Select.mounted)
13. element ui switch开关字体在开关内显示
.switch-box {
// 设置宽度
/deep/.el-switch__core {
width: 55px !important;
}
/deep/.el-switch__label--left{
position: relative;
left: 60px;
color: #fff;
z-index: -1111;
}
/deep/.el-switch__label--right{
position: relative;
right: 60px;
color: #fff;
z-index: -1111;
}
/deep/.el-switch__label.is-active{
z-index: 1111;
color: #fff;
}
/deep/.el-switch__label--right.is-active{
z-index: 1111;
color: #fff !important;
}
/deep/.el-switch__label--left.is-active{
z-index: 1111;
color: #9c9c9c !important;
}
}
14. element ui upload终止上传 return false
15. element ui 下拉框不随着页面滚动而移动问题
popper-append-to-body = "false" 加这个属性,加了下拉框就不会随页面滚动而滚动了。要改其样式就不能加scoped了
16. element ui日期区间选择,限制选择区间
pickerOptions: {
disabledDate (time) {
// 区间为当前时间至一个月后
return (time.getTime() < Date.now() - 8.64e7) || (time.getTime() > new Date(new Date().setMonth(new Date().getMonth() + 1)).valueOf());
}
}
17. 如何在element 表单验证validator的验证规则里传参(再套一层函数)
let pureNumber = (msg) => {
return (rule, value, callback) => {
const valReg = /^[0-9]*$/
if (!valReg.test(value) && !!value) {
return callback(new Error(${msg}应为纯数字!))
} else {
callback()
}
}
}
18. position: sticky;top: 0; 简单实现导航栏跟随置顶效果
19. watch 深度监听deep
watch 监听 '$route.meta', deep: true深度监听,只监听数组变化,属性变化监听无效
数组(一维、多维)的变化不需要通过深度监听,对象数组中对象的属性变化则需要deep深度监听。
watch: {
'$route.meta': {
handler (value) {
console.log(value)
value.businessBread && value.businessBread.length && (this.businessBread = value.businessBread)
value.pageDescrib && (this.pageDescrib = value.pageDescrib)
},
deep: true
}
}
20. 校验时间不交叉不重复
let timeCrossRules = (rule, value, callback) => {
if (value[0] === value[1]) {
return callback(new Error('时间区间不能为0'))
}
let dateValue = this.form.availables && this.form.availables.map(ele => ele.dateValue)
// 判断数组都不为空
if (dateValue.some((value) => { return !value })) {
return
}
// 排序
dateValue = dateValue.sort((next, last) => { return Number(next[0].split(':').join('')) - Number(last[0].split(':').join('')) })
// 如果前一个数组的第0个小于当前数组的第1个,则return出来,为true,就表示有重复和交叉
let dataFlag = dateValue.some((value, index, array) => {
if (index > 0) {
return Number(dateValue[index][0] && dateValue[index][0].split(':').join('')) < (Number(dateValue[index - 1] && dateValue[index - 1][1].split(':').join('')))
}
})
// 为true,就表示有重复和交叉
if (dataFlag) {
callback(new Error('时间不能交叉或重叠'))
}
callback()
}
21. JSON.parse(JSON.stringify())的弊端
1.无法解决循环引用的问题 如:
const a = {val: 2}
a.targe = a
2. 无法拷贝一些特殊对象,诸如 RegExp, Date, Set, Map等
3. 无法拷贝函数
var obj = {a: 1, b: {a: 1, b: 2}, c: /a/, d: function (){}}
JSON.parse(JSON.stringify(obj))
// {a: 1, b: {a: 1, b: 2}, c: {}}
22. 完整的深拷贝函数 (见神三元 掘金)
23. 普通深拷贝 (缺点是无法解决循环引用)
deepcopy = function (source) {
if (!source) {
return source
}
let sourceCopy = source instanceof Array ? [] : {}
for (let item in source) {
sourceCopy[item] =
typeof source[item] === 'object' ? deepcopy(source[item]) : source[item]
}
return sourceCopy
}
或
function deepClone (tar) {
if (typeof tar === 'object') {
let cloneTar = Array.isArray(tar) ? [0] : {}
for (const key in tar) {
cloneTar[key] = deepClone(tar[key])
}
return cloneTar
} else {
return tar
}
}
24. 可以用函数parseInt(3).toString(2),将10进制的3转换为2进制
25. lodash的常用方法
- _.omit(obj, [ 'type' ]) // 拿到obj里除了type属性组成的对象
2. _.debounce(function () {...}, 1000, { 'leading': true, 'trailing': false }) // 防抖
3. 判断两个对象是否相等
_.isEqual(obj1, obj2)
4. 取一个对象里有相同的属性并赋值 pick: 赋值, keys: 拿相同的属性
var model = {
fname: null,
lname: null
}
var credentials = {
fname: 'xyz',
lname: 'abc',
age: 23
}
var result = _.pick(credentials, _.keys(model))
console.log(result, 'ddddd')
5.拿到相同属性的值,并保留原来的值
let tar = {a: '', b: '', c: '', e: ''}
let obj = {a: '1', b: '2', c: '3', d: '5'}
console.log(this.getEqualData(tar, obj), 'ddddd')
getEqualData (tar, obj) {
tar = _.cloneDeep(tar)
obj = _.cloneDeep(obj)
for (const item of Object.keys(tar)) {
for (const ite of Object.keys(obj)) {
if (item === ite) {
tar[item] = obj[ite]
}
}
}
return tar
},
26. some、every
some: 遇到一个true就为true,全为false才为false
every: 全为true才为true, 碰到一个false则为false
27. 创建一个虚拟标签,获取标签信息,如音频 图片等
new Image()
beforeAvatarUpload (file, typeList = ['jpeg'], size = 5) {
const isType = typeList.some((ele) => file.type === image/${ele})
const isLtM = file.size / 1024 / 1024 < size
let alertMsg = ''
if (!isType) {
let msg = typeList.reduce((pre, cur) => {
if (cur === 'jpeg') {
return pre + ',jpg,jpeg'
} else {
return pre + ',' + cur
}
}, '')
alertMsg = 请上传${msg.substr(1)}格式的图片!
// this.{msg.substr(1)}格式的图片!`)
} else if (!isLtM) {
alertMsg = 请选择小于${size}M大小的图片!
}
// 图片文件大小限制,限制宽高分别为1280px和800px
let _this = this
let imgWidth = ''
let imgHight = ''
const viewport = new Promise(function (resolve, reject) {
let width = 100
let height = 10
let _URL = window.URL || window.webkitURL
let img = new Image()
img.onload = function () {
imgWidth = img.width
imgHight = img.height
let valid = img.width === width && img.height === height
console.log(typeof img.width, img.height, valid, 'with')
alertMsg = valid ? alertMsg : '上传文件的图片大小不合符标准,宽需要为1280px,高需要为800px。当前上传图片的宽高分别为:' + imgWidth + 'px和' + imgHight + 'px'
isType && isLtM && valid ? resolve() : reject(new Error())
}
img.src = _URL.createObjectURL(file)
}).then(() => {
return file
}, () => {
_this.$message.warning(alertMsg)
return Promise.reject(new Error())
})
console.log(viewport)
return viewport
// return isType && isLtM
},
28. 自定义指令监听div宽高变化
directives: { // 使用局部注册指令的方式, 监听某元素宽高变化
resize: { // 指令的名称
bind (el, binding) { // el为绑定的元素,binding为绑定给指令的对象
let width = ''
let height = ''
function isReize () {
const style = document.defaultView.getComputedStyle(el)
if (width !== style.width || height !== style.height) {
binding.value() // 关键
}
width = style.width
height = style.height
}
el.vueSetInterval = setInterval(isReize, 300)
},
unbind (el) {
clearInterval(el.vueSetInterval)
}
}
}
****监听浏览器窗口大小变化 ,自带的resize组件(注意 及时销毁)
window.addEventListener('resize', function () { console.log('resize'); });
29. 重置vue当前data里的值
this.form = this.$options.data().form
30. IE兼容问题
-
new Date(“2019-02-22”)转换失败,需要换成斜杠格式的日期,如:var t = new Date (“2019/02/22”)
-
this.$route.query 如果参数太长,会导致ie页面奔溃,样式全部坍塌
3. elementUI的下拉框,分页器,联级选择器在ie中点击后会出现光标闪动(在main.js中注册element时在这三个组件的生命周期brforeMount里设置属性setAttribute('unselectable', 'on'))
- noscript 标签不支持javascript的浏览器会显示此标签
31. 区分ie ie6 ie7 ie8的方法
var isIE=!!window.ActiveXObject;
var isIE6=isIE&&!window.XMLHttpRequest;
var isIE8=isIE&&!!document.documentMode;
var isIE7=isIE&&!isIE6&&!isIE8;
32. 用nodejs,将base64转化成png文件,或者相反
// 将base64格式转成png格式
const baseData = require('./data.js')
const fs = require('fs');
const pngPath = 'config/image/'+ Date.now() +'.png';
const base64 = baseData.replace(/^data:image/\w+;base64,/, "");//去掉图片base64码前面部分data:image/png;base64
const dataBuffer = new Buffer(base64, 'base64'); //把base64码转成buffer对象,
fs.writeFile(pngPath, dataBuffer, function(err){ //用fs写入文件
if(err){
console.log(err, '------------------------err');
}else{
console.log('写入成功!');
}
})
// 将png或者其它格式转成base64
const path = require('path');
const mineType = require('mime-types');
let filePath = path.resolve('config/image/1586918583626.png');
let data = fs.readFileSync(filePath);
data = new Buffer(data).toString('base64');
let base64Data = 'data:' + mineType.lookup(filePath) + ';base64,' + data;
console.log(base64Data, 'base64Data')
// fs.writeFileSync(path.resolve('your/save/file/path'), base64);
33. 提交时,滚动到第一个验证提示处 scrollIntoView
toError () {
if (document.getElementsByClassName('is-error').length > 0) {
document.getElementsByClassName('is-error')[0].scrollIntoView({behavior: 'smooth', block: 'start', inline: 'nearest'});
}
}
34. 创建一个长度为10,值为undefined的数组
Array.apply(null, {length: 10})
Array.apply(null, Array(10))
Array(10).fill()
Array.from(Array(10))
Array.from({length: 10} , () => undefined)
[...Array(10)]
Array.from(Array(100), (v,k) =>k) // 生成0-100的数组
35. 怎样把一个类数组变成数组
1.for of循环添加进去
2.es6新方法 Array.from(类数组)
- Array.prototype.slice.call(类数组)
36. 防抖,节流
export let debounce = function (fn, time = 2000) {
let timeout = null;
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn.call(this, arguments);
}, time);
};
};
export let throttle = function (fn, time = 2000) {
let canRun = true;
return function () {
let this_ = this;
let arg = arguments;
if (!canRun) return;
canRun = false;
setTimeout(() => {
canRun = true;
fn.call(this_, arg);
}, time);
};
};
37. 文件流怎么下载excel文件
后台返回excel文件回来,转成blob格式下载,axios请求时就处理
import Axios from '@/assets/js/AxiosPlugin.js';
import FileSaver from 'file-saver';
export const exportLocationsData = data => {
return Axios({
responseType: 'blob',
method: 'post',
url: exportLocations,
data: data
})
.then(
res => {
let blob = new Blob([res.data], {type: 'application/vnd.ms-excel'});
FileSaver.saveAs(blob, '足迹地图点位信息.xlsx');
}
);
};
38. vue富文本 大神推荐: tinymce
管理后台富文本也是一个非常重要的功能,楼主在这里也踩了不少的坑。楼主在项目里最终选择了 tinymce
这里在简述一下推荐使用tinymce的原因:tinymce 是一家老牌做富文本的公司(这里也推荐 ckeditor,也是一家一直做富文本的公司,新版本很不错),它的产品经受了市场的认可,不管是文档还是配置的自由度都很好。在使用富文本的时候有一点也很关键就是复制格式化,之前在用一款韩国人做的富文本summernote被它的格式化坑的死去活来,但 tinymce 的去格式化相当的好,它还有一个增值项目就是powerpaste,那是无比的强大,支持从word里面复制各种东西,都不会有问题。富文本还有一点也很关键,就是拓展性。楼主用tinymce写了好几个插件,学习成本和容易度都不错,很方便拓展。最后一点就是文档很完善,基本你想得到的配置项,它都有。tinymce也支持按需加载,你可以通过它官方的build页定制自己需要的plugins。 我再来分析一下市面上其它的一些富文本:
- summernote 先来说一个我绝对不推荐的富文本。这是一个韩国人开源的富文本(当然不推荐的理由不是因为这个),它对很多富文本业界公认的默认行为理解是反起到而行的,而且只为用了一个dialog的功能,引入了boostrap,一堆人抗议就是不改。格式化也是差劲。。反正不要用!不要用!不要用!
- ckeditor ckeditor也是一家老牌做富文本的公司,楼主旧版后台用的就是这个,今年也出了5.0版本,ui也变美观了不少,相当的不错,而且它号称是插件最丰富的富文本了。推荐大家也可以试用一下。
- quill 也是一个非常火的富文本,长相很不错。基于它写插件也很简单,api设计也很简单。楼主不选择它的原因是它对图片的各种操作不友善,而且很难改。如果对图片没什么操作的用户,推荐使用。
- medium-editor 大名鼎鼎的medium的富文本(非官方出品),但完成度还是不很不错,拓展性也不错。不过我觉得大部分用户还是会不习惯medium这种写作方式的。
- Squire 一个比较轻量的富文本,压缩完才11.5kb,相对于其它的富文本来说是非常的小了,推荐功能不复杂的建议使用。
- wangEditor 一个国人写的富文本,用过感觉还是不错的。不过毕竟是个人的,不像专门公司做富文本的,配置型和丰富性不足。前端几大禁忌就有富文本 为什么都说富文本编辑器是天坑?,不过个人能做成这样子很不容易了。
- 百度UEditor 没有深入使用过,只在一个angular1X的项目简单用过,不过说着的ui真的不好看,不符合当今审美了,官方也已经很久没跟新过了。
楼主列举了很多富文本但并没有列举任何 vue 相关的富文本,主要是因为富文本真的比想象中复杂,在前面的文章里也说过了,其实用 vue 封装组件很方便的,没必要去用人家封装的东西什么vue-quill vue-editor这种都只是简单包了一层,没什么难度的。还不如自己来封装,灵活性可控性更强一点。还有一点基于 vue 真没什么好的富文本,不像 react 有 facebook 出的 draft-js,ory 出的 editor,这种大厂出的产品。
当然你也可以选择一些付费的富文本编辑器,作者自己公司里面有一个项目就使用了 froala-editor 这款编辑器。不管是美观和易用性都是不错的,公司买的是专业版,一年也就 $349 ,价格也是很合理的,但其实省去的程序员开发陈本可能远不止这个价钱。
作者:花裤衩
链接:juejin.im/post/593121…
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
39. Webpack常用配置
39.1 Loaders
*babel-loader
将JS代码向低版本转换
*style-loader
动态创建 style 标签,将 css 插入到 head 中
*css-loader
负责处理 @import 等语句
*postcss-loader/ autoprefixer
自动生成浏览器兼容性前缀
*less-loader
负责处理编译 .less 文件,将其转为 css
*eslint-loader
.eslintrc.js文件校验规则
*vue-loader
负责处理编译 .vue 文件,将其转为 css
*url-loader
处理本地的资源文件,可以指定在文件大小小于指定的限制时,返回 DataURL
*vue-style-loader
处理vue文件里style文件
39.2 Plugins
*webpack.DefinePlugin
在src等开发文件中能访问到里面的全局变量,一般定义process.env.NODE_ENV区分环境
允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。如果在开发构建中,而不在发布构建中执行日志记录,则可以使用全局常量来决定是否记录日志。这就是 DefinePlugin 的用处,设置它,就可以忘记开发和发布构建的规则。
*webpack.HotModuleReplacementPlugin也被称为 HMR
热更新
*webpack.NoEmitOnErrorsPlugin
在编译出现错误时,使用 NoEmitOnErrorsPlugin 来跳过输出阶段。
如果你在使用 CLI(命令行界面command-line interface),启用此插件后,webpack 进程遇到错误代码将不会退出。
*webpack.HashedModuleIdsPlugin
该插件会根据模块的相对路径生成一个四位数的hash作为模块id, 建议用于生产环境
*webpack.optimize.ModuleConcatenationPlugin
过去 webpack 打包时的一个取舍是将 bundle 中各个模块单独打包成闭包。这些打包函数使你的 JavaScript 在浏览器中处理的更慢。相比之下,一些工具像 Closure Compiler 和 RollupJS 可以提升(hoist)或者预编译所有模块到一个闭包中,提升你的代码在浏览器中的执行速度。
这个插件会在 webpack 中实现以上的预编译功能。
*webpack.optimize.CommonsChunkPlugin
CommonsChunkPlugin 插件,是一个可选的用于建立一个独立文件(又称作 chunk)的功能,这个文件包括多个入口 chunk 的公共模块。
通过将公共模块拆出来,最终合成的文件能够在最开始的时候加载一次,便存到缓存中供后续使用。这个带来速度上的提升,因为浏览器会迅速将公共的代码从缓存中取出来,而不是每次访问一个新页面时,再去加载一个更大的文件
*webpack.NamedModulesPlugin
当开启 HMR(webpack.HotModuleReplacementPlugin) 的时候使用该插件会显示模块的相对路径,建议用于开发环境
*webpack.DllPlugin
打包不经常更新的模块,生成vendor.dll.js,让热更新更加快
用某种方法实现了拆分 bundles,同时还大大提升了构建的速度
*webpack.optimize.UglifyJsPlugin
压缩 只是为了包更小一点
*html-webpack-plugin
打包html,引入对应的js
*friendly-errors-webpack-plugin
*vue-skeleton-webpack-plugin
vue页面骨架,在浏览器加载慢时,加入自定义页面或动画,不至于出现空白
*add-asset-html-webpack-plugin
*copy-webpack-plugin
将单个文件或整个目录复制到构建目录
*webpack-bundle-analyzer
*speed-measure-webpack-plugin
*extract-text-webpack-plugin / mini-css-extract-plugin
抽离css,即将CSS文件单独打包,这可能是因为打包成一个JS文件太大,影响加载速度,也有可能是为了缓存
*optimize-css-assets-webpack-plugin
将抽离出来的css进行压缩
40. requestAnimationFrame
html5提供的专门用于请求动画的API,更适合代替setTimeout / setInterval渲染动画
41. requestAnimationFrame如何平滑滚动到页面顶部
const scrollToTop = () => {
const c = document.documentElement.scrollTop || document.body.scrollTop;
if (c > 0) {
window.requestAnimationFrame(scrollToTop);
window.scrollTo(0, c - c / 8);
}
}
scrollToTop()
42.可选运算符?.插件: @babel/plugin-proposal-optional-chaining