iOS原生app中集成react-native组件

在iOS原生app中增加ReactNative

  1. 新建一个iOS的项目,打开XCode,新建一个项目,选择Tabbed App

  2. 运行一下看看效果

  3. 新建一个react-native项目,和iOS的项目中同一个目录下

    brew install node
    brew install watchman
    npm install -g react-native-cli
    react-native init ReactNativeProject
    
  4. 启动react-native服务器
    react-native start
    
  5. 安装CocoaPods
    brew install cocoapods
    
  6. 初始化CocoaPods,在你的iOS项目根目录里运行
    pod init
    
  7. 编辑生成的Podfile,增加react-native的pod
    # Uncomment the next line to define a global platform for your project
    # platform :ios, '9.0'
    target 'demo' do
    # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
    use_frameworks!
    # Pods for demo
    pod 'React', :path => ‘../ReactNativeProject/node_modules/react-native', :subspecs => [
    'Core',
    'CxxBridge', # Include this for RN >= 0.47
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
    'RCTText',
    'RCTNetwork',
    'RCTWebSocket', # needed for debugging
    # Add any other subspecs you want to use in your project
    ]
    pod "yoga", :path => "../ReactNativeProject/node_modules/react-native/ReactCommon/yoga"
    pod 'DoubleConversion', :podspec => '../ReactNativeProject/node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
    pod 'GLog', :podspec => '../ReactNativeProject/node_modules/react-native/third-party-podspecs/GLog.podspec'
    pod 'Folly', :podspec => '../ReactNativeProject/node_modules/react-native/third-party-podspecs/Folly.podspec'
    target 'demoTests' do
    inherit! :search_paths
    # Pods for testing
    end
    target 'demoUITests' do
    inherit! :search_paths
    # Pods for testing
    end
    end
    
  8. 运行pod install安装依赖,得到以下输出:
    $ pod install
    Setting up CocoaPods master repo
    $ /usr/bin/git clone https://github.com/CocoaPods/Specs.git master --progress
    Cloning into 'master'...
    remote: Counting objects: 1799117, done.
    remote: Compressing objects: 100% (377/377), done.
    remote: Total 1799117 (delta 157), reused 35 (delta 35), pack-reused 1798692
    Receiving objects: 100% (1799117/1799117), 500.73 MiB | 320.00 KiB/s, done.
    Resolving deltas: 100% (981561/981561), done.
    Checking out files: 100% (203691/203691), done.
    Setup completed
    Analyzing dependencies
    Fetching podspec for `DoubleConversion` from `../ReactNativeProject/node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`
    Fetching podspec for `Folly` from `../ReactNativeProject/node_modules/react-native/third-party-podspecs/Folly.podspec`
    Fetching podspec for `GLog` from `../ReactNativeProject/node_modules/react-native/third-party-podspecs/GLog.podspec`
    Fetching podspec for `React` from `../ReactNativeProject/node_modules/react-native`
    Fetching podspec for `yoga` from `../ReactNativeProject/node_modules/react-native/ReactCommon/yoga`
    Downloading dependencies
    Installing DoubleConversion (1.1.5)
    Installing Folly (2016.09.26.00)
    Installing GLog (0.3.4)
    Installing React (0.51.0)
    Installing boost (1.59.0)
    Installing yoga (0.51.0.React)
    Generating Pods project
    Integrating client project
    
  9. 打开iOS项目目录下的demo.xcworkspace

  10. 在AppDelegate.swift文件中引入React

    import React
    
  11. 声明react-native组件的UIViewController并加入到tab中
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    var tab = self.window?.rootViewController as! UITabBarController
    let jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios")
    let rootView = RCTRootView(
    bundleURL: jsCodeLocation,
    moduleName: "ReactNativeProject",
    initialProperties: nil,
    launchOptions: nil
    )
    let vc = UIViewController()
    vc.view = rootView
    vc.title = "rn"
    tabbar.viewControllers?.append(vc)
    return true
    }
    
  12. 设置允许localhost的http访问

  13. 运行看看效果

代码: https://github.com/shengoo/iOS-rn-h5

使用Webpack loader打包css文件

Loaders

style-loader

style-loader adds CSS to the DOM by injecting a style tag.

css-loader

The css-loader interprets @import and url() like import/require() and will resolve them.

postcss-loader

Use it after css-loader and style-loader, but before other preprocessor loaders like e.g sass|less|stylus-loader, if you use any.

Autoprefixer

Autoprefixer is a service for managing vendor prefixes. It adds missing prefixes and deletes obsolete ones.

Extract CSS

Extract text from a bundle, or bundles, into a separate file.
Needed in production mode.

Configuration

Use in js:

import from './file.css'
import from './file.less'
import from './file.sass'

Development

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    'style-loader',
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
              plugins: [
                autoprefixer({
                  browsers: ['> 5%']
                })
              ]
            }
                    }
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
              plugins: [
                autoprefixer({
                  browsers: ['> 5%']
                })
              ]
            }
                    },
                    'less-loader'
                ]
            }
        ]
    }
}

Production

module.exports = {
    module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
                        {
              loader: 'css-loader',
              options: {
                importLoaders: 1,
                minimize: true,
                sourceMap: true,
              },
            },
            {
              loader: 'postcss-loader',
              options: {
                plugins: [
                  autoprefixer({
                    browsers: ['> 5%']
                  })
                ]
              }
            },
          ]
        })
      },
      {
        test: /\.less$/,
        use: ExtractTextPlugin.extract({
          fallback: 'style-loader',
          use: [
                        {
              loader: 'css-loader',
              options: {
                importLoaders: 1,
                minimize: true,
                sourceMap: true,
              },
            },
            {
              loader: 'postcss-loader',
              options: {
                plugins: [
                  autoprefixer({
                    browsers: ['> 5%']
                  })
                ]
              }
            },
            'less-loader']
        })
      },
    ]
  },
  plugins: [
    // Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
    new ExtractTextPlugin({filename: '[name].[hash:8].css'})
  ],
}

Mac开发环境配置

Mac开发配置

System Preferences

 > 关于本机 > 软件更新

触控板

  •  > 系统偏好设置 > 触控板
    • 光标与点击
      • ✓ 查询与数据监测器 > 用一个手指用力点按
      • ✓ 辅助点按 > 用两个手指点按或轻点
      • ✓ 轻拍来点按
      • ✓ 静默点按(声音变小)
      • ✓ 用力点按和触觉反馈
    • 滚动缩放
      • ✓ 默认全选
    • 更多手势
      • ✓ 默认全选
  •  > 系统偏好设置 > 辅助功能
    启用三指拖移

  •  > 系统偏好设置 > 键盘 > 快捷键
    打开所有控制

Dock

  •  > 系统偏好设置 > Dock
    • ✓ 将窗口最最小化为应用程序图标
    • ✓ 自动显示和隐藏 Dock

Finder

  • Finder > 显示

    • 显示标签页栏
    • 显示路径栏
    • 显示状态栏
  • Finder > 偏好设置
    • 通用
      • 开启新 Finder 窗口时打开:HOME「用户名」目录
    • 边栏
      • 添加 HOME「用户名」目录 和 创建代码文件目录
      • 将 共享的(shared) 和 标记(tags) 目录去掉

Show/Hide Hidden Files the Long Way

  1. Open Terminal found in Finder > Applications > Utilities
  2. In Terminal, paste the following: defaults write com.apple.finder AppleShowAllFiles YES
  3. Press return
  4. Hold the ‘Option/alt’ key, then right click on the Finder icon in the dock and click Relaunch.

quicklook plugins

// run after brew cask installed
brew cask install qlcolorcode
brew cask install qlstephen
brew cask install qlmarkdown
brew cask install quicklook-json
brew cask install qlprettypatch
brew cask install quicklook-csv
brew cask install betterzipql
brew cask install webpquicklook
brew cask install suspicious-package

Scroll Reverser

当你在浏览一个很长的网页时,你看完了当前显示的内容,想要看后续的内容,你可以在 Trackpad 上双指上滑,或者鼠标滚轮向上滚动。这是被称作“自然”的滚动方向。
然而在 Windows 里鼠标滚动的行为是相反的:鼠标滚轮向下滚动才会让浏览器显示后续的内容,向上滚动会达到页面的顶部。你可以在 OS X 的系统偏好设置里修改(选择System Preferences >Trackpad,在Scroll & Zoom标签页中不选中Scroll direction: natural),但是这样会同时改变鼠标滚轮的方向和 Trackpad 的方向。
要想只改变鼠标滚轮的方向,而保持 Trackpad 依旧是“自然”的,我们需要 Scroll Reverser:
brew cask install scroll-reverser

vim配置

vi ~/.vimrc

color desert "颜色设置
syntax on "语法高亮
set number "自动显示行号
set cursorline "突出显示当前行
set ts=4 "设置tab长度为4
set shiftwidth=4 "设定 << 和 >> 命令移动时的宽度为 4

Homebrew

  • Homebrew : package manager for macOS
    包管理工具,官方称之为The missing package manager for OS X。
    安装步骤:先打开 Terminal 应用,输入:
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
    有了 brew 以后,要下载工具,比如 MySQL、Gradle、Maven、Node.js 等工具,就不需要去网上下载了,只要一行命令就能搞定:
    brew install mysql gradle maven node

  • Homebrew-Cask
    brew-cask 允许你使用命令行安装 OS X 应用。比如你可以这样安装 Chrome:brew cask install google-chrome。还有 Evernote、Skype、Sublime Text、VirtualBox 等都可以用 brew-cask 安装。
    安装:
    brew tap caskroom/cask

iterm2

iTerm2 是最常用的终端应用,是 Terminal 应用的替代品。提供了诸如Split Panes等一群实用特性。

安装:
brew cask install iterm2

Git

XCode会安装git,无需单独安装。

  • SourceTree
    SourceTree 是 Atlassian 公司出品的一款优秀的 Git 图形化客户端。如果你发现命令行无法满足你的要求,可以试试 SourceTree。
    安装:
    brew cask install sourcetree
    用 brew-cask 安装会自动增加命令行工具stree到$PATH里。在命令行中输入stree可以快速用 SourceTree 打开当前 Git 仓库。详细用法请参见stree –help。

Text Editors

  • Sublime Text
    brew cask install sublime-text

  • Visual Studio Code
    brew cask install visual-studio-code

Softwares

  • Browsers

    • Chrome
      brew cask install google-chrome

  • dropbox
    brew cask install dropbox

  • postman
    brew cask install postman

  • teamviewer
    brew cask install teamviewer

  • shadowsocksx-ng
    brew cask install shadowsocksx-ng

  • Alfred
    brew cask install alfred

  • BetterSnapTool
    https://itunes.apple.com/cn/app/bettersnaptool/id417375580?mt=12

  • Go2Shell
    https://itunes.apple.com/cn/app/go2shell/id445770608?mt=12
    brew cask install go2shell

  • Sip : Color picker
    brew cask install sip

  • Snip
    https://itunes.apple.com/cn/app/snip/id512505421?mt=12
    brew cask install snip

  • The Unarchiver
    https://itunes.apple.com/cn/app/the-unarchiver/id425424353?mt=12
    brew cask install the-unarchiver

NodeJS

brew install nodejs
NodeJS Packages

  • n : Node version management
    npm install -g n

  • change nodejs to LTS version

    mkdir /usr/local/n
    sudo mkdir /usr/local/n
    sudo chown -R $(whoami) /usr/local/n
    n lts
    

Java

  • install java 8 instead of java 9
    brew tap caskroom/versions
    brew cask install java8
    
  • Maven
    brew install maven

IDE

  • XCode
    https://itunes.apple.com/cn/app/xcode/id497799835?mt=12

    • CocoaPods
      sudo gem install cocoapods

  • Android Studio
    brew cask install android-studio

  • IntelliJ Idea
    brew cask install intellij-idea

  • WebStorm

    brew cask install webstorm

让很多Promise一个接一个进行

Promise是我们处理异步操作的时候经常用到的。
有的时候我们会需要顺序执行很多异步操作,如果操作比较少的时候还可以写成a.then(b).then(c)...,可是如果异步操作很多的时候就不能这样写了。
可以利用数组的reduce函数达到同样的效果,这个时候不管有多少异步操作都可以连在一起了。
代码如下:

function waitPromise(time) {
    console.log(time);
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('resolved');
        }, time);
    });
}
function log(data) {
    return new Promise((resolve, reject) => {
        console.log(data + '@' + (new Date().getSeconds()));
        resolve();
    });
}
var ps = [];
for (var i = 0; i < 3; i++) {
    let time = (i + 1) * 1000;
    ps.push(() => waitPromise(time));
    ps.push(log);
}
console.log('started' + '@' + (new Date().getSeconds()));
var p = Promise.resolve();
ps.reduce((p, c) => {
    return p.then(c)
}, p).then(() => {
    console.log('all finished');
}).catch(reject => {
    console.log('reject', reject)
});

输出结果:

started@59
1000
resolved@0
2000
resolved@2
3000
resolved@5
all finished

 

解决UC浏览器、微信浏览器使用display:flex;的兼容性问题

在android平台的uc浏览器和微信浏览器中使用display: flex;会出问题。
使用display: flex;的时候需要加上display: -webkit-box;
使用flex: 1;的时候要加上:

-webkit-box-flex: 1;
-moz-box-flex: 1;
-ms-flex: 1;

使用align-items: center;的时候需要加上:-webkit-box-align: center;
使用flex-direction: column;的时候需要加上:

-webkit-box-orient: vertical;
-moz-box-orient: vertical;
box-orient: vertical;

这里有个demo大家可以看一下
请点击:测试页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
  <title>Demo for flex on uc</title>
  <style type="text/css">
  html,body{
    padding: 0;
    margin: 0;
  }
  .demo1{
    background-color: yellow;
    text-align: center;
    height: 80px;
    display: -webkit-flex;
    display: flex;
    -webkit-align-items: center;
    align-items: center;
    /* for uc */
    display: -webkit-box;
    -webkit-box-align: center;
  }
  .demo1>div{
    background-color: green;
    margin: 2px;
    -webkit-flex: 1;
    flex: 1;
    /* for uc */
    -webkit-box-flex: 1;
    -moz-box-flex: 1;
    -ms-flex: 1;
  }
  .demo2{
    background-color: yellow;
    width: 80px;
    height: 200px;
    display: -webkit-flex;
    display: flex;
    -webkit-flex-direction: column;
    flex-direction: column;
    -webkit-align-items: center;
    align-items: center;
    /* for uc */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -moz-box-orient: vertical;
    box-orient: vertical;
    -webkit-box-align: center;
  }
  .demo2>div{
    background-color: green;
    width: 40px;
    margin: 2px;
    -webkit-flex: 1;
    flex: 1;
    /* for uc */
    -webkit-box-flex: 1;
    -moz-box-flex: 1;
    -ms-flex: 1;
  }
  </style>
</head>
<body>
<h2>左右排列,上下居中</h2>
<div class="demo1">
  <div>flex</div>
  <div>flex</div>
  <div>flex</div>
  <div>flex</div>
  <div>flex</div>
</div>
<h2>上下排列,左右居中</h2>
<div class="demo2">
  <div>flex</div>
  <div>flex</div>
  <div>flex</div>
  <div>flex</div>
  <div>flex</div>
</div>
</body>
</html>

CSS中Transform和Transition的区别

Transform

Transform意思是变形,指的是变成什么样子,虽然经常在动画里用到这个属性,但是Transform本事是静态的,跟动画没什么关系。

Transition

Transition意思是过渡,指的是怎么变,这个是可以做到动画的。

Transition配合一般属性

div
{
width:100px;
height:100px;
background:blue;
transition:width 2s;
-moz-transition:width 2s; /* Firefox 4 */
-webkit-transition:width 2s; /* Safari and Chrome */
-o-transition:width 2s; /* Opera */
}
div:hover
{
width:300px;
}





请把鼠标指针移动到蓝色的 div 元素上,就可以看到过渡效果。
注释:本例在 Internet Explorer 中无效。


性能优化之:HTTP 缓存

通过网络获取内容既缓慢,成本又高:大的响应需要在客户端和服务器之间进行多次往返通信,这拖延了浏览器可以使用和处理内容的时间,同时也增加了访问者的数据成本。因此,缓存和重用以前获取的资源的能力成为优化性能很关键的一个方面。
好消息是每个浏览器都实现了 HTTP 缓存! 我们所要做的就是,确保每个服务器响应都提供正确的 HTTP 头指令,以指导浏览器何时可以缓存响应以及可以缓存多久。

如果在应用中使用 Webview 来获取和显示网页内容,可能需要提供额外的配置标志,以确保启用了 HTTP 缓存,并根据用途设置了合理的缓存大小,同时,确保缓存持久化。查看平台文档并确认您的设置!

http-request
服务器在返回响应时,还会发出一组 HTTP 头,用来描述内容类型、长度、缓存指令、验证令牌等。例如,在上图的交互中,服务器返回了一个 1024 字节的响应,指导客户端缓存响应长达 120 秒,并提供验证令牌(x234dff),在响应过期之后,可以用来验证资源是否被修改。

Expires和Cache-Control: max-age标头

这些标头用于指定相应时间段,浏览器可在指定的这段时间内使用已缓存的资源,而无需查看网络服务器是否提供了新版资源。这些缓存标头功能强大,没有任何应用条件限制。在设置这些标头并下载资源后,浏览器不会为资源发出任何GET请求,除非过期日期到期或达到时间最大值,亦或是用户清除了缓存。

Last-Modifed和ETag标头

这些标头可用于指定浏览器应如何确定用于缓存的文件是否相同。在Last-Modified标头中指定的是日期,而在ETag标头中指定的则可以是唯一标识资源的任意值(通常为文件版本或内容哈希值)。Last-Modified是功能“较弱”的缓存标头,因为浏览器会使用试探法来确定是否需要从缓存中抓取内容。
借助这些标头,浏览器可以通过在用户明确重新加载页面时发出条件式GET请求,有效地更新其已缓存资源。除非您在服务器端更改资源,否则条件式GET请求不会返回完整的响应,因此相较于完整GET请求,此类请求的延迟较小。

我应使用哪个缓存标头?

对于所有可缓存资源,指定一个ExpiresCache-Control max-age以及一个Last-ModifiedETag至关重要。您没必要同时指定ExpiresCache-Control: max-age;或同时指定Last-ModifiedETag

使用 ETag 验证缓存的响应

  • 服务器通过 ETag HTTP 头传递验证令牌
  • 通过验证令牌可以进行高效的资源更新检查:如果资源未更改,则不会传输任何数据。

让我们假设在首次获取资源 120 秒之后,浏览器又对该资源发起了新请求。首先,浏览器会检查本地缓存并找到之前的响应,不幸的是,这个响应现在已经’过期’,无法在使用。此时,浏览器也可以直接发出新请求,获取新的完整响应,但是这样做效率较低,因为如果资源未被更改过,我们就没有理由再去下载与缓存中已有的完全相同的字节。
这就是 ETag 头中指定的验证令牌所要解决的问题:服务器会生成并返回一个随机令牌,通常是文件内容的哈希值或者某个其他指纹码。客户端不必了解指纹码是如何生成的,只需要在下一个请求中将其发送给服务器:如果指纹码仍然一致,说明资源未被修改,我们就可以跳过下载。
http-cache-control
在上面的例子中,客户端自动在If-None-MatchHTTP 请求头中提供 ETag 令牌,服务器针对当前的资源检查令牌,如果未被修改过,则返回304 Not Modified响应,告诉浏览器缓存中的响应未被修改过,可以再延用 120 秒。注意,我们不必再次下载响应 – 这节约了时间和带宽。
作为网络开发人员,您如何利用高效的重新验证? 浏览器代替我们完成了所有的工作:自动检测是否已指定了验证令牌,并会将验证令牌附加到发出的请求上,根据从服务器收到的响应,在必要时更新缓存时间戳。实际上,我们唯一要做的就是确保服务器提供必要的 ETag 令牌:查看服务器文档中是否有必要的配置标志。

提示:HTML5 Boilerplate 项目包含了所有最流行的服务器的配置文件样例,并且为每个配置标志和设置都提供了详细的注释:在列表中找到您喜欢的服务器,查找适合的设置,然后复制/确认您的服务器配置了推荐的设置。

Cache-Control

  • 每个资源都可以通过 Cache-Control HTTP 头来定义自己的缓存策略
  • Cache-Control 指令控制谁在什么条件下可以缓存响应以及可以缓存多久

最好的请求是不必与服务器进行通信的请求:通过响应的本地副本,我们可以避免所有的网络延迟以及数据传输的数据成本。为此,HTTP 规范允许服务器返回 一系列不同的 Cache-Control 指令,控制浏览器或者其他中继缓存如何缓存某个响应以及缓存多长时间。

Cache-Control 头在 HTTP/1.1 规范中定义,取代了之前用来定义响应缓存策略的头(例如 Expires)。当前的所有浏览器都支持 Cache-Control,因此,使用它就够了。

http-cache-control-highlight

no-cache 和 no-store

no-cache表示必须先与服务器确认返回的响应是否被更改,然后才能使用该响应来满足后续对同一个网址的请求。因此,如果存在合适的验证令牌 (ETag),no-cache 会发起往返通信来验证缓存的响应,如果资源未被更改,可以避免下载。
相比之下,no-store更加简单,直接禁止浏览器和所有中继缓存存储返回的任何版本的响应 – 例如:一个包含个人隐私数据或银行数据的响应。每次用户请求该资源时,都会向服务器发送一个请求,每次都会下载完整的响应。

public和 private

如果响应被标记为public,即使有关联的 HTTP 认证,甚至响应状态码无法正常缓存,响应也可以被缓存。大多数情况下,public不是必须的,因为明确的缓存信息(例如max-age)已表示 响应可以被缓存。
相比之下,浏览器可以缓存private响应,但是通常只为单个用户缓存,因此,不允许任何中继缓存对其进行缓存 – 例如,用户浏览器可以缓存包含用户私人信息的 HTML 网页,但是 CDN 不能缓存。

max-age

该指令指定从当前请求开始,允许获取的响应被重用的最长时间(单位为秒) – 例如:max-age=60表示响应可以再缓存和重用 60 秒。

定义最优 Cache-Control 策略

http-cache-decision-tree
按照上面的决策树来确定您的应用使用的特定资源或一组资源的最优缓存策略。理想情况下,目标应该是在客户端上缓存尽可能多的响应、缓存尽可能长的时间,并且为每个响应提供验证令牌,以便进行高效的重新验证。

Cache-Control 指令 说明
max-age=86400 浏览器和任何中继缓存均可以将响应(如果是`public`的)缓存长达一天(60 秒 x 60 分 x 24 小时)
private, max-age=600 客户端浏览器只能将响应缓存最长 10 分钟(60 秒 x 10 分)
no-store 不允许缓存响应,每个请求必须获取完整的响应。

根据 HTTP Archive,在排名最高的 300,000 个网站中(Alexa 排名),所有下载的响应中,几乎有半数可以由浏览器进行缓存,对于重复性网页浏览和访问来说,这是一个巨大的节省! 当然,这并不意味着特定的应用会有 50% 的资源可以被缓存:有些网站可以缓存 90% 以上的资源, 而有些网站有许多私密的或者时间要求苛刻的数据,根本无法被缓存。
审查您的网页,确定哪些资源可以被缓存,并确保可以返回正确的 Cache-Control 和 ETag 头。

废弃和更新已缓存的响应

  • 在资源”过期”之前,将一直使用本地缓存的响应
  • 通过将文件内容指纹码嵌入网址,我们可以强制客户端更新到新版的响应
  • 为了获得最佳性能,每个应用需要定义自己的缓存层级

浏览器发出的所有 HTTP 请求会首先被路由到浏览器的缓存,以查看是否缓存了可以用于实现请求的有效响应。如果有匹配的响应,会直接从缓存中读取响应,这样就避免了网络延迟以及传输产生的数据成本。然而,如果我们希望更新或废弃已缓存的响应,该怎么办?
例如,假设我们已经告诉访问者某个 CSS 样式表缓存长达 24 小时 (max-age=86400),但是设计人员刚刚提交了一个更新,我们希望所有用户都能使用。我们该如何通知所有访问者缓存的 CSS 副本已过时,需要更新缓存? 这是一个欺骗性的问题 – 实际上,至少在不更改资源网址的情况下,我们做不到。
一旦浏览器缓存了响应,在过期以前,将一直使用缓存的版本,这是由 max-age 或者 expires 指定的,或者直到因为某些原因从缓存中删除,例如用户清除了浏览器缓存。因此,在构建网页时,不同的用户可能使用的是文件的不同版本;刚获取该资源的用户将使用新版本,而缓存过之前副本(但是依然有效)的用户将继续使用旧版本的响应。
所以,我们如何才能鱼和熊掌兼得:客户端缓存和快速更新? 很简单,在资源内容更改时,我们可以更改资源的网址,强制用户下载新响应。通常情况下,可以通过在文件名中嵌入文件的指纹码(或版本号)来实现 – 例如 style.x234dff.css。
http-cache-hierarchy
因为能够定义每个资源的缓存策略,所以,我们可以定义’缓存层级’,这样,不但可以控制每个响应的缓存时间,还可以控制访问者看到新版本的速度。例如,我们一起分析一下上面的例子:

  • HTML 被标记成no-cache,这意味着浏览器在每次请求时都会重新验证文档,如果内容更改,会获取最新版本。同时,在 HTML 标记中,我们在 CSS 和 JavaScript 资源的网址中嵌入指纹码:如果这些文件的内容更改,网页的 HTML 也会随之更改,并将下载 HTML 响应的新副本。
  • 允许浏览器和中继缓存(例如 CDN)缓存 CSS,过期时间设置为 1 年。注意,我们可以放心地使用 1 年的’远期过期’,因为我们在文件名中嵌入了文件指纹码:如果 CSS 更新,网址也会随之更改。
  • JavaScript 过期时间也设置为 1 年,但是被标记为 private,也许是因为包含了 CDN 不应缓存的一些用户私人数据。
  • 缓存图片时不包含版本或唯一指纹码,过期时间设置为 1 天。

组合使用 ETag、Cache-Control 和唯一网址,我们可以提供最佳的方案:较长的过期时间,控制可以缓存响应的位置,以及按需更新。

缓存检查表

不存在最佳的缓存策略。根据您的通信模式、提供的数据类型以及应用特定的数据更新要求,必须定义和配置每个资源最适合的设置以及整体的’缓存层级’。
在定义缓存策略时,要记住下列技巧和方法:

  1. 使用一致的网址:如果您在不同的网址上提供相同的内容,将会多次获取和存储该内容。提示:注意,网址区分大小写!
  2. 确保服务器提供验证令牌 (ETag):通过验证令牌,如果服务器上的资源未被更改,就不必传输相同的字节。
  3. 确定中继缓存可以缓存哪些资源:对所有用户的响应完全相同的资源很适合由 CDN 或其他中继缓存进行缓存。
  4. 确定每个资源的最优缓存周期:不同的资源可能有不同的更新要求。审查并确定每个资源适合的 max-age。
  5. 确定网站的最佳缓存层级:对 HTML 文档组合使用包含内容指纹码的资源网址以及短时间或 no-cache 的生命周期,可以控制客户端获取更新的速度。
  6. 搅动最小化:有些资源的更新比其他资源频繁。如果资源的特定部分(例如 JavaScript 函数或一组 CSS 样式)会经常更新,应考虑将其代码作为单独的文件提供。这样,每次获取更新时,剩余内容(例如不会频繁更新的库代码)可以从缓存中获取,确保下载的内容量最少。

jQuery.extend 和 jQuery.fn.extend 的区别

jQuery.extend

jQuery.extend可以扩展jQuery对象本身。
用来在jQuery命名空间上增加新函数,或者合并对象。
1. 增加方法:

$.extend({
  hello: function () {
    console.log(this)//function jQuery(selector, context)
  }
});
$.hello();
  1. 合并对象
var obj1 = {'name' : 'sheng00'};
var obj2 = {'sex' : 'Male'};
$.extend(obj1, obj2);
console.log(obj1)//Object {name: "sheng00", sex: "Male"}

jQuery.fn.extend

用来扩展 jQuery 元素集来提供新的方法(通常用来制作插件)

$.fn.extend({
  turn_red: function () {
    return this.each(function () {
      this.style.color = 'red'
    });
  }
});
$('.elements').turn_red(); // sets color to red

效果和jQuery.fn.turn_red=function(){}是一样的

.net rest api 下载文件代码

[HttpGet]
public HttpResponseMessage GetImage()
{
  //文件路径
  var path = HostingEnvironment.MapPath("~/App_Data/5.jpg");
  FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
  Image img = Image.FromStream(fileStream);
  MemoryStream ms = new MemoryStream();
  img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
  HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
  result.Content = new ByteArrayContent(ms.ToArray());
  result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpg");
  result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
  {
      FileName = "5.jpg"//文件名
  };
  return result;
}

通信协议——HTTP、TCP、UDP – 博客

TCP HTTP UDP:

都是通信协议,也就是通信时所遵守的规则,只有双方按照这个规则“说话”,对方才能理解或为之服务。
TCP HTTP UDP三者的关系:
TCP/IP是个协议组,可分为四个层次:网络接口层、网络层、传输层和应用层。
在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。
在传输层中有TCP协议与UDP协议。
在应用层有FTP、HTTP、TELNET、SMTP、DNS等协议。
因此,HTTP本身就是一个协议,是从Web服务器传输超文本到本地浏览器的传送协议。
socket:
这是为了实现以上的通信过程而建立成来的通信管道,其真实的代表是客户端和服务器端的一个通信进程,双方进程通过socket进行通信,而通信的规则采用指定的协议。socket只是一种连接模式,不是协议,tcp、udp,简单的说(虽然不准确)是两个最基本的协议,很多其它协议都是基于这两个协议如,http就是基于tcp的,.用socket可以创建tcp连接,也可以创建udp连接,这意味着,用socket可以创建任何协议的连接,因为其它协议都是基于此的。
下面我们主要来看一下和我们互联网生活密切相关的协议:HTTP
什么是Http协议
HTTP全称是HyperText Transfer Protocal,即:超文本传输协议,从1990年开始就在WWW上广泛应用,是现今在WWW上应用最多的协议, Http是应用层协议,当你上网浏览网页的时候,浏览器和Web服务器之间就会通过HTTP在Internet上进行数据的发送和接收。Http是一个基于请求/响应模式的、无状态的协议。即我们通常所说的Request/Response。

URL:
URL(Uniform Resource Locator) 地址用于描述一个网络上的资源, 基本格式如下
schema://host[:port#]/path/…/[?query-string][#anchor]
scheme 指定低层使用的协议(例如:http, https, ftp)
host HTTP服务器的IP地址或者域名
port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,必须指明,例如 http://www.cnblogs.com:8080/
path 访问资源的路径
query-string 发送给http服务器的数据
anchor- 锚
URL 的一个例子
http://www.mywebsite.com/sj/test/test.aspx?name=sviergn&x=true#stuff
Schema: http
host: www.mywebsite.com
path: /sj/test/test.aspx
Query String: name=sviergn&x=true
Anchor: stuff
HTTP的Request/Response:
先看Request 消息的结构, Request 消息分为3部分
第一部分叫Request line,
第二部分叫Request header,
第三部分是body. header和body之间有个空行,
结构如下图

第一行中的Method表示请求方法,比如”POST”,”GET”, Path-to-resoure表示请求的资源, Http/version-number 表示HTTP协议的版本号
当使用的是”GET” 方法的时候, body是为空的
比如我们打开博客园首页的request 如下
GET http://www.cnblogs.com/ HTTP/1.1
Host: www.cnblogs.com
抽象的东西,难以理解,老感觉是虚的, 所谓眼见为实, 实际见到的东西,我们才能理解和记忆。 我们今天用Fiddler,实际的看看Request和Response.
下面我们打开Fiddler 捕捉一个博客园登录的Request 然后分析下它的结构, 在Inspectors tab下以Raw的方式可以看到完整的Request的消息,
如下图

Accept
作用: 浏览器端可以接受的媒体类型,
例如: Accept: text/html 代表浏览器可以接受服务器回发的类型为 text/html 也就是我们常说的html文档,
如果服务器无法返回text/html类型的数据,服务器应该返回一个406错误(non acceptable)
通配符 * 代表任意类型
例如 Accept: / 代表浏览器可以处理所有类型,(一般浏览器发给服务器都是发这个)
Referer:
作用: 提供了Request的上下文信息的服务器,告诉服务器我是从哪个链接过来的,比如从我主页上链接到一个朋友那里,他的服务器就能够从HTTP Referer中统计出每天有多少用户点击我主页上的链接访问他的网站。
例如: Referer:http://translate.google.cn/?hl=zh-cn&tab=wT
Accept-Language
作用: 浏览器申明自己接收的语言。
语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等;
例如: Accept-Language: en-us
Content-Type
作用:
例如:Content-Type: application/x-www-form-urlencoded
Accept-Encoding:
作用: 浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate),(注意:这不是只字符编码);
例如: Accept-Encoding: gzip, deflate
User-Agent
作用:告诉HTTP服务器, 客户端使用的操作系统和浏览器的名称和版本.
我们上网登陆论坛的时候,往往会看到一些欢迎信息,其中列出了你的操作系统的名称和版本,你所使用的浏览器的名称和版本,这往往让很多人感到很神奇,实际上,服务器应用程序就是从User-Agent这个请求报头域中获取到这些信息User-Agent请求报头域允许客户端将它的操作系统、浏览器和其它属性告诉服务器。
例如: User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; CIBA; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; InfoPath.2; .NET4.0E)
Connection
例如: Connection: keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接
例如: Connection: close 代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭, 当客户端再次发送Request,需要重新建立TCP连接。
Content-Length
作用:发送给HTTP服务器数据的长度。
例如: Content-Length: 38
Host(发送请求时,该报头域是必需的)
作用: 请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的
例如: 我们在浏览器中输入:http://www.guet.edu.cn/index.html
浏览器发送的请求消息中,就会包含Host请求报头域,如下:
Host:http://www.guet.edu.cn
此处使用缺省端口号80,若指定了端口号,则变成:Host:指定端口号
Pragma
作用: 防止页面被缓存, 在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一样
Pargma只有一个用法, 例如: Pragma: no-cache
Cookie:
作用: 最重要的header, 将cookie的值发送给HTTP 服务器
Accept-Charset
作用:浏览器申明自己接收的字符集,这就是本文前面介绍的各种字符集和字符编码,如gb2312,utf-8(通常我们说Charset包括了相应的字符编码方案);
我们再看Response消息的结构, 和Request消息的结构基本一样。 同样也分为三部分
第一部分叫Response line,
第二部分叫Response header,
第三部分是body. header和body之间也有个空行,
结构如下图
HTTP/version-number表示HTTP协议的版本号, status-code 和message 请看下节[状态代码]的详细解释.
我们用Fiddler 捕捉一个博客园首页的Response然后分析下它的结构, 在Inspectors tab下以Raw的方式可以看到完整的Response的消息, 如下图
Cache-Control
作用: 这个是非常重要的规则。 这个用来指定Response-Request遵循的缓存机制。各个指令含义如下
Cache-Control:Public 可以被任何缓存所缓存()
Cache-Control:Private 内容只缓存到私有缓存中
Cache-Control:no-cache 所有内容都不会被缓存
还有其他的一些用法, 我没搞懂其中的意思, 请大家参考其他的资料
Content-Type
作用:WEB服务器告诉浏览器自己响应的对象的类型和字符集,
例如:
Content-Type: text/html; charset=utf-8
Content-Type:text/html;charset=GB2312
Content-Type: image/jpeg
Expires
作用: 浏览器会在指定过期时间内使用本地缓存
例如: Expires: Tue, 08 Feb 2022 11:35:14 GMT
Last-Modified:
作用: 用于指示资源的最后修改日期和时间。(实例请看上节的If-Modified-Since的实例)
例如: Last-Modified: Wed, 21 Dec 2011 09:09:10 GMT
Server:
作用:指明HTTP服务器的软件信息
例如:Server: Microsoft-IIS/7.5
X-AspNet-Version:
作用:如果网站是用ASP.NET开发的,这个header用来表示ASP.NET的版本
例如: X-AspNet-Version: 4.0.30319
X-Powered-By:
作用:表示网站是用什么技术开发的
例如: X-Powered-By: ASP.NET
Connection
例如: Connection: keep-alive 当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接
例如: Connection: close 代表一个Request完成后,客户端和服务器之间用于传输HTTP数据的TCP连接会关闭, 当客户端再次发送Request,需要重新建立TCP连接。
Content-Length
指明实体正文的长度,以字节方式存储的十进制数字来表示。在数据下行的过程中,Content-Length的方式要预先在服务器中缓存所有数据,然后所有数据再一股脑儿地发给客户端。
例如: Content-Length: 19847
Date
作用: 生成消息的具体时间和日期
例如: Date: Sat, 11 Feb 2012 11:35:14 GMT
HTTP协议之Get和Post
Http协议定义了很多与服务器交互的方法,最基本的有4种,分别是GET,POST,PUT,DELETE. 一个URL地址用于描述一个网络上的资源,而HTTP中的GET, POST, PUT, DELETE就对应着对这个资源的查,改,增,删4个操作。 我们最常见的就是GET和POST了。GET一般用于获取/查询资源信息,而POST一般用于更新资源信息.
我们看看GET和POST的区别

  1. GET提交的数据会放在URL之后,以?分割URL和传输数据,参数之间以&相连,如EditPosts.aspx?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的Body中.

  2. GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.

  3. GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值,也就是说Get是通过地址栏来传值,而Post是通过提交表单来传值。

  4. GET方式提交数据,会带来安全问题,比如一个登录页面,通过GET方式提交数据时,用户名和密码将出现在URL上,如果页面可以被缓存或者其他人可以访问这台机器,就可以从历史记录获得该用户的账号和密码.