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);
}
- Vuex 状态管理器
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
图片引用自网络:
vuex
Vuex 的具体使用。
- 通过父组件进行过渡
不是方法的方法:
子组件 A 通过事件 $emit 传值传给父组件。
父组件通过属性 props 传值给子组件 B。
深层次嵌套组件传值
- 依赖注入 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 这样真正的状态管理方案了。
- $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>
最后渲染出来的结果为:
作用域插槽
官网给这种插槽起名叫做作用域插槽,更多了解。
总结
- 组件之间传值无非就是通过属性、事件和操作 Vue 实例进行的。
- 操作实例进行组件件通信,实例属性 $root、$parent、$children 分别对应了根实例、父实例、子实例。
3 ref 子组件引用,在操作表单元素时会应用的到。
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,简单的应用不要使用 Vuex。
- 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 不是密码学里的散列运算)。
比如这个 URL:http://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:
params:
原型,原型链,闭包
原型与原型链
- 所有函数都有一个特别的属性:
prototype
: 显式原型属性
- 所有实例对象都有一个特别的属性:
__proto__
: 隐式原型属性
- 显式原型与隐式原型的关系
- 函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
- 实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
- 原型对象即为当前实例对象的父对象
- 原型链
- 所有的实例对象都有__proto__属性, 它指向的就是原型对象
- 这样通过__proto__属性就形成了一个链的结构---->原型链
- 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
- 当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作