note

85 阅读21分钟

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兼容问题:

  1. new Date(“2019-02-22”)转换失败,需要换成斜杠格式的日期,如:var t = new Date   (“2019/02/22”)

  2. 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的常用方法

  1. _.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.message.error(请上传message.error(`请上传{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兼容问题

  1. new Date(“2019-02-22”)转换失败,需要换成斜杠格式的日期,如:var t = new Date   (“2019/02/22”)

  2. this.$route.query 如果参数太长,会导致ie页面奔溃,样式全部坍塌

3. elementUI的下拉框,分页器,联级选择器在ie中点击后会出现光标闪动(在main.js中注册element时在这三个组件的生命周期brforeMount里设置属性setAttribute('unselectable', 'on'))

  1. 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(类数组)

  1. 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