<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Mahdi fakhr</title>
        <link>https://virgool.io/feed/@mahdifakhr</link>
        <description>UI DESIGNER AT XENIAC</description>
        <language>fa</language>
        <pubDate>2026-06-16 16:54:52</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/35872/avatar/w7FotR.png?height=120&amp;width=120</url>
            <title>Mahdi fakhr</title>
            <link>https://virgool.io/@mahdifakhr</link>
        </image>

                    <item>
                <title>پیاده سازی reactivity فریمورک ویو در جاواسکریپت</title>
                <link>https://virgool.io/Xeniac/%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-reactivity-%D9%81%D8%B1%DB%8C%D9%85%D9%88%D8%B1%DA%A9-%D9%88%DB%8C%D9%88-%D8%AF%D8%B1-%D8%AC%D8%A7%D9%88%D8%A7%D8%B3%DA%A9%D8%B1%DB%8C%D9%BE%D8%AA-lyhw73sypply</link>
                <description>توی این آموزش قصد داریم تا با هم از صفر یک سیستم دیتا reactivity رو در جاواسکریپت پیاده سازی کنیم.چرا reactivity?براتون یه مثال میزنم، کد زیر رو در نظر بگیریدconst price = 100
const fee = 10

const total = () =&gt; {
    return price + fee
}

render(total()) // 110

// new value
price = 300
render(total()) // 310همونطوری که میبینید اگر ما بخوایم بدون استفاده از reactivity یک مقدار رو آپدیت کنیم، باید این کار رو به صورت دستی انجام بدیم و در هر مرحله مثل کد بالا، متد های خودمون رو صدا بزنیماما reactivity به ما کمک میکنه این پروسه رو اتوماتیک کنیم و اجازه بدیم روند برنامه صدا زدن متد ها و آپدیت داده های ما رو به عهده بگیره.خروجی این آموزش به این صورت خواهد بودخروجی نهاییری اکتیویتی در ویو جی استو این آموزش سعی میکنیم تا سیستم شبیه به فریم ورک vue رو پیاده سازی کنیم ( نسخه 2 )ما میخوایم ورودی شبیه به کد زیر داشته باشیم ( درست مثل ویو )const App = Observable({
    data() {
        return {
            price: 300,
            fee: 10,
        }
    },
    computed: {
        totalSpend() {
            return this.price + this.fee
        },
    },

    methods: {
        sayHello() {
            console.log(&amp;quothello world&amp;quot)
        },
    },
})خب برای شروع متد Observable رو تعریف میکنیمconst Observable = (input) =&gt; {
    const deps = {}
    let effect = null
    const _app = {
        ...input.data(),
    }
}از _deps برای نگه داری وابستگی های متغیر ها به متد های computed استفاده میشهاز effect برای نگهداری computed فعال و در حال اجرا استفاده میشهاز _app به عنوان ورودی برنامه که به صورت دیفالت متغیر های داخل آبجکت Data رو داخلش میریزیم، استفاده میشهبرای ری اکتیو کردن داده ها، در جاوااسکریپت API وجود داره به اسمProxy. این API به شما اجازه میده درخواست های ارسالی به آبجکت رو شخصی سازی کنید. ما از این API برای خوندن و نوشتن داده توی متغیر هامون استفاده میکنیمما به 2 تا Proxy نیاز خواهیم داشت. یکی برای گوش کردن به تغییرات دیتا در ابجکت _app و دیگری برای گوش کردن به تغییرات computed هاابتدا با دیتا شروع میکنیم:const Observable = (input) =&gt; {
    const deps = {}
    let effect = null
    const _app = {
        ...input.data(),
    }

    // base proxy
    const _appProxy = new Proxy(_app, {
        get(target, key) {
            return target[key]
        },
        set(target, key, value) {
            target[key] = value
            return true
        },
    })

    return _appProxy
}در مرحله بعد Method هامون رو به آبجکت _app اضافه میکنیم// init methods
_app.methods = {}
Object.keys(input.methods).forEach((key) =&gt; {
    _app.methods[key] = input.methods[key].bind(_app)
})تو اینجا از input.methods[key].bind(_app) استفاده شده. برای زمانی هست که وقتی از this استفاده کردیم رفرنس رو به ابجکت _app از دست ندیمتو مرحله بعد computed هامون رو اضافه میکنیم// init computed -- convert to getters
Object.keys(input.computed).forEach((key) =&gt; {
    Object.defineProperty(_appProxy, key, {
        get() {
            const res = effect.call(_appProxy)
            return res
        },
    })
})توی اینجا به جای Proxy از DefineProperty علت خاصی نداره فقط میخواستم نشون بدم از هر دو روش میشه این کار رو انجام داد. این کد به ما کمک میکنه، زمانی که کاربر یکی از متد های computed رو صدا زد، ما به اون فاکنشن دسترسی داشته باشیم اضافه کردن reactivity به computed هاما نیاز به مکانیزمی داریم تا بتونیم تشخیص بدیم که computed های ما چه متغیر هایی رو اجرا میکنن و بعد از تشخیص، این computed ها رو به عنوان dependency به متغیر هامون اضافه میکنیم تا در صورت تغییر مقدار هر کدوم از متغیر ها، computed هامون رو دوباره اجرا بکنیمقطعه کد زیر رو نظر بگیریدconst App = Observable({
    data() {
        return {
            price: 300,
        }
    },
    computed: {
        getPrice() {
            return this.price
        },
    },
})

App.getPrice // 300اگر کد بالا رو اجرا بکنیم، Getter ای که در داخل فاکنشن Obseravable تعریف کردیم اجرا میشه. ما میدونیم که توی جاوااسکریپت در هر لحظه فقط یک دستور میتونه اجرا بشه پس: وقتی که getPrice اجرا میشه، ما باید اون رو به عنوان effect درحال اجرا در نظر بگیریمپس کد بخش computed ما به این صورت تغییر میکنه:// init computed -- convert computeds to getters
Object.keys(input.computed).forEach((key) =&gt; {
    Object.defineProperty(_appProxy, key, {
        get() {
            effect = input.computed[key]
            const res = effect.call(_appProxy)
            effect = null
            return res
        },
    })
})قبل از اجرا شدن متد computed ما، اون رو در درون متغیر effect قرار میدیم و بعد از اتمام اجرا، اون رو به null تغییر میدیمبه این نکته توجه کنید وقتی که computed ما اجرا میشه effect.call(_appProxy) چون در داخل این متد از this.price استفاده کردیم، این باعث میشه Proxy ما برای دیتا اجرا بشهاز اونجایی که ما effect و متد فعال فعلی رو ذخیره داریم، میتونیم این متد رو به عنوان dependency به متغیر ها اضافه کنیمپس بخش get ما به این صورت تغییر میکنه// base proxy
const _appProxy = new Proxy(_app, {
    get(target, key) {
        if (!deps[key]) {
            deps[key] = new Set()
        }
        effect &amp;&amp; deps[key].add(effect)
        return target[key]
    },
    // --------------
})ابتدا چک میکنم تا ببینیم متغیر فعلی دارای در داخل ابجکت deps وجود داره یا نه، اگر وجود نداشت اون رو ایجاد میکنیم. علت استفاده از new Set() به این خاطر هست که ما نمیخوایم یک فاکنشن چند بار اجرا بشهبعد از ذخیره deps ها ما همچین ساختاری رو خواهیم داشتconsole.log(deps)
/*  {
        price:  getPrice() { return this.price; }
    }
*/حالا اگر هرکدوم از این دیتا های ما تغییر بکنند، ما dependency های اون را دونه به دونه صدا میزنیمبرای اینکه متوجه بشیم آیا تغییر کردنند یا نه از set در proxy استفاده میکنیم کد ما به این صورت تغییر میکنه:// base proxy
const _appProxy = new Proxy(_app, {
    // ----------------
    set(target, key, value) {
        target[key] = value
        const _deps = deps[key]
        if (_deps) {
            _deps.forEach((effect) =&gt; {
                return effect.call(_appProxy)
            })
        }
        return true
    },
})همونطور که میبینیدconst _deps = deps[key]
if (_deps) {
    _deps.forEach((effect) =&gt; {
        return effect.call(_appProxy)
    })
}همه ی deps ها رو اجرا میکنیم تا مطلع بشن که دیتای ما تغییر کردهاگر تا اینجا دنبال کرده باشید کد ما به این صورت خواهد بودconst Observable = (input) =&gt; {
    const deps = {}
    let effect = null
    const _app = {
        ...input.data(),
    }

    // base proxy
    const _appProxy = new Proxy(_app, {
        get(target, key) {
            if (!deps[key]) {
                deps[key] = new Set()
            }
            effect &amp;&amp; deps[key].add(effect)
            return target[key]
        },
        set(target, key, value) {
            target[key] = value
            const _deps = deps[key]
            if (_deps) {
                _deps.forEach((effect) =&gt; {
                    return effect.call(_appProxy)
                })
            }
            return true
        },
    })

    // computed proxy
    // init methods
    _app.methods = {}
    Object.keys(input.methods).forEach((key) =&gt; {
        _app.methods[key] = input.methods[key].bind(_app)
    })

    // init computed -- convert computeds to getters
    Object.keys(input.computed).forEach((key) =&gt; {
        Object.defineProperty(_appProxy, key, {
            get() {
                effect = input.computed[key]
                const res = effect.call(_appProxy)
                effect = null
                return res
            },
        })
    })
    return _appProxy
}خب کار ما اینجا تموم میشه. برای اینکه ببینیم کدی که زدیم کار میکنه یا نه یه پروژه کوچیک با هم انجام میدیمیک ابجکت از کلاس Observable مون میسازیم با ورودی های زیر:const App = Observable({
    data() {
        return {
            price: 300,
            fee: 10,
        }
    },
    computed: {
        totalSpend() {
            return this.price + this.fee
        },
    },

    methods: {
        price_up() {
            this.price++
        },
        price_down() {
            this.price--
        },

        fee_up() {
            this.fee++
        },
        fee_down() {
            this.fee--
        },
    },
})یک فاکنشن تعریف میکنیم به اسم render وظیفه این فاکنشن اضافه کردن کد html یک جدول به صفحه ماست (مثل گیف پروژه نهایی در بتدای پروژه)const render = () =&gt; {
    const appElem = document.getElementById(&amp;quotapp&amp;quot)
    appElem = `
    &lt;table class=&amp;quottable&amp;quot&gt;
            &lt;tbody&gt;
                &lt;tr&gt;
                    &lt;td class=&amp;quotbold&amp;quot&gt;&lt;p&gt;product price&lt;/p&gt;&lt;/td&gt;
                    &lt;td class=&amp;quotprice bold&amp;quot &gt;${App.price}$&lt;/td&gt;
                    &lt;td&gt;
                        &lt;div class=&amp;quotbtn-group-vertical&amp;quot role=&amp;quotgroup&amp;quot aria-label=&amp;quotFirst group&amp;quot&gt;
                            &lt;button type=&amp;quotbutton&amp;quot class=&amp;quotbtn btn-secondary&amp;quot @click=&amp;quotprice_up&amp;quot&gt;+&lt;/button&gt;
                            &lt;button type=&amp;quotbutton&amp;quot class=&amp;quotbtn btn-secondary&amp;quot @click=&amp;quotprice_down&amp;quot&gt;-&lt;/button&gt;
                        &lt;/div&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td class=&amp;quotbold&amp;quot&gt;Fee&lt;/td&gt;
                    &lt;td class=&amp;quotprice bold&amp;quot&gt;${App.fee}%&lt;/td&gt;
                    &lt;td&gt;
                        &lt;div class=&amp;quotbtn-group-vertical&amp;quot role=&amp;quotgroup&amp;quot aria-label=&amp;quotFirst group&amp;quot&gt;
                            &lt;button type=&amp;quotbutton&amp;quot class=&amp;quotbtn btn-secondary&amp;quot @click=&amp;quotfee_up&amp;quot&gt;+&lt;/button&gt;
                            &lt;button type=&amp;quotbutton&amp;quot class=&amp;quotbtn btn-secondary&amp;quot @click=&amp;quotfee_down&amp;quot&gt;-&lt;/button&gt;
                        &lt;/div&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr&gt;
                    &lt;td class=&amp;quotbold&amp;quot&gt;Total&lt;/td&gt;
                    &lt;td class=&amp;quotprice bold&amp;quot&gt;${App.totalSpend}$&lt;/td&gt;
                    &lt;td&gt;&lt;/td&gt;
                &lt;/tr&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
    `
}

render()همونطور که میبینید در داخل جدول از String Template ها استفاده شده و داده هامون رو قراره نشون بده&lt;td class=&amp;quotprice bold&amp;quot&gt;${App.fee}%&lt;/td&gt;همچنین مثل فریمورک vue از custom attribute ها استفاده کردیم تا بتونیم به click event گوش کنیم&lt;button @click=&amp;quotfee_up&amp;quot&gt;+&lt;/button&gt; &lt;button @click=&amp;quotfee_down&amp;quot&gt;-&lt;/button&gt;خب حالا به اونت کلیک روی صفحه گوش میدیم و برسی میکنیم که کاربر کجا کلیک کرده اگر روی المنت هایی که click attribute دارن کلیک کرده باشه، فاکنشن ها رو اجرا میکنیمdocument.addEventListener(&amp;quotclick&amp;quot, (e) =&gt; {
    const customClickAttr = e.target.attributes
    // get click attribute from list of attributes
    const clickAttr = customClickAttr.getNamedItem(&amp;quot@click&amp;quot)
    if (clickAttr) {
        const methodName = clickAttr.value
        App.methods[methodName]()
        render()
    }
})بعد از اینکه دیتامون رو اپدیت کردیم نیاز هست تا صفحه رو هم آپدیت کنیم. به خاطر همین بعد از اجرا متد هامون، متد render رو دوباره صدا میزنیمخب پروژه ما اینجا تموم میشه، برای تمرین اگر دوست داشتید سعی کنید مثل Vue قابلیت Watcher و گوش کردن به همه تغییرات متغیر ها رو پیاده کنید.اگر این آموزش براتون جالب بود میتونید کد های این پروژه روی گیت هاب مشاهده کنید[سورس کد پروژه]همچنین میتونید این مطلب رو در بلاگ شخصی من بخونید https://mediv.vercel.app/blog/data-reactivity-in-javascript HAPPY CODING</description>
                <category>Mahdi fakhr</category>
                <author>Mahdi fakhr</author>
                <pubDate>Thu, 17 Mar 2022 14:14:48 +0330</pubDate>
            </item>
                    <item>
                <title>ساخت modal های داینامیک در vue راحت تر از همیشه!</title>
                <link>https://virgool.io/Xeniac/%D8%B3%D8%A7%D8%AE%D8%AA-modal-%D9%87%D8%A7%DB%8C-%D8%AF%D8%A7%DB%8C%D9%86%D8%A7%D9%85%DB%8C%DA%A9-%D8%AF%D8%B1-vue-%D8%B1%D8%A7%D8%AD%D8%AA-%D8%AA%D8%B1-%D8%A7%D8%B2-%D9%87%D9%85%DB%8C%D8%B4%D9%87-n0ygaqmloqwz</link>
                <description>سلام. امیدوارم حالتون خوب باشههمه ی ما، توی پروژه های مختلف مجبور شدیم از modal استفاده کنیم. این modal ها معمولا توی یک تمپلیت و قالب خاصی طراحی شدن و دست برنامه نویس برای تغییرات و شخصی سازی کردن اون ها، تا حدودی بسته میشه.xmodal v 0.1.0 ?ما برای رفع این مشکل دنبال راهی بودیم که بتونیم کامپوننت های خودمون رو به صورت داینامیک و بدون محدودیت با توجه دیزاین متناسب با وبسایت خودمون، به عنوان modal استفاده کنیم. این ایده باعث شد که ما پکیج xmodal رو طراحی و پیاده سازی کنیم!این پکیج بهتون کمک میکنه تا از شر modal های زشت ? از پیش طراحی شده خلاص بشید و کامپوننت خودتون رو رو به عنوان modal، در پروژتون استفاده کنید.? درواقع xmodal یک چارچوب و wrapper در اطراف کامپوننت های شماست که اون رو به صورت یک modal توی صفحه مورد نظر، بارگذاری میکنه.به همین راحتی شما میتونید modal های اختصاصی خودتون رو داشته باشید.مخزن گیتهاب / پیش نمایشxmodal demoاستفاده از این پکیج خیلی راحته، تنها کاری که باید بکنید اینه که یک کامپوننت طراحی بکنید و اون رو به پکیج معرفیش کنید. ( رفرنس بدید ) بقیه کارا به صورت اتوماتیک انجام میشه و حالا شما میتونید از کامپوننت مورد نظرتون به صورت یه modal استفاده کنید. ( مثل گیف بالا )امکانات این پکیج:قابلیت اضافه کردن کامپوننت ها به صورت داینامیک ?قابلیت کنترل و تغییر و اضافه کردن animation برای کامپوننت مورد نظر ?️قابلیت اضافه کردن تایمر برای کنترل کردن زمان بسته شدن کامپوننت ⏰قابلیت قفل کردن (Disable) کامپوننت و باز بسته کردن اون بر اساس شروط v-if v-else ?کنترل کردن کامپوننت و تنظیمات اون از طریق global fuctions در هرجای برنامه ?قابلیت ست کردن تنظیمات پیش فرض برای همه modal ها ?قابلیت ارسال اطلاعات به کامپوننت از طریق props ?قابلیت های شخصی سازی رنگ پس زمینه و میزان شفافیت بکگراند modal ?و...این پکیج در npm موجوده و میتونید با دستورات زیر نصبش کنیدnpm i xmodal-vue
// or
yarn add xmodal-vueبرای خوندن داکیومنت هم میتونید به مخزن گیت هاب مراجعه کنید. اینجا سرتون رو درد نمیارم. مخزن گیتهاب / پیش نمایشممنونم که وقت گذاشتید و این مطلب رو تا آخر خوندید.HAPPY CODING ?</description>
                <category>Mahdi fakhr</category>
                <author>Mahdi fakhr</author>
                <pubDate>Tue, 07 Apr 2020 09:29:53 +0430</pubDate>
            </item>
            </channel>
</rss>