<
>

vue3基础知识剖析

2022-08-05 17:53:02 来源:易采站长站 作者:

目录
声明vue3.0有哪些新特性vue3.0的优缺点如何解锁vue3.0体验vue3.0的4中姿势核心的composition APIsetupsetup语法糖ref、reactivewatch跟watchEffectcomputed(计算属性)组件通信propsemit插槽vue2中的使用vue3中的使用生命周期vue-router 4.0Composition API路由守卫keep-alive 和 transition 必须用在 router-view 内部style新特性深度选择器全局选择器通过module自定义注入名称与组合式 API 一同使用Typescript基础&项目中如何使用Typescript基本的数据类型枚举元祖任意值 Any空值 voidinterface函数参数类型与返回值类型泛型交叉类型联合类型函数重载vue3项目中如何集成TS状态管理Pinia

前言

前段时间,由新东方出品直播带货品牌东方甄选火爆全网,其中最受大家关注的主播董宇辉,用网友的调侃来说“长着一个颗粒无收的脸,却拥有五谷丰登的灵魂”。他在直播中推荐五常大米时说:“厨房里充满了饭香,就是人间浪漫。”,介绍水蜜桃:“这个水蜜桃,美好的像穿越大峡谷的风,像仲夏夜的梦”。卖牛排,告诉观众这是“Original>。让网友赞叹不绝,大家都说这买的不是吃的,买的是知识付费,买的是灵魂洗礼。最重要的是,他尽然还是一个英语老师。很多人感叹,好好读书太重要了,因为知识能给人带来力量,带来高贵的灵魂。相比那些快节奏、声嘶力竭、充满商业诱导的的直播模式,简直就是降维打击

从笔者的角度来看,董宇辉的成功并非偶然,能够饱读诗书,一定源于自己多年不断的思考跟总结,不断的追求学习的本质才能让自己在无意之间沉淀的像个诗人,像个哲学家。这背后的付出,常人肯定无法想象。作家周岭说过“所谓的学习,不是努力,努力,在努力。而是反馈,反馈,再反馈。光靠一味的输入,而不输出,这种学习大概率是低效率的”。就像咱们前端技术圈一样,框架层出不穷,版本迭代快的让人无法喘息。很多小伙伴都焦虑的呐喊,学不动了。笔者认为,真正高效的学习一定是需要在输入的同时,要有很好的输出,让自己积累更多的正向反馈,就像我们平时学习某一种技术栈一样,光是一味的学习不行,还要做出高质量的实践跟输出才行!

笔者这篇文章会从vue3基础的知识点开始剖析,特别是在将composition API的时候,在代码示例中不会一上来就使用setup语法糖,而是用早期的setup函数,这样方便于初学的小伙伴们理解跟学习。文章篇幅较大,接下来,请您花个10分钟耐心的看完,或许会有不一样的收货。

声明

    本文中下边所有的示例代码都可以直接访问这个网站点击这里查看效果需要源代码的小伙伴可以在评论区下留言,或者私信我。本文章的讲解的所有实例面向对vue3的初学者,如有讲解不到位,或者有偏差的地方,欢迎大家留言指出。

    vue3.0有哪些新特性

      Composition>组件通信生命周期自定义Hook插槽v-model的更改更加纯粹的Tree-shaking配合状态管理的Pinia配合升级的vue-router 4.x配合升级的打包工具vite配合TS在项目中自由使用

      vue3.0的优缺点

      优点

        使用vue3最大的优势个人认为倒不是它的Api,而是配合使用的vite打包工具,特别是大型项目本地启动要比当前的webpack5要快至少2倍以上(项目中测试过)比起vue>,Composition Api的优势要明显的多,如果习惯了setup语法糖的写法,你会发现爽的飞起,很多之前在vue 2.x中大量重复逻辑不存在了底层通过Proxy来实现双向绑定,性能上提升了很多TypeScript支持度更好,可以很愉快的在项目中使用TypeScript

        缺点

          如果还有IE情节的公司,那vue3确实不太适合,因为vue3已经抛弃了对IE11的支持,再说了 微软人家自己都不打算维护IE了,兄弟们,放弃IE拥抱chrome吧!Composition Api的写法需要花一点点时间来适应,毕竟学习新语法还是需要成本的

          如何解锁vue3.0

          体验vue3.0的4中姿势

            通过CDN
            <script src="https://unpkg.com/vue@next"></script>
            
              npm
              # 最新稳定版
              npm install vue@next
              npm install -D @vue/compiler-sfc
              

              如果你是从Vue 2.x升级的,请注意 @vue/compiler-sfc 替换掉了 vue-template-compiler

                vue-cli
                npm install -g @vue/cli
                vue upgrade --next
                
                  vite
                  npm init vite@latest <project-name> -- --template vue
                  cd <project-name>
                  npm install
                  npm run dev

                  推荐使用第4种方式,直接使用官方推荐最新的vite打包工具,直接初始化项目。

                  核心的composition>

                  setup

                    setupvue3提出的一个非常重要的选项,也是Composition>最为核心的语法之一。setup执行时机是在beforeCreate之前执行的。setup返回的是一个对象,对象中的所有属性都是可以在template中使用setup中不能使用thissetup中注册生命周期onMountedwatchcomputed等,我们会在下边详细讲解

                    setup参数

                      propscontext
                      <script>
                      export default {
                        setup (props, context) {
                          return {}
                        }
                      }
                      </script>

                      setup语法糖

                      既然上边提到了setup语法,那就有必要把setup语法糖介绍一下,我们在实际的项目开发中在熟悉了setup语法的本质后,也推荐大家使用setup语法糖来编写,这样也可以大大提升开发效率。

                        不需要像上述一样return,只需要在<script>中声明一下即可任何在 <script setup> 声明的顶层的绑定 (包括声明的变量,函数声明,以及 import 引入的内容) 都可以在模板中直接使用组件在语法糖中可以自动注册,无需再通过components进行注册
                        <script setup>
                        import {ref} from 'vue'
                        let property = ref('这里是响应式属性');
                        // 这里我们引入了子组件SetUp.vue
                        import SetUp from '@/components/SetUp.vue'
                        </script>

                        ref、reactive

                          refreactive都是vue3中用来做数据定义使用的,如同vue2中在data中做数据定义一样,示例代码如下:
                          <template>
                              <h3>{{ state.count }}</h3>
                              <h3>{{ num }}</h3>
                              <el-button @click="handleAdd" type="primary">ref计算</el-button>
                          </template>
                          
                          <script>
                          import { ref, reactive } from 'vue'
                          
                          export default {
                              setup() {
                                  const num = ref(0)
                                  const state = reactive({ count: 1 })
                                  function handleAdd() {
                                      state.count++;
                                      num.value += 2;
                                  }
                                  return {
                                      state,
                                      num,
                                      handleAdd
                                  }
                              }
                          }
                          </script>
                            refreactive的区别在哪呢?很多人分不清楚,网上有很多文章简单的定义为ref负责处理基本数据类型的双向绑定,reactive负责处理对象的双向绑定。其实,这样笔者会觉得给很多初学者带来很多误导,其实ref也可以处理对象的双向绑定,就像下边这段代码一样。
                            <template>
                                <el-button @click="handleAdd" type="primary">ref计算</el-button>
                                <h3>{{ obj.count }}</h3>
                            </template>
                            
                            <script>
                            export default {
                                setup() {
                                    // ref 对象双向绑定
                                    const obj = ref({ count: 1 })
                                    function handleAdd() {
                                        obj.value.count = obj.value.count + 1
                                    }
                                    return {
                                        obj,
                                        handleAdd
                                    }
                                }
                            }
                            </script>

                            watch跟watchEffect

                            watchEffect

                              当传入一个函数时,可以响应式的自动收集依赖,当依赖变更时重新运行该函数;使用是需要配置flush:>,否则依赖在监听时无法被立即更新也可以使用stop来立即停止对函数的监听
                              <template>
                                  <div ref="root">This is a root element</div>
                              </template>
                              <script>
                              import {ref, watchEffect} from 'vue'
                              export default {
                                  setup() {
                                      const root = ref(null)
                                      watchEffect(() => {
                                          console.log(`watchEffect监听:${root.value}`);
                                      }, {
                                          flush: 'post'
                                      })
                                      return {
                                          root
                                      }
                                  },
                              }
                              </script>
                              

                              watch

                              watch API 与选项式 API this.$watch (以及相应的 watch 选项) 完全等效。watch 需要侦听特定的数据源,并在单独的回调函数中执行副作用。默认情况下,它也是惰性的——即回调仅在侦听源发生变化时被调用。

                              watchEffect 相比,watch

                                是一个返回任意值的getter函数是一个包装的对象,可以是ref对象、也可以reactive对象可以同时监听多个数据源监听是需要配置deep: true,否则回调函数无法被触发
                                <template>
                                    <h3>监听单个数据源1:{{state1.count}}</h3>
                                    <button @click="handleWatchSingle1">watch监听测试1</button>
                                    <h3>监听单个数据源2:{{state2}}</h3>
                                    <button @click="handleWatchSingle2">watch监听测试2</button>
                                    <h3>监听复杂对象数据源:{{state3.player}}</h3>
                                    <button @click="handleWatchSingle3">watch监听测试3</button>
                                </template>
                                <script>
                                import {ref, reactive, watch} from 'vue'
                                
                                export default {
                                    setup() {
                                        const state1 = reactive({ count: 1 })
                                        const state2 = ref(0)
                                        const state3 = reactive({
                                            player: {
                                                name: 'James',
                                                achievement: ['4次NBA常规赛mvp', '03年选秀状元', '4次NBA总冠军']
                                            }
                                        })
                                        watch(() => state1.count, (newVal, oldVal) => {
                                            console.log('watch监听reactive中的newVal:', newVal);
                                            console.log('watch监听reactive中的oldVal:', oldVal);
                                        })
                                        watch(() => state2.value, (newVal, oldVal) => {
                                            console.log('watch监听ref中的newVal:', newVal);
                                            console.log('watch监听ref中的oldVal:', oldVal);
                                        })
                                        watch(() => state3.player, (newVal, oldVal) => {
                                            console.log('watch监听复杂对象中的newVal:', newVal);
                                            console.log('watch监听复杂对象中的oldVal:', oldVal);
                                        }, {
                                            deep: true,
                                            // immediate: true
                                        })
                                        // 同时监听多个值
                                        // watch([() => state1.count, state2.value], ([newVal1, newVal2], [oldVal1, oldVal2]) => {
                                        //     console.log('watch监听中的newVal:', newVal1, newVal2);
                                        //     console.log('watch监听oldVal:', oldVal1, oldVal2);
                                        // })
                                        function handleWatchSingle1() {
                                            state1.count++
                                        }
                                        function handleWatchSingle2() {
                                            state2.value++
                                        }
                                        function handleWatchSingle3() {
                                            state3.player = {
                                                name: 'Wade',
                                                achievement: ['3次NBA总冠军', '曾经的热火三巨头之一', '1次NBA总决赛mvp']
                                            }
                                        }
                                        return {
                                            state1,
                                            state2,
                                            state3,
                                            handleWatchSingle1,
                                            handleWatchSingle2,
                                            handleWatchSingle3
                                        }
                                    },
                                }
                                </script>
                                

                                computed(计算属性)

                                  接受一个>getter 函数,并根据getter 的返回值返回一个不可变的响应式 ref 对象。接受一个具有 getset 函数的对象,用来创建可写的 ref 对象
                                  <template>
                                      <div style="margin-top:30">
                                          <h3>computedNum值为:{{computedNum}}</h3>
                                          <h3>computedNum2值为:{{computedNum}}</h3>
                                          <button @click="handleComputed">computed计算测试</button>
                                      </div>
                                  </template>
                                  
                                  <script>
                                  import { ref, computed } from 'vue'
                                  
                                  export default {
                                      setup() {
                                          const state = ref(1)
                                          const computedNum = computed(() => {
                                              return state.value + 1
                                          })
                                          console.log('computed缓存后的值:', computedNum.value);
                                          // 只可读属性,不可写,会抛出警告 Write operation failed: computed value is readonly
                                          function handleComputed() {
                                              computedNum.value++
                                          }
                                          const computedNum2 = computed({
                                              get: () => state.value + 2,
                                              set: val => {
                                                  count.value = val - 0
                                              }
                                          })
                                          return {
                                              computedNum,
                                              computedNum2,
                                              handleComputed
                                          }
                                      },
                                  }
                                  </script>
                                  

                                  组件通信

                                  组件通信这块跟vue2的区别不大,我们就拿常用的props跟emit来讲解一下。

                                  props

                                    父级组件向子组件传递数据

                                    emit

                                      子组件想父组件传递数据需要通过emits选项来定义组件可触发的事件

                                      父组件

                                      <template>
                                         <Children :msg1="msg1" :msg2="msg2" @childClick="handleClick" />
                                      </template>
                                      <script>
                                      import {ref, reactive} from 'vue';
                                      import Children from './children.vue'
                                      
                                      export default {
                                          setup() {
                                              const msg1 = ref('给子组件传递的消息1')
                                              const msg2 = reactive({
                                                  name: '给子组件传递的消息2'
                                              })
                                              return {
                                                  msg1,
                                                  msg2
                                              }
                                          },
                                          methods: {
                                              handleClick(val) {
                                                  console.log('接收子组件emit过来的数据:', val);
                                              }
                                          },
                                          components: { Children }
                                      }
                                      </script>

                                      子组件

                                      <template>
                                          <div style="margin-top: 30px">props传递给子组件的消息:{{ msg1 }}</div>
                                          <button @click="$emit('childClick', 6666)" style="margin-top: 30px">向父组件emits事件</button>
                                      </template>
                                      <script>
                                      export default {
                                          props: ['msg1', 'msg2'],
                                          emits: ['childClick'],
                                          setup(props) {
                                              console.log('子组件接收父级组件传递过来的消息:', props);
                                          },
                                      }
                                      </script>

                                      插槽

                                      vue2中的使用

                                      子组件

                                      <template>
                                          <slot name="title"></slot>
                                      </template>

                                      父组件

                                      <template slot="title">
                                          <h2>周岭:《认知觉醒》</h2>
                                      <template>

                                      vue3中的使用

                                      vue3插槽中提供了v-slot:name>

                                      子组件

                                      我们定一个可循环的插槽content

                                      <template>
                                          <!-- <slot name="title"></slot> -->
                                          <div v-for="(item, index ) in items" :key="index">
                                              <slot :item="item" name="content"></slot>
                                          </div>
                                      </template>
                                      
                                      <script setup>
                                      import {ref} from 'vue';
                                      const items = ref(['认知觉醒', '认知驱动']);
                                      </script>

                                      父组件

                                      父组件中可以有两种方式来引入子组件中的插槽,其一是通过v-slot:content="scopend"的方式,其二是通过简写#content="{item}"的方式

                                      <template>
                                          <SlotChild>
                                              <!-- <template v-slot:content="scoped">
                                                  <div>{{ scoped.item }}</div>
                                              </template> -->
                                      
                                              <template #content="{item}">
                                                  <div>{{ item }}</div>
                                              </template>
                                          </SlotChild> 
                                      </template>
                                      
                                      <script setup>
                                      import SlotChild from './SlotChild.vue'
                                      </script>

                                      生命周期

                                      vue3的声明周期如果是使用选项性Api的话,原来的生命周期钩子可以照常使用,那如果选用vue3组合式Api的话,生命周期需要通过import引入的方式在setup中调用。下图是vue3跟vu2声明周期的区别

                                      <template>
                                          <div id="test">
                                              <h3>{{ counter }}</h3>
                                              <button @click="handleClick">声明周期测试</button>
                                          </div>
                                      </template>
                                      
                                      <script>
                                      import {
                                          ref,
                                          onMounted,
                                          onBeforeMount,
                                          onBeforeUpdate,
                                          onUpdated,
                                          onBeforeUnmount,
                                          onUnmounted
                                      } from 'vue'
                                      export default {
                                          setup() {
                                              const counter = ref(0);
                                              console.log('....');
                                              function handleClick() {
                                                  counter.value += 1;
                                              }
                                              onBeforeMount(() => {
                                                  console.log("组件挂载之前");
                                              });
                                              onMounted(() => {
                                                  console.log("DOM挂载完成");
                                              });
                                              onBeforeUpdate(() => {
                                                  console.log("DOM更新之前", document.getElementById("test").innerHTML);
                                              });
                                              onUpdated(() => {
                                                  console.log("DOM更新完成", document.getElementById("test").innerHTML);
                                              });
                                              onBeforeUnmount(() => {
                                                  console.log("实例卸载之前");
                                              });
                                              onUnmounted(() => {
                                                  console.log("实例卸载之后");
                                              });
                                              return {
                                                  counter,
                                                  handleClick
                                              }
                                          },
                                      }
                                      </script>
                                      

                                      vue-router>

                                      vue-router 3.x跟vue-router 4.x比起来写法上的区别

                                      vue-router 3.x

                                      // router/index.js
                                      import Vue from 'vue'
                                      import Router from 'vue-router'
                                      import routes from './routes'
                                      
                                      Vue.use(Router)
                                      
                                      const router = new Router({
                                        routes
                                      })
                                      export default router
                                      
                                      // main.js
                                      import Vue from 'vue'
                                      import router from './router'
                                      // ...
                                      
                                      new Vue({
                                        el: '#app',
                                        router,
                                        components: { App },
                                        template: '<App/>'
                                      })

                                      vue-router 4.x

                                      // router/index.js
                                      import { createRouter } from 'vue-router'
                                      import routes from './routes'
                                      
                                      const router = createRouter({
                                        history: createWebHistory(), // history模式
                                        routes
                                      })
                                      
                                      // main.js
                                      import { createApp } from 'vue'
                                      import router from './router'
                                      
                                      const app = createApp(App)
                                      app.use(router)
                                      app.mount('#app')
                                        new Router()改成createRouter()mode: 'history'改成 history: createWebHistory()

                                        Composition>

                                        useRouter、useRoute

                                        通过useRouter进行路由跳转

                                        <template>
                                          <div class="mg30">
                                            <el-button @click="handleJump" type="primary">关于我们</el-button>
                                          </div>
                                        </template>
                                        
                                        <script setup>
                                        import { useRouter } from 'vue-router'
                                        
                                        const router = useRouter()
                                        
                                        const handleJump = (query) => {
                                          router.push({ 
                                            name: "about", 
                                            query: {
                                              id: 1
                                            }
                                          }) 
                                        }
                                        </script>

                                        通过useRoute来获取传递过来的id

                                        <template>
                                            <div>关于我们</div>
                                        </template>
                                        
                                        <script setup>
                                        import { useRoute } from 'vue-router'
                                        
                                        const route = useRoute()
                                        console.log('id>>>', route.query.id);
                                        </script>

                                        路由守卫

                                        全局守卫

                                        /router/index.js

                                        详情页面meta中添加登录标识needLogin

                                        let routes = [
                                            {
                                                path: '/detail',
                                                name: 'detail',
                                                component: () => import('@/views/detail.vue'),
                                                meta: {
                                                    needLogin: true
                                                }
                                            }
                                        ]

                                        main.js

                                        添加守卫

                                        import router from './router'
                                        
                                        // 全局路由守卫
                                        router.beforeEach((to, from) => {
                                            if (to.meta.needLogin) {
                                                return {
                                                    name: 'login'
                                                }
                                            }
                                        })

                                        路由独享守卫

                                        /router/index.js

                                        let routes = [
                                            {
                                                path: '/category/:id',
                                                name: 'category',
                                                component: () => import('@/views/category.vue'),
                                                beforeEnter: (to, from) => {
                                                    // 如果不是正确的分类,跳转到NotFound的页面
                                                    console.log('id>>>>', to.params.id);
                                                    if (!["0", "1", "2"].includes(to.params.id)) { 
                                                      return {
                                                        name: "NotFound",
                                                        // 这个是在地址栏保留输入的信息,否则地址栏会非常的丑
                                                        params: { pathMatch: to.path.split("/").slice(1) },
                                                        query: to.query,
                                                        hash: to.hash,
                                                      };
                                                    }
                                                }
                                            }
                                        ]

                                        组件内部守卫

                                        <template>
                                            <div>关于我们</div>
                                        </template>
                                        
                                        <script setup>
                                        import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
                                        
                                        
                                        // 页面内部的路由守卫
                                        onBeforeRouteLeave((to, from) => {
                                            const answer = window.confirm('是否确认离开') 
                                            if (answer) { 
                                                console.log('不离开'); 
                                                return false 
                                            }
                                        })
                                        
                                        // 对于一个带有动态参数的路径 /category/:catId,在 /category/1 和 /category/2 之间跳转的时候, 会触发onBeforeRouteUpdate的路由钩子函数,在钩子函数中可以进行数据的更新。
                                        onBeforeRouteUpdate((to, from) => {
                                             console.log('to>>>', to);
                                             console.log('from>>>', from);
                                            // if (to.params.id !== from.params.id) {
                                            //     userData.value = await fetchUser(to.params.id)
                                            // }
                                        })
                                        </script>

                                        keep-alive>
                                        // vue-router 3
                                        <keep-alive> 
                                         <router-view /> 
                                        </keep-alive> 
                                         
                                        // vue-router 4 
                                        <router-view v-slot="{component}"> 
                                         <keep-alive> 
                                          <component :is="component" /> 
                                         </keep-alive> 
                                        </router-view> 

                                        style新特性

                                        跟vue2不同的是,vue3中提供了提供了很多不同的选择器方便我们在样式编写上更加的灵活多变。

                                        深度选择器

                                        类似于sass语法中的v::deep,不过vue3中的样式自带深度作用域

                                        <style scoped>
                                        .parent :deep(div) {
                                            margin-bottom: 10px;
                                        }
                                        </style>
                                        
                                        <template>
                                          <div class="parent">
                                            <div class="set-up">:deep 深度作用域测试</div>
                                          </div>
                                        </template>

                                        全局选择器

                                        不用像vue2一样写全局作用域时,需要单独开启一个style标签,同时去掉scoped属性;vue3提供了一种便捷的写法,只需要使用global属性传递你想全局修改的样式即可。

                                        <template>
                                            <div>全局选择器测试</div>
                                            <p :class="$style.green">module样式测试</p>
                                        </template>
                                        
                                        <style scoped>
                                        :global(div) {
                                            color: red;
                                        }
                                        </style>

                                        <style module> 标签会被编译为 CSS Modules 并且将生成的 CSS 类作为 $style 对象的键暴露给组件。

                                        <template>
                                            <p :class="$style.green">module样式测试</p>
                                        </template>
                                        
                                        <style module>
                                        .green {
                                            color: green;
                                        }
                                        </style>

                                        通过module自定义注入名称

                                        <template>
                                            <p :class="classes.blue">useCssModule样式测试</p>
                                        </template>
                                        
                                        <style module="classes">
                                        .blue {
                                            color: blue;
                                        }
                                        </style>
                                        

                                        与组合式>
                                        <script>
                                        import { h, useCssModule } from 'vue'
                                        export default {
                                          setup() {
                                            const style = useCssModule()
                                            return () =>
                                              h(
                                                'div',
                                                {
                                                  class: style.success
                                                },
                                                'Task complete!'
                                              )
                                          }
                                        }
                                        </script>
                                        
                                        <style module>
                                        .success {
                                          color: #090;
                                        }
                                        </style>
                                        

                                        Typescript基础&项目中如何使用Typescript

                                        对于TS,笔者认为小项目中也不必集成TS,反倒会提升项目的编译成本。那如果是大型项目的话,有必要尝试接入TS,一方面可以减少不必要的类型判断及文档注释,同时可以及早的发现错误,做静态类型检查时就可以及时的发现问题。另一方面,类、接口的使用更易于构建和维护组件;那么,对于初学者我们有必要对TS的一些基本用法做一下普及。

                                        基本的数据类型

                                        /**
                                         * @description: 基本的数据类型
                                         * @return {*} boolean(布尔值)number(数值) Array<number> (泛型数组)Object (对象)null undefined
                                         */
                                        let isDone: boolean = false;
                                        console.log('isDon', isDone);
                                        
                                        let num: number = 1;
                                        console.log('num', num);
                                        
                                        let str: string = '认知觉醒';
                                        console.log('str', str);
                                        
                                        let arr: number[] = [1, 2, 3];
                                        console.log('arr', arr);
                                        
                                        // 泛型数组
                                        let arr2: Array<number> = [1, 2, 3]
                                        console.log('arr2', arr2);
                                        
                                        let obj: Object = { id: 1 }
                                        console.log('obj', obj);
                                        
                                        let u: undefined = undefined
                                        console.log('u', u);
                                        
                                        let n: null = null;
                                        console.log('n', n);
                                        

                                        枚举

                                        // 数字类型枚举与数字类型
                                        enum CardSuit {
                                            Clubs,
                                            Diamonds,
                                            Hearts,
                                            Spades
                                        }
                                        
                                        console.log('CardSuit', CardSuit.Clubs); // 0
                                        let col = CardSuit.Clubs;
                                        col = 0 // 安全有效的
                                        console.log('col', col); // 0
                                        
                                        // 数字类型枚举与字符串类型
                                        enum Tristate {
                                            False,
                                            True,
                                            Unkonw
                                        }
                                        console.log('字符串', Tristate[0]); // 'False'
                                        console.log('number', Tristate['False']); // 0
                                        console.log('字符串', Tristate[Tristate.False]); // 'False'
                                        
                                        // 字符串枚举
                                        enum LogLevel {
                                            info = 'info',
                                            warn = 'warn',
                                            error = 'error'
                                        }
                                        console.log('LogLevel', LogLevel.info); // 'info'
                                        

                                        元祖

                                        /**
                                         * @description: 元祖
                                         * @return {*} 允许数组各元素的类型不必相同
                                         */
                                        let x: [string, number, boolean];
                                        x = ['hello', 10, true];
                                        console.log('正确元祖', x); // ['hello', 10, true]
                                        // y = [10, 'hello', false]
                                        // console.log('错误的元祖', y);
                                        

                                        任意值>
                                        /**
                                         * @description: 任意值 Any
                                         * @return {*} 表示任意类型, 通常用于不确定内容的类型,比如用户的输入或者是第三方库代码;实际项目中,此类型建议少用
                                         */
                                        let notSure: any = 4;
                                        notSure = 'maybe a string instead';
                                        console.log('notSure', notSure); // 'maybe a string instead'
                                        notSure = true;
                                        console.log('notSure', notSure); // true
                                        

                                        空值>
                                        /**
                                         * @description: 空值 void
                                         * @return {*} 与any相反,通常用于函数,表示没有返回值
                                         */
                                        const voidFunc = (): void => {
                                            console.log('这个函数没有返回任何值');
                                            // return msg; // 不能return
                                        }
                                        voidFunc()
                                        

                                        interface

                                        /**
                                         * @description: 接口 interface
                                         * @return {*} 类型契约,跟我们平时与服务端接口要先定义字段是一个道理
                                         */
                                        interface Point {
                                            x: number
                                            y: number
                                            z?: number
                                            readonly l: number
                                        }
                                        
                                        const point: Point = { x: 10, y: 20, z: 30, l: 40 }
                                        console.log('point', point);
                                        
                                        const point2: Point = { x: '10', y: 20, z: 30 } // Error x应该是Number类型
                                        
                                        const point3: Point = { x: 10, y: 20, z: 30 } // Error l字段也是必传
                                        
                                        const point4: Point = { x: 10, y: 20, z: 30, l: 40, m: 50 } // Error m字段没有定义
                                        
                                        const point5: Point = { x: 10, y: 20, l: 40 } // 正常
                                        point5.l = 20; // Error l字段是只读类型,不能修改
                                        

                                        函数参数类型与返回值类型

                                        /**
                                         * @description: 函数参数类型与返回值类型
                                         * @return {*}
                                         */
                                        function sum(a: number, b: number): number {
                                            return a + b;
                                        }
                                        console.log('sum', sum(2, 3)); // 5
                                        
                                        // 配合interface使用
                                        interface Point {
                                            x: number
                                            y: number
                                        }
                                        
                                        function sum2({x, y}: Point): number {
                                            return x + y;
                                        }
                                        console.log('sum2', sum2({x: 1, y: 2})); // 3
                                        

                                        泛型

                                        /**
                                         * @description: 泛型
                                         * @return {*} 泛型的意义在于函数的重用性,设计原则希望组件不仅能够支持当前的数据类型,同时也支持未来的数据类型
                                         * 语法:<T>(arg: T): T
                                         */
                                        
                                        // 比如我们最初设计函数identity 入参为String
                                        function identity(arg: String) {
                                            return arg;
                                        }
                                        console.log(identity('hello')); // hello
                                        
                                        // 后来随着业务的迭代我们又需要支持 Number
                                        function identity2(arg: String) {
                                            return arg;
                                        }
                                        console.log(identity(2)); // Argument of type 'number' is not assignable to parameter of type 'String'
                                        
                                        // 那我们为什么不用any呢?使用any会导致丢失掉一些信息,我们无法确定要返回值到底是属于什么数据类型
                                        const hello1: String = 'Hello vue3';
                                        const hello2: Number = 666;
                                        function say<T>(arg: T): T {
                                            return arg;
                                        }
                                        console.log('泛型1:', say(hello1)); // Hello vue3
                                        console.log('泛型2:', say(hello2)); // 666
                                        
                                        // 泛型约束
                                        // 我们使用同样的例子,加了一个console,但是很不幸运,报错了,因为泛型无法保证每种类型都有.length 属性
                                        const hello3: String = 'Hello vue3';
                                        function say2<T>(arg: T): T {
                                            console.log(arg.length); // Property 'length' does not exist on type 'T'
                                            return arg;
                                        }
                                        console.log('泛型3:', say2(hello3)); // Hello vue3
                                        
                                        interface Lengthwise {
                                            length: number
                                        }
                                        
                                        function say3<T extends Lengthwise>(arg: T): T {
                                            console.log(arg.length);
                                            return arg;
                                        }
                                        console.log(say3(1)); // Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.
                                        console.log(say3({ value: 'hello vue', length: 10 })); // '{ value: 'hello vue', length: 10 }'
                                        
                                        

                                        交叉类型

                                        interface foo {
                                            x: number
                                        }
                                        
                                        interface bar {
                                            b: string
                                        }
                                        
                                        type intersection = foo & bar
                                        
                                        const result: intersection = {
                                            x: 10,
                                            b: 'hello'
                                        }
                                        
                                        console.log('result', result);

                                        联合类型

                                        /**
                                         * @description: 联合类型
                                         * @return {*} 表示一个值可以为几种数据类型之一
                                         */
                                        type arg = string | number | boolean
                                        
                                        const foo = (arg: arg): any => {
                                            console.log('arg', arg);
                                        }
                                        foo(1)
                                        foo('1')
                                        foo(true)
                                        

                                        函数重载

                                        /**
                                         * @description: 函数重载
                                         * @return {*} 1个函数可以执行多项任务的能力
                                         */
                                        
                                        // add函数,它可以接收string类型的参数进行拼接,也可以接收number类型的参数进行相加
                                        function add <T, U>(arg1: T, arg2: U) {
                                          // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2
                                          if (typeof arg1 === 'string' && typeof arg2 === 'string') {
                                            return arg1 + arg2
                                          } else if (typeof arg1 === 'number' && typeof arg2 === 'number') {
                                            return arg1 + arg2
                                          }
                                        }
                                        console.log('number类型相加', add(1, 2));
                                        console.log('string类型拼接', add('1', '2'));
                                        

                                        vue3项目中如何集成TS

                                          首先,你可以在初始化项目的时候就选择TS模板,直接将TS相关配置集成到项目中去。当然,你也可以手动去配置TS

                                          安装TS

                                          npm i typescript

                                          项目根目录新建tsconfig.json文件,用于TS的编译基础文件

                                          {
                                            "compilerOptions": {
                                              "target": "esnext",
                                              "module": "esnext",
                                              "sourceMap": true,
                                              "outDir": "./dist",
                                              "rootDir": "./src",
                                              "strict": true,
                                              "moduleResolution": "node",
                                              "esModuleInterop": true,
                                              "skipLibCheck": true,
                                              "forceConsistentCasingInFileNames": true
                                            },
                                            "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
                                          }
                                            项目中使用

                                            script标签中声明langg="ts",然后就可以愉快的使用TS的项目语法了,下边这段代码只是一些简单的示例。

                                            <template>
                                               <div>
                                                <h2>标题:{{book.title}}</h2>
                                                <h2>作者:{{book.author}}</h2>
                                                <h2>出版日期:{{book.year}}</h2>
                                                <hr>
                                                <h3>{{allTitle}}</h3>
                                                <el-button @click="setTitle('我是传入的数据')" type="primary">设置数据</el-button>
                                              </div>
                                            </template>
                                            
                                            <script lang="ts">
                                            import { defineComponent, ref, toRefs, reactive } from 'vue';
                                            
                                            // 定义Book接口
                                            interface Book {
                                                title: String
                                                author: String
                                                year?: Number,
                                                handleChangeName?(): void
                                            }
                                                  
                                            export default defineComponent ({
                                                data() {
                                                    let book: Book = {
                                                        title: 'vue3 typescript',
                                                        author: "vue Team",
                                                        year: 2020,
                                                    }
                                                    return {
                                                        book
                                                    }
                                                },
                                                setup() {
                                                    let year1 = ref<String | Number>('2022')
                                                    console.log('year1', year1.value);
                                                    
                                                    // 第一种方式
                                                    // const book1: Book = reactive({
                                                    // name: year1.value,
                                                    // desc: "vue3 进阶学习加油",
                                                    // setNamechange(){
                                                    //     this.name = "我是新设置的"
                                                    // }
                                                    // });
                                                    // // 第二种方式
                                                    // const book2 = reactive<Book>({
                                                    // name: "vue3--typeScript",
                                                    // desc: "学习ts加油",
                                                    // year: 2020,
                                                    // });
                                                    // // 第三种方式
                                                    // const book3 = reactive({
                                                    //     name: "vue3--typeScript-第三种方式",
                                                    //     desc: "ts类型第三种方式",
                                                    //     year: 2022,
                                                    // }) as Book;
                                                
                                                    return {
                                                        // ...toRefs(book1),
                                                        // book2,
                                                        // book3,
                                                        // year1,
                                                    };
                                                },
                                                computed: {
                                                    // 返回值类型为String
                                                    allTitle(): String {
                                                        return `欢迎语 : ${this.book.title}`
                                                    }
                                                },
                                                methods: {
                                                    // 入参为String 返回空值
                                                    setTitle(arg: String): void {
                                                        this.book.title = arg;
                                                        this.book.year = 2022
                                                        this.book.author = '尤雨溪'
                                                    }
                                                }
                                            })
                                            </script>

                                            状态管理Pinia

                                            由于本文章篇幅较大,会在之后的文章中单独来讲解。

                                            到此这篇关于vue3基础知识剖析的文章就介绍到这了,更多相关vue3基础知识内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!

暂时禁止评论

微信扫一扫

易采站长站微信账号