在图表页上显示测量值
如果您有 Visio 标准版,可以通过使用字段,链接到形状的高度和宽度属性来显示形状的尺寸。有关使用域的详细信息,请参阅使用字段来保持当前显示的信息。
如果您有 Visio 专业版,您可以使用尺寸形状以显示大小。尺寸形状显示他们正在粘附到,并且原始形状调整文字时自动更新该形状的大小。
尺寸形状包含在一些模板内,并可以在任何模板打开尺寸度量模具。
若要打开尺寸度量模具
在“形状”窗口中,单击“更多形状”。
指向其他 Visio 方案,,然后单击尺寸度量-建筑或尺寸度量-工程。
将尺寸形状拖动到要测量的形状,然后将尺寸线形状的端点粘附到要测量的点。
注意: 若要更改度量单位,右键单击尺寸形状,然后单击快捷菜单上的精度和单位。
示例
如果您有 Visio 专业版,请按下列步骤将指导您如何使用尺寸形状的示例。
使用尺寸形状 (示例)
单击文件选项卡,单击新建,单击类别,单击地图和平面布置图,然后双击家居规划。
单击庭园附属设施模具的标题栏和混凝土通道形状拖到绘图页。
您可能需要滚动以找到该模具或其他人的模具下的列表。
拖动手柄以扩大此混凝土通道形状上。
单击尺寸度量-建筑模具的标题栏并将水平形状拖到绘图页上通道形状上方。
如果看不到此列表中的模具,请按照上述过程,以打开尺寸度量模具中的说明进行操作。
请将形状手柄右侧的水平尺寸形状拖到通道形状的右边缘,直至绿色框中显示指示形状粘附在一起。
将水平形状左侧的形状手柄拖到通道形状的左边缘。
拖动黄色菱形 ( ),拖移到通道上方的位置右侧以便尺寸线不会遮住通道形状。
本文未完成,部分测试方法、条件或结果可能有误,请谨慎参考! :)
本文基于 MySQL 的 InnoDB BTREE 方法的索引进行测试。
以一张包含 2000 万条记录的表做实验:
CREATE TABLE `dt_read` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime(0) NOT NULL,
`a_id` int(11) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
);
这张表是用于记录文章点击量的,
`id` 为主键,int(11) 自增;
`time` 为非空 datetime,表示文章打开时间,测试数据是从 2017-03-11 至 2018-04-28;
`a_id` 为非空 int(11),表示文章 ID,在此表中不唯一,测试数据是从 1 至 260218。
体验“全表扫描”
首先来体验一下什么是全表扫描,执行下面语句:
SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' LIMIT 10
> 时间: 0.012s
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' LIMIT 10
> 时间: 7.317s
表中数据是按主键从小到大排列的,当查询条件为 `time` < '2020-1-1' 时,能很快地从表的前端找到 10 条满足条件的数据,所以不再继续判断后面的记录,立刻返回结果,耗时 0.012 秒;但当条件改为 `time` < '2000-1-1' 时,同样逐条判断,直到最后一条也没有找到,这种情况就是所谓的“全表扫描”,耗时 7 秒。
索引对 ORDER BY 的 ASC 和 DESC 的影响
我们给 `time` 建一个索引,同样执行刚才需要全表扫描的语句:
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' LIMIT 10
> 时间: 0.012s
创建 `time` 的索引后,相当于生成了一张按 `time` 字段排列的新表,这时 MySQL 就能够很快地定位并找到符合条件的记录,避免了全表扫描。
试试按 `time` 倒序排:
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `time` DESC LIMIT 10
> 时间: 0.013s
结论:索引对 ORDER BY 的顺序(ASC)和倒序(DESC)都是有效的。
索引字段的次序对 WHERE 和 ORDER BY 的影响
删除所有索引,创建一个新的索引,字段依次为 `time`, `a_id`。
分别执行以下查询:
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' AND `a_id` < 0 LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` WHERE `a_id` < 0 AND `time` < '2000-1-1' LIMIT 10
> 时间: 0.013s
结论:MySQL 会自动优化 WHERE 条件的次序来匹配最合适的索引。
但在 ORDER BY 中却不是这么回事了:
SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` ORDER BY `a_id`, `time` LIMIT 10
> 时间: 14.066s
原因也很好理解,对两个字段进行排序,先后次序肯定会影响结果集,因此只能以 SQL 语句指定的字段次序来 ORDER BY,这样,按索引的字段次序进行 ORDER BY 查询无疑是更快的。
索引中的字段必须依次使用
保持上例创建的索引不变,即 `time`, `a_id`。
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' AND `a_id` < 0 LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` WHERE `a_id` < 0 LIMIT 10
> 时间: 6.438s
上句合理利用了索引的字段,而下句跳过了 `time`,直接 WHERE 了 `a_id`,这是不受该索引支持的。
我们可以想象一下这张由索引生成的虚拟表,其实就是一张普通的平面二维表格,按索引指定的字段次序进行了排序,所以全表中仅仅是索引指定的第一个字段是按大小排列的,第二个字段是在第一个字段值相同的区域内按大小排列,后同。所以,跳过索引指定的第一个字段直接对第二个字段进行检索,是无法应用该索引的。这个结论也同样也体现在 ORDER BY 语句中:
SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` ORDER BY `a_id` LIMIT 10
> 时间: 29.566s
WHERE 和 ORDER BY 混合
保持上例创建的索引不变,即 `time`, `a_id`。
先来执行这两句:
SELECT * FROM `dt_read` ORDER BY `a_id` LIMIT 10
> 时间: 12.29s
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 0.013s
仅仅 WHERE 了一个 `time`,对 ORDER BY `a_id` 的效率却有质的提升,是因为 WHERE 中的 `time` 和 ORDER BY 中的 `a_id` 一起找到了索引吗?答案是否定的。
我们把时间改大,让它能马上找到符合条件的数据:
SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 22.34s
为什么这个语句就不走索引了呢?
其实,一个简单的 SELECT 查询语句,首先执行 WHERE,然后 ORDER BY,最后是 LIMIT。每一步都独自去找了索引,而非 WHERE 和 ORDER BY 混在一起去找索引。必须保证每一步是快的,最终才是快的。
当 `time` < '2000-1-1' 时,WHERE 用到了索引,所以很快,ORDER BY 却没有用到索引,但为什么也很快呢?因为 WHERE 的结果集非常小(示例中为 0 条)。
当 `time` < '2020-1-1' 时,WHERE 也用到了索引,但其结果集非常大(示例中为所有记录),再 ORDER BY `a_id` 就非常慢了,因为我们没有创建以 `a_id` 开头的索引。
现在把索引改成只有 `time` 一个字段。
SELECT * FROM `dt_read` WHERE `time` < '2020-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 6.033s
因为索引里有 `
SELECT * FROM `dt_read` WHERE `time` < '2000-1-1' ORDER BY `a_id` LIMIT 10
> 时间: 0.013s
SELECT * FROM `dt_read` WHERE `a_id` < 0 ORDER BY `time` LIMIT 10
> 时间: 6.033s
第二句先 WHERE `a_id`,后 ORDER BY `time` 是不能匹配所建的索引的。
索引中的字段越多越好
分别在创建索引(`time`)和索引(`time`, `a_id`)的情况下执行下面语句:
本例使用 ORDER BY 而不是 WHERE 来测试是因为,在 WHERE 的多个条件下,如果符合前一条件的筛选结果集过小会导致判断第二条件时数据量不足,无法判断索引是否起作用。
SELECT * FROM `dt_read` ORDER BY `time` LIMIT 10
仅创建索引(`time`)的情况下:
> 时间: 0.013s
仅创建索引(`time`, `a_id`)的情况下:
> 时间: 0.013s
SELECT * FROM `dt_read` ORDER BY `time`, `a_id` LIMIT 10
仅创建索引(`time`)的情况下:
> 时间: 15.015s
仅创建索引(`time`, `a_id`)的情况下:
> 时间: 0.014s
可以看到,在索引字段依次使用的前提下,索引字段数不少于查询字段数才能避免全表扫描。
虽然索引中的字段越多越好,但必须依次使用,否则也是无效索引。
索引对 INSERT / UPDATE / DELETE 的效率影响
分别在创建索引(`time`)和索引(`time`, `a_id`)的情况下执行下面语句:
INSERT INTO `dt_read` (`time`, `a_id`) VALUES ('2018-4-28', 260218)
不建索引的情况下:
> 时间: 0.01s
仅创建索引(`time`)的情况下:
> 时间: 0.01s
同时创建索引(`time`)和索引(`time`, `a_id`)的情况下:
> 时间: 0.01s
UPDATE `dt_read` SET `time` = '2018-4-28' WHERE `id` = 20000000(注:存在该 id 值的记录)
不建索引的情况下:
> 时间: 0.01s
仅创建索引(`time`)的情况下:
> 时间: 0.01s
同时创建索引(`time`)和索引(`time`, `a_id`)的情况下:
> 时间: 0.01s
虽然在 INSERT / UPDATE / DELETE 时数据库会更新索引,但从实测数据来看,索引对其效率的影响可忽略不计。
一些误区
“in 语法效率很低”?
in 语法也是应用索引的,网传 in 会比一个一个 WHERE OR 要慢得多的说法是不靠谱的。in 主键和 in 索引同理。
另外:
对于字符串类型,LIKE '%abc%' 是不能应用索引的,但 LIKE 'abc%' 可以。更多关于字符串类型的索引,请查阅全文索引(FULLTEXT)。
索引的字段是可以指定长度的,类似字符串索引指定前面若干唯一字符就可以优化效率。
本文系个人实践总结,欢迎批评指正!
官网示例 | 国内示例 | ||
Metronic | 收费 | 最新 | |
Unify | 收费 | ||
Angulr | 收费 | Angular HTML | 本站 Angular v2.2.0 本站 HTML v2.2.0 |
AdminLTE | 免费开源 | 最新 | 本站 v2.4.2 |
Color Admin | 收费 | 最新 | |
更多 …… |
浏览示例前,将以下域名的重定向加入到 hosts 中可以加快页面的打开速度:
127.0.0.1 fonts.googleapis.com
127.0.0.1 ajax.googleapis.com
127.0.0.1 player.vimeo.com
127.0.0.1 www.vimeo.com
函数功能:添加/修改/删除/获取 URL 中的参数(锚点敏感)
/// <summary>
/// 设置 URL 参数(设 value 为 undefined 或 null 删除该参数)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// <param name="value">参数值</param>
/// </summary>
function fn_set_url_param(url, key, value) {
// 第一步:分离锚点
var anchor = "";
var anchor_index = url.indexOf("#");
if (anchor_index >= 0) {
anchor = url.substr(anchor_index);
url = url.substr(0, anchor_index);
}
// 第二步:删除参数
key = encodeURIComponent(key);
var patt;
// &key=value
patt = new RegExp("\\&" + key + "=[^&]*", "gi");
url = url.replace(patt, "");
// ?key=value&okey=value -> ?okey=value
patt = new RegExp("\\?" + key + "=[^&]*\\&", "gi");
url = url.replace(patt, "?");
// ?key=value
patt = new RegExp("\\?" + key + "=[^&]*$", "gi");
url = url.replace(patt, "");
// 第三步:追加参数
if (value != undefined && value != null) {
value = encodeURIComponent(value);
url += (url.indexOf("?") >= 0 ? "&" : "?") + key + "=" + value;
}
// 第四步:连接锚点
url += anchor;
return url;
}
/// <summary>
/// 获取 URL 参数(无此参数返回 null,多次匹配使用“,”连接)
/// <param name="url">URL</param>
/// <param name="key">参数名</param>
/// </summary>
function fn_get_url_param(url, key) {
key = encodeURIComponent(key);
var patt = new RegExp("[?&]" + key + "=([^&#]*)", "gi");
var values = [];
while ((result = patt.exec(url)) != null) {
values.push(decodeURIComponent(result[1]));
}
if (values.length > 0) {
return values.join(",");
} else {
return null;
}
}
调用示例:
var url = window.location.href;
// 设置参数
url = fn_set_url_param(url, 'a', 123);
// 获取参数
console.log(fn_get_url_param, url, 'a');
压缩版:
function fn_set_url_param(b,c,e){var a="";var f=b.indexOf("#");if(f>=0){a=b.substr(f);b=b.substr(0,f)}c=encodeURIComponent(c);var d;d=new RegExp("\\&"+c+"=[^&]*","gi");b=b.replace(d,"");d=new RegExp("\\?"+c+"=[^&]*\\&","gi");b=b.replace(d,"?");d=new RegExp("\\?"+c+"=[^&]*$","gi");b=b.replace(d,"");if(e!=undefined&&e!=null){e=encodeURIComponent(e);b+=(b.indexOf("?")>=0?"&":"?")+c+"="+e}b+=a;return b}
function fn_get_url_param(b,c){c=encodeURIComponent(c);var d=new RegExp("[?&]"+c+"=([^&#]*)","gi");var a=[];while((result=d.exec(b))!=null){a.push(decodeURIComponent(result[1]))}if(a.length>0){return a.join(",")}else{return null}};
在网站开发过程中遇到上传图片等文件的功能,需要在服务器上设置该目录可写入,并且必须防止被上传 .php 等可执行的脚本文件。
根据文件所有者的不同,我们分两种情况讨论:
一、程序上传的用户与执行 PHP 的用户不同(例如程序代码通过 SSH 上传(root 用户),而 PHP 以 www 身份运行)
由于 Linux 上的文件默认权限是 644,目录默认权限是 755,所以在这种情况下,PHP 不能修改 root 上传的程序文件,也不能将客户端上传的图片文件保存到服务器上。
例如我们将客户端上传的文件指定到 /upload/ 目录,那么,
第 1 步,赋予它写入权限:
chmod 777 upload
递归所有子目录请加 -R 参数,但会将文件也更改为 777,赋予了执行权限,这是不建议的。文件若需写入权限请设为 666。
第 2 步,设置该目录下不允许执行 PHP:
我们无法保证客户端上传完全符合我们要求的文件,那么必须禁止任何文件的执行权限。(此处指 PHP 的执行权限,注意与 sh 执行权限的区别,后者由 chmod 命令修改)
nginx 的 .conf 文件配置示例:
location ~ /upload/.*\.(php|php5)?$
{
deny all;
}
Apache 的 .htaccess 文件配置示例:
RewriteRule upload/(.*).(php)$ – [F]
特别注意:上面的代码必须加在 PHP 引用配置的上方才有效(如 nginx 的 PHP 引用配置 include enable-php-**.conf;)
默认 Linux 系统是大小写敏感的,所以规则中的正则表达式无须忽略大小写(但必须与实际的目录或文件名大小写一致),因为 MIME 类型中设置的是小写的 .php,那么类似大写的 .PHP 文件是不被 php-fpm 解释的,结果是被当作普通文本返回到客户端。
提问:有些目录需要设置为可写入,但里面还有正常的 php 文件,禁止执行会有问题吗?(例如 Discuz! 的 config 目录,ThinkPHP 的 Runtime 目录)
解答:以 Discuz! 为例,config 下面虽然有 config_*.php 等配置文件,但这些文件并非直接供客户端浏览,而是被其它 .php 文件引用(include 方式、IO 方式等),当 config 目录禁止执行 PHP 后并不会对它们造成影响。
我们在开发程序的过程中应避免在允许写入的目录下放置直接供客户端浏览的 .php 文件。
二、程序上传的用户与执行 PHP 的用户相同(例如程序代码通过 FTP 上传,且和 PHP 一样,都使用 www 用户)
常见于虚拟空间。这种情况下,通过 PHP 上传的文件可以保存到该网站下面的任何目录下(甚至是网站目录之外,即传说中的跨站),原因大家都懂,默认 755 中第一个是“7”,即所有者拥有写入权限。
因此,除了做到上述设置,我们必须严格控制客户端上传文件的保存路径,做到以下几点:
必须更改客户端上传的文件名,而非直接使用原文件名;
不得从客户端文件名获取扩展名,或者只控制允许的后缀名。
最可靠的文件类型判别方式是分析文件签名,参考:文件签名表,以防篡改后缀名欺骗。
举个早期的栗子:上传文件名为 abc.asp;.jpg,未改文件名保存到服务器,浏览器直接访问该文件,IIS 6 当作 abc.asp 来解析。
获取
在 NuGet 中搜索 ZXing.Net
简单示例
var qr = new ZXing.QrCode.QRCodeWriter();
var matrix = qr.encode("http://xoyozo.net/", ZXing.BarcodeFormat.QR_CODE, 200, 200);
var writer = new ZXing.BarcodeWriter()
{
Format = ZXing.BarcodeFormat.QR_CODE
};
Bitmap bitmap = writer.Write(matrix);
扩展示例
string content = "http://xoyozo.net/";
var hints = new Dictionary<ZXing.EncodeHintType, object>();
hints.Add(ZXing.EncodeHintType.ERROR_CORRECTION, ZXing.QrCode.Internal.ErrorCorrectionLevel.H); // 纠错级别
hints.Add(ZXing.EncodeHintType.CHARACTER_SET, Encoding.Default.WebName); // 编码:gb2312
hints.Add(ZXing.EncodeHintType.MARGIN, 0); // 出血码元数(标准为 4,美观为 2)
var qr = new ZXing.QrCode.QRCodeWriter();
var matrix = qr.encode(content, ZXing.BarcodeFormat.QR_CODE, 200, 200, hints);
var writer = new ZXing.BarcodeWriter()
{
Format = ZXing.BarcodeFormat.QR_CODE,
Renderer = new ZXing.Rendering.BitmapRenderer
{
Foreground = Color.Black, // 前景色(默认黑色)
Background = Color.White, // 背景色(默认白色)
},
};
Bitmap bitmap = writer.Write(matrix);
将 Bitmap 写入到流
Stream stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
将 Bitmap 保存到磁盘
string path = "D:\wwwroot\upload\abc.png";
bitmap.Save(path, ImageFormat.Png);
更多
对比 ThoughtWorks.QRCode 和 ZXing.Net
获取
在 NuGet 中搜索 ThoughtWorks.QRCode
简单示例
var qr = new ThoughtWorks.QRCode.Codec.QRCodeEncoder();
Bitmap bitmap = qr.Encode("http://xoyozo.net/");
扩展示例
string content = "http://xoyozo.net/";
var qr = new ThoughtWorks.QRCode.Codec.QRCodeEncoder
{
// 纠错级别,L (7%)、M (15%)、Q (25%)、H (30%)
QRCodeErrorCorrect = ThoughtWorks.QRCode.Codec.QRCodeEncoder.ERROR_CORRECTION.H,
// 码元尺寸(像素)
QRCodeScale = 4,
// 前景色(默认黑色)
QRCodeForegroundColor = Color.Black,
// 背景色(默认白色)
QRCodeBackgroundColor = Color.White,
};
// 根据内容确定 Mode,参:http://en.wikipedia.org/wiki/QR_code#Storage
if (Regex.IsMatch(content, @"^\d+$"))
{
qr.QRCodeEncodeMode = ThoughtWorks.QRCode.Codec.QRCodeEncoder.ENCODE_MODE.NUMERIC;
}
else if (Regex.IsMatch(content, @"^[0-9A-Z $%*+-./:]+$"))
{
qr.QRCodeEncodeMode = ThoughtWorks.QRCode.Codec.QRCodeEncoder.ENCODE_MODE.ALPHA_NUMERIC;
}
else
{
qr.QRCodeEncodeMode = ThoughtWorks.QRCode.Codec.QRCodeEncoder.ENCODE_MODE.BYTE;
}
Bitmap bitmap = qr.Encode(content, Encoding.Default); // 编码,简体中文系统默认为 gb2312
裁切掉多余的 1 像素
ThoughtWorks.QRCode 生成的四周没有留白的二维码图片,其右边和下边分别会多出 1 像素,使用以下方法来调整图片大小
// 创建一个新的图片(宽度和高度各缩小 1 像素)
Bitmap bitmap2 = new Bitmap(bitmap.Size.Width - 1, bitmap.Size.Height - 1);
// 以新图片来绘图
Graphics g2 = Graphics.FromImage(bitmap2);
// 新旧图片绘制到新图片中(左上角对齐)
g2.DrawImage(bitmap, 0, 0);
将 Bitmap 写入到流
Stream stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
若已裁切 1 像素,请修改为 bitmap2
将 Bitmap 保存到磁盘
string path = "D:\wwwroot\upload\abc.png";
bitmap.Save(path, ImageFormat.Png);
若已裁切 1 像素,请修改为 bitmap2
更多
对比 ThoughtWorks.QRCode 和 ZXing.Net
jquery.barrager.js 是一款优雅的网页弹幕插件,支持显示图片,文字以及超链接。支持速度、高度、颜色、数量等自定义。
使用方法参官方网站:http://yaseng.org/jquery.barrager.js/
jquery.barrager.min.js 会报错,改用 jquery.barrager.js(版本 1.1)
实战示例:https://xoyozo.net/Demo/DanMu
该示例重写了部分 CSS 样式。
注:info 属性支持 HTML,因此要自己过滤危险标签,建议仅保留 <img /> 标签
如果要显示在指定区域,可以使用 bottom 属性,例:
bottom: (Math.random() * (0.95 - 0.5) + 0.5) * $(window).height()
建议加 loading 锁,以适应网络不通畅的环境(ajax 时间超过定时间隔会导致请求到重复的数据)
跨域环境可以使用 JSONP 来实现,服务器端需设置 Access-Control-Allow-Origin 为 *。
Session 有多种存储模式,默认是 InProc
,顾名思义是“在进程内”,即在 IIS 中,随网站重启而重启,网站发布后(或改动 .dll 文件)会导致应用重新运行,Session 清空。
Session 可以使用 StateServer
来保存,需要服务器开启 ASP.NET State Service 服务,此种方式要求保存在 Session 的信息必须序列化,然后从 Session 中获取的时候也要反序列化,这就导致性能有略微的损失。
Session 还可以配置为 SQLServer
,将 Session 存储在数据库中,同样需要序列化。
当配置为 Custom
时,可以在使用 Memcached、Redis 等第三方缓存技术来实现 Session 的管理,本文不作讨论。
Off
模式即关闭 Session,这种方式如果要实现用户登录功能,就必须依赖 Cookie 等来实现。
下面针对常见的三种模式进行比较:
InProc | StateServer | SQLServer | |
存储位置 | 应用进程内 | ASP.NET State Service 服务 | SQL Server 数据库 |
远程存储 | 不支持 | 支持 | 支持 |
存储格式 | 任意 | 序列化的 | 序列化的 |
可能导致重启(丢失)的情况 | 配置文件中 processModel 标签的 memoryLimit 属性; Global.asax 或者 Web.config 文件被更改; Bin 文件夹中的 Web 程序(DLL)被修改; 杀毒软件扫描了一些 .config 文件; 系统资源紧张进行资源回收导致 IIS 进程崩溃或重启等。 | ASP.NET State Service 服务停止; | - |
Session_End() 事件 | 有 | 无 | 无 |
部署难度 | 无 | 简单 | 稍复杂 |
Web.config 配置示例 (<system.web> 节点内) | <sessionState mode="InProc" /> | <sessionState mode="StateServer" /> | <sessionState mode="SQLServer" sqlConnectionString ="data source=x.x.x.x; user id=[user]; password=[pwd]" /> |
因此,如果你仅仅为了标题所述的“每次发布后丢失 Session”而烦恼,那么推荐使用 StateServer
模式来存储 Session。
注:
如果 Session 的 Key 是在应用启动时随机生成的话,使用 StateServer 和 SQLServer 还是会“丢失” Session。
这是对 Session 的配置,对 Cache 无效。
这个简单的技巧用来解决使固定表头(thead
)和滚动表体 (tbody
) 的问题。这使得数据表更易于浏览。当用户滚动表格时,固定表头为用户所注意的列提供了上下文。看下面图示你就明白了:
默认情况下,overflow
属性不适用于表格分组元素 thead
, tbody
, tfoot
。你可以在下面的示例中看到:
为了使其工作,
第一步是:设置 tbody
为 display:block
,以便我们可以应用 height
和 overflow
属性。
下一步将是:设置thead
中的 tr
元素设置为 display:block
。
所以最终的CSS会是:
.fixed_header tbody{
display:block;
overflow:auto;
height:200px;
width:100%;
}
.fixed_header thead tr{
display:block;
}
这样,创建表格非常简单而且富有语义,并且没有依赖 JavaScript 。