Every preparation

241 阅读24分钟

一、YY

准备

1 如何制定前端项目的开发规范?

答:
这件事情本来应该由项目的 leader 主导的,但是因为我刚调入的项目组的 leader 是偏向后端的,对前端开发没有太多的经验,所以才交给我来做。所以我就参考目前主流的开发规范(vue 官方 以及 airbnb 爱彼迎)以及上一个 team 的规范,制定了一套当前项目组的开发规范。比起一开始的规范,主要解决了两个问题:一是尾逗号的问题,这样提交代码到 git 上就不会因为加了一个逗号,导致变更两行了。第二个是限制每行的最大长度,这样看起来比较清晰。第三个是:方法强制要求写注释,方便后面的人理解这个方法的意思。

2 为什么选择 Jest ,在项目中有何应用?

答:
Jest 是 Facebook 出品的一个测试框架,大厂出品会有保障些。相比其他框架,Jest 内置了常用的测试工具,如自带断言、测试覆盖率工具,可以开箱即用。
主要是为了提高代码的质量和可读性。在这个项目中,主要是测逻辑层的一些复杂/通用的方法,view 层的 UI 是没有测试的。没有测 UI 一方面是刚开始引入 Jest,逻辑层都覆盖的还不是很全面。另外就是我本身还没有想好怎么做会比较方便。覆盖率目前是 20% 左右。
覆盖率是集成的 Istanbul,有四个测量维度(行、方法、分支<if 块>、语句),yarn run jest --coverage

3 对 Vuex 的理解,为什么不用 Vuex 去管理状态?

4 说说你上家公司的架构是怎么样的,可以画出图吗?

5 你是怎么去做首屏加载优化的?

答:
当时那个项目,首页加载慢的原因,一个是白屏时间长,一个是数据loading时间长。
通过工具分析后,发现白屏时间长是因为在页面初始化的时候,加载了过多不必要的js资源,例如没有用到的图片、字体等,且首页背景图比较空,所以视觉上看起来加载时间也长,针对这些采取的措施是,延迟不必要资源的加载时间,然后使用骨架图提高用户体验。
第二个数据loading时间过长的问题,是因为首页的模块比较多,每个模块都拆分出一个接口去请求,同时发送的请求数超过了浏览器最大的并发数,所以造成有些模块的数据展示的比较慢。针对这个问题,我选择合并了一些请求来解决这个问题。图片资源过多也是一个问题,首先压缩了图片,对一些简单的图片使用base64编码。

6 算法

伪代码写排序和查找,能分析他们的复杂度;了解下为什么需要平衡二叉树,更深则涉及广度搜索和深度搜索

6.1 冒泡排序

  1. 每次比较相邻两个元素,如果第一个比第二个大,则交换位置;
  2. 对比每一对相邻的元素,从开始一对到最后一对,这样在最后的就是最大的元素;
  3. 针对所有元素重复以上步骤,除了最后一个;
  4. 重复1~3,直到排序完成
function bubbleSort(array) {
    let len = array.length;
    for(let outer = len; outer >= 2; outer --) {
        for(let inner = 0; inner < outer - 1; inner ++) {
            if(array[inner] > array[inner+1]) {
                [array[inner], array[inner+1]] = [array[inner+1], array[inner]]
            }
        }
    }
}

6.2 选择排序

原理:从未排序的队列中找出最小(最大)的元素,放在排序队列的第一位,然后再从未排序队列找出最小(最大)的元素,放来已排序队列的最后一位,以此类推,直到排序完成。

function selectSort(array) {
    let len = array.length;
    for(let outer=0; outer < len - 1; outer ++) {
        for(let inner = outer; inner < len; inner ++) {
            if(array[outer]>array[inner]) {
                [array[outer], array[inner]] = [array[inner],array[outer]]
            }
        }
    }
}

6.3 插入排序

原理:通过构建有序队列,对于未排序的队列,从后往前扫描,找到对应的位置并插入。在扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入位置。

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已排序的元素序列从后往前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位
  4. 重复步骤3,直到找到已排序的元素小于或等于新元素的位置
  5. 将该新元素插入到该位置后
  6. 重复步骤2~5
function insertSort(array){
    let len = array.length;
    // 外循环从1开始,默认0是有序队列
    for(let outer=1; outer<len;outer++){
        for(let inner=outer;inner>0;inner--){
            if(array[inner]>array[inner-1]) {
                [array[inner], array[inner-1]] =[array[inner-1], array[inner]]
            } else {
                break;
            }
        }
    }
}

6.4 快速排序

原理:通过一趟排序,将待排序队列分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

function quickSort(array) {
    if(arr.length <= 1) {
        return arr;  //递归出口
    }
    var left = [],
        right = [],
        current = arr.splice(0,1); 
    for(let i = 0; i < arr.length; i++) {
        if(arr[i] < current) {
            left.push(arr[i])  //放在左边
        } else {
            right.push(arr[i]) //放在右边
        }
    }
    return quickSort(left).concat(current,quickSort(right));
}

7 设计模式

7.1 观察者模式

当对象之间存在一对多的依赖关系时,可使用观察者模式。例如,当一个对象有变更时,会自动通知依赖他的其他所有对象。观察者模式,属于行为模式。

// 可观察者
const Observable = (function(){
    let message = {}
    
    return {
        on: function(type, fn) {
            if(typeof message[type] === 'undefined') {
                message[type] = [fn];
            } else {
                message[type].push(fn);
            }
        },
        off: function(type, fn) {
            if(message[type] instanceof Array) {
                message = message.filter(i => i !== fn);
            }
        },
        notify: function(type, args) {
            if(!message[type]) {
                return;
            }
            
            let events = {
                type: type,
                args: args || {},
            };
            for(let i =0; i< message[type].length: i++) {
                message[type][i].call(this, events)
            }
        },
    }
})()

//订阅消息
Observe.on('say', function (data) {
	console.log(data.args.text);
})
Observe.on('success',function () {
    console.log('success')
});

//发布消息
Observe.notify('say', { text : 'hello world' } )
Observe.notify('success');  

7.2 状态模式

在状态模式(State Pattern)中,类的行为是基于它的状态改变的。
允许一个对象在其内部状态改变的时候改变它的行为,对象看起来似乎修改了它的类。

class ActiveBtnState {
    handleClick() {
        console.log('active btn')
    }
}

class DisableBtnState {
    handleClick() {
        console.log('disabel btn')
    }
}

class NormalBtnState {
    handleClick() {
        console.log('normal')
    }
}

Class Button {
    constructor() {
        this.active = new ActiveBtnState();
        this.disable = new DisableBtnState();
        this.normal = new NormalBtnState();
        this.currentState = 'normal';
    }
    
    changeState(state) {
        this.currentState = state;
    }
    
    handleClick() {
        switch(this.currentState) {
            case: 'active':
                this.active.handleClick();
                break;
            case: 'normal':
                this.normal.handleClick();
                break;
            case: 'disable':
                this.disable.handleClick();
                break;
        }
    }
}

7.3 责任链模式

为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。

function order500(type) {
    if(type === 1) {
        console.log('500元');
    } else {
        return 'next';
    }
}

function order200(type){
    if(type === 2) {
        console.log('200元');
    } else {
        return 'next';
    }
}

function orderNormal(type) {
    if(type > 0) {
        console.log('10元');
    } else {
        console.log('下次继续鸭')
    }
}

var chain = function(fn) {
    this.fn = fn;
    this.next = null;
}
chain.prototype.setNext = function (nextHandle) {
    return this.next = nextHandle;
}
chain.prototype.passRequest = function () {
    var ret = this.fn.apply(this,arguments);
    if(ret === 'next') {
        return this.next && this.next.passRequest.apply(this.next,arguments);
    }
    return ret;
}

//现在我们把3个函数分别包装成职责链节点:
var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);

// 然后指定节点在职责链中的顺序
chainOrder500.setNext(chainOrder200);
chainOrder200.setNext(chainOrderNormal);

//最后把请求传递给第一个节点:
chainOrder500.passRequest(1);  
chainOrder500.passRequest(2);  
chainOrder500.passRequest(0);  

7.4 工厂模式

定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行

8 在小程序中遇到过什么问题?

9 在上一家公司有什么收获?

答:

10 对前端工程化的理解

答:
工程化就是系统化、模块化、规范化开发的一个过程,要解决的是如何提高编码、测试、维护阶段的生产效率,是一种管理学或者说方法论。
前端工程化要解决什么:

  • 制定各项规范(编码规范、开发流程规范、接口等文档规范)
  • 使用版本控制,高效安全的管理源代码
  • 使用合适的前端技术和框架,提高生产效率,降低维护难度
    • 模块化的方式组织代码
      • CommonJs: module.exports.sayHello = function() {}
      • AMD:define
      • UMD:允许同时是使用 AMD 和 CommonJs
      • ES Modules: export const a = {}; import {a} from xxx
    • 组件化的编程思想
    • 将数据层分离管理
    • 使用面向对象或函数编程的方式组织架构
  • 提高代码的可测性,引入单元测试,提高代码质量
  • 通过使用各种自动化的工程工具,提升整个开发、部署效率

11 三次握手和四次挥手

  1. 三次握手 建立连接前,客户端和服务端需要通过握手来确认对方:
  • 客户端发送 syn(同步序列编号) 请求,进入 syn_send 状态,等待确认
  • 服务端接收并确认 syn 包后发送 syn+ack 包,进入 syn_recv 状态
  • 客户端接收 syn+ack 包后,发送 ack 包,双方进入 established 状态
  1. 四次挥手
  • 客户端 -- FIN --> 服务端, FIN—WAIT
  • 服务端 -- ACK --> 客户端, CLOSE-WAIT
  • 服务端 -- ACK, FIN --> 客户端, LAST-ACK
  • 客户端 -- ACK --> 服务端,CLOSED

可参考:juejin.cn/post/684490…

12 react 和 vue 之间的区别

还是说说相似之处:

  • 使用 virtual dom
  • 提供了响应式和组件化的视图组件
  • 将注意力保持在核心库,其他功能如路由交给相关的库

不同之处:

  • 运行时性能:React 在更新一个组件时,整个子组件树也会重新渲染,除非使用 shouldComponentUpdate 进行优化。而 vue 是在渲染的时候自动追踪的,所以系统能精准的知道哪个组件确实需要被渲染。
  • HTML & CSS:React 使用 jsx,能使用完整的 JavaScript 语言来进行编程(使用临时变量),且开发工具对 jsx 的支持度高于 vue 模版。而 vue 的模版是开发者更容易接受。
  • 响应式原理不同:vue 是依赖收集,递归监听 data 的所有属性直接修改。当数据改变时,自动找到引用组件重新渲染。 而 react 基于状态机,手动优化,数据不可变,需要 setState 驱动新的 State 替换老的 State。当数据改变时,以组件为根目录,默认全部重新渲染。
  • diff 算法不同:vue diff 使用双向链表,边对比边更新 dom,而 react 主要使用 diff 队列保存需要更新哪些 DOM,得到 patch 树,再统一操作批量更新DOM。

13 px、em、rem、vh、vm 之间的区别

  • px:代表物理屏幕上能显示出的最小的一个点
  • em:是相对于父级的字体大小
  • rem:是相对于 HTML 根元素的字体大小
  • vh、vw:相对于视口的高度和宽度, 1vh 等于 1/100 的视口高度,1vw 等于 1/100 的视口宽度

14 JavaScript 跨域访问

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。
这就要说到同源策略了。同源策略是一种约定,同源是指“协议+域名+端口”三者相同。
同源会限制以下内容:

  • Cookie、localStroage、IndexDB 等存储性内容的访问
  • DOM 节点
  • Ajax 请求

但是有三个标签是允许跨域加载资源的:

  • <img src="xxx" />
  • <link href='xxx' />
  • <script src='xx' />

跨域的方式有以下几种:

  1. jsonp:利用 <script> 标签没有跨域限制的漏洞,网页可以从其他来源动态产生 json 数据,但需要对方服务器支持。缺点是仅支持get请求。
  2. cors:需要前后端支持,服务端设置 access-control-allow-origin 属性即可。
  3. nginx 反向代理:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
    此处不再赘述,详细可以参考:juejin.cn/post/684490…

15 Http 状态码以及请求方法

  1. 1开头:表示临时响应并需要请求者继续执行操作的状态代码。
    • 100:请求者需继续发出请求。服务器已接收一部分请求,等待剩余。
    • 101:(切换协议) 请求者已要求服务器切换协议,服务器已确认并准备切换。
  2. 2开头:(成功)表示成功处理了请求的状态代码。
    • 200:成功
    • 201:已创建
    • 202:已接受,但未处理
    • 203:非授权信息,返回的信息可能来自另一来源
    • 204:无返回内容
    • 205:重置内容
    • 206:成功处理部分 get 请求
  3. 3开头:(重定向) 表示要完成请求,需要进一步操作。
    • 300 (多种选择) 针对请求,服务器可执行多种操作。
    • 301 (永久移动) 请求的网页已永久移动到新位置。
    • 302 (临时移动)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
    • 303 (查看其他位置) 请求者应当对不同的位置使用单独的 GET,请求来检索响应时,服务器返回此代码。
    • 304 (未修改) 自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
    • 305 (使用代理)请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理。
    • 307 (临时重定向)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
  4. 4开头:(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。
    • 400 (错误请求) 服务器不理解请求的语法。
    • 401 (未授权) 请求要求身份验证。对于需要登录的网页,服务器可能返回此响应。
    • 403 (禁止) 服务器拒绝请求。
    • 404 (未找到) 服务器找不到请求的网页。
  5. 5开头:(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。
    • 500 (服务器内部错误) 服务器遇到错误,无法完成请求。
    • 501 (尚未实施) 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
    • 502 (错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。
    • 503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
    • 504 (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
    • 505 (HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。

16 盒模型

盒模型分为 IE盒模型W3C标准盒模型
区别在于:IE盒模型的属性 width,height 包含 border 和 padding,由 content + border + padding 组成,而 W3C标准盒模型只包含 content。
可以通过 box-sizing 属性控制,默认是 content-box 即标准盒模型,border-box 为 IE盒模型。

17 块级元素和内联元素的区别

答:
块级元素能够设置width和height,而且padding和bottom各个方向上都能够设定。而内联元素的width和height都无法定义,padding各个方向上能够设定(注意,非常多同学都觉得padding-top/bottom是不能够设定,这是一个大大的误解。不信你自己试一试。答案立即揭晓),然后margin-left/right能够设定,可是margin-top/bottom是不能够设定的了。

18 前端 CDN

答:
cdn是指内容分发网络。另外分两种,一种叫做静态cdn,一种是动态cdn。静态cdn是把你服务器的资源,拉到离请求端更近的地方存储,这样请求的时候就更快。动态cdn,因为资源是动态变化的,就不能拉到边缘节点存起来,这样没意义。他是优化请求端到服务端的传输链路,具体优化方式要比较熟悉网络的相关知识,减少传输时间

19 DOM 的 onload 和 DomContentLoaded 事件有什么区别

答:

  • 当 onload 事件触发时,页面上所有的 DOM、样式表、脚本、图片、flash 都已经加载完成
  • 当 DOMContentLoaded 事件触发时,仅当 DOM 加载完成,不包括图片、样式表、flash 这些

20 requestAnimationFrame 方法的作用?应用场景?

答:
作用:浏览器可以优化并行的动画动作,更合理的重新排列动作序列。并把能合并的动作放在一个渲染周期内完成,从而呈现更流畅的动画效果,节省 CPU。
应用场景:游戏、动画

21 网站的登陆是如何保持的,一个完整的登陆流程如何实现

答:
通过 cookies 来保持的,cookies 里面存储 token,每次请求到后端服务器的时候,都会带上 token,验证用户是否已经登陆,或者登陆状态又没有过期。
流程:输入用户名、密码 -》 点击登陆,发送登陆请求 -》 服务器端验证密码,生成token -》写入到 cookies ,返回成功

22 编写一个函数,用于从cookie里面获取数据

var getParam = function(name) {
    var reg = new RegExp("(\?|#|&") + name + "=([^&#\?]*)(&|#|$|\?)");
    var m = window.location.href.match(reg);
    
    return decodeURI(!m ? '' :  m[2]);
}

23 编写一个 Emitter 事件分发类,有 on、off、trigger、once 等方法

详见:www.cnblogs.com/penghuwan/p…

24 在 CSS3 中设置什么样式,可以改变默认盒子模型计算方式?

box-sizing: border-box;

25 支持正则的方法

参考链接:www.runoob.com/jsref/jsref…

26 浏览器事件循环(Event loop)

事件循环是一个执行模型,指: 执行一个宏任务,然后执行清空微任务列表,循环再执行宏任务,再清微任务列表,在不同的地方有不同的实现。

微任务 microtask(jobs):

  • process.nextTick (Node独有)
  • Promise
  • Object.observe
  • MutationObserver

宏任务 macrotask(task):

27 从浏览器输入 url 到展示的过程

  • DNS 解析
  • TCP 三次握手
  • 发送请求,分析 URL,设置请求头
  • 服务器返回请求的文件(HTML)
  • 浏览器渲染
    • HTML parser -》 DOM tree
      • 表计算法
      • DOM 树构
    • Css parser -》 Style tree
      • 解析 css 代码,生成样式树
    • attachment --> Render Tree
      • 结合 dom tree 和 style tree 生成渲染树
    • layout:布局
    • GPU painting:像素绘制页面

28 Typescript

www.srcmini.com/3507.html
泛型?

29 单页面应用首屏显示比较慢,原因是什么?有什么解决方案?

首页白屏的原因:
单页面应用的 html 是靠 js 生成的,所有需要的资源都要下载到浏览器进行解析。因为首屏需要加载很大的 js 文件(app.js vendor.js),所以网速差的时候会产生一定程度的白屏。 解决方案:

  • 优化 webpack 减少打包体积, code-split 按需加载
  • 服务端优化,在服务端事先拼接好首页所需的 html
  • 处理加载的时间片,合理安排加载顺序,尽量不要有大面积空隙
  • 合理使用缓存
  • 首页加 loading 或 骨架图
  • 使用首屏SSR + 跳转SPA方式来优化
    1. 使用 ssr 的优势:
      • 更好的 seo,搜素引擎爬虫抓取工具可以直接查看完全渲染的页面
      • 更快的内容到达时间,无需等待所有的 JavaScript 都完成下载并执行,用户会更快的看到完整渲染的页面。
    2. 需要权衡的地方:
      • 开发条件所限:浏览器特定的代码只能在某些生命周期钩子函数中使用。
      • 涉及构建设置和部署的更多要求。需要在 node.js server 运行环境
      • 更多的服务器端负载。

30 在页面上渲染一千行以上的数据后,滚动页面时,导致页面卡顿严重,原因是什么?有什么办法可以避免卡顿?

30.1

原因:

  1. 渲染的元素多,会导致大量的 dom,从而导致 dom 在初始化或者 reflow、repaint 的时候发生大量计算,导致卡帧、掉帧,主观上会显得页面卡顿
  2. 渲染过多的元素,同时触发滚动事件,会占用大量内存

解决方案:
使用虚拟列表,只渲染可视区域的列表元素,当可视区滚动时,根据滚动的 offset 大小以及所有列表元素的位置,计算在可视区应该渲染哪些元素。

30.2 顺便说下懒加载:

懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种很好的优化网页性能的方式。

为什么要用呢?

  • 提升用户体验
  • 减少无效资源的加载
  • 防止并发加载的资源过多会阻塞 js 的加载

原理:

  1. 将页面上 img 的 src 属性设置为空字符串,真实的路径则设置在 data-original 属性中
  2. 监听页面滚顶的 scroll 事件,在 scroll 事件的回调中,判断懒加载的图片是否进入

问题

1 使用 css 描述一个 div 宽度比父元素宽度小 50px

.child {
    width: calc(100% - 50px);
}

2 以下 JavaScript 代码输出的结果是什么?请做出解释

const m = [1, 2, 3]
const fn = (n, ...m) => n + m
console.log(fn(1))

3 使用 typescript 声明一个函数当入参为 number 类型时返回 string 类型,入参为 string 类型时返回 number 类型。

见问题8

4 如何提高 webpack 构建速度?

5 使用 React Hook(或 Vue) 描述一个 fetch 异步请求组件

6 ES6 两种导出方式的区别

  1. 命名导出(每个模块包含任意数量)
  2. 默认导出(每个模块包含一个) 命名导出:
// 导出早前定义的函数
export { myFunction,myVariable }; 

// 导出单个特性(可以导出var,let,const,function,class)
export let myVariable = Math.sqrt(2);
export function myFunction() { ... };

默认导出:

// 默认导出函数
export default function(){}

// 默认导出类
export default class{}

7 下面哪些会触发 Http 请求

// A
<textarea>
    <img scr='a.png' style="display:none;" alt="my home">
</textarea>

// B
<textarea style="display:none;">
    <img scr='a.ong' style="visibility:hidden" alt="my home">
</textarea>

// C
<div style="display:none;background-image:url('a.png);"></div>

// D
<textarea>
<span style="display:none;background-image:url('a.png);" alt="my home"></span>
</textarea>

正确答案是 C。 解析:在 textarea 中会被解析为纯文本,而不是 dom 元素。

  • 解析HTML【遇到标签加载图片】 —> 构建DOM树
  • 加载样式 —> 解析样式【遇到背景图片链接不加载】 —> 构建样式规则树
  • 加载javascript —> 执行javascript代码
  • 把DOM树和样式规则树匹配构建渲染树【遍历DOM树时加载对应样式规则上的背景图片
  • 计算元素位置进行布局
  • 绘制【开始渲染图片】

原理参考:segmentfault.com/a/119000001…

8 说说 typescript 的函数重载

这个概念是强类型语言中才有的,在 JavaScript 中依据不同的类型/参数个数执行一些不同的函数体,很常见。
关于函数重载,最精确的定义放在最前面,最后函数实现时,需要使用 | 操作符或者 ? 操作符,把所有可能的输入类型全部包含进去。

// 这里是声明
function change(val: number):string
function change(val: string): number

// 这里是实现
function change(val: number | string) {
    if(typeof val === 'string') {
        return Number(val)
    } else if(typeof val === 'number') {
        return String(val)
    }
}

9 Vuex 的作用是什么?

vuex 是一个转为 Vue.js 开发的状态管理模式。采用集中式存储管理应用的所有组件的状态,并以一定的规则保证状态以一种可预测的方式发生变化。

10 首屏加载做了哪些优化?会想起上次做的优化,现在你觉得还可以做哪些改进?

11 block、inline 和 inline-block

  • block
    • 独占一行,默认自动填满父元素宽度
    • 可以设置 width、height 属性
    • 可以设置 margin、padding 属性
    • 常见块级元素:div, form, table, p, pre, h1~h6, dl, ol, ul 等
  • inline
    • 不会独占一行,相邻元素会排列在同一行里,直到排列不下才会换行,宽度随元素内容变化而变化
    • 无法设置 width、height
    • 可以设置 padding、左右margin,但不可以上下margin
    • 常见行内元素:span、a、strong、em、label、input、select、textarea、img、br 等
  • inline-block
    • 应用此特性的元素呈现为内联对象,周围元素保持在同一行,但可以设置宽度和高度地块元素的属性 可在一下情况进行切换:
  1. 让一个 inline 元素从新的一行开始
  2. 让块元素和其他元素保持在同一行
  3. 可以设置元素的宽高
  4. 默认与文字同宽

兼容性:IE 不支持该属性,但使用display:inline-block在IE下会触发 layout,从而使内联元素拥有了该属性的表象。

inline-block 为什么会存在间隙:inline-block 元素间有空格或是换行 解决方案:

  • 设置 font-size:0
  • 设置 letter-spacing 为负数

12 npm 发包怎么控制发上去之后,不被别人下载?

  • npm init -y
  • npm version <update_type> // patch补丁版本、minor次版本、major主版本,更新包
  • npm publish // 发布包

步骤一:在 package.json 中设置 private: true,npm 将拒绝发布它,这是为了防止一个私有模块被无意间发出去。
步骤二:配置 publishConfig,这个配置是会在模块发布时用到的一些值的集合。如果你不想模块被默认被标记为最新的,或者默认发布到公共仓库,可以在这里配置tag或仓库地址。

13 如何配置项目 eslint 出错的情况下,代码无法 push 到远端?

使用 git hook。
任何时候当前版本库中出现如 commitpush 等特殊事件时,都会触发执行一个或多个任意的 shell 脚本,称之为 git 钩子。它存放在.git/hooks目录下。
从顺序上可分为:

  • 前置钩子,在动作完成前调用
  • 后置钩子,在动作完成后执行

14 typescript 中的 interface 和 type 有什么区别,使用场景?

  • An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.
  • An interface can have multiple merged declarations, but a type alias for an object type literal cannot.

interface是接口,type是类型,本身就是两个概念。只是碰巧表现上比较相似。 希望定义一个变量类型,就用type,如果希望是能够继承并约束的,就用interface。 如果你不知道该用哪个,说明你只是想定义一个类型而非接口,所以应该用type。

相同点:

  1. 都可以描述一个对象或函数
  2. interface 可以 extends、implement,type 可以通过交叉类型实现同样的行为

不同点:

  1. type 可以声明基本类型别名、联合类型、元组 等类型,interface 不可以
  2. type 语句中可以使用 typeof 获取实例的类型进行赋值,interface 不可以
  3. interface 能声明合并

15 vue 怎么实现数据监听,array 类型呢?

vue 的双向绑定,是通过数据劫持结合发布者-订阅者模式的方式来实现的。

对 array 类型,vue 内部实现了一组观察数组的变异方法,例如 push、pop、shift 等。更改我们需要监听的Array数组属性值(属性值为函数),在监听函数里执行数组的原生方法,并通知所有注册的观察者进行响应式处理。

const arrayProto = Array.prototype
const arrayMethods = Object.create(arrayProto)
;[
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
].forEach(item=>{
	Object.defineProperty(arrayMethods,item,{
	    value:function mutator(){
	    	//缓存原生方法,保证不污染数组的原生方法,之后调用
	    	const original = arrayProto[item]	
	    	let args = Array.from(arguments)
		    original.apply(this,args)
	    },
	})
})
// 调用
let obarr = []
obarr.__proto__ = arrayMethods

// 如果浏览器不支持 __protp__,直接使用 defineProperty
Object.defineProperty(obarr, 'push', {
    value: arrayMethods.push
})

16 描述一下状态机模式。是否有看过 git 上比较成熟的使用状态机实现的项目?

17 怎么拦截网页上的广告弹出?(思路)

1、对 URL 请求进行拦截。梳理出常见的广告url,进行过滤

18 动态切换主题的原理是什么?

应该是替换类的值。。

19 git相关知识

1 reabse

功能:可以把本地未push的分叉提交历史整理成直线
目的:使得查看历史提交的变化时更容易,因为分叉的提交需要三方对比