美文网首页Reactvue
从0搭建Vue3.0+TypeScript+antd-Vue+a

从0搭建Vue3.0+TypeScript+antd-Vue+a

作者: Benzic | 来源:发表于2020-09-14 16:58 被阅读0次

    背景

    Vue3.0出来了很长一段时间了,Vue3.0对于TypeScript的支持也有了质的提升,因为自己现在用React稍微多一些,所以也想在Vue中加入JSX,试试利用一下Vue3.0的新特性搭建一个基础框架。
    所需主要库包括:

    • UI antd-Vue 2.0
    • TypeScript
    • Axios
    • 状态管理采取的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
    

    创建项目

    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')
    

    目录结构

    image.png

    创建组件

    这里稍微和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导入,页面直接调用

     <Button type="danger">这是一个按钮</Button>
    
    引入按钮组件

    改造原有HOME页面

    原有Home组建的改造也和React写法一致,这里必须要先申明图片declare,不然要报错的。

    // 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
    //调用
    <Content content={props.msg}></Content>
    

    按道理来说content会展示出来,而实际上查看却没有content内容,打开控制台会发现,content内容变成一个而属性跑到节点上去了,节点内部却没有任何内容。这个问题 尤大大也出来解释过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会不优雅,就想用props,怎么办,就可以使用props方法,只是写法和之前略有区别:
    //  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

    现在就能正确拿到props内容,同时也不会有一个属性叫content;

    状态管理

    本来采取的是Vuex的方式,后来想着既然都使用了Vue3.0了何不用 provide和inject;于是这篇文章就改了;
    在context文件夹下创建button.ts 主要用于button组件的状态管理:
    provide:是一个对象,或者是一个返回对象的函数。里面呢就包含要给子孙后代的东西,也就是属性和属性值。
    ref:用于类似于Hooks的 useRef用于存储变量
    Ref:是 ref的types
    inject:一个字符串数组,或者是一个对象。
    computed:计算属性

    //  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的函数
    export const buttonProvide = () => {
        const count = ref<number>(0);
    
        // 计算属性
        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组建的状态provide、inject方法就写好了,接下来需要另外写个index.ts 统一将他们暴露出去。

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

    现在就可以使用了,这里必须先将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>
        )
      }
    })
    

    至于调用就下面这样

    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

    项目gitHub地址:https://github.com/Benzic/vue3.0-typescript-antdVue-tsx
    欢迎star 谢谢

    相关文章

      网友评论

        本文标题:从0搭建Vue3.0+TypeScript+antd-Vue+a

        本文链接:https://www.haomeiwen.com/subject/vzvaektx.html