整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          深入學習Vue的data、computed、watch來實現最精簡響應式系統


          者:ssh

          轉發鏈接:https://mp.weixin.qq.com/s/PNCXukFWp-DSgklRa_mARg


          者:莫夭

          轉發鏈接:https://zhuanlan.zhihu.com/p/102668383

          前言

          我日常工作都是使用React來做開發,但是我對React一直不是很滿意,特別是在推出React Hooks以后。

          前面文章小編也詳細整理了React.js和Vue.js的語法:

          一篇文章教你并列比較React.js和Vue.js的語法【實踐】

          不可否認React Hooks極大地方便了開發者,但是它又有非常多反直覺的地方,讓我難以接受。所以在很長一段時間,我都在嘗試尋找React的替代品,我嘗試過不少別的前端框架,但都有各種各樣的問題或限制。

          更多關于React和Vue 學習實踐項目相關文章,請見本篇文章底部

          在看到了Vue 3.0 Composition-API的設計,確實有眼前一亮的感覺,它既保留了React Hooks的優點,又沒有反復聲明銷毀的問題,而Vue一直都是支持JSX語法的,3.0對TypeScript的支持又非常好,所以我開始嘗試用Vue + TSX來做開發。

          Vue 3.0已經發布了alpha版本,可以通過以下命令來安裝:

          npm install vue@next --save

          簡單示例

          先來看看用Vue3.0 + TSX寫一個組件是什么什么樣子的。

          實現一個Input組件:

          import { defineComponent } from 'vue';
          
          interface InputProps {
            value: string;
            onChange: (value: string) => void;
          }
          const Input = defineComponent({
            setup(props: InputProps) {
              const handleChange = (event: KeyboardEvent) => {
                props.onChange(event.target.value);
              }
          
              return () => (
                <input value={props.value} onInput={handleChange} />
              )
            }
          })

          可以看到寫法和React非常相似,和React不同的是,一些內部方法,例如handleChange,不會在每次渲染時重復定義,而是在setup這個準備階段完成,最后返回一個“函數組件”。

          這算是解決了React Hooks非常大的一個痛點,比React Hooks那種重復聲明的方式要舒服多了。

          Vue 3.0對TS做了一些增強,不需要像以前那樣必須聲明props,而是可以通過TS類型聲明來完成。

          這里的defineComponent沒有太多實際用途,主要是為了讓TS類型提示變得友好一點。

          Babel插件

          為了能讓上面那段代碼跑起來,還需要有一個Babel插件來轉換上文中的JSX,Vue 3.0相比2.x有一些變化,不能再使用原來的vue-jsx插件。

          我們都知道JSX(TSX)實際上是語法糖,例如在React中,這樣一段代碼:

          const input = <input value="text" />

          實際上會被babel插件轉換為下面這行代碼:

          const input = React.createElement('input', { value: 'text' });
          

          Vue 3.0也提供了一個對應React.createElement的方法h。但是這個h方法又和vue 2.0以及React都有一些不同。

          例如這樣一段代碼:

          <div class={['foo', 'bar']} style={{ margin: '10px' }} id="foo" onClick={foo} />

          在vue2.0中會轉換成這樣:

          h('div', {
            class: ['foo', 'bar'],
            style: { margin: '10px' }
            attrs: { id: 'foo' },
            on: { click: foo }
          })

          可以看到vue會將傳入的屬性做一個分類,會分為class、style、attrs、on等不同部分。這樣做非常繁瑣,也不好處理。

          在vue 3.0中跟react更加相似,會轉成這樣:

          h('div', {
            class: ['foo', 'bar'],
            style: { margin: '10px' }
            id: 'foo',
            onClick: foo
          })

          基本上是傳入什么就是什么,沒有做額外的處理。

          當然和React.createElement相比也有一些區別:

          • 子節點不會作為以children這個名字在props中傳入,而是通過slots去取,這個下文會做說明。
          • 多個子節點是以數組的形式傳入,而不是像React那樣作為分開的參數

          所以只能自己動手來實現這個插件,我是在babel-plugin-transform-react-jsx的基礎上修改的,并且自動注入了h方法。

          實際使用

          在上面的工作完成以后,我們可以真正開始做開發了。

          渲染的節點

          上文說到,子節點不會像React那樣作為children這個prop傳遞,而是要通過slots去取:

          例如實現一個Button組件

          // button.tsx
          import { defineComponent } from 'vue';
          import './style.less';
          
          interface ButtonProps {
            type: 'primary' | 'dashed' | 'link'
          }
          const Button = defineComponent({
            setup(props: ButtonProps, { slots }) {
              return () => (
                <button class={'btn', `btn-${props.type}`}>
                  {slots.default()}
                </button>
              )
            }
          })
          
          export default Button;

          然后我們就可以使用它了:

          import { createApp } from 'vue';
          import Button from './button';
          
          // vue 3.0也支持函數組件
          const App = () => <Button>Click Me!</Button>
          
          createApp().mount(App, '#app');

          渲染結果


          Reactive

          配合vue 3.0提供的reactive,不需要主動通知Vue更新視圖,直接更新數據即可。

          例如一個點擊計數的組件Counter:

          import { defineComponent, reactive } from 'vue';
          
          const Counter = defineComponent({
            setup() {
              const state = reactive({ count: 0 });
              const handleClick = () => state.count++;
              return () => (
                <button onClick={handleClick}>
                  count: {state.count}
                </button>
              )
            }
          });

          渲染結果


          這個Counter組件如果用React Hooks來寫:

          import React, { useState } from 'react';
          
          const Counter = () => {
            const [count, setCount] = useState(0);
            const handleClick = () => setCount(count + 1);
            return (
              <button onClick={handleClick}>
                count: {count}
              </button>
            )
          }

          對比之下可以發現Vue 3.0的優勢:

          在React中,useState和定義handleClick的代碼會在每次渲染時都執行,而Vue定義的組件重新渲染時只會執行setup中最后返回的渲染方法,不會重復執行上面的那部分代碼。

          而且在Vue中,只需要更新對應的值即可觸發視圖更新,不需要像React那樣調用setCount。

          當然Vue的這種定義組件的方式也帶來了一些限制,setup的參數props是一個reactive對象,不要對它進行解構賦值,使用時要格外注意這一點:

          例如實現一個簡單的展示內容的組件:

          // 錯誤示例
          import { defineComponent, reactive } from 'vue';
          
          interface LabelProps {
            content: string;
          }
          const Label = defineComponent({
            setup({ content }: LabelProps) {
              return () => <span>{content}</span>
            }
          })

          這樣寫是有問題的,我們在setup的參數中直接對props做了解構賦值,寫成了{ content },這樣在后續外部更新傳入的content時,組件是不會更新的,因為破壞了props的響應機制。以后可以通過eslint之類的工具來避免這種寫法。

          正確的寫法是在返回的方法里再對props做解構賦值:

          import { defineComponent, reactive } from 'vue';
          
          interface LabelProps {
            content: string;
          }
          const Label = defineComponent({
            setup(props: LabelProps) {
              return () => {
                const { content } = props;  // 在這里對props做解構賦值
                return <span>{content}</span>;
              }
            }
          })

          生命周期方法

          在Vue 3.0中使用生命周期方法也非常簡單,直接將對應的方法import進來即可使用。

          import { defineComponent, reactive, onMounted } from 'vue';
          
          interface LabelProps {
            content: string;
          }
          const Label = defineComponent({
            setup(props: LabelProps) {
          
              onMounted(() => { console.log('mounted!'); });
          
              return () => {
                const { content } = props;
                return <span>{content}</span>;
              }
            }
          })

          vue 3.0對tree-shaking非常友好,所有API和內置組件都支持tree-shaking。

          如果你所有地方都沒有用到onMounted,支持tree-shaking的打包工具會自動將其去掉,不會打進最后的包里。

          指令和過渡效果

          Vue 3.0還提供了一系列組件和方法,來使JSX也能使用模板語法的指令和過渡效果。

          使用Transition在顯示/隱藏內容塊時做過渡動畫:

          import { defineComponent, ref, Transition } from 'vue';
          import './style.less';
          
          const App = defineComponent({
            setup() {
              const count = ref(0);
              const handleClick = () => {
                count.value ++;
              }
          
              return () => (
                <div>
                  <button onClick={handleClick}>click me!</button>
                  <Transition name="slide-fade">
                    {count.value % 2 === 0 ?
                      <h1>count: {count.value}</h1>
                    : null}
                  </Transition>
                </div>
              )
            }
          })
          


          // style.less
          .slide-fade-enter-active {
            transition: all .3s ease;
          }
          .slide-fade-leave-active {
            transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
          }
          .slide-fade-enter, .slide-fade-leave-to {
            transform: translateX(10px);
            opacity: 0;
          }


          渲染結果

          也可以通過withDirectives來使用各種指令,例如實現模板語法v-show的效果:

          import { defineComponent, ref, Transition, withDirectives, vShow } from 'vue';
          import './style.less';
          
          const App = defineComponent({
            setup() {
              const count = ref(0);
              const handleClick = () => {
                count.value ++;
              }
          
              return () => (
                <div >
                  <button onClick={handleClick}>toggle</button>
                  <Transition name="slide-fade">
                    {withDirectives(<h1>Count: {count.value}</h1>, [[
                      vShow, count.value % 2 === 0
                    ]])}
                  </Transition>
                </div>
              )
            }
          })

          這樣寫起來有點繁瑣,應該可以通過babel-jsx插件來實現下面這種寫法:

          <h1 vShow={count.value % 2 === 0}>Count: {count.value}</h1>

          優缺點

          在我看來Vue 3.0 + TSX完全可以作為React的替代,它既保留了React Hooks的優點,又避開了React Hooks的種種問題。

          但是這種用法也有一個難以忽視的問題:它沒辦法獲得Vue 3.0編譯階段的優化。

          Vue 3.0通過對模板的分析,可以做一些前期優化,而JSX語法是難以做到的。

          例如“靜態樹提升”優化:

          如下一段模板(這是模板,并非JSX):

          <template>
            <div>
              <span>static</span>
              <span>{{ dynamic }}</span>
            </div>
          </template>

          如果不做任何優化,那么編譯后得到的代碼應該是這樣的:

          render() {
            return h('div', [
              h('span', 'static'),
              h('span', this.dynamic)
            ]);
          }
          

          那么每次重新渲染時,都會執行3次h方法,雖然未必會觸發真正的DOM更新,但這也是一部分開銷。

          通過觀察,我們知道h('span', 'static')這段代碼傳入的參數始終都不會有變化,它是靜態的,而只有h('span', this.dynamic)這段才會根據dynamic的值變化。

          在Vue 3.0中,編譯器會自動分析出這種區別,對于靜態的節點,會自動提升到render方法外部,避免重復執行。

          Vue 3.0編譯后的代碼:

          const __static1 = h('span', 'static');
           
          render() {
            return h('div', [
              __static1,
              h('span', this.dynamic)
            ])     
          }

          這樣每次渲染時就只會執行兩次h。換言之,經過靜態樹提升后,Vue 3.0渲染成本將只會和動態節點的規模相關,靜態節點將會被復用。

          除了靜態樹提升,還有很多別的編譯階段的優化,這些都是JSX語法難以做到的,因為JSX語法本質上還是在寫JS,它沒有任何限制,強行提升它會破壞JS執行的上下文,所以很難做出這種優化(也許配合prepack可以做到)。

          考慮到這一點,如果你是在實現一個對性能要求較高的基礎組件庫,那模板語法仍然是首選。

          另外JSX也沒辦法做ref自動展開,使得ref和reactive在使用上沒有太大區別。

          后話

          我個人對Vue 3.0是非常滿意的,無論是對TS的支持,還是新的Composition API,如果不限制框架的話,那Vue以后肯定是我的首選。

          推薦React和Vue學習資料文章:

          《一篇文章教你并列比較React.js和Vue.js的語法【實踐】》

          面上門店收銀管理系統那么多,老板應該怎么選?一般我們在討論系統功能的時候,都在說收銀、會員儲值等等,其實,系統對門店內部的統籌也十分重要,今天我們就來看看云上鋪收銀管理系統如何幫助老板管理門店內部。

          員工提成

          員工提成自動生成報表,不管是售卡,服務,商品推銷,都可以設置相應的提成比例,不同的項目,不同的卡類也可以單獨設置,這么一來,不會因為門店不同時期的優惠或者是其他什么緣故導致提成不明晰,操作透明,讓員工更放心,更有動力。

          排班、交班

          系統智能排班,提升門店員工人效,解決人工排班帶來的班次沖突問題。每日根據排班表完成工作交班,交班表記錄當日門店的收銀、營業情況。

          員工權限

          在系統可自由設置不同的崗位,如:收銀員、店長、經理等,不同的崗位有不同的權限,收銀員能進行消費收銀,店長能進行庫存盤點、會員管理、查看報表等。

          數據統計

          擁有統計報表功能,商家通過后臺查看門店員工的銷售排行和員工月提成工資明細,及時掌握員工狀態,進行人員管理;還能通過查看自家商品的銷售排行和消耗工錢明細,合理規劃產品使用情況,對店鋪的經營做出分析,以便更好的經營管理店鋪。

          https://www.yunvip123.com/NewsDetail/Info-3261.html


          主站蜘蛛池模板: 国产一区二区视频在线观看| 精品视频一区二区三区| 亚洲av区一区二区三| 久久久国产精品亚洲一区| 狠狠做深爱婷婷综合一区| 精品国产AV无码一区二区三区| 中文字幕在线无码一区| 国产在线无码视频一区| 久久中文字幕无码一区二区| 蜜桃无码AV一区二区| 国产一区二区三区不卡观| 一区二区视频免费观看| 日本福利一区二区| 日韩精品免费一区二区三区 | 欧美日本精品一区二区三区| 久久久国产一区二区三区| 视频一区二区三区人妻系列| 色噜噜一区二区三区| 日本免费精品一区二区三区| 国产高清视频一区二区| 国产av一区二区精品久久凹凸 | 日韩久久精品一区二区三区| 国产精品高清一区二区三区不卡| 无码aⅴ精品一区二区三区浪潮| 中文字幕无码免费久久9一区9| 亚洲成av人片一区二区三区| 一区视频在线播放| 国产一区二区在线观看app| 狠狠色婷婷久久一区二区| 日本精品高清一区二区| 国产婷婷色一区二区三区| 久久一区二区精品| 无码国产精品一区二区免费vr| 人妻无码一区二区三区免费| 一区二区三区四区视频| 极品尤物一区二区三区| 另类ts人妖一区二区三区| 无码欧精品亚洲日韩一区夜夜嗨 | 综合无码一区二区三区| 免费日本一区二区| 国产对白精品刺激一区二区|