<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های عباس باقری</title>
        <link>https://virgool.io/feed/@abasb75</link>
        <description>برنامه نویس و طراح وب‌سایت</description>
        <language>fa</language>
        <pubDate>2026-04-15 04:36:09</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/143039/avatar/g885p2.png?height=120&amp;width=120</url>
            <title>عباس باقری</title>
            <link>https://virgool.io/@abasb75</link>
        </image>

                    <item>
                <title>استفاده از کتابخانه jdf در لاراول</title>
                <link>https://virgool.io/avasam-laravel-edu/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-jdf-%D8%AF%D8%B1-%D9%84%D8%A7%D8%B1%D8%A7%D9%88%D9%84-fms3gkt5hidb</link>
                <description>کتابخانه jdf برای استفاده از تقویم شمسی در php بکار می رود.برای استفاده از کتابخانه jdf در لاراول دستور زیر را در ترمینال وارد کنید:composer require abasb75/jdfبرای استفاده از توابع این کتابخانه میتوانید از الگوی زیر استفاده کنید:use Abasb75\Jdf\JDF;

public function myController(){
   ...
   $shamsi = JDF::gregorian_to_jalali(2012,12,12);
   ...
}</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Fri, 17 Nov 2023 10:03:57 +0330</pubDate>
            </item>
                    <item>
                <title>نصب نسخه خاص php روی اوبونتو</title>
                <link>https://virgool.io/@abasb75/%D9%86%D8%B5%D8%A8-%D9%86%D8%B3%D8%AE%D9%87-%D8%AE%D8%A7%D8%B5-php-%D8%B1%D9%88%DB%8C-%D8%A7%D9%88%D8%A8%D9%88%D9%86%D8%AA%D9%88-bm4d3k6bbgpk</link>
                <description>به طور پیشفرض apt install php نسخه‌ای  از php را برای ما نصب میکند که ممکن است با نیاز نرم افزار ما متناسب نباشد، یا نیاز به داشتن چند نسخه php داریم.دستور زیر را در ترمینال وارد میکنیم :sudo apt show phpاین دستور نسخه پیشفرض php که ربپازیتوری برای شما نصب میکند را نمایش میدهد، خروجی این دستور به صورت زیر است :خب با دستور apt install php برای ما نسخه 8.1 نصب میشود. ولی اگر به نسخه‌ی دیگری مانند 7.4 نیاز داشتیم باید چه کنیم؟ابتدا باید ondrej/php ppa را روی سیستم ابونتور نصب کنیم :sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/phpریپازیتورهای ابونتو را آپدیت میکنیم :sudo apt updateاکنون نسخه‌ای که نیاز داریم نصب میکنیم. توجه کنید میتوانیم چند نسخه مختلف را نصب کنیم :sudo apt install php5.6
sudo apt install php7.0
sudo apt install php7.1
sudo apt install php7.2
sudo apt install php7.3
sudo apt install php7.4
sudo apt install php8.0
sudo apt install php8.1
sudo apt install php8.2برای nginx :sudo apt install php5.6-fpm
sudo apt install php7.0-fpm 
sudo apt install php7.1-fpm 
sudo apt install php7.2-fpm 
sudo apt install php7.3-fpm 
sudo apt install php7.4-fpm 
sudo apt install php8.0-fpm 
sudo apt install php8.1-fpm 
sudo apt install php8.2-fpmنصب ماژولهای phpدر صورتی که نیاز به نصب ماژولهای php8.2 (یا هر نسخه دیگری داشتید) فقط کافیست دستور زیر را در ترمینال وارد کنید سپس دکمه tab را فشار دهید:sudo apt isntall php8.2لیست ماژولهایی که میتوانید نصب کنید برای شما نمایش میدهد :دقت کنید بجای دکمه enter دکمه ماژول را وارد کنیدبرای نصب یک ریپازیتوری مانند php8.2-zip کافیست دستورsudo apt install php8.2-zipرا وارد کنید.نسخه پیشفرض phpما می‌توانیم چند نسخه php را نصب کنیم. اما برای اینکه یک نسخه را پیشفرض سیستم عامل در نظر بگیریم کافیست دستور زیر را وارد کنیم :sudo update-alternatives --set php /usr/bin/php8.2بجای 8.2 نسخه مورد نظر خود را قرار می‌دهیم.استفاده برای آپاچیبرای فعال کردن نسخه خاص برای آپاچی ابتدا نسخه ای که آپاچی در حال حاضر از آن استفاده میکند را غیرفعال میکنمsudo a2dismod php8.1سپس نسخه مورد نظر خود را برای آپاچی فعال میکنیم :sudo a2enmod php8.2</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Fri, 02 Jun 2023 15:33:54 +0330</pubDate>
            </item>
                    <item>
                <title>چگونه در nextjs از پایگاه داده mysql استفاده کنیم؟</title>
                <link>https://virgool.io/@abasb75/%DA%86%DA%AF%D9%88%D9%86%D9%87-%D8%AF%D8%B1-nextjs-%D8%A7%D8%B2-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-mysql-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%DA%A9%D9%86%DB%8C%D9%85-ptsecjr1bsgl</link>
                <description>در این پست نحوه اتصال nextjs به دیتابیس mysql را بررسی میکنیم.برای اتصال به دیتابیس نیازی به دیگر فریمورکها یا زبانهای برنامه‌نویسی سمت سرور مانند nodejs و php نیست.ایجاد سرور mysqlابتدا یک سرور mysql راه‌اندازی و دیتابیس خود را به آن اضافه میکنیم: در این مثال من از نرم‌افزار xampp استفاده کردم. دیتابیس mysql را میتوان به روشهای دیگری مانند نصب مستقیم یا استفاده از داکر هم راه‌اندازی کرد.نصب کتابخانه serverless-mysqlبرای شروع استفاده از mysql در nextjs ابتدا باید کتابخانه‌ای مانند serverless-mysql را نصب کنیم :npm install serverless-mysqlمعرفی کانفیگ mysqlفایل db.js را به سورس پروژه nextjs خود اضافه می‌کنیم:import mysql from &#039;serverless-mysql&#039;;

const db = mysql({
   config: {
      host: &#039;localhost&#039;,
      port: &#039;3306&#039;,
      database: &#039;jabira&#039;,
      user: &#039;root&#039;,
      password: &#039;&#039;,
   }
});

export default async function excuteQuery({ query, values }) {
   try {
      const results = await db.query(query, values);
      await db.end();
     return results;
   } catch (error) {
      return { error };
   }
}تابع executeQuery به ما کمک میکند بتوانیم کوئری های sql مورد نیاز خود را اجرا کنیم.گرفتن دیتا در page.jsاکنون به سراغ فایل page.js خود رفته و از تابع executeQuery برای ارسال درخواست به پایگاه داده استفاده می‌کنیم :import excuteQuery from &amp;quot@/db&quot;

export default async function Home() {
   const result = await excuteQuery({
      query: &#039;SELECT * FROM `pages`;&#039;,
      values: [],
   });
   return ( &lt;Component dbData={result}  /&gt;)
}</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Sun, 21 May 2023 09:27:02 +0330</pubDate>
            </item>
                    <item>
                <title>ایجاد پروژه جدید React بدون CRA</title>
                <link>https://virgool.io/@abasb75/%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%AC%D8%AF%DB%8C%D8%AF-react-%D8%A8%D8%AF%D9%88%D9%86-cra-zzxj49anbuls</link>
                <description>در این پست به چگونگی ساخت پروژه reactjs بدون ابزارهایی مانند &quot;create-react-app&quot; پرداخته می‌شود.ساخت پروژه جدید node js ابتدا یک فولدر با نام دلخواه ایجاد میکنیم، سپس وارد این فولدر می‌شویم و ترمینال را درون این پوشه بازکرده سپس با  کمک دستور زیر فایل package.json مربوط به npm را ایجاد می‌کنیم:npm initnpm initنصب typescriptابزار typescript کمک می‌کند در هنگام توسعه به تمییزتر شدن کدها کمک می‌کند:npm i --save-dev typescript @types/node سپس فایل tsconfig.ts را ایجاد می‌کنیم:{
   &amp;quotcompilerOptions&amp;quot: {
         &amp;quotesModuleInterop&amp;quot: true,
         &amp;quotjsx&amp;quot: &amp;quotreact&amp;quot,
        &amp;quotmodule&amp;quot: &amp;quotesnext&amp;quot,
        &amp;quotmoduleResolution&amp;quot: &amp;quotnode&amp;quot,
       &amp;quotlib&amp;quot: [
          &amp;quotdom&amp;quot,
         &amp;quotesnext&amp;quot
       ],
      &amp;quotstrict&amp;quot: true,
      &amp;quotsourceMap&amp;quot: true,
      &amp;quottarget&amp;quot: &amp;quotesnext&amp;quot,
   },
   &amp;quotexclude&amp;quot: [
      &amp;quotnode_modules&amp;quot
   ]
}نصب webpackابزار webpack را برای ایجاد build نهایی و یا ایجاد server هنگام توسعه نیاز داریم.npm install --save-dev webpack webpack-cli webpack-dev-server css-loader html-webpack-plugin mini-css-extract-pluginهمچنین :npm install --save-dev  ts-loaderاکنون در روت پروژه یک فایل بنام webpack.config.js ایجاد می‌کنیم :const prod = process.env.NODE_ENV === &#039;production&#039;;

const HtmlWebpackPlugin = require(&#039;html-webpack-plugin&#039;);
const MiniCssExtractPlugin = require(&#039;mini-css-extract-plugin&#039;);

module.exports = {
   mode: prod ? &#039;production&#039; : &#039;development&#039;,
   entry: &#039;./src/index.tsx&#039;, // index.ts/index.js path
   output: {
      path: __dirname + &#039;/dist/&#039;,  //build directory
   },
   module: {
   rules: [
      {
         test: /\.(ts|tsx)$/,
         exclude: /node_modules/,
         resolve: {
            extensions: [&#039;.ts&#039;, &#039;.tsx&#039;, &#039;.js&#039;, &#039;.json&#039;],
         },
         use: &#039;ts-loader&#039;,
      },
      {
         test: /\.css$/,
         use: [MiniCssExtractPlugin.loader, &#039;css-loader&#039;],
      },
   ]
   },
   devtool: prod ? undefined : &#039;source-map&#039;,
   plugins: [
      new HtmlWebpackPlugin({
         template: &#039;./public/index.html&#039;, //index.html path
      }),
      new MiniCssExtractPlugin(),
   ],
};نصب Babelمرورگرها قادر به درک ECMAScript نیستند. این ابزار کدهای ECMAScript را به کدهای جاوااسکریپت قابل درک برای مرورگرها تبدیل می‌کند:npm install --save-dev babel-loader @babel/preset-env @babel/core @babel/plugin-transform-runtime @babel/preset-react @babel/eslint-parser @babel/runtime @babel/cliنصب eslint  ابزار eslint در هنگام توسعه به پیدا کردن مشکلات کد به توسعه دهنده کمک می‌کند:npm install --save-dev eslint eslint-config-airbnb-base eslint-plugin-jest eslint-config-prettierنصب pathاز این ابزار به جهت کار با مسیرها استفاده می‌کنیم:npm install --save-dev pathنصب react کتابخانه react سرعت توسعه برنامه‌های جاوااسکریپتی را با کمک کدهای از پیش نوشته افزایش می‌دهد:npm install react react-domهمچنین :npm install --save-dev @types/react @types/react-domساخت فایل index.htmlدرون فولدر پروژه یک فولدر بنام public کنار package.json ایجاد می‌کنیم و سپس درون این فولدر فایل index.html را قرار می‌دهیم:دقت شود که ما یک تگ div درون فایل html با شناسه &quot;my-app-root&quot; هم ایجاد کردیم.ساخت فایل index.tsدرون پوشه src فایل index.ts خود را اضافه میکنیم:اجرا در مرورگربرای تست فایل ابتدا سراغ فایل package.json رفته و اسکریپت test و build را به این فایل اضافه می‌کنیم:اسکریپت‌های مربوط به test و start در ویندوز:&amp;quotstart&amp;quot: &amp;quotwebpack serve --port 3000&amp;quot, 
&amp;quotbuild&amp;quot: &amp;quotset NODE_ENV=production &amp;&amp; webpack&amp;quotدرون لینوکس:&amp;quotstart&amp;quot: &amp;quotwebpack serve --port 3000&amp;quot,
 &amp;quotbuild&amp;quot: &amp;quotNODE_ENV=production webpack&amp;quotسپس با دستور زیر می‌توان اپ را روی پورت 3000 اجرا کرد:npm startبیلد گرفتن از پروژهبرای گرفتن build کافیست دستور زیر را در ترمینال وارد میکنیم:npm run build</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Sat, 25 Mar 2023 09:10:14 +0330</pubDate>
            </item>
                    <item>
                <title>چگونه از redux به همراه 13 nextjs استفاده کنیم</title>
                <link>https://virgool.io/@abasb75/%DA%86%DA%AF%D9%88%D9%86%D9%87-%D8%A7%D8%B2-redux-%D8%A8%D9%87-%D9%87%D9%85%D8%B1%D8%A7%D9%87-13-nextjs-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%DA%A9%D9%86%DB%8C%D9%85-ixzwvm6mdd3j</link>
                <description>با تغییراتی که در nextjs نسخه 13 بوجود آمده است، نحوه استفاده از redux هم کمی متفاوت شده است.برای شروع کار ابتدا redux را نصب می‌کنیم: npm install @reduxjs/toolkit react-reduxسپس sliceها و store خود را ایجاد میکنیم. (شروع کار با redux toolkit)فایل store.js :import { configureStore } from &amp;quot@reduxjs/toolkit&amp;quot
import counter from &amp;quot./counterSlice&amp;quot  

import { createSlice } from &amp;quot@reduxjs/toolkit&amp;quot  

iconst initialState:CounterState = {
    value:0, 
}  

const counterSlice = createSlice({
    name: &amp;quotcounter&amp;quot,
    initialState,
    reducers:{
       increment: (state,action) =&gt; {
          state.value += action.payload
      },
      decrement: (state) =&gt; {
         state.value -= 1
     },
  } 
}); 

export const {increment , decrement} = counterSlice.actions; 
export default counterSlice.reducer;


const store = configureStore({
    reducer:{
       counter,   
    } 
}) ;

export default store;همان طور که می‌دانید برای استفاده از کتابخانه react-redux باید کامپوننتهای خود را درون کامپوننت Provider قرار دهیم. همچنین همان طور که میدانید در نسخه 13 فریمورک next.js علاوه بر تغییر در ساختار فایل‌ها پوشه app جایگزین پوشه pages شده است. درون نسخه 13 به سراغ فایل app/layout.js می‌رویم :export default function RootLayout({ children }) {
   return (&lt;html lang=&amp;quoten&amp;quot&gt;
         &lt;body &gt;
            &lt;Provider store={store}&gt;
              {children}
            &lt;/Provider&gt;
        &lt;/body&gt;
   &lt;/html&gt;);
}دقت کنید به دلیل اینکه layout درون سرور رندر می‌شود، ما نمی‌توانیم مانند کد بالا مستقیما از Provider درون فایل layout.js استفاده کنیم، پس یک کامپوننت سمت کلاینت ایجاد میکنم.فایل ClientProvider را مانند نمونه ایجاد می‌کنیم :&amp;quotuse client&amp;quot
import store from &amp;quot./store&amp;quot
import {Provider} from &amp;quotreact-redux&amp;quot

export default ClientProvider({children}){
   return(&lt;Provider store={store}&gt;
      {children}
   &lt;/Provider&gt;
}اکنون ClientProvider را درون فایل layout.js را جایگزین Provider می‌کنیم:import ClientProvider from &amp;quot./ClientProvider&amp;quot

export default function RootLayout({ children }) {
    return (&lt;html lang=&amp;quoten&amp;quot&gt;
          &lt;body &gt;
             &lt;Provider&gt;
               {children}
             &lt;/Provider&gt;
         &lt;/body&gt;
    &lt;/html&gt;); 
}اکنون می‌توانیم از redux درون کامپوننتهای سمت client استفاده کنیم.ما نمیتوانیم از هوکهای redux درون کامپوننتهای پردازش شده سرور استفاده کنیم.</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Fri, 24 Mar 2023 22:37:34 +0330</pubDate>
            </item>
                    <item>
                <title>دسترسی به کنسول مرورگر  کروم موبایل</title>
                <link>https://virgool.io/@abasb75/%D8%AF%D8%B3%D8%AA%D8%B1%D8%B3%DB%8C-%D8%A8%D9%87-%DA%A9%D9%86%D8%B3%D9%88%D9%84-%D9%85%D8%B1%D9%88%D8%B1%DA%AF%D8%B1-%DA%A9%D8%B1%D9%88%D9%85-%D9%85%D9%88%D8%A8%D8%A7%DB%8C%D9%84-r5tygbxt9u3t</link>
                <description>تا موقعی که روی دستگاه‌های موبایل اجرا نکردیم همه چی خوب کار می‌کرد. ممکن است گاها صفحات وب در مرورگرهای موبایل و یا برخی دستگاه‌های همراه دچار خطا شوند. اما مشکلی که وجود دارد این است که بطور مستقیم امکان دسترسی به لاگهای کنسول مرورگرهای موبایل وجود ندارد. برای دسترسی به کنسول مطابق دستورالعمل زیر عمل می‌کنیم:0. ابتدا سرویس ضدتحریم مانند شکن را در موبایل و رایانه اجرا میکنیم!1. ابتدا وارد تنظیمات تلفن‌همراه شده و سپس از بخش about phone هفت بار روی build number ضربه می‌زنیم تا developer mode موبایل فعال شود.2. در بخش developer option گزینه usb debugging را فعال می‌کنیم.3. موبایل را به رایانه متصل می‌کنیم.4. وارد کروم رایانه می‌شویم و سپس عبارت زیر را درون address bar مرورگر وارد کنید:chrome://inspect/#devicesباید با چنین صفحه‌ای در کروم روبرو شویم :دستگاه های متصل به کرومطبق تصویر ما میتوانیم از دو مرورگر کروم موبایل و kiwi browser موجود در دستگاه ABR-LX9 استفاده کنیم.5. وارد مرورگر موبایل شده یا از طریق باکس مشخص‌شده آدرس صفحه مورد نظر را وارد میکنیم:6. سپس از لیست صفحات باز شده درون موبایل گزینه inspect را انتخاب میکنیم: اکنون میتوانیم به لاگهای کنسول کرومیوم موبایل دسترسی داشته باشیم.دقت کنید نسخه های کروم موبایل و رایانه باید آخرین نسخه باشد.این ویژگی ممکن است در ایران به درستی کار کند و نیاز به ابزارهای ضدتحریم مانند شکن باشد.</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Sat, 18 Mar 2023 21:25:12 +0330</pubDate>
            </item>
                    <item>
                <title>ابزار redux toolkit</title>
                <link>https://virgool.io/@abasb75/%D8%A7%D8%A8%D8%B2%D8%A7%D8%B1-redux-toolkit-ee3xktkomyzv</link>
                <description>ابزار redux toolkit بوجود آمده است تا استفاده برنامه نویسان از redux را آسانتر کند.برای استفاده از redux toolkit ابتدا آنرا به پروژه جاوااسکریپت خود اضافه میکنیم :npm install @reduxjs/toolkit react-reduxساخت sliceابتدا یک فایل بنام store/counterSlice.ts ایجاد میکنیم. برای ایجاد slice ، ابتدا تابع createSlice را از @redux-toolkit به فایل خود ایمپورت میکنیم :import { createSlice } from &#039;@reduxjs/toolkit&#039;;تابع createSlice بعنوان ورودی یک object با سه پراپرتی دریافت میکند :interface CounterState {
   value:number;
}

const initialState:CounterState = {
   value:0,
}

const counterSlice = createSlice({
   name: &#039;counter&#039;,
   initialState,
   reducers:{
      increment: (state,action) =&gt; {
         state.value += action.payload
      },
      decrement: (state) =&gt; {
         state.value -= 1
      },
   }
});پراپرتی name : باید یک نام یکتا برای هر slice در نظر بگیریم.پراپرتی initialState : حالت شروع برای هر slice می‌باشد.پراپرتی reducers : همه reducerهای مرتبط به یک slice را مشخص میکند.در این مثال counterSlice دو reducer برای کاهش و افزایش مقدار شمارنده دریافت میکند.هر reducer دو ورودی دارد. state و action که اختیاری است.برای استفاده از یک slice باید reducer و action های ایجاد شده برای آن slice را export کنیم :export const {increment , decrement} = counterSlice.actions;
export default counterSlice.reducer;فایل counterSlice.ts :import { createSlice } from &#039;@reduxjs/toolkit&#039;;

interface CounterState {
   value:number;
}

const initialState:CounterState = {
   value:0,
}

const counterSlice = createSlice({
   name: &#039;counter&#039;,
   initialState,
   reducers:{
      increment: (state,action) =&gt; {
         state.value += action.payload
      },
     decrement: (state) =&gt; {
        state.value -= 1
     },
 }
});

export const {increment , decrement} = counterSlice.actions;
export default counterSlice.reducer;درون یک برنامه میتوان چندین slice مختلف ایجاد کنیم. برای مثال دیتای هر بخش برنامه را درون یک slice قرار میدهیمایجاد store :برای ایجاد store ابتدا تابع configureStore را به فایل store.ts ایمپورت میکنیم :import { configureStore } from &#039;@reduxjs/toolkit&#039;;سپس reducer مربوط به هر slice را درون فایل خود import میکنیم.import counter from &#039;./counterSlice&#039;;
...در این پروژه فقط یک slice داریم.برای ایجاد store از تابع configutrStore بجای createStore استفاده میکنیم :const store = configureStore({
   reducer:{
      counter,
      ...
}
});این تابع یک object در ورودی دریافت می‌کند. پراپرتی اصلی که باید مقدار دهی کنیم پراپرتی reducer می‌باشد. این پراپرتی لیست کامل reducer های مربوط اسلایس ها را دریافت می‌کند.فایل store/index.ts : import { configureStore } from &#039;@reduxjs/toolkit&#039;
import counter from &#039;./counterSlice&#039;;

const store = configureStore({
   reducer:{
      counter,
   }
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType&lt;typeof store.getState&gt;

// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

export default store;استفاده در کامپوننتهای برنامهبرای استفاده از store در کامپوننتها باید همانند قبل store را به برنامه خود معرفی کنیم :import { Provider } from &#039;react-redux&#039;;
import store, { RootState } from &#039;./store&#039;;

function App() {
   return (&lt;Provider store={store}&gt;
      &lt;Counter /&gt;
   &lt;/Provider&gt;);
}برای نمایش مقدار state به سراغ component خود رفته و با استفاده از هوک useSelector مقدار شمارنده را نمایش میدهیم :import { useSelector, useDispatch } from &#039;react-redux/es/exports&#039;;

function Counter(){
   const counter = useSelector((store:RootState)=&gt;store.counter.value);
   return (&lt;&gt;
       &lt;h3&gt;{counter}&lt;/h3&gt;
       &lt;button &gt;Increment&lt;/button&gt;
      &lt;button &gt;Decrement&lt;/button&gt;
   &lt;/&gt;)
}برای تغییر مقدار شمارنده باید از اکشنهای ایجاد شده هر slice استفاده کنیم:function Counter(){
    const counter = useSelector((store:RootState)=&gt;store.counter.value);
   const dispatch = useDispatch();
   return (&lt;&gt;
      &lt;h3&gt;{counter}&lt;/h3&gt;
     &lt;button ={()=&gt;dispatch(increment(1))}&gt;Increment&lt;/button&gt;
     &lt;button ={()=&gt;dispatch(decrement())}&gt;Decrement&lt;/button&gt;
   &lt;/&gt;)
}</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Fri, 10 Mar 2023 11:53:16 +0330</pubDate>
            </item>
                    <item>
                <title>ذخیره حالات برنامه با کتابخانه redux-persist</title>
                <link>https://virgool.io/@abasb75/%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%D8%AD%D8%A7%D9%84%D8%A7%D8%AA-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D8%A8%D8%A7-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-redux-persist-d8kzkbjkl49l</link>
                <description>با کمک کتابخانه redux-persist می‌توان حالات برنامه را در مرورگر ذخیره کردموقعی که از ریداکس استفاده میکنیم، با هر بار رفرش صفحه، مقادیر state های ریداکس از حافظه رم پاک می‌شوند. ابزار redux-persist به ما کمک میکند، مقادیر stateهای redux را در جایی مانند localstorage مرورگر ذخیره کنیم. با مراجعات بعدی کاربر به صفحه، این ابزار مقادیر ذخیره شده در ذخیره‌گاه محلی مرورگر را، به کاربر نمایش می‌دهد.برای این مقاله من یک شمارنده ایجاد شده است :حالت شروع یا initialState برنامه :const initialState =  {
   loading:true,
   counter:0,
   nightMode: false,
}اعمال کننده یا reducer برنامه :import initialState from &#039;../initialState&#039;
import {State} from &#039;../initialState&#039;

function reducer ( state:State=initialState , action:any ){
   switch (action.type){
      case &#039;COUNTER_UP&#039;:
         const counter = state.counter;
         return {
            ...state,
            counter:counter+1,
        }
   default:
      return state;
   }
}با هر ارسال اکشن COUNTER_UP به store ، مقدار شمارنده یک واحد افزایش می‌یابد.اکنون میتوانیم store را ایجاد کنیم :import { legacy_createStore as createStore,Store, applyMiddleware} from &#039;redux&#039;;
import initialState from &#039;./initialState&#039;;
import reducer from &#039;./reducer&#039;;

const store:Store = createStore(
   reducer,
   initialState
);export {store};اکنون میتوانیم در اپ خود از store استفاده کنیم :import ReactDOM from &#039;react-dom/client&#039;;
import { Provider } from &#039;react-redux/es/exports&#039;;
import App from &#039;./App&#039;;
import {store} from &#039;./store&#039;;

const root = ReactDOM.createRoot(
   document.getElementById(&#039;root&#039;) as HTMLElement
);

root.render(
   &lt;Provider store={store}&gt;
      &lt;App /&gt;
   &lt;/Provider&gt;
);برای دریافت مقدارهای state ریداکس و همچنین ارسال اکشن به store در کامپوننت App.jsimport { useSelector } from &#039;react-redux&#039;
import { useDispatch } from &#039;react-redux/es/exports&#039;

function App() {
   const counter = useSelector((state)=&gt;state.counter);
   const dispatch = useDispatch();
   const counterUp = () =&gt; {
      dispatch({type:&#039;COUNTER_UP&#039;});
   }

   return (&lt;&gt;
      &lt;h1&gt;{counter}&lt;/h1&gt;
      &lt;button ={counterUp}&gt;counter up&lt;/button&gt;
   &lt;/&gt;);
}

export default App;نتیجه کار تا اینجا : شمارنده بدون redux-persist همین‌طور که درون تصویر مشخص است، با هر بار رفرش مرورگر، مقدار شمارنده پاک می‌شود.1. نصب redux-persist برای اضافه کردن redux-persist به پروژه جاوااسکریپت خود فقط کافیست دستور زیر را در ترمینال وارد کنیم.npm install redux-persist2. کانفیگ redux-persistبرای استفاده از redux-persisit نیازی به ایجاد تغییر در reducer و اکشنها نداریم. فقط کافیست درون فایل store تغییرات کوچکی ایجاد کنیم، ابتدا نیاز به یک تعیین persistConfig برای redux-persist داریم :آبجکت persistConfig ، مجموعه‌ای از پراپرتیهای از پیش مشخص شده است.import storage from &#039;redux-persist/lib/storage&#039;; // defaults to localStorage for web

const persistConfig = {
  key: &#039;root&#039;,
  storage: storage,
  blacklist: [&#039;loading&#039;] 
};پراپرتی key :کلید دسترسی به محتوای ذخیره شده است.پراپرتی storage : محل ذخیره حالات برنامه را تعیین میکند، برای استفاده در محیط وب، معمولا از localstorage استفاده میشود.برای استفاده از redux-persist مقداردهی storage و key الزامی است ولی سایر پراپرتی‌ها اختیاری می‌باشد.پراپرتی blacklist : لیست state هایی که نمیخواهیم در localstorage ذخیره شوند را مشخص میکنیم.پراپرتی whitelist : برعکس blacklist، لیست stateهایی که میخواهیم در localstorage ذخیره شوند را مشخص میکند.import storage from &#039;redux-persist/lib/storage&#039;; // defaults to localStorage for web  

const persistConfig = {   
   key: &#039;root&#039;,   
   storage: storage,   
   blacklist: [&#039;nightMode&#039;,&#039;counter&#039;] 
};فقط یکی از پراپرتیهای blacklist و یا whitelist را مقدار دهی میکنیم.پراپرتی version : نسخه state را مشخص میکند. برای زمانی که برنامه خود را آپدیت میکنیم میتواند مفید باشد.پراپرتی debog : مقدار boolean دریافت میکند و مشخص میکند که همزمان با تغییرات log در کنسول ثبت شود یا خیر!پراپرتی stateReconciler : نحوه تطبیق دادن state ها را مشخص میکند.3. ساخت persistorباید ابتدا reducer پایه خود را به کمک تابع persistReducer ، برای استفاده در استور به persistedReducer تبدیل کنیم و سپس آنرا به createStore بجای reducer پاس دهیم.import reducer from &#039;./reducer&#039;;
import { persistReducer } from &#039;redux-persist&#039;;
import storage from &#039;redux-persist/lib/storage&#039;;

const persistConfig = {
   key:&#039;root&#039;,
   storage,
   blacklist:[&#039;loading&#039;],
}

const persistedReducer = persistReducer( persistConfig , reducer );

const store:Store = createStore(
   persistedReducer,
);اکنون با استفاده از store می‌توان persistor مناسب را ایجاد کرد:import persistStore from &#039;redux-persist/es/persistStore&#039;;

const persistor = persistStore(store);برای استفاده در برنامه باید persistor را همراه با استور export کرد :export {store , persistor};فایل کامل store.js :import { legacy_createStore as createStore,Store, applyMiddleware} from &#039;redux&#039;;
import reducer from &#039;./reducer&#039;;
import { persistReducer } from &#039;redux-persist&#039;;
import storage from &#039;redux-persist/lib/storage&#039;;
import persistStore from &#039;redux-persist/es/persistStore&#039;;

const persistConfig = {
   key:&#039;root&#039;,
   storage:storage,
   blacklist:[&#039;loading&#039;]
}

const persistedReducer = persistReducer( persistConfig , reducer );
   const store:Store = createStore(
   persistedReducer,
);

const persistor = persistStore(store);
export {store , persistor};4. استفاده از persistor در برنامهبرای استفاده از persistor باید برنامه خود را بوسیله کامپوننت PersistGate بپوشانیم، و مقدار persistor را بعنوان props به آن پاس دهیم :import ReactDOM from &#039;react-dom/client&#039;;
import { Provider } from &#039;react-redux/es/exports&#039;;
import App from &#039;./App&#039;;
import {persistor, store} from &#039;./store&#039;;
import { PersistGate } from &#039;redux-persist/integration/react&#039;;

const root = ReactDOM.createRoot(
document.getElementById(&#039;root&#039;) as HTMLElement
);

root.render(
   &lt;Provider store={store}&gt;
      &lt;PersistGate persistor={persistor}&gt;
         &lt;App /&gt;
      &lt;/PersistGate&gt;
   &lt;/Provider&gt;
);اکنون تغییرات حالات برنامه در storage ذخیره می‌شود و با رفرش صفحه به آن دسترسی خواهیم داشت. خروجی برنامه به این شکل است :شمارنده با استفاده ار redux-persist</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Sun, 26 Feb 2023 13:56:30 +0330</pubDate>
            </item>
                    <item>
                <title>استفاده از کتابخانه redux-saga</title>
                <link>https://virgool.io/@abasb75/%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-redux-saga-asdyqoevwpmi</link>
                <description>کتابخانه redux-saga به ما کمک میکند تا بتوانیم توابع action creator ناهمزمان ایجاد کنیم.1. نصب redux-sagaبرای استفاده از redux-saga ابتدا آنرا به پروژه خود اضافه میکنیم :npm install redux react-redux redux-saga2. ساخت worker هافرض کنید ما درون پروژه خود میخواهیم از سمت سرور، لیست کاربران را دریافت کنیم. redux-saga در حقیقت یک middleware هست که اکشنهایی که به store ارسال می‌شوند را watch میکند و در مثال ما اگر type اکشن برابر با FETCH_USER_LIST بود، از سرور اطلاعات را بارگیری میکند و نتیجه را برای store ارسال میکند.برای راحتی کار یک فایل بنام sagas.js ایجاد میکنیم. در این فایل ابتدا worker های مورد نیاز خود را اضافه میکنیم :دقت کنید worker ها یک تابع سازنده یا function generator هستند.import { call, put, takeEvery } from &#039;redux-saga/effects&#039;;

function fetchUsersApi(apiUrl) {
   return fetch(apiUrl)
   .then(response =&gt; response.json())
   .catch(error =&gt; ({ error }))
}

// workers
function* fetchUsers() {
   try {
      const response:Response = yield call(fetchUsersApi,&#039;https://dummyjson.com/users&#039;);
      yield put({ type: &#039;USER_FETCH_SUCCEEDED&#039;, users: response });
   } catch (error) {
      yield put({ type: &#039;USER_FETCH_FAILED&#039;, message: error })
   }
}ابتدا worker ساخته شده را بررسی میکنیم :// workers 
function* fetchUsers() {
    try {
       const response:Response = yield call(fetchUsersApi,&#039;https://dummyjson.com/users&#039;);
       yield put({ type: &#039;USER_FETCH_SUCCEEDED&#039;, users: response });
    } catch (error) {
       yield put({ type: &#039;USER_FETCH_FAILED&#039;, message: error })
    }
 }ابتدا بوسیله تابع call میتوانیم از api اطلاعات را بگیریم و پس از آماده شدن response اکشن USER_FETCH_SUCCEEDED را با استفاده از تابع put برای store ارسال میکنیم یا اصطلاحا dispach کنیم. ما میتوانیم تعداد زیادی worker تعریف کنیم، برای این مثال من دو worker نیاز دارم :function* fetchUserById(action){
   const id = action.payload.id;
   try {
      const response:Response = yield call(fetchUsersApi,&#039;https://dummyjson.com/users/&#039;+id);
      if(response.status){
         yield put({ type: &#039;USER_BY_ID_FETCH_SUCCEEDED&#039;, users: response });
      }else{
         yield put({ type: &#039;USER_BY_ID_FETCH_FAILED&#039;, message: &#039;error&#039; })
      }
   } catch (error) {
      yield put({ type: &#039;USER_BY_ID_FETCH_FAILED&#039;, message: error })
   }
}این worker هم همانند worker بالا ابتدا اطلاعات را از api میخواند و بر اساس نتیجه نوع response اکشن مناسب برای store می فرستد3.ایجاد watcherوظیفه watcher بسیار ساده است. این تابع همواره در سطح middleware نگهبانی می‌دهد و با توجه به type اکشن ، worker مناسب را صدا میزند. برای درک بهتر این موضوع watcher خود را به فایل sagas اضافه میکنیم ://watcher
function* mySaga() {
   yield takeEvery(&#039;FETCH_USER_LIST&#039;, fetchUsers);
   yield takeEvery(&#039;FETCH_USER_BY_ID&#039;, fetchUserById);
}

export default mySaga;تابع watcher هم همانند worker از نوع تابع سازنده می‌باشد.این تابع با توجه به type اکشن، worker مورد نظر را صدا میزند. ما میتوانیم هز تعداد worker  را درون تابع watcher خود اضافه کنیم. همین طور که از export مشخص است، ما در نهایت mySaga را برای استفاده در store لازم داریم. تابع takeEvery دو ورودی دریافت میکند، ورودی اول type اکشنهاست و ورودی دوم worker مرتبط با هر اکشن می‌باشد.میتوان بجای takeEvery از تابع takeLatest هم استفاده کرد، اما این دوتابع دقیقا مثل یکدیگر عمل نمیکنند، تابع takeLatest همواره فقط یک درخواست fetch را بررسی میکند واگر درخواست دیگری از قبل بود، آن درخواست متوقف میکند.فایل saga.js :import { call, put, takeEvery } from &#039;redux-saga/effects&#039;;

function fetchUsersApi(apiUrl:string) {
   return fetch(apiUrl)
   .then(response =&gt; response.json())
   .catch(error =&gt; ({ error }))
}

// workers
function* fetchUsers() {
   try {
      const response:Response = yield call(fetchUsersApi,&#039;https://dummyjson.com/users&#039;);
      yield put({ type: &#039;USER_FETCH_SUCCEEDED&#039;, users: response });
   } catch (error) {
      yield put({ type: &#039;USER_FETCH_FAILED&#039;, message: error })
   }
}

interface Action{
   type:&#039;FETCH_USER_BY_ID&#039;
   payload:number;
}

function* fetchUserById(action:Action){
   const id = action.payload;
   try {
      const response:Response = yield call(fetchUsersApi,&#039;https://dummyjson.com/users/&#039;+id);
      if(response.status){
          yield put({ type: &#039;USER_BY_ID_FETCH_SUCCEEDED&#039;, users: response });
      }else{
         yield put({ type: &#039;USER_BY_ID_FETCH_FAILED&#039;, message: &#039;error&#039; })
      }
   } catch (error) {
      yield put({ type: &#039;USER_BY_ID_FETCH_FAILED&#039;, message: error })
   }
}

//watcher
function* mySaga() {
   yield takeEvery(&#039;FETCH_USER_LIST&#039;, fetchUsers);
   yield takeEvery(&#039;FETCH_USER_BY_ID&#039;, fetchUserById);
}
export default mySaga;4. معرفی میان افزار saga به storeپس از آماده شدن warcher ، باید saga را بعنوان یک middlewar به redux معرفی کنیم. به سراغ جایی میرویم که store خود را ایجاد کرده بودیم :import { legacy_createStore as createStore,Store, applyMiddleware} from &#039;redux&#039;;
import initialState from &#039;./initialState&#039;;
import reducer from &#039;./reducer&#039;;
import createSagaMiddleware from &#039;redux-saga&#039;;
import mySaga from &#039;./sagas&#039;;

const sagaMiddleware = createSagaMiddleware();

const store:Store = createStore(
   reducer,
   initialState,
   applyMiddleware(sagaMiddleware)
);

sagaMiddleware.run(mySaga);

export default store;ابتدا تابع createSagaMiddleware را از redux-sage به فایل خود import میکنیم. و به‌وسیله این تابع یک sagaMiddleware ایجاد میکنیم :import createSagaMiddleware from &#039;redux-saga&#039;; 
const sagaMiddleware = createSagaMiddleware();میان‌افزار sagaMiddleware را به store خود به عنوان یک middleware پاس میدهیم :const store:Store = createStore(    
    reducer,
    initialState,
    applyMiddleware(sagaMiddleware) 
);پس از آن، باید watcher را که درون فایل saga.js ایجاد کرده بودیم به sagaMiddleware بدهیم :sagaMiddleware.run(mySaga);5. استفاده در برنامه به سراغ فایل App.js میرویم :import { useEffect} from &#039;react&#039;;
import { useSelector } from &#039;react-redux&#039;;
import { useDispatch } from &#039;react-redux/es/exports&#039;;
import {State} from &#039;./store/initialState&#039;;

function App() {
   const loading = useSelector((state)=&gt;state.loading);
   const users = useSelector((state)=&gt;state.users);
   const dispatch = useDispatch();

   useEffect(()=&gt;{
      dispatch({type:&#039;FETCH_USER_LIST&#039;})
   },[]);
   useEffect(()=&gt;{
      console.log(users);
   },[users])

   if(loading){
      return &lt;h1&gt;Loading&lt;/h1&gt;
   }
   return &lt;h1&gt;Loaded!&lt;/h1&gt;
}

export default App;در این برنامه وقتی کامپوننت رندر شود اکشن زیر برای store ارسال میشود :dispatch( {type:&#039;FETCH_USER_LIST&#039;} ) در سطح middleware وقتی که watcher با اکشنی که type آن مقدار FETCH_USER_LIST را دارد، روبرو می‌شود، worker متناظر با آن یعنی fetchUsers را صدا می‌زند ://watcher 
function* mySaga() { 
   yield takeEvery(&#039;FETCH_USER_LIST&#039;, fetchUsers); 
   ...
 }سپس درون این worker عملیات fetch از طریق api صورت گرفته و براساس api برگشتی، اکشن مورد نظر به store ارسال (dispatch) میشود، و در reducer بر روی state تغییرات اعمال میشود.function* fetchUsers() { 
   try { 
      const response:Response = yield call(fetchUsersApi,&#039;https://dummyjson.com/users&#039;);
      yield put({ type: &#039;USER_FETCH_SUCCEEDED&#039;, users: response });
   } catch (error) {
      yield put({ type: &#039;USER_FETCH_FAILED&#039;, message: error })   
   }
}امیدوارم پست مفید واقع شود.</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Fri, 24 Feb 2023 17:50:36 +0330</pubDate>
            </item>
                    <item>
                <title>رفع و حذف منابع مسدود کننده رندر</title>
                <link>https://virgool.io/@abasb75/%D8%B1%D9%81%D8%B9-%D9%88-%D8%AD%D8%B0%D9%81-%D9%85%D9%86%D8%A7%D8%A8%D8%B9-%D9%85%D8%B3%D8%AF%D9%88%D8%AF-%DA%A9%D9%86%D9%86%D8%AF%D9%87-%D8%B1%D9%86%D8%AF%D8%B1-l6pjopvyy6b7</link>
                <description>رفع و حذف منابع مسدودکننده رندر به معنای تنظیم یک اولویت مناسب جهت بارگیری سورسهای ضروری و غیرضروری است.این پست ترجمه‌ای از این مطلب میباشد. ممکن است نوع جمله بندی به کاررفته با منبع اصلی متفاوت باشد. ممکن است بنا بر توصیه خود مطلب بخشهای غیر ضروری هم حذف شده باشند :)فایلها و سورسهای مختلف مانند  HTML ، CSS و جاوااسکریپت در کنار همدیگر محتوا و ظاهر و عملکرد صفحات وب را تشکیل می‌دهند و به کاربران اجازه میدهند با صفحه شما ارتباط برقرار کنند.کدهای MTML  چگونگی بارگیری سورسهای مختلف را تعیین میکند. بهینه سازی ساختار لینک‌دهی به سورسهای مختلف (مانند جاوااسکریپت و فایل استایل) در این کدها، میتواند کمک بسیار مهم و موثری به بهبود (زمان) بارگیری و نمایش صفحه شما کند.مسدود کننده رند یا Eliminate render-blocking چیست؟اگر شما در سرویسهایی مانند Google&#x27;s PageSpeed سرعت وب‌سایت خود را تست کنید میتوانید معیار Eliminate render-blocking resources را در لیست معیارهای سنجش سرعت سایت خود ببینید. کنترل منابع مسدودکننده رندر (به عبارتی منابع کاهش‌دهنده سرعت رندر وب‌سایت)  میتواند به افزایش سرعت وب‌سایت کمک کند. موقعی که کاربران وارد صفحه وب میشوند، مرورگر، ابتدا کدهای HTML را بارگیری و پردازش میکند و سپس منابع خارجی مانند فایلهای استایل، جاوااسکریپت و تصاویر به صفحه اضافه میشوند. ترتیب تفسیر این منابع و نمایش آنها روی صفحه critical rendering path نامیده می‌شود و همچنین تاثیر مهمی روی سرعت بارگیری و نمایش صفحات دارد.عبارت Render blocking به معنای مواردی همچون تگ script و link است، که تا وقتی هنوز به طورکامل بارگیری نشده‌اند، فرایند رندر صفحات را متوقف میکنند. و نتیجه آن هم تاخیر نمایس (افزایش زمان) نمایش محتوا به کاربر است. به مثال زیر دقت کنید :&lt;!-- other &lt;head&gt; stuff --&gt; 

&lt;!-- conventional render blocking CSS &amp; JavaScript references --&gt; 
&lt;link rel=&#039;stylesheet&#039; href=&#039;styles.css&#039;&gt; 
&lt;script src=&#039;scripts.js&#039;&gt; &lt;/head&gt;در این مثال ابتدا باید فایلهای styles.css و scripts.js بارگیری شوند، سپس ادامه پردازش برای نمایش محتوا انجام شود.مفهوم Eliminating render blocking resources به معنای کنترل این منابع خارجی (فایلهای جاوااسکریپت و css ) برای کاهش تاخیر نمایش محتوا به کاربران است.در یک صفحه وب اگر فایلهای کمی وجود داشته باشد و سرعت اینترنت هم مناسب باشد، این صفحه هم سریعا به کاربر نمایش داده می‌شود.  اما اگر تعداد فایلهای مختلف مانند جاوااسکریپت و استایل زیاد شود در سرعت اینترنت پایین، سرعت رندر صفحه به طور چشمگیری کاهش می‌یابد. کنترل این منابع به سرعت بارگیری و نمایش صفحه میتواند کمک کند.بارگیری با اولویت مناسبدر اکثر سایتها، بارگیری اولیه بخش زیادی از کدهای جاوااسکریپت و css برای نمایش محتوای اصلی صفحات وب ضروری نیست. و فقط سرعت بارگیری این صفحات را کاهش می‌دهد. به عبارتی برای اینکه محتوای صفحه به کاربر نمایش داده شود لازم نیست، همه کدهای css و جاوااسکریپت از قبل بارگیری شده باشند. بجای بارگیری تمام فایلها قبل از نمایش محتوا روی صفحه، میتوان از یک استراتژی کارآمدتر و کاربرپسندتر استفاده کرد. ساختار کدهای HTML به گونه‌ای باشد که فقط بخشهای ضروری ابتدا بارگیری شوند، فایلهای جاوااسکریپت و css میتوانند در پس‌زمینه درحالی کاربر قادر به نمایش محتوای صفحه است بارگیری شود. و بعد از بارگیری روی صفحه اعمال شوند.تعیین اینکه کدام منابع ضروری یا حیاتی (critical) هستند کاملا به محتوای هر صفحه بستگی دارد. مثلا اگر در بلاگ یک اسلایدر هم وجود دارد، کدهای جاوااسکریپت این اسلایدر ضروری نیستند یا اصطلاحا non-critical می‌باشند. روش مناسب این است که ابتدا محتوای مقاله به کاربر نمایش داده شود، سپس کدهای جاوااسکریپت بارگیری شوند و روی اسلایدر اعمال شوند. برای فایلهای css هم میتوان اولویت‌بندی تعیین کرد. فایلهای cssبارگیری محتوای css در ابتدای بارگیری صفحه تا حد زیادی از تجربه ناخوشایند دیدن محتوای صفحه بدون استایل برای کاربر جلوگیری میکند. برای css اولویت بندی بارگیری صفحه به معنای نمایش محتوای css نیست. بلکه باید استایلها را اولویت بندی کرد. به گونه‌ای که تجربه ناخوشایند نمایش صفحه بدون استایل انجام نشود و تاخیر زیادی برای نمایش محتوای صفحه ایجاد نشود. در این استراتژی استایلهای حیاتی css در ابتدا بارگیری و روی صفحه اعمال می‌شوند. این استایلها یا درون تگ style تعریف شده‌اند یا اگر در فایل خارجی هستند، حجم مناسبی (کمتر از 10kb) دارند. استایلهای غیرحیاتی بعد از بارگیری صفحه، در پس زمینه بارگیری میشوند و سپس روی صفحه اعمال می‌شوند. به این تکنیک اصطلاحا asynchronous CSS میگوئیم. در این بخش یک مثال از استایلهای حیاتی و غیرحیاتی آمده است :&lt;!-- other &lt;head&gt; stuff --&gt; 

&lt;!-- very small file for critical CSS (or optionally inlined with a &lt;style&gt; block) --&gt;
&lt;link rel=&#039;stylesheet&#039; href=&#039;critical.css&#039;&gt;  &lt;!-- under ~10KB --&gt;

&lt;!-- async non-critical CSS --&gt;
&lt;link rel=&#039;stylesheet&#039; media=&#039;print&#039; =&#039;this.=null;this.removeAttribute(&#039;media&#039;);&#039; href=&#039;non-critical.css&#039;&gt;  
&lt;!-- no-JS fallback for non-critical CSS --&gt;

&lt;noscript&gt;
   &lt;link rel=&#039;stylesheet&#039; href=&#039;non-critical.css&#039;&gt; 
&lt;/noscript&gt;  

&lt;/head&gt;استایلهای حیاتی : همه استایلهای ضروری که برای اینکه کاربر با یک صفحه ناخوشایند روبرو نشود به شیوه عادی به صفحه خود لینک میکنیم. این استایل باید تا حدامکان حجم آن کاهش یابد تا تاخیری در نمایش محتوا بوجود نیاید :&lt;!-- other &lt;head&gt; stuff --&gt;
 &lt;link rel=&#039;stylesheet&#039; href=&#039;critical.min.css&#039;&gt; &lt;!-- under ~10KB --&gt; 

&lt;/head&gt;همچنین این فایل باید کوچک شده یا اصطلاحا minified باشد.استایلهای داخلی : استفاده از تگ style میتواند روش مناسبی برای استفاده از استایلهای ضروری باشد. و تاثیر زیادی در کاهش سرعت روی صفحه‌ای که به خوبی بهینه شده نباشد، ندارند. این روش جلوی توقف رندر بخاطر یک فایل خارجی در html را میگیرد. &lt;!-- other &lt;head&gt; stuff --&gt; 

&lt;style&gt;.main-menu{...}.main-menu a{...}/*-- etc --*/&lt;/style&gt; 

&lt;/head&gt;یک نقطه ضعف برای استفاده از این متد این است که استایلها توسط مرورگر کش نمی‌شوند و هربار با لود صفحه باید مجددا بارگیری شوند.استایلهای غیر ضروری : استایلهای غیر ضروری در ابتدای بارگیری، بارگیری نمی‌شوند بلکه پس از نمایش اولیه محتوا بارگیری شده و به کاربر نمایش داده می‌شوند. برای این عمل، روشهای مختلفی وجود دارد. یکی از روش‌های توصیه شده استفاده از ویژگی media در  تگ لینک است :&lt;!-- other &lt;head&gt; stuff including critical CSS --&gt; 

&lt;!-- optionally increase loading priority --&gt; 
&lt;link rel=&#039;preload&#039; as=&#039;style&#039; href=&#039;non-critical.css&#039;&gt; 
&lt;link rel=&#039;stylesheet&#039; media=&#039;print&#039; =&#039;this.=null;this.removeAttribute(&#039;media&#039;);&#039; href=&#039;non-critical.css&#039;&gt; 

&lt;!-- no-JS fallback --&gt;
&lt;noscript&gt;
     &lt;link rel=&#039;stylesheet&#039; href=&#039;non-critical.css&#039;&gt;
&lt;/noscript&gt;  

&lt;/head&gt;در این روش استایلهای غیرضروری در پس‌زمینه بارگیری شده و سپس به کاربر نمایش داده می‌شود. این روش وابسته به جاوااسکریپت است.یک روش دیگری که برای جلوگیری از اتلاف وقت می‌توانیم انجام دهیم استفاده از شرط برای ویژگی media است. مثلا در این مورد اگر حداقل سایز عرض صفحه 60em باشد، فایلهای css بارگیری می‌شوند.&lt;link rel=&#039;stylesheet&#039; href=&#039;critical-general.css&#039;&gt;
&lt;link rel=&#039;stylesheet&#039; media=&#039;(min-width:60em); href=&#039;critical-large.css&#039;&gt;همچنین در روش دیگر میتوان فایلهای css را پایین تگ body اضافه کرد.&lt;!-- other &lt;head&gt; stuff --&gt; 
&lt;link rel=&#039;stylesheet&#039; href=&#039;critical.css&#039;&gt; 
&lt;/head&gt;  
&lt;body&gt;     

&lt;!-- page content --&gt; 
&lt;link rel=&#039;stylesheet&#039; href=&#039;non-critical.css&#039;&gt; 
&lt;/body&gt;جاوااسکریپت : از ویژگی defer برای تگهای script میتوانیم، کمک بگیریم تا سرعت رندر صفحه خود را افزایش دهیم. با استفاده از ویژگی فایل جاوااسکریپت بعد از load صفحه، در پس زمینه بارگیری و سپس اجرا می‌شود :&lt;!-- other &lt;head&gt; stuff --&gt; 

&lt;script defer src=&#039;sitewide.js&#039;&gt; 
&lt;script defer src=&#039;jquery.min.js&#039;&gt; 
&lt;script defer src=&#039;page-specific.js&#039;&gt; 

&lt;/head&gt;البته از ویژگی async هم میتوان استفاده کرد، اما این دو از قاعده متفاوتی استفاده میکنند.ویژگی async : پس از بارگیری کامل فایل script اجرا می‌شوند.ویژگی defer : پس از بارگیری کامل صفحه اجرا میشوند.به عبارتی اولویت با async است.جاوااسکریپت داخلی : بهتر است بدانیم اسکریپت داخلی کش نمیشود، پس بهتر است آنرا به یک یا چند فایل خارجی منتقل کنیم. برای اسریکتهای داخلی نمیتوانیم از ویژگی defer و async استفاده کنیم. در مثال زیر، برای اینکه اسکریپت داخلی پس از اسکریپت خارجی با ویژگی defer اجرا میشود از یک event listener بنام DOMContentLoaded استفاده میکنیم:&lt;!-- other &lt;head&gt; stuff --&gt; 
&lt;script defer src=&#039;scripts.js&#039;&gt; 
&lt;script defer src=&#039;jquery.min.js&#039;&gt;  

&lt;/head&gt; 
&lt;body&gt;     
&lt;!-- page content --&gt; 
 
   document.addEventListener(&#039;DOMContentLoaded&#039;, function(){ 
      // more jQuery-dependent stuff 
   }); 
 

&lt;/body&gt;</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Fri, 17 Feb 2023 12:21:14 +0330</pubDate>
            </item>
                    <item>
                <title>سازنده تابع در جاوااسکریپت - function generator</title>
                <link>https://virgool.io/@abasb75/%D8%B3%D8%A7%D8%B2%D9%86%D8%AF%D9%87-%D8%AA%D8%A7%D8%A8%D8%B9-%D8%AF%D8%B1-%D8%AC%D8%A7%D9%88%D8%A7%D8%A7%D8%B3%DA%A9%D8%B1%DB%8C%D9%BE%D8%AA-function-generator-r8u6ozkwowpa</link>
                <description>با اینکه در جاوااسکریپت از function generator زیاد استفاده نمیکنیم ولی ممکن است زمانی استفاده از آن ضروری شود. سازنده تابع یا function generator در es6 معرفی شد. برای درک function generator بهتر است. یکی ایجاد و از آن استفاده کنیم :function* counterGenerator(){
   // your code
}برای تعریف function generator باید بعد از کلمه کلیدی function یک کاراکتر * قرار دهیم تا مشخص شود میخواهیم یک  function generator ایجاد کنیم و نه یک تابع معمولی!بهتر است ابتدا یک مثال بزنیم :function counterGenerator(){
   return 1;
}

const counter = ounterGenerator();
console.log(counter);  // output : 1انتظار داریم با اجرای این کد، تابع counterGenerator مقدار 1 را به ما تحویل دهد، انتظار بجایی است اما برای یک function generator چطور؟function* counterGenerator(){
    return 1; 
}  

const counter = ounterGenerator(); 
console.log(counter);  // output : ounterGenerator objectاکنون با اجرای این دستور counterGenerator به ما یک object تحویل میدهد. برای استفاده از این object باید از یک متد درون آن بنام next استفاده کنیم. برای مثال :function* counterGenerator(){
     return 1;  
} 
 
const counter = ounterGenerator();  
const result = counter.next();
console.log(result);   

// { value: 1, done: true }خروجی متد next یک object است. که پراپرتی value مقدار برگشتی از تابع و done وضعیت اجرای تابع را نمایش میدهد. برای درک هدف از استفاده از function generator بهتر است با کلمه کلیدی yield آشنا شویم. yield عملکردی تقریبا مشابه return دارد، اما با اندکی تفاوت. به کد زیر دقت کنید :function* counterGenerator() {
    yield 1;
    yield 2;
    yield 3;
    yield 4;
 }  

const counter = counterGenerator(); 
console.log(counter.next());    // output : {value: 1, done: false}اکنون با اجرای next مقدار 1 را برگشت میدهد و done هم false است.console.log(counter.next());   // output : {value: 2, done: false}با اجرای مجدد next مقدار 2 را برگشت میدهد و done همچنان false است.console.log(counter.next());   // output : {value: 3, done: false}با اجرای سوم next مقداری که برای yield سوم تعیین کردیم، یعنی 3 را برگشت میدهد، ضمنا همچنان done مقدار false را دارد.console.log(counter.next());   // output : {value: 4, done: false}با اجرای چهارم next مقدار yield چهارمی را برگشت میدهد، یعنی مقدار چهارم برگشت داده میشود ، دقت کنید همچنان done مقدار fale دارد.console.log(counter.next());   // output : {value: undefined, done: true}  
console.log(counter.next());   // output : {value: undefined, done: true}
console.log(counter.next());   // output : {value: undefined, done: true}اکنون که همه yield ها برگشت داده شدند مقدار done به true تغییر کرد یعنی دیگر کار تابع سازنده ما به اتمام رسیده.function* counterGenerator() {
     yield 1;
     yield 2;
     return 3;
     yield 4;
}

const counter = counterGenerator();
console.log(counter.next());   // output : {value: 1, done: false}
console.log(counter.next());   // output : {value: 2, done: false}
console.log(counter.next());   // output : {value: 3, done: true}
console.log(counter.next());   // output : {value: undefined, done: true}
console.log(counter.next());   // output : {value: undefined, done: true}همینطور که مشاهده میکنید با دفعه اول و دوم اجرای next مقدار خروجی yield هم به ترتیب 1 و 2 میشود. اما با اجرای سوم مقدار 3 برگشت داده میشود و done هم true میشود، وقتی done برابر با true قرار گرفت دیگر همواره value برابر با undefined میشود یعنی تابع ما چیز دیگری برای برگشت دادن ندارد.متد next یک object با دو پراپرتی برگشت میدهد، پراپرتی value مقدار برگشتی از yield را و پراپرتی done هم به ما میگوید که کار تابع سازنده ما تمام شده یا خیر!با استفاده تابع سازنده قادرخواهیم بود فرایندها را ترتیب گذاری کنیم. همچنین از توابع سازنده درون حلقه نیز میتوانیم استفاده کنیم :function* counterGenerator() {
     yield 1; 
     yield 2; 
     yield 3; 
     yield 4; 
}  

const counter = counterGenerator();
for (var i=1;i&lt;=5;i++){
   console.log( counter.next() );
}
/* output :
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 4, done: false }
{ value: undefined, done: true }
*/یا درون حلقه for ... of :function* counterGenerator() { 
     yield 1; 
      yield 2;  
      yield 3; 
      yield 4;  
}

const counter = counterGenerator(); 

for (count of counter){
   console.log( count );
}

/* output : 
1
2
3
4
*/ساخت شمارنده بینهایت با تابع سازندهبه کد زیر دقت کنید :function* counterGenerator(){
   let count = 1;
   while (true) {
      yield count++;
   }
}

const counter = counterGenerator();   
for (var i=0; i&lt;10; i++){
    console.log( counter.next().value ); 
}
/*
1
2
3
4
5
6
7
8
9
10
*/با هر بار فراخوانی next مقدار count برگشتی از yield هم یکی افزایش میابد.const counter = counterGenerator();
setInterval(()=&gt;
   console.log(counter.next().value);
},1000);با استفاده از setInterval میتوان یک ثانیه شمار ایجاد کرد. اینکه منطقی باشد یا خیر، بحث دیگری است...پس دانستیم که با تابع سازنده میتوان فرایندهای مختلف را ترتیب گذاری کرد سپس برای اینکه فرایند اجرای دستورات را برای لحظاتی متوقف کرد و سپس آنرا ادامه داد از آن استفاده کنیم.برای مثال وقتی از درون ریداکس یک اکشن را dispatch میکنیم :store.dispatch({type:ACTION});تابع سازنده میتواند در سطح middleware تا نهایی شدن اکشن، ارسال به reducer را متوقف کند. پس از آماده شدن اکشن دستور next را اجرا و ادامه مراحل ارسال اکشن به reducer اجرا می شود.</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Wed, 01 Feb 2023 10:08:06 +0330</pubDate>
            </item>
                    <item>
                <title>ریداکس و api - بخش چهارم</title>
                <link>https://virgool.io/@abasb75/%D8%B1%DB%8C%D8%AF%D8%A7%DA%A9%D8%B3-%D9%88-api-%D8%A8%D8%AE%D8%B4-%DA%86%D9%87%D8%A7%D8%B1%D9%85-mg0lmorubprn</link>
                <description>برای اینکه بتوانیم از اکشنهای ناهمزمان مثل گرفتن اطلاعات از api استفاده کنیم، باید از redux-thunk استفاده کنیم.قبل از شروع توصیه میکنم که سایر پستهای من در مورد ریداکس را مطالعه بفرمایید :مدیریت حالات برنامه با ریداکسریداکس - بخش دومریداکس در کنار ریکت - بخش سومبیایید از فرمت اکشنها شروع کنیم. هر اکشن یک آبجکت مانند نمونه زیر است :const action =  { 
   type: &#039;ADD_TO_LIST&#039;,
   payload:itemData,
}ویژگی type به نوع اکشن و payload هم دیتای مورد نیاز برای تغییر state است. ما برای reducer باید مستقیما یک object بفرستیم یا یک تابع action creator مانند زیر که یک object برمیگرداند :به توابعی مانند تابع زیر که برای ما یک اکشن با فرمت مناسب تولید میکنند action creator میگوییم.function addToList(itemData) {
   return {
      type:&#039;ADD_TO_LIST&#039;,
      payload:itemData,
   }
}

store.dispatch(itemData);اما مثلا وقتی که اکشن دریافت دیتا از سرور داریم :function getFromAPI(){
   result = loadData();
   return {
      type:&#039;LOAD_FROM_DATA&#039;,
      payload:result,
   }
}

store.dispatch(getFromAPI());در این حالت dispatch به درستی عمل نخواهد کرد، چرا که به طور معمول ارسال اکشن به reducer بصورت همزمان یا سنکرون انجام میشود. در مثالهای بالاتر اکشن مورد نیاز reducer توسط یک تابع سنکرون ساخته میشود، dispatch آنرا فورا تحویل میگیرد و به reducer تحویل میدهد. اما در این مثال ما یک مشکل داریم : برای اینکه اکشن (یا همان object با فرمت اکشن) آماده شود، باید اطلاعات از سرور خوانده شوند، روی دیتای خوانده شده پردازش انجام شود و درنهایت تبدیل به فرمت مناسب برای اکشن شود، که این فرآیندی زمانبر است. پس ما نمیتوانیم از اکشن getFromAPI به طور معمول استفاده کنیم، زیرا یک action creator آسنکرون است. برای حل این مشکل باید از middleware کمک بگیریم :const middleware = store =&gt; next =&gt; action (){
   const action = awaitForAction()
   next(action);
}درون middleware کاری که باید انجام بدهیم این است که کاری کنیم تا وقتی که اکشن نهایی همراه با دیتای موردنیاز آماده نشده، صبر کند و بعد از آماده شدن اکشن نهایی، آنرا به reducer تحویل دهد. بطور مثال وقتی میخواهیم دیتا از سرور بخوانیم ، باید برنامه صبر کند تا دیتا از سرور بیاید و وقتی دیتا تبدیل به فرمت اکشن مناسب شد، تابع next اجرا شود. کاری دردسرساز است، اما خبر خوب این است redux-thunk همین کار را برای ما میکند. به ما کمک میکند تا اکشنهای ناهمزمان یا اصطلاحا آسنکرون را بتوانیم dispatch کنیم.خلاصه اگر میخواهیم با api درون redux کار کنیم یا باید خودمان یک middleware ایجاد کنیم یا از یک middleware آماده همانند redux-thunk استفاده کنیم.استفاده از redux-thunkابتدا کتابخانه های redux و redux-thunk را نصب میکنیم :npm i redux redux-thunkبرای استفاده باید یک store ایجاد کنیم :import {createStore,applyMiddleware} from &#039;redux&#039;;
import thunk from &#039;redux-thunk&#039;;

const initialState = {
   list:[],
   loading:true,
   error:false,
}

function reducer(state=initialState,action){
   switch(action.type){
      case GET_LIST:
         return {...state,loading:false,error:false,list:action.list};
      case SET_LOADINRG:
         return {...state,loading:true};
      case SET_ERROR:
         return {...state,loading:false,error:true}
      default:
         return state;
   }
}

const store = createStore(
   reducer,
   initialState,
   applyMiddleware(thunk), 
);دقت کنید thunk را به عنوان middleware به store معرفی کردیم.function setLoading() = {
   return {
      type: SET_LOADING,
   }
}

store.dispatch(setLoading() );این اکشن سنکرون یا همزمان است. موقعی که dispatch میشود یک اکشن برمی گرداند و سپس آن اکشن مستقیما به reducer ارسال میشود و در نتیجه loading مقدار برابر با true میگیرد.حالا به action creator زیر دقت کنید :function getData() { 
   return dispatch =&gt; {
      fetch(apiUrl).then(res=&gt;res.json()).then(data=&gt;{
         dispatch({
            type:GET_LIST,
            list:data.list,
         });
      }).catch(err=&gt;{
         dispatch({
             type:SET_ERROR,
          });
      });
   }
 }

  store.dispatch(getData());تابع action creator ناهمزمان، برخلاف مدل همزمان یک تابع برای middleware ارسال میکند. این تابع در middleware اجرا میشود (کاری که برای انجام آن redux-thunk را نصب کردیم) درون این تابع وقتی نتیجه از سرور با موفقیت رسید، اکشن GET_LIST برای reducer ارسال میشود. اگر هم فرایند با خطا همراه بود، اکشن SET_ERROR به reducer فرستاده میشود.امیدوارم پست مفید بوده باشه</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Tue, 31 Jan 2023 22:29:20 +0330</pubDate>
            </item>
                    <item>
                <title>ریداکس در کنار ریکت - بخش سوم</title>
                <link>https://virgool.io/@abasb75/%D8%B1%DB%8C%D8%AF%D8%A7%DA%A9%D8%B3-%D8%AF%D8%B1-%DA%A9%D9%86%D8%A7%D8%B1-%D8%B1%DB%8C%DA%A9%D8%AA-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-wromirsvhv5r</link>
                <description>کتابخانه react-redux با این هدف آمده است که کار با redux را در پروژه react آسان کند.برای شروع ابتدا میتوانید پستهای قبلی من در مورد ریداکس را مطالعه کنید :مدیریت حالات برنامه با ریداکسریداکس - بخش دوم ریداکس یک state manager سراسری برای کامپوننتهای react فراهم میکند. کامپوننتها با store ریداکس که محل عرضه stateهاست، سر و کار دارند. store را subscribe میکنند و با آپدیت state آن مقدار جدید را از store دریافت میکنند و state خود را آپدیت میکنند. تا اینجا علاوه بر state ریداکس، خود کامپوننتها نیز باید دارای یک state محلی باشند. تا به واسطه آپدیت آن state ، آن کامپوننت rerender شود.کتابخانه react-redux به عنوان یک واسط جدید بین کامپوننتها و ریداکس عمل میکند. به قولی نمایندگی جدیدتر و برنامه‌نویس پسندتر برای عرضه state ریداکس به کامپوننتهاست. بطوری که برای آپدیت یک کامپوننت نیازی به تعریف state محلی درون کامپوننت نباشد. به هرحال بهتر از استفاده از subscribe هست.شروع کار با react-reduxبه سراغ پروژه ریکتی خود میرویم و redux و react-redux را نصب میکنیم :npm i redux react-reduxبرای استفاده در reducer اکشنهای خود را می‌سازیم. const COUNTER_UP = &#039;COUNTER_UP&#039;;
const COUNTER_DOWN = &#039;COUNTER_DOWN&#039;;

function counterUp(value){
   return {
      type: COUNTER_UP,
      payload:value,
   }
}

function counterUp(value){
    return {
       type: COUNTER_DOWN,
       payload:value,
    }
 }اکشن COUNTER_UP مقدار value را به reducer میفرستد و به اندازه value به state شمارنده اضافه خواهد شد. اکشن COUNTER_DOWN مقدار value را به reducer میفرستد و به اندازه value از state شمارنده کم خواهد شد.به سراغ ایجاد reducer میرویم :function reducer(state={initialState} , action){
   switch(action.type){
      case COUNTER_UP:
         const currentCounter = state.counter;
          return {...state,counter:currentCounter + action.payload}
      case COUNTER_UP:
          const currentCounter = state.counter;
          return {...state,counter:currentCounter - action.payload}
      default:
          return state;
   }
}اکنون store خود را ایجاد میکنیم :import {lagacy_createStore as createStore} from &#039;redux&#039;;

const initialState = {
   counter:0,
}
const store = createStore(
   reducer,
   initialState,
);

export default store;خب اکنون store ما آماده است و میتوانیم در پروژه خود از آن استفاده کنیم.فراهم کننده یا Providerبرای شروع استفاده از react-redux، کامپوننت Provider را از react-redux به پروژه خود import میکنیم:import { Provider } from &#039;react-redux&#039;;کامپوننتهایی که قصد داریم درون آنها از store استفاده کنیم را با کامپوننت Provider باید بپوشانیم. همچین store را هم باید بعنوان props برای کامپوننت Provider مشخص کنیم. اکنون در تمام کامپوننتهای فرزند Provider میتوانیم از store ریداکس استفاده کنیم. البته منظور از تمام فرزندان شامل فرزندان فرزندان فرزندان ... هم میشود.اتصال ریداکس به کامپوننتهابرای اینکه از state ها و dispatch درون کامپوننت خودمان بتوانیم استفاده کنیم باید redux را به react component متصل کنیم یا اصطلاحا connect کنیم. چیز عجیب و وحشتناکی نیست. بسیار ساده این کار انجام میشود.اول connect را از react-redux ایمپورت میکنیم : import {connect} from &#039;react-redux&#039;;آماده کردن dispatch برای کامپوننت: به کد زیر دقت کنید.const mapDispatchToProps = (dispatch) =&gt; {
   return {
      doCounterUp:(value)=&gt;{dispatch(counterUp(value));  } ,
      doCounterDown:(value)=&gt;{dispatch(counterDown(value))} ,
   }
}این بخش دو تابع را برای ارسال به کامپوننت به عنوان props آماده میکند. تابع doCounterUp وقتی درون کامپوننت ما صدا زده شود، اکشن counterUp را به reducer میفرستد یا dispatch میکند. تابع doCounterDown هر وقت درون کامپوننت ما صدا زده شود، اکشن counterDown را برای reducer میفرستد.چیز پیچیده ای نیست یک تابع که در ورودی dispatch را میگیرد و از آن میتوانیم در توابعی که میخواهیم برای کامپوننت بعنوان props ارسال کنیم، استفاده کنیم.آماده کردن state برای کامپوننت : به کد زیر دقت کنید.const mapStateToProps = (state) =&gt; {
   return {
      counter: state.counter
   };
};برخی ویژگی هایی که برای استفاده در کامپوننت لازم داریم را با استفاده از mapStateToProps آماده ارسال به کامپوننت بعنوان props میکنیم. کل state را از ورودی میگیرد و آن بخشهایی از state که در کامپوننت لازم داریم، تا نمایش بدهیم را بعنوان props مشخص میکند.اکنون باید store خود را به کامپوننت B کانکت کنیم :export default connect(
   mapStateToProps,
   mapDispatchToProps
)(CompnentB);اکنون با استفاده از connect ، استور را به ComponentB متصل کردیم. همچنین از طریق mapStateToProps مقدار شمارنده درون redux state را بعنوان counter به این کامپوننت فرستادیم.دو تابع هم بعنوان dispatch کننده به این کامپوننت بعنوان props ارسال کردیم. اکنون برای استفاده میتوانیم بعنوان props از counter و doCounterUp و doCounterDown استفاده کنیم. برای نمایش شمارنده درون تابع render :render() {
   return (&lt;&gt;
      &lt;h3&gt;Counter in Component B : {this.props.counter}&lt;/h3&gt;
      &lt;button ={()=&gt;{}}&gt;Add&lt;/button&gt;
      &lt;button ={()=&gt;{}}&gt;Minus&lt;/button&gt;
   &lt;/&gt;);
}برای تغییر شمارنده redux state هم باید از توابع موجود در props این کامپوننت استفاده کنیم.render() {
   return (&lt;&gt;
      &lt;h3&gt;Counter in Component B : {this.props.counter}&lt;/h3&gt;
      &lt;button ={()=&gt;{this.props.doCounterUp(1)}}&gt;Add&lt;/button&gt;
      &lt;button ={()=&gt;{this.props.doCounterDown(1)}}&gt;Minus&lt;/button&gt;
   &lt;/&gt;);
}به همین راحتی با mapStateToProps ، حالت یا state های مورد نیاز رو بعنوان props به کامپوننتها میفرستیم و dispatch ها را با mapDispatchToProp. دقت کنید خروجی connect را بجای کامپوننت اصلی export کنیم:export default connect( mapStateToProps, mapDispatchToProps )(CompnentB);ComponentBکامپوننتهای تابعیخبر خوب اینجاست که استفاده از react-redux درون functional component هنوز هم ساده‌تر هست. میتوانیم از هوکهای react-redux به سادگی استفاده کنیم.برای نمایش مقدار state موجود در store فقط کافیست مانند نمونه از هوک useSelector استفاده کرد :import { useSelector } from &amp;quotreact-redux&amp;quot

export default function CompnentC(){
   const counter = useSelector((state)=&gt;state.counter);
   return (&lt;h3&gt;Counter State in C : {counter}&lt;/h3&gt;);
}هوک useSelector یک تابع در ورودی میگیرد که مقدار شمارنده را از state میگیرد و به ما پاس میدهد. با استفاده از این هوک هرگاه counter آپدیت شد، مقدار شمارنده درون کامپوننت C هم تغییر میکند.هوک useDispatch هم برای dispatch یا ازسال اکشن به reducer کاربرد دارد. برای نمونه import { useSelector , useDispatch } from &#039;react-redux&#039;;
import { counterUp , counterDown } from &#039;../state/actions&#039;;

ComponentB() {    
   const counter = useSelector((state)=&gt;state.counter);
   const dispatch = useDispatch();
   return (&lt;&gt;
       &lt;h3&gt;Counter in Component B : {counter}&lt;/h3&gt;
       &lt;button ={ ()=&gt;dispatch(counterUp(1)) }&gt;Add&lt;/button&gt;
       &lt;button ={()=&gt;dispatch(counterDown(1)) }&gt;Minus&lt;/button&gt;
    &lt;/&gt;);
 }به همین راحتی درون functional component میتوان اکشن را dispatch کرد.توصیه react-rdux به استفاده از هوک هاست. اما روش connect هم به خوبی کار میکند.مقایسه subscribe با useSelectorکامپوننت A : import { useEffect, useState } from &amp;quotreact&amp;quot
import store from &amp;quot../store&amp;quot

export default function CompnentA(){
   useEffect(()=&gt;{
      store.subscribe(()=&gt;{
         const state = store.getState();
         setCount(state.counter)
      });
   },[]);
   const [count,setCount] = useState(0);
   return (&lt;h3&gt;Counter State in A : {count}&lt;/h3&gt;);

}در کامپوننت A موقعی که اولین بار کامپوننت ایجاد شود ما باید store را subscribe کنیم. پس از آن، هربار که state موجود در store ریداکس تغییر کرد، setCounter درون ComponentA صدا زده میشود و پس از آن این کامپوننت rerender میشود. یعنی در حقیقت rerender شدن مجدد این کامپوننت وابسته به state درون خودش است نه state ریداکس!کامپوننت C:import { useSelector } from &amp;quotreact-redux&amp;quot
import {State} from &#039;../store&#039;;

export default function CompnentC(){
   const counter = useSelector((state:State)=&gt;state.counter);
   return (&lt;h3&gt;Counter State in C : {counter}&lt;/h3&gt;);
}درون کامپوننت C دیگر ما هیچ state یا حالتی تعریف نکردیم. به واسطه هوک useSelector با هر تغییر state موجود در store ، کامپوننت c هم آپدیت میشود.</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Tue, 31 Jan 2023 19:45:48 +0330</pubDate>
            </item>
                    <item>
                <title>ریداکس - بخش دوم</title>
                <link>https://virgool.io/@abasb75/%D8%B1%DB%8C%D8%AF%D8%A7%DA%A9%D8%B3-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-hjghjruovadt</link>
                <description>در این پست به نحوه استفاده از توابع ریداکس و middleware ها میپردازیم.اول اینکه بخش اول این سری پستهای ریداکس را از این لینک مشاهده کنید. در این پست به توابع و نحوه استفاده از آنها در ریداکس میپردازیم.برای نصب redux در پروژه خود، دستور زیر را در ترمینال وارد میکنیم :npm i reduxوارد ویرایشگر میشویم و شروع میکنیم به کدنویسی...ایجاد و استفاده از store برای ایجاد store ابتدا تابع createStore را از پکیج redux به فایل خود import میکنیم :import { createStore } from &#039;redux&#039;;میتوانیم بجای createStore از تابع legacy_createStore  استفاده کنیم :import { legacy_createStore  as createStore } from &#039;redux&#039;;این تابع دو ورودی دریافت میکند.const store = createStore(
reducer,
initialState, 
);ورودی اول همان reducer و ورودی دوم initialState یا حالت اولیه برنامه ماست. مثلا برای برنامه یک todolist میتوانیم این حالت را داشته باشیم:const initialState = {
   list:[], //list of to do list
}برای شروع یک لیست خالی از to do list ها داریم.برای ایجاد تابع reducer هم ابتدا باید اکشن های خود را مشخص کنیم:اضافه کردن به لیستحذف از لیستبرای هرکدام از اکشن ها یک type خاص با حروف بزرگ تعریف میکنیم :const ADD_TO_LIST = &#039;ADD_TO_LIST&#039;;
const REMOVE_FROM_LIST = &#039;REMOVE_FROM_LIST&#039;;اکشنها یک آبجکت با type اجباری و payload اختیاری هستند. برای اضافه کردن به todolist خود، باید برای reducer یک action با type ثابت ADD_TO_LIST و payload هم  که اطلاعات todo جدید را دارد، بفرستیم :function addToList(id, todoTitle,todoText ){
   return {
      type:ADD_TO_LIST ,
      payload:{
         id, 
         todoTitle,
         todoText,
      }
   };
}برای حذف از لیست هم باید برای reducer ثابت REMOVE_FROM_LIST به همراه id آن todo را ارسال کنیم.function removeFromList(id ){
    return {
       type:REMOVE_FROM_LIST ,
       payload:{
          id
       }
    };
 }پس از مشخص شدن اکشنها به سراغ ایجاد تابع reducer میرویم. این تابع دو ورودی دریافت میکند. ورودی اول state قبلی و ورودی دوم اکشن ارسالی در dispatch . اکشنها همواره مقدار type را دارند پس باید بر اساس type ارسالی نوع عملیات روی state را مشخص کنیم:function toDoListReducer( state={initialState},action ){
   switch(action.type){
      case ADD_TO_LIST:
         const list = state.list;
         list.push(action.payload);
         return {...state,list}
      case REMOVE_FROM_LIST:
         const list = state.list.filter(id=&gt;id!==action.payload.id);
         return {...state,list}
      default:
         return state;
   }
}پس از ایجاد reducer و initialState استور خود را ایجاد میکنیم:import { legacy_createStore  as createStore } from &amp;quotredux&amp;quot

const initialState = { list:[], //list of to do list }

const ADD_TO_LIST = &#039;ADD_TO_LIST&#039;; 
const REMOVE_FROM_LIST = &#039;REMOVE_FROM_LIST &#039;;

function addToList(id, todoTitle,todoText ){
    return {
       type:ADD_TO_LIST ,
       payload:{
          id,
          todoTitle,
          todoText,
       }
    };
 }

function removeFromList(id ){
     return {
        type:REMOVE_FROM_LIST ,
        payload:{
           id
        }
     };
}

function toDoListReducer( state={initialState},action ){
    switch(action.type){
       case ADD_TO_LIST: 
         const list = state.list; 
         list.push(action.payload);
          return {...state,list}
       case REMOVE_FROM_LIST:
          const list = state.list.filter(id=&gt;id!==action.payload.id);
          return {...state,list}
       default:
          return state;
    }
 }

const create = createStore(
   toDoListReducer , //reducer
   initialState, // initial state
);اکنون میتوانیم از store خود بخوانیم یا به آن اضافه کنیم. برای افزودن به store باید از تابع dispatch استفاده کنیم.store.dispatch( addToList(1, &#039;todo 1&#039;,&#039;todo text 1&#039; ) );
store.dispatch( {type:ADD_TO_LIST, payload:{ 2, &#039;todo 2&#039;,&#039;todo text 2&#039; }} );این دو خط هر کدام یک اکشن افزودن به لیست به reducer ارسال میکنند. در نتیجه 2 مورد اکنون به لیست ما در store افزوده شده است.تابع dispatch اکشن را برای reducer ارسال میکند.برای مشاهده لیست موجود در state هم از تابع subscribe استفاده میکنیم :store.subscribe(()=&gt;{
   const state = store.getState();
   console.log(state.list);
});با هر بار تغییر state در store تابعی که به subscribe پاس داده ایم اجرا میشود. و لیست ما در کنسول چاپ میشود.تابع getState : هر وقت این تابع صدا زده شود مقدار فعلی state را میدهد.میان افزار یا همان middlewareتابع dispatch یک اکشن را برای reducer ارسال میکند. اما بین مبدا تا مقصد این اکشن، یک نفر نشسته و همواره اکشن را بررسی و سپس اجازه ارسال آن به reducer را میدهد. به این شخص میگوییم middleware!در حقیقت middleware هم یک تابع است که قبل از reducer قرار میگیرد. یعنی وقتی dispatch اکشن را میفرستد ابتدا middleware آنرا بررسی میکند یا تغییر میدهد و سپس آنرا برای reducer میفرستد.به نمونه کد middleware دقت کنید:const logger = store=&gt;next=&gt;action=&gt;{
   console.log(&#039;before reduce :&#039;, store.getState() );
   next(action);
   console.log(&#039;next reduce :&#039;, store.getState() );
}این تابع store و action را میگیرد. در خط اول مقدار state قبل از اجرای reducer در کنسول چاپ میشود.تابع next : اکشن ورودی را برای reducer میفرستد.و خط سوم مقدار state بعد از تغییر state درون reducer را چاپ میکند.برای store خود می توانیم بی نهایت middleware تعریف کنیم. middleware بالا تنها کاری که میکرد چاپ کردن مقدار state، قبل و بعد از تغییر آن است. میخواهیم یک middleware تعریف کنیم که حروف اول title در todoList را قبل از اعمال در reducer به حروف بزرگ تبدیل میکند:const captlizer = store=&gt;next=&gt;action=&gt;{
   if(actio.type === ADD_TO_LIST){
      let title = action.payload.todoTitle;
      let captlizeTitle = title[0].toUpperCase();    
      action.payload.todoTitle = captlizeTitle;
   }
   next(action);
 }این middleware موقعی که action در تابع dispatch ارسال شود. ابتدا action را میگیرد، اگر type اکشن افزودن به لیست بود، حرف اول عنوان todo را به حروف بزرگ تبدیل میکند و سپس اکشن تغییر یافته را به وسیله تابع next به reducer ارسال میکند.ما تعداد زیادی middleware میتوانیم تعریف کنیم. اینجا دو middleware مشخص کردیم، یکی captlizer و دیگری logger! برای هر کدام هم نقشی مشخص کردیم. اکنون چگونه middleware های خود را به store معرفی کنیم؟در حقیقت تابع createStore میتواند سه ورودی هم دریافت کند.  ورودی سوم middleware های ماست که البته اختیاری است. پس کد خود را به صورت زیر تغییر میدهیم.const store = createStore(
   reducer, //reducer
   initialState, //initial state
   applyMiddleware(logger, captlizer),  // middleware
);تابع applyMiddleware : این تابع از redux به فایل ما import میشود. این تابع middleware های ما را دریافت میکند و برای تحویل به store آماده میکند. ما بینهایت middleware میتوانیم آماده کنیم و به store معرفی کنیم.وقتی کار شلوغ میشوددر یک پروژه بزرگ، ما تعداد زیادی action داریم. وقتی تعداد اکشنها زیاد شود به تبع آن حجم تابع reducer هم زیاد میشود. مثلا 200 اکشن داریم برای اینکه از این 200 اکشن درون یک reducer استفاده کنیم به یک reducer خیلی خیلی بزرگ نیاز خواهیم داشت. برای حل این موضوع هم فکری کرده‌اند. ما میتوانیم تعداد زیادی reducer تعریف کنیم و سپس آنها را به هم بچسپانیم. دقت کنید همچنان مقدار type برای اکشن باید یکتا باشد.تابع combineReducers : چند reducer را با هم می آمیزاند و یک reducer واحد برای معرفی به store ایجاد میکند.دقت کنید هر store فقط یک reducer دارد. با این تابع، چند reducer را به یک reducer تبدیل میکنیم.برای استفاده فقط کافیست :import { combineReducers } from &#039;redux&#039;;

function reducer1(state,action){
...
}

function reducer2(state,action){
...
}

...

const reducer = combineReducers({
   reducer1,
   reducer2,
   ...
});در ورودی این تابع هر تعداد reducer میتوان پاس داد.امیدوارم پست مفید واقع شده باشد. </description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Tue, 31 Jan 2023 15:44:46 +0330</pubDate>
            </item>
                    <item>
                <title>مدیریت حالات برنامه با ریداکس</title>
                <link>https://virgool.io/@abasb75/%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D8%AD%D8%A7%D9%84%D8%A7%D8%AA-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D8%A8%D8%A7-%D8%B1%DB%8C%D8%AF%D8%A7%DA%A9%D8%B3-pg2gpbvqjodx</link>
                <description>ریداکس یک کتابخانه مدیریت حالت برای جاوااسکریپت است، که معمولا همراه کتابخانه‌هایی مثل reactjs استفاده میشود.ریداکس بهترین روش نیست ولی خب بدترین هم نیست. در برخی پروژه ها استفاده از ریداکس منطقی هم نیست. اجباری هم نیست که حتما باید از redux استفاده کنیم، روشهای دیگری هم وجود دارد که همین کار را میکند، شما خودتان هم میتوانید یک state manager جدید پیاده سازی کنید. اما واقع بین باشیم حتی اگر خوشمان نیاید ولی کسی بابت خوشایند ما به ما پول نمیده. اگر میخواهیم برنامه نویس فرانت شویم و از دیگران پول بگیریم، باید با redux هم آشنا باشیم.هدف از استفاده از ریداکس چیست؟در وب اپلیکیشنها state زیادی در کامپوننتهای زیادی داریم، اکثر این state ها ارتباط مستقیمی با هم ندارند. ولی خب state هایی هم هستند که به یکدیگر ارتباط پیدا میکنند. بگذارید مثال سبد خرید فروشگاه آنلاین را بزنیم. در این مثال، وقتی کاربر روی دکمه سبد خرید کلیک میکند state دکمه افزودن به سبد خرید باید تغییر کند مثلا رنگ دکمه و متنش تغییر کند. state تعداد محصولات خرید هم باید درون هدر سایت تغییر کند و همچنین درون خود سبد خرید هم باید لیست محصولات تغییر کند. پس تا اینجا حداقل سه تا کامپوننت مجزا داریم که یک state مشترک دارند. و آن هم لیست سبد خرید است. هندل کردن این اتفاق بدون ریداکس کار سختی نیست ولی با ریداکس این کار آسانتر میشود. به عبارتی ما state لیست خرید را در store ریداکس تعریف میکنیم و درون هر کامپوننتی که بخواهیم به آن دسترسی پیدا میکنیم و آنرا تغییر میدهیم.بگذارید روی مثال خودمان متمرکز شویم شمارنده بدون ریداکسهمونطور که درون تصویر مشخصه سه تا کامپوننت داریم. state مشترک بین این سه کامپوننت counter هست. از این به بعد بهش میگیم شمارنده.کامپوننت A :import { useState } from &#039;react&#039;;
   export default function CompnentA(){
   const [count,setCount] = useState(0);
   return (&lt;h3&gt;Counter State in A : {count}&lt;/h3&gt;);
}کامپوننت B :import React from &amp;quotreact&amp;quot
class CompnentB extends React.Component{

   state = {counter:0,}

   render(): React.ReactNode {
      return (&lt;&gt;
         &lt;h3&gt;Counter in Component B : {this.state.counter}&lt;/h3&gt;
         &lt;button ={()=&gt;this.setState({counter:this.state.counter+1})}&gt;Add&lt;/button&gt;
         &lt;button ={()=&gt;this.setState({counter:this.state.counter-1})}&gt;Minus&lt;/button&gt;
      &lt;/&gt;);
   }

}

export default CompnentB;همینطور که میبینید هرکدام از دکمه ها کلیک شود مقدار شمارنده یک واحد تغییر میکند.کامپوننت C:import { useState } from &#039;react&#039;; 
export default function CompnentC(){
   const [count,setCount] = useState(0); 
   return (&lt;h3&gt;Counter State in A : {count}&lt;/h3&gt;);
 }این کامپوننت مشابه A مقدار شمارنده را باید نمایش دهد.اکنون ما سه کامپوننت داریم که میخواهیم مقدار  فعلی شمارنده را در هر کدام نمایش دهیم. بدون ریداکس state های کامپوننتها را باید به یکدیگر پاس دهیم. اما میریم سراغ کار خودمان یعنی ریداکس...ریداکس چگونه کار میکند؟ممکن است مفاهیمی که من استفاده میکنم با مفاهیم رسمی متفاوت باشد اما صرفا جهت ساده سازی از برخی مفاهیم نابجا ممکن است استفاده کنم.حالت یا state : در پروژه شمارنده، state مشترک بین چند کامپوننت، counter است.بجای اینکه هر state  را درون هر کامپوننت بصورت محلی تعریف کنیم، یک state سراسری برای ریداکس تعریف میکنیم و همه کامپوننتهایی که این state را لازم دارند میتوانند آن را از store ریداکس دریافت کنند.حالت شروع یا initialState : همین طور که ما درون هر کامپوننت یک state با مقدار اولیه تعریف میکنیم، باید برای redux هم یک حالت شروع تعریف کنیم. مثلا درون پروژه شمارنده state اولیه بصورت زیر است.interface State {
   counter:number;
}

const initialState:State = {
   counter:0,
}محل عرضه یا store : کامپوننتهای مختلف از طریق یک واسط همانند فروشگاه به state های ریداکس دسترسی دارند. مثلا کامپوننت a از طریق store مقدار state شمارنده را درخواست کند.دنبال کردن یا subscribe : طرفداران اپل که همیشه اخبار این شرکت را دنبال میکنند و سریعا با عرضه محصول جدید، آن را تهیه میکنند. بحث subscribe درون ریداکسن هم تقریبا همین است. کامپوننتها همواره store را دنبال میکنند یا اصطلاحا subscribe میکنند و همان لحظه که state تغییر کرد مقدار آن را دریافت و نمایش میدهند.اکشن یا action : دستورالعملی که برای ریداکس ارسال میشود. مثلا وقتی کاربر روی دکمه add در پروژه شمارنده کلیک میکند، در حقیقت یک action برای redux برای ریداکس ارسال میشود که به state شمارنده را اضافه کن یا وقتی دکمه minus کلیک شود دستورالعملی برای کاهش مقدار state شمارنده ارسال می‌کند. اعمال کننده یا reducer : در بحث ریداکس، یک بخش مهم داریم بنام reducer و وظیفه اش هم خیلی ساده است. اکشن را از کاربر و state قبلی را دریافت میکند و برروی state ها تغییرات را بر اساس آن اکشن اعمال میکند و در نهایت state جدید را تحویل فروشگاه یا store برای عرضه به مشتریها که همان کامپوننتها باشد میدهد.ارسال یا dispatch : منظور از dispatch در بحث ریداکس ، ارسال اکشن به reducer است.احتمالا بحثها برای کسی که تابه حال با ریداکس کار نکرده باشد کمی گنگ به نظر برسد. به تصویر زیر دقت کنید :عملکرد ریداکسوقتی کاربر روی دکمه ای کلیک کرد یک اکشن (دستورالعمل) به reducer ارسال میشود. reducer هم برحسب وظیفه state قبلی را از فروشگاه میگیرد و براساس action دریافتی، تغییرات جدید را را روی state اعمال میکند و به فروشگاه برای عرضه تحویل میدهد. کامپوننتها هم همواره منتظر هستند که state تغییر کند تا سریعتر آنرا دریافت کنند. مثلا مقدار شمارنده 1 شد، همه کامپوننتها مقدار 1 را دریافت میکنند.امیدوارم خوب و کافی قسمت تئوری را توضیح داده باشم.شروع کار با ریداکسابتدا باید ریداکس را به پروژه خود اضافه کنیم :npm i redux روش مرسوم این است درون پروژه خود یک دایرکتوری به نام store ایجاد میکنیم و فایل index را به آن اضافه میکنیم :سپس درون این فولدر یک فولدر دیگر برای action ها و یک فولدر هم برای reducer ها اضافه میکنیم :به سراغ فایل index موجود درون store میرویم و initialeState را تعریف میکنیم :interface State {
   counter:number;
}

export const initialState:State =  {
   counter:0,
}

export type {State}ایجاد دستورالعملهامرسوم است ابتدا action هایی را برای ریداکس تعیین کنیم. مثلا در پروژه شمارنده، دو action داریم. یکی اضافه کردن به شمارنده و دیگری کاهش مقدار شمارنده.interface CounterAction {
   type: &#039;ADD_TO_COUNTER&#039; | &#039;MINUS_FROM_COUNTER&#039;;
   payload: {
      value:number
   }
}

export const addToCounter:CounterAction = {
   type:&#039;ADD_TO_COUNTER&#039;,
   payload : {
      value:1,
   }
}

export const minusFromCounter:CounterAction = {
   type:&#039;MINUS_FROM_COUNTER&#039;,
   payload : {
      value:1,
   }
}ما دو اکشن MINUS_FROM_COUNTER و ADD_TO_COUNTER را در فایل actions/index.ts تعریف کردیم. در هر اکشن یک type داریم یک payload. ویژگی type را حتما باید مقداردهی کنیم. مقداری که برای type در نظر میگیریم مثل id برای این اکشن است. و باید مختص خودش باشد. نمیتوانیم دو اکشن با type یکسان داشته باشیم.در عوض مقداردهی و استفاده از payload اختیاری است. مثلا من در payload مقدار value برابر با یک را قرار دادم تا درون reducer از آن استفاده کنم.چیز عجیبی نیست هر اکشن یک object است که پراپرتی type برای آن اجباری و payload هم اختیاری است.ایجاد reducerپس از اینکه نوع اکشنهای موجود در برنامه خود را تعیین کردیم نوبت به reducer میشود. به سراغ فولدر reducer می رویم و reducer خود را درون آن ایجاد میکنیم.import { State } from &amp;quot..&amp;quot
import { CounterAction } from &amp;quot../actions&amp;quot

function reducer(state:State,action:CounterAction):State{
   const currentCounter = state.counter;
   switch(action.type){
      case &#039;ADD_TO_COUNTER&#039;:
         return {...state,counter:currentCounter + action.payload.value}
      case &#039;MINUS_FROM_COUNTER&#039;:
         return {...state,counter:currentCounter - action.payload.value}
      default:
         return state;
   }
}

export default reducer;همینطور که میبینید reducer هم چیز عجیبی نیست. یک تابع است که state قبلی و action کاربر را دریافت میکند و بر اساس نوع اکشن یک عملیات روی state، که اینجا کم کردن یا افزودن به آن است، انجام میدهد و state جدید را برگشت میدهد.خیلی ساده بود. شاید بخاطر همین سادگی پیچیده بنظر برسد ولی در حقیقت خیلی ساده بود. اکشن صرفا یک object ساده است و reducer هم صرفا یک تابع که action را میگیرد و روی state تغییر اعمال میکند. یک منطق ساده دارد و آن هم این است که اگر دستور افزودن رسیده به شمارنده اضافه کن و دستور کاهش رسید از شمارنده کم کن!تعریف storeبرای استفاده از state و reducer که تعریف کردیم باید یک store ایجاد کنیم، تا کامپوننتها با مراجعه به آن مقدار state ها را دریافت کنند و به کاربر نمایش دهند. به سراغ فایل store/index.ts میرویم :محتوای فایل store/index.ts بصورت زیر است :import {  Store, legacy_createStore as createStore } from &amp;quotredux&amp;quot
import reducer from &amp;quot./reducer&amp;quot

interface State {
   counter:number;
}
export const initialState:State =  {
   counter:0,
}
const store:Store = createStore(
   reducer, //reducer
   initialState, //state
);
export default tore;
export type {State}برای ساخت store مورد نیاز باید از تابع createStore استفاده کنیم. این تابع در ورودی reducer و initialState را دریافت میکند و یک store آماده استفاده به ما تحویل میدهد.دقت کنید تابع createStore دیگر پشتیبانی نمیشود، بجای آن میتوان تابع legacy_createStore را استفاده کرد.اکنون store ما آماده استفاده است. ارسال اکشن به reducerبه سراغ کامپوننت b که دکمه های افزایش و کاهش ما در آن قرار دارد، میرویم. وقتی کاربر روی دکمه Add کلیک کرد اکشن ADD_TO_COUNTER به reducer ارسال شود و اگر کاربر روی دکمه minus کلیک کرد، اکشن MINUS_FROM_COUNTER به reducer ارسال شود یا اصطلاحا dispatch شود. پس برای هر دکمه تابع مربوط به آن را تغییر میدهیم.ابتدا store که ایجاد کرده بودیم را برای استفاده در کامپوننت b فراخوانی میکنیم :import store from &amp;quot../store&amp;quotاکنون میتوانیم از آن استفاده کنیم.برای دکمه add تابعی به همین نام تعریف میکنیم. و درون آن اکشن ADD_TO_COUNTER را dispach میکنیم :add(){
   store.dispatch(addToCounter);
}برای دکمه minus هم باید اکشن minusFromCounter را dispatch کنیم :minus(){ 
   store.dispatch(minusFromCounter)
}تابع dispatch در ورودی یک اکشن دریافت میکند و آنرا برای reducer ارسال میکند. reducer هم بر اساس type این اکشن با state رفتار میکند.محتوای componentB به زیر تغییر کرد :import React from &amp;quotreact&amp;quot
import store from &amp;quot../store&amp;quot
import { addToCounter, minusFromCounter } from &amp;quot../store/actions&amp;quot

class CompnentB extends React.Component{

   state = {counter:0,}

   add(){ store.dispatch(addToCounter); }

   minus(){ store.dispatch(minusFromCounter) }

   render(): React.ReactNode {
      return (&lt;&gt;
         &lt;h3&gt;Counter in Component B : {this.state.counter}&lt;/h3&gt;
         &lt;button ={this.add}&gt;Add&lt;/button&gt;
         &lt;button ={this.minus}&gt;Minus&lt;/button&gt;
      &lt;/&gt;);
   }

}

export default CompnentB;نتیجه کار تا اینجا به این صورت هست که کاربر با زدن دکمه جمع یا منها هیچ چیزی در خروجی مشاهده نمیکند.نمایش state به کاربرانبرای اینکه بتوانیم نتیجه را به کاربران نمایش دهیم، باید از تابع subscribe استفاده کنیم. به سراغ componentDidMount در کامپوننت b می رویم : componentDidMount() {
   store.subscribe(()=&gt;{
   const state = store.getState();
      this.setState({counter:state.counter});
   });
}تابع subscribe در ورودی یک تابع میگیرد و هر وقت مقدار state ریداکس تغییر کرد، آن تابع اجرا میشود. مثلا در این مثال وقتی کاربر دکمه add یا minus را فشار داد، مقدار counter کامپوننتB تغییر میکند و دقیقا همان مقدار counter موجود در redux state میشود.مثلا در شکل بالا، وقتی کاربر دکمه add را فشار داد، اکشن ADD_TO_COUNTER به وسیله dispatch به reducer میرسد و reducer مقدار state را تغییر میدهد. با تغییر state در ریداکس، state شمارنده در کامپوننت B هم در لحظه تغییر خواهد کرد.تازه رسیدیم به اول کار، یعنی موقعی که componentB مقدار شمارنده را نمایش میداد برای سایر کامپوننت ها باید از subscribe استفاده کنیم. به سراغ کامپوننت A میرویم :در کامپوننتهای مبتنی بر کلاس باید در componentDidMount تابع subscribe را فراخوانی کنیم و درون کامپوننتهای مبتنی بر تابع، با هوک useEffect.چگونه unsubscribe کنیم؟unsubscribe:Unsubscribe|null=null;
componentDidMount() {
   this.unsubscribe = store.subscribe(()=&gt;{
      const state = store.getState();
      this.setState({counter:state.counter});
   })
}وقتی تابع subscribe را اجرا کنیم در خروجی تابع unsubscribe را برمیگرداند که میتوانیم از آن در بخشهای مختلف برنامه برای عمل unsubscribe در صورت لزوم استفاده کنیم.خلاصه مطلب کامپوننتها store را subscribe کرده اند و همیشه به state آن نگاه میکنند و با آپدیت state ریداکس state هر کامپوننت نیز آپدیت میشود. و در نتیجه همیشه همه کامپوننتها مقدار یکسان counter را نمایش میدهند.وقتی کاربر روی یک دکمه کلیک میکند، یک اکشن dispatch میشود و به reducer فرستاده میشود. reducer هم بر اساس type اکشن مقدار state را تغییر میدهد و به store میفرستد. به محض تغییر state هم کامپوننتها rerender میشوند.این پست درون وبسایت شخصی من منتشر شده است.ادامه این مطلب در ویرگول</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Mon, 30 Jan 2023 15:45:50 +0330</pubDate>
            </item>
                    <item>
                <title>ساخت حالت شب با کمی جاوااسکریپت و css</title>
                <link>https://virgool.io/@abasb75/%D8%B3%D8%A7%D8%AE%D8%AA-%D8%AD%D8%A7%D9%84%D8%AA-%D8%B4%D8%A8-%D8%A8%D8%A7-%DA%A9%D9%85%DB%8C-%D8%AC%D8%A7%D9%88%D8%A7%D8%A7%D8%B3%DA%A9%D8%B1%DB%8C%D9%BE%D8%AA-%D9%88-css-qmza64zon4jn</link>
                <description>در این پست با کدهای جاوااسکریپت و استفاده از ویژگیهای css حالت شب یا darkMode یا هر اسم دیگه ای که داره رو به صفحه وب خود اضافه میکنیم.اول اینکه چیزی میخوایم بسازیم شبیه به تصویر زیر هست. وقتی کاربر دکمه را فشار دارد رنگ نوشته ها و پس زمینه به حالت شب تغییر کنه. البته نمیخوایم زیاد پیچیده اش کنیم.حالت شب  با استفاده از جاوااسکریپتاول از همه ایجاد اسکلت صفحه وباول از همه فایل index.html را ایجاد میکنیم و تگهای مورد نیاز خودم را به آن اضافه میکنم :&lt;!DOCTYE html&gt;
&lt;html&gt;
     &lt;head&gt;
          &lt;title&gt;Dark Mode&lt;/title&gt;
         &lt;link href=&#039;style.css&#039; rel=&#039;stylesheet&#039; /&gt;
    &lt;/head&gt;
    &lt;body class=&#039;light&#039;&gt;
         &lt;div id=&#039;wrapper&#039;&gt;
               &lt;h1&gt;DarkMode Web Page&lt;/h1&gt;
              &lt;p&gt;press toggle darkMode button to enable/disable darkMode.&lt;/p&gt;
             &lt;button id=&#039;toggleDarkModeButton&#039;&gt;toggle darkMode&lt;/button&gt;
         &lt;/div&gt;
    &lt;/body&gt;
    
    
&lt;/html&gt;در این بخش تاکنون کار خاصی نکردیم، فقط برای دکمه toggle مقدار id را مشخص کرده ایم. همچنین به تگ body کلاس پیشفرض light را داده ایم.دلیل استفاده از single quoate در تگهای html ، محیط ویرگول است.دوم گوشته برای صفحه وببرای اینکه صفحه ما قابل تحمل شود فایل style.css خود را به آن اضافه میکنیم. ما ابتدا نیاز داریم که با متغییرها یا variable در css آشنا باشیم. خیلی راحت میتوان به شیوه زیر متغیر تعریف کرد.:root {
      --varname:#0A0A0A;
}مثلا برای رنگ 0A0A0A یک متغیر بنام varname تعریف کردیم و اکنون میتوانیم در فایلهای css خود از آن استفاده کنیم. مثلا اگر جایی میخواهیم رنگ نوشته را 0A0A0A بگذاریم بجای کد رنگ از متغیر آن استفاده میکنیم.p {
    color: var(--varname);
}یک متغیر تعریف و از آن استفاده کردیم. ما میتوانیم برای هر تگ دلخواه یک متغیر تعریف کنیم و این متغیر در تمام فرزندان آن تگ قابل استفاده است. مثلا اگر برای تگ body یک متغیر مثل کد زیر تعریف کنیم برای تمام فرزندان تگ body قابل استفاده است.body {
      --color: #0000CC;
}
h1 {
    color: var(--color);
}اکنون تگ h1 رنگ موجود در متغیر color را دریافت میکند.میریم به سراغ فایل style.css و کار خودمان. ما دو حالت داریم یا darkMode یا lightMode برای هر حالت برای تگ body یک کلاس مشخص میکنیم. اگر darkMode بود تگ body کلاس dark را داشته باشد ولی اگر lightMode فعال بود این تگ کلاس light را داشته باشد. پس دو حالت داریم و در هر حالت یک کلاس به تگ body میبخشیم. همان طور که در فایل index.html مشخص کردیم حالت پیشفرض light mode است. تفاوت حالت dark و light در رنگ نوشته‌ها و رنگ پس زمینه صفحه میباشد. پس کد زیر را به صفحه خود اضافه میکنیم :.light{
     --backgroundColor:#F1F1F1;
     --textColor:#3A3A3A;
}

.dark{
    --backgroundColor:#2C2C2C;
   --textColor:#E622B5;
}اگر حالت light بود رنگ پس زمینه و نوشته ها درون متغیر backgroundColor و textColor ذخیره میکنیم ولی اگر حالت night بود رنگهای دیگری را به این متغیرها نسبت میدهیم. دقت کنید این دو کلاس را برای تگ body در نظر گرفتیم.ما درون فایل html خود تگ div با شناسه (id) wrapper را داریم که نیازمند دریافت رنگ پس زمینه است :#wrapper{
      width: 600px;
     height: 400px;
     background: var(--backgroundColor);
     padding: 20px;
     transition: .5s all;
     margin: 100px auto;
}خب با استفاده از متغیر backgroundColor اگر حالت night بود wrapper هم پس زمینه F1F1F1 را میگیرید ولی اگر حالت dark فعال بود wrapper هم پس زمینه 2C2C2C را به خود میگیرد. در قسمت بالاتر گفته شده که متغیرهای تگ body به تمام فرزندانش میرسد و مقدار این متغیر هم بستگی به نوع کلاس این تگ دارد.برای رنگ نوشته ها هم به همین صورت میتوانیم از متغیر textColor استفاده کنیم :#wrapper p ,
#wrapper h1{
      color: var(--textColor);
      transition: .5s all;
}

#wrapper button{
     background: #3498DB;
     border: none;
    color: #FDFDFD;
    padding: 14px 28px;
   cursor: pointer;
   border-radius: 5px;
}با استفاده از ویژگی transition هم میتوانیم به تغییر رنگها در هنگام فعال یا غیر فعال‌سازی حال dark mode حالت انیمیشن‌گونه ببخشیم.دمیدن روح به صفحه وبتنها کاری که در قسمت جاوااسکریپت باید انجام دهیم این است : وقتی کاربر روی دکمه toggle کلیک کرد کلاس را برای تگ body تغییر دهد :
     var toggleDarkModeButton = document.getElementById(&#039;toggleDarkModeButton&#039;);
     toggleDarkModeButton.addEventListener(&#039;click&#039;, toggleDarkModeFunction);
    
      function toggleDarkModeFunction(){
            document.body.classList.toggle(&#039;dark&#039;);
           document.body.classList.toggle(&#039;light&#039;);
     }
به وسیله id دکمه را پیدا میکنیم و با addEventListener مشخص میکنیم وقتی کاربر روی دکمه click کرد تابع toggleDarkModeFunction اجرا شود. در این تابع هم اگر تگ body کلاس dark داشت آنرا حذف میکنیم ولی اگر نداشت به لیست کلاسهایش اضافه میکنیم. همچنین برای کلاس light هم همینطور!البته میتوانیم تابع خود را بهتر هم کنیم ولی هدف آشنایی با روند کلی کار بود نه جزئیات!برای استفاده در کتابخانه هایی مثل reactjs خیلی راحت یک state در کامپوننت تعریف کرد و با توجه به مقدارش به تگ root خود کلاس مناسب را ببخشیم.ذخیره برای مراجعات بعدیاکنون اگر صفحه توسط کاربر رفرش شود، دوباره وضعیت به حالت اول برمیگردد. برای اینکه موقعی که کاربر دوباره به صفحه ما مراجعه کرد بتوانیم با همان تنظیمات دلخواهش صفحه را به او نمایش دهیم از localstorage استفاده میکنیم.همچنین برای مطالعه این پست و یا پستهای دیگر من، میتوانید به وبسایت من مراجعه کنید.</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Sun, 29 Jan 2023 09:20:26 +0330</pubDate>
            </item>
                    <item>
                <title>عبارات با قاعده یا regular expressions</title>
                <link>https://virgool.io/@abasb75/%D8%B9%D8%A8%D8%A7%D8%B1%D8%A7%D8%AA-%D8%A8%D8%A7-%D9%82%D8%A7%D8%B9%D8%AF%D9%87-%DB%8C%D8%A7-regular-expressions-tjv1omebrara</link>
                <description>عبارات با قاعده کاربرد بسیاری در کار با رشته ها در زبانهای برنامه نویسی و پایگاه داده دارند. در این پست به  بررسی عبارات باقاعده یا همون regular expressions خارجیا میپردازیم.اول اینکه از عبارات با قاعده برای چک کردن اینکه یک رشته از یک الگو و قاعده پیروی میکند یا خیر، استفاده میشود. و مسئله دوم این است که سینتکس عبارات باقاعده در اکثر زبانها مشترک است. ما در این پست به بررسی عبارات باقاعده با سینتکس جاوااسکریپت و php میپردازیم.آیا یک رشته شامل یک کاراکتر است یا خیر؟در زبانهای برنامه نویسی جاوااسکریپت و php ما نیاز داریم که عبارات با قاعده خود را بین کاراکتری مثل / قرار دهیم. در مثال زیر نیازی به پوشش عبارت باقاعده بین &#x27; و &quot; نیست. مثلا :var regex = /a/عبارت با قاعده بالا تمام رشته هایی که حرف a درون آنهاست را میپذیرد. برای درک بهتر به مثال زیر دقت کنید :var regex = /a/
const term = &#039;abbas bagheri&#039;;
var output = regex.exec(term);
console.log(output);
// output : true

regex = /a/
const term2  = &#039;sourosh&#039;;
output = regex.exec(term);
console.log(output);
// output : falseدر مثال اول چون در رشته حرف a داشتیم جواب true شد و در رشته دوم چون a نداشتیم جواب آزمایش منفی بود. بهمین سادگی یک قاعده ساده نوشتیم و قاعده ما این بود که رشته باید درونش کاراکتر a باشد.آیا یک رشته شامل یک رشته دیگر است یا خیر؟برای مثال در ورودی از کاربر یک رشته میگیریم و میخواهیم چک کنیم که آیا در این رشته ورودی عبارت &#x27;ab&#x27; وجود دارد یا خیر؟&lt;?php
$regex = &#039;/ab/&#039;;
$input = &#039;abbas&#039;;
$output =  preg_match($regex,$input);
echo $output;
// output : 1 (true)

$regex = &#039;/ab/&#039;;
$input = &#039;bagheri&#039;;
$output =  preg_match($regex,$input); 
echo $output; 
// output : 0 (false)در مثال اول چون در رشته abbas رشته ab وجود داشت جواب درست بود و در رشته bagheri جواب منفی شد. یک قاعده ساده ایجاد کردیم و این قاعده چک میکند که درون رشته ای که از کاربر می آید رشته ab وجود دارد یا نه؟حساسیت عبارات با قاعده به حروف کوچک و بزرگبه مثال زیر دقت کنید :&lt;?php 
$regex = &#039;/ab/&#039;;
$input = &#039;Abbas&#039;;
$output =  preg_match($regex,$input); 
echo $output; 
// output : 0 (false)مثلا کاربر بجای abbas رشته Abbas را وارد کند. تفاوت این دو فقط درون کوچک و بزرگ بودن حروف است. جواب کد منفی میشود زیرا درون رشته Abbas رشته ab وجود ندارد. در واقع عبارات باقاعده به طور پیش فرض به حروف کوچک و بزرگ حساس هستند. ما میتوانیم این حساسیت را با modifier i تغییر دهیم. گفتیم قاعده خود را بین دو علامت / قرار میدهیم. برای تغییر modifier حساسیت به حروف بزرگ و کوچک بعد از / پایان قاعده یک کاراکتر i قرار میدهیم.&lt;?php  
$regex = &#039;/ab/i&#039;;
$input = &#039;Abbas&#039;;
$output =  preg_match($regex,$input);  
echo $output;  
// output : 1 (true)در مثال بالا قاعده خود را طوری نوشتیم که به حروف کوچک و بزرگ حساس نباشد. گاها پیش می آید که نیاز داریم قاعده ای بنویسیم که به حروف بزرگ و کوچک حساس نباشد از modifier i استفاده میکنیم. در ادامه ممکن است به modifier تغییر حالت یا اصلاح هم بگوییم.آیا یک رشته با یک رشته و کاراکتر شروع میشود یا خیر؟مثلا ما یک رشته از کاربر میگیریم. میخواهیم بررسی کنیم که این رشته دریافتی با ab شروع میشود یا خیر؟ برای این مورد باید با کاربر کاراکتر ^ در عبارات با قاعده آشنا شویم.var regex = /^ab/iدر این مثال وقتی عبارت با قاعده ما با ^ شروع میشود به معنای این است که ab حتما باید در ابتدای رشته ورودی قرار بگیرید. به مثال زیر توجه کنید:var regex = /^ab/i
var output = regex.exec(&#039;Abbas Bagheri&#039;);
console.log(output); // output : 1 or true

regex = /^ab/i
output = regex.exec(&amp;quot Abbas Bagheri&amp;quot);
console.log(output); // output : 0 or falseتفاوت این دو مثال این است که در مثال اول رشته ما با ab شروع میشود ولی در مثال دوم رشته ما با کاراکتر فضای خالی شروع میشود. دقت کنید که عبارات باقاعده به فضای خالی یا اسپیس هم حساس هستند. پس اگر کاراکتر ^ در ابتدای عبارت باقاعده بیاید به معنای این است که رشته ورودی باید با این کاراکتر یا این رشته یا به عبارتی این قاعده شروع شود.آیا یک رشته با یک رشته یا کاراکتر پایان میابد یا خیر؟مسئله: از کاربر یک رشته میگیریم، میخواهیم بررسی کنیم این رشته با کاراکتر i پایان میابد یا خیر؟برای این مورد باید با کاربرد کاراکتر $ در پایان عبارات باقاعده آشنا شویم. وقتی در انتهای عبارات با قاعده خود کاراکتر دلار $ قرار میدهیم دو اتفاق رخ میدهد. اول اینکه قاعده ما روز به روز گرونتر میشود و دوم به این معناست فقط رشته هایی را میپذیرد که با این قاعده پایان میابند. به مثال زیر توجه کنید :var regex = /i$/i 
var output = regex.exec(&#039;Abbas Bagheri&#039;); 
console.log(output); // output : 1 or true  

regex = /i$/i 
output = regex.exec(&amp;quotAbbas Baghery&amp;quot); 
console.log(output); // output : 0 or falseدر مثال اول رشته ما با i خاتمه میافت پس جواب مثبت بود و رشته مثال دوم با y، پس جواب منفی شد. پس به کاربرد کاراکترهای ^ در ابتدای رشته و $ در انتهای رشته حتما باید آشنا شده باشید.اگر در ابتدای قاعده (بعد از / شروع) کاراکتر ^ بگذاریم به معنای این است که حتما رشته ما با این قاعده شروع شود مثلا حرف اولش a باشد و $ در پایان قاعده (قبل از / بسته) به معنای این است که رشته ورودی باید با این قاعده پایان یاید.کاراکتر . یا نقطه در عبارات با قاعدهمسئله : یک قاعده ایجاد کنید که رشته سه حرفی را بپذیرد که  با a شروع میشوند و با e هم خاتمه میابند.به قاعده زیر دقت کنید.regex = /^a/میدانیم این قاعده به این معناست که رشته ما باید با کاراکتر  a (نه A) شروع شود.regex = /e$/معنای این قاعده هم این است که رشته ما با کاراکتر e پایان یابد.به قاعده زیر دقت کنید :regex = /^ٌae$/دقت کنید برای حل این مسئله نمیتوانیم از قاعده بالا استفاده کنیم چون این قاعده رشته هایی میپذیرد که با ae شروع شده و با همان ae پایان میابد. یعنی اگر کاربر دقیقا در ورودی ae وارد کرد (حتی بدون فاصله) جواب مثبت میشود و به ازای تمام رشته های دیگر مانند aejkjijcae جواب منفی است.برای حل مسئله بالا باید با کاربرد کاراکتر . (غریبه نیست، همان نقطه خودمان میشود) در عبارات باقاعده آشنا شویم. این کاراکتر به این معناست که بجایش هر کاراکتری میتواند قرار بگیرد. وقتی میگوییم هر کاراکتری منظومان دقیقا همه کاراکترهای موجود در کامپیوتره. مانند خود . یا ! یا حروف یا اعداد!!به قاعده زیر دقت کنید:regex = /a.e/این قاعده رشته هایی را میپذیرد که درونشان یک رشته سه حرفی باشد، طوری که آن رشته سه حرفی با a شروع شده و با e پایان یابد و نقطه وسط هم منظورش اینه که بجایش هر چیزی باشد مانند : ate ، a8e یا a#eمثلا قاعده بالا رشته های ateful و pater را میپذیرد. دقت کنید بجای کاراکتر نقطه فقط یک کاراکتر میتواند باشد. مثلا agger قبول نیست. چون بین a و e دو کاراکتر داریم.ما گفتیم رشته ما سه حرف دارد که با a شروع شده و با e هم پایان یابد. ولی در قاعده بالا این مورد را در نظر نگرفتیم. ما کاربرد کاراکترهای ^ و $ را میدانیم. پس قاعده خود را به این صورت تغییر میدهیم :regex = /^a.e$/خب جواب مسئله ما این قاعده است. ^ به این معناست که رشته ورودی باید با a شروع شده و کاراکتر $ به این معناست که رشته ورودی باید با e پایان یابد. کاراکتر نقطه هم به این معناست که بین a و e یک کاراکتر باید باشد.کاراکتر ? یا علامت سوال لاتین : کاراکتر اختیاری کنندهاز علامت سوال یا ? برای اختیاری کردن وجود یک کاراکتر در رشته استفاده میکنیم. مثلا در مثال بالا اگر مسئله را به صورت زیر تغییر دهیم :مسئله : یک عبارت باقاعده طراحی کنید که رشته هایی را بپذیرد یک با a شروع شده و با e پایان یابد. بین a و e یک کاراکتر میتواند قرار بگیرد.به قاعده زیر دقت کنید :regex = /^a.e$/این قاعده به این معناست که حتما باید بین a و e یک کاراکتر (نه بیشتر از یکی) وجود داشته باشد. ولی برای مسئله ما این مورد خوب نیست چون اگر کاربر در ورودی ae را وارد کرد جواب منفی میشود. برای حل این موضوع باید بگوییم که کاراکتر بین a و e میتواند وجود نداشته باشد یا به عبارتی وجودش اختیاری است. اینجا از ؟ کمک میگیریم. قاعده خودر را بصورت زیر تغییر میدهیم:regex = /^a.?e$/با علامت سوال مشخص کردیم که وجود کاراکتر بین a و e اختیاری است.به قاعده زیر دقت کنید:regex = /^a.e?$/در این قاعده وجود کاراکتر e در پایان رشته اختیاری شده یعنی تنها رشته هایی را میپذیرد که با a شروع شده و بعد از a یک کاراکتر دیگر هم باید وجود داشته باشد و در کاراکتر سوم یا باید e باشد یا چیزی نباشد مثلا age و ag را تایید میکند. چون مشخص کردیم e اختیاری است ولی agx را نمیپذیرد.کاراکتر + یا جمع : حداقل یک بار تکرارمسئله :  یک قاعده طراحی کنید بطوری که رشته های سه حرفی را بپذیرد که با a شروع شده و با i هم خاتمه میابند. مثلا ai قبول نیست ولی aeri و a9993n#dsei قبول است.به قاعده زیر دقت کنید :regex = /^a.i$/این قاعده تنها برای کلمات سه حرفی جواب میداد. اما ما نیاز داریم که بین a و i بین یک تا بینهایت کاراکتر داشته باشیم. برای هندل کردن این موضوع از کاراکتر + کمک میگیریم.به قاعده زیر دقت کنید.regex = /^a+i$/دقت کنید به کمک کاراکتر + در عبارت باقاعده مشخص میکنیم کاراکتر قبل از خودش حداقل یک بار تا بی نهایت بار میتواند تکرار شود. یعنی در این قاعده میتوانیم بینهایت a را تکرار کنیم یعنی کلمات i و aaaai و aaaaabi را میپذیرد ولی i را بدلیل عدم وجود a یا aiii را بدلیل اینکه i چند بار تکرار شده را نمیپذیرد.به قاعده زیر دقت کنید:regex = /^ai+$/در این قاعده بر عکس مثال بالاتر مشخص شده رشته با یک a شروع شده و با بین 1 تا بینهایت i پایان یابد مثلا ai و aiiiiiiiiiiiiiiiiiiiiiiiiiiiiii .به این قاعده دقت کنید :regex = /^a.i$/به این قاعده دقت کنید بین a و i یک کاراکتر باید باشد. اما ما میخواهیم بین a و i حداقل یک کاراکتر تا بینهایت وجود داشته باشد. برای این مورد از کاراکتر + کمک میگیریم.regex = /^a.+i$/قاعده بالا جواب مسئله است. رشته ورودی را میپذیرد که با a شروع شده، با i تمام شده و بین این دو حداقل یک کاراکتر میتوان وجود داشته باشد.کاراکتر * یا ستاره : حداقل صفر بار تکرارتفاوت کاراکتر * با + فقط در تعداد تکرار است. به مسئله زیر دقت کنید:مسئله :  یک قاعده طراحی کنید بطوری که رشته هایی را بپذیرد که با a شروع شده و با i هم خاتمه میابند. مثلا ai ، aei و  a#456i قبول است.به قاعده زیر دقت کنید :regex = /^a.+i$/این قاعده تمام رشته های سه حرفی برای مسئله بالا را پوششن میدهد. اما ai را پوشش نمیدهد، زیرا بین a و e هیچ کاراکتری نداریم. گفتیم + بعد از یک کاراکتر به این معناست که حداقل آن کاراکتر یک بار تکرار در رشته ورودی شود. ولی ما نیاز داریم که ai را هم بپذیرد. فقط کافیست + را به * تغییر دهیم:regex = /^a.*i$/این قاعده جواب مسئله ماست. به قطعه کد زیر دقت کنید.const words = [ &#039;abas baghei&#039; ,  &#039;Abas Bagheri&#039; , &#039;Abbbi&#039;, &#039;ai&#039;, &#039;ci&#039; ];
for (var i=0;i&lt;5,i++){
      var regexWithPlus = /^a.+i$/i
      var regexWithStar =  /^a.*i$/
      console.log(&#039; plus :&#039;, regexWithPlus.exec(words[i]) );
      console.log( &#039;star :&#039;, regexWithStart.exec(words[i]) );
}
// output i=0 ; plus : true , star : true
// output i=1 ; plus : true , star : false
// output i=2 ; plus : true , star : false
// output i=3 ; plus : false , star : true
// output i=4 ; plus : false, star : falseدر مثال بالا برای abas bagheri جواب هردو مثبت شد، زیرا بین a و i چند کاراکتر وجود دارد.در مثال دوم برای Abas Bagheri حالت با علامت جمع به دلیل modifier i  مثبت شد ولی حالت با علامت ستاره منفی شد.در مثال سوم هم به دلیل  مثال قبلی همین اتفاق رخ داد.در مثال چهارم برای کلمه ai در حالت با علامت جمع منفی شد زیرا حداقل یک کاراکتر بین a و i وجود ندارد ولی در قاعده با علامت * مثبت شد زیرا کاراکتر قبل از علامت ستاره میتواند صفر بار تکرار شود یا به معنای دیگر میتواند وجود نداشته باشد. میشه به شکلی غیر رسمی گفت * ترکیت + و ? است.در مثال ci هم چون رشته با a شروع نشده بود در هردو حالت منفی شد.کاراکتر | : OR خارجیا و &lt;یا&gt; برای مامسئله : یک برنامه طراحی کنید که رشته ورودی را بررسی کند و فقط آنهایی که با ab شروع شده و یا با e تمام شده را بپذیرد.طبق این مسئله رشته ما باید با ab شروع شده باشد مثلا abbas یا خود ab و یا باید با e تمام شده باشد مثلا age. لزومی ندارد که هردو شرط برقرار باشند.به قواعد زیر دقت کنید :startWithABRegex = /^ab/  // ex: ab ,abbas , abadis , ...
endWithERegrx = /e$/   // ex: age , e , ajdjjde ,...قاعده اول میشود شرط اول و قاعده دوم میشود شرط دوم و به همین راحتی از آن استفاده میکند.var startWithABRegex = /^ab/  // ex: ab ,abbas , abadis , ... 
var endWithERegrx = /e$/   // ex: age , e , ajdjjde ,...
var input = &#039;age&#039;
console.log( (startWithABRegex.exec(input) ||  startWithABRegex.exec(input))  );
// output : true

startWithABRegex = /^ab/  // ex: ab ,abbas , abadis , ...  
endWithERegrx = /e$/   // ex: age , e , ajdjjde ,...
input = &#039;abbas&#039;
console.log( (startWithABRegex.exec(input) ||  startWithABRegex.exec(input))  ); 
// output : true

startWithABRegex = /^ab/  // ex: ab ,abbas , abadis , ...   
endWithERegrx = /e$/   // ex: age , e , ajdjjde ,... 
input = &#039;bagheri&#039;
console.log( (startWithABRegex.exec(input) ||  startWithABRegex.exec(input))  );  
// output : falseدر مثال اول age با e پایان یافت پس نتیجه true میشود. در مثال دوم abbas با ab شروع شده پس دوباره شرط برقرار است و true میشود ولی در مثال سوم هیچ کدام از دوحالت برقرار نیست. پس نتیجه false میدهد و رشته را نمیپذیرد.ما آمدیم و دو قاعده مجزا تعریف کردیم و با or جاوااسکریپت رشته ورودی را برای دو قاعده چک کردیم. تا یکی از دوقاعده لااقل true شود. اما در مسئله باید یک قاعده طراحی کنیم نه دوتا یا سه تا یا بیشتر .... :)برای حل این موضوع دو قاعده را در یک قاعده کنار هم مینویسیم و بین آن دو علامت | قرار میدهیم :var regex = /^ab|e$/  // ex: ab ,abbas , abadis , age , e , ajdjjde ,...
var input = &#039;age&#039;
console.log( regex.exec(input)  ); // output : true

var regex = /^ab|e$/  // ex: ab ,abbas , abadis , age , e , ajdjjde ,...  
input = &#039;abbas&#039;
console.log( regex.exec(input)  ); // output : true

var regex = /^ab|e$/  // ex: ab ,abbas , abadis , age , e , ajdjjde ,...  
 input = &#039;bagheri&#039;;
console.log( regex.exec(input)  ); 
// output : falseاین قاعده رشته هایی را میپذیرد یکی از دو قاعده در طرفین کاراکتر | را داشته باشد یا با ab شروع شود یا با e پایان یابد. دقت کنید برای | یا or فقط یکی از شرطها درست باشد کافیست.به مثالهای زیر دقت کنید :regex = /^a|i$|ab|^s.*y$/این قاعده از ترکیب 4 قاعده دیگر بوجود آمده. رشته ورودی اگر یکی از شرایط را داشت، قاعده ما آنرا میپذیرد.حالت اول /a^/ : تمام رشته هایی که با کاراکتر a شروع شده باشند.حالت دوم /$i/ : تمام رشته هایی که با کاراکتر i تمام شده باشند.حالت سوم /ab/ : تمام رشته هایی که شامل ab شوند. لزومی ندارد ab در ابتدا بیاید یا در انتهای رشته.حالت چهارم /$y*.s^/ : تمام رشته هایی که با y شروع شده و با s تمام شوند مانند yes و ys و yaaaas!regex = /cc|er/این قاعده هم رشته هایی را میپذیرد که درون خود cc یا er را داشته باشند.دقت کنید در جاوااسکریپت و php علامت | دو کاراکتر را از هم جدا نمیکند مثلا در مثال بالا منظور c یا e نیست. بلکه دو قاعده قبل و بعد خودش را کامل از هم جدا میکند و دو قاعده مجزا در نظر میگیرد.کاراکتر () یا داخل پرانتز : ایجاد گروهمسئله : یک عبارت منظم ( باقاعده) طراحی کنید که فقط رشته هایی بپذیرد که در آن چند بار ab تکرار شده باشد. مانند ab و abababab قبول است ولی abaab قبول نیست.به عبارت باقاعده زیر دقت کنید :regex = /ab/در این قاعده ما مشخص کردیم درون رشته ما ab وجود داشته باشد. مثلا abas هم قبول میکند. برای مسئله ما خوب نیست.regex = /^ab$/گفتیم این قاعده هم ab را فقط میپذیرد.regex = /^ab.*ab$/این قاعده هم مشخص میکند که رشته ها با ab شروع شده و با ab هم پایان یابند . مثلا abejenjeab را میپذیرد. و حتی ab را نمیپذیرد.ما باید با کاربرد پرانتز درون عبارات منظم آشنا شویم. در حقیقت وقتی درون یک پرانتز باز و بسته، یک رشته یا عبارت منظم قرار دهیم میتوان با عبارت داخل پرانتز مانند یک کاراکتر رفتار کرد(البته این یک بیان برای درک بهتر پرانتزها بود. ). یا به عبارتی کاراکترهای درون پرانتز را باهم گروه کرد. بهرحال به مثال های زیر دقت کنید.regex = /^.(ab)?$/     // examples: c , cab , i , iabاین مثال رشته هایی را می پذیرد که با یک کاراکتر دلخواه شروع شده و بعد از این یک کاراکتر میتوانیم ab داشته باشیم یا نه. مثلا رشته های c و cab هردو را میپذیرد. regex = /^u(ab)*$/     // examples: u , uab , uabab , uababab, ...این الگو مشخص میکند رشته با u شروع شده و بعد از آن بین 0 تا بینهایت بار ab تکرار شود.regex = /^u(ab)+$/     // examples: uab , uabab , uababab, ...این الگو مشخص میکند رشته با u شروع شده و بعد از آن بین 0 تا بینهایت بار ab تکرار شود. تنها تفاوت با مثال بالاتر در این است که u را نمیپذیرد زیرا باید ab حداقل یکبار تکرار شود. به مثال زیر اکنون دقت کنید.regex = /^uab+$/     // examples: uab, uabb, uabbb, uabbbbb, ...اگر پرانتز را دور ab قرار نداده بودیم علامت جمع فقط برای کاراکتر قبل از خودش یعنی کاراکتر b  عمل میکرد. اما با پرانتز دور ab مشخص کردیم به ab نگاه کند. یا به عبارت دیگر ab مانند یک کاراکتر عمل کند نه دو کاراکتر مجزا!خب برمیگردیم به مسئله خودمان. رشته باید با ab شروع شده و پایان یابد و ab هم بین 1 تا بینهایت بار تکرار شود. گفتیم برای حداقل یکبار تکرار علامت جمع و اینکه علامت جمع به ab نگاه کند میگذاریمشان درون پرانتز.regex = /^(ab)+$/ // ex : ab,abab , ababab , abababababababab , ...خب جواب مسئله ما این است. متاسفانه یا خوشبختانه کاربرد علامت پرانتز فقط این نیست. مسئله بالا را کمی تغییر میدهیم.مسئله : یک عبارت منظم ( باقاعده) طراحی کنید که فقط رشته هایی بپذیرد که در آن چند بار ab و ai تکرار شده باشد یا با ac شروع شده باشند. مثلا aceecec و aiaiaiai هم مورد قبول است. خب ما میتوانیم سه تا قاعده مجزا طراحی کنیم :regexAC = /^ac/    //ex: aceijeiej, ac , acsksks
regexAB = /^(ab)+$/    //ex: ab , abab, abababab , ...
regexAI = /^(ai)+$/    //ex: ai, aiai , aiaiaiaiaiمیدونیم که با کاراکتر | میتوان سه قاعده را کنار هم گذاشت و مسئله را حل کرد.regex = /^ac|^(ab)+$|^(ai)+$/    //ex: aceijeiej, ac , acsksks ,ab , abab, abababab , ai, aiai , aiaiaiaiai , ...جواب درست است اما با پرانتز هم میتوان کمی قاعده را کوتاه تر و جمع و جورتر کرد.regex = /^ac|^(ab|ai)+$/    //ex: aceijeiej, ac , acsksks ,ab , abab, abababab , ai, aiai , aiaiaiaiai , ...برای درک بهتر این موضوع به قاعده زیر دقت کنید.regex = /^(ab|ai)+$/    //ex: ab , abab, abababab , ai, aiai , aiaiaiaiai , ...در بخش قبل گفتیم که علامت | دو بخش قاعده را از هم جدا میکند. و دو قاعده مجزا در نظر میگیرد، اما وقتی از علامت | درون پرانتز استفاده کنیم فقط به درون پزانتز نگاه میکند و کاری به بیرون پرانتز ندارد.مسئله : قاعده ای طراحی کنید که رشته ای را بپذیرد که با ab یا aca شروع شده و با i هم خاتمه یابد. مثلا abbas bagheri  و acarri مورد قبول هستند ولی adri مورد قبول نیست.دو قاعده داریم یا با ab شروع شده یا با aca :regex = /^ab.*i$|^aca.*i$/به کمک پرانتز میتوان خلاصه تر نوشت :regex = /^(ab|aca).*i$/در این قاعده رشته باید با ab یا aca شروع شده و بعد از آن هرچیزی میتواند باشد ولی این رشته باید با i خاتمه یابد. اما برای ab و aca حرف a در ابتدا مشترک است. پس :regex = /^a(b|ca).*i$/در این قاعده حرف ابتدایی رشته باید a باشد، بعد از a یکی از رشته های درون پرانتز بیاید و بعد از آن هر کاراکتری و در نهایت رشته با i تمام شود.به مثال زیر توجه کنید :regex = /^a(bu|ci|th)+z$/این قاعده رشته هایی میپذیرد که a در ابتدا و z در انتهای آن باشد. و بین این دو فقط تکرار با یکی از موارد داخل پرانتز  (منظور این نیست که مثلا فقط bu تکرار شده باشد.) مجاز است. مثلا abubucicicibuthz مجاز است. کاراکتر {} یا کروشه : محدودیت تعداد تکرارمسئله : یک قاعده طراحی کنید که رشته 9 حرفی را بپذیرد. همه کاراکترها مجاز هستند پس باید از نقطه استفاده کنیم. قاعده ما با نقطه شروع شده با نقطه پایان میابند و یک نقطه هم باید بینشان باشه.regex = /^...$/این قاعده مسئله را حل میکند. کاراکتر شروع هر چیزی میتواند باشد. کاراکتر انتها و میانی هم همینطور. اما اگر مسئله بجای سه حرفی گفت 1000 حرفی چطور؟با استفاده از کروشه تعداد تکرار هر کاراکتر را تعیین میکنیم.regex = /^.*$/وقتی بعد از یک کاراکتر ستاره بگذاریم به معنی این است که این کاراکتر بین صفر تا بینهایت بار میتواند تکرار داشته باشد.regex = /^.+$/وقتی بعد از یک کاراکتر علامت جمع بگذاریم به معنی این است که این کاراکتر بین یک تا بینهایت بار میتواند تکرار داشته باشد.regex = /^.{1000}$/علامت کروشه به معنای آن است که کاراکتر قبل از آن دقیقا باید به تعداد عدد داخل کروشه تکرار شود. مثلا اینجا فقط رشته های 1000 کاراکتره را میپذیرد. پس راه حل منطقی تر مسئله بالا استفاده از {} است.regex = /^.{3}$/به مثال زیر دقت کنید :regex = /^ab{2}/این قاعده فقط رشته هایی را میپذیرید که در ابتدای آن abb وجود داشته باشد.regex = /^(ab){2}/این قاعده رشته هایی را میپذیرد که در ابتدای آن abab امده باشد.regex = /^(ab|cd){2}/این قاعده رشته ای را میپذیرد که در ابتدای آن یکی از ترکیبات ab با cd آمده باشد مثلا cdab ابتدای رشته باشد. دقت کنید abab و cdcd هم مجاز است.مسئله : قاعده ای طراحی کنید که رشته هایی را بپذیرد که درون آن ab سه تا پنج  بار  پشت سر هم تکرار شده باشد. مثلا ascasabababllkregex = /(ab){3,5}/اگر در کروشه بجای یک عدد دو عدد قرار دهیم به معنای محدودیت تعداد تکرار است. دقت کنید منظور اینجا بین 3 تا 5 است یعنی 3 4 و 5 نه فقط 3 و 5.علامت [] براکت : محدودیت نوع کاراکترمسئله : یک قاعده طراحی کنید که فقط رشته های عددی که بین 5 تا 15 رقم دارند را بپذیرد.ما میتوانیم تعداد تکرار کاراکتر را محدود کنیم.regex = /.{5,15}/این قاعده تمام رشته هایی که بین 5 تا 15 کاراکتر هستند را میپذیرد. گفتیم علامت نقطه یعنی همه کاراکترها!برای اینکه بتوانیم نوع کاراکتر را محدود کنیم بجای (نه بعد از) علامت نقطه از براکت استفاده میکنیم :regex = /[0123456789]{5,15}/به قاعده بالا دقت کنید. اول اینکه براکت با پرانتز کاملا معنای متفاوتی دارد. دوم اینکه وقتی کاراکتری را درون براکت میگذاریم یعنی آن کاراکتر مجاز است بجای براکت بنشیند. مثلا یکی از ارقامی که گذاشتیم. بجای رقم میتوان حروف لاتین یا عربی گذاشت. یا هر کاراکتری که وجود دارد.regex = /^[2ی54v re]+$/مثلا این برنامه فقط رشته هایی را میپذیرد که فقط شامل کاراکترهای مجود درون براکت باشند. مثلا 23rv.دقت کنید اسپس که درون براکت گذاشتیم این معنا را میدهد که اسپیس هم کاراکتر مجاز است.برمیگردیم سر مسئله :regex = /[0123456789]{5,15}/گفتیم این قاعده رشته هایی که شامل رشته ای دیگر که متشکل از ارقام عددی است را میپذیرد. مثلا a232443nkjn را هم میپذیر. چون نقطه شروع و پایان را درون قاعده محدود نکردیم پس این قاعده را به این تغییر میدهیم :regex = /^[0123456789]{5,15}$/اکنون فقط رشته های عددی که بین 5 تا 15 رقم را دارند میپذیرد. البته میتوان این را خلاصه تر کرد.regex = /^[0-9]{5,15}$/اینجا علامت - به معنای تا است. اینجا یعنی 0 تا 9. البته میتوان مثلا بجای 1234 هم نوشت 1 تا 4!مسئله : قاعده ای تعریف کنید که فقط کلمات لاتین را بپذیرد. میدانیم کلمه فقط از حروف لاتین ساخته شده است. نه از اعداد و یا سایر کاراکترها. پس درون براکت فقط کاراکترهای حروف لاتین را قرار میدهیم.regex = /^[abcdefghijklmnopqrstuvwxyz]$/دقت کنید کل این براکت فقط بجای آن یک نقطه عمل میکند. پس قاعده بالا فقط کلمه یک حرفی را میپذیرد.regex = /^[abcdefghijklmnopqrstuvwxyz]+$/اینجا تمام کلماتی که حداقل یک حرف دارند را میپذیرد. البته گفتیم قاعده به بزرگی و کوچکی حروف حساس است. پس دو راه دارم :regex = /^[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]+$/راه دوم استفاده از modifier i است.regex = /^[abcdefghijklmnopqrstuvwxyz]+$/iبین حروف هم میتوان از - یا تا استفاده کرد. مثلا گفت a-z به معنی a تا z است.regex = /^[a-z]+$/iیا بدون modifier :regex = /^[a-zA-Z]+$/اکنون این قاعده فقط کلمات را میپذیرد. برای اینکه ارقام را هم بپذیرد :regex = /^[a-zA-Z0-9]+$/دقت کنید بجای ارقام و حروف سایر کاراکترها مانند نقطه و یا هر کاراکتر دیگری را درون براکت گذاشت.کاراکتر [^] یا : برعکس براکتوقتی در ابتدای شروع براکت کاراکتر ^ را قرار دهیم، دقیقا کار براکت برعکس میشود. دقت کنید کاراکتر ^ در ابتدای براکت با ^ در ابتدای قاعده بحث کاملا متفاوتی دارد. مثلا در قاعده زیر :regex = /^[0-9]+$/ رشته هایی که  فقط شامل ارقام عددی باشند را میپذیرد ولی قاعده زیر :regex = /^[^0-9]+$/فقط رشته هایی را میپذیرد که شامل ارقام عددی نباشند.کاراکتر \ : استفاده از کاراکترهای خاصمثلا ما میخواهیم درون عبارات با قاعده خود از کاراکترهایی مانند نقطه ، علامت سوال، اسلش و هر کاراکتر رزرو شده دیگه ای که در عبارات باقاعده معنای خاصی دارند، استفاده کنیم. تنها کاری که باید کنیم قبل از آن کاراکتر \ میگذاریم.مثلا :regex = /^[a-zA-Z0-9\.\[\]\{\}\/]+$/در این جا علاوه بر حروف و ارقام ؛ رشته های دارای کاراکترهای نفطه و برکت و گیومه و اسلش را هم میپذیریم.regex = /^_\+/این قاعده فقط رشته هایی را میپذیرد که با +_ شروع میشوند.کاربرد \ با حروف خاص برخی حروف اگر بعد از \ بیایند درون دستورات عبارات با قاعده معنای خاصی دارند. مثلا برای اینکه کنترل کنیم که فقط رشته های عددی پذیرفته شوند از قاعده زیر استفاده میکردیم :regex = /^[0-9]+$/روش دیگر استفاده از d\ است :regex = /^\d+$/این دو قاعده معادل همدیگر هستند. برعکس d\ میشود D\ : یعنی دو قاعده زیر معادل یکدیگر هستند.regex = /^[0-9]+$/
regex = /^\D+$/هر دو این قاعده فقط رشته هایی را میپذیرند که هیچ رقم عددی درون آن نباشد.دقت کنید که d خلاصه شده digit است.برای کلمات هم w\ و W\ را داریم که برعکس یکدیگر عمل میکنند.حروف w\ : به معنای کاراکتری است که فقط شامل حروف لاتین است.regex = /^[a-zA-Z]+$/ 
regex = /^\w+$/حروف W\ : به معنای کاراکتری است که فقط شامل غیر حروف لاتین است.regex = /^[^a-zA-Z]+$/ 
regex = /^\W+$/حروف s\ : به معنای کاراکتر اسپیس است. شامل تمام اسپیسهاست.regex = /^[  ]+$/  
regex = /^\s+$/حروف S\ : به معنای کاراکتر غیر اسپس است. شامل تمام کاراکترها به غیر از اسپیس.regex = /^[^ ]+$/   
regex = /^\S+$/کاراکتر modifier : اصلاح حالتکاراکترهایی که بعد از پایان قاعده قرار میدهیم. و هرکدام حالت قاعده را تغییر میدهد. مانند i.مدیفایر i : برای کنترل اینکه قاعده ایجاد شده نسبت به حروف بزرگ و کوچک حساس نباشد استفاده میکنند. regex = /^a.+$/iدر این حالت abas و Abas توسط این قاعده پذیرفته میشوند و پاسخ true برمیگردد.مدیفایر s : با کمک این مدیفایر، قاعده رشته ورودی را یک خط در نظر میگیرد. مثلا :$input = &#039;I am abbas
bagheri&#039;;
$regex = &#039;abbas.bagheri/s&#039;;
echo preg_match($regex , $input);
// output : 1 or trueمدیفایر m : با این مدیفایر، رشته دریافتی را بر اساس خط به خط بررسی میکند. و برای خط جداگانه بررسی میکند.$input = &#039;abax
abei
abef
abld
ddde&amp;quot
$regex = &amp;quotab.{2}/m&#039;;
echo preg_match($regex , $input); در مثال بالا قاعده رشته را خط به خط بررسی میکند یکبار برای abax یک بار برای abei و تا خط آخر و خروجی یک آرایه برمیگرداند.برخی کاربردهای عبارت باقاعدهبررسی شماره تلفن همراه کاربر :شماره تلفن به دو صورت وارد میشود یا 09015827703 یا 98901827703+. یا با 0 شروع میشود یا 98+ phoneRegex = /^(\+98|0)/رقم بعدی 9 است :phoneRegex = /^(\+98|0)9/باتوجه به شماره های اپراتورهایی که در ایران داریم رقم بعدی 0 1 2 3 و 9 باشد :phoneRegex = /^(\+98|0)9[01239]/و سپس شماره تلفن با 8 کاراکتر عددی دیگر پایان میابد :var phoneRegex = /^(\+98|0)9[01239][0-9]{8}$/
input = &#039;09015827703&#039;
phoneRegex.exec(input); // return true

var phoneRegex = /^(\+98|0)9[01239][0-9]{8}$/ 
input = &#039;09515827703&#039;
phoneRegex.exec(input); // return falseامیدوارم پست مفید بوده باشه.</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Fri, 27 Jan 2023 11:46:25 +0330</pubDate>
            </item>
                    <item>
                <title>اضافه کردن کتابخانه جاوااسکریپت به npm</title>
                <link>https://virgool.io/@abasb75/%D8%A7%D8%B6%D8%A7%D9%81%D9%87-%DA%A9%D8%B1%D8%AF%D9%86-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-%D8%AC%D8%A7%D9%88%D8%A7%D8%A7%D8%B3%DA%A9%D8%B1%DB%8C%D9%BE%D8%AA-%D8%A8%D9%87-npm-llvoft3pkykk</link>
                <description>در این پست یک پکیج جاوااسکریپت می سازیم و آنرا به npm اضافه میکنیم.برای شروع ابتدا یک فولدر به نام fraction ایجاد میکنیم.mkdir fraction
cd fractionبا دستور زیر این پروژه را یک پروژه npm تعریف میکنیم.npm init -y # or use npm initدرون پوشه fraction یک فایل بنام package.json به وجود آمده است. که محتویات آنرا به صورت زیر تغییر میدهیم.  {  &amp;quotname&amp;quot: &amp;quot@abasb75/fraction&amp;quot,
   &amp;quotversion&amp;quot: &amp;quot0.0.1&amp;quot,
   &amp;quotdescription&amp;quot: &amp;quotfraction with equation-parse&amp;quot,
  &amp;quotmain&amp;quot: &amp;quotindex.js&amp;quot,
 &amp;quotscripts&amp;quot: { 
         &amp;quottest&amp;quot: &amp;quotecho \&amp;quotError: no test specified\&amp;quot &amp;&amp; exit 1&amp;quot  
  },&amp;quotkeywords&amp;quot:    &amp;quotreact fraction&amp;quot ,    &amp;quotfraction&amp;quot,     &amp;quotfractions&amp;quot,  &amp;quotmath&amp;quot       ],
   &amp;quotauthor&amp;quot: &amp;quotAbbas Bagheri &lt;abasbagheria@gmail.com&gt;&amp;quot,
  &amp;quotlicense&amp;quot: &amp;quotISC&amp;quot
}ویژگی name نام کتابخانه مورد نظر ماست که من @abasb75/fraction در نظر گرفتم. دقت کنید این نام باید یکتا باشد و قبلا کسی آنرا در npm ثبت نکرده باشد.ویژگی version نسخه کتابخانه را مشخص میکند. من برای شروع 0.0.1 در نظر گرفتم.ویژگی description یک توضیح کوتاه یک جمله ای در مورد کتابخانه است.ویژگی main آدرس اصلی فایل شروع برنامه است.ویژگی keywords کلمات کلیدی کتابخانه که برای بهبود نمایش کتابخانه در جستجو میتوا از آن کمک گرفتویژگی author هم نام سازنده کتابخانه است.پس از ایجاد پروژه npm ابتدا کتابخانه های مورد استفاده را به پروژه خود اضافه میکنیم.ما قصد داریم در این پروژه از کتابخانه equation-parser استفاده کنیم.npm install -S equation-parsernpm install typescript --save-devnpm install @types/node --save-devاکنون باید لیست کتابخانه های مورد استفاده به فایل package.json اضافه شده باشد.یک پوشه بنام src می سازیم.mkdir srcدر این پوشه فایلهای زیر را اضافه میکنیم.fraction/
-- package.json
-- package-lock.json
-- src/
-- -- index.ts
-- -- src/
-- -- -- parse.ts
-- -- -- renderTree.ts
-- -- -- compute.ts
-- -- -- parsedFraction.tsدقت کنید من یک پوشه بنام پوشه src در کنار فایل index.ts ایجاد کردم و هر دو را درون فولدر src که از قبل ایجاد کرده بودم قرار دادم.فایل parsedFraction.ts :import {EquationNode, EquationParserError} from &#039;equation-parser&#039;;  
export type ParsedFraction =  EquationNode | EquationParserError;فایل parse.ts :import {parse} from &#039;equation-parser&#039;;
import {ParsedFraction} from &#039;./parsedFraction&#039;;

export default function (term:string,error?:Function):ParsedFraction{
 const parsedFraction:ParsedFraction = parse(term);
if(parsedFraction.type==&amp;quotparser-error&amp;quot &amp;&amp; error){
        error();
    }
    return parsedFraction;
} فایل renderTree.ts :import {parse,renderTree} from &#039;equation-parser&#039;;
import {ParsedFraction} from &#039;./parsedFraction&#039;;

export default function (term:string,error?:Function){
   const parsedFraction:ParsedFraction = parse(term);
   if(parsedFraction.type==&amp;quotparser-error&amp;quot &amp;&amp; error){
       error();
  }
    return renderTree(parsedFraction);
}فایل compute.ts :import parse from &#039;./parse&#039;;
import {ParsedFraction} from &#039;./parsedFraction&#039;;

export default function (term:string,error?:Function):number{
   const parsedFraction:ParsedFraction = parse(term,error);
    if(parsedFraction.type !== &amp;quotparser-error&amp;quot){
        return result(parsedFraction,error ? error : ()=&gt;{} );
    }else{
        return 0;
    }
}
      
function result(parsedFractions:ParsedFraction,error:Function):number{
    if(parsedFractions.type == &amp;quotnumber&amp;quot){
        return parseInt(parsedFractions.value);
    }else if(parsedFractions.type === &amp;quotmultiply-dot&amp;quot){
        return result(parsedFractions.a,error) * result(parsedFractions.b,error);
    }else if(parsedFractions.type === &amp;quotdivide-fraction&amp;quot){
        return result(parsedFractions.a,error) / result(parsedFractions.b,error);
    }else if(parsedFractions.type === &amp;quotplus&amp;quot){
        return result(parsedFractions.a,error) + result(parsedFractions.b,error);
    }else if(parsedFractions.type === &amp;quotminus&amp;quot){
        return result(parsedFractions.a,error) - result(parsedFractions.b,error);
    }else if(parsedFractions.type === &amp;quotblock&amp;quot){
        return result(parsedFractions.child,error);
    }else{
      error();
       return 0;
  }
}فایل index.ts :import compute from &amp;quot./src/compute&amp;quot
import renderTree from &amp;quot./src/renderTree&amp;quot
import parse from &amp;quot./src/parse&amp;quot
      
export {compute,renderTree,parse} ;اگر در فولدر fraction فایل tsconfig.json وجود نداشت آنرا بوجود می آوریم و اگر هم موجود بود محتوای آنرا به زیر تغییر میدهیم.فایل tsconfig.json : {
&amp;quotcompilerOptions&amp;quot: {
      &amp;quotstrict&amp;quot: true,
      &amp;quotdeclaration&amp;quot: true,
      &amp;quotesModuleInterop&amp;quot: true,
      &amp;quotoutDir&amp;quot: &amp;quotdist&amp;quot,
      &amp;quottarget&amp;quot: &amp;quotes6&amp;quot,
      &amp;quotmodule&amp;quot: &amp;quotes6&amp;quot,
      &amp;quotmoduleResolution&amp;quot: &amp;quotnode&amp;quot,
    },
    &amp;quotinclude&amp;quot: [&amp;quotsrc&amp;quot]
} باید کدهای typescript خود را کامپایل کرده و خروجی جاوااسکریپت بگیریم. برای این کار دستور زیر را در ترمینال وارد میکنیم.tscبعد از پایان کامپایل یک فولدر جدید به نام dist به پروژه ما اضافه شد.مسیر dist را در فایل tsconfig.json مشخص کرده بودیم.&amp;quotoutDir&amp;quot: &amp;quotdist&amp;quot,فایل README.md یا راهنما را برای کتابخانه خود بسازید و به پروژه اضافه کنید.اکنون پروژه خود را به گیتهاب اضافه کنید و آدرس آنرا به فایل package.json اضافه کنید.&amp;quotrepository&amp;quot: {
    &amp;quottype&amp;quot: &amp;quotgit&amp;quot,
    &amp;quoturl&amp;quot: &amp;quotgit+https://github.com/abasb75/fraction.git&amp;quot
},
&amp;quotbugs&amp;quot: {
    &amp;quoturl&amp;quot: &amp;quothttps://github.com/abasb75/fraction/issues&amp;quot
},
&amp;quothomepage&amp;quot: &amp;quothttps://github.com/abasb75/fraction#readme&amp;quot,
فایلهای README.md و package.json را در فولدر dist کپی کنیدالبته میتوان فرایند کامپایل و کپی فایلها را به صورت دستور به nodejs معرفی کرد. برای اینکار در مکینتاش و لینوکس کدهای زیر را به فایل package.json اضافه میکنیم:&amp;quotscripts&amp;quot: {
    &amp;quotclean&amp;quot: &amp;quotrm -rf dist&amp;quot,
    &amp;quotbuild&amp;quot: &amp;quotnpm run clean &amp;&amp; tsc &amp;&amp; cp package.json README.md ./dist&amp;quot 
} فایل package.json ما در نهایت به این تبدیل شد :{
   &amp;quotname&amp;quot: &amp;quot@abasb75/fraction&amp;quot,
    &amp;quotversion&amp;quot: &amp;quot0.0.1&amp;quot,
    &amp;quotdescription&amp;quot: &amp;quotformula computer&amp;quot,
    &amp;quotmain&amp;quot: &amp;quotindex.js&amp;quot,
    &amp;quotscripts&amp;quot: {
      &amp;quottest&amp;quot: &amp;quotecho \&amp;quotError: no test specified\&amp;quot &amp;&amp; exit 1&amp;quot
    },
    &amp;quotkeywords&amp;quot: [
      &amp;quotreact fraction&amp;quot,
     &amp;quotfraction&amp;quot,
      &amp;quotfractions&amp;quot,
      &amp;quotmath&amp;quot,
      &amp;quotcompute&amp;quot,
      &amp;quot&amp;quot
    ],
  &amp;quotauthor&amp;quot: &amp;quotAbbas Bagheri &lt;abasbagheria@gmail.com&gt;&amp;quot,
  &amp;quotlicense&amp;quot: &amp;quotISC&amp;quot,
  &amp;quotrepository&amp;quot: {
    &amp;quottype&amp;quot: &amp;quotgit&amp;quot,
    &amp;quoturl&amp;quot: &amp;quotgit+https://github.com/abasb75/fraction.git&amp;quot
  },
  &amp;quotdependencies&amp;quot: {
    &amp;quotequation-parser&amp;quot: &amp;quot^1.0.0&amp;quot
  },
  &amp;quotdevDependencies&amp;quot: {
    &amp;quot@types/node&amp;quot: &amp;quot^18.11.18&amp;quot,
    &amp;quot@types/react&amp;quot: &amp;quot^18.0.26&amp;quot,
    &amp;quottypescript&amp;quot: &amp;quot^4.9.4&amp;quot
  }
} فایلهایی که تا الآن داریم:fraction/
-- package.json
-- package-lock.json
-- .gitignore
-- src/
-- -- index.ts
-- -- src/
-- -- -- parse.ts
-- -- -- renderTree.ts
-- -- -- compute.ts
-- -- -- parsedFraction.ts
-- dist/
-- -- index.js
-- -- index.d.ts
-- -- package.json
-- -- README.md
-- -- src/
-- -- -- parse.js
- -- -- renderTree.js
-- -- -- compute.js
-- -- -- parsedFraction.js
-- -- -- parse.d.ts
-- -- -- renderTree.d.ts
-- -- -- compute.d.ts
-- -- -- parsedFraction.d.ts
-- node_modules/
-- -- * فایل gitignore. را میسازیم و مواردی که نمیخواهیم در گیت آپلود شود به آن اضافه میکنیم:node_modulesپروژه را در گیتهاب آپلود میکنیم:برای انتشار پروژه در npm اگر در این سایت حساب نداریم به این سایت مراجعه میکنیم و حساب کاربری جدیدی میسازیم. سپس وارد ترمینال م شده و در npm لاگین میکنیم.npm login
npm whoamiپس از وارد پوشه dist شده و دستور انتشار را وارد میکنیم.cd dist
npm publish --access publicبرای استقاده از این کتابخانه در سایر پروژه های جاوااسکریپتی فقط کافیست دستور زیر را وارد کنیم.npm i @abasb75/fractionآدرس پروژه در گیتهابآدرس پروژه در npmjsخواندن مقاله در وب سایت من</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Sun, 01 Jan 2023 15:58:56 +0330</pubDate>
            </item>
                    <item>
                <title>اتصال به پوشه های اشتراک گذاری شده ویندوز در لینوکس</title>
                <link>https://virgool.io/@abasb75/%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D8%A8%D9%87-%D9%BE%D9%88%D8%B4%D9%87-%D9%87%D8%A7%DB%8C-%D8%A7%D8%B4%D8%AA%D8%B1%D8%A7%DA%A9-%DA%AF%D8%B0%D8%A7%D8%B1%DB%8C-%D8%B4%D8%AF%D9%87-%D9%88%DB%8C%D9%86%D8%AF%D9%88%D8%B2-%D8%AF%D8%B1-%D9%84%DB%8C%D9%86%D9%88%DA%A9%D8%B3-he7uwfcops9n</link>
                <description>در این پست نحوه اتصال به پوشه های به اشتراک گذاشته شده را در لینوکس ابونتو بررسی میکنیم.ابتدا وارد ترمینال میشویم و cifs را نصب میکنیم :sudo apt update
sudo apt install cifs-utilsما در ویندوز یک پوشه بنام shared_folder را به اشتراک گذاشتیم. و پوشه از طریق ادرس زیر در دسترس است.//192.168.142.1/shared_folder میخواهیم در ابونتو به این فولدر دسترسی پیدا کنیم. برای این کار ابتدا یک پوشه در لینوکس می سازیم :mkdir /mnt/shared_folderشما این پوشه را با هر نامی که دوست داشتید میتوانید بسازید ولی من برای راحتی کار نام همان پوشه ویندوز را انتخاب کردم.فرض کنید ip ویندوزی ما 192.168.142.1 است. و ما در لینوکس به این ip متصل هستیم. برای تست اتصال میتوانیم از دستور زیر استفاده کنیم :ping 192.168.142.1پس از اطمینان از اتصال با دستور زیر پوشه shared_folder را در لینوکس mount میکنیم :mount.cifs //192.168.142.1/shared_folder /mnt/shared_folder -o در صورتی که برای اتصال به پوشه اشتراک گذاری شده نیاز به رمز عبور و نام کاربری باشد :mount.cifs //192.168.142.1/shared_folder /mnt/shared_folder -o user=shared_user,password=shared_passwordیا :sudo mount -t cifs -o username=shared_folder,password=shared_password,uid=1000,gid=1000 //192.168.142.1/shared_folder /mnt/shared_folder برای بدست آوردن uid و gid دستور زیر را در ترمینال وارد میکنیم :idبرای اطمینان از mount شدن پوشه دستورات زیر را اجرا میکنیم :cd /mnt/shared_folder
lsاکنون باید محتویات پوشه ویندوزی را در لینوکس مشاهده کنیم. اما همچنان مشکلی وجود دارد. موقعی که سیستم لینوکسی ری استارت شود لازم است دوباره دستور mount اجرا شود. برای اینکه این کار بصورت خودکار انجام شود باید مراحل زیر را دنبال کنیم.ابتدا وارد فولد etc می شویم :cd /etcسپس باید فایل fstab موجود در این فولدر را ویرایش کنیم :sudo nano fstabکد زیر را به این فایل اضافه میکنیم. قاعدتا باید ip ، نام پوشه ها، نام کاربری و سایر اطلاعات مربوط به خود را جایگزین کنیم ://192.168.142.1/shared_folder /mnt/shared_folder cifs username=shared_user,password=shared_password,gid=1000,uid=1000 0 0سپس برای اینکه مطمئین شویم کارها بدون مشکل پیش رفته است دستور زیر را اجرا میکنیم :sudo mount -aاکنون با هر بار روشن دستگاه لینوکسی باید اتصال به فولدر به اشتراک گذاری شده به طور اتوماتیک انجام شود. امیدوارم این پست مفید بوده باشه.مشاهده این پست در وب سایت من</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Thu, 01 Sep 2022 07:15:43 +0430</pubDate>
            </item>
                    <item>
                <title>راهکارهای ساده برای امنیت وب سایت php</title>
                <link>https://virgool.io/@abasb75/%D8%B1%D8%A7%D9%87%DA%A9%D8%A7%D8%B1%D9%87%D8%A7%DB%8C-%D8%B3%D8%A7%D8%AF%D9%87-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A7%D9%85%D9%86%DB%8C%D8%AA-%D9%88%D8%A8-%D8%B3%D8%A7%DB%8C%D8%AA-php-xrpexfe9gkxy</link>
                <description>در این پست به بررسی چند راه کار ساده برای افزایش امنیت وب سایت می پردازیم.چک کردن ورودی کاربر به هیچ وجه نباید به ورودی که از طرف کاربر می آید اطمینان کرد. چه ورودی یک input ساده باشد چه یک درخواست ajax و حتی فایلها! هر داده ای که از کاربر گرفته میشود قبل از نمایش یا انجام کاری در دیتابیس باید چک شود. مثلا داده ورودی یک شماره تلفن است. شماره تلفن چیزی غیر از عدد نیست. مثلا با کد زیر چک میکنیم که واقعا شماره تلفن است یا ورودی دیگر :$phone = htmlspecialchars( $_POST[&#039;phone&#039;]);
if(preg_match(&#039;#^09[0-9]{9}$#&#039; , $phone)){
       ... do something
}قبل از نمایش اطلاعات وارد شده توسط کاربر یا استفاده از ورودی کاربر مقدار وارد شده را چک کنید که دارای کدهای جاوااسکریپت و html نباشد. $phone = htmlspecialchars( $_POST[&#039;phone&#039;]);دقت کنید، از این تابع صرفا موقعی استفاده شود که قرار است چیزی در مرورگر نمایش داده شود، و برای ذخیره‌سازی نباید از این تابع استفاده کرد.استفاده از MySQL Prepared Statementsبرای وارد کردن اطلاعات و یا کار با اطلاعات در دیتابیس میتوانید از قابلیت prepared statement در php استفاده کنید.$email = $_POST[&#039;email&#039;];
$phone= $_POST[&#039;phone&#039;];
$mysqli = new mysqli($host,$user,$pass,$db);
$sql = &amp;quotSELECT id, title FROM `phone`=? and `phone`=?&amp;quot
$stmt = $mysqli-&gt;prepare($sql);
$stmt-&gt;bind_param(&amp;quotss&amp;quot,$email,$phone);
$stmt-&gt;execute();
$result = $stmt-&gt;get_result();
while($res = $result-&gt;fetch_assoc){
...
}استفاده از این روش تا حد زیادی از sql injection جلوگیری میکند.استفاده از متد پست در ارسال فرمهاروش خیلی ساده ای که میتونه کار خرابکارها را یک کمی سختتر کند. اما به معنی ایمن بودن و عدم نیاز به چک کردن داده های ورودی از این روش نیست.غیرفعال کردن حالت autocomplete در ورودیهای حساس&lt;input type=&#039;password&#039; id=&#039;email&#039; name=&#039;password&#039; autocomplete=&#039;off&#039;&gt;این برای مواقعی میتواند مفید باشد که فرد نفوذگر از طریق ریموت یا مستقیم به دسک تاپ کاربر بتواند متصل شود و بتواند autocomplete های کاربر را مشاهده کند. عدم رمزنگاری یا شکستن رمز در سمت کاربر یا frontendرمزها یا داده های حساس را در سمت سرور رمزنگاری کنید و هیچ گاه این کار در سمت کاربر نباید رخ دهد. برای امنیت ارسال اطلاعات میتوانید از ssl استفاده کنید. یا اگر نیاز به ذخیره داده در سمت کاربر دارید میتوانید از رمزنگاری متفاوت از سمت سرور استفاده کرد.رمزنگاری داده های حساس روی دیتابیسرمزعبور، ایمیل، شماره تلفن، اطلاعات مالی ، داده های شخصی و ... مواردی هستند که  نباید مستقیما و بدون رمزنگاری در دیتابیس ذخیره شوند تا اگر دیتابیس نشت پیدا کرد، با این کار جلو استفاده هکر از اطلاعات کاربران گرفته خواهد شد. به دلایلی روشهای عمومی کدگذاری امن نیستند.عدم ارجاع مستقیم کاربر به صفحاتسعی کنید تا حد امکان کاربر نتواند بطور مستقیم به فایلها دسترسی داشته باشد. مثلا شما تعدادی صفحه دارید برای مثال صفحات زیر :index.php
single.php
ontact.php
tele.phpهیچگاه به شیوه زیر به فایلها ارجاع ندهید.https://exam.ir/router.php?page=tele.phpو در کدهای php :$page = $_GET[&#039;page&#039;];
include $page;این شیوه بسیار غلط و نفوذپذیر هست. هرگز از این شیوه برای ارجاع کاربران به فایلها استفاده نشود. روش جایگزین استفاده از htaccess و سوئیچ برای دسترسی به صفحات است. به طور مثال : https://exam.ir/page/teleدر فایل htaccess : RewriteRule page$ router.php?page=index
RewriteRule page/(.+)$ router.php?page=$1در فایل php :$page = $_GET[&#039;page&#039;];
if($page == &#039;index&#039;){
inclue &#039;index.php&#039;;
}elseif($page == &#039;tele&#039;){
 inclue &#039;tele.php&#039;;
}
...یا روش دیگر ارسال همه درخواستها به فایلی مانند index.php است.عدم ارجاع مستقیم کاربران به فایلهاهیچگاه و به هیچ وجه کاربران را مستقیم به فایلها ارجاع ندهید. یا بر اساس داده ورودی از کاربر فایلی را بارگیری نکنید.$file = $_POST[&#039;file&#039;];
fopen&#40;$file&#41;;این یک روش غلط برای بارگیری فایل از طرف کاربر میباشد. مثلا کاربر میتوان مقدار http://evil.us/evil.jpg را به عنوان ورودی ارسال کند. یا یکی از فایلهای حساس شما را درخواست کند. سعی کنید فایلها را جوری بارگیری کنید که هیچ گاه فایل غیر مرتبط بارگیری نشود.همچنین در فایل php.ini میتوانید قابلیت بارگیری فایل از طریق url را غیر فعال کنید.allow_url_fopen = Offیا از طریق ایجاد تغییر در فایل httpd.conf :php_admin_flag allow_url_fopen Offمطمئن شوید کاربر به پوشه ها دسترسی ندارد.کاربران نیازی به مشاهده پوشه ها و دایرکتوریهای شما ندارند پس مطمئن شوید هیچگاه کاربران به دایرکتوریها دسترسی ندارند و نمیتوانند لیست فایلهای موجود در دایرکتوریها را مشاهده کنند.تعویض دوره ای رمزها و اطلاعات حساس سرور :رمز ، نام کاربری و ... مرتبط با دیتابیس ، پنل ادمین و ... به صورت دوره ای تعویض شوند تا اگر کسی قبلا دسترسی به سیستم و سرور ما پیدا کرده است، مجددا دسترسی او قطع شود.تعیین محدودیت برای کاربر دیتابیسکاربر دیتابیس که عموما نیازی به مواردی مثل DROP ، DELETE و بسیاری از قابلیتهای دیگر دیتابیس ندارد. ضمن اینکه بصورت دوره‌ای رمز کاربر دیتابیس را تعویض میکنیم، دسترسی هایی هم که به آن نیاز ندارد از او سلب میکنیم. در محیط کاربری سی پنل براحتی این کار امکان پذیر است.استفاده از چند کاربر دیتابیس با سطح دسترسی مختلفروشی که تا حدی از خرابکاری های مرتبط با sql میتواند جلوگیری کند. مثلا برای کاربر عادی و کاربر ادمین از دو کاربر دیتابیس متفاوت با سطح دسترسی متفاوت میتوان استفاده کرد.استفاده از فایل robots.txtاین فایل جلوی خرابکاری هکر و ربات مخرب را نمیگیرد. اما مسلما دوست ندارید که پنل ادمین یا کاربران در سرویسهایی مثل گوگل برای جستجوگر قابل نمایش باشد.غیر فعال کردن توابع پی اچ پی حساسدر بسیاری از موارد ما نیازی به بسیاری از توابع حساس php نداریم. میتوان در فایل php.ini این توابع را غیر فعال کرد.disable_functions = exec, passthru, shell_exec, system, proc_open, popen, curl_exec, curl_multi_exec, parse_ini_file, show_sourceایجاد محدودیت در آپلود فایلاگر در برنامه شما فایلی آپلود نمیشود. پس در فایل php.ini  این قابلیت را از سرور خود حذف کنید :file_uploads = offدر صورتی که به آپلود فایل نیاز دارید میتوانید حداکثر حجمی برای فایل ورودی در نظر بگیرید.upload_max_filesize = 10M
post_max_size = 16Mبکاپ گیری دوره ایاین کار باعث میشود که در صورت اشکال یا تخریب اطلاعات شما بتوانید در مدت زمان کمی سرویس خود را مجددا فعال کنید.استفاده از input مخفی در فرمهابرای جلوگیری از حملات CSRF یکی از کارهایی که علاوه بر ارسال فرمها از طریق درخواست POST میتوانیم انجام دهیم استفاده از input مخفی با مقدار از پیش تعیین شده هست:$random = create_random_string();
$_SESSION[&#039;random&#039;] = $random;
echo &#039;&lt;input type=&amp;quothidden&amp;quot value=&amp;quot&#039;. $random .&#039;&amp;quot&gt;&#039;;و در موقع دریافت فرم :$random = $_POST[&#039;random&#039;];
if($random == $_SESSION[&#039;random&#039;]){
...
}استفاده از فریمورکها : استفاده از فریمورکهای مختلف مزیتی که دارد این است که بسیاری از مشکلات امنیتی کاهش پیدا میکند اما وقتی از یک فریمورکی مانند لاراول استفاده می‌کنید باید دقت کنید چون ممکن است برخی مشکلات امنیتی همچنان وجود داشته باشند و برنامه نویس از آنها غافل باشد.عدم ذخیره داده های حساس روی کوکی :نکته اول اینکه هیچ گاه نباید داده های حساس مانند id شماره تلفن ، ایمیل یا موارد مرتبط را روی کوکی ذخیره کنید. همچنین هیچگاه داده ها را بصورت خام و کدگذاری نشده روی کوکی ذخیره نکنید. همچینین با قابلیتهای کوکی مانند httponly و تاریخ انقضا بخوبی آشنا باشید. تا حد زیادی میتواند جلو سو استفاده افراد خرابکار را بگیرید.دقت کنید برای کوکی چه داده های حساس و چه داده های غیر حساس به هیچ وجه بصورت خام ذخیره انجام ندهید.امنیت سشن :برای امنیت در بخش سشن راه های متعددی وجود دارد. راه کار اول : ذخیره داده ها روی سشن بصورت رمزگذاری شدهراه کار دوم : وابسته کردن سشن ها به ip کاربر$_SESSION[&#039;fingerprint&#039;] = md5($_SERVER[&#039;HTTP_USER_AGENT&#039;] . PHRASE . $_SERVER[&#039;REMOTE_ADDR&#039;]);راه کار سوم : بازسازی sessoon id به صورت مداومsession_start();
session_regenerate_id();راه کار چهارم : جلوگیری از دسترسی اسکریپتها مانند جاوااسکریپت به سشنها ini_set(&#039;session.cookie_httponly&#039;, true);راه کار پنجم : ذخیره اطلاعات سشن در دیتابیساین راه حل وجود دارد اما بخاطر افزایش پردازش و کند شدن سرعت اجرا سایت و خطرات امنیتی دیگر بهتر است از آن خودداری شود.بهتر است چهار راه کار اول همزمان با هم مورد استفاده قرار گیرند.عدم دسترسی کاربر به هر شی موجود در سرورمطمئن شوید کاربر هیچ گاه بطور مستقیم هیچ دسترسی به هیچ کدام از اشیا سرور را ندارد. چه فایل چه اسکریپت و یا هر چیز دیگر ...مثلا هیچ گاه فایلی مستقیما و بدون بررسی وارد نشود. یا هیچ ورودی از سمت کاربر بدون چک کردن و صحت سنجی مستقیما مورد استفاده قرار نگیرد و موارد مشابه!توابع و فایلهای بدون نیاز به اعتبار سنجی :کدنویسی شما باید جوری باشد که هیچ کدام از فایلها اسکریپت و یا توابع موجود بدون اعتبارسنجی از کاربر به تنهایی قابلیت انجام عمل حساسی را مثلا روی دیتابیس را نداشته باشد. مثلا یک تابع دارید که اطلاعات کاربر را از دیتابیس میگیرد. و به این شکل مورد استفاده قرار میگیرد.if(user_is_valid){
   load_data(user_id);
}فرض کنید این تابع load_data خارج از دستور چک if قرار بگیرد. میتواند مشکلات امنیتی را بوجود بیاورد.فرض کنید بجای تابع load_data از یک فایل php بنام load_data.php استفاده کنیم. در این صورت برای فراخوانی اطلاعات کاربر دستور زیر را بکار مگیریم :if(user_is_valid){
   load_data.php
}در صورتی که شخص نفوذگر توانست از نقطه ضعف اگاه شد با بارگیری فایل load_data.php اطلاعات کاربر را بارگیری میکند.بخاطر زیاد شدن مطالب فعلا به این حد بسنده میکنیم. ولی فراموش نکنید هیچ سرور یا سایتی غیرقابل نفوذ نیست. فقط ممکنه کسی هنوز راه نفوذ بهش رو پیدا نکرده باشه.موارد بیشتر در وب سایت من</description>
                <category>عباس باقری</category>
                <author>عباس باقری</author>
                <pubDate>Sat, 27 Aug 2022 06:13:22 +0430</pubDate>
            </item>
            </channel>
</rss>