近期面试题整理汇总(持续更新)(中)

本文涉及的产品
.cn 域名,1个 12个月
简介: 我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,我们称之为 hash,大型框架的路由系统大多都是哈希实现的。

hash



我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓的锚点,比如典型的回到顶部按钮原理、Github 上各个标题之间的跳转等,路由里的 # 不叫锚点,我们称之为 hash,大型框架的路由系统大多都是哈希实现的


同样我们需要一个根据监听哈希变化触发的事件 ——hashchange 事件


我们用 window.location 处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中,这样我们跳转页面就可以在 hashchange 事件中注册 ajax 从而改变页面内容。


hashchange 在低版本 IE 需要通过轮询监听 url 变化来实现,我们可以模拟如下


(function(window) {
// 如果浏览器不支持原生实现的事件,则开始模拟,否则退出。
if ( "onhashchange" in window.document.body ) { return; }
var location = window.location,
 oldURL = location.href,
 oldHash = location.hash;
// 每隔100ms检查hash是否发生变化
setInterval(function() {
   var newURL = location.href,
   newHash = location.hash;
  // hash发生变化且全局注册有onhashchange方法(这个名字是为了和模拟的事件名保持统一)
   if ( newHash != oldHash && typeof window.onhashchange === "function"  ) {
     // 执行方法
     window.onhashchange({
       type: "hashchange",
       oldURL: oldURL,
       newURL: newURL
     });
     oldURL = newURL;
     oldHash = newHash;
   }
 }, 100);
})(window)

个人感觉还是hash方案好一点,因为照顾到低级浏览器,就是多了#号感觉不太美观,两者兼容也是可以的,只能根据浏览器的版本给出对应的方案 不过也支持IE8+ 还是不错的


兄弟组件的传值


注意:注册的 Bus 要在组件销毁时卸载,否则会多次挂载,造成触发一次但多个响应的情况。


beforeDestroy () {

this.$Bus.$off('sendMessage', this.message);

}


  1. Vuex 状态管理器


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


图片引用自网络:


vuex


Vuex 的具体使用。


  1. 通过父组件进行过渡


不是方法的方法:


子组件 A 通过事件 $emit 传值传给父组件。

父组件通过属性 props 传值给子组件 B。


深层次嵌套组件传值


  1. 依赖注入 provide/inject

provide 选项允许我们指定我们想要提供给后代组件的数据/方法。

provide: function () {
return {
getMap: this.getMap
}
}


然后在任何后代组件里,我们都可以使用 inject 选项来接收指定的我们想要添加在这个实例上的属性:


inject: ['getMap']


provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。


// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo'
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}

然而,依赖注入还是有负面影响的。它将你应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的。这是出于设计的考虑,因为使用它们来创建一个中心化规模化的数据跟使用 $root 做这件事都是不够好的。如果你想要共享的这个属性是你的应用特有的,而不是通用化的,或者如果你想在祖先组件中更新所提供的数据,那么这意味着你可能需要换用一个像 Vuex 这样真正的状态管理方案了。


  1. $attrs/inheritAttrs


这个两个属性是 2.4 新增的特性。


$attrs:


官网介绍的很累赘,暂且理解为非 props 属性集合。更多介绍。


当一个组件中没有声明任何 prop 时,this.$attrs 可以获取到所有父作用域的属性绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传给其内部组件 —— 在创建高级别的组件时非常有用。


inheritAttrs:


控制元素属性是否显示在 dom 上,默认值为 true。


默认情况下父作用域的不被认作 props 的特性绑定 (attribute bindings) 将会“回退”且作为普通的 HTML 特性应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过 (同样是 2.4 新增的) 实例属性 $attrs 可以让这些特性生效,且可以通过 v-bind 显性的绑定到非根元素上。


祖先组件:

<template>
<div>
<List-item :title="title" :message="message"></List-item>
</div>
</template>
<script>
import ListItem from "./List-item";
export default {
data() {
return {
  title: "我是title",
  message: "传给后代"
}
},
components: {
ListItem
}
}
</script>
父组件:
<template>
<div>
<h1>{{title}}</h1>
<h2>{{$attrs.message}}</h2>
<!-- 通过 v-bind="$attrs" 传入后代组件-->
<ListItem2 v-bind='$attrs'></ListItem2>
</div>
</template>
<script>
import ListItem2 from './List-item2'
export default {
props: {
title: String
},
components: {
ListItem2
},
// 默认为 true,如果传入的属性子组件没有 prop 接受,就会以字符串的形式作为标签的属性存在 <div message="传给后代"></div>
// 设为 false,在 dom 中就看不到这些属性 <div>...</div>
inheritAttrs: false
}
</script>
后代组件:
<template>
<div>
{{$attrs.message}}
</div>
</template>
<script>
export default {
mounted() {
console.log(this.$attrs)    // {message: "传给后代"}
}
}
</script>

渲染出来的结果为:


$attrs/inheritAttrs


插槽 slot 与子组件传值


在实际项目中确实有遇到插槽后备内容 动态显示的情况,所以这里要补充一下插槽 后备内容 是如何与子组件进行通信的。


插槽后备内容是指:写在父组件中,包含在子组件标签里的,与子组件中的 slot 对应。

<template>
<child-component>

我是插槽的后备内容

</child-component>
</template>
比如这里有一个含有 slot 的 current-user 组件,它的模版结构是这样的:
<!-- 子组件 current-user.vue -->
<template>
<div>
<div>current-user组件</div>
<slot>插槽里默认显示:{{user.firstName}}</slot>
</div>
</template>
<script>
export default {
data() {
return {
  user: {
    firstName: "zhao",
    lastName: "xinglei"
  }
}
}
}


它的父组件是这样的:

<!-- 父组件 Users.vue -->
<template>
<div>
<div>我是Users组件</div>
<current-user>
  我是插槽里的后备内容: {{user.lastName}}(我想显示为子组件中 user.lastName )
</current-user>
</div>
</template>
<script>
import CurrentUser from './Current-User.vue'
export default {
components: {
CurrentUser
}
}


我们看到,在父组件 Users 中,为子组件 current-user 提供的后备内容中,想要显示子组件定义的 user.firstName 是不能做到的。


官网中提供一个指令 v-slot,它与 props 结合使用从而达到插槽后备内容与子组件通信的目的。


我们首先需要在子组件的 slot 中传递一个 props(这个props 叫做插槽props),这里我们起名叫 user:


<!-- 子组件 current-user.vue -->
<template>
<div>
<div>current-user组件</div>
<slot :user="user">

 

插槽里默认显示:{{user.firstName}}


</slot>
</div>
</template>


在父组件中,包含插槽后备内容的子组件标签上我们绑定一个 v-slot 指令,像这样:


<template>
<div>
<div>我是Users组件</div>
<!-- slotProps里的内容就是子组件传递过来的 props -->
<!-- "user": { "firstName": "zhao", "lastName": "xinglei" } -->
<current-user v-slot="slotProps">
  {{slotProps}}
</current-user>
</div>
</template>


最后渲染出来的结果为:


作用域插槽

官网给这种插槽起名叫做作用域插槽,更多了解。


总结

  1. 组件之间传值无非就是通过属性、事件和操作 Vue 实例进行的。
  2. 操作实例进行组件件通信,实例属性 $root、$parent、$children 分别对应了根实例、父实例、子实例。

3 ref 子组件引用,在操作表单元素时会应用的到。

  1. Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,简单的应用不要使用 Vuex。
  2. Vue.observable() 让一个对象可响应,可以作为最小化的跨组件状态存储器(本文未提到)。


说说vue的响应式


Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。


Mixin


混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。


// 定义一个混入对象
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log('hello from mixin!')
    }
  }
}
// 定义一个使用混入对象的组件
var Component = Vue.extend({
  mixins: [myMixin]
})
var component = new Component() // => "hello from mixin!"


vue的订阅发布者


##### 1、vue响应原理:
vue.js采用数据劫持结合发布-订阅者模式,通过Object.defineProperty()来劫持data中各个属性的setter、getter,在数据变动时,发布消息给订阅者,触发响应的监听回调。
(setter和getter是对象的存储器属性,是一个函数,用来获取和设置值)
##### 2、发布-订阅者模式的作用:
##### 1、vue响应原理:
vue.js采用数据劫持结合发布-订阅者模式,通过Object.defineProperty()来劫持data中各个属性的setter、getter,在数据变动时,发布消息给订阅者,触发响应的监听回调。
(setter和getter是对象的存储器属性,是一个函数,用来获取和设置值)
##### 2、发布-订阅者模式的作用:
处理一对多的场景,应用于不同情况下的不同函数调用
优点:低耦合性,易于代码维护;
缺点:若订阅的消息未发生,需消耗一定的时间和内存。
[![复制代码](https://upload-images.jianshu.io/upload_images/23849911-0de625688003404e.gif?imageMogr2/auto-orient/strip)](javascript:void(0); "复制代码") 
<pre><!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Vue发布-订阅模式</title>
</head>
<body>
    <div id="app"> 订阅试图-1:<span class="box-1">第一个值</span>
        订阅试图-2:<span class="box-2">第二个值</span>
    </div>
    <script>
        //订阅器模型
        var Dep = {
            list: {},
            listen: function (key, fn) {
                (this.list[key] || (this.list[key] = [])).push(fn);
            },
            trigger: function () { var key = Array.prototype.shift.call(arguments);
                fns = this.list[key]; if (!fns || fns.length == 0) return; for (var i = 0, fn; fn = fns[i++];) {
                    fn.apply(this, arguments);//发布消息附带的参数
 }
            }
        }; //劫持的方法 Object.defineProperty方法,给对象的属性赋值
        var dataHijack = function ({ data, tag, datakey, selector }) { debugger
            var value = '';
            el = document.querySelector(selector);
            Object.defineProperty(data, datakey, { //拿到数据
                get: function () {
                    console.log('我获取到值了'); return value;
                }, //设置数据
                set: function (newVal) {
                    console.log('我设置值了');
                    value = newVal;
                    Dep.trigger(tag, newVal); //发布消息,更新变化
 }
            }) //绑定观察者
            Dep.listen(tag, function (text) {
                el.innerHTML = text;
            })
        }; var dataObj = {}; //数据
        //数据劫持
dataHijack({
            data: dataObj,
            tag: 'view-1',
            datakey: 'one',
            selector: '.box-1' });
        dataHijack({
            data: dataObj,
            tag: 'view-2',
            datakey: 'two',
            selector: '.box-2' }); </script>
</body>
</html></pre>
[![复制代码](https://upload-images.jianshu.io/upload_images/23849911-c0f72b5bf7fc48f0.gif?imageMogr2/auto-orient/strip)](javascript:void(0); "复制代码") 
[![复制代码](https://upload-images.jianshu.io/upload_images/23849911-09d49fa4ab9cd9bd.gif?imageMogr2/auto-orient/strip)](javascript:void(0); "复制代码") 
<pre>        // jquery中的发布-订阅者
        //创建一个事件池 $.Callback()
        let $pond= $.Callback();
        $('.submit').click(function(){ //发布  点击的时候通知事件池中的方法执行,同时传递实参
            $pond.fire(100,200);
        });
        let fn1=function(){console.log(1)}
        let fn2=function(){console.log(2)}
        let fn3=function(n,m){console.log(3,n+m)} //把需要做的事情添加到事件池中
        //事件池相当于一个登记册,把所有订阅者收集到上面
 $pond.add(fn1);
        $pond.add(fn2);
        $pond.add(fn3); </pre>


5.HTTP




常见的状态码


各类别常见状态码:
2xx (3种)
200 OK:表示从客户端发送给服务器的请求被正常处理并返回;
204 No Content:表示客户端发送给客户端的请求得到了成功处理,但在返回的响应报文中不含实体的主体部分(没有资源可以返回);
206 Patial Content:表示客户端进行了范围请求,并且服务器成功执行了这部分的GET请求,响应报文中包含由Content-Range指定范围的实体内容。
3xx (5种)
301 Moved Permanently:永久性重定向,表示请求的资源被分配了新的URL,之后应使用更改的URL;
302 Found:临时性重定向,表示请求的资源被分配了新的URL,希望本次访问使用新的URL;
       301与302的区别:前者是永久移动,后者是临时移动(之后可能还会更改URL)
303 See Other:表示请求的资源被分配了新的URL,应使用GET方法定向获取请求的资源;
      302与303的区别:后者明确表示客户端应当采用GET方式获取资源
304 Not Modified:表示客户端发送附带条件(是指采用GET方法的请求报文中包含if-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)的请求时,服务器端允许访问资源,但是请求为满足条件的情况下返回改状态码;
307 Temporary Redirect:临时重定向,与303有着相同的含义,307会遵照浏览器标准不会从POST变成GET;(不同浏览器可能会出现不同的情况);
4xx (4种)
400 Bad Request:表示请求报文中存在语法错误;
401 Unauthorized:未经许可,需要通过HTTP认证;
403 Forbidden:服务器拒绝该次访问(访问权限出现问题)
404 Not Found:表示服务器上无法找到请求的资源,除此之外,也可以在服务器拒绝请求但不想给拒绝原因时使用;
5xx (2种)
500 Inter Server Error:表示服务器在执行请求时发生了错误,也有可能是web应用存在的bug或某些临时的错误时;
503 Server Unavailable:表示服务器暂时处于超负载或正在进行停机维护,无法处理请求;


Webpack



Webpack性能优化


优化一: vue-router路由懒加载


`懒加载:`也叫延迟加载,即在需要的时候进行加载,随用随载。      使用懒加载的原因: `vue`是单页面应用,使用`webpcak`打包后的文件很大,会使进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。运用懒加载后,就可以按需加载页面,提高用户体验。


懒加载的写法:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
   {
      path: '/',
      component: resolve => require(['@/components/DefaultIndex'],resolve),
      children: [
        {
          path: '',
          component: resolve => require(['@/components/Index'],resolve)
        },
        {
          path: '*',
          redirect: '/Index'
        }
      ]
})
复制代码


非懒加载的路由配置:


import Vue from 'vue'
import Router from 'vue-router'
import DefaultIndex from '@/components/DefaultIndex'
import Index from '@/components/Index'
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
   {
      path: '/',
      component: 'DefaultIndex ',
      children: [
        {
          path: '',
          component: 'Index'
        },
        {
          path: '*',
          redirect: '/Index'
        }
      ]
})
复制代码


优化二:webpack压缩图片(减少图片大小)


一般在`vue`项目中用`webpack`打包时,会根据`webpack.base.conf.js`中`url-loader`中设置`limit`大小来对图片处理,对小于`limit`的图片转化为`base64`格式,其余的不做操作。所以对有些较大的图片资源,在请求资源的时候,加载会很慢,可以用`image-webpack-loader`来压缩图片。


安装:
npm install image-webpack-loader --save-dev
复制代码

配置:


 在`webpack.base.conf.js`文件中引入配置(此项目我用的是脚手架搭建的,所以是`webpack.base.conf.js`)


1: 引入:

require("image-webpack-loader")


2:配置:

module: {
    rules: [
    ...(config.dev.useEslint ? [createLintingRule()] : []),
    {
      test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
      loader: 'url-loader',
      options:   {
       loader: 'image-webpack-loader',
       options: {
        bypassOnDebug: true,
      }
    }
  },


或者也可配置为:


{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  use:[
    {
    loader: 'url-loader',
    options: {
      limit: 10000,
      name: utils.assetsPath('img/[name].[hash:7].[ext]')
      }
    },
    {
      loader: 'image-webpack-loader',
      options: {
        bypassOnDebug: true,
      }
    }
  ]
}
复制代码


优化三:打包后的js过大,将js打包多个文件


由于`webpack`打包后的`js`过大,以至于在加载资源时间过长。所以将文件打包成多个`js`文件,在需要的时候按需加载。



优化方案:
entry:{ 
main:'xxx.js'
} 
plugins:{
new commonsChunkPlugin({
name:'commons',
minChunks:function(module){
  // 下边return参考的vue-cli配置
  // any required modules inside node_modules are extracted to vendor
  return (
   module.resource &&
   /\.js$/.test(module.resource) &&
   module.resource.indexOf(
   path.join(__dirname, '../node_modules')
   ) === 0
  )
 }
}) ,
// 以下才是关键
new commonsChunkPlugin({
name:'charts',
chunks:['commons'] 
minChunks:function(module){
  return (
   module.resource &&
   /\.js$/.test(module.resource) &&
   module.resource.indexOf(
   path.join(__dirname, '../node_modules')
   ) === 0 && ['jquery.js', 'highcharts.js','echarts'].indexOf( module.resource.substr(module.resource.lastIndexOf('/')+1).toLowerCase() ) != -1
  )
 }
}) 
}
复制代码


优化四:去掉不必要的插件


1:打包时,将一些不必要的插件可以去掉,以防止打包一些无用的资源,导致打包后的文件过大,影响性能。


2:在引入第三方插件的时候,如果该插件的组件过大,可以按需引入,如`element-ui`。


3:使用`webpack.optimize.UglifyJsPlugin`插件压缩混淆[js](http://lib.csdn.net/base/javascript "JavaScript知识库")代码,使用方法如下:


plugins: [//webpack.config.jsnew webpack.optimize.UglifyJsPlugin({    warnings: false,
    compress: {
        join_vars: true,
        warnings: false,
    },
    toplevel: false,
    ie8: false,
]
复制代码


优化五: gzip压缩


web前端项目,静态资源放在`cdn`上比较多,`gzip`的压缩是非常必要的,它直接改变了`js`文件的大小,减少两到三倍。 参考[加速nginx: 开启gzip和缓存](https://link.jianshu.com/?t=https%3A%2F%2Fwww.darrenfang.com%2F2015%2F01%2Fsetting-up-http-cache-and-gzip-with-nginx%2F),`nginx``gzip`配置非常简单,在你对应的域名底下,添加下面的配置,重启服务即可。`gzip_comp_level`的值大于`2`的时候并不明显,建议设置在`1或者2`之间。


# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\.";
复制代码
 这种方法我 没有使用过,有用过的亲,可以留言,沟通一下下。


优化六: 服务器缓存


为了提高服务器获取数据的速度,`nginx`缓存着静态资源是非常必要的。如果是测试服务器对`html`应该不设置缓存,而`js`等静态资源环境因为文件尾部会加上一个`hash`值,这可以有效实现缓存的控制。


location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { 
  access_log   off; 
  expires      30d;
}
location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
  access_log   off;
  expires      24h;
}
location ~* ^.+\.(html|htm)$ {
  expires      1h;
}


6.其他


Angular和vue的区别

angularjs和vue的区别:

1、vueJS简单易学,而angularJS的上手较高;

2、vue专注于View层,主打的是快速便捷,而angularJS功能则比较全面,当然体量也较大,相对没有vue那么便捷;

3、angularJS的指令都是ng-xxx,而vueJS的指令都是v-xxx;

4、angularJS的所有指令和方法都是绑定在$scope上的,而vueJS是new出来一个实例,所有的方法和指令都在这个实例上,一个页面上可以有多个vue实例,但是angularJS的对象只能有一个;

5、angularJS是由google开发和维护的,vueJS是由个人维护的;

6、vueJS一般用于移动端的开发,而angularJS一般应用于大型的项目

Post和get请求区别

GET 被强制服务器支持
浏览器对URL的长度有限制,所以GET请求不能代替POST请求发送大量数据
GET请求发送数据更小
GET请求是安全的
GET请求是幂等的
POST请求不能被缓存
POST请求相对GET请求是「安全」的


Css动画跟js动画的区别


CSS动画
优点: (1)浏览器可以对动画进行优化。
         1、 浏览器使用与 requestAnimationFrame 类似的机制,requestAnimationFrame比起setTimeout,setInterval设置动画的优势主要是:1)requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。2)在隐藏或不可见的元素中requestAnimationFrame不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。
   2、强制使用硬件加速 (通过 GPU 来提高动画性能)
      (2)代码相对简单,性能调优方向固定
          (3)对于帧速表现不好的低版本浏览器,CSS3可以做到自然降级,而JS则需要撰写额外代码
缺点:
  1、 运行过程控制较弱,无法附加事件绑定回调函数。CSS动画只能暂停,不能在动画中寻找一个特定的时间点,不能在半路反转动画,不能变换时间尺度,不能在特定的位置添加回调函数或是绑定回放事件,无进度报告
    2、  代码冗长。想用 CSS 实现稍微复杂一点动画,最后CSS代码都会变得非常笨重。
JS动画
优点:
    (1)JavaScript动画控制能力很强, 可以在动画播放过程中对动画进行控制:开始、暂停、回放、终止、取消都是可以做到的。
        (2)动画效果比css3动画丰富,有些动画效果,比如曲线运动,冲击闪烁,视差滚动效果,只有JavaScript动画才能完成
        (3)CSS3有兼容性问题,而JS大多时候没有兼容性问题
缺点:
   (1)JavaScript在浏览器的主线程中运行,而主线程中还有其它需要运行的JavaScript脚本、样式计算、布局、绘制任务等,对其干扰导致线程可能出现阻塞,从而造成丢帧的情况。
         (2)代码的复杂度高于CSS动画
总结:如果动画只是简单的状态切换,不需要中间过程控制,在这种情况下,css动画是优选方案。它可以让你将动画逻辑放在样式文件里面,而不会让你的页面充斥 Javascript 库。然而如果你在设计很复杂的富客户端界面或者在开发一个有着复杂UI状态的 APP。那么你应该使用js动画,这样你的动画可以保持高效,并且你的工作流也更可控。所以,在实现一些小的交互动效的时候,就多考虑考虑CSS动画。对于一些复杂控制的动画,使用javascript比较可靠。
css 动画和 js 动画的差异
1. 代码复杂度,js 动画代码相对复杂一些
2. 动画运行时,对动画的控制程度上,js 能够让动画,暂停,取消,终止,css动画不能添加事件
3. 动画性能看,js 动画多了一个js 解析的过程,性能不如 css 动画好
Hash跟history的区别

vue-router 中hash模式和history模式。


在vue的路由配置中有mode选项,最直观的区别就是在url中hash 带了一个很丑的 # ,而history是没有#的。vue默认使用hash。


mode:"hash";
mode:"history";
hash


—— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。

比如这个 URLhttp://www.aaa.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。


history


—— 利用了 HTML5HistoryInterface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)


这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。


因此可以说,hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(通过调用浏览器提供的接口)来实现前端路由。


history模式的问题


      通过history api,我们丢掉了丑陋的#,但是它也有个问题:不怕前进,不怕后退,就怕刷新,f5,(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的。


      在hash模式下,前端路由修改的是#中的信息,而浏览器请求时不会将 # 后面的数据发送到后台,所以没有问题。但是在history下,你可以自由的修改path,当刷新时,如果服务器中没有相应的响应或者资源,则会刷新出来404页面。

跨域问题


2. 前端解决跨域问题



1> document.domain + iframe (只有在主域相同的时候才能使用该方法)


1) 在www.a.com/a.html中:

document.domain = 'a.com';var ifr = document.createElement('iframe');ifr.src = 'http://www.script.a.com/b.html';ifr.display = none;document.body.appendChild(ifr);ifr.onload = function(){    var doc = ifr.contentDocument || ifr.contentWindow.document;    //在这里操作doc,也就是b.html    ifr.onload = null;};


2) 在www.script.a.com/b.html中:

document.domain = 'a.com';


2> 动态创建script


这个没什么好说的,因为script标签不受同源策略的限制。


function loadScript(url, func) {  var head = document.head || document.getElementByTagName('head')[0];  var script = document.createElement('script');  script.src = url;   script.onload = script.onreadystatechange = function(){    if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){      func();      script.onload = script.onreadystatechange = null;    }  };   head.insertBefore(script, 0);}window.baidu = {  sug: function(data){    console.log(data);  }}loadScript('http://suggestion.baidu.com/su?wd=w',function(){console.log('loaded')});//我们请求的内容在哪里?//我们可以在chorme调试面板的source中看到script引入的内容


3> location.hash + iframe


原理是利用location.hash来进行传值。


假设域名a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息。


1) cs1.html首先创建自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面

2) cs2.html响应请求后再将通过修改cs1.html的hash值来传递数据

3) 同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没有变化,一旦有变化则获取获取hash值


注:由于两个页面不在同一个域下IE、Chrome不允许修改parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe


代码如下:

先是a.com下的文件cs1.html文件:


function startRequest(){    var ifr = document.createElement('iframe');    ifr.style.display = 'none';    ifr.src = 'http://www.cnblogs.com/lab/cscript/cs2.html#paramdo';    document.body.appendChild(ifr);} function checkHash() {    try {        var data = location.hash ? location.hash.substring(1) : '';        if (console.log) {            console.log('Now the data is '+data);        }    } catch(e) {};}setInterval(checkHash, 2000);


cnblogs.com域名下的cs2.html:


//模拟一个简单的参数处理操作switch(location.hash){    case '#paramdo':        callBack();        break;    case '#paramset':        //do something……        break;} function callBack(){    try {        parent.location.hash = 'somedata';    } catch (e) {        // ie、chrome的安全机制无法修改parent.location.hash,        // 所以要利用一个中间的cnblogs域下的代理iframe        var ifrproxy = document.createElement('iframe');        ifrproxy.style.display = 'none';        ifrproxy.src = 'http://a.com/test/cscript/cs3.html#somedata';    // 注意该文件在"a.com"域下        document.body.appendChild(ifrproxy);    }}


a.com下的域名cs3.html


//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值parent.parent.location.hash = self.location.hash.substring(1);


4> window.name + iframe


window.name 的美妙之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。


1) 创建a.com/cs1.html

2) 创建a.com/proxy.html,并加入如下代码

<head>  <script>  function proxy(url, func){    var isFirst = true,        ifr = document.createElement('iframe'),        loadFunc = function(){          if(isFirst){            ifr.contentWindow.location = 'http://a.com/cs1.html';            isFirst = false;          }else{            func(ifr.contentWindow.name);            ifr.contentWindow.close();            document.body.removeChild(ifr);            ifr.src = '';            ifr = null;          }        };     ifr.src = url;    ifr.style.display = 'none';    if(ifr.attachEvent) ifr.attachEvent('onload', loadFunc);    else ifr.onload = loadFunc;     document.body.appendChild(iframe);  }</script></head><body>  <script>    proxy('http://www.baidu.com/', function(data){      console.log(data);    });  </script></body>


3 在b.com/cs1.html中包含:


<script>    window.name = '要传送的内容';</script>


5> postMessage(HTML5中的XMLHttpRequest Level 2中的API)


1) a.com/index.html中的代码:

<iframe id="ifr" src="b.com/index.html"></iframe><script type="text/javascript">window.onload = function() {    var ifr = document.getElementById('ifr');    var targetOrigin = 'http://b.com';  // 若写成'http://b.com/c/proxy.html'效果一样                                        // 若写成'http://c.com'就不会执行postMessage了    ifr.contentWindow.postMessage('I was there!', targetOrigin);};</script>


2) b.com/index.html中的代码:

<script type="text/javascript">    window.addEventListener('message', function(event){        // 通过origin属性判断消息来源地址        if (event.origin == 'http://a.com') {            alert(event.data);    // 弹出"I was there!"            alert(event.source);  // 对a.com、index.html中window对象的引用                                  // 但由于同源策略,这里event.source不可以访问window对象        }    }, false);</script>


6> CORS


CORS背后的思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。


IE中对CORS的实现是xdr

var xdr = new XDomainRequest();xdr.onload = function(){    console.log(xdr.responseText);}xdr.open('get', 'http://www.baidu.com');......xdr.send(null);
其它浏览器中的实现就在xhr中
var xhr =  new XMLHttpRequest();xhr.onreadystatechange = function () {    if(xhr.readyState == 4){        if(xhr.status >= 200 && xhr.status < 304 || xhr.status == 304){            console.log(xhr.responseText);        }    }}xhr.open('get', 'http://www.baidu.com');......xhr.send(null);


实现跨浏览器的CORS


function createCORS(method, url){    var xhr = new XMLHttpRequest();    if('withCredentials' in xhr){        xhr.open(method, url, true);    }else if(typeof XDomainRequest != 'undefined'){        var xhr = new XDomainRequest();        xhr.open(method, url);    }else{        xhr = null;    }    return xhr;}var request = createCORS('get', 'http://www.baidu.com');if(request){    request.onload = function(){        ......    };    request.send();}


7> JSONP


JSONP包含两部分:回调函数和数据。


回调函数是当响应到来时要放在当前页面被调用的函数。

数据就是传入回调函数中的json数据,也就是回调函数的参数了。


function handleResponse(response){    console.log('The responsed data is: '+response.data);}var script = document.createElement('script');script.src = 'http://www.baidu.com/json/?callback=handleResponse';document.body.insertBefore(script, document.body.firstChild);/*handleResonse({"data": "zhe"})*///原理如下://当我们通过script标签请求时//后台就会根据相应的参数(json,handleResponse)//来生成相应的json数据(handleResponse({"data": "zhe"}))//最后这个返回的json数据(代码)就会被放在当前js文件中被执行//至此跨域通信完成


jsonp虽然很简单,但是有如下缺点:


1)安全问题(请求代码中可能存在安全隐患)

2)要确定jsonp请求是否失败并不容易


8> web sockets


web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)


web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。


只有在支持web socket协议的服务器上才能正常工作。


var socket = new WebSockt('ws://www.baidu.com');//http->ws; https->wsssocket.send('hello WebSockt');socket.onmessage = function(event){    var data = event.data;}


vue 的params和query的区别


初学vue的时候,不知道如何在方法中跳转界面并传参,百度过后,了解到两种方式,params 与 query。然后,错误就这么来了:


   router文件下index.js里面,是这么定义路由的:

<pre>{
  path:"/detail",
  name:"detail",
  component:home
}</pre>

 

 我想用params来传参,是这么写的,嗯~

; "复制代码")

this.$router.push({
path:"/detail",
params:{
name:'nameValue',
code:10011
}
});


; "复制代码")

  结果可想而知,接收参数的时候:

<pre>this.$route.params.code //undefined</pre>

  这是因为,params只能用name来引入路由,下面是正确的写法:

; "复制代码")


<pre>this.$router.push({
name:"detail",
params:{
name:'nameValue',
code:10011
}
});</pre>


; "复制代码")


这回就对了,可以直接拿到传递过来的参数nameValue了。


说完了我的犯傻,下面整理一下这两者的差别:


1、用法上的

刚才已经说了,query要用path来引入,params要用name来引入,接收参数都是类似的,分别是this.$route.query.name和this.$route.params.name。

注意接收参数的时候,已经是$route而不是$router了哦!!


2、展示上的

query更加类似于我们ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示

query:1.JPG
params:2.JPG

原型,原型链,闭包


原型与原型链



  • 所有函数都有一个特别的属性:
  • prototype : 显式原型属性
  • 所有实例对象都有一个特别的属性:
  • __proto__ : 隐式原型属性
  • 显式原型与隐式原型的关系
  • 函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
  • 实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
  • 原型对象即为当前实例对象的父对象
  • 原型链
  • 所有的实例对象都有__proto__属性, 它指向的就是原型对象
  • 这样通过__proto__属性就形成了一个链的结构---->原型链
  • 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
  • 当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作
相关文章
|
6月前
|
人工智能 算法
【阅读】一周翻过《构建之法》,笔记整理
🚩 前言 我的阅读方式 我拿到这本书挺久了,之前已经零散地看过一部分,最近一周集中地花了一些时间,将整本书看过了一遍。看得比较粗略,正如“好读书,不求甚解”(我甚至没有去看书中提到的那些参考资料)。
51 0
|
边缘计算 网络协议 网络架构
DoIP看这篇就够了,吐血整理
DoIP看这篇就够了,吐血整理
DoIP看这篇就够了,吐血整理
|
前端开发 JavaScript 容器
2023年前端面试整理(持续更新中)
2023年前端面试整理(持续更新中)
太牛了!腾讯T9耗时69天整理出最全架构师进阶核心知识点笔记
每一个程序员应该都想着如何快速提升自己(反正我是这样想的),从程序员进阶到架构师再到技术专家再到CTO 。当然这其中需要大量的知识储备,是一个不断学习的过程,话不多说下面我直接上图。
|
算法
|
人工智能 架构师 前端开发
清华大牛带领20位大厂专家耗时三年整理出这份2000页Java进阶指南
自我介绍: 马士兵教育: 由马士兵老师携手BATJ名师为大家提供java架构师培训,人工智能培训,大数据培训,软件测试培训,Web前端培训,区块链培训等课程,课程均由一线互联网公司技术大牛精心研发,全程项目式教学!
|
前端开发 测试技术
测试领域专业术语整理-持续更新
测试领域专业术语整理-持续更新
301 0
|
SQL 消息中间件 设计模式
近期大厂面试题总结
近期大厂面试题总结
143 0
|
Web App开发 设计模式 移动开发
近期面试题整理汇总(持续更新)(上)
1.HTML和CSS Html5新增标签和属性
160 0
近期面试题整理汇总(持续更新)(上)
|
存储 Web App开发 自然语言处理
近期面试题整理汇总(持续更新)(下)
• 变量提升与函数提升 • 变量提升: 在变量定义语句之前, 就可以访问到这个变量(undefined) • 函数提升: 在函数定义语句之前, 就执行该函数 • 先有变量提升, 再有函数提升
150 0
近期面试题整理汇总(持续更新)(下)

相关实验场景

更多
下一篇
无影云桌面