function isWasmSupported() { try { if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") { const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)); if (module instanceof WebAssembly.Module) return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; } } catch (e) { } return false; } if(!isWasmSupported()) { alert("WebAssembly is not available in your browser. Please try using the latest version of Chrome, Firefox, Edge or Safari."); }
React 16x - قسمت 1 - معرفی و شروع به کار
- ابتدا پوشهی node_modules/.cache را حذف کنید (مهم!).
- سپس دو بستهی «npm install react-app-polyfill core-js» را نصب کنید.
- چند سطر زیر را به ابتدای فایل index.js اضافه کنید:
import "react-app-polyfill/ie11"; import "react-app-polyfill/stable"; import 'core-js/features/array/find'; import 'core-js/features/array/includes'; import 'core-js/features/number/is-nan';
"browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all", "ie 10" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version", "ie 10" ] }
سپس برنامه را اجرا کنید:
- Mozilla Firefox 7.0 Final – مرورگر فایرفاکس | کـاوه | www.barnameha.com
- اشتباهات رایج برنامه نویسی در شیرپوینت – قسمت اول | ramin | pspcommunity.org
- فایرفاکس ۷ با کروم ۱۴ شاخ به شاخ شدند! | علی مختاری | www.mywindows.ir
- Namespace Global و VB | blogs.msdn.com
- Quick Launch در نگارش بعدی ویژوال استودیو | blogs.msdn.com
- اطلاعاتی بیشتر در مورد پروژه Masive | wekeroad.com
- اولین به روز رسانی RAD Studio XE2 | vcldeveloper.com
- کتابخانههای قابل انتقال در دات نت 4 و نیم | channel9.msdn.com
- نگارش جدید Ajax Control Toolkit منتشر شد | stephenwalther.com
- نگارش جدیدی از TPL Dataflow منتشر شد | blogs.msdn.com
using System; namespace WpfLargeLists.Models { public class User { public int Id { set; get; } public string FirstName { set; get; } public string LastName { set; get; } public string Address { set; get; } public DateTime DateOfBirth { set; get; } } }
<ListView ItemsSource="{Binding UsersTab1}" Grid.Row="1" Margin="3"> <ListView.View> <GridView> <GridViewColumn Header="Id" Width="50" DisplayMemberBinding="{Binding Id, Mode=OneTime}" /> <GridViewColumn Header="FirstName" Width="100" DisplayMemberBinding="{Binding FirstName, Mode=OneTime}" /> <GridViewColumn Header="LastName" Width="100" DisplayMemberBinding="{Binding LastName, Mode=OneTime}" /> <GridViewColumn Header="Address" Width="100" DisplayMemberBinding="{Binding Address, Mode=OneTime}" /> <GridViewColumn Header="DateOfBirth" Width="150" DisplayMemberBinding="{Binding DateOfBirth, Mode=OneTime}" /> </GridView> </ListView.View> </ListView>
راه حل توصیه شده برای بارگذاری تعداد بالایی رکورد در WPF : استفاده از UI Virtualization
UI Virtualization روشی است که در آن تنها المانهایی که توسط کاربر در حال مشاهده هستند، تولید و مدیریت خواهند شد. در این حالت اگر 1000 رکورد را به یک ListBox یا ListView ارسال کنید و کاربر بر اساس اندازه صفحه جاری خود تنها 10 رکورد را مشاهده میکند، WPF فقط 10 عنصر را در VisualTree مدیریت خواهد کرد. با اسکرول به سمت پایین، مواردی که دیگر نمایان نیستند dispose شده و مجموعه نمایان دیگری خلق خواهند شد. به این ترتیب میتوان حجم بالایی از اطلاعات را در WPF با میزان مصرف پایین حافظه و همچنین مصرف CPU بسیار کم مدیریت کرد. این مجازی سازی در WPF به وسیله VirtualizingStackPanel در دسترس است.
برای اینکه WPF virtualization به درستی کار کند، نیاز است یک سری شرایط مقدماتی فراهم شوند:
- از کنترلی استفاده شود که از virtualization پشتیبانی میکند؛ مانند ListBox و ListView.
- ارتفاع کنترل لیستی باید دقیقا مشخص باشد؛ یا درون یک ردیف از Grid ایی باشد که ارتفاع آن مشخص است. برای نمونه اگر ارتفاع ردیف Grid ایی که ListView را دربرگرفته است به * تنظیم شده، مشکلی نیست؛ اما اگر ارتفاع این ردیف به Auto تنظیم شده، کنترل لیستی برای محاسبه vertical scroll bar خود دچار مشکل خواهد شد.
- کنترل مورد استفاده نباید در یک کنترل ScrollViewer محصور شود؛ در غیر اینصورت virtualization رخ نخواهد داد. علاوه بر آن در خود کنترل باید خاصیت ScrollViewer.HorizontalScrollBarVisibility نیز به Disabled تنظیم گردد.
- در کنترل در حال استفاده، ScrollViewer.CanContentScroll باید به true تنظیم شود.
مورد مشخص بودن ارتفاع بسیار مهم است. برای نمونه در برنامهای پس از فعال سازی مجازی سازی، کنترل لیستی کلا از کار افتاد و حرکت scroll bar آن سبب بروز CPU Usage بالایی میشد. این مشکل با تنظیم ارتفاع آن به شکل زیر برطرف شد:
Height="{Binding Path=RowDefinitions[1].ActualHeight, RelativeSource={RelativeSource AncestorType=Grid}}"
- پس از اعمال موارد یاد شده، باید VirtualizingStackPanel کنترل را فعال کرد. ابتدا دو خاصیت زیر باید مقدار دهی شوند:
VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"
<ListBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling" /> </ItemsPanelTemplate> </ListBox.ItemsPanel>
<ListView.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling" /> </ItemsPanelTemplate> </ListView.ItemsPanel>
<ListView ItemsSource="{Binding UsersTab2}" Grid.Row="1" Margin="3" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.CanContentScroll="True" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling"> <ListView.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling" /> </ItemsPanelTemplate> </ListView.ItemsPanel> <ListView.View> <GridView> <GridViewColumn Header="Id" Width="50" DisplayMemberBinding="{Binding Id, Mode=OneTime}" /> <GridViewColumn Header="FirstName" Width="100" DisplayMemberBinding="{Binding FirstName, Mode=OneTime}" /> <GridViewColumn Header="LastName" Width="100" DisplayMemberBinding="{Binding LastName, Mode=OneTime}" /> <GridViewColumn Header="Address" Width="100" DisplayMemberBinding="{Binding Address, Mode=OneTime}" /> <GridViewColumn Header="DateOfBirth" Width="150" DisplayMemberBinding="{Binding DateOfBirth, Mode=OneTime}" /> </GridView> </ListView.View> </ListView>
در این مثال دو برگه را ملاحظه میکنید. برگه اول حالت normal ابتدای بحث است و برگه دوم پیاده سازی UI Virtualization را انجام داده است.
//برای ذخیره مقادیر از ساختار نام و مقدار استفاده میکنیم که نامها را اینجا ثبت کرده ام var Variables={ posts:"posts", postsComments:"postsComments", shares:"shares", sharesComments:"sharesComments", } //برای ذخیره زمان آخرین تغییر سایت برای هر یک از مطالب به صورت جداگانه نیاز به یک ساختار نام و مقدار است که نامها را در اینجا ذخیره کرده ام var DateContainer={ posts:"dtposts", postsComments:"dtpostsComments", shares:"dtshares", sharesComments:"dtsharesComments", interval:"interval" } //برای نمایش پیامها به کاربر var Messages={ SettingsSaved:"تنظیمات ذخیره شد", SiteUpdated:"سایت به روز شد", PostsUpdated:"مطلب ارسالی جدید به سایت اضافه شد", CommentsUpdated:"نظری جدیدی در مورد مطالب سایت ارسال شد", SharesUpdated:"اشتراک جدید به سایت ارسال شد", SharesCommentsUpdated:"نظری برای اشتراکهای سایت اضافه شد" } //لینکهای فید سایت var Links={ postUrl:"https://www.dntips.ir/feeds/posts", posts_commentsUrl:"https://www.dntips.ir/feeds/comments", sharesUrl:"https://www.dntips.ir/feed/news", shares_CommentsUrl:"https://www.dntips.ir/feed/newscomments" } //لینک صفحات سایت var WebLinks={ Home:"https://www.dntips.ir", postUrl:"https://www.dntips.ir/postsarchive", posts_commentsUrl:"https://www.dntips.ir/commentsarchive", sharesUrl:"https://www.dntips.ir/newsarchive", shares_CommentsUrl:"https://www.dntips.ir/newsarchive/comments" }
chrome.runtime.onInstalled.addListener(function(details) { var now=String(new Date()); var params={}; params[Variables.posts]=true; params[Variables.postsComments]=false; params[Variables.shares]=false; params[Variables.sharesComments]=false; params[DateContainer.interval]=1; params[DateContainer.posts]=now; params[DateContainer.postsComments]=now; params[DateContainer.shares]=now; params[DateContainer.sharesComments]=now; chrome.storage.local.set(params, function() { if(chrome.runtime.lastError) { /* error */ console.log(chrome.runtime.lastError.message); return; } }); });
chrome.storage.local.set('mykey':myvalue,....
chrome.storage.local.set(mykey:myvalue,...
"background": { "scripts": ["const.js","init.js"] }
نکته:نمی توان در تعریف بک گراند هم فایل اسکریپت معرفی کرد و هم فایل html
"background": { "page": "background.htm" }
<html> <head> <script type="text/javascript" src="const.js"></script> <script type="text/javascript" src="https://www.google.com/jsapi"></script> <script type="text/javascript" src="init.js"></script> <script type="text/javascript" src="omnibox.js"></script> <script type="text/javascript" src="rssreader.js"></script> <script type="text/javascript" src="contextmenus.js"></script> </head> <body> </body> </html>
- کدنویسی راحتتر و خلاصهتر برای خواندن RSS
- استفاده اجباری از یک پروکسی به خاطر Content Security Policy و حتی CORS
"content_security_policy": "script-src 'self' https://*.google.com; object-src 'self'"
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
استفاده از این Api در rssreader.js
google.load("feeds", "1"); google.setOnLoadCallback(alarmManager);
function alarmManager() { chrome.storage.local.get(DateContainer.interval,function ( items) { period_time==items[DateContainer.interval]; chrome.alarms.create('RssInterval', {periodInMinutes: period_time}); }); chrome.alarms.onAlarm.addListener(function (alarm) { console.log(alarm); if (alarm.name == 'RssInterval') { var boolposts,boolpostsComments,boolshares,boolsharesComments; chrome.storage.local.get([Variables.posts,Variables.postsComments,Variables.shares,Variables.sharesComments],function ( items) { boolposts=items[Variables.posts]; boolpostsComments=items[Variables.postsComments]; boolshares=items[Variables.shares]; boolsharesComments=items[Variables.sharesComments]; chrome.storage.local.get([DateContainer.posts,DateContainer.postsComments,DateContainer.shares,DateContainer.sharesComments],function ( items) { var Vposts=new Date(items[DateContainer.posts]); var VpostsComments=new Date(items[DateContainer.postsComments]); var Vshares=new Date(items[DateContainer.shares]); var VsharesComments=new Date(items[DateContainer.sharesComments]); if(boolposts){var result=RssReader(Links.postUrl,Vposts,DateContainer.posts,Messages.PostsUpdated);} if(boolpostsComments){var result=RssReader(Links.posts_commentsUrl,VpostsComments,DateContainer.postsComments,Messages.CommentsUpdated); } if(boolshares){var result=RssReader(Links.sharesUrl,Vshares,DateContainer.shares,Messages.SharesUpdated);} if(boolsharesComments){var result=RssReader(Links.shares_CommentsUrl,VsharesComments,DateContainer.sharesComments,Messages.SharesCommentsUpdated);} }); }); } }); }
- آدرس فیدی که قرار است از روی آن بخواند
- آخرین به روزسانی که از سایت داشته متعلق به چه تاریخی است.
- نام کلید ذخیره سازی تاریخ آخرین تغییر سایت که اگر بررسی شد و مشخص شد سایت به روز شده است، تاریخ جدید را روی آن ذخیره کنیم.
- در صورتی که سایت به روز شده باشد نیاز است پیامی را برای کاربر نمایش دهیم که این پیام را در اینجا قرار میدهیم.
function RssReader(URL,lastupdate,datecontainer,Message) { var feed = new google.feeds.Feed(URL); feed.setResultFormat(google.feeds.Feed.XML_FORMAT); feed.load(function (result) { if(result!=null) { var strRssUpdate = result.xmlDocument.firstChild.firstChild.childNodes[5].textContent; var RssUpdate=new Date(strRssUpdate); if(RssUpdate>lastupdate) { SaveDateAndShowMessage(datecontainer,strRssUpdate,Message) } } }); }
var strRssUpdate = result.xmlDocument.firstChild.firstChild.childNodes[5].textContent;
function SaveDateAndShowMessage(DateField,DateValue,Message) { var params={ } params[DateField]=DateValue; chrome.storage.local.set( params,function(){ var options={ type: "basic", title: Messages.SiteUpdated, message: Message, iconUrl: "icon.png" } chrome.notifications.create("",options,function(){ chrome.notifications.onClicked.addListener(function(){ chrome.tabs.create({'url': WebLinks.Home}, function(tab) { }); }); }); }); }
"permissions": [ "storage", "tabs", "alarms", "notifications" ]
chrome.notifications.create("",options,function(){ chrome.notifications.onClicked.addListener(function(){ chrome.tabs.create({'url': WebLinks.Home}, function(tab) { });}); });
"web_accessible_resources": [ "icon.png" ]
خوب؛ کار افزونه تمام شده است ولی اجازه دهید این بار امکانات افزونه را بسط دهیم:
"options_page": "popup.html"
جایزگزینی صفحات یا Override Pages
"chrome_url_overrides": { "newtab": "newtab.htm" }
ایجاد یک تب اختصاصی در Developer Tools
"devtools_page": "devtools.htm"
<script src="devtools.js"></script>
chrome.devtools.panels.create( "Dotnettips Updater Tools", "icon.png", "devtoolsui.htm", function(panel) { } );
- One-Time Requests یا درخواستهای تک مرتبهای
- Long-Lived Connections یا اتصالات بلند مدت یا مصر
درخواستهای تک مرتبه ای
window.addEventListener("load", function() { chrome.extension.sendMessage({ type: "dom-loaded", data: { myProperty : "value" } }); }, true);
chrome.extension.onMessage.addListener(function(request, sender, sendResponse) { switch(request.type) { case "dom-loaded": alert(request.data.myProperty ); break; } return true; });
var port = chrome.runtime.connect({name: "my-channel"}); port.postMessage({myProperty: "value"}); port.onMessage.addListener(function(msg) { // do some stuff here });
chrome.runtime.onConnect.addListener(function(port) { if(port.name == "my-channel"){ port.onMessage.addListener(function(msg) { // do some stuff here }); } });
نمونه کد
آپلود نهایی کار در Google web store
برای آپلود نهایی کار به google web store که در آن تمامی برنامهها و افزونههای کروم قرار دارند بروید. سمت راست آیکن تنظیمات را بزنید و گزینه developer dashboard را انتخاب کنید تا صفحهی آپلود کار برای شما باز شود. دایرکتوری محتویات اکستنشن را zip کرده و آپلود نمایید. توجه داشته باشید که محتویات و سورس خود را باید آپلود کنید نه فایل crx را. بعد از آپلود موفقیت آمیز، صفحهای ظاهر میشود که از شما آیکن افزونه را در اندازه 128 پیکسل میخواهد بعلاوه توضیحاتی در مورد افزونه، قیمت گذاری که به طور پیش فرض به صورت رایگان تنظیم شده است، لینک وب سایت مرتبط، لینک محل پرسش و پاسخ برای افزونه، اگر لینک یوتیوبی در مورد افزونه دارید، یک شات تصویری از افزونه و همینطور چند تصویر برای اسلایدشو سازی که در همان صفحه استاندارد آنها را توضیح میدهد و در نهایت گزینهی جالبتر هم اینکه اکستنشن شما برای چه مناطقی تهیه شده است که متاسفانه ایران را ندیدم که میتوان همه موارد را انتخاب کرد. به خصوص در مورد ایران که آی پیها هم صحیح نیست، انتخاب ایران چنان تاثیری ندارد و در نهایت گزینهی publish را میزنید که متاسفانه بعد از این صفحه درخواست میکند برای اولین بار باید 5 دلار آمریکا پرداخت شود که برای بسیاری از ما این گزینه ممکن نیست.
سورس پروژه را میتوانید از اینجا ببینید و خود افزونه را از اینجا دریافت کنید.
نکته: راههای اشاره شده برای مقابله با شنود پارامترها برای تمام شرایط قابل استفاده نیستند.
راه حل اول: استفاده از دستور With Recompile
مشکل شنود پارامتر این است که در اولین اجرای پروسیجر، پلن اجرایی را بر اساس پارامترهای ارسالی اولیه ایجاد میکند. راه حل غلبه بر این مشکل، کامپایل مجدد پروسیجر، بعد از هر اجرای آن است. بهمین جهت از دستور WITH RECOMPILE هنگامیکه قصد ایجاد پروسیجر را دارید استفاده نمایید. مانند کد زیر:
CREATE PROC [dbo].[DisplayBillingInfo] @BeginDate DATETIME, @EndDate DATETIME WITH RECOMPILE AS SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @BeginDate AND @EndDate;
DBCC FREEPROCCACHE; EXEC dbo.DisplayBillingInfo @BeginDate = '2005-01-01', @EndDate = '2005-01-03'; EXEC dbo.DisplayBillingInfo @BeginDate = '1999-01-01', @EndDate = '1999-12-31';
راه حل دوم: غیر فعال نمودن شنود پارامتر
روش دیگر برطرف کردن مشکلات مرتبط با شنود پارامتر، غیر فعال کردن آن است. البته منظور از غیر فعال کردن، غیر فعال نمودن گزینهای در بانک اطلاعاتی نیست؛ بلکه با تغییر متن و نحوهی اجرا، میتوان شنود را غیر فعال نمود. در کد زیر با تغییر نحوه اجرای پروسیجر، قابلیت شنود پارامتر غیر فعال شده است:
CREATE PROC [dbo].[DisplayBillingInfo] @BeginDate DATETIME, @EndDate DATETIME WITH RECOMPILE AS DECLARE @StartDate DATETIME; DECLARE @StopDate DATETIME; SET @StartDate = @BeginDate; SET @StopDate = @EndDate; SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @StartDate AND @StopDate;
راه حل سوم: ایجاد چند نوع پروسیجر
راه دیگر، ایجاد پروسیجرهای متفاوت برای پارامترهایی با کاردینالیتی متفاوت است. بهعبارت دیگر، دسته بندی پارامترهای ارسالی و ایجاد پروسیجرهایی خاص همان دسته. در مثالهای این سری از مطالب، دو دسته پارامتر 1) بازه زمانی کوتاه، مثلا چند روز و 2) بازه زمانی بلند، مثلا ماهیانه وجود داشت که میتوانید 2 دسته پروسیجر را یکی برای بازههای روزانه و دیگری برای بازههای زمانی ماهیانه ایجاد نمایید.
CREATE PROC [dbo].[DisplayBillingInfoNarrow] @BeginDate DATETIME, @EndDate DATETIME AS SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @BeginDate AND @EndDate; GO CREATE PROC [dbo].[DisplayBillingInfoWide] @BeginDate DATETIME, @EndDate DATETIME AS SELECT BillingDate, BillingAmt FROM BillingInfo WHERE BillingDate between @BeginDate AND @EndDate; GO DROP PROCEDURE [dbo].[DisplayBillingInfo]; GO CREATE PROC [dbo].[DisplayBillingInfo] @BeginDate DATETIME, @EndDate DATETIME AS IF DATEDIFF(DD,@BeginDate, @EndDate) < 4 EXECUTE DisplayBillingInfoNarrow @BeginDate, @EndDate ELSE EXECUTE DisplayBillingInfoWide @BeginDate, @EndDate GO
Lambda Syntax و کارآیی
مسلماْ کامپایلر خیلی سریعتر از برنامه نویسها میتوانند این موارد رو ایجاد کند.
پس قبل از اجرای برنامه متدها و هندلرها وجود دارند و نمیتواند از این بابت در سرعت اجرا تاثیری داشته باشند. بنظر من تنها دلیل کندتر بودن (خیلی کم) متدهای بی نام در زمان اجرا این است که چون متدهای بی نام در کلاسی مجزا تعریف شده است نیاز به نمونه سازی (
newobj
) دارند در حالی که متدهای بانام از این مورد مبرا هستند.با این توصیف و با چشم پوشی نسبت به زمان اندکی که کامپایلر صرف متدهای بی نام میکند استفاده از متدهای بی نام بصرفهتر هستند و برنامه رو هم زیباتر و خواناتر میکنند.
نحوه فعالسازی NET Core 2.1 Preview 2. در Visual Studio 2017
Runtime کار هاست و اجرای برنامهها را انجام میدهد و عموما برای توزیع برنامهها نیاز است. البته با نصب SDK مجموعهی Runtime هم نصب میشود و نیازی به نصب مجزا ندارد.
اما در CPUها چند هسته ای امروزی هر هسته قادر به اجرای یک وظیفه به صورت مجزا میباشد و این CPUها برای انجام کارهای همزمان عملکرد بسیار بسیار بهتری نسبت به CPUهای تک هسته ای دارند.
با توجه به این موضوع برای اینکه بتوان از قابلیتهای چند هسته ای CPUهای امروزی استفاده کرد باید برنامه نویسی موازی (Parallel Programming) انجام داد و روشهای کلاسیک مناسب این کار نمیباشند.