C# 封装miniblink 使用HTML/CSS/JS来构建.Net 应用程序界面和简易浏览器

时间:2021-09-04 13:26:28

MiniBlink的作者是 龙泉寺扫地僧

miniblink是什么?   (抄了一下 龙泉寺扫地僧 写的简洁)

Miniblink是一个全新的、追求极致小巧的浏览器内核项目,
其基于chromium最新版内核,去除了chromium所有多余的部件,只保留最基本的排版引擎blink。
Miniblink保持了10M左右的极简大小,是所有同类产品最小的体积,同时支持windows xp、npapi。

为什么要做miniblink?

市面上作为嵌入的组件的可用的浏览器内核,不外乎这几个:webkit、cef、nwjs、electron。

cef:优点是由于集成的chromium内核,所以对H5支持的很全,同时因为使用的人也多,各种教程、示例,资源很多。但缺点很明显,太大了。最新的cef已经夸张到了100多M,还要带一堆的文件。同时新的cef已经不支持xp了(chromium对应版本是M49)。而且由于是多进程架构,对资源的消耗也很夸张。如果只是想做个小软件,一坨文件需要带上、超大的安装包,显然不能忍受。

nwjs,或者最近大火的electron:和cef内核类似,都是chromium内核。缺点和cef一模一样。优点是由于可以使用nodejs的资源,同时又自带了各种api的绑定,所以可以用的周边资源非常丰富;而基于js的开发方案,使得前端很容易上手。所以最近N多项目都是基于nwjs或electron来实现。例如vscode,atom等等。

原版webkit:现在官网还在更新windows port,但显然漫不在心,而且最新的webkit也很大了,超过20几M。最关键的是,周边资源很少,几乎没人再基于webkit来做开发。同时由于windows版的saferi已经停止开发了,所以用webkit就用不了他的dev tools了。这是个大遗憾。

WKE:这是个很老的webkit内核的裁剪版了。小是小,但bug太多了。

那么关键点来了,使用miniblink有啥好处呢??

首先,miniblink对大小要求非常严格。原版chromium、blink里对排版渲染没啥大用的如音视频全都被砍了,只专注于网页的排版和渲染。甚至为了裁剪大小,我不惜使用vc6的crt来跑mininblink。这个也算前无古人后无来者了。

其次,miniblink紧跟最新chromium,这意味着chromium相关的资源都可以利用。在未来的规划里,我是打算把electron的接口也加上的,这样可以无缝替换electron。使用miniblink的话,开发调试时用原版electron,发布的时候再替换掉那些dll,直接可以无缝切换,非常方便。

miniblink如何裁剪到这么小?

这个比较复杂了。主要就是把blink从chromium抽离了出来,同时补上了cc层(硬件渲染层)。现在的blink,已经不是当年的那个webkit了,渲染部分全走cc层,复杂无比。我这大半年都在重写他那个蛋疼又复杂的cc层。

和webkit比,miniblink架构有什么优势

现在的webkit版本,已经比miniblink落后太多了。blink一直在加入各种极富创造力和想象力的功能、组件。例如,blink早就加入多线程解析html token、blink gc回收器、多线程录制回放渲染机制。这些能让blink的解析渲染速度极大提升。下一次,我会先开源出blink gc组件,这东西很有意思,在c++里硬是搞出了一个垃圾回收机制,能让你像写java一样写c++。

miniblink 开源地址:https://github.com/weolar/miniblink49

我们可以通过miniblink做桌面应用的UI,超小的附加dll,压缩之后大约5M,比起Cefsharp 小太多了。VSCode也是用  Electron 网页开发的

Html开发UI和WPF对比有什么优势?

1、 Html 前端资源丰富,各种框架

2、 会Html的人比会WPF的人多太多了,入门简单

3、.Net2.0照样可以用

4、网站和桌面程序界面可以统一,甚至大部分前端代码复用

资源消耗方面,和WPF差不多,毕竟浏览器也是内存消耗大户

不过对比electron 这种纯 Html CSS + js的,我更喜欢 用C# 代替JS 做业务逻辑,我感觉JS写大项目 代码比较乱,维护比C#麻烦。所以 JS做前端辅助比较合适,业务逻辑用C#实现

接下来,我写个简单的MiniBlink封装案例!更多功能你们可以自行封装 或者 看看 DSkin

采用 PInvoke 和Winform封装方式,MiniBlink对外提供的接口主要在 wke.h ,miniblink 的dll可以通过GitHub源码编译出来。具体封装规则,你们可以百度查查看

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
//by:DSkin namespace MiniBlink
{
public static class MiniblinkPInvoke
{
const string miniblinkdll = "node.dll"; [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeSetHandle(IntPtr webView, IntPtr wnd); [DllImport(miniblinkdll, CharSet = CharSet.Unicode)]
public static extern IntPtr wkeGetStringW(IntPtr @string); [DllImport(miniblinkdll, CharSet = CharSet.Unicode)]
public static extern IntPtr wkeToStringW(IntPtr @string); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr wkeGetString(IntPtr @string); [DllImport(miniblinkdll)]
public static extern void wkeSetDebugConfig(IntPtr webView, string debugString, IntPtr param); [DllImport(miniblinkdll)]
public static extern Int64 wkeRunJSW(IntPtr webView, [In] [MarshalAs(UnmanagedType.LPWStr)] string script); [return: MarshalAs(UnmanagedType.I1)]
[DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern bool wkeFireMouseEvent(IntPtr webView, uint message, int x, int y, uint flags); [return: MarshalAs(UnmanagedType.I1)]
[DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern bool wkeFireMouseWheelEvent(IntPtr webView, int x, int y, int delta, uint flags); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool wkeFireKeyUpEvent(IntPtr webView, uint virtualKeyCode, uint flags, [MarshalAs(UnmanagedType.I1)] bool systemKey); [return: MarshalAs(UnmanagedType.I1)]
[DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern bool wkeFireKeyDownEvent(IntPtr webView, uint virtualKeyCode, uint flags, [MarshalAs(UnmanagedType.I1)] bool systemKey); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool wkeFireKeyPressEvent(IntPtr webView, uint charCode, uint flags, [MarshalAs(UnmanagedType.I1)] bool systemKey); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeSetFocus(IntPtr webView); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeKillFocus(IntPtr webView); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeResize(IntPtr webView, int w, int h); [DllImport(miniblinkdll)]
public static extern IntPtr wkeCreateWebView(); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeDestroyWebView(IntPtr webView); [DllImport(miniblinkdll)]
public static extern void wkeInitialize(); [DllImport(miniblinkdll)]
public static extern void wkeLoadFile(IntPtr webView, [In, MarshalAs(UnmanagedType.LPWStr)] string filename); [DllImport(miniblinkdll)]
public static extern void wkeLoadHTML(System.IntPtr webView, [In()] [MarshalAs(UnmanagedType.LPStr)] string html); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeOnURLChanged2(IntPtr webView, UrlChangedCallback2 callback, IntPtr callbackParam); [DllImport(miniblinkdll, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeLoadURLW(IntPtr webView, [In, MarshalAs(UnmanagedType.LPWStr)] string url); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkeOnPaintUpdated(IntPtr webView, wkePaintUpdatedCallback callback, IntPtr callbackParam); [DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern void wkePaint(IntPtr webView, IntPtr bits, int pitch);
[DllImport(miniblinkdll, CallingConvention = CallingConvention.Cdecl)]
public static extern WkeCursorInfo wkeGetCursorInfoType(IntPtr webView); public static string Utf8IntptrToString(this IntPtr ptr)
{
var data = new List<byte>();
var off = ;
while (true)
{
var ch = Marshal.ReadByte(ptr, off++);
if (ch == )
{
break;
}
data.Add(ch);
}
return Encoding.UTF8.GetString(data.ToArray());
}
     //调用这个之后要手动调用  Marshal.FreeHGlobal(ptr);
public static IntPtr Utf8StringToIntptr(this string str)
{
byte[] utf8bytes = Encoding.UTF8.GetBytes(str);
IntPtr ptr = Marshal.AllocHGlobal(utf8bytes.Length + );
Marshal.Copy(utf8bytes, , ptr, utf8bytes.Length);
Marshal.WriteByte(ptr, utf8bytes.Length, );
return ptr;
} public enum WkeCursorInfo
{
WkeCursorInfoPointer = ,
WkeCursorInfoCross = ,
WkeCursorInfoHand = ,
WkeCursorInfoIBeam = ,
WkeCursorInfoWait = ,
WkeCursorInfoHelp = ,
WkeCursorInfoEastResize = ,
WkeCursorInfoNorthResize = ,
WkeCursorInfoNorthEastResize = ,
WkeCursorInfoNorthWestResize = ,
WkeCursorInfoSouthResize = ,
WkeCursorInfoSouthEastResize = ,
WkeCursorInfoSouthWestResize = ,
WkeCursorInfoWestResize = ,
WkeCursorInfoNorthSouthResize = ,
WkeCursorInfoEastWestResize = ,
WkeCursorInfoNorthEastSouthWestResize = ,
WkeCursorInfoNorthWestSouthEastResize = ,
WkeCursorInfoColumnResize = ,
WkeCursorInfoRowResize = ,
}
public enum wkeMouseMessage : uint
{
WKE_MSG_MOUSEMOVE = 0x0200,
WKE_MSG_LBUTTONDOWN = 0x0201,
WKE_MSG_LBUTTONUP = 0x0202,
WKE_MSG_LBUTTONDBLCLK = 0x0203,
WKE_MSG_RBUTTONDOWN = 0x0204,
WKE_MSG_RBUTTONUP = 0x0205,
WKE_MSG_RBUTTONDBLCLK = 0x0206,
WKE_MSG_MBUTTONDOWN = 0x0207,
WKE_MSG_MBUTTONUP = 0x0208,
WKE_MSG_MBUTTONDBLCLK = 0x0209,
WKE_MSG_MOUSEWHEEL = 0x020A,
}
public enum wkeMouseFlags
{
WKE_LBUTTON = 0x01,
WKE_RBUTTON = 0x02,
WKE_SHIFT = 0x04,
WKE_CONTROL = 0x08,
WKE_MBUTTON = 0x10,
} [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void wkePaintUpdatedCallback(IntPtr webView, IntPtr param, IntPtr hdc, int x, int y, int cx, int cy); [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void UrlChangedCallback2(IntPtr webView, IntPtr param, IntPtr frameId, IntPtr url);
}
}
 using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
//by:DSkin namespace MiniBlink
{
public class TestControl : Control
{
IntPtr handle = IntPtr.Zero;
string url = string.Empty; IntPtr bits = IntPtr.Zero;
Size oldSize;
MiniblinkPInvoke.UrlChangedCallback2 UrlChangedCallback2;
MiniblinkPInvoke.wkePaintUpdatedCallback wkePaintUpdatedCallback; public TestControl()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.DoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint, true);
} public string Url
{
get
{
return url;
} set
{
url = value;
if (handle != IntPtr.Zero)
{
MiniblinkPInvoke.wkeLoadURLW(handle, value);
}
}
} protected override void OnCreateControl()
{
base.OnCreateControl();
if (!DesignMode)
{
MiniblinkPInvoke.wkeInitialize();
handle = MiniblinkPInvoke.wkeCreateWebView();//创建 WebView
MiniblinkPInvoke.wkeSetHandle(handle, this.Handle);
MiniblinkPInvoke.wkeResize(handle, Width, Height); UrlChangedCallback2 = (webView, param, frameId, url) =>
{
this.url = MiniblinkPInvoke.wkeGetString(url).Utf8IntptrToString();
OnUrlChanged(EventArgs.Empty);
};
MiniblinkPInvoke.wkeOnURLChanged2(handle, UrlChangedCallback2, IntPtr.Zero); wkePaintUpdatedCallback = (IntPtr webView, IntPtr param, IntPtr hdc, int x, int y, int cx, int cy) =>
{
Invalidate();
};
MiniblinkPInvoke.wkeOnPaintUpdated(handle, wkePaintUpdatedCallback, IntPtr.Zero); Url = url;
}
} protected override void OnPaint(PaintEventArgs e)
{
if (handle != IntPtr.Zero)
{
if (bits == IntPtr.Zero || oldSize != Size)
{
if (bits != IntPtr.Zero)
{
Marshal.FreeHGlobal(bits);
}
oldSize = Size;
bits = Marshal.AllocHGlobal(Width * Height * );
} MiniblinkPInvoke.wkePaint(handle, bits, );
using (Bitmap bmp = new Bitmap(Width, Height, Width * , PixelFormat.Format32bppPArgb, bits))
{
e.Graphics.DrawImage(bmp, , );
}
}
base.OnPaint(e);
if (DesignMode)
{
e.Graphics.DrawString("MiniBlinkBrowser", this.Font, Brushes.Red, new Point());
e.Graphics.DrawRectangle(Pens.Black, new Rectangle(, , Width - , Height - ));
}
} protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
if (handle != IntPtr.Zero && Width > && Height > )
{
MiniblinkPInvoke.wkeResize(handle, Width, Height);
}
} protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
if (handle != IntPtr.Zero)
{
uint flags = GetMouseFlags(e);
MiniblinkPInvoke.wkeFireMouseWheelEvent(handle, e.X, e.Y, e.Delta, flags);
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
uint msg = ;
if (e.Button == MouseButtons.Left)
{
msg = (uint)MiniblinkPInvoke.wkeMouseMessage.WKE_MSG_LBUTTONDOWN;
}
else if (e.Button == MouseButtons.Middle)
{
msg = (uint)MiniblinkPInvoke.wkeMouseMessage.WKE_MSG_MBUTTONDOWN;
}
else if (e.Button == MouseButtons.Right)
{
msg = (uint)MiniblinkPInvoke.wkeMouseMessage.WKE_MSG_RBUTTONDOWN;
}
uint flags = GetMouseFlags(e);
if (handle != IntPtr.Zero)
{
MiniblinkPInvoke.wkeFireMouseEvent(handle, msg, e.X, e.Y, flags);
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
uint msg = ;
if (e.Button == MouseButtons.Left)
{
msg = (uint)MiniblinkPInvoke.wkeMouseMessage.WKE_MSG_LBUTTONUP;
}
else if (e.Button == MouseButtons.Middle)
{
msg = (uint)MiniblinkPInvoke.wkeMouseMessage.WKE_MSG_MBUTTONUP;
}
else if (e.Button == MouseButtons.Right)
{
msg = (uint)MiniblinkPInvoke.wkeMouseMessage.WKE_MSG_RBUTTONUP;
}
uint flags = GetMouseFlags(e);
if (handle != IntPtr.Zero)
{
MiniblinkPInvoke.wkeFireMouseEvent(handle, msg, e.X, e.Y, flags);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (this.handle != IntPtr.Zero)
{
uint flags = GetMouseFlags(e);
MiniblinkPInvoke.wkeFireMouseEvent(this.handle, 0x200, e.X, e.Y, flags); switch (MiniblinkPInvoke.wkeGetCursorInfoType(handle))
{
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoPointer:
Cursor = Cursors.Default;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoCross:
Cursor = Cursors.Cross;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoHand:
Cursor = Cursors.Hand;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoIBeam:
Cursor = Cursors.IBeam;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoWait:
Cursor = Cursors.WaitCursor;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoHelp:
Cursor = Cursors.Help;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoEastResize:
Cursor = Cursors.SizeWE;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoNorthResize:
Cursor = Cursors.SizeNS;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoNorthEastResize:
Cursor = Cursors.SizeNESW;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoNorthWestResize:
Cursor = Cursors.SizeNWSE;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoSouthResize:
Cursor = Cursors.SizeNS;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoSouthEastResize:
Cursor = Cursors.SizeNWSE;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoSouthWestResize:
Cursor = Cursors.SizeNESW;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoWestResize:
Cursor = Cursors.SizeWE;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoNorthSouthResize:
Cursor = Cursors.SizeNS;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoEastWestResize:
Cursor = Cursors.SizeWE;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoNorthEastSouthWestResize:
Cursor = Cursors.SizeAll;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoNorthWestSouthEastResize:
Cursor = Cursors.SizeAll;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoColumnResize:
Cursor = Cursors.Default;
break;
case MiniblinkPInvoke.WkeCursorInfo.WkeCursorInfoRowResize:
Cursor = Cursors.Default;
break;
default:
Cursor = Cursors.Default;
break;
}
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (handle != IntPtr.Zero)
{
MiniblinkPInvoke.wkeFireKeyDownEvent(handle, (uint)e.KeyValue, , false);
}
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
base.OnKeyPress(e);
if (handle != IntPtr.Zero)
{
e.Handled = true;
MiniblinkPInvoke.wkeFireKeyPressEvent(handle, (uint)e.KeyChar, , false);
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
if (handle != IntPtr.Zero)
{
MiniblinkPInvoke.wkeFireKeyUpEvent(handle, (uint)e.KeyValue, , false);
}
} protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
if (handle != IntPtr.Zero)
{
MiniblinkPInvoke.wkeSetFocus(handle);
}
} protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
if (handle != IntPtr.Zero)
{
MiniblinkPInvoke.wkeKillFocus(handle);
}
} protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
base.OnPreviewKeyDown(e);
switch (e.KeyCode)
{
case Keys.Down:
case Keys.Up:
case Keys.Left:
case Keys.Right:
case Keys.Tab:
e.IsInputKey = true;
break;
}
}
private static uint GetMouseFlags(MouseEventArgs e)
{
uint flags = ;
if (e.Button == MouseButtons.Left)
{
flags = flags | (uint)MiniblinkPInvoke.wkeMouseFlags.WKE_LBUTTON;
}
if (e.Button == MouseButtons.Middle)
{
flags = flags | (uint)MiniblinkPInvoke.wkeMouseFlags.WKE_MBUTTON;
}
if (e.Button == MouseButtons.Right)
{
flags = flags | (uint)MiniblinkPInvoke.wkeMouseFlags.WKE_RBUTTON;
}
if (Control.ModifierKeys == Keys.Control)
{
flags = flags | (uint)MiniblinkPInvoke.wkeMouseFlags.WKE_CONTROL;
}
if (Control.ModifierKeys == Keys.Shift)
{
flags = flags | (uint)MiniblinkPInvoke.wkeMouseFlags.WKE_SHIFT;
}
return flags;
} protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (handle != IntPtr.Zero)
{//资源释放
MiniblinkPInvoke.wkeDestroyWebView(handle);
handle = IntPtr.Zero;
}
if (bits != IntPtr.Zero)
{
Marshal.FreeHGlobal(bits);
bits = IntPtr.Zero;
}
} public event EventHandler UrlChanged; protected virtual void OnUrlChanged(EventArgs e)
{
if (UrlChanged != null)
{
UrlChanged(this, e);
}
} }
}

代码写好之后,生成一下,工具箱里就有控件了,然后拖到界面,设置Url,运行就有效果了

C# 封装miniblink 使用HTML/CSS/JS来构建.Net 应用程序界面和简易浏览器

C# 封装miniblink 使用HTML/CSS/JS来构建.Net 应用程序界面和简易浏览器

如果你想偷懒,要更完整的功能你可以看看 DSKin

DSkin已经封装好 Html构建桌面UI的快捷开发方式,资源内嵌,C#和JS互相调用,无需写麻烦的参数数据转换,通过Vue实现C#属性和页面双向绑定,页面和C#类对应等等,就像WebForm开发。 窗体阴影、系统按钮那些都是Html+CSS定义的

C# 封装miniblink 使用HTML/CSS/JS来构建.Net 应用程序界面和简易浏览器

C# 封装miniblink 使用HTML/CSS/JS来构建.Net 应用程序界面和简易浏览器的更多相关文章

  1. 开源组件NanUI一周年 - 使用HTML&sol;CSS&sol;JS来构建&period;Net Winform应用程序界面

    NanUI是什么 NanUI基于ChromiumFX项目进行开发,它能让你在你的Winform应用程序中使用HTML5/CSS3/Javascript等网页技术来呈现用户界面(类似Electron). ...

  2. Spring MVC程序中得到静态资源文件css&comma;js&comma;图片

    转载自:http://www.blogjava.net/fiele/archive/2014/08/24/417283.html 用 Spring MVC 开发应用程序,对于初学者有一个很头疼的问题, ...

  3. Spring MVC程序中得到静态资源文件css&comma;js&comma;图片文件的路径问题总结

    上一篇 | 下一篇 Spring MVC程序中得到静态资源文件css,js,图片 文件的路径 问题总结 作者:轻舞肥羊 日期:2012-11-26 http://www.blogjava.net/fi ...

  4. HTML&plus;CSS&plus;JS简介

    1.HTML与 CSS 1 1.1 HTML 1 1.2 HTML5 2 1.2.1 HTML5的特性 3 1.3  CSS 4 2.JavaScript 6 2.1特性 7 2.2编程 8 3.Sp ...

  5. js活jQuery实现动态添加、移除css&sol;js文件

    下面是在项目中用到的,直接封装好的函数,拿去在js中直接调用就可以实现css.js文件的动态引入与删除.代码如下 动态加载,移除,替换css/js文件 // 动态添加css文件 function ad ...

  6. JavaWeb&lpar;HTML &plus;css&plus;js&plus;Servlet&period;&period;&period;&period;&rpar;

    注意 1.不要把函数命名为add(),不然容易和自带的冲突报错 2.是createElement 不要把create中的e写掉了 3.记得是getElementsByTaxName和getElemen ...

  7. 前端工程师面试问题归纳(一、问答类html&sol;css&sol;js基础)

    一.参考资源 1.前端面试题及答案整理(一) 2.2017年前端面试题整理汇总100题 3.2018最新Web前端经典面试试题及答案 4.[javascript常见面试题]常见前端面试题及答案 5.W ...

  8. IOS HTML&plus;CSS&plus;JS 总结

    一.HTML + CSS 1.能看到标签的结构* 父子关系<p>    <span>123</span></p> * 属性<img src=&qu ...

  9. CSS &amp&semi; JS 制作滚动幻灯片

    ==================纯CSS方式==================== <!DOCTYPE html> <html> <head> <met ...

随机推荐

  1. Visual Studio EventHandler Delegate 和 EventArgs

    EventHandler代理 用来表示处理一个没有事件数据(event data)的事件(event)的 方法. 无论何时事件发生时,事件代理就被调用来触发以前事件驱动的其他事件(监听当前事件TCur ...

  2. win10进入安全模式的方法(F8不管用&sol;开不开机)

    win10默认不能进入安全模式,这时候开机黑屏怎么办?下面介绍强制进入安全模式的方法 1. 关机情况下,按开机键开机,等到出现徽标(下图),这时候长按开机键强制关机. 2. 再次开机,出现徽标,再次强 ...

  3. &lt&semi;实训&vert;第八天&gt&semi;超级管理员管理linux用户行为权限附监控主机状态

    作为运维工程师,系统管理员,你最大的权力就是给别人分配权力,而且你还能时时控制着他们,今天就给大家介绍一下关于管理用户这一方面的前前后后.  开班第八天: 主要课程大纲:(下面我将把自己的身份定位成一 ...

  4. 重点关注之自定义序列化方式&lpar;Protobuf和Msgpack&rpar;

    除了默认的JSON和XML序列化器外,如果想使用其它格式的(比如二进制)序列化器,也是可以的.比如著名的Protobuf和Msgpack,它们都是二进制的序列化器,特点是速度快,体积小.使用方法如下. ...

  5. 通过gdb跟踪进程调度分析进程切换的过程

    作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 本实验目的:通过gdb在lin ...

  6. element的height与width

    关于一个element所有的高度宽度 ele.style.width,ele.style.height:操纵style样式.+"px" offsetWidth.offsetHeig ...

  7. PHP单例模式--典型的三私一公

    单例模式:即一个类只被实例化一次,当其他人对其再次实例化时,便返回第一次实例化的对象.这种模式可以极大地节约资源.典型应用于数据库类的实例化. 以实例化一个Mysql数据库类为例: 要实现一个类只实例 ...

  8. PHP修改记录

    1. Http请求错误--20151123 参考网址:http://www.lvtao.net/dev/php-nginx-uploadfiy.html 是配置问题,修改php.ini文件: uplo ...

  9. 【不定期更新】FPGA&sol;IC岗位常见笔试面试题总结(基础知识)

    1 数字IC(ASIC)设计流程: IC设计分为前端和后端.前端设计主要将HDL语言-->网表,后端设计是网表-->芯片版图. 前端主要有需求分析与架构设计.RTL设计.仿真验证.逻辑综合 ...

  10. 《Practical Vim》第十章&colon;复制和粘贴

    第十章和第十一章讲了 Vim 的寄存器功能, 寄存器,是用于保存文本的特定的容器.它的内容: 既可以是类似于系统剪切板功能的,用于粘贴的文本 也可以是录制成的宏的命令. 第十章讲使用寄存器使用复制与粘 ...