(1):一些发布方式

ClickOnce是什么玩意儿,这个问题嘛,在21世纪的互联网严重发达的时代,估计也没有必要大费奏章去介绍了,弄不好的话,还有抄袭之嫌。因此,有关ClickOnce的介绍,各位朋友可以直接查找MSDN文档,或者看下面这里,这是百度百科上的,其实也是Ctrl + C版本。http://baike.baidu.com/view/1390498.htm

如果你对这些理论的东西不感兴趣,没关系,上面所提及的内容你完全可以无视之。我们只要知道,ClickOnce是一种应用程序部署方式即可。那什么是部署呢? 我们不妨查一查词典,部署的释义为安排、布置等,而放到我们的应用程序来说,我们通俗的叫法就是安装。即把你开发好的应用程序布置到客户的计算机上,或者说安装到客户的计算机上,这就是部署。

所以我们可以说ClickOnce应用程序就是一类安装包装,它可以一步到位进行安装,而不像我们所使用的传统安装包那样,要N个"下一步",然后才是完成。

也许有人也说,发布ClickOnce包不是很简单吗? 在VS里面点几下鼠标就完事了吗? 你还拿来写博客干吗? 首先,法律并没有规定简单的东西不能拿来写博客;其次,对于许多初学者朋友来说,我想是有参考价值,至少会有;再者,有些小问题可能大家经常会忽略的。

我们先来发布一个Windows Forms应用程序,练练手。

准备条件:先确保安装了IIS,如果没有,就赶紧动手吧,因为下面我们要做的例子,是把ClickOnce同时发布到本地文件和IIS服务器上的。至于如何安装,请Search the Internet吧。

  1. 以管理员身份运行VS。新建一个Windows窗体应用程序项目。
  2. 把窗口的标题(Text属性)改成你要的标题,我这里改为"宇宙无敌应用程序"。然后再拖一个Label控件,把文本改为"超级应用,天下无敌。"。大概就如下图所示。是不是很霸气?

  1. 好了,就这样吧,够简单,这就是我们待会儿要发布的应用程序。
  2. 好,开始发布ClickOnce安装程序。打开"解决方案资源管理器",在项目节点上右击,从弹出的快捷菜单中选择【属性】,打开项目属性窗口,切换到"发布"选项卡。

  1. 如下图所示,有两个发布位置。

发布位置可以选择本地文件夹,也可以选择IIS上的Web服务器地址,当然FTP也行。这两个地方也没什么特别,如果你计划把应用程序直接发布到本地,就这样填:

如果你想把安装程序发布到服务器,可以这样填,也可以单击右边的按钮来选择一个位置。

如果你希望在本地文件目录中发布一份,同时又想放到服务器上让用户下载,可以这样填。

  1. 安装模式一般选择第二项,不用解释,看字面意思就明白了,如下图。

  1. 我们的程序一般来说不可能是"终极"版,通常我们会不断更新或升级,所以,在"发布"页面中,我们可以单击"更新"按钮,随后会弹出一个窗口,如下图。

我们可以设置应用程序在什么时候检查更新,这里我改为每次运行时都检查更新。

获取更新位置,可填可不填,通常与发布位置相同。

点击确定按钮,关闭窗口。

  1. 发布版本号,勾选"随每次发布自动递增修订号"。这样一来,我们既可以自己填写版本号,如果忘记修改,也可以自动更新。

  1. 单击"发布向导"按钮,打开向导窗口,这里我们可以重新设置发布位置。

  1. 单击下一步,选择一种安装来源。

我们前面设置了IIS服务器的位置,所以默认是从网站安装,如果我们的应用程序是刻到光盘中来分发给客户的,也可以选择从CD/DVD安装。

  1. 单击下一步,选择应用程序的运行方式,如果选择CD/DVD安装,则不需要此步。

通常较好的做法是选择既可以离线使用,也可以在连接时使用。

  1. 单击下一步,再单击完成按钮,这时候我们会看到VS正在发布ClickOnce应用程序。

只要看到"发布成功"四个字就说明大功告成了。

下面我们来测试一下。

本地安装:我们上面的例子是生成了两个ClickOnce安装程序的,一个在本机文件中,一个在IIS服务器上。找到发布后的本地目录,如上面的d:\MyPublic\。双击setup.exe或MyApp.application文件,都可以启用安装程序。

我们看到,出现安全提示,如果确定应用程序来源合法,可以点击"安装"按钮进行安装。

安装成功后,我们的超级应用程序就运行起来了。

既然是叫ClickOnce了,那为什么我们刚才Click了不止一次呢? 那是因为缺少可信任的发布者证书导致出现安全提示的。在下一节中,我们会介绍如何自己制作证书,并在客户计算机中安装证书,那时候大家就会看到真正的ClickOnce,只要Click一下就可以安装了。

现在,我们可以打开"程序和功能"控制面板把应用程序卸载掉。

网站安装:打开浏览器,输入刚才发布的Web地址,在后面加上一个.application后缀的文件名。比如我们这个例子的清单文件叫MyApp.application。但我们会发现,找不到资源。

打开服务器上的目录一看,里面竟然是空的,我晕。

没关系,我们只要把刚才的发布选项改一下就行了。

然后重新发布就可以了。

我们看到,安装程序可以被激活了。

无论是哪种方式发布,无非就是把安装给客户,然后安装到他们的机器上罢了,所以,大家觉得哪种做法最简单就用哪种。这些东西也没有说什么硬性的条条框框,一切都是技巧而已。

(2):自动更新

上次我们说了如何用最基本的方式用ClickOnce技术部署应用程序项目,本篇我们来认识一下如何让应用程序具备自动更新的功能。

我们依然通过实例来学习。

第一步,随便建一个应用程序项目,至于是控制台、WPF还是WinForm就随意吧,我们的重点发布应用程序。比如这样:

为了更好的演示,我们可以在窗口上显示当前发布的版本号。我这里是一个WinForm项目,故我就用一个Label控件来显示当前发布版本。

注意,这里的版本号是ClickOnce发布的版本号,不是程序集的版本号。所以要使用ApplicationDeployment类(位于System.Deployment.Application命名空间,在程序集System.Deployment.dll中,如果没有引用,就引用吧)。通过静态属性CurrentDeployment可以得到一个与当前部署的应用程序有关的ApplicationDeployment对象。然后从ApplicationDeployment的CurrentVersion属性中就能得到当前版本号。代码如下,至于写在哪个地方,你自己想想吧,这是很基础的事了。

using System.Deployment.Application;

……

public partial class Form1 : Form

{

public Form1()

{

InitializeComponent();

// 获取当前部署

ApplicationDeployment appd = ApplicationDeployment.CurrentDeployment;

// 取得版本号

this.lblVer.Text = appd.CurrentVersion.ToString();

}

}

好,OK,现在我们可以发布该应用程序了,因为我们要让它能够自动更新,一来我们要发布到IIS或其他服务器上,记得以管理员身份运行VS,不然发布不上去;另外,要开启检查更新功能,上一节中说过,操作也很简单。

在项目属性窗口中,切换到"发布"选项卡,单击"更新…"按钮,启用检查更新即可,至于如何更新,按照默认就可以了,没有必要去改了。

然后确定,这样就算配置好了。然后发布到服务器上。

第二步,通过浏览器输入地址,安装并运行应用程序,如下图所示,注意现在的版本是1.0.0.0。

第三步,关闭应用程序,回到VS,我们把刚才的程序改一下,来模拟升级。比如我在窗口上再加一个按钮,如下图。

第四步,同样,打开项目属性中的"发布"选项卡,再发布一次。

我们看到修订版本号变成1了。

然后直击点击下面的"立即发布"按钮即可。

第五步,发布成功后,从"开始"中再次运行刚才安装的应用程序,会看到提示更新的对话框。

点击确定,就会自动下载并安装更新。这时候我们看到版本号是1.0.0.1了。

(3):使用证书

在讲述证书的使用前,我们先来了解另外一个知识——发布网页。

在前面所说的ClickOnce部署中,如果大家细心的话,应该会发现这么个问题。

如上图,发布成功后,在"输出"窗口中提示无法查看发布网页。

好,我们先不管那是什么,现在我们不妨发布一个项目,但在"项目属性"窗口中的"发布"选项卡上,点击"选项"按钮,打开"发布选项"对话框。

在"说明"页中输入基本信息,产品名称和发布者。

然后进入到"部署"页,在部署网页处输入一个页面名称,最好用index.htm或default.htm之类的名字,因为IIS等服务器的默认页面通常是这些。并且勾选"每次发布后都自动生成部署网页"项。

单击确定回到发布页,然后我们点击一下"立即发布",仔细观察。这一次我们发现,"输出"窗口不再提示无法打开部署页面了,而且发布完成后打开了一个页面,就是我们刚才输入的发布网页,是VS为我们自动生成的。

我们可以用VS或其他网页编辑工具来修改这个页面,一旦我们修改后,记得回到项目属性中的发布页,再次打开发布选项对话框,去掉"每一次发布后自动生成部署网页"项前面的对勾,不然,你所修改的页面在下一次发布后会被自动生成的页面所覆盖。

好,这段内容就说到这儿。下面开始我们的正题。

大家还记得这个界面不?

我们在安装ClickOnce应用程序的时候,都会弹出这个安全警告,虽然点击"安装"就可以开始安装,但是,这就变成不是ClickOnce了,而是ClickTwice了,是吧,我们至少点击了两下。

这是由于没有识别到受信任的证书造成的。下面我们来看看如何自己来做证书。

其实VS在发布ClickOnce部署时是为我们生成了一个临时证书的,但这样不太好控制,我们希望自己来做一个符合我们要求的证书,至少在证书的过期日期上我们可以自己来定。对于我们小开发者或小团队来说,到证书机构购买证书似乎有点"装逼"了,反正证书就是用来标识我们发布者身份,确保我们的应用安装包在传播过程中不被修改,我们倒不如自己弄一个证书更简便。

在项目属性窗口中,我们切换到"签名"选项卡,这里看到VS生成的临时证书。

我们来自己做个证书吧,自己给自己颁发证书,非常有荣誉感的。要完成这件事,我们需要用到几个命令行工具,其实大家网上搜一下,也是有很多相关文章的,这里我也顺便给大家演示一遍。

a、首先粉墨登场的是makecert工具,大家看看它的名字就知道它的长相,有点帅,作用当然是创建一个.cer文件,即证书文件。至于是啥类型的证书,纯属理论课,大家维基百科一下就有了。

打开Visual Studio命令行窗口,不要告诉我你找不到,然后我们最好用CD命令修改一下当前目录,我们希望把创建的文件放到哪里就定位到哪里,我呢计划把这些荣誉证书文件放到C:\Users\Admin下,即我的个人目录。输入以下命令:

cd %USERPROFILE%\证书

这样,第一步也完成了,看:

b、用makecert命令生成一个证书文件my.cer,并且附带一个密钥my.pvk。输入以下命令:

makecert –r –n "CN=老周" –b 10/08/2013 –e 08/11/2016 –sv my.pvk my.cer

按回车后,提示创建密码,输入密码,你喜欢,为了好玩,我这里用123456789作为密码。

点击OK按钮,然后又弹出一个窗口,注意,这个和上面那个不同,上面那个是创建密码,下面这个是用刚才创建的密码来创建证书。刚才我设置了123456789,所以这里还是要输入123456789,不要输错密码,不然要从头再来了。

点击OK按钮,好,证书和私钥文件就创建完成了。就是这两个文件:

解释一下,-r参数表示自签名,说白了就是自己给自书颁发证书,一定要加上-r,-n是证书的名字,通常用你的大名或者你公司的名字,以CN开头,格式为CN=<your name>,比如我叫老周(本来收小周,就因为叫小周的人太多,所以我叫老周),就用CN=老周。

-b是证书有效期的开始日期,格式mm/dd/yyyy,注意,别写错,哪怕你是1月份也不要写成1,应该写成01;-e是证书有效期的过期日期,格式和前面一样。

-sv是创建密钥文件,文件名为*.pvk,-sv要加上,后面你会发现,有用的。

最后就是证书文件的名字,文件名为*.cer。

c、证书是生成了,但你会看到VS要的.pfx文件,不是.cer文件,所以我们必须转换,但是没有工具可以一部到位,所以,我们依次用两个工具来转换。

先是用cert2spc,把.cer文件转换为.spc文件,不要问为什么,到了后面一步你就知道为什么要转为.spc文件了。继续在命令行中输入:

Cert2spc my.cer my.spc

完成后我们看到又多了一个文件:

我们离真相越来越近了,还剩最后一个,我们的.pfx证书就要完工了。最后一步就是把.pvk文件转为.pfx文件。所以用pvk2pfx,继续在命令行中输入以下命令:

pvk2pfx -pvk my.pvk -spc my.spc -pfx my.pfx -pi 123456789 –po 123456789 –f

这个工具比较复杂,-pvk指定刚才生成的.pvk文件;-spc就是刚才生成的.spc文件;-pfx是要生成的.pfx文件的名字,-pi是.pvk文件的密码,即我们前面创建的123456789,-po是生成的.pfx文件的密码,你可以重新设,我这里还是用123456789。

pfx弄好了,现在我们把它放进VS中。VS解决方案中,可以把<项目名>_TemporaryKey.pfx删了,那个是VS生成的临时证书,现在我们有自己的证书了,临时证书就没用了,我们可以送它去见列宁了。

打开项目属性窗口,切换到"签名"选项卡,点击"从文件选取"按钮,导入刚才生成的.pfx证书。

然后输入密码,密码就是上面执行pvk2pfx命令时-po参数指定的密码,我仍使用了123456789。

OK,证书就替换了。

你只要把刚才的.cer文件(即证书)发给客户,客户在安装你的ClickOnce包之前先把证书安装到他的计算中的可信任区域中,然后再去安装你的应用,就不会有安全提示了。以后你发布的应用都用这个证书就行了,不过,注意证书不要过期了,过期了就重新生成一个日期较新的。

在客户的机子上,打开控制面板,搜索"证书",然后打开基于计算机的证书管理器,注意不是基于当前用户。

打开证书管理,在"受信任的根证书颁发机构"节上右击,从菜中选择导入。

在"受信任的发布者"节上的也用同样的方法导入一次。随后我们再发布一次ClickOnce部署包,现在就真正ClickOnce了,一点击就全自动安装了,没有安全提示了。

(4):下载多个安装包

有时候,我们可能会一次性发布多个安装包,当然在网页上多加几个链接让用户逐个安装也是可取的。不过,也可以弄得更方便些,即用户先安装一个,作为一个"引导程序",然后通过这个程序去下载安装其他应用程序。

我们还是说说真实的例子,这样好理解一点。假设我开发了两个应用程序,一个叫App1,另一个叫App2。我把这两个应用程序同时发布。

我们可以在IIS服务器的根目录下新建两子目录,我们分别把这两个应有程序发布到这两个文件夹下,如下面两张截图所示。

第一个应用

第二个应用

最后,重点是如何做这个"引导"安装包,即我们通过这个应用程序来下载前面两个应用来安装。

别急,在开工之前,我想让大家背书,不多,小学生也能背下来的,总共就两句话:

第一句:凡是和ClickOnce部署有关的类都位于System.Deployment.Application命名空间下。

第二句:开菜单——做菜——试菜——开饭。

第一句话就不用解释了,如果你不明白说明你还没入门。我们把重点放在第二句话,这句话可能大家觉得很搞笑,怎么好像在做饭啊? 是啊,"民以食为天",做饭很重要。

这里说的是做这个"引导"安装应用的步骤,我们要用到InPlaceHostingManager类,它可以实现实时下载安装应用程序包,不要问我这个类在哪里,前面叫你背了第一句话。

InPlaceHostingManager类使 用按以下几步。

第一、new一个InPlaceHostingManager对象,这是废话。构造函数中我们要传 一个URI,部署清单的URI,打包ClickOnce后,那个以.application后缀结尾的文件就是。

第二、开菜单。调用GetManifestAsync方法获取清单,获取后,无论成功与否会引发GetManifestCompleted事件,所以在调用方法前要为GetManifestCompleted事件附加处理代码,这是基础知识,估计我不必解释了。

第三、做菜。调用AssertApplicationRequirements方法检查一下你有没有这个权限,要明白自己是吃几碗饭的,如果权限不够会引发异常,如果力所能及,方法调用后一切正常。一个bool类型的参数表示当权限不足时是否尝试提升,你懂的。

如果权限检查通过,调用DownloadApplicationAsync方法就可以下载应用程序了,在这过程中会引发DownloadProgressChanged和DownloadApplicationCompleted,这两个事件我不说了,你知识怎么处理了,和使用BackgroundWorker一样,如果你不懂,请回家好好细读《C#入门经典》。

第四、试菜。打开"开始"菜单或"开始"屏幕,看看有没有新安装应用的快捷方式,如果有,那就下载安装成功了。

界面是包括两个Label和两个ProgressBar,标签用来显示文本,进度条当然表示下载的进度。

然后处理Form的Load事件,启动对App1和App2项目的安装包的下载和安装。

public partial class Form1 : Form

{

InPlaceHostingManager appDown1 = null;

InPlaceHostingManager appDown2 = null;

// uris

Uri app1Uri, app2Uri;

public Form1()

{

InitializeComponent();

app1Uri = new Uri(AppMain.Properties.Settings.Default.app1);

app2Uri = new Uri(AppMain.Properties.Settings.Default.app2);

appDown1 = new InPlaceHostingManager(app1Uri, false);

appDown2 = new InPlaceHostingManager(app2Uri, false);

}

private void Form1_Load(object sender, EventArgs e)

{

// 开始下载第一个应用程序

appDown1.GetManifestCompleted += appDown1_GetManifestCompleted;

appDown1.GetManifestAsync();

// 开始下载第二个应用程序

appDown2.GetManifestCompleted += appDown2_GetManifestCompleted;

appDown2.GetManifestAsync();

}

void appDown2_GetManifestCompleted(object sender, GetManifestCompletedEventArgs e)

{

if (e.Error != null)

{

MessageBox.Show(e.Error.Message); return;

}

lblSecond.Text = "即将下载" + e.ProductName + "," + e.Version.ToString();

try

{

appDown2.AssertApplicationRequirements(true);

}

catch (Exception ex)

{

MessageBox.Show(ex.Message); return;

}

appDown2.DownloadProgressChanged += appDown2_DownloadProgressChanged;

appDown2.DownloadApplicationCompleted += appDown2_DownloadApplicationCompleted;

appDown2.DownloadApplicationAsync();

}

void appDown2_DownloadApplicationCompleted(object sender, DownloadApplicationCompletedEventArgs e)

{

lblSecond.Text = "下载完成。";

}

void appDown2_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)

{

this.progressbarSecond.Value = e.ProgressPercentage;

this.lblSecond.Text = "已下载" + e.BytesDownloaded.ToString() + "字节,共" + e.TotalBytesToDownload.ToString() + "字节。";

}

void appDown1_GetManifestCompleted(object sender, GetManifestCompletedEventArgs e)

{

if (e.Error != null)

{

MessageBox.Show("出错:" + e.Error.Message); return;

}

this.lblFirst.Text = "即将下载" + e.ProductName + "," + e.Version.ToString();

try

{

appDown1.AssertApplicationRequirements(true);

}

catch (Exception ex)

{

MessageBox.Show(ex.Message);

return;

}

appDown1.DownloadProgressChanged += appDown1_DownloadProgressChanged;

appDown1.DownloadApplicationCompleted += appDown1_DownloadApplicationCompleted;

appDown1.DownloadApplicationAsync();

}

void appDown1_DownloadApplicationCompleted(object sender, DownloadApplicationCompletedEventArgs e)

{

lblFirst.Text = "下载完成。";

}

void appDown1_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)

{

this.progressbarFirst.Value = e.ProgressPercentage;

this.lblFirst.Text = "已下载" + e.BytesDownloaded.ToString() + "字节,共" + e.TotalBytesToDownload.ToString() + "字节。";

}

}

而应用安装包的下载地址,我们放到配置文件中,以便修改。

打开项目属性窗口,切换到"设置"选项卡,然后输入App1的下载地址是http://localhost/App1/App1.application;App2的下载地址是http://localhost/App2/App2.application

VS会为我们生成相应的属性,如上面代码中,我们通过当前应用程序所在的程序集中的.Properties.Settings.Default.app1就可以得到app1的值。

两个应用项目,加上这个用来安装其他应用包的程序,共三个,把它们都发布到指定的路径下。

用来引导的应用就放到根目录,用户只需安装这个就可以了,然后利用这个应用程序来安装其他两个。

App1放到/App1下。

App2放到/App2下。

发布完成后,IIS的根目录如下。

然后安装根目录下的"引导"包。安装后运行应用程序,就会自动下载另外两个安装包并自动安装。

安装完成了,然后打开"开始"菜单或"开始"屏幕,就能看到三个应用程序了。

ClickOnce部署的更多相关文章

  1. ClickOnce部署(5):自定义安全权限

    今天我们来探讨一下在ClickOnce部署中如何严格控制应用程序的权限. 演示应用 为了在下文中能更好地演示,我们先要做一个测试项目.也为了显得简单易懂,我使用最常用且最常见的WinForm项目,这是 ...

  2. ClickOnce部署(4):下载多个安装包

    有时候,我们可能会一次性发布多个安装包,当然在网页上多加几个链接让用户逐个安装也是可取的.不过,也可以弄得更方便些,即用户先安装一个,作为一个"引导程序",然后通过这个程序去下载安 ...

  3. ClickOnce部署(3):使用证书

    在讲述证书的使用前,我们先来了解另外一个知识——发布网页. 在前面所说的ClickOnce部署中,如果大家细心的话,应该会发现这么个问题. 如上图,发布成功后,在"输出"窗口中提示 ...

  4. 获取使用ClickOnce部署的应用程序的版本号

    引子 在编写使用ClickOnce部署的应用程序时,需要在程序的标题栏.软件变更记录.软件关于等页面读取显示当前的版本号. 之前很傻瓜的做法就是在Resource中维护一个string值,在使用到的地 ...

  5. 以管理员身份启动ClickOnce部署的应用程序

    ClickOnce方式部署应用简单方便,估计很多人都用过,但这种方式存在一定的“缺陷”,即以管理员方式启动应用的问题,虽然出于安全考虑可以理解,但给需要管理员权限才能正常运行的程序带来了一定的麻烦,这 ...

  6. 为通过ClickOnce部署的应用程序进行数字签名

    为通过ClickOnce部署的应用程序进行数字签名 ClickOnce是.NET用于Windows应用程序的一种便捷部署方式.不过由于便捷,导致缺少自定义操作的空间.比如需要对通过ClickOnce部 ...

  7. ClickOnce部署疑难杂症:更新时部署与应用程序标识不一致问题。要安装此应用程序,请修改此文件的清单版本或卸载之前存在的应用程序。

    使用ClickOnce部署winform应用程序.无论是安装或者自动更新都极为方便,但有时候一些疑难杂症也令人头疼 1.注意每次部署完成之后 setup.exe无需覆盖,只需要在Application ...

  8. C# ClickOnce部署WinForm程序

    之前做过ClickOnce部署应用程序的项目,今天做一次全面的总结.那么这些都是微软提供方便分布式部署的相关解决方法,这种方法既有弊端,也有优点. 最大的缺点: 远程部署,不能更换安装目录:并且每次安 ...

  9. 转:ClickOnce部署Winform程序的方方面面

    1. ClickOnce简介 微软官方对ClickOnce的解释是:ClickOnce 是一项部署技术,您可以利用这项技术来创建基于 Windows 的自行更新的应用程序,并且安装和运行这类应用程序所 ...

随机推荐

  1. python+Eclipse+pydev环境搭建

    python+Eclipse+pydev环境搭建   本文重点介绍使用Eclipse+pydev插件来写Python代码,  以及在Mac上配置Eclipse+Pydev 和Windows配置Ecli ...

  2. hihocoder挑战赛13A

    #1191 : 小W与网格 描述 给定一个n*m的网格,左上角(1, 1),右下角(n, m). 小w在(i, j),他会从"上左下右"四个方向中选定两个不同但正交的方向,然后他只 ...

  3. XV Open Cup named after E.V. Pankratiev. GP of Tatarstan

    A. Survival Route 留坑. B. Dispersed parentheses $f[i][j][k]$表示长度为$i$,未匹配的左括号数为$j$,最多的未匹配左括号数为$k$的方案数. ...

  4. XCode6.3上使用opencv教程(MacOSX 10.10)

    OpenCV 是一个基于(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows和Mac OS操作系统上.它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Pyth ...

  5. bootstrap之google fonts

    bootstrap之google fonts 在学习一个bootstrap时,看到了一行引用代码:@import url(http://fonts.googleapis.com/css?family= ...

  6. Where product development should start

    We all need to know our customers in order to create products they’ll actually buy. This is why the  ...

  7. socket.io安装部署

    需要node.js环境 创建package.json npm init 下载相关依赖 npm install --save express@4.10.2npm会在当前目录下载所需要的依赖到node_m ...

  8. Jquery、简单的下拉列表、网页左部导航菜单

    简单的下拉菜单.左部导航使用. 2016-5-13 记 效果图如下: <!DOCTYPE html> <html lang="en"> <head&g ...

  9. 享元模式 - Flyweight

    Flyweight(享元模式) 定义 GOF:运用共享技术有效地支持大量细粒度的对象. GOF的定义比较专业化,通俗来说,当你有大量相似的实例时,你把其中相同的实例取出来共享. 例子 在你的游戏场景中 ...

  10. 禁用nested loop join里的spool

    禁用nested loop join里的spool 转载自: https://blogs.msdn.microsoft.com/psssql/2015/12/15/spool-operator-and ...