背景
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)
創(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麻掸,解決的方法我這里有兩種酥夭。
- 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
- 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
現(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;
項(xiàng)目gitHub地址:https://github.com/Benzic/vue3.0-typescript-antdVue-tsx
歡迎star 謝謝