查看原文
其他

分享一个中型公司面试原题,卷起来~

前端之神 2023-02-12

The following article is from 前端早茶 Author 广东靓仔

本文适合最近在考虑新机会的的小伙伴阅读,简历指导、模拟面试可关注「前端之神」找我

一、前言

 这套题目是某位群友亲身经历过的,感谢小伙伴share~

这套题目,考得比较实用也还挺全面,下面我们来看看具体的内容。

二、原题

讲讲事件循环

JavaScript 是一门单线程语言,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程。
一、同步和异步

所谓单线程,无非就是同步队列和异步队列,js代码是自上向下执行的,在主线程中立即执行的就是同步任务,比如简单的逻辑操作及函数,而异步任务不会立马立马执行,会挪步放到到异步队列中,比如ajax、promise、事件、计时器等等。

也就是先执行同步,主线程结束后再按照异步的顺序再次执行。

二、事件循环(Event Loop)

事件循环意思就是:等待主线程中任务全部完成后,再回来把异步队列中任务放到主程序中运行,这样反复的循环

三、宏观任务和微观任务(先执行微观任务,再执行宏观任务):

在事件循环中,每进行一次循环操作称为tick,tick 的任务处理模型是比较复杂的,里边有两个词:分别是 Macro Task (宏任务)和 Micro Task(微任务)。

简单来说:
宏观任务主要包含:setTimeout、setInterval、script(整体代码)、I/O、UI 交互事件、setImmediate(Node.js 环境)
微观任务主要包括:Promise、MutaionObserver、process.nextTick(Node.js 环境)
规范:先执行微观任务,再执行宏观任务
(microtask 优先于 task 执行,所以如果有需要优先执行的逻辑,放入microtask 队列会比 task 更早的被执行。)

事件委托

事件冒泡:
JS中当出发某些具有冒泡性质的事件是,首先在触发元素寻找是否有相应的注册事件,如果没有再继续向上级父元素寻找是否有相应的注册事件作出相应,这就是事件冒泡。
事件委托:
利用事件冒泡的特性,将本应该注册在子元素上的处理事件注册在父元素上,这样点击子元素时发现其本身没有相应事件就到父元素上寻找作出相应。
这样做的优势有:
1.减少DOM操作,提高性能。
2.随时可以添加子元素,添加的子元素会自动有相应的处理事件。
适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。
Tips: mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

sass和less的区别

共同点:

sass和less都是css的预编译处理语言,他们引入了mixins,参数,嵌套规则,运算,颜色,名字空间,作用域,JavaScript赋值等 加快了css开发效率,

当然这两者都可以配合gulp和grunt等前端构建工具使用

主要区别:
1.编译环境不一样:

sass的安装需要Ruby环境的,是在服务端上处理的,而Less是需要引入less.js来处理Less代码输出css到浏览器,也可以在开发环节使用Less,然后编译成css文件,直接放到项目中。

2.变量符不一样:

Less是@,而Scss是$

3.输出设置

Sass提供4中输出选项:nested, compact, compressed 和 expanded.

4.Sass支持条件语句,可以使用if{}else{},for{}循环等等。而Less不支持。
5. 引用外部CSS文件

scss引用的外部文件命名必须以开头, 文件名如果以下划线开头的话,Sass会认为该文件是一个引用文件,不会将其编译为css文件.

6.Sass和Less的工具库不同

Sass有工具库Compass

Less有UI组件库Bootstrap

http的状态码、http缓存

状态码分为5类:

状态码分为5类:
1**信息,服务器收到请求,需要请求者继续执行操作
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误

301:

(永久移动)请求的网页已被永久移动到新位置。服务器返回此响应(作为对GET或HEAD请求的响应)时,会自动将请求者转到新位置。

302:

(临时移动)服务器目前正从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。此代码与响应GET和HEAD请求的301代码类似,会自动将请求者转到不同的位置。

304:

响应禁止包含消息体,如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。

305:被请求的资源必须通过指定的代理才能被访问。

400:

语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求,或者请求参数有误。

403:服务器已经理解请求,但是拒绝执行它。

404:请求失败,请求所希望得到的资源未被在服务器上发现。

500:

服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。


http缓存请求相应头:

1.Cache-Control
请求/响应头,缓存控制字段,可以说是控制http缓存的最高指令,要不要缓存也是它说了算。
它有以下常用值
  1.1 no-store:所有内容都不缓存
  1.2 no-cache:缓存,但是浏览器使用缓存前,都会请求服务器判断缓存资源是否是最新,它是个比较高贵的存在,因为它只用不过期的缓存。
  1.3  max-age=x(单位秒) 请求缓存后的X秒不再发起请求,属于http1.1属性,与下方Expires(http1.0属性)类似,但优先级要比Expires高。
  1.4 s-maxage=x(单位秒) 代理服务器请求源站缓存后的X秒不再发起请求,只对CDN缓存有效(这个在后面会细说)
  1.5 public 客户端和代理服务器(CDN)都可缓存
  1.6 private 只有客户端可以缓存
2.Expires 响应头,代表资源过期时间,由服务器返回提供,GMT格式日期,是http1.0的属性,在与max-age(http1.1)共存的情况下,优先级要低。
3.Last-Modified 响应头,资源最新修改时间,由服务器告诉浏览器。
4.if-Modified-Since 请求头,资源最新修改时间,由浏览器告诉服务器(其实就是上次服务器给的Last-Modified,请求又还给服务器对比),和Last-Modified是一对,它两会进行对比。
5.Etag 响应头,资源标识,由服务器告诉浏览器。
6.if-None-Match 请求头,缓存资源标识,由浏览器告诉服务器(其实就是上次服务器给的Etag),和Etag是一对,它两会进行对比。


http缓存方案

1.md5/hash缓存

不推荐缓存html文件,这样每次html加载渲染都可以感知文件变化,反正文件没变还是使用本地缓存,文件名都变了说明修改过,重新请求缓存就好了。

2.CDN缓存(作为了解)
1.CDN通过分流的形式,大大减轻源站的访问压力。
2.CDN也解决了跨地区访问问题,根本上为访问提供了加速。

强缓存与协商性缓存(弱缓存)

了解了上面不同浏览器行为对http缓存的不同影响,理解强缓存与协商性缓存就很容易了。

强缓存:

不发起http请求,直接使用本地缓存,比如浏览器地址栏回车,使用浏览器的刷新按钮,在Expires或max-age生效的情况下,触发的都是强缓存。

协商性缓存(弱缓存):

在使用本地缓存前,先与服务器协商,核对缓存文件是否为最新。比如设置了cache-control=no-cache,不管你做任何操作,都会发起请求,这一类就是协商性缓存了。

css盒模型

1. 基本概念:
    标准模型和IE模型
/* 标准模型 */
box-sizing:content-box;

 /*IE模型*/
box-sizing:border-box;
根据盒模型解释边距重叠:父元素没有设置margin-top,而子元素设置了margin-top:20px;可以看出,父元素也一起有了边距。

BFC(边距重叠解决方案)

BFC(block formatting context)块级格式化上下文

BFC原理:

(1). 内部的box会在垂直方向上,一个接一个地放

(2). 每个元素的margin box的左边,与包含块border box的左边相接触(对于从做往右的格式化,否则相反)

(3). box垂直方向的距离由margin决定,属于同一个bfc的两个相邻box的margin会发生重叠

(4). bfc的区域不会与浮动区域的box重叠

(5). bfc是一个页面上的独立的容器,外面的元素不会影响bfc里的元素,反过来,里面的也不会影响外面的

(6). 计算bfc高度的时候,浮动元素也会参与计算

创建方式:

1. 脱离文档流(float不为none时)

2.position为absolute或fixed

3. display 为inline-block、table-cell、table-caption、flex、inline-flex。

4. overflow不为visible

5. 根元素。

应用场景:

1. 自适应两栏布局

2. 清除内部浮动

3. 防止垂直margin重叠

小程序分包机制

离线包机制

微信小程序采用的是类似离线包加载方案,用户第一次打开时会先下载好所有代码,然后再加载页面;当用户再次进入时,会直接使用已下载的代码,省去了代码下载的过程,打开速度更快。

存在问题:

第一次打开小程序时白屏时间很长,因为要先下载好所有的代码,代码越多,白屏时间越长

代码有部分更新时,没办法进行增量更新,每次发版用户都需要重新下载全部代码
分包加载

离线包和M页的一种结合机制,即把代码划分成主包+N个分包

打开小程序,默认先加载主包
进入分包页面时,再加载对应分包
好处:进入主包页面时,需要下载的代码量小了很多,白屏时间更短,体验更佳

目录

├── app.js
├── app.json
├── app.wxss
├── packageA
│   └── pages
│       ├── cat
│       └── dog
├── packageB
│   └── pages
│       ├── apple
│       └── banana
├── pages
│   ├── index
│   └── logs
└── utils

 

app.json 

subpackages 字段声明项目分包结构:

{
  "pages":[
    "pages/index",
    "pages/logs"
  ],
"subpackages": [
  {
    "root""packageA",
    "pages": [
      "pages/cat",
      "pages/dog"
    ]
  }, {
    "root""packageB",
    "name""pack2",
    "pages": [
      "pages/apple",
      "pages/banana"
    ]
  }
  ]
}

subpackages 中,每个分包的配置有以下几项:

字段类型说明rootString分包根目录nameString分包别名,分包预下载时可以使用pagesStringArray分包页面路径,相对与分包根目录independentBoolean分包是否是独立分包


打包原则

声明 subpackages 后,将按 subpackages 配置路径进行打包,subpackages 配置路径外的目录将被打包到 app(主包) 中

app(主包)也可以有自己的 pages(即最外层的 pages 字段)

subpackage 的根目录不能是另外一个 subpackage 内的子目录

tabBar 页面必须在 app(主包)内

引用原则

packageA 无法 require packageB JS 文件,但可以 require app、自己 package 内的 JS 文件

packageA 无法 import packageB 的 template,但可以 require app、自己 package 内的 template

packageA 无法使用 packageB 的资源,但可以使用 app、自己 package 内的资源

js的基本数据类型和引用类型

值类型(基本类型):
字符串(String)、数字(Number)、布尔(Boolean)、对空(Null)、未定义(Undefined)、Symbol。

引用数据类型:
对象(Object)、数组(Array)、函数(Function)。

说说js缓存

缓存的好处:

(1).当页面渲染的数据过多时,为了减轻对内存的占用,对初次接收且会用到的数据进行本地缓存,是有着大好处的.
(2).受网速等各种因素的影响,当渲染数据过多时,若存在频繁的切换页面,用户体验效果不佳。

本地存储

storage来对数据进行存储(sessionStorage、localStorage)

sessionStorage

临时的会话存储,只要当前的会话窗口未关闭,存储的信息就不会丢失,即便刷新了页面或者在编辑器中更改了代码,存储的会话信息也不会丢失。

localStorage

不主动去清除,会一直将数据存储在客户端的储存方式,即使关闭了浏览器,下次打开的时候仍然可以看到之前存储的未主动清除的数据(即便是
杀毒软件或者浏览器自带的清除功能,也不能将localStorage存储的数据清除掉)
storage存储的数据只能是字符串类型,其他类型的数据需做类型转换
storage直接属于顶层对象window.

cookie

cookie属于较老且最常见用的最多的技术了,cookie的优点很多,用起来也比较方便
cookie缺陷:4kb,cookie本地存储的数据会被发送到服务器

cookie和storage的区别

1.cookie兼容所有的浏览器(本地cookie谷歌不支持),storage不支持IE6~8;
2.二者对存储的内容均有大小限制,前者同源情况写一般不能存储4kb的内容,后者同源一般能存储只能存储5MB的数据
3.cookie有过期时间,localStorage是永久存储(如果你不手动去删除的话)
4.一些浏览器处于安全的角度可能会禁用cookie,但无法禁用localStorage

jquery的ajax实现原理

1.jQuery底层在实现ajax功能时,调用了浏览器中的XMLHttpRequest对象,在最新的2.0版本后,由于不考虑IE浏览器的支持,直接使用该对象获取用户请求的数据,包括地址和参数等。
2.它将对象打开请求地址时的相关参数封装在相关函数如ajax()的配置项中,一旦传入了必需选项,则直接调用相应的send()方法进行数据的请求。
3.jQuery底层将对象请求后返回的数据结果,直接封装到相关函数的success方法中,所以,一旦异步请求成功,返回的数据对象直接注入到方法中,因此,可以调用success方法获取服务端返回的数据。
拓展:

1、ajax和jsonp这两种技术在调用方式上“看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装;

  2、ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是通过HTTP来动态添加<script>标签来调用服务器提供的js脚本。
  3、其实ajax与jsonp的区别不在于是否跨域,ajax通过服务端代理一样可以实现跨域,jsonp本身也不排斥同域的数据的获取。
  4、jsonp是一种方式或者说非强制性协议,如同ajax一样,它也不一定非要用json格式来传递数据,如果你愿意,字符串都行,只不过这样不利于用jsonp提供公开服务。
  5、jsonp整个过程中,本地站点一直处于主动的地位,主动的发送请求,主动的加载远程js.而第三方站点则处于被动的响应。

axios的实现原理

// 拦截器构造函数
function InterceptorManager() {
    this.handlers = [];
}
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
    this.handlers.push({
        fulfilled: fulfilled,
        rejected: rejected
    });
    return this.handlers.length - 1;
};


// 请求拦截和响应拦截均是拦截器的实例化
function Axios(instanceConfig) {
    this.defaults = instanceConfig;
    this.interceptors = {
        requestnew InterceptorManager(),
        responsenew InterceptorManager()
    };
}


// 用数组存储执行链
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
// 将请求拦截成对推入执行链的头部
// 知道这个原理以后,我们就知道在设置多个请求拦截时,会按照设置时的顺序,倒序处理

this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 将响应拦截成对推入执行链的尾部,执行时按照设置时的顺序,正序处理
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
});
// 依次成对执行,并用新的promise代替旧的promise,最后返回最新的promise
while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
}
return promise;
// 取消请求
function CancelToken(executor) {
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }
  // 在 CancelToken 上定义一个 pending 状态的 promise ,将 resolve 回调赋值给外部变量 resolvePromise
  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });


  var token = this;
  // 立即执行 传入的 executor函数,将真实的 cancel 方法通过参数传递出去。
  // 一旦调用就执行 resolvePromise 即前面的 promise 的 resolve,就更改promise的状态为 resolve。
  // 那么xhr中定义的 CancelToken.promise.then方法就会执行, 从而xhr内部会取消请求
  executor(function cancel(message) {
    // 判断请求是否已经取消过,避免多次执行
    if (token.reason) {
      return;
    }
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}


CancelToken.source = function source() {
  // source 方法就是返回了一个 CancelToken 实例,与直接使用 new CancelToken 是一样的操作
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  // 返回创建的 CancelToken 实例以及取消方法
  return {
    token: token,
    cancel: cancel
  };
};

package.json版本命名的几种方式

~和^

npm install --save xxx, 会优先考虑使用 ^而不是~

以版本号x.y.z为例

x:主版本号, 当你做了不兼容的API修改

y:次版本号, 当你做了向下兼容的功能性问题

z:修订号, 当你做了向下兼容的问题修复

~x.y.z, 会更新到y最新的版本, 

^x.y.z, 会更新到x的最新版本

Tips: 通常会使用package-lock.json来解决发布版本不一致问题

浏览器的重绘与回流

重绘不一定需要重排(比如颜色的改变)

重排(回流)必然导致重绘(比如改变网页位置)


重排(Reflow)(回流):

当渲染树的一部分必须更新并且节点的尺寸发生了变化,浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。


重绘(Repaint):

是在一个元素的外观被改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。比如改变某个元素的背景色、文字颜色、边框颜色等等


减少重排次数和缩小重排影响范围解决方式:

1. 将多次改变样式属性的操作,合成一次操作。

2. 将需要多次重排的元素,嗯,position属性。设为absolute。或fixed。使其脱离文档流,这样,他的变化就不会影响到其他的元素。

3. 在内存中多次操作节点。完成后再添加到文档中去。

4. 如果对一个元素进行复杂的操作,可以将display属性设为none嗯使其隐藏。操作完后再显示。

5. 在需要经常获取那些引起浏览器重排的属性值时,要缓存到变量。

用css隐藏dom的几种方式

方式一:display:none

.hide {
    display:none;
}
将元素设置为display:none后,元素在页面上将彻底消失,元素本来占有的空间就会被其他元素占有,也就是说它会导致浏览器的重排和重绘。

方式二:visibility:hidden


.hidden{
  visibility:hidden
}
元素在页面消失后,其占据的空间依旧会保留着,所以它只会导致浏览器重绘而不会重排。

方式三:opacity:0
.transparent {
  opacity:0;
}
设置透明度为0后,元素只是隐身了,它依旧存在页面中。

方式四:设置height,width等盒模型属性为0

display: flex几个常用的属性

 display:flex;/*flex块级,inline-flex:行内快*/


6个属性

flex-direction

 justify-content:space-around;
 /**
 *
center:水平居中,
 *
flex-start:靠左;
 *
flex-end:靠右
 *
space-between:两边的向两边靠,中间等分
 *
space-around:完美的平均分配
 **/

align-items:

align-items:stretch;
/**
*
center:垂直居中
*
flex-start至顶
*
flex-end:至底
*
space-between
*
space-around
**/
  

 flex-direction:

 flex-direction: row;
 /**
 *
column从上向下的排列
 *
column-reverse
 *
row:从左到右
 *
row-reverse:从右向左
 **/
 

flex-wrap:

flex-wrap:wrap;
/**
*
wrap行显示(父容器不够显示的时候,从上到下)、
*
nowrap(当容器不够宽的时候,子元素会平分父容器的宽或者高)、
*
wrap-reverse:从下向上
**/
 

  /*flex-flow是flex-direction、flex-wrap的缩写*/

vue父子组件的生命周期过程

加载渲染过程:beforeCreate->created->beforeMount->beforeCreate->created->beforeMount->mounted->mounted。


子组件更新过程:beforeUpdate->beforeUpdate->updated->updated


父组件更新过程:beforeUpdate->updated


销毁过程:beforeDestroy->beforeDestroy->destroyed->destroyed

vue的双向绑定

vue2.x版本的数据双向绑定主要通过Object.defineProperty()方法来进行数据劫持以及发布者-订阅模式来实现的。

概括描述:

1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。

3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。


vue3.x采用数据劫持结合发布者-订阅者模式的方式,通过new Proxy()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。


小结:Vue 3.0与Vue 2.0的区别仅是数据劫持的方式由Object.defineProperty更改为Proxy代理,其他代码不变。

虚拟dom性能好在哪里

优势:
1、虚拟 DOM 不会立马进行排版与重绘操作
2、虚拟 DOM 进行频繁修改,然后一次性比较并修改真实 DOM 中需要改的部分,最后在真实 DOM 中进行排版与重绘,减少过多DOM节点排版与重绘损耗
3、虚拟 DOM 有效降低大面积真实 DOM 的重绘与排版,因为最终与真实 DOM 比较差异,可以只渲染局部



缺点:

1、无法进行极致优化:虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
2、首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢。

父子组件的通信

1、Prop(常用)
2、$emit (组件封装用的较多)
3、.sync语法糖 (较少)
4、$attrs 和 $listeners (组件封装用的较多)
5、provide 和 inject (高阶组件/组件库用的较多)
6、其他方式通信
(EventBus、Vuex、$parent、$root、broadcast / dispatch【vue1.0的vue2.0去掉了】)

说说vue虚拟dom的算法

核心:

  • 渲染函数:渲染函数是用来生成Virtual DOM的。Vue推荐使用模板来构建我们的应用界面,在底层实现中Vue会将模板编译成渲染函数,当然我们也可以不写模板,直接写渲染函数,以获得更好的控制。

  • VNode 虚拟节点:它可以代表一个真实的 dom 节点。通过 createElement 方法能将 VNode 渲染成 dom 节点。简单地说,vnode可以理解成节点描述对象,它描述了应该怎样去创建真实的DOM节点。

  • patch(也叫做patching算法):虚拟DOM最核心的部分,它可以将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新。这点我们从单词含义就可以看出, patch本身就有补丁、修补的意思,其实际作用是在现有DOM上进行修改来实现更新视图的目的。Vue的Virtual DOM Patching算法是基于Snabbdom的实现,并在些基础上作了很多的调整和改进。

详细说明

虚拟DOM就是使用JS对象模拟真实的DOM结构,用JavaScript对象描述DOM的层次结构。
vue当中虚拟DOM的实现是参考一个库,snabbdom,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。


diff 算法包括几个步骤:

1、用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
2、当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
3、把所记录的差异应用到所构建的真正的DOM树上,视图就更新了


虚拟节点有哪些属性

children: 值可能是undefined(是undefined表示没有子元素),也可能使数组

data: {}

elm: undefined。elm是undefined说明这个节点还没有在DOM树上

key: undefined

sel: “div”。选择器

text:文字描述


patch函数

import {
  init,
  classModule,
  propsModule,
  styleModule,
  eventListenersModule,
  h,
from "snabbdom";

// 创建patch函数
let patch = init([classModule, propsModule, styleModule, eventListenersModule]);

// 创建一个虚拟节点,但是它还没有在DOM树上。要想把它放在DOM树上,需要patch函数
let vnode1 = h('a', {props: {href'http://www.baidu.com',target:'_blank'}}, '百度一下')

let container = document.getElementById('container');
// 让虚拟结点上树,patch函数只能让一个虚拟结点上树。如果vnode2和vnode3要上树,需要把这个注释掉
patch(container, vnode1);

let vnode2 = h('div',{class:{"box":true}},'我是一个盒子');

let vnode3 = h('ul',{},[
    h('li','苹果'), // 第二个参数可以没有
    h('li','香蕉'), // 这里已经调用了h函数
    h('li',[
        h('p',{},'桔子'),
        h('p',{},'哈哈')
    ]),
    h('li','西瓜'),
    h('li',h('span','火龙果')) // 如果children只有一个子元素,第三个参数可以不要数组
]);

vue-router两种模式的区别

hash模式

    原理

window.onhashchange = function(event){
  console.log(event.oldURL, event.newURL);
  let hash = location.hash.slice(1);
  document.body.style.color = hash;
}
每次 hash 值的变化,会触发hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。然后我们便可以监听hashchange来实现更新页面部分内容的操作
HashHistory有两个方法HashHistory.push()将新路由添加到浏览器访问历史的栈顶 和 HashHistory.replace()替换掉当前栈顶的路由

history模式

  原理

window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)


pushState() replaceState()。通过这两个 API 

(1)可以改变 url 地址且不会发送请求,
(2)不仅可以读取历史记录栈,还可以对浏览器历史记录栈进行修改。
除此之外,还有popState().当浏览器跳转到新的状态时,将触发popState事件.

拓展:

$router与$route的区别

 1. $route从当前router跳转对象里面可以获取name、path、query、params等(<router-link>传的参数由 this.$route.query或者 this.$route.params 接收)
2. $router为VueRouter实例。想要导航到不同URL,则使用$router.push方法;返回上一个history也是使用$router.go方法

vue-router的history模式在后端需要做什么配置

因为我们的应用是个单页客户端应用,所以呢,我们要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是我们app 依赖的页面。

以下选择一种方案即可:


nginx

 location / {
    try_files $uri $uri/ /index.html;
  }


Apache

  <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    RewriteRule ^index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.html [L]
  </IfModule>

原生 Node.js

const http = require('http')
  const fs = require('fs')
  const httpPort = 80
 
  http.createServer((req, res) => {
    fs.readFile('index.htm''utf-8', (err, content) => {
      if (err) {
        console.log('We cannot open "index.htm" file.')
      }
 
      res.writeHead(200, {
        'Content-Type''text/html; charset=utf-8'
     })
 
      res.end(content)
    })
  }).listen(httpPort, () => {
    console.log('Server listening on: http://localhost:%s', httpPort)
  })
当然也有其他不同的方案,按需自取

三、最后

       这套题目,考到的知识点比较多,有很多是日常开发中会涉及到的知识点。在日常开发工作中,我们可以多做些总结,对某些问题进行归类。从一点一滴提升自己的综合能力。

关注我,一起携手进阶

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存