從0搭建Vue3.0+TypeScript+antd-Vue+axios+JSX前端框架

背景

Vue3.0出來了很長一段時間了堂淡,Vue3.0對于TypeScript的支持也有了質(zhì)的提升宛逗,因?yàn)樽约含F(xiàn)在用React稍微多一些帖池,所以也想在Vue中加入JSX,試試?yán)靡幌耉ue3.0的新特性搭建一個基礎(chǔ)框架。
所需主要庫包括:

  • UI antd-Vue 2.0
  • TypeScript
  • Axios
  • 狀態(tài)管理采取的Provide慌申、Inject的方案
  • JSX(TSX)
  • Vue-router

package.json的依賴如下:

{
  "name": "vue-cli",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "ant-design-vue": "^2.0.0-beta.9",
    "axios": "^0.20.0",
    "normalize.css": "^8.0.1",
    "core-js": "^3.6.5",
    "style-resources-loader": "^1.3.3",
    "vue": "^3.0.0-0",
    "vue-router": "^4.0.0-0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-typescript": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0-0",
    "postcss-import": "^12.0.1",
    "postcss-px-to-viewport": "^1.1.1",
    "postcss-url": "^8.0.0",
    "node-sass": "^4.12.0",
    "sass-loader": "^8.0.2",
    "postcss-write-svg": "^3.0.1",
    "typescript": "~3.9.3"
  }
}

安裝

  • yarn安裝Vue + TypeScript
npm i -g @vue/cli 
OR
yarn global add @vue/cli

創(chuàng)建項(xiàng)目

vue create vue-cli
選擇配置
yarn serve
  • yarn安裝antd-design-vue
yarn add ant-design-vue@next -S

引入antd-Vue

import { createApp } from 'vue'
import { Button, message } from 'ant-design-vue';
import { App } from './App'
import router from './router'
import 'ant-design-vue/dist/antd.css';
import 'normalize.css'
const app = createApp(App)
app.use(Button)
app.use(router)
app.config.globalProperties.$message = message;
app.mount('#app')

目錄結(jié)構(gòu)

image.png

創(chuàng)建組件

這里稍微和React中的JSX不一樣的地方就是陌选,不是用children去取中間子元素,而是用slots獲取

// compoents/Button.tsx
import { defineComponent } from 'vue';
import { Button } from 'ant-design-vue';
const ButtonCom = defineComponent({
    setup(props: {}, { slots }) {
        return () => (
            <Button type="primary">
                {slots.default && slots.default()}
            </Button>
        )
    }
})
export default ButtonCom;

引入組件

引入組件的方式和React一致蹄溉,頂部import導(dǎo)入咨油,頁面直接調(diào)用

 <Button type="danger">這是一個按鈕</Button>
引入按鈕組件

改造原有HOME頁面

原有Home組建的改造也和React寫法一致,這里必須要先申明圖片declare柒爵,不然要報(bào)錯的役电。

// views/Home/index.tsx
import { defineComponent } from 'vue';
import HelloWorld from "@/components/HelloWorld/index";
import logo from '@/assets/logo.png'
interface HomeProps { }
const Home = defineComponent({
    setup(props: HomeProps) {
        return () => (
            <div class="home">
                <img alt="Vue logo" src={logo} />
                <HelloWorld />
            </div>
        )
    }
})
export default Home;

在src新建 images.d.ts文件

// images.d.ts
declare module '*.svg'
declare module '*.png'
declare module '*.jpg'
declare module '*.jpeg'
declare module '*.gif'
declare module '*.bmp'
declare module '*.tiff'

重新打包,圖片加載正常

這里有一個問題棉胀,比如這樣的組件
//  components/Content/index.tsx
import { defineComponent, reactive, onMounted } from 'vue';

interface LabelProps {
    content: any;
}
const Label = defineComponent({
    setup(props: LabelProps) {
        onMounted(() => { console.log('mounted!'); });
        return () => {
            const { content } = props;
            return <span>{content}</span>;
        }
    }
})
export default Label
//調(diào)用
<Content content={props.msg}></Content>

按道理來說content會展示出來法瑟,而實(shí)際上查看卻沒有content內(nèi)容,打開控制臺會發(fā)現(xiàn)唁奢,content內(nèi)容變成一個而屬性跑到節(jié)點(diǎn)上去了霎挟,節(jié)點(diǎn)內(nèi)部卻沒有任何內(nèi)容。這個問題 尤大大也出來解釋過https://github.com/vuejs/rfcs/pull/154麻掸,解決的方法我這里有兩種酥夭。

image.png

  • attrs
    既然它成為了屬性,那么就把他用屬性的方式取出來
//  components/Content/index.tsx
import { defineComponent, reactive, onMounted } from 'vue';

interface LabelProps {
    content: any;
}
const Label = defineComponent({
    setup(props: LabelProps, { attrs }: any) {
        console.log(attrs)
        onMounted(() => { console.log('mounted!'); });
        return () => {
            const { content } = attrs;
            return <span>{content}</span>;
        }
    }
})
export default Label
image.png
  • props
    有人會覺得attrs會不優(yōu)雅脊奋,就想用props熬北,怎么辦,就可以使用props方法诚隙,只是寫法和之前略有區(qū)別:
//  components/Content/index.tsx
import { defineComponent, reactive, onMounted } from 'vue';

const Label = defineComponent({
    props: {
        content: String,
    },
    setup: (props) => {
        return () => (
            <p>{props.content}</p>
        )
    }
})
export default Label
image.png

現(xiàn)在就能正確拿到props內(nèi)容讶隐,同時也不會有一個屬性叫content;

狀態(tài)管理

本來采取的是Vuex的方式,后來想著既然都使用了Vue3.0了何不用 provide和inject最楷;于是這篇文章就改了整份;
在context文件夾下創(chuàng)建button.ts 主要用于button組件的狀態(tài)管理:
provide:是一個對象,或者是一個返回對象的函數(shù)籽孙。里面呢就包含要給子孫后代的東西烈评,也就是屬性和屬性值。
ref:用于類似于Hooks的 useRef用于存儲變量
Ref:是 ref的types
inject:一個字符串?dāng)?shù)組犯建,或者是一個對象讲冠。
computed:計(jì)算屬性

//  src/context/button.ts
import { provide, ref, Ref, inject, computed } from 'vue'
import { getTestApi } from '@/api/testApi'

定義interface

//  src/context/button.ts
interface ListContext {
    count: Ref<number>,
    count2: Ref<number>,
    changeCount: (data: number) => void
}

provide方法:

//  src/context/button.ts
// provide名稱,推薦用Symbol
const listymbol = Symbol()
// 提供provide的函數(shù)
export const buttonProvide = () => {
    const count = ref<number>(0);

    // 計(jì)算屬性
    const count2 = computed(() => {
        return count.value * 2
    })
   //在這里可以引入axios api做異步請求
    const changeCount = async function (data: number) {
         try {
            let res: any = await getTestApi("async")
         } catch (error) {
            console.log(error)
            count.value = count.value + data
            console.log(count.value)
         }
    }

    provide(listymbol, {
        count,
        count2,
        changeCount
    })
}

inject方法:

//  src/context/button.ts
export const buttonInject = () => {
    const listContext = inject<ListContext>(listymbol);
    if (!listContext) {
        throw new Error(`buttonInject must be used after buttonProvide`);
    }
    return listContext
};

這就是一個button組建的狀態(tài)provide适瓦、inject方法就寫好了竿开,接下來需要另外寫個index.ts 統(tǒng)一將他們暴露出去谱仪。

//  src/context/index.ts
import { buttonProvide, buttonInject } from './button'
console.log("buttonInject", buttonInject)

export { buttonInject }
export const useProvider = () => {
    buttonProvide()
}

現(xiàn)在就可以使用了,這里必須先將provide掛載到某個你需要共用的地方否彩,可以是某幾個組件的父頁面疯攒,也可以是APP.tsx

// App.tsx
import { defineComponent } from 'vue';
import { useProvider } from '@/context/index'
import '@/assets/stylus/index.scss'
export const App = defineComponent({
  name: 'App',
  props: {
    content: String,
  },
  setup: (props) => {
    useProvider()
    return () => (
      <div>
        <div id="nav">
          <router-link to="/">Home</router-link> |
          <router-link to="/about">About</router-link>
        </div>
        <router-view />
      </div>
    )
  }
})

至于調(diào)用就下面這樣

import { defineComponent } from 'vue';
import { Button } from 'ant-design-vue';
import { buttonInject } from '@/context/index'     //引入入口index.ts
interface ButtonProps {
    type: any
}
const ButtonCom = defineComponent({
    setup(props: ButtonProps, { slots }) {
        const { changeCount, count, count2 } = buttonInject()        //獲取到方法屬性 就可以使用了
        const handleClick = () => {
            changeCount(1)
        };
        return () => (
            <Button type={props.type} onClick={handleClick}>
                {slots.default && slots.default()}count:{count.value}count2:{count2.value}   
            </Button>
        )
    }
})

export default ButtonCom;
chrome-capture (10).gif

項(xiàng)目gitHub地址:https://github.com/Benzic/vue3.0-typescript-antdVue-tsx
歡迎star 謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市列荔,隨后出現(xiàn)的幾起案子敬尺,更是在濱河造成了極大的恐慌,老刑警劉巖贴浙,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砂吞,死亡現(xiàn)場離奇詭異,居然都是意外死亡崎溃,警方通過查閱死者的電腦和手機(jī)蜻直,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來袁串,“玉大人概而,你說我怎么就攤上這事“闫牛” “怎么了到腥?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蔚袍。 經(jīng)常有香客問我乡范,道長,這世上最難降的妖魔是什么啤咽? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任晋辆,我火速辦了婚禮,結(jié)果婚禮上宇整,老公的妹妹穿的比我還像新娘瓶佳。我一直安慰自己,他們只是感情好鳞青,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布霸饲。 她就那樣靜靜地躺著,像睡著了一般臂拓。 火紅的嫁衣襯著肌膚如雪厚脉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天胶惰,我揣著相機(jī)與錄音傻工,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛中捆,可吹牛的內(nèi)容都是我干的鸯匹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼泄伪,長吁一口氣:“原來是場噩夢啊……” “哼殴蓬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起臂容,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤科雳,失蹤者是張志新(化名)和其女友劉穎根蟹,沒想到半個月后脓杉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡简逮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年球散,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片散庶。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蕉堰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悲龟,到底是詐尸還是另有隱情屋讶,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布须教,位于F島的核電站皿渗,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏轻腺。R本人自食惡果不足惜乐疆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贬养。 院中可真熱鬧挤土,春花似錦、人聲如沸误算。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儿礼。三九已至咖杂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜘犁,已是汗流浹背翰苫。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奏窑。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓导披,卻偏偏與公主長得像,于是被迫代替她去往敵國和親埃唯。 傳聞我的和親對象是個殘疾皇子撩匕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348