者: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插件來轉換上文中的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相比也有一些區別:
所以只能自己動手來實現這個插件,我是在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');
渲染結果
配合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.js和Vue.js的語法【實踐】》
面上門店收銀管理系統那么多,老板應該怎么選?一般我們在討論系統功能的時候,都在說收銀、會員儲值等等,其實,系統對門店內部的統籌也十分重要,今天我們就來看看云上鋪收銀管理系統如何幫助老板管理門店內部。
員工提成
員工提成自動生成報表,不管是售卡,服務,商品推銷,都可以設置相應的提成比例,不同的項目,不同的卡類也可以單獨設置,這么一來,不會因為門店不同時期的優惠或者是其他什么緣故導致提成不明晰,操作透明,讓員工更放心,更有動力。
排班、交班
系統智能排班,提升門店員工人效,解決人工排班帶來的班次沖突問題。每日根據排班表完成工作交班,交班表記錄當日門店的收銀、營業情況。
員工權限
在系統可自由設置不同的崗位,如:收銀員、店長、經理等,不同的崗位有不同的權限,收銀員能進行消費收銀,店長能進行庫存盤點、會員管理、查看報表等。
數據統計
擁有統計報表功能,商家通過后臺查看門店員工的銷售排行和員工月提成工資明細,及時掌握員工狀態,進行人員管理;還能通過查看自家商品的銷售排行和消耗工錢明細,合理規劃產品使用情況,對店鋪的經營做出分析,以便更好的經營管理店鋪。
https://www.yunvip123.com/NewsDetail/Info-3261.html
*請認真填寫需求信息,我們會在24小時內與您取得聯系。