工具 vue3 Vue3+Ts 定西 2024-02-06 2025-06-02 创建vue3项目 不适用vue-cli了,直接上vite
npm create vue@latest
然后选择一些配置项就行了,ts就ts呗
Vue3核心语法 setup setup是vue3中一个新的配置项,值是一个函数,它是Composition Api表演的舞台,组件中所用到的:数据,方法,计算属性,监视等等,均配置在setup中
setup中无法使用this,是undefined 
 
想要只是用一个script,但又想自定义组件名,可以使用这个插件
 
1 npm i vite-plugin-vue-setup-extend -D 
 
然后在vite.config.ts中引入
1 2 3 4 import  VueSetupExtend  from  'vite-plugin-vue-setup-extend'   plugins : [     VueSetupExtend (),   ], 
 
 
setup基本使用 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <template >     <div  class ="person" >          <h2 > 姓名:{{ name }}</h2 >          <h2 > 年龄:{{ age }}</h2 >          <button  @click ="changeName" > 修改名字</button >          <button  @click ="changeAge" > 修改年龄</button >          <button  @click ="showTel" > 查看联系方式</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person1" > let  name = '张三'   let  age = 18 let  tel = '12321312' function  changeName ( ) {    name = 'zhangsan'  } function  changeAge ( ) {    age += 1  } function  showTel ( ) {    alert (tel) } </script > <style  scoped > .person  {    background-color : blue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
ref 使用ref创建基本类型的响应式数据 
上面写的数据都不是响应式的,更改了数据,界面也不会变化,使用ref可以创建响应式的数据
使用时,要注意:
插值语法中不写.value 
函数中使用必须写.value 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import  { ref } from  'vue' let  name = ref ('张三' )let  age = ref (18 )function  changeName ( ) {    name.value  = 'zhangsan'  } function  changeAge ( ) {    age.value  += 1  } 
 
 
ref定义引用类型的数据 
实际上ref中的value还是Proxy,也就是说还是ractive
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 <template >     <div  class ="person" >          <h2 > 一辆{{ car.brand }}值{{ car.price }}</h2 >          <button  @click ="changePrice" > 修改汽车价格</button >          <hr >          <h2 > 游戏列表</h2 >          <ul >              <li  v-for ="g in games"  :key ="g.id" > {{ g.name }}</li >          </ul >          <button  @click ="changeFirstGame" > 修改第一个游戏</button >          <hr >          <h2 > {{ obj.a }}</h2 >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { ref,reactive } from  'vue' let  car = ref ({    brand : '奔驰' ,     price : 100  }) let  games = ref ([    { id : 'dasfs1' , name : '元神'  },     { id : 'dasfs2' , name : '源神'  },     { id : 'dasfs3' , name : '猿神'  } ]) let  obj = reactive ({    a : 999  }) console .log (car) console .log (obj) function  changePrice ( ) {    car.value .price  += 10      console .log (car.value .price ) } function  changeFirstGame ( ) {    games.value [0 ].name  = '原神'      console .log (games) } </script > <style  scoped > .person  {    background-color : blue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
reactive 使用ref创建引用数据类型的响应式数据 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <template >     <div  class ="person" >          <h2 > 一辆{{ car.brand }}值{{ car.price }}</h2 >          <button  @click ="changePrice" > 修改汽车价格</button >          <hr >          <h2 > 游戏列表</h2 >          <ul >              <li  v-for ="g in games"  :key ="g.id" > {{ g.name }}</li >          </ul >          <button  @click ="changeFirstGame" > 修改第一个游戏</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { reactive } from  'vue' let  car = reactive ({    brand : '奔驰' ,     price : 100  }) let  games = reactive ([    { id : 'dasfs1' , name : '元神'  },     { id : 'dasfs2' , name : '源神'  },     { id : 'dasfs3' , name : '猿神'  } ]) function  changePrice ( ) {    car.price  += 10      console .log (car.price ) } function  changeFirstGame ( ) {    games[0 ].name  = '原神'      console .log (games) } </script > <style  scoped > .person  {    background-color : blue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
ref对比reactive 
ref用来定义:基本数据类型,引用数据类型 
reactive用来定义:引用数据类型 
 
区别 
ref创建的变量必须使用.value(可以使用volar创建自动添加.value) 
reactive重新分配一个新的对象,会失去 响应式(可以使用Object.assign去整体替换) 
 
使用原则 
若需要一个基本类型的响应式数据,必须使用ref 
若需要一个响应式对象,层级不深,ref,reactive都可以 
若需要一个响应式对象,且层级较深,推荐使用reactive 
 
**注意Object.assign(car,{ brand: ‘bwm’, price: 1 })**的用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 <template >     <div  class ="person" >          <h2 > 一辆{{ car.brand }}值{{ car.price }}</h2 >          <button  @click ="changePrice" > 修改汽车价格</button >          <button  @click ="changeCar" > 修改汽车</button >          <hr >          <h2 > 当前求和为:{{ sum }}</h2 >          <button  @click ="changeSum" > 点我sum+1</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { ref, reactive } from  'vue' let  car = reactive ({    brand : '奔驰' ,     price : 100  }) let  sum = ref (0 )console .log (car) function  changeCar ( ) {              Object .assign (car,{ brand : 'bwm' , price : 1  })      } function  changePrice ( ) {    car.price  += 10      console .log (car.price ) } function  changeSum ( ) {    sum.value  += 1  } </script > <style  scoped > .person  {    background-color : blue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
toRefs 将reactive创建的对象解构出,并且关联原对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <template >     <div  class ="person" >          <h2 > 姓名{{ person.name }}</h2 >          <h2 > 年龄{{ age }}</h2 >          <button  @click ="changeName" > 修改姓名</button >          <button  @click ="changeAge" > 修改年龄</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { ref, reactive, toRefs } from  'vue' let  person = reactive ({    name : '张三' ,     age : 18  }) let  { name, age } = toRefs (person)console .log (name, age)function  changeAge ( ) {    age.value  += 1  } function  changeName ( ) {    name.value  += '~'  } </script > <style  scoped > .person  {    background-color : blue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
计算属性 写法还是有两种,一种是箭头函数,但是相当于只读,只有get,写上set,就可以读写了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <template >     <div  class ="person" >          姓: <input  type ="text"  v-model ="firstName" >  <br >          名: <input  type ="text"  v-model ="lastName" >  <br >          全名: <span > {{ fullName }}</span >          <button  @click ="changeFullName" > 将全民改为li-si</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { ref, computed } from  'vue' let  firstName = ref ('张' )let  lastName = ref ('三' )let  fullName = computed ({    get ( ) {         return  firstName.value .slice (0 , 1 ).toUpperCase () + firstName.value .slice (1 ) + '-'  + lastName.value      },     set (val ) {                                                      const  [str1,str2] = val.split ('-' )         firstName.value  = str1         lastName.value  = str2         console .log ('set' , val)     } }) function  changeFullName ( ) {    fullName.value  = 'li-si'  } </script > <style  scoped > .person  {    background-color : skyblue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
watch vue3中的watch只能监视以下四种数据
ref定义的数据 
reactive定义的数据 
函数返回一个值 
一个包含上述内容的数组 
 
情况一 监视ref定义的基本类型数据:直接写数据名即可,监视的是其value值的改变
1 2 3 4 watch (sum, (newValue, oldValue ) =>  {    console .log ("sum变化了" )     console .log (newValue, oldValue) }) 
 
停止监视:
1 2 3 4 5 6 7 8 const  stopWatch = watch (sum, (newValue, oldValue ) =>  {    console .log ("sum变化了" )     console .log (newValue, oldValue)          if  (newValue >= 10 ) {         stopWatch ()     } }) 
 
情况二 监视ref定义的对象类型的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 watch (person, (newValue, oldValue ) =>  {    console .log ('person变化了' )     console .log (newValue, oldValue) }, { deep : true  }) 
 
情况三 监视reactive定义的对象类型的数据,且默认开启了深度监视
该深度监视无法关闭 
1 2 3 4 5 watch (person, (newValue ) =>  {    console .log ('person变化了' )     console .log (newValue) }) 
 
情况四 监视ref或reactive定义的对象类型数据的某个属性,注意点如下
若该属性不是对象类型,需要写成函数形式 
若该属性值是对象类型,可直接编,也可写成函数式,不过建议写成函数 
 
第一种:
 
1 2 3 4 watch (() =>  { return  person.name  }, (newValue ) =>  {    console .log ("person.name变化了" ) }) 
 
箭头函数如果只有return语句,那么花括号和return可以省略
1 2 3 watch (() =>  person.name , (newValue ) =>  {    console .log ("person.name变化了" ) }) 
 
第二种: 对象属性仍然是对象,那么可以直接写,也可以写成函数式
 
1 2 3 4 5 6 7 8 9 10 watch (person.car , (newValue ) =>  {    console .log ("person.car变化了" ) }) watch (() =>  person.car , (newValue ) =>  {    console .log ("person.car变化了" ) },{deep :true }) 
 
结论就是,如果监视的是对象的对象属性,那么最好写成函数式,需要深度监视就写上deep 
情况五 监视上述多个数据
就是直接拿数组包起来,然后看是写箭头函数还是写普通属性
1 2 3 watch ([() =>  person.name , () =>  person.car .c1 ], () =>  {    console .log ("person.name或person.car.c1变化了" ) }) 
 
 
总结  实际用到时情况一和情况四较常用,也就是基本类型和监视对象
watchEffect 当监视多个属性时,使用watch要写多个,是比较麻烦的
1 2 3 4 5 6 7 watch ([temp, height], (value ) =>  {    let  [newTemp, newHeight] = value     if  (newTemp >= 60  || newHeight >= 80 ) {         console .log ("发送请求" )     } }) 
 
使用watchEffect
相当于监听函数中用到的属性,变化时就会执行整个函数 
1 2 3 4 5 watchEffect (() =>  {    if  (temp.value  >= 60  || height.value  >= 80 ) {         console .log ("send" )     } }) 
 
标签的ref属性 可以使用在普通html标签上,或者dom元素,也可以使用在组件上,来获取组件实例,但是默认是拿不到组件内的实例数据的,可以在组件的最后使用defineExpose来暴露数据
Person.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <template >     <div  class ="person" >          <h1 > 中国</h1 >                   <h2  ref ="title2" > beijing</h2 >          <button  @click ="showLog" > 点我输出h2元素</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { ref } from  'vue' let  title2 = ref ()let  a = ref (2 )function  showLog ( ) {              console .log (title2.value ) } defineExpose ({ a })</script > <style  scoped > .person  {    background-color : skyblue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
App.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template >     <Person  ref ="ren" > </Person >      <button  @click ="showLog" > 测试</button >  </template > <script  lang ="ts"  setup  name ="App" > import  Person  from  './components/Person.vue' import  { ref } from  'vue' let  ren = ref ()function  showLog ( ) {    console .log (ren.value .a ) } </script > <style > </style > 
 
TS中的接口,自定义类型,泛型 注意写法 
ts文件:
1 2 3 4 5 6 7 8 9 10 11 12 export  interface  PersonInter  {    id : string ,     name : string ,     age : number ,     x?: number   } export  type  Persons  = Array <PersonInter >
 
使用时(导入时,如果ts的文件名是index.ts,那么就可以省去文件名,直接写到文件夹的名字即可 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <template >     <div  class ="person" >          ???     </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { type PersonInter , type Persons  } from  '@/types' let  person : PersonInter  = { id : '312231123' , name : '张三' , age : 60  }let  personList1 : Array <PersonInter > = [    { id : '312231131223' , name : '张三' , age : 60  },     { id : '31223112123' , name : '张1' , age : 61  },     { id : '312231213123' , name : '张2' , age : 62  },     { id : '312231113223' , name : '张3' , age : 63  } ] let  personList2 : Persons  = [    { id : '312231131223' , name : '张三' , age : 60  },     { id : '31223123' , name : '张1' , age : 61  },     { id : '312231213123' , name : '张2' , age : 62  },     { id : '312231113223' , name : '张3' , age : 63  } ] console .log (personList1)console .log (personList2)</script > <style  scoped > .person  {    background-color : skyblue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
 
涉及到响应式的数据,有别的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  { type Persons  } from  '@/types' let  personList : Persons  = reactive ([    { id : '1' , name : '张三' , age : 18  },     { id : '2' , name : '里斯' , age : 19  },     { id : '3' , name : '王五' , age : 20  } ]) let  personList = reactive<Persons >([    { id : '1' , name : '张三' , age : 18  },     { id : '2' , name : '里斯' , age : 19  },     { id : '3' , name : '王五' , age : 20  } ]) 
 
props props的几种写法 父组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template >     <Person  :list ="personList"  />  </template > <script  lang ="ts"  setup  name ="App" > import  Person  from  './components/Person.vue' import  { reactive } from  'vue' import  { type Persons  } from  '@/types' let  personList = reactive<Persons >([    { id : '1' , name : '张三' , age : 18  },     { id : '2' , name : '里斯' , age : 19  },     { id : '3' , name : '王五' , age : 20  } ]) console .log (personList)</script > <style > </style > 
 
子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <template >     <div  class ="person" >          <ul >              <li  v-for ="l in list"  :key ="l.id" > 姓名:{{ l.name }},年龄:{{ l.age }},</li >          </ul >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { type Persons  } from  '@/types' import  { defineProps, withDefaults } from  'vue' withDefaults (defineProps<{ list?: Persons  }>(), {    list : () =>  [{ id : '1' , name : '涨啊' , age : 1  }] }) </script > <style  scoped > .person  {    background-color : skyblue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
生命周期 分为4种,创建,挂载,更新,卸载,而创建,就是setup
父子组件的挂载顺序: 子先挂载,父后挂载 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 <template >     <div  class ="person" >          <h2 > 当前求和为:{{ sum }}</h2 >          <button  @click ="add" > 点击加一</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  { onBeforeMount, onBeforeUnmount, onBeforeUpdate, onMounted, onUnmounted, onUpdated, ref } from  'vue' let  sum = ref (0 )function  add ( ) {    sum.value  += 1  } onBeforeMount (() =>  {    console .log ('挂载前' ) }) onMounted (() =>  {    console .log ('挂载完毕' ) }) onBeforeUpdate (() =>  {    console .log ('更新前' ) }) onUpdated (() =>  {    console .log ('更新完毕' ) }) onBeforeUnmount (()=> {    console .log ('卸载前' ) }) onUnmounted (()=> {    console .log ('卸载完毕' ) }) </script > <style  scoped > .person  {    background-color : skyblue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
常用的钩子 
挂载完毕 
更新完毕 
卸载之前 
 
hooks 所以你明白了vue3的组合式api有什么用了吧,把一个功能的数据和方法甚至钩子都能写到一个ts文件中
注意hooks的命名,以及hooks里面要写上一个函数,并返回数据和函数之类的东西 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <template >     <div  class ="person" >          <h2 > 当前求和为:{{ sum }}</h2 >          <h2 > 放大:{{ bigSum }}</h2 >          <button  @click ="add" > 点我sum+1</button >          <hr >          <img  v-for ="dog in dogList"  :src ="dog" >          <button  @click ="getDog" > 再来一只狗</button >      </div >  </template > <script  lang ="ts"  setup  name ="Person" > import  useSum from  '@/hooks/useSum' import  useDog from  '@/hooks/useDog' const  { sum, add, bigSum } = useSum ()const  { dogList, getDog } = useDog ()</script > <style  scoped > img  {    height : 100px ;     margin-right : 10px ; } .person  {    background-color : skyblue;     box-shadow : 0  0  10px ;     padding : 20px ; } button  {    margin : 0  5px ; } </style > 
 
两个use
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  { computed, onMounted, ref } from  'vue' export  default  function  ( ) {         let  sum = ref (0 )     let  bigSum = computed (() =>  {         return  sum.value  * 10      })     onMounted (() =>  {         add ()     })     function  add ( ) {         sum.value  += 1      }          return  { sum, add, bigSum } } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import  { onMounted, reactive } from  'vue' import  axios from  'axios' export  default  function  ( ) {    let  dogList = reactive ([         'https://images.dog.ceo/breeds/pembroke/n02113023_6140.jpg'      ])     async  function  getDog ( ) {         try  {             let  result = await  axios.get ('https://dog.ceo/api/breed/pembroke/images/random' )             dogList.push (result.data .message )         } catch  (error) {             alert (error)         }     }          onMounted (() =>  {         getDog ()     })          return  { dogList, getDog } } 
 
路由 基本使用 路由的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import  { createRouter, createWebHistory } from  "vue-router" import  Home  from  "@/components/Home.vue" import  News  from  "@/components/News.vue" import  About  from  "@/components/About.vue" const  router = createRouter ({    history : createWebHistory (),      routes : [         {             path : '/home' ,             component : Home          },         {             path : '/news' ,             component : News          },         {             path : '/about' ,             component : About          }     ] }) export  default  router
 
然后去main.ts中要引入
1 2 3 import  router from  './router' createApp (App ).use (router).mount ('#app' )
 
使用RouterView和RouterLink来实现路由跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template >     <div  class ="app" >          <h2 > vue 路由测试</h2 >                   <div  class ="navigate" >              <RouterLink  active-class ="active"  to ="/home" > 首页</RouterLink >              <RouterLink  active-class ="active"  to ="/news" > 新闻</RouterLink >              <RouterLink  active-class ="active"  to ="/about" > 关于</RouterLink >          </div >                   <div  class ="main-content" >              <RouterView > </RouterView >          </div >      </div >  </template > <script  lang ="ts"  setup  name ="App" > import  { RouterView ,RouterLink  } from  "vue-router" ;</script > 
 
 
两个注意点 
路由组件通常存放到pages或views文件夹下,一般组件通常存放在components文件夹 
通过点击导航,视觉效果上消失了的路由组件,默认是被卸载的,需要的时候再去挂载 
 
to的两种写法 一种直接写路径,一种给to加上冒号,然后写对象
1 2 3 4 5 <div  class ="navigate" >     <RouterLink  active-class ="active"  to ="/home" > 首页</RouterLink >      <RouterLink  active-class ="active"  to ="/news" > 新闻</RouterLink >      <RouterLink  active-class ="active"  :to ="{ path: '/about' }" > 关于</RouterLink >  </div > 
 
命名路由 其实就是给路由的配置上起名字,然后跳转时可以使用对象形式指定路由名
1 2 3 4 5 {     name : 'zhuye' ,     path : '/home' ,     component : Home  }, 
 
1 2 3 4 5 <div  class ="navigate" >     <RouterLink  active-class ="active"  to ="/home" > 首页</RouterLink >      <RouterLink  active-class ="active"  :to ="{ name: 'xinwen' }" > 新闻</RouterLink >      <RouterLink  active-class ="active"  :to ="{ path: '/about' }" > 关于</RouterLink >  </div > 
 
路由嵌套 这个很久之前用到过,后来写课设好像就再没用到过了哈哈啊哈操,注意子路由不写斜杠 
1 2 3 4 5 6 7 8 9 10 11 12 {             name : 'xinwen' ,             path : '/news' ,             component : News ,             children : [                 {                                          path : 'detail' ,                     component : Detail                  }             ]         }, 
 
路由传参 query参数 两种写法,第一种拼接,太麻烦了,如果传参比较多,还是用对象形式比较好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <li  v-for ="news in newsList"  :key ="news.id" >                            <RouterLink  :to ="{           path: '/news/detail',         query: {           id: news.id,           title: news.title,           content: news.content,         }       }" >        {{ news.title }}       </RouterLink >      </li >  
 
接收时,要使用useRoute 
1 2 3 4 5 6 import  { toRefs } from  "vue" ;import  { useRoute } from  "vue-router" ;const  route = useRoute ()let  { query } = toRefs (route)
 
然后就可以使用query来获取数据了
1 2 3 4 5 <ul >     <li > 编号: {{ query.id }}</li >      <li > 标题: {{ query.title }}</li >      <li > 内容: {{ query.content }}</li >  </ul > 
 
params 这个东西说实话没有query好用,我还以为是请求体那种呢
用的时候要把参数拼接到路由配置文件中
注意事项:
如果想要传递params参数,只能使用name 
path中拼接参数 
无法传递对象和数组,只能传递基本类型 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 {             name : 'xinwen' ,             path : '/news' ,             component : News ,             children : [                 {                                          name : 'xiangqing' ,                                          path : 'detail/:id/:title/:content' ,                     component : Detail                  }             ]         }, 
 
传递时依然是两种写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <li  v-for ="news in  newsList "  :key ="news.id" >                            <RouterLink  :to ="{           // 如果想要传递params参数,只能使用name         // 且params不能传递对象和数组,只能传递基本类型         name: 'xiangqing',         params: {           id: news.id,           title: news.title,           content: news.content         }       }" > {{ news.title }}</RouterLink >     </li >  
 
接收和query类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <template >     <ul >          <li > 编号: {{ params.id }}</li >          <li > 标题: {{ params.title }}</li >          <li > 内容: {{ params.content }}</li >      </ul >  </template > <script  lang ="ts"  setup  name ="Detail" > import  { toRefs } from  "vue" ;import  { useRoute } from  "vue-router" ;const  route = useRoute ()let  {params} = toRefs (route)</script > <style > </style > 
 
路由props 现在的问题就是,接收的地方太麻烦了,props可以解决这个问题
可以实现直接使用,不显示接受 ,这个就相当于,在路由时,给路由组件传递了props
简单的使用
在路由的配置上加上props: true 
路由组件接收propsdefineProps(['id', 'title', 'content']) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {             name : 'xinwen' ,             path : '/news' ,             component : News ,             children : [                 {                                          name : 'xiangqing' ,                                          path : 'detail/:id/:title/:content' ,                     component : Detail ,                                          props : true                  }             ]         }, 
 
但是这种写法,只能和params配合,并不能使用query参数 
 
第二种写法:函数式写法,可自己决定将什么作为props给路由组件,主要用于query传递
注意props函数接收和返回的参数 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {             name : 'xinwen' ,             path : '/news' ,             component : News ,             children : [                 {                     name : 'xiangqing' ,                     path : 'detail' ,                     component : Detail ,                                          props (route ) {                         return  route.query                      }                 }             ]         }, 
 
接收时仍一样
 
还有一种写法,就是直接用props,相当于在路由组件上写props,适合自定义数据,不依赖query和params
1 2 3 4 props :{    a :100 ,     b :200  } 
 
replace 路由跳转默认是push,也就是带有历史记录的跳转
而replace,会清除之前的记录
1 <RouterLink  replace  active-class ="active"  to ="/home" > 首页</RouterLink > 
 
编程式路由导航 还是这个好用,甚至之前开发都几乎没用过标签来导航
用起来区别仅在于,不能使用this.router.push了,而要引入useRouter,然后获取到router来使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <template >   <ul >      <li  v-for ="news in  newsList "  :key ="news.id" >        <button  @click ="showNewsDetail(news)" > 查看新闻</button >                             <RouterLink  :to ="{           // 如果想要传递params参数,只能使用name         // 且params不能传递对象和数组,只能传递基本类型         name: 'xiangqing',         query: {           id: news.id,           title: news.title,           content: news.content         }       }" > {{ news.title }}</RouterLink >     </li >    </ul >       <div  class ="news-content" >      <RouterView > </RouterView >    </div >  </template > <script  lang ="ts"  setup  name ="News" > import  { reactive } from  "vue" import  { RouterView , RouterLink , useRouter } from  "vue-router" const  newsList = reactive ([  { id : '001' , title : '十种食物' , content : '你猜是什么'  },   { id : '002' , title : '如何一夜暴富' , content : '一步登天'  },   { id : '003' , title : '好消息' , content : '快过年了'  } ]) interface NewsInter  {   id : string,   title : string,   content : string } const  router = useRouter ()function  showNewsDetail (news: NewsInter ) {  router.push ({     path : '/news/detail' ,     query : {       id : news.id ,       title : news.title ,       content : news.content      }   }) } </script > <style > </style > 
 
重定向 这玩意没啥说的,不过我一般习惯直接将根目录设置为某个组件
1 2 3 4 {     path : '/' ,     redirect : '/home'  }, 
 
pinia 代替vue2中的vuex(vuex我都忘了怎么用了)
再说一下解构赋值 1 2 3 4 5 6 7 8 9 10 11 async  function  getLoveTalk ( ) {                   let  { data : { content : title }     } = await  axios.get ('https://api.uomg.com/api/rand.qinghua?format=json' )     let  obj = {         id : nanoid (), title     }     talkList.unshift (obj) } 
 
let { data: { content: title }就是解构出result中的data,再从data中解构出content,并命名为title
下面的obj就可以直接写title了(title:title替换成title)
搭建pinia环境 npm i pinia就安装好了
然后到main.ts中引入配置
1 2 3 4 5 6 7 8 9 import  { createPinia } from  'pinia' const  pinia = createPinia ()createApp (App ).use (pinia).mount ('#app' )
 
使用pinia 
首先在src目录下创建store目录
 
然后创建组件名小驼峰的ts文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import  { defineStore } from  "pinia" export  const  useTalkLoveStore = defineStore ('talkLove' , {         state ( ) {         return  {             talkList : [                 { id : '001' , title : '哈哈哈'  },                 { id : '001' , title : '嘿嘿嘿'  },                 { id : '001' , title : '呵呵呵'  },             ]         }     }, }) 
 
 
引入useTalkLoveStore
1 2 3 import  { useTalkLoveStore } from  '@/store/talkLove' const  talkLoveStore = useTalkLoveStore ()
 
 
此时,这个talkLoveStore中就可以直接取到里面的数据了:talkLoveStore.talkList
 
修改数据的方法 
就是直接修改 
使用$patch,适用于批量修改1 2 3 4 5 countStore.$patch({     address : '汉中' ,     nickName : '灼灼某人'  }) 
 
第三种,使用action(注意使用this来获取state中的数据)1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import  { defineStore } from  "pinia" export  const  useCountStore = defineStore ('count' , {         actions : {                  increment (n: any  ) {                          this .sum  += n         }     },     state ( ) {         return  {             sum : 1 ,             address : 'hanzhong' ,             nickName : 'zzmr'          }     }, }) 
 
 
然后在组件中直接使用useCountStore来调用increment即可
 
但是现在每次读取数据时,都要使用store.xx,不够优雅 
1 2 3 <h2 > 当前求和为:{{ countStore.sum }}</h2 > <h3 > 地址: {{ countStore.address }}</h3 > <h3 > 昵称: {{ countStore.nickName }}</h3 > 
 
所以可以使用解构赋值,但是直接解构会导致数据丢失响应式,但如果加上toRefs(),那又太多余了,因为只有store中的数据需要进行响应式,所以可以使用storeToRefs来只使store中的数据变成响应式.
1 2 3 4 5 6 import  { storeToRefs } from  'pinia' ;import  { useCountStore } from  '@/store/count' const  countStore = useCountStore ()let  { sum, address, nickName } = storeToRefs (countStore)
 
getters 对store中的数据进行处理后再返回 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import  { defineStore } from  "pinia" export  const  useCountStore = defineStore ('count' , {         actions : {                  increment (n: number  ) {             this .sum  += n         }     },     state ( ) {         return  {             sum : 1 ,             address : 'hanzhong' ,             nickName : 'zzmr'          }     },     getters : {         bigSum (state ) {             return  state.sum  * 10          },         upperNickName (state ) {             return  this .nickName .toUpperCase ()         }     } }) 
 
其实就是加上getters配置,然后写函数,在使用时,可以直接将函数名当函数的返回值使用
1 2 3 <h3 > bigSum:{{ bigSum }}</h3 > <h3 > upperNickName:{{ upperNickName }}</h3 > let { sum, address, nickName, bigSum, upperNickName } = storeToRefs(countStore) 
 
订阅 其实就是监视store中的数据变化
1 2 3 4 talkLoveStore.$subscribe((mutate,state )=> {     console .log ('数据发生变化' )     console .log (mutate,'@' ,state) }) 
 
mutate是修改的操作,state中能取到store中的数据
store组合式写法 就是,就是去掉配置项,数据就直接定义,函数就可以直接使用变量
组件间通信 好像之前写的时候,基本就是本地存储+事件总线,而且后面写项目事件总线都用的少了哈哈哈操
props 父与子,子与父传递数据
父给子传,就直接:xxx="xxx"
 
这样就能把父亲中的car传递给子组件,而子组件中需要接收:
 
这样子组件就能拿到父组件给的car了
 
子给父传,需要父提供一个方法,并将这个方法像参数一样传递给子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <template >   <div  class ="father" >      <h3 > 父组件</h3 >  		<h4 > 汽车:{{ car }}</h4 >  		<h4  v-show ="toy" > 子给的玩具:{{ toy }}</h4 >  		<Child  :car ="car"  :sendToy ="getToy" />    </div >  </template > <script  setup  lang ="ts"  name ="Father" > 	import  Child  from  './Child.vue'  	import  {ref} from  'vue'  	 	let  car = ref ('奔驰' ) 	let  toy = ref ('' ) 	 	function  getToy (value:string ){ 		toy.value  = value 	} </script > 
 
子组件中,接收方法后,直接调用该方法及传递参数 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <template >   <div  class ="child" >      <h3 > 子组件</h3 >  		<h4 > 玩具:{{ toy }}</h4 >  		<h4 > 父给的车:{{ car }}</h4 >  		<button  @click ="sendToy(toy)" > 把玩具给父亲</button >    </div >  </template > <script  setup  lang ="ts"  name ="Child" > 	import  {ref} from  'vue'  	 	let  toy = ref ('奥特曼' ) 	 	defineProps (['car' ,'sendToy' ]) </script > 
 
这里是直接在template中调用了sendToy并传递了数据,如果要在script中写,就要接收到defineProps返回的数据来调用了
自定义事件 首先说一下就是一个点击事件,如果没有传递参数,是可以接受到事件的dom对象的,但是如果传递了参数,那就不能接收到dom元素了,此时可以使用$event来传递dom元素
1 <button  @click ="test(1, $event)" > 点我</button > 
 
 
自定义事件,常用于子传父 父组件如下,就是在父组件中给子组件标识一个@sendToy="getToy",因为是@开头,所以是事件的意思,@什么,子组件就可以使用什么方法,=的方法就是这个事件的回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template > 	<div  class ="father" >  		<h3 > 父组件</h3 >  		<h4 > {{ str }}</h4 >  		<Child  @sendToy ="getToy"  />  	</div >  </template > <script  setup  lang ="ts"  name ="Father" > import  Child  from  './Child.vue' import  { ref } from  'vue' let  str = ref ('你好' )function  getToy (toy: string ) {	str.value  = toy } </script > 
 
子组件如下,要使用defineEmits(['sendToy'])来接收,然后使用emit('sendToy',参数)来触发自定义事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <template > 	<div  class ="child" >  		<h2 > 子组件</h2 >  		<h3 > 玩具:{{ toy }}</h3 >  		<button  @click ="emit('sendToy',toy)" > 发送玩具</button >  	</div >  </template > <script  setup  lang ="ts"  name ="Child" > import  { ref } from  "vue" ;let  toy = ref ('奥特曼' )const  emit = defineEmits (['sendToy' ])</script > 
 
mitt 把这玩意当成vue2的事件总线
安装:npm i mitt
创建一个工具类?名叫emitter.ts
1 2 3 4 5 6 7 import  mitt from  "mitt" const  emitter = mitt ()export  default  emitter
 
emitter有以下的函数:
方法使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 emitter.on ('test1' , () =>  {   console .log ('test1被调用了' ) }) emitter.on ('test2' , () =>  {   console .log ('test2被调用了' ) }) setInterval (() =>  {  emitter.emit ('test1' )   emitter.emit ('test2' ) }, 2000 ) setTimeout (() =>  {     emitter.off ('test1' )       }, 5000 ) 
 
 
组件间通信实现  还是这个简单还用,记得使用前导入依赖:import emitter from '@/utils/emitter';
1 2 3 4 5 6 7 8 let  toy = ref ('奥特曼' )function  sendToy ( ) {	emitter.emit ('toy' , toy.value ) } 
 
孩子二接收,在卸载组件时解绑事件
1 2 3 4 5 6 7 8 9 10 11 let  toy = ref ('' )emitter.on ('toy' , (value: any  ) =>  { 	toy.value  = value }) onUnmounted (()=> {	emitter.off ('toy' ) }) 
 
v-model 一般不会自己写组件然后用这个来传递数据,不过ui组件库大量使用的就是这个 
这玩意还挺神奇,之前用el-input时可以直接使用v-model来绑定数据,但是我们自己的组件并不能实现,底层是这么写的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <template >   <div  class ="father" >      <h3 > 父组件</h3 >                                    <ZzmrInput  v-model ="username"  />         </div >  </template > <script  setup  lang ="ts"  name ="Father" > import  { ref } from  "vue" import  ZzmrInput  from  './ZzmrInput.vue' let  username = ref ('zhangsan' )</script > 
 
ZzmrInput.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 <template >     <input  type ="text"  :value ="modelValue"  @input ="emit('update:modelValue', (<HTMLInputElement>$event.target).value)" >  </template > <script  lang ="ts"  setup  name ="ZzmrInput" > defineProps (['modelValue' ])const  emit = defineEmits (['update:modelValue' ])</script > <style > </style > 
 
这里多次用到了$event
对于原生事件:$event就是事件对象,能.target 
对于自定义事件:$event就是触发事件时,所传递的数据,不能.target 
 
$attrs$attrs用于实现当前组件的父组件,向当前组件的子组件通信 :祖->孙
具体说明:$attrs是一个对象,包含所有父组件传入的标签属性
父传给子数据,子不接收,这个数据就存到了$attrs中,子再将$attrs用v-bind="$attrs"传给孙组件,就实现了祖->孙
到时候我肯定不用,说不定还是mitt梭哈 
$refs,$parent$refs用于父传子$parent用于子传父
到时候我肯定不用,说不定还是mitt梭哈 +1
provide_inject 这玩意也是用于祖孙之间传递的,与$attrs不同在于,这个不需要打扰中间人,可以直接实现祖孙传递
插槽 之前好像也不怎么用这个玩意
默认插槽 总结就是,将组件标签写成双标签,然后里面写上内容,这就是插槽的内容,然后到组件中,写上slot标签,就是展示这些内容的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 <template >   <div  class ="father" >      <div  class ="content" >        <Game  title ="热门游戏" >                   <ul >            <li  v-for ="item in games"  :key ="item.id" > {{ item.name }}</li >          </ul >        </Game >        <Game  title ="今日美食" >          <img  :src ="imageUrl" >        </Game >        <Game  title ="影视推荐" >          <video  :src ="videoUrl"  controls > </video >        </Game >      </div >    </div >  </template > <script  setup  lang ="ts"  name ="Father" > import  Game  from  './Game.vue' import  { ref, reactive } from  'vue' let  games = reactive ([  { id : '001' , name : 'war3'  },   { id : '002' , name : 'rdr2'  },   { id : '003' , name : 'gta5'  } ]) let  imageUrl = ref ('http://47.109.139.173:9000/food.guide/b1ba1c68-7b43-4c9f-9082-09bfcc9d8c57.jpg' )let  videoUrl = ref ('http://47.109.139.173:9000/food.guide/20231017_222546.mp4' )</script > 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 <template >   <div  class ="game" >      <h2 > {{ title }}</h2 >           <slot > </slot >    </div >  </template > <script  setup  lang ="ts"  name ="Game" > defineProps (['title' ])</script > 
 
具名插槽 当一个插槽不能满足需求时,可以使用具名插槽,就是给插槽的内容加上名字,使用template分开,一个萝卜一个坑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <Game  title ="热门游戏" >   <template  v-slot:s1 >      <ul >        <li  v-for ="item in games"  :key ="item.id" > {{ item.name }}</li >      </ul >    </template >    <template  v-slot:s2 >      s2呼呼哈嘿   </template >  </Game > <Game  title ="今日美食" >   <template  v-slot:s1 >      <img  :src ="imageUrl" >    </template >  </Game > <Game  title ="影视推荐" >   <template  v-slot:s1 >      <video  :src ="videoUrl"  controls > </video >    </template >  </Game > 
 
slot标签上也加上名字
1 2 3 4 5 6 <div  class ="game" >   <h2 > {{ title }}</h2 >    <slot  name ="s1" > s1插槽默认内容</slot >    <br >    <slot  name ="s2" > s2插槽默认内容</slot >  </div > 
 
对于template上写v-slot:xx这种写法,可以简写成:#xx
1 2 3 4 5 6 <Game  title ="影视推荐" >      <template  #s1 >      <video  :src ="videoUrl"  controls > </video >    </template >  </Game > 
 
作用域插槽 插槽的数据在插槽组件中,而结构或者说使用插槽的地方可以接收插槽的数据
插槽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <template >     <div  class ="game" >          <h2 > 游戏列表</h2 >                   <slot  :games ="games" > </slot >      </div >  </template > <script  lang ="ts"  setup  name ="Game" > import  { reactive } from  'vue' let  games = reactive ([    { id : '001' , name : 'war3'  },     { id : '002' , name : 'rdr2'  },     { id : '003' , name : 'gta5'  } ]) </script > 
 
使用插槽的地方,要使用v-slot="params",也可以解构赋值v-slot="{games}"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <template >   <div  class ="father" >      <h1 > 父组件</h1 >      <div  class ="content" >        <Game >          <template  v-slot ="params" >            <ul >              <li  v-for ="g in params.games"  :key ="g.id" > {{ g.name }}</li >            </ul >          </template >        </Game >        <Game >          <template  v-slot ="params" >            <ol >              <li  v-for ="g in params.games"  :key ="g.id" > {{ g.name }}</li >            </ol >          </template >        </Game >        <Game >                   <template  v-slot ="{games}" >            <h3  v-for ="g in games"  :key ="g.id" > {{ g.name }}</h3 >          </template >        </Game >      </div >    </div >  </template > <script  setup  lang ="ts"  name ="Father" > import  { ref, reactive } from  'vue' import  Game  from  './Game.vue' </script > 
 
如果要给插槽加名字,那么可以在v-slot:xx="{xxx}",冒号后面就可以跟名字,也可以#xx="{xxx}"这么些 
ui组件库中会大量使用这个作用域插槽 
其他API shallowRef与shallowReactive shallowRef 
作用:创建一个响应式数据,但只对顶层属性进行响应式处理 
特点:只跟踪引用值的变化,不关心值内部的属性变化 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let  person = shallowRef ({	name : '张三' , 	age : 18  }) let  car = shallowReactive ({	brand : 'bwm' , 	options : { 		color : '白色' , 		price : 10  	} }) 
 
readonly与shallowReadonly readonly就是根据一个响应式数据生成一个只读的响应式数据,尝试修改只读数据的话,会报错
shallowReadonly是浅层次的只读,对于对象的第一层属性是只读的,但是深层的属性是可以修改的
toRaw 用于获取一个响应式对象的原始对象,toRaw返回的对象不再是响应式的,不会触发视图更新
官网描述:这是一个可以用于临时读取而不引起代理访问/跟踪开销,或是写入而不触发更改的特殊方法,不建议保存对原始对象的持久引用,请谨慎使用
 
何时使用?在需要将响应式对象传递给非Vue的库或外部系统时,使用toRaw可以确保它们收到的是普通对象
 
1 2 3 function  getRaw ( ) {	console .log (toRaw (person)) } 
 
markRaw 标记一个数据,让其不可成为响应式数据,如下,就算bookR使用了reactive,也无法变成响应式数据 
例如使用mock.js时,为了防止误把mock.js变为响应式对象,可以使用markRaw去标记mock.js
 
1 2 3 4 5 6 7 8 9 10 let  book = markRaw ({	name : 'gc' , 	price : 1  }) let  bookR = reactive (book)function  changeName ( ){	bookR.name  = 's'  	console .log (bookR) } 
 
customRef 作用:创建一个自定义的ref,并对其依赖项跟踪和更新触发进行逻辑控制
使用注意事项
创建一个初始数据 
注意get中的track()调用,告诉vue数据msg很重要,要对msg进行持续关注,一旦msg变化就去更新 
注意set中的trigger()调用,通知vue一下数据变化了 
 
下面实现了一个防抖的效果,后面写项目可以考虑用 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let  initValue = '你好' let  timer : number let  msg = customRef ((track, trigger ) =>  {	return  { 		 		get ( ) { 			track ()   			return  initValue 		}, 		 		set (value ) { 			clearTimeout (timer) 			timer = setTimeout (() =>  { 				initValue = value 				trigger ()   			}, 1000 ) 		} 	} }) 
 
 
但一般开发会将自定义的ref封装到hooks中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import  { customRef } from  'vue' export  default  function  (initValue: string , delay: number  ) {    let  timer : number      let  msg = customRef ((track, trigger ) =>  {         return  {                          get ( ) {                 track ()                   return  initValue             },                          set (value ) {                 clearTimeout (timer)                 timer = setTimeout (() =>  {                     initValue = value                     trigger ()                   }, delay)             }         }     })     return  { msg } } 
 
然后组件中直接调用
1 2 3 4 import  useMegRefs from  '@/hooks/useMegRefs' let  { msg } = useMegRefs ('zzmr' , 2000 )
 
Vue3新组件 Teleport 这玩意?我会用得到?
就是可以把<teleport>标签中包裹的标签,传送到指定的标签内:
1 2 3 4 5 6 7 <teleport  to ="body" >   <div  class ="modal"  v-show ="isShow" >      <h2 > 弹窗标题</h2 >      <h2 > 弹窗内容</h2 >      <el-button  @click ="isShow = false" > 关闭弹窗</el-button >    </div >  </teleport > 
 
App.vue中,Modal是放到app的div下的,但是使用了teleport就可以直接将这个玩意传送到body下
1 2 3 4 <div  class ="app" > 	<img  src ="http://47.109.139.173:9000/food.guide/b1ba1c68-7b43-4c9f-9082-09bfcc9d8c57.jpg"  alt ="" >  	<Modal > </Modal >  </div > 
 
Suspense 等待异步组件时渲染一些额外内容,让应用有更好的用户体验
子组件中包含着异步任务,在请求成功之前,用于展示一些东西,给用户更好的体验 ,但是无法作为骨架屏使用,而且这个东西是一个实验性的组件,未来可能移除
全局API转移到应用对象 
app.component 
app.config 
app.directive 
app.mount 
app.unmount 
app.use 
 
其他 
过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from。
 
keyCode 作为 v-on 修饰符的支持。
 
v-model 指令在组件上的使用已经被重新设计,替换掉了 v-bind.sync。
 
v-if 和 v-for 在同一个元素身上使用时的优先级发生了变化。
 
移除了$on、$off 和 $once 实例方法。
 
移除了过滤器 filter。
 
移除了$children 实例 propert。