WebSocket 是什么原理(知乎上的很搞笑)

作者:Ovear
链接:https://www.zhihu.com/question/20215561/answer/40316953
来源:知乎

一、WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)
首先HTTP有1.1和1.0之说,也就是所谓的keep-alive,把多个HTTP请求合并为一个,但是Websocket其实是一个新协议,跟HTTP协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是HTTP协议上的一种补充可以通过这样一张图理解

有交集,但是并不是全部。
另外Html5是指的一系列新的API,或者说新规范,新技术。Http协议本身只有1.0和1.1,而且跟Html本身没有直接关系。。
通俗来说,你可以用HTTP协议传输非Html数据,就是这样=。=
再简单来说,层级不一样

二、Websocket是什么样的协议,具体有什么优点
首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
简单的举个例子吧,用目前应用比较广泛的PHP生命周期来解释。
1) HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么HTTP1.0,这次HTTP请求就结束了。
在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。
但是请记住 Request = Response , 在HTTP中永远是这样,也就是说一个request只能有一个response。而且这个response也是被动的,不能主动发起。

教练,你BB了这么多,跟Websocket有什么关系呢?
_(:з」∠)_好吧,我正准备说Websocket呢。。
首先Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。
在握手阶段是一样的
——-以下涉及专业技术内容,不想看的可以跳过lol:,或者只看加黑内容——–
首先我们来看个典型的Websocket握手(借用Wikipedia的。。)

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

熟悉HTTP的童鞋可能发现了,这段类似HTTP协议的握手请求中,多了几个东西。
我会顺便讲解下作用。

Upgrade: websocket
Connection: Upgrade

这个就是Websocket的核心了,告诉Apache、Nginx等服务器:注意啦,窝发起的是Websocket协议,快点帮我找到对应的助理处理~不是那个老土的HTTP。

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

首先,Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,告诉服务器:泥煤,不要忽悠窝,我要验证尼是不是真的是Websocket助理。
然后,Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议。简单理解:今晚我要服务A,别搞错啦~
最后,Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本),在最初的时候,Websocket协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么Firefox和Chrome用的不是一个版本之类的,当初Websocket协议太多可是一个大难题。。不过现在还好,已经定下来啦~大家都使用的一个东西~ 脱水:服务员,我要的是13岁的噢→_→

然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦!

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~

Upgrade: websocket
Connection: Upgrade

依然是固定的,告诉客户端即将升级的是Websocket协议,而不是mozillasocket,lurnarsocket或者shitsocket。
然后,Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key。服务器:好啦好啦,知道啦,给你看我的ID CARD来证明行了吧。。
后面的,Sec-WebSocket-Protocol 则是表示最终使用的协议。

至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。
具体的协议就不在这阐述了。
——————技术解析部分完毕——————

你TMD又BBB了这么久,那到底Websocket有什么鬼用,http long poll,或者ajax轮询不都可以实现实时信息传递么。

好好好,年轻人,那我们来讲一讲Websocket有什么用。
来给你吃点胡(苏)萝(丹)卜(红)

三、Websocket的作用
在讲Websocket之前,我就顺带着讲下 long poll 和 ajax轮询 的原理。
首先是 ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
场景再现:
客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) —- loop

long poll
long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
场景再现
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。   等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop

从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性
何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。
简单地说就是,服务器是一个很懒的冰箱(这是个梗)(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。

说完这个,我们再来说一说上面的缺陷(原谅我废话这么多吧OAQ)
从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。
ajax轮询 需要服务器有很快的处理速度和资源。(速度)
long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
所以ajax轮询 和long poll 都有可能发生这种情况。

客户端:啦啦啦啦,有新信息么?
服务端:月线正忙,请稍后再试(503 Server Unavailable)
客户端:。。。。好吧,啦啦啦,有新信息么?
服务端:月线正忙,请稍后再试(503 Server Unavailable)

客户端:

然后服务端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。(我错了。。这又是梗。。)

————————–
言归正传,我们来说Websocket吧
通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。
一种需要更快的速度,一种需要更多的'电话'。这两种都会导致'电话'的需求越来越高。
哦对了,忘记说了HTTP还是一个无状态协议。(感谢评论区的各位指出OAQ)
通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。

所以在这种情况下出现了,Websocket出现了。
他解决了HTTP的这几个难题。
首先,被动性,当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。
所以上面的情景可以做如下修改。
客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
服务端:balabalabalabala
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈

就变成了这样,只需要经过一次HTTP请求,就可以做到源源不断的信息传送了。(在程序设计中,这种设计叫做回调,即:你有信息了再来通知我,而不是我傻乎乎的每次跑来问你)
这样的协议解决了上面同步有延迟,而且还非常消耗资源的这种情况。
那么为什么他会解决服务器上消耗资源的问题呢?
其实我们所用的程序是要经过两层代理的,即HTTP协议在Nginx等服务器的解析下,然后再传送给相应的Handler(PHP等)来处理。
简单地说,我们有一个非常快速的接线员(Nginx),他负责把问题转交给相应的客服(Handler)
本身接线员基本上速度是足够的,但是每次都卡在客服(Handler)了,老有客服处理速度太慢。,导致客服不够。
Websocket就解决了这样一个难题,建立后,可以直接跟接线员建立持久连接,有信息的时候客服想办法通知接线员,然后接线员在统一转交给客户。
这样就可以解决客服处理速度过慢的问题了。

同时,在传统的方式上,要不断的建立,关闭HTTP协议,由于HTTP是非状态性的,每次都要重新传输identity info(鉴别信息),来告诉服务端你是谁。
虽然接线员很快速,但是每次都要听这么一堆,效率也会有所下降的,同时还得不断把这些信息转交给客服,不但浪费客服的处理时间,而且还会在网路传输中消耗过多的流量/时间。
但是Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求,这样就解决了接线员要反复解析HTTP协议,还要查看identity info的信息。
同时由客户主动询问,转换为服务器(推送)有信息的时候就发送(当然客户端还是等主动发送信息过来的。。),没有信息的时候就交给接线员(Nginx),不需要占用本身速度就慢的客服(Handler)
——————–
至于怎么在不支持Websocket的客户端上使用Websocket。。答案是:不能
但是可以通过上面说的 long poll 和 ajax 轮询来 模拟出类似的效果

laravel5集成angular2

这个问题我之前一直在思考

学习angular的过程中都是把angular视为一个完整的前端存在

也就是脱离了后端的独立存在

在代码里不去插入动态语言

在前端完成数据的存储,通信,绑定,修改

与后端可以用http协议交互,像app一样只做展示的前端操作

angular2通过ng server创建http服务

我想要把angular2结合laravel5一起使用

开始我觉得是不是angular2就像上面说的那样还是通过ng server创建服务

运行在4200端口 然后nginx反向代理到4200端口访问angular

然后angular通过http协议与laravel后端通信

google了之后发现使用angular2的开发模式(development mode)可以直接使用

而之前ng server只是单独为了让程序跑起来提供的一个服务

如今又laravel了 就可以不用创建那个服务 直接在这个上面跑

哎 还是基础不好思维有限 慢慢补吧

让我们开始吧!首先要安装基础laravel应用

image.png

我们需要获取angular2和typescript源码

在新创建的文件夹(larangular)中可以看到package.json文件

修改

{
  "private": true,
  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "axios": "^0.15.3",
    "bootstrap-sass": "^3.3.7",
    "cross-env": "^3.2.3",
    "jquery": "^3.1.1",
    "laravel-mix": "0.*",
    "lodash": "^4.17.4",
    "vue": "^2.1.10",
    "concurrently": "^1.0.0",
    "del": "^2.2.0",
    "gulp": "^3.8.8"
  },
  "dependencies": {
    "angular2": "2.0.0-beta.0",
    "bootstrap-sass": "^3.0.0",
    "elixir-typescript": "^1.1.2",
    "es6-promise": "^3.0.2",
    "es6-shim": "^0.33.3",
    "laravel-elixir": "^4.0.0",
    "reflect-metadata": "0.1.2",
    "rxjs": "5.0.0-beta.0",
    "systemjs": "0.19.6",
    "zone.js": "0.5.10"
  }
}

现在执行npm install,所需要的文件将被下载到node_modules文件夹

image.png

完成之后,我们要添加Typescript在laravel项目的resouce/assets目录

在这个文件夹里我们要创建2个文件app.component.ts 和 boot.ts

import {Component} from 'angular2/core';
 
@Component({
    selector: 'my-app',
    template: '<h1>My First Angular 2 App</h1>'
})
export class AppComponent { }
import {bootstrap}    from 'angular2/platform/browser'
import {AppComponent} from './app.component'
 
bootstrap(AppComponent);

下面我们需要编写一个简单的Elixir task来完成Typescript

在主文件夹中创建gulpfile.js

var elixir = require('laravel-elixir');
var elixirTypscript = require('elixir-typescript');

/*
 |--------------------------------------------------------------------------
 | Elixir Asset Management
 |--------------------------------------------------------------------------
 |
 | Elixir provides a clean, fluent API for defining some basic Gulp tasks
 | for your Laravel application. By default, we are compiling the Sass
 | file for our application, as well as publishing vendor resources.
 |
 */

elixir(function(mix) {
    mix.sass('app.scss');

    mix.copy('node_modules/angular2', 'public/angular2');
    mix.copy('node_modules/rxjs', 'public/rxjs');
    mix.copy('node_modules/systemjs', 'public/systemjs');
    mix.copy('node_modules/es6-promise', 'public/es6-promise');
    mix.copy('node_modules/es6-shim', 'public/es6-shim');
    mix.copy('node_modules/zone.js', 'public/zone.js');

    mix.typescript('app.js','public/','/**/*.ts',{
        "target": "ES5",
        "module": "system",
        "moduleResolution": "node",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false,
    });

});

然后安装gulp

image.png

修改如下文件 node_modules/elixir_typescript/index.js 

new Task(pluginName, function () {
       var tsResult = gulp.src(assetPath + search)
           .pipe(ts(options, undefined, _laravelReporter.ElixirMessage()));
       return tsResult
           //  .pipe(concat(outputFileName))
             .pipe(gulp.dest(outputFolder));


   })

如果不修改会出现下面的错误

image.png

现在在程序根目录下执行gulp

image.png

全部执行完成

修改views

<!doctype html>
<html lang="{{ config('app.locale') }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css?family=Raleway:100,600" rel="stylesheet" type="text/css">

        <!-- Styles -->
        <style>
            html, body {
                background-color: #fff;
                color: #636b6f;
                font-family: 'Raleway', sans-serif;
                font-weight: 100;
                height: 100vh;
                margin: 0;
            }

            .full-height {
                height: 100vh;
            }

            .flex-center {
                align-items: center;
                display: flex;
                justify-content: center;
            }

            .position-ref {
                position: relative;
            }

            .top-right {
                position: absolute;
                right: 10px;
                top: 18px;
            }

            .content {
                text-align: center;
            }

            .title {
                font-size: 84px;
            }

            .links > a {
                color: #636b6f;
                padding: 0 25px;
                font-size: 12px;
                font-weight: 600;
                letter-spacing: .1rem;
                text-decoration: none;
                text-transform: uppercase;
            }

            .m-b-md {
                margin-bottom: 30px;
            }
        </style>

        <script src="es6-shim/es6-shim.min.js"></script>
        <script src="systemjs/dist/system-polyfills.js"></script>
         
        <script src="angular2/bundles/angular2-polyfills.js"></script>
        <script src="systemjs/dist/system.src.js"></script>
        <script src="rxjs/bundles/Rx.js"></script>
        <script src="angular2/bundles/angular2.dev.js"></script>

        <script>
            System.config({
              "defaultJSExtensions": true,
              packages: {
                app: {
                  format: 'register',
                  defaultExtension: 'js'
                }
              }
            });
         
         
            System.import('typescript/boot')
                  .then(null, console.error.bind(console));
          </script>
    </head>
    <body>
        <div class="flex-center position-ref full-height">
            @if (Route::has('login'))
                <div class="top-right links">
                    @if (Auth::check())
                        <a href="{{ url('/home') }}">Home</a>
                    @else
                        <a href="{{ url('/login') }}">Login</a>
                        <a href="{{ url('/register') }}">Register</a>
                    @endif
                </div>
            @endif

            <div>
                <div class="title m-b-md">
                    Laravel
                </div>
                <my-app>Loading...</my-app>

                <div>
                    <a href="https://laravel.com/docs">Documentation</a>
                    <a href="https://laracasts.com">Laracasts</a>
                    <a href="https://laravel-news.com">News</a>
                    <a href="https://forge.laravel.com">Forge</a>
                    <a href="https://github.com/laravel/laravel">GitHub</a>
                </div>
            </div>
        </div>
    </body>
</html>

成功了!!!

image.png

我把它放在了这里

https://git.oschina.net/iamleokim/larangular.git

ng2-admin分析

ng2-admin\src\index.html

通过<app></app>作为容器

<body>
  <app>
  </app>
  <div id="preloader">
    <div></div>
  </div>
</body>

ng2-admin\src\app\app.routing.ts

可以看到这里用的是HashLocationStrategy(路由策略)

export const routes: Routes = [
  { path: '', redirectTo: 'pages', pathMatch: 'full' },
  { path: '**', redirectTo: 'pages/dashboard' }
];

export const routing: ModuleWithProviders = RouterModule.forRoot(routes, { useHash: true });

ng2-admin\src\app\app.component.ts

Conponent 的 selector选择‘app’

template 里写了外层div class=“addtional-bg”

设置子路由 router-outlet

@Component({
  selector: 'app',
  styleUrls: ['./app.component.scss'],
  template: `
    <main [class.menu-collapsed]="isMenuCollapsed" baThemeRun>
      <div class="additional-bg"></div>
      <router-outlet></router-outlet>
    </main>
  `
})

ng2-admin\src\app\pages\pages.routing.ts

export const routes: Routes = [
  {
    path: 'login',
    loadChildren: 'app/pages/login/login.module#LoginModule'
  },
  {
    path: 'register',
    loadChildren: 'app/pages/register/register.module#RegisterModule'
  },
  {
    path: 'pages',
    component: Pages,
    children: [
      { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
      { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule' },
      { path: 'editors', loadChildren: './editors/editors.module#EditorsModule' },
      { path: 'components', loadChildren: './components/components.module#ComponentsModule' },
      { path: 'charts', loadChildren: './charts/charts.module#ChartsModule' },
      { path: 'ui', loadChildren: './ui/ui.module#UiModule' },
      { path: 'forms', loadChildren: './forms/forms.module#FormsModule' },
      { path: 'tables', loadChildren: './tables/tables.module#TablesModule' },
      { path: 'maps', loadChildren: './maps/maps.module#MapsModule' }
    ]
  }
];

npm-config

npm config set <key> <value> [-g|--global]
npm config get <key>
npm config delete <key>
npm config list
npm config edit
npm get <key>
npm set <key> <value> [-g|--global]

file:///C:/software/nodejs/node_modules/npm/html/doc/cli/npm-config.html

Angular

Angular的八大件。Angular主要有8个东西构成,包括:

  • 模块 (module)

  • 组件 (component)

  • 模板 (template)

  • 元数据 (metadata)

  • 数据绑定 (data binding)

  • 指令 (directive)

  • 服务 (service)

  • 依赖注入 (dependency injection)

这八大件就好比孙悟空身上的毛,拔下来一吹,能变出各种东西来。所以啊,只要你吃透了这老北京八大件,那做一个客户端应用简直就是分分钟的事儿。

模板基本定义了你的应用长啥样,长得好不好看得有人看着,组件就负责管理这些模板。长得好看没用那不就成了花瓶了么,所以还需要服务来给应用添加一些逻辑,这些东西东一块西一块的乱七八糟还真不行,我们还需要模块来打包所有的组件、服务等等。所以说,模块就是用来打包的。一个应用程序有很多模块打的包,一层一层的,最开始那个就叫做根模块。我们通过引诱(其实是引导吧)根模块来启动程序。

我偷偷从官网偷了一张照片来给大家感受一下这八大件是怎么互相配合的。

  

那下面就浅显地介绍一下这八大件。

模块 (Module)

刚才提到了根模块,是用来启动程序的。没有根模块,再好的程序也跑不起来。所以模块很重要很重要。Angular的应用,都是模块化的。每个feature特性都有自己的子模块,无论是子模块还是根模块定义的结构都差不多。我们可以把模块看成一个行李箱,里面装满了各种东西。包括declaration、imports、providers等。declaration用来放各种小零件,包括组件、指令、管道等。imports是导入其他的模块。providers用来盛放各种service。for一个example,一个module大概长这个样子。关于Module的细节,下一篇Angular的文章我会详细说哒。

  

组件 (Component) 元数据 (Metadata) 模板 (Template)

这仨可以一起说。

模板就是HTML,就是你告诉Angular,喂喂喂,这儿你给我加个按钮,那儿你给我来个日历就是这样。除了<h1> <p>等等这些常见的html元素,我们还可以加自定义的元素在模板里面。

组件负责让模板看起来没那么无聊。比如你在模板里面定义了一个按钮,那你按了之后发生了什么啊?或者你定义了一个input box,你输入你的名字后会发生什么啊?这就需要Component来定义。

那每个Component都不一样,我们怎么区分呢?其实应该说Angular怎么识别呢?就是靠这个叫做元数据的东西。元数据会定义一些配置细节,告诉Angular:嘿Angular,如果你想把这个东西给大家看,那你得先去做ABCDEFG这几件事儿。Angular按照元数据的指示,根据Template的样子最后创建视图。

数据绑定 (Data Binding)

如果没有任何框架,我们自己写数据绑定,那简直就是人间地狱啊,噩梦啊。之前我们可怜的前端程序员都是用jQuery,我们要纯手工打造各种数据更新,UI更新,经常犯错,bug一堆堆,debug简直就是家常便饭。后来越来越多的框架都有数据绑定了,无论是单向的双向的还是五花板门乱七八糟项的,都帮我们可怜的前端程序员省去了很多时间。这样我们就可以早早下班回家老婆孩子热炕头了。

Angular2比Angular1更好滴实现了数据绑定。一共有四种吧:

  1. {{someString}}这个叫插值绑定,从Component到DOM,之前在Angular1里面也有

  2. [someThing] 这个叫属性绑定,也是从Component到DOM

  3. (event)这个是事件绑定,从DOM到Component

  4. 最后一个就是把上面这两个结合在一起[(ng…)] 双向绑定

  

除了这四种现在在各大框架中都十分普遍的绑定方式外,Angular2里面还有一种爸爸对儿子的@input绑定和儿子对爸爸的@output绑定。这种方式让爸爸跟儿子毫无代沟,交流起来丝毫不费力。

指令 (directive)

指令跟组件(Component)非常类似,其实组件就相当于一个指令。指令地位崇高,包括结构型指令和属性指令。

结构型我们在Angular1里面也经常用啊,就是现在语法不太一样了,比如ngfor, ngIf等等,都是结构型指令。这些一般是不会改变任何元素的行为啦,外观啦等等。相反,属性型就非要改外观或者行为。

服务(Service)

以前呐,总听说中国服务业不发达,我回家一看,这挺发达的啊,到处灯红酒绿的。人想活的好啊,还得靠服务业,不是大保健那种服务啦。

  

应用程序中的服务无处不在无孔不入,哪儿都有它。上面介绍的组件里面有一些变量,有些用户输入的变量啦,用户的选择啦,这些数据都乖乖地待在前端呢,不直接跟服务器对话。要不你用户爱输入什么输入什么,爱上传什么上传什么,服务器不就炸裂了么?组件要与服务(service)直接沟通。这样服务里面就可以加一些验证逻辑,比如你想下载小黄片什么的,服务可以阻止你(其实没有这种服务)。服务就像大宅子里面的大管家,所有的琐事儿都是服务来管。大管家希望每一个组件尽量精简,其余的细节内容都交给服务托管。那组件中如何使用这些服务呢?就靠下面要介绍的依赖注入。

依赖注入(Dependency Injection)

Angular通过依赖注入让组件可以使用服务。Angular创建一个组件的时候,首先你得跟上级领导请求,喂,领导啊,我这个组件需要这些服务,领导说,那好吧,那给你个注射器(injector)你用去吧。就像这样:

constructor( private service: MyService)

这个注射器啊会存放以前创建的那个服务,上面这行代码就是用service来存放之前创建好的MyService。那有些同学就要问了,你啥时候创建MyService的,我咋不知道?哦对,那如果这个服务还没有被创建的话,聪明的注射器同学就会先创建一个MyService服务实例,然后把这个服务跟Angular讲。这就是依赖注入啦。

  

iframe 内部刷新外部界面

今天在做 erp系统的时候 用layer 调用的iframe 想在内部操作完之后 让外部列表刷新

以为直接用js的刷新页面就可以 结果刷新之后只是刷新iframe里面的页面

研究了一下 其实只要用parent. 就可以调用外部的function 

这样直接在外部写好刷新function 然后再内部调用就可以了

在IE6,7,8 parseInt()前导为”0″ 转换值不准确

parseInt()有两种模式,通常我们使用普通模式,ECMAScript规范指出,如果一个字符串以&ldquo;0&rdquo;开头(而不是&ldquo;0x&rdquo;或&ldquo;0X&rdquo;开头),parseInt()可能把它解释为一个八进制数或者十进制数。由于这一行为不确定,在IE6,7,8和较低版本的火狐、Opera、Safari、Chrome中,会以八进制方式进行转换,所以造就了这个奇葩的结果。parseInt() 方法还有基模式,可以把二进制、八进制、十六进制或其他任何进制的字符串转换成整数。基是由 parseInt() 方法的第二个参数指定的,所以要解析十六进制的值,需如下调用 parseInt() 方法:

 

 

小结:parseInt 函数的完整形式是:parseInt(string, radix),第二个参数是所谓的基数,设置string的格式,常用的有2、8、10、16,表示string是多少进制的数。强烈建议在使用parseInt()解析字符串时指定基数,多数情况下接受用户的输入很有可能得到&ldquo;050&rdquo;类似值,为避免不必要的逻辑错误,所以务必要指定基数。例如: parseInt(&ldquo;050&Prime;, 10); //这里指定被解析数字的基数是十进制。避免了各种浏览器解析不一致的问题。