健儿

健儿 查看完整档案

杭州编辑北方民族大学  |  化工机械 编辑公司  |  前端搬砖 编辑填写个人主网站
编辑

掌握好原生js。

个人动态

健儿 发布了文章 · 4月16日

记录腾讯云重庆分公司社招面试经历

直接就是三道笔试题,做了20分钟,发现每一道能做完整,最后一道是算法,看题都难,果断放弃,总体来说就是笔试估计就是0分吧。
然后就跟面试官说,要不就算了吧,我一道都不会,他说没关系,笔试只是考察思路,然后继续开始面试了。
不管是滴滴还是腾讯的面试,一轮上几乎不会问很基础的问题了,例如什么css啊,js基础问题啊,(但阿里的一轮却问,有时候css还问得挺深的,害...)
因为我简历上有写,开发npm插件(实际项目就是一个简单的dialog,然后发布到了npm上,可供多个h5仓库使用,当时也是做得比较简陋的)主要问:

1、npm 插件开发发布命令和流程?
2、package.json文件重要的信息有哪些?
3、dependencies 和devDependencies的区别?
4、^和~的区别和含义?
问题1、2都回答出来了,3基本回答出来了吧,4就错了。

再问到jenkins的发布流程(都是根据我简历里的内容写的)
1、jenkins发布流程和配置
2、npm run build 执行到底发生了什么?
都回答出来了,其实2问题,主要考察对webpack打包工具的配置是否熟悉。
因为之前还自己写过一个index.html 自动inject dist静态资源的插件,所以稍微细问了下,虽然当时也是写得比较简陋,但基本还是实现了。只是后来想想用webpack插件的方式开发是最正规的。
再延伸到vue-cli脚手架配置是否熟悉,害,我还真少配,每次用到去搜索,用了就忘了。。。

再问到http,tcp和ip协议
这种东西吧,感觉就是平时工作中太偏向理论的知识了,如果没有很深刻的理解,看了书也很容易忘记,所以问我三次握手是什么?我几乎是没回答出的。

再问到vuex
三大模块,大致说了下流程,基本是没问题。但问到mutation主要是拿来干嘛的,我其实回答有点晦涩,因为曾经的项目里,mutation我还省掉过一次。

vue-router有几种模式,各种模式的区别(这里涉及到history和hash模式在nginx上配置的区别,就是刷新后出现404的情况,问我为何会这样,我没回答出)

大概就是这样的。
总体来看偏向运维的面试吧,除了后面的vuex
不知道能不能进2面,就这样吧。
三到面试题,如果有需求,可以私聊我。

查看原文

赞 0 收藏 0 评论 0

健儿 发布了文章 · 4月14日

微信公众号开发遇到的坑

先搞懂两个概念,微信sdk初始化和微信授权

微信sdk初始化
wx.config({...}),实际是有能力拿到微信提供的sdk,比如扫一扫,相册等手机的原生功能

微信授权,实际就是url+appId,重定向到微信的服务器地址,最终微信会返回url,会在url上拼上一个临时的code,用此code,可以调用后端写好的接口,可以用户用户的openid(同一个公号,同一个用户openid是一致的)

tips:
1、因为授权返回的code是临时的,所以只能用一次,如果重复使用,后端的接口会报错,大概提示此code已经被用过了。
2、因为是重定向到微信服务器地址,然后在回跳回来(带有code的回跳),所以这里实际是发生了两次reload页面,或者说至少一次reload。所以在此以前请求的api,前端的请求日志都会被清掉。这里之前在项目中就出现了获取用户的openid接口被调用了两次(临时code被用了两次),而请求network看见只请求了一次,差点还把后端坑了。

微信授权最容易出现的一个报错:
redirect_uri参数错误,或redirect_uri,10003报错,实际是未配置微信公号后台没配置访问的url域名,具体配置方法自行百度。但恶心的来了,微信公号后台配置网页授权只有两个地址,那万一项目需要配置两个以上,就无法搞了。

比如a和b 这两个域名已经占用了授权域名,但本地要访问一个c域名。可能你们自然会想到修改电脑的hosts文件啊

没问题。

你把本地的localhost 指向到了c域名,这样访问c域名依然还是会出现redirect_uri参数错误(因为公号只配置了a和b两个域名,而此时又不能修改授权)

查看原文

赞 0 收藏 0 评论 0

健儿 回答了问题 · 4月9日

cookie的失效时间已经是session,关闭浏览器(Mac下chrome)咋没有清空?

试了无数次,发现 微信浏览器,微信网页开发者工具也是这样的,真的好气啊。一直以为自己设置有问题,关闭浏览器,那个session状态的cookie始终存在,奇葩。

关注 5 回答 4

健儿 赞了文章 · 3月25日

从零实现一个 promise

切入点从场景描述出发,即先定义好我们要实现的功能

执行器函数

构造函数入参 executor 自执行函数。会在在 new 的时候同步执行,传入 resolve 和 reject 状态扭转函数。自执行函数内部根据异步任务执行结果(成功或失败)调用状态扭转函数,把状态传递给后续的 then。

状态转化

promise 有三种状态,默认是 pending,成功之后为 fulfilled,失败之后为 failed。且状态一旦发生改变,之后不可再改变也不可逆

then 方法

then 方法接受两个函数,表示成功、失败状态下的回调函数。回调函数中 return 中值会当作参数传给后续的then。以此实现链式调用。

判断一个变量是否为 promise 需要两个条件,首先要是对象,然后对象上有 then 方法,以此得出 then 执行的结果也是一个 promise

实现

class Promise {
    constructor(executor){
        this.status = 'pending',
        this.value = undefined;
        this.reason = undefined;
        this.onFulfilled =undefined;
        this.onRejected = undefined;
        this.onFulfilledList = [];
        this.onRejectedList = [];
        try {
            executor(this.resolve, this.reject);
        } catch(e) {
            this.reject(e)
        }
    }
    resolve(value){
        if(this.status == 'pending') {
            this.status = 'fullfilled';
            this.value = value;
            this.onFulfilledList.forEach(item => item())
        }
    }
    reject(reason){
        if(this.status == 'pending') {
            this.status = 'rejected';
            this.reason = reason;
            this.onRejectedList.forEach(item => item())
        }
    }
    then(onFulfilled, onRejected){
        // then 之后要返回一个新的 promise
        let result;
        if(this.status == 'fullfilled' && onFulfilled) {
            result = onFulfilled(this.value)
            return Promise.resolve(result);
        }

        if(this.status == 'rejected' && onRejected) {
            result = onRejected(this.reason);
            return Promise.resolve(result);
        }

        if(this.status == 'pending') {
            onFulfilled && this.onFulfilledList.push(()=>onFulfilled(this.value));
            onRejected && this.onRejectedList.push(() => onRejected(this.reason));
        }
    }
}

Promise.resolve = function(value) {
    if(typeof value == 'object' && value.then) {
        return value;       
    } else {
        return new Promise((resolve, reject)=>{
            resolve(value)
        })
    }
}

Promise.all = function(list) {
    return new Promise((resolve,reject) => {
        let result = [];
        let count = 0;
        for(let i = 0; i < list.length; i++)  {
            if(typeof list[i] == 'object' && list[i].then) {
                Promise.resolve(list[i]).then(data =>{
                    result[i] = data;
                    count++;
                },reject)
            }else {
                result[i] = list[i];
                count++;
            }
        }
        if(count == list.length) {
            resolve(result);
        } 
    })
   
}

// Promise.race  同理,只要有一个成功,全部  resolve
// Promise.finally 不管成功失败,传递的回调函数都会执行,执行之后仍然可以跟then

(完)

查看原文

赞 21 收藏 14 评论 0

健儿 赞了文章 · 3月25日

nextTick源码解析

1、整体入手

阅读代码和画画是一样的,忌讳一开始就从细节下手(比如一行一行读),我们先将细节代码折叠起来,整体观察nextTick源码的几大块。

折叠后代码如下图

整体观察代码结构

整体观察代码结构

上图中,可以看到:

  1. nextTick等于一个立即执行函数。函数执行后,内部返回另一个匿名函数function (cb, ctx)。从语义化命名可以分析,第一个参数cb是个回调函数、ctx这里先猜测应该是个上下文。
  2. return返回之前,立即执行函数被调用后,函数内部先用var定义了三个参数、用function声明一个函数。

先不管这些变量是干啥用的。光从语义化命名上瞎分析一下:

  • callbacks可能是一个装callback回调的数组,可能是将来有多个回调的时候模拟队列执行效果用的。
  • pending是一个布尔值。pending这个单词在接口请求中会看到,可能是用来标识某个状态是否正在进行中的。
  • timeFunc目前看来就不知道具体干啥的了。
  • nextTickHandler函数先不管。用到的时候再来看。

以上,就是初始化对代码的分析。

2、逐行解析

看完大的代码块结构后,可以按照js引擎解析代码的顺序来分析源码了。前边的变量和函数声明看完后,就到解析if语句了。

在if条件中,有一个判断:typeof MutationObserver !== 'undefined' && !hasMutationObserverBug

MutationObserver这玩意儿是干啥的?

A、MutationObserver

度娘说他“提供了监视对DOM树所做更改的能力”。大白话粗糙理解就是他能监听dom修改。

是HTML5中的一个新特性。

MutationObserver()

该属性提供一个构造函数MutationObserver()

通过new MutationObserver()可以得到一个新的观察器,它会在触发指定 DOM 事件时,调用指定的回调函数。

「MutationObserver 对 DOM 的观察不会立即启动;而必须先调用 observe() 方法来确定,要监听哪一部分的 DOM 以及要响应哪些更改。」

observe(target[, options])

启用观察者,开始根据配置监听指定DOM。无返回值。

接收两个参数:

  • target是Node/Element节点,表示要监听的DOM对象。
  • options是监听配置,配置了target的哪些变动需要出发callback回调。配置项相关参数参照MutationObserverInit配置字典

    • attributes: true|false, 观察受监视DOM元素的任意一个属性值变更
    • attributeFilter: 监听多个特定属性,放到数组里。如:['class', 'id', 'src']
    • characterData: true|false, 为true,则在更改指定要 监听的文本节点的内容时,将调用callback回调。
    • childList:true|false, 为 true 就监视指定DOM对象添加或删除新的子节点的情况
    • 还有其他好几个扩展情况。参考MutationObserverInit配置字典
> ❝
> 
> 当调用 observe() 方法时,childList,attributes 或者 characterData 三个属性之中,至少有一个必须为 true,否则会抛出 TypeError 异常。
> 
> ❞

语法

`// 得到要观察的元素
var elementToObserve = document.querySelector("#targetElementId");

// 构造MutationObserver对象,传递一个函数当做参数
var observer = new MutationObserver(callback);

// 启用观察者observe(), 监听的DOM对象是elementToObserve
observer.observe(elementToObserve, { // 监听规则,当子节点或目标节点整个节点树中的所有节点被添加/删除的时候,触发上边的callback回调函数
  subtree: true,
  childList: true
});
`

当MutationObserver监听到我们注册的DOM被改变(无论是DOM节点改变、还是DOM的属性被改变,主要监听DOM的哪部分改变啥还是看你的配置项)时,回调函数callback就会被调用。_(有点像我们派到云云DOM对象中的一个间谍,监视我们指定的dom,当发生改变时就告知我们)_

callback回调函数拥有两个参数:一个是描述所有被触发改动的 MutationRecord 对象数组,另一个是调用该函数的MutationObserver 对象。 不过这都是该属性的用法了,VUE关于nextTick的源码里关于这个属性没用到callback的这俩参数。这里不做展开讲解,详情可以看这里MDN MutationObserver()

B、if条件成立

好了,掌握了MutationObserver和他的用法后,再来回归源码,if里边的代码就很好理解了:MutationObserver判断成功

首先,作为H5新特性,其兼容性就是不太好(IE爸爸:看我干嘛!)MutationObserver兼容性

所以,vue这里做了容错,先判断MutationObserver的类型是否为“undefined”,来检查浏览器是否支持该特性。如果支持这个属性且无bug,那么就走if语句的内容

if语句内部三个var:

`var counter = 1
var textNode = document.createTextNode(counter)
var observer = new MutationObserver(nextTickHandler)
`

  • 定义了一个counter数字
  • textNode变量用于存放document.createTextNode创建的一个文本节点,文本内容是counter的值
  • new MutationObserver()这一行,相信有了上边知识点的铺垫,你就很容易理解了。构造并返回一个新的observer,用于在指定的DOM(就是上边的textNode)发生变化时,调用回调函数nextTickHandler

接下来观察者observer,根据MutationObserverInit配置字段的设置,监听textNode元素。当textNode文本节点的文本内容发生一丢丢变化时,就会立即触发nextTickHandler回调函数。

`var observer = new MutationObserver(nextTickHandler)
observer.observe(textNode, {
  characterData: true
})
`

再接下来就是把代码顶部定义的timerFunc变量赋值为一个函数。

`timerFunc = function () {
  counter = (counter + 1) % 2
  textNode.data = counter
}
`

函数内部通过(counter + 1) % 2的表达式思想,让counter的值因为每次timeFunc函数的调用都会变成0/1。 并通过将counter变化后的值赋值给textNode节点,实现改变textNode文本节点的内容,达到触发observer监听、进而调取nextTickHandler回调函数的目的。

至此,if语句内部流程就走完了。我们趁热打铁,先不看else里的内容(脚指头掰也能想到里边应该是不兼容MutationObserver后的降级方案了。

根据if里边的思路,我们该看nextTickHandler里都是啥了,监听了DOM变化后,每次回调都干了撒?

C、nextTickHandler()

逐句阅读代码:

`// 1
pending = false
`

每次nextTickHandler调用,pending先置为false,之前猜测pending是一个锁的想法,进一步得到了验证。

`// 2
var copies = callbacks.slice(0)
`

利用数组的slice()方法,传入起始下标0,不传终点下标,得到一个浅拷贝callbacks的新数组,并复制给copies

`// 3
callbacks = []
`

重新赋值callback为一个空数组

`// 4
for (var i = 0; i < copies.length; i++) {
    copies[i]()
}
`

最后遍历copies数组,顺序调取copies队列里的函数。 郁闷了,这个copies里的(确切的说是callbacks里的)每一项函数都是个啥?哪来的?

这得看看callbacks这个变量在哪里赋值了、赋值的都是啥。于是我们 全局搜索callbacks,发现除了目前看到的三个,还有一个在return的匿名函数里。

callbacks全局搜索

callbacks全局搜索

D、return

本着哪里不会点哪里的原则,说明到了我们观察返回的这个匿名函数内部代码的时候了。

源码里,nextTick等于一个立即执行函数,函数执行完毕return一个匿名函数如下,也就是说,下边的代码就是我们调用nextTick的时候调用的函数。

`function (cb, ctx) {
  var func = ctx
      ?
      function () {
          cb.call(ctx)
      }
      :
      cb
  callbacks.push(func)
  if (pending) return
  pending = true
  timerFunc(nextTickHandler, 0)
}
`

nextTick用法

我们先回忆一下nextTick的用法:

`// modify data
vm.msg = 'Hello'
// DOM not updated yet
Vue.nextTick(function () {
  // DOM updated
})
`

可以看到,nextTick的第一个参数传入一个匿名函数。函数里边代码就是我们开发者执行nextTick后要运行的内容。

于是我们知道了,我们调用nextTick时传入的function () { // DOM updated }对应的就是return 后边匿名函数的cb参数。

执行上下文

在匿名函数里边,先判断nextTick调用时第二个参数是否填,如果没填就直接将cb函数赋值给func变量。

`var func = ctx
  ?
  function () {
      cb.call(ctx)
  }
  :
  cb
`

如果填了第二个参数,func就等于一个匿名函数,函数内部利用call调用cb回调,改变cb内部this指向。由call调用时的传参为ctx可以推导出,nextTick的第二个参数ctx是一个上下文参数,用于改变第一个参数内部的this指向。

callbacks队列

紧接着将func函数推送到callbacks队列中:callbacks.push(func)。说明callbacks(也就是nextTickHandler函数里的copies)里存的就是nextTick的第一个回调函数参数。for循环执行的也就是他们。

pending加锁

if (pending) return

利用闭包,判断如果上一个nextTick未执行完毕,则本次的nextTick不能完整执行、会运行到了if这里被中断。

如果pending为false,说明上次的nextTick回调函数已经完了,可以进行本次执行。并紧接着pending = true将本次的nextTick调用状态改为pending中。

这pending就好像收费站的栅栏,上一辆车过去后立马落下杆子,上一辆车未缴费完毕、开走之前,不收起杆子。每次起杆子前,都看下是否有上一辆车正在堵着通道在缴费,如果没有,则可以开启杆子,让一辆车过去,放过一辆车后立马又落下杆子阻止后边的车。

timerFunc

最后调用timerFunc(nextTickHandler, 0)

先来看看timerFunc是啥:

立即执行函数里声明后未被初始化

`var timerFunc
`

紧接着判断MutationObserver可用的话,在if代码块里被赋值为函数:

`timerFunc = function () {
  counter = (counter + 1) % 2
  textNode.data = counter
}
`

函数里修改counter的值并赋值给textNode.data:

这个我们上边分析过,当指定的DOM“textNode”文本节点的文本内容发生变化时,MutationObserver对象的ovserve监听方法就会立即调用回调函数nextTickHandler

于是我们知道了整个流程:timerFunc调用,也就等于nextTickHandler调用,nextTickHandler调用后,内部遍历调用copies的每一项,即遍历调用多个nextTick的第一个函数参数(这是因为pending把下一个nextTick拦住了,不过每次调用nextTick时的第一个回调参数都被push到callbacks里了,当有几个被阻塞的nextTick回调还没被执行的情况下,callbacks数组里就可能不止一个回调函数,因此就需要用for循环依次调用)。

至此,我们的整个流程终于疏通完了。

等等,人家调用timerFunc时有传参啊。MutationObserver里给timerFunc赋值时,匿名函数没接收参数啊。

优雅降级

这时我们全局搜索timerFunc,发现我们漏了一个else代码块还没看:

`else {
  const context = inBrowser ?
      window :
      typeof global !== 'undefined' ? global : {}
  timerFunc = context.setImmediate || setTimeout
}
`

这里,用“inBrowser”判断是否为浏览器环境,然后给context赋值为window/global/{},

给timerFunc赋值为context.setImmediate(ie或者node环境)或者window.setTimeout(其他环境),主要看当前运行的环境。

这里是vue的降级处理方式,如果浏览器不支持MutationObserver的话,就用setImmediate,如果不支持setImmediate的话,就用setTimeout来模拟异步方式。

当流程走到else代码块里的话,timerFunc调用就需要传递一个匿名函数(这里为nextTickHandler)和一个interval的值(这里为0)了

本文使用mdnice排版

查看原文

赞 1 收藏 1 评论 0

健儿 发布了文章 · 3月24日

记录一次滴滴的前端一轮面试

整个面试过程持续了1个小时,里面有很多问题都没回答起来,但面试官却一直在引导我,换一种方式的问题再问我,可能平时写业务代码太不注重原理和架构方面的东西了吧。我大概还记得问了什么,记录下,空了得好好去看看。

小程序篇:
1、小程序如何做到数据和view层事实变更的。
这个基本回答出来了
setdata的js逻辑层和view层通过jsbrage

2、小程序是如何拿到原生api比如vieo 和 相册功能的
算是回答出来了吧
通过小程序暴露出来的js-sdk,如何拿到没回答上,
继续问:
拿到的是手机原生自带的还是小程序自己封装的呢?

3、小程序加载主包的页面,和加载分包的页面分别经历了什么?

4、template最多能嵌套几层。

5、路由最多能跳转几层

6、如何实现mixin

vue篇:

1、父组件嵌套子组件,加载他们的触发哪些钩子以及顺序

2、provide 和 inject的 数据具有双向绑定的作用吗

3、如果有一个长数组,不想全部监听所有的数据,如果做到
提示:有听过object.freeze吗

4、a嵌套b,b嵌套c,c嵌套d,如果要监听d里面的某一个数据变化,需要怎么设计(此题应该算js篇,大概是这么问的,有点忘记了,也没太听懂)
一开始是问,比如一个场景是可视化动态加载组件,
如何能知道一个嵌套比较深的组件被更改了。

5、vue.nexttick的实现原理
这个被问过两次了,之前去搜索了下,发现都是什么eventloop啊,异步更新这样的,或者更多是是使用场景,实际原理,我真没去看过,
后来又问,有没有一个降级的方案
害,,,
还是不知道
痛定思痛,今天开车等红绿灯的时候,都忍不住去搜索下这个api的源码,确实看见了降级的说法。
大概看了下有这么一个api,该属性提供一个构造函数MutationObserver()
通过new MutationObserver()可以得到一个新的观察器,它会在触发指定 DOM 事件时,调用指定的回调函数。
哎,有空再去看吧,今天超忙,还要对接口呢。。。

6.computed和watch的使用场景
幸亏没问源码,如果问源码,估计又gg了

js篇:

1、promise的实现原理

2、class的实现原理

3、继承中常见的组合继承,为何要手动修改constructor,其作用是什么

最后的结局问题:
webpack或者gulp等打包工具,或者web-app的性能优化问题
9点准时进入umeet视频面试,大约前面20分钟的时候,就有问题就回答不上了,但面试官没有立刻结束,还是不停的会其他问题,真的很感动,最后问了几个简单的基础问题,幸亏都回答上了。
接下来大概聊了下,说我已经6年开发经验了,所以他是按照p7的标准来面试我的,不仅要会用,更要用架构和原理上面的东西。我说虽然有6年,但由于是转行,前面1年基本是布局,第2年开始慢慢接触跟后端对接口,用jq写小插件,所以进步会比别人慢很多,哎,当然这些都是借口啦。。。
不管是自己是否是业务场景不够,还是半路出家,人家不会管的哦,只会看你确实已经有6年开发经验了。
结果晚上做梦,梦见也在面试。。。
一晚上没睡好。。。
家里两只猫一只猫还在打呼噜把我吵醒,心累的生活。

不管怎么样,第一次面试滴滴,虽然1面感觉比阿里的1面难太多了,但遇见一个这么好的面试官,一切都是值得的。

查看原文

赞 0 收藏 0 评论 1

健儿 发布了文章 · 3月16日

前端面试知识点

昨天我老乡突然找我,一开始把我吓一跳,以为出什么事了。
你们看:
image
原来要要跳槽,想让我给他稍微梳理下知识点。其实我既没有大厂经验,也没在独角兽公司工作,让我帮忙,我一开始还挺觉得,我”何德何能“,但不管怎么说,把自己知道的,一五一十的说给别人听,多少对于别人是有一些用的吧。
于是,晚上大家都洗漱后,就开启了一个”前端互助群“的群聊语音,我噼里啪啦说了20多分钟,但老乡男票说有一些听了立刻就忘记了,希望我能整理一份知识点笔记出来,所以我想整理下,不仅可以方便他,也可以方便更多的前端求职者。
ps:
这些知识点是我面试的时候常考点,可能罗列并不会很全。
一、html、css
1、定位,以及如何实现居中布局(常用方法)

二、js基础
1、typeof有几种值
1.1==和隐式转化逻辑

2、如何判断a是数组

3、堆和栈
延伸很多发散知识点
3.1函数执行的执行环境,执行栈
3.2深拷贝,如何写一个深拷贝函数(常考)
3.3内存溢出
3.4event Loop

4、函数
4.1箭头函数和function函数区别,this指向,bind,call
4.2new 一个对象发生了什么(4步)
4.3参数传参到底是引用传值还是?
4.4闭包,作用域,以及作用域链(常考,这里可以跟函数的执行环境结合考)
4.5高阶函数
4.6继承(构造函数,原型链,组合继承)

5、算法
4.1常用排序哪些(比如冒泡,时间复杂度多少)
4.2递归(要掌握)
4.3二叉树(能懂更好)

三、框架这里直说vue

1、vue2.0双向绑定原理(3+1对象,常考),以及3.0
2、vue的diff算法(ast)
3、vue常用钩子函数,父子,兄弟state交互

四、web安全
1、常用的几种攻击方式,如何防范

五、http/https
1、输入url到底发生了什么(常考)
2、https为何比http更安全
3、http缓存(强制和协商)

大致就是这么写,后续会想到以后逐步添加进来的,也欢迎各位留言,把常考的知识点和题型评价出来,谢谢。

查看原文

赞 0 收藏 0 评论 0

健儿 发布了文章 · 2月24日

从图解来理解vue设计的双向绑定架构图

先看别人绘制的图
image
我的手绘图
image
其实一共是3+1+view渲染模块
3:
observer:监听器 vue2.0 用Object.defineProty、vue3.0 用proxy
watcher:订阅器
compile:解析器
Dep:订阅器收集器
view:渲染dom

通过看图,先找有几条通路
a、observer-->Dep-->watcher-->view
b、compile-->watcher-->Dep
c、compile-->view

c路径是最简单的,c主要实现是解析指令如v-model等,初始化到view视图层。

b路径compile-->watcher 形成初始化的订阅器,watcher-->Dep订阅器被添加到订阅器容易里,这里实际是在observer 阶段eg:vue2.0的Object.defineProty的get方法的时候被添加的,但为何没有observer这个回路呢?设计者用了一个兼容的写法,强制执行了data监听器的observer方法里Object.defineProty的get方法。

a路径observer-->Dep-->watcher 是通知变化,此阶段eg: vue2.0的Object.defineProty的set方法触发的,最后watcher-->view 变化反应到view层

不难发现,到能到底view层的就只有complie和watcher,一个是模板初始化到view层,一个是update后更新到view层。不管是哪条路径到达view层实际都是对dom进行原生事件的操作(watcher是在第三个参数回调函数里更改view)
如:

node.textContent = typeof value == 'undefined' ? '' : value;

具体看看watcher大致实现:

function Watcher(vm, exp, cb) { 
      this.cb = cb; 
      this.vm = vm;  // vm data对象
      this.exp = exp;  // exp data对象的键key
      this.value = this.get();  // 将自己添加到订阅器的操作
}
 
Watcher.prototype = {
    update: function() {
       this.run();
    },
    run: function() { 
       var value = this.vm.data[this.exp]; 
       var oldVal = this.value; 
       if (value !== oldVal) { 
         this.value = value; 
         this.cb.call(this.vm, value, oldVal);
       }
    },
    get: function() {
        Dep.target = this;  // 缓存自己
        var value = this.vm.data[this.exp]  // 强制执行监听器里的get函数
        Dep.target = null;  // 释放自己
        return value;
    }
};

理解了这个回路,然后再逐一去理解回路实现的逻辑就很容易理解和记忆了。

查看原文

赞 0 收藏 0 评论 0

健儿 发布了文章 · 2月24日

彻底搞懂--水平垂直居中

水平垂直居中是面试常考的环节
今天就一步一步分析下是怎么做到的,以前呢多少有点死记硬背的感觉,没真正去梳理过。

先看看整个实现的流程图片吧。
image

1、当left:50%如图1
2、当top:50% 如图2
3、要想呈现如图3的情况,有两种实现方案
(1)方案1
在已知目标对象宽高的情况下,可以通过设置margin-left:-宽/2
margin-top:-高/2就可以。
(2)方案2
在未知目标对象宽高情况下,用transform:translate(-50%. -50%)
即可。
为何translate(-50%. -50%)这样设置就能实现呢?
因为translate(x,y,z)移动是相对自己的中心位置来偏移的,
2图可看出,目标对象中心位置正好距离视图中心位置x,y相差-50%*目标对象的宽或高,所以这种方法可以实现。
这里就稍微对过程的实现步骤和translate做了进一步的解释。

查看原文

赞 0 收藏 0 评论 0

健儿 赞了文章 · 2月23日

js节流和防抖的区别

在监听某类事件时,如监听元素滚动或表单input事件时,处理函数在短时间内被频繁调用。
如果处理函数还需要调用后台接口,那么可能上次还没有响应,下一次请求又来了。这样无意中增加了服务器的压力,而且对用户来说,也会造成卡顿,这不是用户和程序员想要的。
节流和防抖大家应该都用过,但一点我不明白,节流和防抖都是延迟执行,那么它们的区别在哪里?

先看看节流的实现
// 节流
var canNext = true;
function throttle () {
    if(!canNext) {
        return;
    }

    canNext = false;
    setTimeout(function () {
        console.log('节流方法执行了')
        canNext = true;
    }, 200)
};

canNext变量作为状态记录者,当它的值为 false 时,表示上一次调用正在执行,直接跳出本次调用,直到上一次的执行完毕,把true 赋给canNext,这时如果有调用,会执行这次调用。

下面我们再看看防抖的实现
// 防抖
var timer = null;
function debounce () {
    clearTimeout(timer);
    timer = setTimeout(function() {
        console.log('防抖方法执行了')
    }, 200)
}

第一次 timer变量保存着这个定时器的标识符(用于关闭当前定时器),如果在200毫秒内调用多次,只会执行最后一次

在以匀速滚动时,两个方法执行结果如图

防抖

image

节流

image

大家应该看出区别了,虽然都是延时执行,但两个方法在执行次数上还是有区别。
节流为什么会出现多次执行?我们再看看代码

// 节流
var canNext = true;
    function throttle () {
        if(!canNext) {
            return;
        }

        canNext = false;
        setTimeout(function () {
            console.log('节流方法执行了')
            canNext = true;
        }, 200)
    };

在密集调用时,节流方法相当于每200毫秒执行一次,再看看防抖。

// 防抖
var timer = null;
function debounce () {
    clearTimeout(timer);
    timer = setTimeout(function() {
        console.log('防抖方法执行了')
    }, 200)
}

防抖方法在200以内调用,总是执行最后一次的调用,~~~~这下我总算明白了。

那么它们各自的使用场景有哪些呢?
防抖

  • 短信验证码
  • 提交表单
  • resize 事件
  • input 事件(当然也可以用节流,实现实时关键字查找)

节流

  • scroll 事件,单位时间后计算一次滚动位置
  • input 事件(上面提到过)
  • 播放事件,计算进度条

暂时只能想到这些,未尽之处,望大家指正。

查看原文

赞 3 收藏 1 评论 0

认证与成就

  • 获得 22 次点赞
  • 获得 5 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2016-08-23
个人主页被 1.8k 人浏览