博客

  • SharePoint 2013 Installation

    Windows Server Fabric 下载链接 http://go.microsoft.com/fwlink/?LinkId=235496

    这个下载链接是从安装报错日志里找出来的。如果是直接网上搜索的话,一般是搜索到WindowsServerAppFabric_x64_6.1.exe 这个安装的时候,输入MSDN上的参数会报错,无法识别。 所以,掉坑里的亲,使用上面的链接吧。

    另外,安装的过程如果遇到什么问题,还是仔细查看一下日志,比较好。PS:如果亲是联网安装的话,似乎这些都不是问题,因为安装程序可以自动下载。

    无外网安装SharePoint 2013,继续折腾

    官方安装说明:http://technet.microsoft.com/en-us/library/cc262485.aspx#section7

    • /AppFabric:<file> 从 <file> 安装 Windows Server AppFabric(AppFabric 必须用 /i CacheClient,CachingService,CacheAdmin /gac 选项来安装)

    PrerequisiteInstaller.exe /AppFabric:C:\Software\sp_require\WindowsServerAppFabricSetup_x64.exe

    找到:http://jimshu.blog.51cto.com/3171847/1355793  这个写得比较全面,不过没有把所有的文件打包,还得一个一个去下载,不够傻瓜

    —–全文转载过来

    一、启动安装向导

      插入 SharePoint 2013 安装光盘,自动显示运行向导。

      如果向导不能自动出现,请打开光盘的文件夹,运行根目录下的splash.hta文件。

    wKiom1Lu_XHyBmEOAALVUouVm6M475.jpg

      首先,运行“安装必备软件”。

    wKioL1Lu_Z_wINoTAAFIzNHINwU118.jpg

    二、在 Windows Server 2008 R2 安装必备软件

    1. 在线安装

      使用在线安装时,需要确保本机已经连接到Internet,安装向导将自动从Internet下载所需的产品和更新。

    wKiom1LvAe-hKml2AAM5oU2_yjg844.jpg

    wKioL1LvAcuQCvl8AANpExXwnkw098.jpg

    wKiom1LvAe-TwrtfAAEqQnCn9xw018.jpg

      某些产品和更新在安装完成之后需要重启操作系统。

    wKioL1LvAcvRRTPlAAMo3aPXjzs949.jpg

      重启之后,安装将自动继续进行。

    wKiom1LvAfCB1KokAAEq1QdRmJ8921.jpg

      某些产品和更新安装完成之后,需要重启操作系统才能继续。

    wKioL1LvBQWhwHi0AAMNtUCnLqE548.jpg

      重启之后,完成安装。

    wKioL1LvB07C3OlJAAPwt4B1258251.jpg

    2. 离线安装

      所谓离线安装,就是事先将上述产品和更新的安装程序下载到本地,然后再通过命令行执行安装程序。参考官方说明《从网络共享安装 SharePoint 2013 的必备组件》http://technet.microsoft.com/zh-cn/library/ff686793

      第一步,准备一个安装文件夹。例如,SharePoint 2013 Foundation 版本默认将安装文件解压到 C:\Program Files (x86)\MSECache\SharePoint2010\ 。然后将SharePoint安装光盘的根目录下面 msvcr100.dll 和 prerequisiteinstaller.exe 两个文件复制到这个安装文件夹(安装SharePoint 2013 Foundation 版本时可省略此步骤)。

      第二步,根据以下链接下载文件,然后复制到安装文件夹下面的PrerequisiteInstallerFiles文件夹。

    wKiom1L18F2ySsK1AAQO_hEh9cM944.jpg

    (1) Microsoft .NET Framework 4.5

      下载链接 http://go.microsoft.com/fwlink/?LinkId=225702 或 http://download.microsoft.com/download/b/a/4/ba4a7e71-2906-4b2d-a0e1-80cf16844f5f/dotnetfx45_full_x86_x64.exe

      dotnetfx45_full_x86_x64.exe  48.0MB

    (2) Windows Management Framework 3.0

      下载链接http://www.microsoft.com/en-us/download/details.aspx?id=34595

      For Windows Server 2008 R2: Windows6.1-KB2506143-x64.msu  15.8MB

      For Windows Server 2008: Windows6.0-KB2506146-x64.msu  14.4MB

    (3) Microsoft SQL Server 2008 R2 SP1 Native Client

      下载链接 http://www.microsoft.com/zh-cn/download/details.aspx?id=26728

      2052\x64\sqlncli.msi  8.1MB

    (4) Windows Identity Foundation (KB974405)

      下载链接 http://go.microsoft.com/fwlink/p/?LinkID=226830 或 http://download.microsoft.com/download/D/7/2/D72FD747-69B6-40B7-875B-C2B40A6B2BDD/Windows6.1-KB974405-x64.msu

    Windows6.1-KB974405-x64.msu  1.5MB

    (5) Microsoft Sync Framework Runtime v1.0 SP1 (x64)

      下载链接 http://go.microsoft.com/fwlink/p/?LinkID=224449 或 http://download.microsoft.com/download/E/0/0/E0060D8F-2354-4871-9596-DC78538799CC/Synchronization.msi

      Synchronization.msi  1.0MB

    (6) Windows Server AppFabric

      下载链接 http://go.microsoft.com/fwlink/?LinkId=235496 或 http://download.microsoft.com/download/A/6/7/A678AB47-496B-4907-B3D4-0A2D280A13C0/WindowsServerAppFabricSetup_x64.exe

    WindowsServerAppFabricSetup_x64.exe  32.0MB

    (7) Windows Identity Extensions

      下载链接 http://go.microsoft.com/fwlink/?LinkID=252368 或 http://download.microsoft.com/download/0/1/D/01D06854-CA0C-46F1-ADBA-EBF86010DCC6/rtm/MicrosoftIdentityExtensions-64.msi

    MicrosoftIdentityExtensions-64.msi  0.3MB

    (8) Microsoft Information Protection and Control Client

      下载链接 http://go.microsoft.com/fwlink/p/?LinkID=219568 或 http://download.microsoft.com/download/9/1/D/91DA8796-BE1D-46AF-8489-663AB7811517/setup_msipc_x64.msi

    setup_msipc_x64.msi  4.7MB

    (9) Microsoft WCF Data Services 5.0

      下载链接 http://www.microsoft.com/zh-cn/download/details.aspx?id=29306

    WcfDataServices.exe  5.5MB

    (10) Cumulative Update Package 1 for Microsoft AppFabric 1.1 for Windows Server (KB2671763)

      下载链接 http://www.microsoft.com/zh-cn/download/details.aspx?id=29241

    AppFabric1.1-RTM-KB2671763-x64-ENU.exe  1.3MB

    AppFabric1.1-RTM-KB2671763-x64-CHS.exe  1.3MB

      第三步,运行以下命令行;

    C:\Program Files (x86)\MSECache\SharePoint2010>  prerequisiteinstaller.exe  /SQLNCLi:prerequisiteinstallerfiles\sqlncli.msi  /PowerShell:prerequisiteinstallerfiles\Windows6.1-KB2506143-x64.msu  /NETFX:prerequisiteinstallerfiles\dotNetFx45_Full_x86_x64.exe  /IDFX:prerequisiteinstallerfiles\Windows6.1-KB974405-x64.msu  /Sync:prerequisiteinstallerfiles\Synchronization.msi  /AppFabric:prerequisiteinstallerfiles\WindowsServerAppFabricSetup_x64.exe  /IDFX11:prerequisiteinstallerfiles\MicrosoftIdentityExtensions-64.msi  /MSIPCClient:prerequisiteinstallerfiles\setup_msipc_x64.msi  /WCFDataServices:prerequisiteinstallerfiles\WcfDataServices.exe  /KB2671763:prerequisiteinstallerfiles\AppFabric1.1-RTM-KB2671763-x64-CHS.exe

      后续操作步骤,就类似于运行SharePoint 2013安装向导的“安装必备软件”,只是不需要连接到Internet 在线下载所需的安装包。完成安装后,需要重新启动计算机。

      离线安装结束并重启计算机后,如果需要验证安装结果,可以直接运行以下命令查看。

    C:\Program Files (x86)\MSECache\SharePoint2010>  prerequisiteinstaller.exe

      如果看到“所有必备组件均已安装或启用”,则表示所需的组件均已成功实现了离线安装。

    三、安装 SharePoint Server 2013

    1. 启动安装向导

    wKiom1LvXPzyX1dNAAFKlAAw30w352.jpg

    2. 输入产品密钥

      安装向导自动根据产品密钥判断后续安装的版本,例如:企业版、标准版、评估版。

    wKiom1L0eYmD6sI6AAFSSsqqUxw777.jpg

    3. 阅读软件许可条款

    wKiom1LvXP3wJ5B4AAOJRdWAVtM763.jpg

    4. 选择服务器类型、文件位置

    wKioL1L0eYyBbEWTAAF37yG6pdE975.jpg

    wKiom1L0ebDze0Y0AAHuHMupXBc985.jpg

    5. 开始安装

    wKioL1L0eaTCNWzOAACrdtA66D4360.jpg

    wKiom1L0ecnifbKYAADWN2kWFPU726.jpg

    本文出自 “我们一起追过的MSSQL” 博客,请务必保留此出处http://jimshu.blog.51cto.com/3171847/1355793

  • OSChina年度热门第二的代码赏析

    image

    <html>
    <head>
    <title>DZ新闻中心----DZ是世界上最牛的人!</title>
    
    
    <script type="text/javascript">
        var str = ["D","Z","新","闻","中","心","----","D","Z","是","世","界","上","最","牛","的","人","!"," "," "," "];
    var len = str.length;
    
    var index = 0;
    function rollTitle(){
        var title = "";
        if(index == len){
            index = 0;
        }else{
            index ++;
        }
        for(var i = 0; i < len; ++i){
            var j = i + index;
            if(j >= len){
                title += str[j - len];
            }else{
                title += str[j];
            }
        }
        document.getElementsByTagName("title")[0].innerText=title;
        
    
        setTimeout(rollTitle,500);
    }
    
    (function(){    
        setTimeout(rollTitle,500);
    }());
    </script>
    
    <style>
        p{
            padding-left:20px;
            font-size:35px;
        }
        A{
            COLOR:BLUE;
            text-decoration:none;
        }
    </style>
    
    </head>
    
    
    <body>
    <p>
        <a target="_blank" href="http://news.baidu.com/">百度新闻</a>
    </p>
    <p>
        <a target="_blank" href="http://www.sina.com.cn/">新浪门户</a>
        <a target="_blank" href="http://news.sina.com.cn/">新浪新闻</a>
    </p>
    <p>
        <a target="_blank" href="http://www.qq.com/">腾讯门户</a>    
        <a target="_blank" href="http://news.qq.com/">腾讯新闻</a>
    </p>
    <p>
        <a target="_blank" href="http://cn.msn.com/">MSN资讯</a>
    </p>
    <p>
        <a target="_blank" href="http://www.ifeng.com/">凤凰网</a>
        <a target="_blank" href="http://news.ifeng.com/">凤凰资讯</a>    
    </p>
    <p>
    <a target="_blank" href="http://www.chinanews.com/">中国新闻网</a>
    </p>
    <p>
    <a target="_blank" href="http://www.huanqiu.com/">环球网</a>
    </p>
    </body>
    
    </html>

    欣赏评论:KAO

  • WeixinApi.js

    /**!
     * 微信内置浏览器的Javascript API,功能包括:
     *
     * 1、分享到微信朋友圈
     * 2、分享给微信好友
     * 3、分享到腾讯微博
     * 4、隐藏/显示右上角的菜单入口
     * 5、隐藏/显示底部浏览器工具栏
     * 6、获取当前的网络状态
     * 7、调起微信客户端的图片播放组件
     *
     * @author zhaoxianlie(http://www.baidufe.com)
     */
    var WeixinApi = (function () {
    
        /**
         * 分享到微信朋友圈
         * @param       {Object}    data       待分享的信息
         * @p-config    {String}    appId      公众平台的appId(服务号可用)
         * @p-config    {String}    imageUrl   图片地址
         * @p-config    {String}    link       链接地址
         * @p-config    {String}    desc       描述
         * @p-config    {String}    title      分享的标题
         *
         * @param       {Object}    callbacks  相关回调方法
         * @p-config    {Boolean}   async                   ready方法是否需要异步执行,默认false
         * @p-config    {Function}  ready(argv)             就绪状态
         * @p-config    {Function}  dataLoaded(data)        数据加载完成后调用,async为true时有用,也可以为空
         * @p-config    {Function}  cancel(resp)    取消
         * @p-config    {Function}  fail(resp)      失败
         * @p-config    {Function}  confirm(resp)   成功
         * @p-config    {Function}  all(resp)       无论成功失败都会执行的回调
         */
        function weixinShareTimeline(data, callbacks) {
            callbacks = callbacks || {};
            var shareTimeline = function (theData) {
                WeixinJSBridge.invoke('shareTimeline', {
                    "appid":theData.appId ? theData.appId : '',
                    "img_url":theData.imgUrl,
                    "link":theData.link,
                    "desc":theData.title,
                    "title":theData.desc, // 注意这里要分享出去的内容是desc
                    "img_width":"120",
                    "img_height":"120"
                }, function (resp) {
                    switch (resp.err_msg) {
                        // share_timeline:cancel 用户取消
                        case 'share_timeline:cancel':
                            callbacks.cancel && callbacks.cancel(resp);
                            break;
                        // share_timeline:fail 发送失败
                        case 'share_timeline:fail':
                            callbacks.fail && callbacks.fail(resp);
                            break;
                        // share_timeline:confirm 发送成功
                        case 'share_timeline:confirm':
                        case 'share_timeline:ok':
                            callbacks.confirm && callbacks.confirm(resp);
                            break;
                    }
                    // 无论成功失败都会执行的回调
                    callbacks.all && callbacks.all(resp);
                });
            };
            WeixinJSBridge.on('menu:share:timeline', function (argv) {
                if (callbacks.async && callbacks.ready) {
                    if(!callbacks.__dataLoadedFuncInited) {
                        var loadedCb = callbacks.dataLoaded || new Function();
                        callbacks.dataLoaded = function (newData) {
                            loadedCb(newData);
                            shareTimeline(newData);
                        };
                        callbacks.__dataLoadedFuncInited = true;
                    }
                    // 然后就绪
                    callbacks.ready && callbacks.ready(argv);
                } else {
                    // 就绪状态
                    callbacks.ready && callbacks.ready(argv);
                    shareTimeline(data);
                }
            });
        }
    
        /**
         * 发送给微信上的好友
         * @param       {Object}    data       待分享的信息
         * @p-config    {String}    appId      公众平台的appId(服务号可用)
         * @p-config    {String}    imageUrl   图片地址
         * @p-config    {String}    link       链接地址
         * @p-config    {String}    desc       描述
         * @p-config    {String}    title      分享的标题
         *
         * @param       {Object}    callbacks  相关回调方法
         * @p-config    {Boolean}   async                   ready方法是否需要异步执行,默认false
         * @p-config    {Function}  ready(argv)             就绪状态
         * @p-config    {Function}  dataLoaded(data)        数据加载完成后调用,async为true时有用,也可以为空
         * @p-config    {Function}  cancel(resp)    取消
         * @p-config    {Function}  fail(resp)      失败
         * @p-config    {Function}  confirm(resp)   成功
         * @p-config    {Function}  all(resp)       无论成功失败都会执行的回调
         */
        function weixinSendAppMessage(data, callbacks) {
            callbacks = callbacks || {};
            var sendAppMessage = function (theData) {
                WeixinJSBridge.invoke('sendAppMessage', {
                    "appid":theData.appId ? theData.appId : '',
                    "img_url":theData.imgUrl,
                    "link":theData.link,
                    "desc":theData.desc,
                    "title":theData.title,
                    "img_width":"120",
                    "img_height":"120"
                }, function (resp) {
                    switch (resp.err_msg) {
                        // send_app_msg:cancel 用户取消
                        case 'send_app_msg:cancel':
                            callbacks.cancel && callbacks.cancel(resp);
                            break;
                        // send_app_msg:fail 发送失败
                        case 'send_app_msg:fail':
                            callbacks.fail && callbacks.fail(resp);
                            break;
                        // send_app_msg:confirm 发送成功
                        case 'send_app_msg:confirm':
                        case 'send_app_msg:ok':
                            callbacks.confirm && callbacks.confirm(resp);
                            break;
                    }
                    // 无论成功失败都会执行的回调
                    callbacks.all && callbacks.all(resp);
                });
            };
            WeixinJSBridge.on('menu:share:appmessage', function (argv) {
                if (callbacks.async && callbacks.ready) {
                    if(!callbacks.__dataLoadedFuncInited) {
                        var loadedCb = callbacks.dataLoaded || new Function();
                        callbacks.dataLoaded = function (newData) {
                            loadedCb(newData);
                            sendAppMessage(newData);
                        };
                        callbacks.__dataLoadedFuncInited = true;
                    }
                    // 然后就绪
                    callbacks.ready && callbacks.ready(argv);
                } else {
                    // 就绪状态
                    callbacks.ready && callbacks.ready(argv);
                    sendAppMessage(data);
                }
            });
        }
    
        /**
         * 分享到腾讯微博
         * @param       {Object}    data       待分享的信息
         * @p-config    {String}    imageUrl   图片地址
         * @p-config    {String}    link       链接地址
         * @p-config    {String}    desc       描述
         * @p-config    {String}    title      分享的标题
         *
         * @param       {Object}    callbacks  相关回调方法
         * @p-config    {Boolean}   async                   ready方法是否需要异步执行,默认false
         * @p-config    {Function}  ready(argv)             就绪状态
         * @p-config    {Function}  dataLoaded(data)        数据加载完成后调用,async为true时有用,也可以为空
         * @p-config    {Function}  cancel(resp)    取消
         * @p-config    {Function}  fail(resp)      失败
         * @p-config    {Function}  confirm(resp)   成功
         * @p-config    {Function}  all(resp)       无论成功失败都会执行的回调
         */
        function weixinShareWeibo(data, callbacks) {
            callbacks = callbacks || {};
            var shareWeibo = function (theData) {
                WeixinJSBridge.invoke('shareWeibo', {
                    "content":theData.desc,
                    "link":theData.link,
                    "img_url":theData.imgUrl,
                    "title":theData.title,
                    "img_width":"120",
                    "img_height":"120"
                }, function (resp) {
                    switch (resp.err_msg) {
                        // share_weibo:cancel 用户取消
                        case 'share_weibo:cancel':
                            callbacks.cancel && callbacks.cancel(resp);
                            break;
                        // share_weibo:fail 发送失败
                        case 'share_weibo:fail':
                            callbacks.fail && callbacks.fail(resp);
                            break;
                        // share_weibo:confirm 发送成功
                        case 'share_weibo:confirm':
                        case 'share_weibo:ok':
                            callbacks.confirm && callbacks.confirm(resp);
                            break;
                    }
                    // 无论成功失败都会执行的回调
                    callbacks.all && callbacks.all(resp);
                });
            };
            WeixinJSBridge.on('menu:share:weibo', function (argv) {
                if (callbacks.async && callbacks.ready) {
                    if(!callbacks.__dataLoadedFuncInited) {
                        var loadedCb = callbacks.dataLoaded || new Function();
                        callbacks.dataLoaded = function (newData) {
                            loadedCb(newData);
                            shareWeibo(newData);
                        };
                        callbacks.__dataLoadedFuncInited = true;
                    }
                    // 然后就绪
                    callbacks.ready && callbacks.ready(argv);
                } else {
                    // 就绪状态
                    callbacks.ready && callbacks.ready(argv);
                    shareWeibo(data);
                }
            });
        }
    
        /**
         * 调起微信Native的图片播放组件。
         * 这里必须对参数进行强检测,如果参数不合法,直接会导致微信客户端crash
         * 
         * @param {String} curSrc 当前播放的图片地址
         * @param {Array} srcList 图片地址列表
         */
        function imagePreview(curSrc,srcList) {
            if(!curSrc || !srcList || srcList.length == 0) {
                return;
            }
            WeixinJSBridge.invoke('imagePreview', {
                'current' : curSrc,
                'urls' : srcList
            });
        }
    
        /**
         * 显示网页右上角的按钮
         */
        function showOptionMenu() {
            WeixinJSBridge.call('showOptionMenu');
        }
    
    
        /**
         * 隐藏网页右上角的按钮
         */
        function hideOptionMenu() {
            WeixinJSBridge.call('hideOptionMenu');
        }
    
        /**
         * 显示底部工具栏
         */
        function showToolbar() {
            WeixinJSBridge.call('showToolbar');
        }
    
        /**
         * 隐藏底部工具栏
         */
        function hideToolbar() {
            WeixinJSBridge.call('hideToolbar');
        }
    
        /**
         * 返回如下几种类型:
         *
         * network_type:wifi     wifi网络
         * network_type:edge     非wifi,包含3G/2G
         * network_type:fail     网络断开连接
         * network_type:wwan     2g或者3g
         *
         * 使用方法:
         * WeixinApi.getNetworkType(function(networkType){
         *
         * });
         *
         * @param callback
         */
        function getNetworkType(callback) {
            if (callback && typeof callback == 'function') {
                WeixinJSBridge.invoke('getNetworkType', {}, function (e) {
                    // 在这里拿到e.err_msg,这里面就包含了所有的网络类型
                    callback(e.err_msg);
                });
            }
        }
    
        /**
         * 当页面加载完毕后执行,使用方法:
         * WeixinApi.ready(function(Api){
         *     // 从这里只用Api即是WeixinApi
         * });
         * @param readyCallback
         */
        function wxJsBridgeReady(readyCallback) {
            if (readyCallback && typeof readyCallback == 'function') {
                var Api = this;
                var wxReadyFunc = function () {
                    readyCallback(Api);
                };
                if (typeof window.WeixinJSBridge == "undefined"){
                    if (document.addEventListener) {
                        document.addEventListener('WeixinJSBridgeReady', wxReadyFunc, false);
                    } else if (document.attachEvent) {
                        document.attachEvent('WeixinJSBridgeReady', wxReadyFunc);
                        document.attachEvent('onWeixinJSBridgeReady', wxReadyFunc);
                    }
                }else{
                    wxReadyFunc();
                }
            }
        }
    
        return {
            version         :"1.4",
            ready           :wxJsBridgeReady,
            shareToTimeline :weixinShareTimeline,
            shareToWeibo    :weixinShareWeibo,
            shareToFriend   :weixinSendAppMessage,
            showOptionMenu  :showOptionMenu,
            hideOptionMenu  :hideOptionMenu,
            showToolbar     :showToolbar,
            hideToolbar     :hideToolbar,
            getNetworkType  :getNetworkType,
            imagePreview    :imagePreview
        };
    })();

    转自:https://github.com/zxlie/WeixinApi/blob/master/WeixinApi.js

  • QWrap核心中的几个不起眼小方法使用技巧

    image

    QWrap核心中的几个不起眼小方法使用技巧  —>这不是反向工程么~

    https://github.com/zxlie/WeixinApi/blob/master/WeixinApi.js  这个东东应该是QWrap出来的吧

  • node-webkit:开发桌面+WEB混合型应用的神器 (转)

    bla…bla…

            顾名思义,node-webkit就是nodejs+webkit。

            这样做的好处显而易见,核心奥义在于,用nodejs来进行本地化调用,用webkit来解析和执行HTML+JS。

    快速上手

    下载node-webkit

            点击这里:

            https://github.com/rogerwang/node-webkit

            找到Downloads这一小节,然后下载对应平台的node-webkit预编译包。(为了介绍起来方便,本文后续内容全部针对windows平台,OSX和linux平台上的操作类似,就不做特意说明了。)

            下载完之后解压,可以看到如下内容:

            双击nw.exe直接运行,看到如下界面说明一切都ok,在你的机器上可以运行:

    Hello nw

            老规矩,先来Hello World!

            建一个example1.html,内容如下:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Hello World!</title>
      </head>
      <body>
        <h1>Hello node-webkit!</h1>
        We are using node.js <script>document.write(process.version)</script>.
      </body>
    </html>
    

            在同一级目录下再建一个package.json,内容如下:

    {
      "name": "nw-demo",
      "main": "example1.html"
    }
    

            好了,用你机器上的压缩工具,rar或者7z或者神马神马的,把这两个文件压缩成exampl1.zip,如下图所示:


            注意,用什么工具压缩无所谓,但是一定要压缩成ZIP格式。

            把example1.zip拷贝到与nw.exe同级的目录下,如下图:

            然后用鼠标把example1.zip拖动到nw.exe上运行(用命令行也可以,不过在windows下还是直接拖比较爽一点),效果如下图:

            这样,第一个例子就完成了,然后你自己可以去玩儿了。

            按照官方的搞法,example1.zip可改名成example1.nw,这样显得更加高大上一些。不过由于小僧的BIG一向比较低,私认为用.zip作为后缀也没啥不可以。

    来点儿JS

            第一个例子代码非常简单,你可以自己加点儿你最熟悉的JS代码,比如这样:

    <!DOCTYPE html>
    <html>
      <head>
        <title>Example2</title>
        <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
      </head>
      <body>
        <h1>Example2</h1>
        <script>
            alert("This is example2!");
        </script>
      </body>
    </html>
    

            显然,这里有很多HTML5的习惯写法,那当然是必须的对吧?因为webkit在google的调教之下早已成了HTML5的开路先锋了!

    打包和分发

            如上图,很显然,要想让我们的应用跑起来,以上这些dll和pak文件是少不了的(至于每个文件是用来干嘛的,请参见官方的描述文档)。

            那么,对于我们做的example1这个简单的应用,怎么让我们的用户能运行它呢?当然,让我们的用户自己去下载node-webkit然后解压运行,这也算一种办法,这样的话我们只要把example1.zip文件扔给他就可以了。

            不过这样总觉得有点儿奇怪,要是能把所有相关的内容都打包成一个独立的exe文件就好了,比如打包成example1.exe,这样用户拿到之后直接双击就可以运行,这样显然是极好的。

            OK,let’s go!

            首先,我们要合并example1.zip和nw.exe,形成一个新文件,名为example1.exe,这一点我们通过windows的命令行下的copy命令可以实现:

    copy /b nw.exe+example1.zip example1.exe
    

            注意,nw.exe必须放在+号前面。

            这样就生成了一个名为example1.exe的文件,双击这个文件,看到的效果和第一个例子相同。很显然,这个example1.exe还是要依 赖那些dll文件才能运行,所以,这时候我们需要一款打包工具来帮助我们把这些example1.exe和dll文件都打包到一起,它就是Enigma Virtual Box ,大家可以点击这里下载安装:

    http://enigmaprotector.com/assets/files/enigmavb.exe

            安装完成之后启动,看到如下界面:

            打包过程截图(请按照图中的配置操作):

            点击右下角的Process按钮开始打包,最终获得了一个名为example1_boxed.exe的文件。把这个文件拷贝到其它任意目录中双击,如果能顺利运行,说明打包成功!你可以把它分发给你的用户了!

            官方的文档上还介绍了其它几种打包分发方式,不过看起来都不是很实用,有兴趣的参见这里:

    https://github.com/rogerwang/node-webkit/wiki/How-to-package-and-distribute-your-apps

    未竟的主题

            以上就是关于node-webkit用法的一些简单示例,你可以沿着这个方向自己去探索了。有热心的网友把我们的Ext4.1中文文档打了一个包出来,可以到脚本娃娃的群共享(号83163037)去下载(比较大,不好放上来),入群口令nw

    如何最小化发布?

            在官方发布的内容里面,icudt.dll这个文件有9724k,nw.exe有38396k,很显然,这个体积略显庞大了一些,如果我们的应用本身很小,只有几百K,而打包出来的东西有几十M显然是不合适的,这样非常不方便拷贝和网络传播。

            很显然,我们还需要一款工具,能够根据需要进行最小化的打包发布。

            不过目前我还没找到这样的好东东,各路大神如有知道的,求指教。

    如何利用nodejs调用本地接口?

            很显然,既然nodejs都被打包进来了,网上众多的npm模块都是可以拿过来使用的,具体做法官方主页上都有比较详细的介绍,参见这里https://github.com/rogerwang/node-webkit

    其它案例

            官方的主页上还展示了众多使用node-webkit制作的应用,各位自行点击查看即可。

    展望未来

    其实这条路老早就有人在走

            网上有很多人在争论,未来究竟是原生的应用会胜出,还是WEB APP会胜出,实际上这两者并不是你死我活的关系,而是会互相融合,走向【混合型应用】这样一种形态。

    先看几个界面:




            大家可以把自己的网络断掉测试以上界面,QQ有时候甚至能爆出JS错误,哈哈。

            2011年的时候,我发过一篇文章,《未来五年,桌面应用和WEB走向融合》,当时只是因为看到诸如酷我音乐、腾讯QQ这些桌面应用的开发方式而有 所感悟,对于桌面应用会以什么样的形式和B/S开发模式融合在一起并没有确切的想法。而最近由于打包Ext4.1中文文档的缘故知道了node- webkit,突然有一种恍然大悟的赶脚!

            当然,酷我音乐盒和疼讯目前是用什么样的技术实现的小僧不知道其中的细节(人家也不想让外人知道对吧?)。不过,小僧相信,对于屌丝单兵作战来说,疼讯的实现方式肯定会比node-webkit更加复杂,目测个人无法模仿。

    跨平台+多终端的神器

            毫无疑问,这货会成为2014年的一款神器。因为它具备了以下这些无可比拟的优势:

    1. 人力成本最低:招一个会C++的码农要花多少钱?招一个牛逼哄哄的既会C++还对QT、MFC等了如指掌的码 农又要花多少钱?对比一下,招一个会JS+CSS的呢?如果你是老板,你怎么想?再说了,市面上会C++的码农更多,还是会JS+CSS的码农更多?无需 小僧多言,其中的奥妙您自个儿体会。
    2. APP的体积更小:列位一定还记得当年大明湖畔的疼讯QQ只有十几M的体积,俗话说,岁月是把杀猪刀,在集成了疼讯的各种应用之后,如今的QQ身材早已发福走形。可以想象,如果QQ不使用嵌入浏览器内核的方式,恐怕体积早就超过100M了吧!
    3. 不需要整体更新APP:如果是按照传统的方式开发桌面应用,每次升级的时候都需要用户重新下载整个应用,比如疼讯QQ。但是用node-webkit的这种方式,可以很好地避免这个问题,就像网站一样,网站内容可以不断更新,但是浏览器本身的更新不需要那么频繁。
    4. 跨平台:这一点是由NodeJS来保证的。
    5. 多终端:由于nodejs和chromium本身都是可以在桌面平台和移动平台上运行的,因此用这货来支持多终端开发,保持技术栈的一致性是非常合适的。
    6. 完善的工具链:由于nodejs的兴起,它下面的npm模块数量已经有几十万个;而各种基于chrome内核的定制版浏览器就不用再介绍了。加上传统B/S开发模式下已经具备的完整开发和设计工具,用node-webkit来开发桌面应用已经具备了完善的工具链。

    参考资料

            关于node-webkit作者的介绍参见这里:

            http://www.csdn.net/article/2014-01-08/2818066-Node-Webkit

            node-webkit在github上的地址:

            https://github.com/rogerwang/node-webkit

            由于node-webkit的作者提供的文档都是E文的,为了方便大中华局域网内的一众屌丝,我的好基友@老男孩对文档做了一些翻译,参见这里:

            https://github.com/liu78778/node-webkit

  • jQuery的deferred对象详解 转载

    jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本。

    每个版本都会引入一些新功能。今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能—-deferred对象。

    这个功能很重要,未来将成为jQuery的核心方法,它彻底改变了如何在jQuery中使用ajax。为了实现它,jQuery的全部ajax代码都被改写了。但是,它比较抽象,初学者很难掌握,网上的教程也不多。所以,我把自己的学习笔记整理出来了,希望对大家有用。

    本文不是初级教程,针对的读者是那些已经具备jQuery使用经验的开发者。如果你想了解jQuery的基本用法,请阅读我编写的《jQuery设计思想》和《jQuery最佳实践》。

    ======================================

    jQuery的deferred对象详解

    作者:阮一峰

    一、什么是deferred对象?

    开发网站的过程中,我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。

    通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。

    但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了deferred对象。

    简单说,deferred对象就是jQuery的回调函数解决方案。在英语中,defer的意思是”延迟”,所以deferred对象的含义就是”延迟”到未来某个点再执行。

    它解决了如何处理耗时操作的问题,对那些操作提供了更好的控制,以及统一的编程接口。它的主要功能,可以归结为四点。下面我们通过示例代码,一步步来学习。

    二、ajax操作的链式写法

    首先,回顾一下jQuery的ajax操作的传统写法:

      $.ajax({

        url: “test.html”,

        success: function(){
          alert(“哈哈,成功了!”);
        },

        error:function(){
          alert(“出错啦!”);
        }

      });

    (运行代码示例1)

    在上面的代码中,$.ajax()接受一个对象参数,这个对象包含两个方法:success方法指定操作成功后的回调函数,error方法指定操作失败后的回调函数。

    $.ajax()操作完成后,如果使用的是低于1.5.0版本的jQuery,返回的是XHR对象,你没法进行链式操作;如果高于1.5.0版本,返回的是deferred对象,可以进行链式操作。

    现在,新的写法是这样的:

      $.ajax(“test.html”)

    .done(function(){ alert(“哈哈,成功了!”); })

    .fail(function(){ alert(“出错啦!”); });

    (运行代码示例2)

    可以看到,done()相当于success方法,fail()相当于error方法。采用链式写法以后,代码的可读性大大提高。

    三、指定同一操作的多个回调函数

    deferred对象的一大好处,就是它允许你自由添加多个回调函数。

    还是以上面的代码为例,如果ajax操作成功后,除了原来的回调函数,我还想再运行一个回调函数,怎么办?

    很简单,直接把它加在后面就行了。

      $.ajax(“test.html”)

      .done(function(){ alert(“哈哈,成功了!”);} )

      .fail(function(){ alert(“出错啦!”); } )

    .done(function(){ alert(“第二个回调函数!”);} );

    (运行代码示例3)

    回调函数可以添加任意多个,它们按照添加顺序执行。

    四、为多个操作指定回调函数

    deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。

    请看下面的代码,它用到了一个新的方法$.when():

    $.when($.ajax(“test1.html”), $.ajax(“test2.html”))

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    (运行代码示例4)

    这段代码的意思是,先执行两个操作$.ajax(“test1.html”)和$.ajax(“test2.html”),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。

    五、普通操作的回调函数接口(上)

    deferred对象的最大优点,就是它把这一套回调函数接口,从ajax操作扩展到了所有操作。也就是说,任何一个操作—-不管是ajax操作还是本地操作,也不管是异步操作还是同步操作—-都可以使用deferred对象的各种方法,指定回调函数。

    我们来看一个具体的例子。假定有一个很耗时的操作wait:

      var wait = function(){

        var tasks = function(){

          alert(“执行完毕!”);

        };

        setTimeout(tasks,5000);

      };

    我们为它指定回调函数,应该怎么做呢?

    很自然的,你会想到,可以使用$.when():

      $.when(wait())

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    (运行代码示例5)

    但是,这样写的话,done()方法会立即执行,起不到回调函数的作用。原因在于$.when()的参数只能是deferred对象,所以必须对wait()进行改写:

      var dtd = $.Deferred(); // 新建一个deferred对象

      var wait = function(dtd){

        var tasks = function(){

          alert(“执行完毕!”);

    dtd.resolve(); // 改变deferred对象的执行状态

        };

        setTimeout(tasks,5000);

    return dtd;

      };

    现在,wait()函数返回的是deferred对象,这就可以加上链式操作了。

      $.when(wait(dtd))

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    (运行代码示例6)

    wait()函数运行完,就会自动运行done()方法指定的回调函数。

    六、deferred.resolve()方法和deferred.reject()方法

    如果仔细看,你会发现在上面的wait()函数中,还有一个地方我没讲解。那就是dtd.resolve()的作用是什么?

    要说清楚这个问题,就要引入一个新概念”执行状态”。jQuery规定,deferred对象有三种执行状态—-未完成,已完成和已失败。如果执行状态是”已完成”(resolved),deferred对象立刻调用done()方法指定的回调函数;如果执行状态是”已失败”,调用fail()方法指定的回调函数;如果执行状态是”未完成”,则继续等待,或者调用progress()方法指定的回调函数(jQuery1.7版本添加)。

    前面部分的ajax操作时,deferred对象会根据返回结果,自动改变自身的执行状态;但是,在wait()函数中,这个执行状态必须由程序员手动指定。dtd.resolve()的意思是,将dtd对象的执行状态从”未完成”改为”已完成”,从而触发done()方法。

    类似的,还存在一个deferred.reject()方法,作用是将dtd对象的执行状态从”未完成”改为”已失败”,从而触发fail()方法。

      var dtd = $.Deferred(); // 新建一个Deferred对象

      var wait = function(dtd){

        var tasks = function(){

          alert(“执行完毕!”);

    dtd.reject(); // 改变Deferred对象的执行状态

        };

        setTimeout(tasks,5000);

        return dtd;

      };

      $.when(wait(dtd))

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    (运行代码示例7)

    七、deferred.promise()方法

    上面这种写法,还是有问题。那就是dtd是一个全局对象,所以它的执行状态可以从外部改变。

    请看下面的代码:

      var dtd = $.Deferred(); // 新建一个Deferred对象

      var wait = function(dtd){

        var tasks = function(){

          alert(“执行完毕!”);

          dtd.resolve(); // 改变Deferred对象的执行状态

        };

        setTimeout(tasks,5000);

        return dtd;

      };

      $.when(wait(dtd))

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    dtd.resolve();

    (运行代码示例8)

    我在代码的尾部加了一行dtd.resolve(),这就改变了dtd对象的执行状态,因此导致done()方法立刻执行,跳出”哈哈,成功了!”的提示框,等5秒之后再跳出”执行完毕!”的提示框。

    为了避免这种情况,jQuery提供了deferred.promise()方法。它的作用是,在原来的deferred对象上返回另一个deferred对象,后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。

    请看下面的代码:

      var dtd = $.Deferred(); // 新建一个Deferred对象

      var wait = function(dtd){

        var tasks = function(){

          alert(“执行完毕!”);

          dtd.resolve(); // 改变Deferred对象的执行状态

        };
        setTimeout(tasks,5000);

    return dtd.promise(); // 返回promise对象

      };

    var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作

      $.when(d)

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    d.resolve(); // 此时,这个语句是无效的

    (运行代码示例9)

    在上面的这段代码中,wait()函数返回的是promise对象。然后,我们把回调函数绑定在这个对象上面,而不是原来的deferred对象上面。这样的好处是,无法改变这个对象的执行状态,要想改变执行状态,只能操作原来的deferred对象。

    不过,更好的写法是allenm所指出的,将dtd对象变成wait()函数的内部对象。

      var wait = function(dtd){

    var dtd = $.Deferred(); //在函数内部,新建一个Deferred对象

        var tasks = function(){

          alert(“执行完毕!”);

          dtd.resolve(); // 改变Deferred对象的执行状态

        };
        setTimeout(tasks,5000);

        return dtd.promise(); // 返回promise对象

      };

    $.when(wait())

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    (运行代码示例10)

    八、普通操作的回调函数接口(中)

    另一种防止执行状态被外部改变的方法,是使用deferred对象的建构函数$.Deferred()。

    这时,wait函数还是保持不变,我们直接把它传入$.Deferred():

    $.Deferred(wait)

      .done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

    (运行代码示例11)

    jQuery规定,$.Deferred()可以接受一个函数名(注意,是函数名)作为参数,$.Deferred()所生成的deferred对象将作为这个函数的默认参数。

    九、普通操作的回调函数接口(下)

    除了上面两种方法以外,我们还可以直接在wait对象上部署deferred接口。

      var dtd = $.Deferred(); // 生成Deferred对象

      var wait = function(dtd){

        var tasks = function(){

          alert(“执行完毕!”);

          dtd.resolve(); // 改变Deferred对象的执行状态

        };

        setTimeout(tasks,5000);

      };

    dtd.promise(wait);

      wait.done(function(){ alert(“哈哈,成功了!”); })

      .fail(function(){ alert(“出错啦!”); });

      wait(dtd);

    (运行代码示例12)

    这里的关键是dtd.promise(wait)这一行,它的作用就是在wait对象上部署Deferred接口。正是因为有了这一行,后面才能直接在wait上面调用done()和fail()。

    十、小结:deferred对象的方法

    前面已经讲到了deferred对象的多种方法,下面做一个总结:

      (1) $.Deferred() 生成一个deferred对象。

      (2) deferred.done() 指定操作成功时的回调函数

      (3) deferred.fail() 指定操作失败时的回调函数

      (4) deferred.promise() 没有参数时,返回一个新的deferred对象,该对象的运行状态无法被改变;接受参数时,作用为在参数对象上部署deferred接口。

      (5) deferred.resolve() 手动改变deferred对象的运行状态为”已完成”,从而立即触发done()方法。

      (6)deferred.reject() 这个方法与deferred.resolve()正好相反,调用后将deferred对象的运行状态变为”已失败”,从而立即触发fail()方法。

      (7) $.when() 为多个操作指定回调函数。

    除了这些方法以外,deferred对象还有二个重要方法,上面的教程中没有涉及到。

      (8)deferred.then()

    有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。

      $.when($.ajax( “/main.php” ))

    .then(successFunc, failureFunc );

    如果then()有两个参数,那么第一个参数是done()方法的回调函数,第二个参数是fail()方法的回调方法。如果then()只有一个参数,那么等同于done()。

      (9)deferred.always()

    这个方法也是用来指定回调函数的,它的作用是,不管调用的是deferred.resolve()还是deferred.reject(),最后总是执行。

      $.ajax( “test.html” )

      .always( function() { alert(“已执行!”);} );

    (致谢:本文第一稿发表后,allenm来信指出原文对promise()的理解是错的。现在的第二稿是根据他的文章修改的,在此我表示衷心感谢。)

    (完)