اگر فرقی احساس نکردید منتظر نگارش بعدی EF باشید. این مورد رو برطرف کردن:
«significantly improved warm up time (view generation), especially for large models»
- Performance : کارآیی - که در ارتباط با مدت زمان پاسخ گویی و قابل استفاده شدن برنامه میباشد. (مقایسه به درصد - هرچه بیشتر عملکرد بهتر)
- Size : اندازه - که نشان دهنده حجم نهایی فایلهای تولید شده ( Css-Html-JavaScript ) فریم ورک است. این مقایسه اندازه فریم ورک و تمام وابستگیهای آن است که به bundle نهایی برنامه اضافه شده است (هر چه اندازه فایل کمتر باشد بهتر است چراکه توسط کاربر نهایی زودتر دانلود میشود).
- Lines of Code : تعداد خطوط کد - نشان دهنده این است که یک نویسنده بر اساس این فریم ورکها چند سطر کد را باید برای تهیهی یک برنامهی جدید بنویسد.
npx degit sveltejs/template my-svelte-project cd my-svelte-project npm install npm run dev
We're going to use Dapper.NET on our project; that much is not in doubt. However, we're not going to start development with it, and it will not be the only ORM in use. The plan is to develop this project using Entity Framework, and later optimize to use Dapper.NET in certain scenarios where the system needs a performance boost.
نگاهی سریع به فریم ورک های MVVM
One year ago MVVM wasn’t very famous. I remember the first article I read about it, about using MVVM to simplify the management of treeview controls. In the last six months, MVVM has been quickly promoted to THE methodology to use when developing WPF applications. During this amount of time, famous WPF developers started to merge their existing MVVM classes into libraries. Those libraries are now known as MVVM frameworks and contain classes designed to help developers to use MVVM in their projects
ایجاد یک برنامهی خالی React برای آزمایش توابع Redux
در اینجا برای بررسی توابع Redux، یک پروژهی جدید React را ایجاد میکنیم:
> npm i -g create-react-app > create-react-app state-management-redux-mobx > cd state-management-redux-mobx > npm start
> npm install --save redux
معرفی سریع توابع Redux
Redux، کتابخانهی کوچکی است و تنها از 5 تابع تشکیل شدهاست:
applyMiddleware: function() bindActionCreators: function() combineReducers: function() compose: function() createStore: function()
بررسی تابع compose با یک مثال
پس از ایجاد پروژهی React و افزودن کتابخانهی Redux به آن، به فایل src\index.js این پروژه مراجعه کرده و محتویات آنرا با قطعه کد ذیل، تعویض میکنیم:
import { compose } from "redux"; const makeLouder = text => text.toUpperCase(); const repeatThreeTimes = text => text.repeat(3); const embolden = text => text.bold(); const makeLouderAndRepeatThreeTimesAndEmbolden = compose( embolden, repeatThreeTimes, makeLouder ); console.log(makeLouderAndRepeatThreeTimesAndEmbolden("Hello"));
- سپس سه تابع ساده را برای ضخیم کردن، تکرار و با حروف بزرگ نمایش دادن یک متن ورودی، تعریف کردهایم.
- اکنون با استفاده از متد compose کتابخانهی redux، این سه متد را به صورت ترکیبی، بر روی متن ورودی Hello، اعمال کردهایم.
- در آخر اگر برای مشاهدهی نتیجهی اجرای console.log بر روی آن، به کنسول توسعه دهندگان مرورگر مراجعه کنیم، به خروجی زیر خواهیم رسید:
<b>HELLOHELLOHELLO</b>
بررسی تابع createStore با یک مثال
Store در Redux، جائی است که در آن state برنامه ذخیره میشود. تابع createStore که کار ایجاد store را انجام میدهد، یک پارامتر را دریافت میکند و آنهم تابع reducer است که در قسمت قبل معرفی شد و در سادهترین حالت آن، به نحو زیر با یک متد خالی، قابل فراخوانی است:
import { createStore } from "redux"; createStore(() => {});
import { createStore } from "redux"; const reducer = (state, action) => { return state; }; const store = createStore(reducer); console.log(store);
در شیء store، چهار متد dispatch, subscribe, getState, replaceReducer مشخص هستند:
- کارکرد متد replaceReducer مشخص است؛ یک reducer جدید را به آن میدهیم و reducer قبلی را جایگزین میکند.
- متد dispatch آن مرتبط است به مبحث dispatch actions که در قسمت قبل به آن پرداختیم. هدف آن تغییر state، بر اساس یک action رسیدهاست.
- متد getState، وضعیت فعلی state را باز میگرداند.
- متد subscribe با هر تغییری در state، سبب بروز رخدادی میشود. یکی از کاربردهای آن میتواند به روز رسانی UI، بر اساس تغییرات state باشد. برای مثال کتابخانهی دیگری به نام react redux، دقیقا همین کار را به کمک متد subscribe، انجام میدهد. در این قسمت، هدف ما بررسی پشت صحنهی کتابخانههایی مانند react redux است که چه متدهایی را محصور کردهاند و دقیقا چگونه کار میکنند.
در مثال زیر، مقدار ابتدایی پارامتر state متد reducer را به یک شیء که دارای خاصیت value و مقدار 1 است، تنظیم کردهایم:
import { createStore } from "redux"; const reducer = (state = { value: 1 }, action) => { return state; }; const store = createStore(reducer); console.log(store.getState());
در کامپوننتهای React، این نوع خواص به صورت props ارسال میشوند. کل state در Redux ذخیره شده و سپس قابل دسترسی و خواندن خواهد شد.
بررسی متد dispatch یک store با مثال
در اینجا مثالی را از کاربرد متد dispatch ملاحظه میکنید که یک شیء را میپذیرد:
import { compose, createStore } from "redux"; const reducer = (state = { value: 1 }, action) => { console.log("Something happened!", action); return state; }; const store = createStore(reducer); console.log(store.getState()); store.dispatch({ type: "ADD" });
فرمت شیءای که به متد dispatch ارسال میشود، حتما باید به همراه خاصیت type باشد؛ در غیر اینصورت با صدور استثنائی، این نکته را گوشزد میکند. مقدار آن نیز یک رشتهاست که بیانگر وقوع رخدادی در برنامه میباشد؛ برای مثال کاربر درخواست دریافت اطلاعاتی را کردهاست و یا کاربر از سیستم خارج شدهاست و امثال آن.
خروجی قطعه کد فوق، به صورت زیر است:
با اجرای برنامه، یک رخداد درونی از نوع redux/INITo.2.g.b.y.i@@ صادر شدهاست که هدف آن آغاز و مقدار دهی اولیهی store است. پس از آن زمانیکه متد store.dispatch فراخوانی شدهاست، مجددا console.log داخل متد reducer فراخوانی شدهاست که اینبار به همراه type ای است که ما مشخص کردهایم.
در حالت کلی، اینکه شیء ارسالی توسط dispatch را چگونه طراحی میکنید، اختیاری است؛ اما الگوی پیشنهادی در این زمینه، redux standard actions نام دارد و به صورت زیر است که هدف از آن، یکدست شدن طراحی و پیاده سازی برنامه است:
store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });
- سپس به خاصیت payload، تمام دادههای مرتبط با آن اکشن، مانند نتیجهی بازگشتی از یک API، به صورت یک شیء، انتساب داده میشود.
- خاصیت meta، مرتبط با متادیتا و اطلاعات اضافی است که عموما استفاده نمیشود.
اکنون که طراحی شیء ارسالی به پارامتر action متد reducer مشخص شد، میتوان بر اساس خاصیت type آن، یک switch را طراحی کرد و نسبت به نوعهای مختلف رسیده، واکنش نشان داد:
import { createStore } from "redux"; const reducer = (state = { value: 1 }, action) => { console.log("Something happened!", action); if (action.type === "ADD") { const value = state.value; const amount = action.payload.amount; state.value = value + amount; } return state; }; const store = createStore(reducer); const firstState = store.getState(); console.log(firstState); store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} }); const secondState = store.getState(); console.log(secondState); console.log("secondState === firstState", secondState === firstState);
یک روش حل این مشکل، حذف سطر state.value = value + amount و جایگزینی آن با یک return است که شیء state جدیدی را بازگشت میدهد:
return { value: value + amount };
بررسی متد subscribe یک store با مثال
در ادامه به store همان مثال فوق، متد subscribe را نیز اضافه میکنیم و سپس دوبار، کار store.dispatch را انجام خواهیم داد:
const store = createStore(reducer); const unsubscribe = store.subscribe(() => console.log(store.getState())); store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} }); store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} }); unsubscribe();
- خروجی مستقیم متد store.subscribe نیز یک متد است که unsubscribe نام دارد و میتوان در پایان کار، برای جلوگیری از نشتیهای حافظه، آنرا فراخوانی کرد.
خروجی حاصل از console.log منتسب به متد store.subscribe، با دوبار فراخوانی متد store.dispatch پس از آن، به صورت زیر است:
{value: 3} {value: 5}
بررسی تابع combineReducers با یک مثال
اگر قرار باشد در متد reducer، صدها if action.type را تعریف کرد ... پس از مدتی از کنترل خارج میشود و غیرقابل نگهداری خواهد شد. تابع combineReducers به همین جهت طراحی شدهاست تا چندین متد مجزای reducer را با هم ترکیب کند. بنابراین میتوان در برنامه صدها متد کوچک reducer را که قابلیت توسعه و نگهداری بالایی را دارند، پیاده سازی کرد و سپس با استفاده از تابع combineReducers، آنها را یکی کرده و به متد createStore ارسال کرد. نکتهی مهم اینجا است که هرچند اینبار میتوان تعداد زیادی reducer را طراحی کرد، اما توسط تابع combineReducers، مقدار action ارسالی، از تمام این reducerها عبور داده خواهد شد. به این ترتیب اگر نیاز بود میتوان به یک action، در چندین متد مختلف reducer گوش فرا داد و بر اساس آن تصمیم گیری کرد. بنابراین بهتر است هر reducer تعریف شده در صورتیکه action.type آن با action رسیده تطابق نداشته باشد، همان state قبلی را بازگشت دهد تا بتوان زنجیرهی reducerها را تشکیل داد و بهتر مدیریت کرد.
برای نمونه اگر متد reducer فعلی را به calculatorReducer تغییر نام دهیم، روش معرفی آن توسط متد combineReducers به متد createStore به صورت زیر است:
import { combineReducers, createStore } from "redux"; // ... const calculatorReducer = (state = { value: 1 }, action) => { // ... }; const store = createStore( combineReducers({ calculator: calculatorReducer }) );
بررسی تابع bindActionCreators با یک مثال
فرض کنید میخواهیم متد dispatch را چندین بار فراخوانی کنیم:
store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} }); store.dispatch({ type: "ADD", payload: { amount: 2 }, meta: {} });
const createAddAction = amount => { return { type: "ADD", payload: { amount // = amount: amount }, meta: {} }; };
store.dispatch(createAddAction(2)); store.dispatch(createAddAction(2));
import { bindActionCreators, combineReducers, compose, createStore } from "redux"; // ... const dispatchAdd = bindActionCreators(createAddAction, store.dispatch); dispatchAdd(2); dispatchAdd(2);
میانافزارها (Middlewares) در Redux
پس از اینکه یک اکشن، به سمت reducer ارسال شد و پیش از رسیدن آن به reducer، میتوان کدهای دیگری را نیز اجرا کرد. برای مثال چون این توابع خالص هستند، نمیتوان اعمالی را داخل آنها اجرا کرد که به همراه اثرات جانبی مانند کار با یک API خارجی باشند. با استفاده از میانافزارها در این بین میتوان کدهایی را که با دنیای خارج تعامل دارند نیز اجرا کرد.
یک میانافزار در Redux، همانند سایر قسمتهای آن، تنها یک تابع سادهی جاوا اسکریپتی است:
const logger = ({ getState }) => { return next => action => { console.log( 'MIDDLEWARE', getState(), action ); const value = next(action); console.log({value}); return value; } }
در پایان کار میانافزار باید متد next آنرا فراخوانی کرد تا بتوان میانافزارهای متعددی را زنجیروار اجرا کرد.
در آخر برای معرفی آن به یک store میتوان از تابع applyMiddleware استفاده کرد:
import { applyMiddleware, bindActionCreators, combineReducers, compose, createStore } from "redux"; // ... const store = createStore( combineReducers({ calculator: calculatorReducer }), applyMiddleware(logger) );
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: state-management-redux-mobx-part02.zip
بررسی ساختار کامپوننت Pagination
شبیه به کامپوننت Like که در قسمت قبل ایجاد کردیم، میخواهیم کامپوننت جدید Pagination نیز به طور کامل از اشیاء movie مستقل باشد؛ تا در آینده بتوان از آن در جاهای دیگری نیز استفاده کرد. به همین جهت فایل جدید src\components\common\pagination.jsx را ایجاد کرده و سپس با استفاده از میانبرهای imrc و cc در VSCode، ساختار ابتدایی این کامپوننت را ایجاد میکنیم. هرچند میتوان این کامپوننت را به صورت «Stateless Functional Component» نیز طراحی کرد؛ چون state و متد دیگری بجز render نخواهد داشت و تمام اطلاعات خودش را از والد خود دریافت میکند.
سپس به کامپوننت movies مراجعه کرده و این کامپوننت خالی را import میکنیم:
import Pagination from "./common/pagination";
<Pagination itemsCount={this.state.movies.length} pageSize={this.state.pageSize} onPageChange={this.handlePageChange} />
state = { movies: getMovies(), pageSize: 4 };
handlePageChange = page => { console.log("handlePageChange", page); };
نمایش شماره صفحات گرید، در کامپوننت صفحه بندی
برای رندر کامپوننت صفحه بندی، از کلاسهای مخصوص اینکار که در بوت استرپ تعریف شدهاند، استفاده میکنیم که ساختار کلی آن به صورت زیر است و از یک المان nav که داخل آن ul ای با کلاس pagination و liهایی با کلاس page-item هستند، تشکیل میشود. هر li، به همراه یک anchor است؛ با کلاس page-link تا لینک به صفحهای خاص را ارائه دهد که در اینجا بجای لینک، از کلیک بر روی آنها استفاده خواهیم کرد:
import React, { Component } from "react"; class Pagination extends Component { render() { return ( <nav> <ul className="pagination"> <li className="page-item"> <a className="page-link">1</a> </li> </ul> </nav> ); } } export default Pagination;
تا اینجا اگر برنامه را ذخیره کرده و اجرا کنید، عدد 1 را در پایین جدول فیلمها مشاهده خواهید کرد:
اکنون باید رندر این liها را بر اساس ورودیهای این کامپوننت که پیشتر معرفی کردیم، یعنی pageSize و itemsCount، پویا کنیم. به همین جهت نیاز به آرایهای داریم که بر اساس این ورودیها، شمارهی صفحات مانند [1,2,3] را ارائه دهد تا بر روی آن متد Array.map را فراخوانی کرده و liهای مورد نیاز را به صورت پویا رندر کنیم:
class Pagination extends Component { // ... getPageNumbersArray() { const { itemsCount, pageSize } = this.props; const pagesCount = Math.ceil(itemsCount / pageSize); if (pagesCount === 1) { return null; } const pages = new Array(); for (let i = 1; i <= pagesCount; i++) { pages.push(i); } return pages; } }
سپس به کمک متد map، اعضای این آرایه را تبدیل به لیست liهای نمایش شماره صفحات میکنیم. در اینجا key هر li را نیز به شماره صفحه که منحصربفرد است، تنظیم کردهایم:
class Pagination extends Component { render() { const pages = this.getPageNumbersArray(); if (!pages) { return null; } return ( <nav> <ul className="pagination"> {pages.map(page => ( <li key={page} className="page-item"> <a className="page-link">{page}</a> </li> ))} </ul> </nav> ); }
پس از ذخیرهی این کامپوننت و بارگذاری مجدد برنامه در مرورگر، شمارهی صفحات رندر شده، در پایین جدول مشخص هستند:
با داشتن 9 فیلم در آرایهی movies و نمایش 4 فیلم به ازای هر صفحه، به 3 صفحه خواهیم رسید که به درستی در اینجا رندر شدهاست. یکبار هم برای آزمایش بیشتر، مقدار pageSize را در کامپوننت movies به 10 تنظیم کنید. در این حالت کامپوننت صفحه بندی نباید رندر شود.
مدیریت انتخاب شمارههای صفحات
در این قسمت میخواهیم مدیریت onPageChange={this.handlePageChange} را که به تعریف المان صفحه بندی در کامپوننت movies اضافه کردیم، تکمیل کنیم. برای این منظور در کامپوننت صفحه بندی، قسمت anchor را به صورت زیر تغییر میدهیم تا با کلیک بر روی آن، رخداد onPageChange صادر شود:
<a onClick={() => this.props.onPageChange(page)} className="page-link" style={{ cursor: "pointer" }} > {page} </a>
تا اینجا اگر برنامه را آزمایش کنیم، با کلیک بر روی لینکهای شماره صفحات، شماره صفحهی انتخابی، در کنسول توسعه دهندگان مرورگر لاگ میشود.
اکنون میخواهیم اگر صفحهای انتخاب شد، شمارهی آن صفحه با رنگی دیگر نمایش داده شود. در بوت استرپ برای اینکار تنها کافی است کلاس active را به className هر li اضافه کنیم و برعکس. یعنی اگر page ای مساوی صفحهی جاری انتخاب شده بود (currentPage در اینجا)، آنگاه کلاس page-item active، به المان li اضافه شود. بنابراین در این کامپوننت نیاز است عدد currentPage را نیز دریافت کنیم. به همین جهت ویژگی currentPage را به تعریف المان Pagination در کامپوننت movies اضافه میکنیم:
<Pagination itemsCount={this.state.movies.length} pageSize={this.state.pageSize} onPageChange={this.handlePageChange} currentPage={this.state.currentPage} />
class Movies extends Component { state = { movies: getMovies(), pageSize: 4, currentPage: 1 };
handlePageChange = page => { console.log("handlePageChange", page); this.setState({currentPage: page}); };
پس از این تغییرات، به کامپوننت صفحه بندی مراجعه کرده و بر اساس currentPage دریافتی، کلاس active را به المان li اعمال میکنیم:
<li key={page} className={ page === this.props.currentPage ? "page-item active" : "page-item" } >
در اولین بار نمایش برنامه، عدد 1 در حالت انتخاب شده قرار دارد؛ چون مقدار currentPage موجود در state، همان عدد 1 است. پس از آن با کلیک بر روی اعداد دیگر، با به روز رسانی state، مقدار currentPage تغییر کرده و کامپوننت صفحه بندی نسبت به آن واکنش نشان میدهد.
نمایش صفحه بندی شدهی اطلاعات
تا اینجا لیستی که نمایش داده میشود، حاوی تمام اطلاعات آرایهی this.state.movies است و بر اساس شمارهی صفحهی انتخابی، تغییر نمیکند. به همین جهت با استفاده از متد slice، تکهای از آرایهی movies را که بر اساس شماره صفحهی انتخابی و تعداد ردیفها در هر صفحه نیاز است نمایش داده شود، انتخاب کرده و بازگشت میدهیم:
paginate() { const first = (this.state.currentPage - 1) * this.state.pageSize; const last = first + this.state.pageSize; return this.state.movies.slice(first, last); }
render() { const { length: count } = this.state.movies; if (count === 0) return <p>There are no movies in the database.</p>; const movies = this.paginate();
پس از ذخیرهی تغییرات و بارگذاری مجدد برنامه، اکنون میتوان نمایش صفحه بندی شدهی اطلاعات را شاهد بود:
بررسی صحت نوع پارامترهای ارسالی به کامپوننتها
تا اینجا فرض بر این است که مصرف کنندهی کامپوننت صفحه بندی، دقیقا همان ویژگیهایی را که ما طراحی کردهایم، با همان نامها و همان نوعها را حتما به آن ارسال میکند. همچنین اگر افزونهی eslint را هم در VSCode نصب کرده باشید، به همراه نصب وابستگیهای زیر در خط فرمان:
> npm i -g typescript eslint tslint eslint-plugin-react-hooks jshint babel-eslint eslint-plugin-react eslint-plugin-mocha
به ازای هر خاصیت props استفاده شدهی در کامپوننت صفحه بندی، اخطاری را مانند «'currentPage' is missing in props validation eslint(react/prop-types)» مشاهده خواهید کرد:
که عنوان میکند props validation این خاصیت استفاده شده، فراموش شدهاست.
در نگارشهای قبلی React، امکانات بررسی نوعهای ارسالی به کامپوننتها، جزئی از بستهی اصلی آن بود؛ اما از نگارش 15 به بعد، به بستهی مستقلی منتقل شدهاست که باید به صورت جداگانهای در ریشهی پروژه نصب شود:
> npm i prop-types --save
البته اگر TypeScript را بر روی سیستم خود نصب کرده باشید، دیگر نیازی به نصب بستهی npm فوق را ندارید و prop-types، جزئی از آن است که عموما در یک چنین مسیری قرار دارد و برای کار کردن با آن، تنها ذکر import مرتبط با PropType در ماژولهای برنامه کافی بوده و برنامه در این حالت بدون مشکل کامپایل میشود:
C:/Users/{username}/AppData/Local/Microsoft/TypeScript/3.6/node_modules/@types/prop-types/index
اکنون در ابتدای فایل کامپوننت صفحه بندی، تعریف زیر را اضافه میکنیم:
import PropTypes from "prop-types";
Pagination.propTypes = { itemsCount: PropTypes.number.isRequired, pageSize: PropTypes.number.isRequired, currentPage: PropTypes.number.isRequired, onPageChange: PropTypes.func.isRequired }; export default Pagination;
سپس مقدار این خاصیت جدید را به شیءای تنظیم میکنیم که نام خواص آن، دقیقا همان نام خواص و رویدادهای props استفاده شدهی در این کامپوننت است. در ادامه توسط PropTypes ای که در ابتدای ماژول import میشود، کار تعریف نوع این خواص و اجباری بودن آنها را میتوان مشخص کرد که برای مثال در اینجا سه خاصیت تعریف شده از نوع عددی و اجباری بوده و onPageChange، از نوع func است.
پس از اضافه کردن Pagination.propTypes و مقدار دهی آن، خطاهای eslint ای که در تصویر فوق مشاهده کردید، برطرف میشوند. همچنین اگر فراخوان کامپوننت Pagination مثلا بجای itemsCount یک رشتهی فرضی را وارد کند (برای آزمایش آن در کامپوننت movies، در تعریف المان Pagination، مقدار itemsCount را یک رشته وارد کنید)، چنین خطایی در مرورگر ظاهر خواهد شد که عنوان میکند itemsCount یک عدد را میپذیرد و نوع ورودی آن اشتباه است:
البته این خطا فقط در حالت development مشاهده میشود و در حالت توزیع برنامه، خیر.
بنابراین تعریف propTypes یک best practice ایجاد کامپوننتهای React است که نه فقط بررسی نوعها را فعال میکند، بلکه میتواند به عنوان مستندات آن نیز در جهت تعیین props مورد نیاز، همچنین نوع و اجباری بودن آنها، اطلاعات کاملی را ارائه کند.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-11.zip
مقاله درباره ViewModel
Use ViewModels to manage data & organize code in ASP.NET MVC applications
The concept of the ViewModel isn't just for ASP.NET MVC, as you'll see references to ViewModels throughout the web in articles and blog posts about the MVC, MVP, and MVVM patterns. Those posts and articles can center around any number of technologies such as ASP.NET, Silverlight, WPF, or MVC... This post will investigate ViewModels as they apply to the world of ASP.NET MVC.
CSS3 (~2009-2012):
Level 3 CSS specs as defined by the CSSWG. (immutable)
CSS4 (~2013-2018):
Essential features that were not part of CSS3 but are already a fundamental part of CSS.
CSS5 (~2019-2024):
Newer features whose adoption is steadily growing.
CSS6 (~2025+):
Early-stage features that are planned for future CSS.