React 16x - قسمت 1 - معرفی و شروع به کار
برپایی پیشنیازها
ابتدا کتابخانهی react-router-dom را نصب میکنیم:
npm i react-router-dom --save
import { BrowserRouter } from "react-router-dom"; //... ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById("root") );
برای تکمیل نیازمندیهایی که در مقدمه عنوان شد، این کامپوننتهای جدید را ایجاد میکنیم:
کامپوننت بدون حالت تابعی src\components\customers.jsx با این محتوا:
import React from "react"; const Customers = () => { return <h1>Customers</h1>; }; export default Customers;
کامپوننت بدون حالت تابعی src\components\rentals.jsx با این محتوا:
import React from "react"; const Rentals = () => { return <h1>Rentals</h1>; }; export default Rentals;
کامپوننت بدون حالت تابعی src\components\notFound.jsx با این محتوا:
import React from "react"; const NotFound = () => { return <h1>Not Found</h1>; }; export default NotFound;
کامپوننت بدون حالت تابعی src\components\movieForm.jsx با این محتوا:
import React from "react"; const MovieForm = () => { return ( <div> <h1>Movie Form</h1> <button className="btn btn-primary">Save</button> </div> ); }; export default MovieForm;
ثبت مسیریابیهای مورد نیاز برنامه
پس از نصب کتابخانهی مسیریابی و راه اندازی آن، اکنون نوبت به تعریف مسیریابیهای مورد نیاز برنامه در فایل app.js است:
import "./App.css"; import React from "react"; import { Redirect, Route, Switch } from "react-router-dom"; import Customers from "./components/customers"; import Movies from "./components/movies"; import NotFound from "./components/notFound"; import Rentals from "./components/rentals"; function App() { return ( <main className="container"> <Switch> <Route path="/movies" component={Movies} /> <Route path="/customers" component={Customers} /> <Route path="/rentals" component={Rentals} /> <Route path="/not-found" component={NotFound} /> <Redirect to="/not-found" /> </Switch> </main> ); } export default App;
تا اینجا اگر برنامه را اجرا کنیم، بلافاصله به http://localhost:3000/not-found هدایت میشویم. از این جهت که هنوز مسیریابی را برای / یا ریشهی سایت که در ابتدا نمایش داده میشود، تنظیم نکردهایم. به همین جهت Redirect زیر را پیش از آخرین Redirect تعریف شده اضافه میکنیم تا با درخواست ریشهی سایت، به آدرس /movies هدایت شویم:
<Redirect from="/" to="/movies" />
<Redirect from="/" exact to="/movies" />
افزودن منوی راهبری به برنامه
ابتدا فایل جدید src\components\navBar.jsx را ایجاد میکنیم؛ با این محتوا:
import React from "react"; import { Link, NavLink } from "react-router-dom"; const NavBar = () => { return ( <nav className="navbar navbar-expand-lg navbar-light bg-light"> <Link className="navbar-brand" to="/"> Home </Link> <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation" > <span className="navbar-toggler-icon" /> </button> <div className="collapse navbar-collapse" id="navbarNavAltMarkup"> <div className="navbar-nav"> <NavLink className="nav-item nav-link" to="/movies"> Movies </NavLink> <NavLink className="nav-item nav-link" to="/customers"> Customers </NavLink> <NavLink className="nav-item nav-link" to="/rentals"> Rentals </NavLink> </div> </div> </nav> ); }; export default NavBar;
- ساختار کلی NavBar ای را که ملاحظه میکنید، دقیقا از مثالهای رسمی مستندات بوت استرپ 4 گرفته شدهاست و تمام classهای آن با className جایگزین شدهاند.
- سپس تمام anchorهای موجود در یک منوی راهبری بوت استرپ را به Link و یا NavLink تبدیل کردهایم تا برنامه به صورت SPA عمل کند؛ یعنی با کلیک بر روی هر لینک، بارگذاری کامل صفحه در مرورگر صورت نگیرد و تنها محل و قسمتی که توسط کامپوننتهای Route مشخص شده، به روز رسانی شوند. تفاوت NavLink با Link در کتابخانهی react-router-dom، افزودن خودکار کلاس active به المانی است که بر روی آن کلیک شدهاست. به این ترتیب بهتر میتوان تشخیص داد که هم اکنون در کجای منوی راهبری قرار داریم.
- پس از تبدیل anchorها به Link و یا NavLink، مرحلهی بعد، تبدیل hrefهای لینکهای قبلی به ویژگی to است که هر کدام باید به یکی از مسیریابیهای تنظیم شده، مقدار دهی گردد.
پس از تعریف کامپوننت منوی راهبری سایت، به app.js بازگشته و این کامپوننت را پیش از مسیریابیهای تعریف شده اضافه میکنیم:
import NavBar from "./components/navBar"; // ... function App() { return ( <React.Fragment> <NavBar /> <main className="container"> // ... </main> </React.Fragment> ); } export default App;
به علاوه به فایل index.css برنامه مراجعه کرده و padding این navBar را صفر میکنیم تا از بالای صفحه و بدون فاصلهای نمایش داده شود و container اصلی نیز اندکی از پایین آن فاصله پیدا کند:
body { margin: 0; padding: 0 0 0 0; font-family: sans-serif; } .navbar { margin-bottom: 30px; } .clickable { cursor: pointer; }
اگر دقت کنید چون آدرس http://localhost:3000/movies در حال نمایش است، در منوی راهبری، گزینهی متناظر با آن، با رنگی دیگر مشخص (فعال) شدهاست.
لینک کردن عناوین فیلمهای نمایش داده شده به کامپوننت movieForm
برای تبدیل عناوین نمایش داده شدهی در جدول فیلمها به لینک، به کامپوننت src\components\moviesTable.jsx مراجعه کرده و تغییرات زیر را اعمال میکنیم:
- در قدم اول باید بجای ذکر خاصیت Title در آرایهی ستونهای جدول:
class MoviesTable extends Component { columns = [ { path: "title", label: "Title" },
class MoviesTable extends Component { columns = [ { path: "title", label: "Title", content: movie => <Link to={`/movies/${movie._id}`}>{movie.title}</Link> },
همچنین این Link را هم باید در بالای این ماژول import کرد:
import { Link } from "react-router-dom";
تعریف مسیریابی نمایش جزئیات یک فیلم انتخابی
اگر به تصویر فوق دقت کنید، به آدرسهایی مانند http://localhost:3000/movies/5b21ca3eeb7f6fbccd47181a رسیدهایم که به همراه id هر فیلم هستند. اکنون میخواهیم کلیک بر روی این لینکها را جهت فعالسازی صفحهی نمایش جزئیات فیلم، تنظیم کنیم. به همین جهت به فایل app.js مراجعه کرده و مسیریابی زیر را به ابتدای Switch تعریف شده اضافه میکنیم:
<Route path="/movies/:id" component={MovieForm} />
import MovieForm from "./components/movieForm";
تکمیل کامپوننت نمایش جزئیات یک فیلم
اکنون میخواهیم صفحهی نمایش جزئیات فیلم، به همراه نمایش id فیلم باشد و همچنین با کلیک بر روی دکمهی Save آن، کاربر را به صفحهی movies هدایت کند. به همین جهت فایل src\components\movieForm.jsx را به صورت زیر ویرایش میکنیم:
import React from "react"; const MovieForm = ({ match, history }) => { return ( <div> <h1>Movie Form {match.params.id} </h1> <button className="btn btn-primary" onClick={() => history.push("/movies")} > Save </button> </div> ); }; export default MovieForm;
- چون این کامپوننت، یک کامپوننت تابعی بدون حالت است، props را باید از طریق آرگومان خود دریافت کند و البته در همینجا امکان Object Destructuring خواصی که از آن نیاز داریم، مهیا است؛ مانند { match, history } که ملاحظه میکنید.
- سپس شیء match، امکان دسترسی به params ارسالی به صفحه را مانند id فیلم، میسر میکند.
- با استفاده از شیء history و متد push آن میتوان علاوه بر به روز رسانی تاریخچهی مرورگر، به مسیر مشخص شده بازگشت که در همینجا و به صورت inline، تعریف شدهاست.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-17.zip
- توابع خط فرمان - Command Line API
- توابع کنسول - Console API
<div id="first" class="content">Content1 with css class and id</div> <div class="content"> Content2 with css class <a class="links" href="#">Link1</a> <a href="#">Link2</a> </div> <div> Content3 without css class and id </div> <input type="button" onclick="myFunc()" value="Run myFunc" /> <input type="text" id="myInput" /> <script type="text/javascript"> function myFunc() { loop(1000); loop(50000); } function loop(number) { for (var i = 0; i < number; i++) { } } </script>
$$("div.content");
profile("myFunc Testing"); myFunc(); profileEnd();
- $(id)
معادل دستور document.getElementById است که یک المنت با id داده شده بر میگرداند .
$("first");
- $$(selector)
آرایه ای از المنتهای مطابق با selector داده شده بر میگرداند .
$$("div.content")
به تفاوت دو دستور توجه کنید . خروجی دستور اول ، یک المنت است و خروجی دستور دوم یک آرایه از المنت که بین [ و ] قرار گرفته اند .
برای آشنایی بیشتر با CSS Seletorها به این لینک مراجعه کنید : http://www.w3.org/TR/css3-selectors
- $x(xPathExpression)
آرایه ای از المنت هایی را بر میگرداند که با xPath داده شده مطابقت داشته باشند .
var objects = $x("html/body/div[2]/a") for(var i = 0; i < objects.length; i++) { console.log(objects[i]); }
برای آشنایی بیشتر با عبارات xPath به این لینک مراجعه کنید : http://www.w3schools.com/xpath
- dir(object)
تمام خصوصیات شیء ارسال شده را لیست میکند .
var objects = $x("html/body/div[2]/a") dir(objects);
- dirxml(node)
سورس یک المنت را بصورت درختواره ( tree ) پرینت میکند . همچنین با کلیک بروی هر node ، فایرباگ آن node را در تب html نمایش میدهد .
var node = $("first"); dirxml(node);
توجه کنید که این دستور فقط یک node دریافت میکند . برای همین اگر از دستور $$("#first") استفاده میکنید ، چون این دستور یک آرایه بر میگرداند ، باید اولین عضو آرایه را دریافت و ارسال کنید .
یعنی :
var node = $$("#first")[0]; dirxml(node);
- clear()
این دستور محیط console را خالی میکند . عملکرد این دستور معادل کلیک دکمهی Clear ( در بالا - چپ تب کنسول ) است .
- inspect(object[,tabName])
توسط این دستور میتوانید یک شیء را در مناسبترین تب فایرباگ یا یکی از تبهای مورد نظر خود ، Inspect کنید .
var node = $("first"); inspect(node); // inspect in html tab inspect(node,'dom'); // inspect in dom tab
- keys(object)
آرایه ای از "نام" تمام خصوصیات شیء ارسال شده بر میگرداند .
var obj = $("first"); keys(obj)
- values(object)
آرایه ای از "مقدار" تمام خصوصیات شیء ارسال شده بر میگرداند .
var obj = $("first"); values(obj)
- debug(fn) and undebug(fn)
این متدها یک BreakPoint در ابتدای تابع مشخص شده اضافه/حذف میکنند . ( در تب Script ) . به همین ترتیب هنگامی که تابع مورد نظر فراخوانی شود ، در نقطه ای که BreakPoint قرار داده شده توقف خواهد کرد .
البته میشود BreakPoint را دستی هم قرار داد . در اصل این تابع ، این عملیات را سادهتر میکند .
debug(myFunc); myFunc(); undebug(myFunc);
- monitor(fn) and unmonitor(fn)
این متدها برای فعال/غیرفعال کردن Logging فراخوانیهای یک تابع استفاده میشوند .
در حالت عادی برای پی بردن به اینکه یک تابع اجرا میشود یا نه ، در تابع مورد نظر یک alert قرار میدهیم و تست میکنیم . که این روش در برنامه برنامههای بزرگ صحیح نیست . زیرا در این حالت باید بین حجم زیادی کد به دنبال تابع مورد نظر بگردیم و سپس alert را قرار بدهیم و بعد اطمینان از صحت عملکرد تابع مجدد آن را حذف کرد ، که با اتلاف زمان و به خطر انداختن کدها همراه است .
اما با استفاده از این متدها ، تنها نیاز به داشتن اسم تابع داریم ( و نه مکان تابع در کدهای برنامه ) .
تست monitor :
monitor(myFunc); // now click on "Run myFunc" button
unmonitor(myFunc); // now click on "Run myFunc" button
- monitorEvents(object[, types]) and unmonitorEvents(object[, types])
این متدها عملیات Event Logging برای یک شیء را فعال/غیرفعال میکنند . در کنار شیء مورد نظر ، میتوان نوع رویداد را هم به متد ارسال کرد . در این صورت عملیات Logging فقط برای همان گروه رویداد/رویداد ، فعال/غیرفعال میشود .
منظور از گروه رویداد ، مجموعه رویدادهای یک شیء است . مثلا mousemove , moseover , mousedown , ... در گروه mouse قرار میگیرند . یعنی میتوانید با ارسال کلمهی mouse فقط رویدادهای mouse را تحت نظر بگیرید یا اینکه فقط یک رویداد را مشخص کنید ، مثل mousedown .
راه سادهتر فعال کردن Event Logging ، رفتن به تب Html ، راست کلیک کردن بروی المنت مورد نظر و فعال کردن گزینهی Log Events میباشد .
var obj = $("myInput"); monitorEvents(obj,'keypress');
نتیجه پس از فشردن چند دکمهی کیبورد در myInput :
توضیحات بیشتر : http://getfirebug.com/wiki/index.php/MonitorEvents - profile([title]) and profileEnd()
این متدها ، JavaScript Profiler را فعال/غیرفعال میکنند . هنگام فعال کردن میتوانید یک عنوان هم برای پروفایل مشخص کنید . در قسمت قبلی مقاله در مورد این قابلیت توضیحاتی ارائه شد .
سه را برای اجرای Profiler وجود دارد :
1 - کلیک بروی دکمهی Profiler در بالای تب کنسول .
2 - استفاده از کد console.profile("ProfileTitle") در کدهای جاوا اسکریپت .
3 - استفاده از متد profile("Profile Title") در خط فرمان .
profile("myFunc Testing"); myFunc(); profileEnd();
ستونهای Profiler :Function : نام تابع اجرا شده .
Calls : تعداد دفعات فراخوانی تابع .
Percent : زمان اجرای تابع در زمان کل ، به درصد .
Own Time : زمان اجرای تابع به تنهایی . برای مثال در کد ما ، زمان اجرای تابع myFunc به تنهایی تقریبا صفر است زیرا عمیاتی در خود انجام نمیدهد و زمان صرف شده در این تابع ، برای اجرای 2 با تابع loop است . این زمان ( Own Time ) زمان اجرای تابع ، منهای زمان صرف شده برای فراخوانی توابع دیگر است .
Time : زمان اجرای تابع از نقطهی آغاز تا پایان . مجموع زمان اجرای خود تابع به همراه زمان اجرای توابع فراخوانی شده . در کد ما ، این زمان ، مجموع زمان اجرای خود تابع به همراه دو بار فراخوانی تابع loop است .
Avg : میانگین زمان اجرای هربار تابع . فرمول : Avg = Time / Calls
Min & Max : حداقل و حداکثر زمان اجرای تابع .
File : نام فایل و شماره خطی که تابع در آن قرار دارد .
ولی ما ابزاری مثل commit lint هنوز در دات نت نداریم.فعلا در صورت نیاز باید برای استفاده از آن همچنان node و npm را نصب کنیم.
نکته ای که باید اضافه کنم این است که در ورژن 0.5 به بعد باتوجه به اینکه پشتیبانی از CSharpScript اضافه شده است, برای بررسی متن کامیتها میتوان از اسکریپت زیر که که در مخزن Husky.Net هم وجود دارد استفاده کرد.
/// <summary> /// a simple regex commit linter example /// https://www.conventionalcommits.org/en/v1.0.0/ /// https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type /// </summary> using System.Text.RegularExpressions; private var pattern = @"^(?=.{1,90}$)(?:build|feat|ci|chore|docs|fix|perf|refactor|revert|style|test)(?:\(.+\))*(?::).{4,}(?:#\d+)*(?<![\.\s])$"; private var msg = File.ReadAllLines(Args[0])[0]; if (Regex.IsMatch(msg, pattern)) return 0; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Invalid commit message"); Console.ResetColor(); Console.WriteLine("e.g: 'feat(scope): subject' or 'fix: subject'"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("more info: https://www.conventionalcommits.org/en/v1.0.0/"); return 1;
npm install -D jsHint jsHint-loader
module.exports = { entry:['./shared.js','./main.ts'] ,output:{ filename:'bundle.js' } ,watch :true ,module:{ preLoaders:[ { test:/\.js$/ ,exclude:/node_modules/ ,loader:'jshint-loader' } ], loaders:[ { test:/\.ts$/ ,exclude:/node_modules/ ,loader:'ts-loader' } ] } }
همچنین برای تکمیل قابل ذکر است که وبپک دارای postLoaders نیز میباشد که پس از Loaderهای اصلی اجرا میشوند.
// در حالتی که به صورت محلی وبپک نصب شده است npm run webpack -- -p // درحالتی که وبپک به صورت سراسری اجرا میشود webpack -p
npm install -D strip-loader
// webpack.prod.config.js //تنظیمات قبلی را میخوانیم var devConfig = require("./webpack.config.js"); // لودری که وظیفهی حذف کردن دارد را وارد میکنیم var stripLoader = require("strip-loader"); // مانند قبل یک آبجکت با موارد مورد نظر برای لودر میسازیم var stripLoaderConfig = { test:[/\.js$/,/\.ts$/], exclude :/node_modules/ ,loader:stripLoader.loader("console.log") } // اضافه کردن به لیست لودرهای قبلی devConfig.module.loaders.push(stripLoaderConfig); // و در آخر اکسپورت کردن تنظیمات جدید وقبلی module.exports = devConfig;
loader:stripLoader.loader("console.log")
// در حالتی که وبپک به صورت محلی نصب شده است npm run webpack -- --config webpack.prod.config.js -p // در حالتی که وبپک به صورت سراسری نصب شده باشد webpack --config webpack.prod.config.js -p
// زمانی که وبپک به صورت محلی در پروژه نصب شده است npm run webpackserver -- --config webpack.prod.config.js -p //در حالتی که وبپک به صورت گلوبال ( سراسری ) نصب میباشد webpack-dev-server --config webpack.prod.config.js -p
// new webpack.config.js file //ماژول توکار نود جی اس var path = require("path"); module.exports = { // مشخص کردن زمینه برای فایلهای ورودی context:path.resolve("js"), entry:['./shared.js','./main.ts'] ,output:{ // مشخص کردن محل قرارگیری باندل ساخته شده path:path.resolve("build/js"), // درخواست از سمت چه مسیری برای باندل خواهد آمد ؟ publicPath:"assets/js", filename:'bundle.js' } , devServer:{ //راهنمایی برای وب سرور جهت اینکه فایلها را از چه محلی سرو کند contentBase:"assets" } ,watch :true ,module:{ loaders:[ { test:/\.ts$/ ,exclude:/node_modules/ ,loader:'ts-loader' } ] } }
context:path.resolve("js")
// تغییرات در شی output // این کلید جدید مسیر قرار گیری جدید باندل را به وبپک اطلاع میدهد path:path.resolve("build/js"), // راهنما برای وب سرور وبپک جهت میزبانی مسیر زیر از کلید بالا publicPath:"assets/js",
//index.html <html> <head> first part of webpack tut! </head> <body> <h1>webpack is awesome !</h1> <script src="assets/js/bundle.js"></script> </body> </html>
حال با اجرا کردن وب سرور وبپک میتوان مشاهده کرد که مسیرهای جدید، بدون مشکل توسط وبپک پیدا شده و فایلها سرو میشوند. قابل توجه است که این نوع چینش پروژه قابل تغییر و شخصی سازی برای پروژههای گوناگون میباشد.
ساخت فایلهای سورس مپ (source map)
سادهترین راه جهت ساخت فایلهای سورس مپ با استفاده از یک پرچم در هنگام فراخوانی وبپک به صورت زیر میباشد.
// فعال کردن ساخت سورس مپها npm run webpack -- -d // یا در هنگام نصب گلوبال webpack -d // جهت استفاده به همراه وب سرور npm run webpackserver -- -d // یا به صورت نصب گلوبال webpack-dev-server -d
// webpack.config.js // کلید جدید اضافه شده در فایل پیکربندی devtool:"#source-map"
با اجرای وب سرور وبپک خواهید دید که سورس مپها در منوی توسعه دهندهی مرورگر قابل دستیابی میباشند.
ساخت چندین باندل گوناگون
قصد داریم به پروژه، دو صفحهی دیگر را نیز با نامهای aboutme و contact اضافه کنیم. هر یک از این صفحات اسکریپت مخصوص به خود را خواهد داشت و باندل نهایی نیز شامل تمامی آنها خواهد شد. در صورتی که این خروجی مطلوب ما نباشد و به طور مثال بخواهیم مکانیزمی شبیه به lazy loading اسکریپتها را داشته باشیم و فقط زمانی اسکریپتها بارگذاری شوند که به آنها احتیاج باشد، برای انجام این کار با وبپک به صورت زیر عمل خواهیم کرد.
دو صفحهی html جدید را با عناوین ذکر شدهی بالا به پوشهی assets اضافه میکنیم و برای هریک نیز اسکریپتی با همان نام خواهیم ساخت و در پوشهی js قرار میدهیم.
محتوای صفحات بدین شکل میباشد.
// index.html file <html> <head> <title> third part of webpack tut! </title> </head> <body> <nav> <a href="aboutme.html">about me</a> <a href="contact.html">contact</a> </nav> <h1>webpack is awesome !</h1> <script src="assets/js/shared.js"></script> <script src="assets/js/index.js"></script> </body> </html> // aboutme.html file <html> <head> <title> about me page ! </title> </head> <body> <nav> <a href="index.html">index</a> <a href="contact.html">contact</a> </nav> <h1>webpack is awesome !</h1> <script src="assets/js/shared.js"></script> <script src="assets/js/aboutme.js"></script> </body> </html> // contact.html file <html> <head> <title> contact me page ! </title> </head> <body> <nav> <a href="index.html">index</a> <a href="aboutme.html">about me</a> </nav> <h1>webpack is awesome !</h1> <script src="assets/js/shared.js"></script> <script src="assets/js/contact.js"></script> </body> </html>
var path = require("path"); var webpack = require("webpack"); // وارد کردن پلاگینی از وب پک برای ساخت تکههای مختلف اسکریپتها // معرفی اسکریپت shared.js var commonChunkPlugin = new webpack.optimize.CommonsChunkPlugin("shared.js"); module.exports = { context:path.resolve("js"), //entry:['./shared.js','./main.ts'] // معرفی اسکریپتهای جدید به وبپک entry:{ index:"./main.js", aboutme:"./aboutme.js", contact:"./contact.js" } ,output:{ path:path.resolve("build/js"), publicPath:"assets/js", // filename:'bundle.js' // به جای یک باندل کلی از وبپک میخاهیم برای هر ورودی باندلی جدید بسازد filename:"[name].js" } // رجیستر کردن پلاگین ,plugins:[commonChunkPlugin] , devServer:{ contentBase:"assets" } //,devtool:"#source-map" ,watch :true ,module:{... } }
حال با اجرای وبپک میتوان دید که سه باندل ساخته شده که همگی به اسکریپت shared.js وابستگی دارند و اگر این اسکریپت را از صفحات HTML حذف کنید، با خطا رو به رو خواهید شد. این پلاگین قدرت ساخت باندلهایی با خاصیت مشخص کردن وابستگیها و همچنین تو در توییها خاص را نیز دارد. برای مطالعهی بیشتر میتوانید به اینجا مراجعه کنید: پلاگین commonsChunk
در قسمت بعدی با استفاده از وبپک فایلهای css، فونتها و تصاویر را نیز باندل خواهیم کرد.
فایلهای مطلب:
سورس تا قبل از قسمت ایجاد تغییرات در ساختار فایلهای پروژه :dntwebpack-part3-beforeFileAndFolderManagment.zip
سورس برای بعد از ایجاد تغییرات در ساختار فایلهای پروژه : dntwebpack-part3AfterFileOrganization.zip
ساده سازی تعریف فضاهای نام در C# 10.0
[*.cs] csharp_style_namespace_declarations = file_scoped:warning
dotnet format
dotnet format --verify-no-changes
dotnet new sln
سپس از طریق دستور زیر آنها را در sln ثبت مینماییم:
>dotnet sln add mymvc/mymvc.csproj Project `mymvc\mymvc.csproj` added to the solution. >dotnet sln add models/models.csproj Project `models\models.csproj` added to the solution.