Bootstrap

vue3项目全家桶知识

目录

一:vue3全家桶

二:环境搭建

三:vue3中的SFC变化

四:两种语法规范

五: vue3最重要的特性

六:组合API详解

七:vue3新语法细节



一:vue3全家桶

  1. Vue3:https://vuejs.org/
  2. VueRouter(V4):https://router.vuejs.org/
  3. Pinia(V2):https://pinia.vuejs.org/
  4. Vite构建工具:https://vitejs.dev/
  5. ElementPlus:https://element-plus.gitee.io/zh-CN/
  6. Vant(v3):https://vant-contrib.gitee.io/vant/#/zh-CN
  7. 技术栈:Vue3+VueRouter4+Pinia2+Vant3/ElementPlus

二:环境搭建

  1. @vue/cli
  2. vite(推荐)
  3. vue3在vscode中的插件
    1. volar插件

三:vue3中的SFC变化

  1. 支持多个style标签
  2. 支持多个script标签
  3. template支持多个根节点

四:两种语法规范

  1. 选项式
    1. 向下兼容,完全支持vue2的写法
    2. 可以使用setup()+选项式的写法,也可以完全使用setup()选项式写法
    3. <script lang="ts">
        import { ref } from "vue";
        export default {
          setup(props, context){
            console.log('---props',props)
            console.log('---context',context)
            const num = ref(1000)
            const add = ()=>{
              num.value++
            }
            return {
              num,
              add
            }
          },
          methods: {
            sub () {
              this.num--
            }
          }
        }
      </script>
      
      <template>
        <h1 v-text="num"></h1>
        <button @click="add">自增</button>
        <button @click="sub">自减</button>
      </template>

  2. 组合式(推荐写法)在<script setup>,只支持组合式写法,规避选项式写法。
    1. <script setup lang="ts">
        import {ref} from 'vue'
        const num = ref(1000)
        const add = ()=>{
          num.value++
        }
        const sub = ()=>{
          num.value--
        }
      </script>
      <template>
        <h1 v-text="num"></h1>
        <button @click="add">自增</button>
        <button @click="sub">自减</button>
      </template>

五: vue3最重要的特性

  1. 开发思想的变化:Vue3通过使用组合API,可以方便地封装Hooks,分离组件中的“逻辑关注点”。

  2. 具体怎么实践这种思想?第一步,用组合API替换掉传统的选项写法;第二步,梳理逻辑关注点封装自定义Hooks。

  3. 自定义Hooks有几个需要注意的问题:自定义Hooks一定要以use*开头,自定义Hooks可以被复用,自定义Hooks不要过度与泛滥。

  4. 思考:组件封装 与 Hooks封装,有什么本质的区别?前者是视图结构的封装,后者是逻辑功能的封装。

  5. <template>
        <span v-text="num"></span>
        <button @click="add">自增</button>
        <button @click="sub">自减</button>
    </template>
    
    <script lang="ts" setup>
    import useNum from './hooks/useNum'
    const {num,add,sub} = useNum()
    </script>
    import {ref} from 'vue'
    function useNum(){
        const num = ref(1)
        const add = ()=>{
            num.value++
        }
        const sub = ()=>{
            num.value--
        }
        return {
            num,
            add,
            sub,
        }
    }
    
    export default useNum

六:组合API详解

  1. ref:用于定义声明式变量(等同于data中的变量)(用于定义基本数据类型)
    1. ​
      <template>
          <span v-text="num"></span>
      </template>
      <script lang="ts" setup>
          import {ref} from 'vue'
          const num = ref(100)
          //num 是一个对象。
              // 1:在逻辑中使用时取值需要用num.value
              // 2:在视图中直接使用num即可
          console.log(num)
          // RefImpl {__v_isShallow: false, dep: undefined, __v_isRef: true, _rawValue: 100, _value: 100}
          // dep: Set(1) {ReactiveEffect}
          // __v_isRef: true
          // __v_isShallow: false
          // _rawValue: 100
          // _value: 100
          // value: 100
          // [[Prototype]]: Object
      </script>
      
      ​
  2. reactive:定义响应式变量,一般用于引用数据类型
    1. <template>
          <span v-for="i in  list" :key="i" v-text="i"></span>
      </template>
      
      <script lang="ts" setup>
          import {reactive} from 'vue'
          const list = reactive([1,2,3,4,5])
      </script>

  3. isRef:判断一个变量是否为ref对象
    1. <template>
      </template>
      <script lang="ts" setup>
      import {isRef,ref,reactive} from 'vue'
      
      const num = ref(100)
      const arr = reactive([1,2,3])
      
      console.log(isRef(num))//true
      console.log(isRef(arr))//false
      </script>

  4. unref:返回一个值,如果访问的是ref变量,则返回ref变量的value值,如果不是ref变量则直接返回这个变量
    1. <template>
      </template>
      <script lang="ts" setup>
      import {ref,unref,reactive} from 'vue'
      
          const num = ref(1000)
          const arr = reactive([1,2,3,4,5])
          const obj = {name:'chy',age:80}
      
          const a = unref(num)
          const b = unref(arr)
          const c = unref(obj)
      
          console.log(a,b,c)  
          //1000 
          // Proxy {0: 1, 1: 2, 2: 3, 3: 4, 4: 5} 
          // {name: 'chy', age: 80}
      
      </script>

  5. toRef:把reactive对象中的某个变量变成ref变量
    1. <template>
      
      </template>
      <script lang="ts" setup>
          import {isRef,reactive,toRef} from 'vue'
      
          const obj = reactive({name:'chy',age:19})
      
          const name = toRef(obj.name)
      
          console.log(isRef(name))//true
      </script>

  6. toRefs:把reactive对象变成一个ref对象
    1. 应用:在子组件中接收父组件传递过来的 props时,我们解构其中的某项就失去了响应式,这时候我们需要使用 toRefs把它变成响应式的。
    2. <template>
      </template>
      
      <script lang="ts" setup>
          import { ToRefs,reactive,isRef, toRefs } from 'vue';
          const arr = reactive([1,2,3,4])
          const refvalue = toRefs(arr)
          
          console.log(isRef(refvalue))//false
      </script>

  7. shallowRef:对复杂层级的对象,只将其第一层变成 ref 响应。 (性能优化)
    1. <template>
          <span v-text="obj"></span>
      </template>
      <script lang="ts" setup>
          import {ref,shallowRef,isRef} from 'vue'
      
          const cc = {a:{c:{d:8}},z:3}
          const obj = shallowRef(cc) //cc就是第一层
      
          console.log(isRef(obj)) //true
          console.log(isRef(obj.value.a.c.d)) //false
      
      
      </script>

  8. triggerRef:强制更新一个 shallowRef对象的渲染。
    1. <template>
          <span v-text="obj.z" @click="change"></span>
      </template>
      <script lang="ts" setup>
          import {ref,shallowRef,isRef,triggerRef} from 'vue'
      
          const cc = {a:{c:{d:8}},z:3}
          const obj = shallowRef(cc) //cc就是第一层
      
          console.log(isRef(obj)) //true
          console.log(isRef(obj.value.a.c.d)) //false
      
          const change = ()=>{
              console.log(2222)
              obj.value.z++
              triggerRef(obj)
          }
      
      </script>

  9. readonly:把一个对象变成只读
    1. 语法:const rs = readonly(ref对象 | reactive对象 | 普通对象)
  10. isReadonly:判断一个变量是不是只读的
    1. const bol = isReadonly(变量)
  11. isReactive:判断一个变量是否为reactived的
    1. 被 readonly代理过的 reactive变量,调用 isReactive 也是返回 true的。
  12. isProxy:判断一个变量是不是 readonly 或 reactive的。
  13. toRaw:得到返回 reactive变量或 readonly变量的"原始对象"。
    1. <template>
      </template>
      <script lang="ts" setup>
          import {reactive, toRaw, readonly } from 'vue';
      
          const arr = reactive([1,2,3,4,5])
          const obj =  readonly({name:'chy',age:19})
      
          console.log(toRaw(arr))
          // Array(5)
          // 0: 1
          // 1: 2
          // 2: 3
          // 3: 4
          // 4: 5
          // length: 5
          console.log(toRaw(obj))
          // Object
          // age: 19
          // name: "chy"
      
      </script>

  14. markRaw:把一个普通对象标记成"永久原始",从此将无法再变成proxy了。
    1. 语法:const raw = markRaw({a,b})
  15. shallowReactive:定义一个reactive变量,只对它的第一层进行Proxy,,所以只有第一层变化时视图才更新
    1. <template>
      </template>
      <script lang="ts" setup>
          import { reactive, shallowReactive,isReactive } from 'vue';
      
          const obj = shallowReactive(reactive({name:'chy',age:{chy:30}}))
          
          console.log(obj)
          console.log(isReactive(obj))//true
          console.log(isReactive(obj.name))//false
      </script>

  16. shallowReadonly:定义一个reactive变量,只有第一层是只读的
  17. computed:对响应式变量进行缓存计算。
    1. 语法:const c = computed(fn / {get,set})
  18. watch:用于监听响应式变量的变化,组件初始化时,它不执行。
    1. 语法:const stop = watch(x, (new,old)=>{}),调用stop() 可以停止监听。
    2. 语法:const stop = watch([x,y], ([newX,newY],[oldX,oldY])=>{}),调用stop()可以停止监听。
    3. <template>
            <div v-text="num1" @click="add1"></div>
            <div v-text="num2" @click="add2"></div>
              <div @click="click">点我停止监听</div>
      </template>
      <script lang="ts" setup>
          import { watch, ref } from 'vue';
          
          const num1 = ref(1)
          const num2 = ref(2)
          const add1 = ()=>{
              num1.value ++
          }
          const add2 = ()=>{
              num2.value++
          }
      
          const stop = watch([num1,num2],([a,b],[c,d])=>{
              console.log(a,b)
              console.log('------------------')
              console.log(c,d)
          })
      
          const click = ()=>{
              stop()
          }
      </script>

  19. watchEffect:相当于是 react中的 useEffect(),用于执行各种副作用。
    1. const stop = watchEffect(fn),默认其 flush:'pre',前置执行的副作用。
    2. watchPostEffect,等价于 watchEffect(fn, {flush:'post'}),后置执行的副作用。
    3. watchSyncEffect,等价于 watchEffect(fn, {flush:'sync'}),同步执行的副作用。
    4. 特点:watchEffect 会自动收集其内部响应式依赖,当响应式依赖发变化时,这个watchEffect将再次执行,直到你手动 stop() 掉它。
  20. 生命周期钩子
    1. 选项式的 beforeCreate、created,被setup替代了。setup表示组件被创建之前、props被解析之后执行,它是组合式 API 的入口。
    2. 选项式的 beforeDestroy、destroyed 被更名为 beforeUnmount、unmounted。
    3. 新增了两个选项式的生命周期 renderTracked、renderTriggered,它们只在开发环境有用,常用于调试。
    4. 在使用 setup组合时,不建议使用选项式的生命周期,建议使用 on* 系列 hooks生命周期。
    5. <template>
      </template>
      
      <script lang="ts" setup>
      import {
          onBeforeMount,
          onMounted,
          onBeforeUpdate,
          onUpdated,
          onBeforeUnmount,
          onUnmounted,
          onActivated,
          onDeactivated,
          onErrorCaptured,
          onRenderTracked,
          onRenderTriggered
      } from 'vue'
      
       // 挂载阶段
        onBeforeMount(()=>console.log('---开始挂载'))
        onRenderTracked(()=>console.log('---跟踪'))
        onMounted(()=>console.log('---挂载完成'))
      
        // 更新阶段
        onRenderTriggered(()=>console.log('---触发'))
        onBeforeUpdate(()=>console.log('---开始更新'))
        onUpdated(()=>console.log('---更新完成'))
      
        // 销毁阶段
        onBeforeUnmount(()=>console.log('---开始销毁'))
        onUnmounted(()=>console.log('---销毁完成'))
      
        // 与动态组件有关
        onActivated(()=>console.log('---激活'))
        onDeactivated(()=>console.log('---休眠'))
        
        // 异常捕获
        onErrorCaptured(()=>console.log('---错误捕获'))
      </script>

  21. provide / inject:在组件树中自上而下地传递数据.
    1. provide('key', value)
    2. const value = inject('key', '默认值')
  22. 关于setup代码范式(最佳实践)
    1. 只使用 setup 及组合API,不要再使用vue选项了。
    2. 有必要封装 hooks时,建议把功能封装成hooks,以便于代码的可维护性
    3. 能用 vite就尽量使用vite,能用ts 就尽量使用ts。

七:vue3新语法细节

  1. 在Vue2中,v-for 和 ref 同时使用,这会自动收集 $refs。当存在嵌套的v-for时,这种行为会变得不明确且效率低下。在Vue3中,v-for 和 ref 同时使用,这不再自动收集$refs。我们可以手动封装收集 ref 对象的方法,将其绑定在 ref 属性上。
    1. <template>
          <div v-for="i in 6" :key="i" :ref="setref">
              <span v-for="i in 6" :key="i" :ref="setref" v-text="i"></span>
          </div>
      </template>
      <script lang="ts" setup>
          let arr = []
      
          const setref = (ev)=>{
              arr.push(ev)
          }
          console.log(arr)
      
      </script>

  2. 在Vue3中,使用 defineAsyncComponent 可以异步地加载组件。需要注意的是,这种异步组件是不能用在Vue-Router的路由懒加载中。

    1. <template>
      </template>
      <script lang="ts" setup>
        import {defineAsyncComponent} from 'vue'
        
        const asyncComponent = defineAsyncComponent({
          loader:()=>import('./pages/pageA.vue') ,
          delay:200,//延迟200毫秒
          timeout:3000//超出3000毫秒报错 
        })
      </script>

  3. Vue3.0中的 $attrs,包含了父组件传递过来的所有属性,包括 class 和 style 。在Vue2中,$attrs 是接到不到 class 和 style 的。在 setup 组件中,使用 useAttrs() 访问;在非 setup组件中,使用 this.$attrs /setupCtx.attrs 来访问。

    1. <template>
      </template>
      <script lang="ts" setup>
        import { useAttrs } from 'vue';
      
        const option = useAttrs()
        console.log(option)
      </script>

  4. Vue3中,移除了 $children 属性,要想访问子组件只能使用 ref 来实现了。在Vue2中,我们使用 $children 可以方便地访问到子组件,在组件树中“肆意”穿梭。

  5. Vue3中,使用 app.directive() 来定义全局指令,并且定义指令时的钩子函数们也发生了若干变化。

    1. app.directive('highlight', {
        // v3中新增的
        created() {},
        // 相当于v2中的 bind()
        beforeMount(el, binding, vnode, prevVnode) {
          el.style.background = binding.value
        },
        // 相当于v2中的 inserted()
        mounted() {},
        // v3中新增的
        beforeUpdate() {},
        // 相当于v2中的 update()+componentUpdated()
        updated() {},
        // v3中新增的
        beforeUnmount() {},
        // 相当于v2中的 unbind()
        unmounted() {}
      })

  6. Vue3中新增了 emits 选项。在非<script setup>写法中,使用 emits选项 接收父组件传递过来的自定义,使用 ctx.emit() / this.$emit() 来触发事件。在<script setup>中,使用 defineEmits 来接收自定义事件,使用 defineProps 来接收自定义事件。

    1.   import { defineEmits, defineProps} from 'vue';
      
        //接受父组件传过来的数据
        const props = defineProps({
          value:{type:String,default:''}
        })
        // const emit = defineEmits(['自定义事件1','自定义事件2',...])
        const emit = defineEmits(['自定义事件1','自定义事件2'])
      <template>
        <child :num="num" @change="num = $event"></child>
      </template>
      <script lang="ts" setup>
        import {ref} from 'vue'
        import child from './components/child.vue'
      
        const num = ref(1000)
      </script>
      <template>
          <span v-text="num" @click="emit('change',dev)"></span>
      </template>
      <script lang="ts" setup>
        import { defineEmits, defineProps,ref} from 'vue';
        
        //父传子
        const props = defineProps({
          num:{type:Number,default:0}
        })
      
          //子传父
        const emit = defineEmits(['change'])
        console.log(emit)
      //$emit('change',dev)或者emit('change',dev)
      
        const dev = ref(10)
      </script>

  7. Vue3中 移除了 $on / $off / $once 这三个事件 API,只保留了 $emit 。

  8. Vue3中,移除了全局过滤器(Vue.filter)、移除了局部过滤器 filters选项。取而代之,你可以封装自定义函数或使用 computed 计算属性来处理数据。

  9. Vue3 现在正式支持了多根节点的组件,也就是片段,类似 React 中的 Fragment。使用片段的好处是,当我们要在 template 中添加多个节点时,没必要在外层套一个 div 了,套一层 div 这会导致多了一层 DOM结构。可见,片段 可以减少没有必要的 DOM 嵌套。

  10. 我们已经知道,使用 provide 和 inject 这两个组合 API 可以组件树中传递数据。除此之外,我们还可以应用级别的 app.provide() 来注入全局数据。在编写插件时使用 app.provide() 尤其有用,可以替代app.config.globalProperties。

  11. 在Vue2中,Vue.nextTick() / this.$nextTick 不能支持 Webpack 的 Tree-Shaking 功能的。在 Vue3 中的 nextTick ,考虑到了对 Tree-Shaking 的支持。

  12. Vue3中,对于 v-if/v-else/v-else-if的各分支项,无须再手动绑定 key了, Vue3会自动生成唯一的key。因此,在使用过渡动画 <transition>对多个节点进行显示隐藏时,也无须手动加 key了。

  13. Vue3中,$listeners 被移除了。因此我们无法再使用 $listeners 来访问、调用父组件给的自定义事件了。

  14. Vue2中,根组件挂载 DOM时,可以使用 el 选项、也可以使用 $mount()。但,在 Vue3中只能使用 $mount() 来挂载了。并且,在 Vue 3中,被渲染的应用会作为子元素插入到 <div id='app'> 中,进而替换掉它的innerHTML。

  15. 在Vue2中,组件有一个 render 选项(它本质上是一个渲染函数,这个渲染函数的形参是 h 函数),h 函数相当于 React 中的 createElement()。在Vue3中,render 函数选项发生了变化:它的形参不再是 h 函数了。h 函数变成了一个全局 API,须导入后才能使用。

  16. Vue3中新增了实验性的内置组件 <suspense>,它类似 React.Suspense 一样,用于给异步组件加载时,指定 Loading指示器。需要注意的是,这个新特征尚未正式发布,其 API 可能随时会发生变动。

  17. Vue3中,过渡动画<transition>发生了一系列变化。之前的 v-enter 变成了现在的 v-enter-from , 之前的 v-leave 变成了现在的 v-leave-from 。另一个变化是:当使用<transition>作为根结点的组件,从外部被切换时将不再触发过渡效果。

  18. 同一节点上使用 v-for 和 v-if ,在Vue2中不推荐这么用,且v-for优先级更高。在Vue3中,这种写法是允许的,但 v-if 的优秀级更高。

  19. 在Vue2中,静态属性和动态属性同时使用时,不确定最终哪个起作用。在Vue3中,这是可以确定的,当动态属性使用 :title 方式绑定时,谁在前面谁起作用;当动态属性使用 v-bind='object'方式绑定时,谁在后面谁起作用。

  20. 当使用watch选项侦听数组时,只有在数组被替换时才会触发回调。换句话说,在数组被改变时侦听回调将不再被触发。要想在数组被改变时触发侦听回调,必须指定deep选项。

    1. <template>
        <div v-for='t in list' v-text='t.task'></div>
        <button @click.once='addTask'>添加任务</button>
      </template>
      
      <script setup>
        import { reactive, watch } from 'vue'
        const list = reactive([
          { id:1, task:'读书', value:'book' },
          { id:2, task:'跑步', value:'running'}
        ])
        const addTask = () => {
          list.push({ id:3, task:'学习', value:'study' })
        }
        // 当无法监听一个引用类型的变量时
        // 添加第三个选项参数 { deep:true }  
        watch(list, ()=>{
          console.log('list changed', list)
        }, { deep:true })
      </script>

  21. Vue3中,新增了 <teleport>组件,这相当于 ReactDOM.createPortal(),它的作用是把指定的元素或组件渲染到任意父级作用域的其它DOM节点上。上面第 16个知识点中,用到了 <teleport> 加载 animate.css 样式表,这算是一种应用场景。

    1. <template>
        <!-- 当Modal弹框显示时,将其插入到<body>标签中去 -->
        <teleport to='body'>
          <div
            class='layer' v-if='visibled'
            @click.self='cancel'
          >
            <div class='modal'>
              <header></header>
              <main><slot></slot></main>
              <footer></footer>
            </div>
          </div>
        </teleport>
      </template>
      
      <script setup>
        import { defineProps, defineEmits, toRefs } from 'vue'
        const props = defineProps({
          visibled: { type: Boolean, default: false }
        })
        const emit = defineEmits(['cancel'])
        const cancel = () => emit('cancel')
      </script>
      
      <style lang="scss">
      .layer {
        position:fixed; bottom: 0;
        top: 0; right: 0; left: 0;
        background-color: rgba(0,0,0,0.7);
        .modal {
          width: 520px; position: absolute;
          top: 100px; left: 50%; margin-left: -260px;
          box-sizing: border-box; padding: 20px;
          border-radius: 3px; background-color: white;
        }
      }
      </style>
      <template>
        <Modal :visibled='show' @cancel='show=!show'>
          <div>弹框主体内容</div>
        </Modal>
        <button @click='show=!show'>打开弹框</button>
      </template>
      
      <script setup>
        import { ref, watch } from 'vue'
        import Modal from './components/Modal.vue'
        const show = ref(false)
      </script>

  22. 在Vue3中,移除了 model 选项,移除了 v-bind 指令的 .sync 修饰符。在Vue2中,v-model 等价于 :value + @input ;在Vue3中,v-model 等价于 :modelValue + @update:modelValue 。在Vue3中,同一个组件上可以同时使用多个 v-model。在Vue3中,还可以自定义 v-model 的修饰符。

         

;