Vue3 Npm 
 
1. npm(nodejs) 安装: 安装: 
下载 nodejs 安装(安装过程直接默认下一步即可)。
npm 配置修改: 
npm install cnpm -g :安装 cnpm 淘宝镜像(不建议使用,使用易出错) 
npm config set registry https://registry.npm.taobao.org/ :npm 使用淘宝镜像路径 
npm config set registry https://registry.npmmirror.com/ :npm 使用淘宝镜像路径(推荐 ) 
npm config get registry :查看镜像配置 
npm 依赖缓存位置修改:
npm get cache 或 npm get prefix :查看缓存或保存模块地址 
npm config set cache "E:\myfile\nodejs\data\node_cache" :缓存地址 
npm config set prefix "E:\myfile\nodejs\data\node_global" :全局模块地址
注意由于 node 全局模块大多需要命令访问因此,应在系统环境变量 PATH  中添加 node 的全局模块保存地址。 
 
 
 
 
 
常用命令: 
node -v: 查看 nodejs 版本(检查是否安装成功)
 
npm -v :查看 npm 版本
 
npm install npm -g :npm 更新与全局安装
 
npm install jquery :npm 安装软件包命令,在当前文件夹下安装(这里以jquery为例,在后面加上 -g 表示全局安装)
如需指定版本 npm install jquery@3.0.0 ( ‘软件名@版本号’ ),且它会自动清理当前项目之前安装的版本使用你指定的版本 
npm install bootstrap -save-dev :表示开发环境使用 
 
 
npm list -g :查看全局安装的软件包(去除 -g 查看当前文件夹项目下安装的所有软件包)
 
npm list jquery :查看某个软件包的版本
 
 
2. Package.json: 
npm init :为当前项目生成 package.json 文件(可初始化 npm 项目) 
npm run 配置命令 :运行 package.json 文件 scripts 配置中的命令 
npm install :根据 package.json 文件 dependencies 与 devDependencies 配置为项目安装软件包 
“jquery”: “^3.0.0”  :版本配置 ‘^’  符号表示 3 的大版本不会改变,后面两位数的版本自动更新。去除该符号则版本不会自动更新。此外使用 ‘~’  符号表示最后一位数版本自动更新。 
“main”: “index.js”  :配置当前项目的主入口文件,一般默认为当前项目 src  目录下的 index.js  文件。 
 
Webpack 
  webpack 是一个模块打包器(构建工具)。它主要目标是将 JavaScript 文件打包在一起,打包后的文件用于浏览器中使用,其次它还能 转换、打包、包裹  任何资源(默认只能打包 js 文件,对其它资源打包需要使用到插件)。
 
webpack 官网:webpack 
1. 概念: 
树结构:在一个入口文件中引入所有资源,形成树状依赖关系。 
模块:对于 webpack 来说所有的资源都称之为模块(.js,.css…) 
chunk:打包过程中被操作的模块文件被称之为 chunk。 
bundel:最终打包完成的文件。可能和 chunk 一样,大部分情况下它是多个 chunk 的集合。 
 
2. 安装与使用: npm install webpack -g :安装 webpack 软件包。npm install webpack-cli -g:安装 webpack 脚手架,即命令行执行程序。
npm install webpack webpack-cli -g:两个包同时安装命令。
npm i webpack webpack-cli -D:不全局安装仅为当前项目安装(i -> install,d -> 开发环境)
打包 js 文件: 
在空项目中初始化 npm npm init -y 。 
创建 src  目录与 index.js  主文件(npm 中有介绍默认寻找 src 目录下 index.js),再根据需要创建其它的 JS 文件或者目录,但必须以 ./src/index.js 文件为主文件与其它文件相关联形成树状关系。 
打包 webpack --mode=development (开发环境),webpack --mode=production (生产环境) 
打包成功以后会在项目下生成 dist  目录,目录中的 main.js  就是打包成功后的 JS 文件。 
 
3. 核心配置: 在 webpack 项目中新建 webpack.config.js  (webpack 默认会按此文件配置打包)。
1. 配置选项: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const  {resolve} = require ('path' ); module .exports  = {         entry : './src/index.js' ,          output : {         filename : "build.js" ,          path : resolve (__dirname,'build' )      },          module : {         rules : []     },          plugins : [],          mode : 'production' ,  } 
 
2. 多入口多出口: 
entry 为数组类型:多个入口文件打包形成一个 chunk 并最后输出一个文件 
 
1 2 3 4 5 entry : ['./src/index.js' ,'./src/main.js' ],output : {    filename : "build.js" ,      path : resolve (__dirname,'build' )  }, 
 
entry 为对象类型:多个入口文件打包形成多个 chunk 并最后输出多个文件(如下生成 one.js、two.js) 
 
1 2 3 4 5 6 7 8 entry : {    one : './src/index.js' ,     two : './src/main.js'  }, output : {    filename : "[name].js" ,      path : resolve (__dirname,'build' )  }, 
 
1 2 3 4 5 6 7 8 entry : {    one : './src/index.js' ,     two : ['./src/index.js' ,'./src/main.js' ], }, output : {    filename : "[name].js" ,      path : resolve (__dirname,'build' )  }, 
 
3. 打包 HTML 文件: 
安装打包插件(仅开发环境使用): npm i html-webpack-plugin -D 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const  htmlWebpackPlugin = require ('html-webpack-plugin' ); plugins : [         new  htmlWebpackPlugin (),          new  htmlWebpackPlugin ({                  template : './src/index.html' ,         filename : 'main.html' ,          chunks : ['one' ,'two' ],          minify : {             collapseWhitespace : true ,              removeComments : true           }     }), ], 
 
4. 打包 CSS 文件: 安装 css-loader 和 style-loader ,css-loader 打包单个文件并处理 @import 和 URL 等外部资源。style-loader 在 head 标签中插入 style 标签并将打包的 css 样式利用 js 嵌入其中(只是单纯打包 css 文件可以忽略此插件)。
安装命令: npm i css-loader style-loader -D
1 2 3 4 5 6 7 8 9 10 require ('./style.css' ); module : {    rules : [{         test : /\.css$/ ,                   use : ['style-loader' ,'css-loader' ],     }] }, 
 
5. 打包图片、字体图标,eslint js语法检查(略): webpack打包详解 
6. DevServer: 
target: "web"
1 2 3 4 5 6 7 devServer:  {      port:  8081 ,      compress:  true ,       open:  true ,            hot:  true  } 
 
7. 去除无用的 js、css (略): ES6 语法 1. let、const : var : 可以重复声明、无法限制修改、没有块级作用域。
let : 不能重复声明、拥有块级作用域。
const : 与 let 类似但仅作为常量使用。
2. 箭头函数与 this 指向: 
箭头函数返回对象问题: 
 
1 2 3 4 5 6 7 var  o  = ( )=>{name :'hello' ,num :1 };console .log (o ()); var  o  = ( )=>{return  {name :'hello' }}; console .log (o); var  o  = ( )=>({name :'hello' ,num :1 }); console .log (o ()); 
 
普通函数 this 指向它的调用者。 
箭头函数 this 指向它定义时所处位置的外层 this 。 
 
3. 数组方法: map 映射: arr.map(function(values,index,array){}) 函数返回值为映射数组元素的值。
filter 过滤: arr.filter(function(values){}) 函数返回值 true 表示保留当前数组元素。
forEach : arr.forEach(function(values){}) 迭代器遍历。
reduce 汇总:arr.reduce((newValue,nextValue,index)=>{},?initialValue)
1 2 3 4 5 6 7 8 9 var  array = [2 ,3 ,4 ];array.reduce ((newValue,nextValue )=> {     console .log (newValue,nextValue);      return  newValue+nextValue; }, 0 );  array.reduce ((newValue,nextValue )=> {     console .log (newValue,nextValue);      return  newValue+nextValue; });  
 
4. Module 模块化: 注意模块化必须在服务器中使用,本地无效。vscode 可以使用 Live Server  ,idea 直接点击浏览器图标即可。
1. main.html 中引入主 main.js : 1 <script  src ="main.js"  type ="module" > </script > 
 
2. export 和 import : 
1 2 3 4 5 let  a = 1 ;function  b ( ){    console .log ("js1.js b()" ); } export  {a,b}; 
 
1 2 3 4 5 export  let  a = 1 ; export  default  function  ( ){    console .log ("js2.js default" ); } 
 
1 2 3 4 5 6 7 8 9 import  {a as  a1,b} from  "./js1.js" ; import  {a} from  "./js2.js" ; import  def from  "./js2.js" ; console .log (a1,a);b ();def ();
 
Axios 1. ES6 Promise: 
主要用于异步计算 
可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果 
可以在对象之间传递和操作 promise,帮助我们处理队列 
 
 
基本用法: 
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 new  Promise ((resolve,reject )=> {    console .log ("执行方法 promise1" );          resolve ("方法 promise1 执行完成" );                    reject ("方法 pormise1 执行错误" );      }).then (     res => {         console .log (res);         return  new  Promise ((resolve,reject )=> {             console .log ("执行方法 promise2" );             reject ("方法 promise2 执行错误" );         });     },err => {         console .log (err);     }).then (res => {},err => {          console .log (err); }).catch (err => {          console .log (err); }); 
 
Promise.all(Promise[]) 并发处理: 
1 2 3 4 5 6 7 8 9 Promise .all ([    new  Promise (resolve => {         resolve ("promise1 执行完成" );     }),     new  Promise (resolve => {         resolve ("promise2 执行完成" );     }) ]).then (res =>console .log (res)); 
 
2. 简单使用: 
在 cdnjs 中搜索 axios 引入 js 文件  :
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>
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 axios ('/api' ).then (res => {    console .log (res); }); axios ({    rul : '/api' ,     method : "POST" ,      params : {          name : 'ruoxijun' ,         age : 18 ,     },     data : {          name : 'ruoxijun' ,         age : 18 ,     } }).then (res =>console .log (res))     .catch (err =>console .log (err)); axios.get ('/api' ,{     params : {         name : 'ruoxijun' ,         age : 18 ,     }, }).then (res =>console .log (res)); axios.post ('/api' ,'name=ruoxijun&age=18' ).then (res =>console .log (res)); 
 
axios 并发请求处理  :
axios.all 与 Promise.all 方法类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 axios.all ([     axios.get ('/api' ),     axios.get ('/api' ), ]).then (res =>console .log (res))      .catch (err =>console .log (err)); axios.all ([     axios.get ('/api' ),     axios.get ('/api' ), ]).then (          axios.spread ((res1,res2 )=> {         console .log (res1,res2);     }) ).catch (err =>console .log (err)); 
 
3. 全局配置与封装: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 axios.defaults .baseURL  = "http://localhost" ;  axios.defaults .timeout  = 5000 ;  let  git = axios.create ({    baseurl : "https://github.com/" ,     timeout : 5000 , }); let  local = axios.create ({    baseurl : "https://localhost" ,     timeout : 5000 , }); git.get ("/api" ).then (res =>console .log (res)); 
 
4. 拦截器: 1 2 3 4 5 6 7 8 9 10 11 12 axios.interceptors .request .use (config => {     return  config;  },error => {     return  Promise .reject (error); }); axios.interceptors .response .use (config => {     return  config;  },error => {     return  Promise .reject (error); }); 
 
Vue3 1. 初识 vue3: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const  app = {    data ( ) {         return  {             message : 'hello world'          }     }, }; const  vm = Vue .createApp (app).mount ("#app" );vm.message  = 'hello' ;  vm.$data .message  = 'world' ; 
 
2. vue-cli: Vue-Cli(Command Line Interface) 是 vue 官方提供的脚手架工具。默认已经帮我们搭建好了一套利用 Webpack 管理 vue 的项目结构。
1. 命令须知: 
安装命令: npm install -g @vue/cli 
检查版本: vue -version 或 vue -V 
创建项目: vue create 项目名称 
卸载命令: npm uninstall -g vue-cli 
 
2. 项目创建: 
执行 vue create vue3-demo1 命令开始创建项目: 
 
此处使用上下键加回车进行选择,前两个选项表示创建默认的 vue3  或 vue2  项目。默认创建的项目中包含两个插件:
babel  : 检查 es6转es5
eslint  : 检查 es 语法与修复
第三个选项 Manually select features 表示手动创建项目,建议使用。
默认的 vue3 项目结构: 
 
手动创建项目: 
 
选择 Manually select features 后进入上界面,通过上下键与空格选择或取消,选择完成后回车进入下一步。
之后对项目以及插件进行配置操作如下:
如果最后对项目配置进行了保存删除 C:\当前用户文件夹\.vuerc 文件中可以去除保存的配置。
3. vue.config.js: 当我们需要对 webpack 或插件进行配置时只需要在项目的根路径下新建 vue.config.js ,vue-cli 提供了方便的配置方法,我们也可以选择使用 webpack 原生的配置方法进行配置。
配置详情请参照: Configuration Reference | Vue CLI (vuejs.org) 
1 2 3 4 5 6 7 module .exports  = {         outputDir : 'build' ,     configureWebpack : {          plugins : []     } } 
 
3. Vue3 语法: 1. 基础语法: 
插值表达式: {{msg}} 注意只接受表达式,不能使用语句 
简单指令 v-指令 :
v-pre :不解析该标签中的 vue 语法 
v-once :该标签中的 vue 语法只解析一次 
v-text :向该标签中插入文本 
v-html :向改标签中插入 html(v-html=”不能在此直接书写html”) 
v-show :是否显示该标签 
v-if , v-else , v-else-if :是否渲染该标签 
v-cloak :防止弱网络环境下渲染闪烁问题 
 
 
 
2. v-bind: 
简单使用: 
 
v-bind:属性名 主要对标签的属性进行绑定,除此它还有一种简写的方式 :属性名 。
绑定 style: 
 
1 2 3 4 5 6 7 8 9 10 11   <div :style="['font-size:22px','width:200px',fontcolor,styles]"      style="width:100px;"  > div 1  </div> <div  :style ="{color:'red','font-size':'18px'}" >  div 3 </div > data ( ) {    return  {         fontcolor : 'color:red' ,          styles : {backgroundColor : '#123' },      } } 
 
绑定 class: 
 
style 与 class 的属性绑定都支持 字符串、数组、对象(常用)、方法  。
1 2 3 4 5 6 7 8 9 10 11 12 13 <div :class ="one" > div 3  </div> <div  :class ="{one:isone,istwo}" >  div 4 </div > data ( ) {    return  {         one : 'one' ,         isone : true ,         istwo : true      } }      .one  {color : blue;} .istwo  {font-size :18px;} 
 
3. computed: 
1 2 3 4 <h1 > 插值方式: {{name + msg}}</h1 > <h1 > 方法返回: {{getTitle()}}</h1 > <h1 > 计算属性: {{title}}</h1 > <h1 > 使用 set: {{setTitle()}}</h1 > 
 
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 data ( ) {  return  {     name : 'hello' ,     msg : 'world' ,   } }, methods : {  getTitle ( ) {     return  this .name  + this .msg ;   },   setTitle ( ) {          this .title2 ='hi' ;     return  this .title2 ;   } }, computed : {  title ( ) {      return  this .name  + this .msg ;   },   title2 :{      get ( ) {       return  this .name  + this .msg ;     },     set (newVla ) {       this .msg  = newVla;     }   } } 
 
计算属性只有在 get 方法中 vue 的属性值发生改变时,才会重新计算否则都将使用缓存的数据。而普通方法看你在页面中调用了多少次则运行多少次。
4. v-on: 
v-on:click :绑定一个点击事件,简写 @click 
 
当调用事件方法时没有传入参数,且声明方法时只有一个参数,默认该参数的值就是事件 event 对象。当有多个参数且我们需要事件对象时 $event 作为参数它就代表了当前事件对象(@click="click(val,$event)"); 
事件修饰符: 
stop :阻止事件冒泡 
self :当前事件由该元素本身触发时才执行 
capture :事件捕获模式(如点击事件触发且冒泡时,会优先执行添加该修饰符的外层) 
prevent :阻止默认事件 
once :事件只触发一次 
 
5. v-for: v-for 可以循环渲染标签
v-for="(item, [index]) in arr" :循环数组 
v-for="(val, [key], [index]) in obj" :循环对象 
 
vue 对于循环的标签提供了一个 :key 唯一标识符属性,当循环的数组或者对象改变时它会去对比这个 key 值,只重新渲染改变了的数据,而不去影响未改变的数据增加渲染效率。
<div v-for="(v,i) in arr" :key="i"></div> :使用唯一标识符的循环
在 vue3 中 v-if 优先级大于 v-for ,vue2 反之。 
 
6. v-model: 
<input v-model="val" /> : 
 
修饰符: 
lazy :懒加载 
number :让绑定值(默认 string)转换为 number 类型 
trim :去除绑定值两端的空格 
 
单选框与多选框: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <div > 性别:{{sex}}</div > 男:<input  type ="radio"  value ="男"  v-model ="sex" >  <br />  女:<input  type ="radio"  value ="女"  v-model ="sex" >  <br />  协议:<input  type ="checkbox"  value ="yes"  v-model ="type" >  {{type}} <br />  <div > 爱好:{{like}}</div > java:<input  type ="checkbox"  value ="java"  v-model ="like" >  <br />  html:<input  type ="checkbox"  value ="html"  v-model ="like" >  <br />  php:<input  type ="checkbox"  value ="php"  v-model ="like" >  <br />  <select  v-model ="select" >     <option  value ="1" > 1</option >      <option  value ="2" > 2</option >  </select > <br /> <select  v-model ="selects"  multiple >     <option  value ="1" > 1</option >      <option  value ="2" > 2</option >  </select > 
 
这里 v-model 就代表了它们的 name 属性同时与 value 绑定。
radio 需要默认选中时将绑定的变量设置为你需要的 value 即可。
checkbox 单个时绑定的属性为值为 true/false 表示它是否选中。多个时绑定一个数组,数组的值可以设置为需要默认选中的 value 数组值。
select 标签使用上同理多选绑定数组即可。
7. watch: 1 2 3 4 5 6 7 8 watch :{     message (newVal, oldVal ){},      obj : {          handler (newVal, oldVal ){},          immediate : true ,          deep : true ,      } } 
 
8. Teleport: 1 2 3 4 <Teleport  :disabled ="true"  to ="body" >    <div >  Hello from the modal! </div >  </Teleport > 
 
9. component: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script > import  Foo  from  './Foo.vue' ;import  Bar  from  './Bar.vue' ;export  default  {  components : { Foo , Bar  },    data ( ) {     return  {       view : 'Foo'       }   } } </script > <component  :is ="view"  /> <component  :is ="Foo"  /> 
 
4. 组件化开发: 1. 创建组件: 我在 components 文件夹中创建了一个名为 MyComponent.vue 文件,这是组件化开发一个组件的基本格式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template >   <div  v-text ="msg" > </div >  </template > <script > export  default  {  name : "Demo" ,   data ( ){     return  {       msg : "this is MyComponent" ,     }   } } </script > <style  scoped  lang ="scss" >           </style > 
 
关于 lang=”scss” 使用:学习Scss-看这篇就够了 - SegmentFault 思否 
2. 引用组件: 在 App.vue 中引用 MyComponent.vue 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <template >   <div >           <MyComponent  class =“my-style” > </MyComponent >       <my-component > </my-component >     </div >  </template > <script > import  MyComponent  from  '@/components/MyComponent' ; export  default  {  name : 'App' ,   components : {     MyComponent ,    } } </script > 
 
3. props(父传子): 
props 数组定义组件属性: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 <template >     <div > 接收的父类消息: {{parentMsg}}</div >  </template > <script > export  default  {  props : ['parentMsg' , ],  } </script > <my-component  :parent-msg ='msg' > </my-component > 
 
props 对象定义组件属性并添加验证: 
 
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 props : {     propA : Number ,       propB : [String , Number ],      propC : {     type : String ,     required : true    },      propD : {     type : Number ,     default : 100    },      propE : {     type : Object ,     default : { message : 'hello'  },                       },      propF : {     validator : function  (value ) {              return  ['success' , 'warning' , 'danger' ].indexOf (value) !== -1      }   } } 
 
type 可以是如下类型,type 也可以是一个自定义构造器,使用 instanceof 检测:
String 
Number 
Boolean 
Array 
Object 
Date 
Function 
Symbol 
 
4. $emit(子传父): 
子组件无法直接改变 props 接收到的数据,可以通过向父组件传递数据方式通知父组件来改变数据。 
官方建议:强烈建议使用 emits 记录每个组件所触发的所有事件(与 props 属性类似)。 
 
在子组件中: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <template >   <div >      <div > 接收的父类数据: {{number}}</div >      <button  @click ="addNumber(2)" > +</button >    </div >  </template > <script > export  default  {  props : ['number' ],      emits : ['addNumber' ],    methods : {     addNumber (num ) {              this .$emit('addNumber' ,num);     }   }, } </script > 
 
在父组件中: 
 
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 <template >   <div >      {{number}}     <my-component  :number ="number"  @add-number ="addNumber" > </my-component >    </div >  </template > <script > import  MyComponent  from  '@/components/MyComponent' ;export  default  {  components : {     MyComponent ,   },   data ( ) {     return  {       number : 0 ,     }   },   methods : {     addNumber (num ) {        this .number  += num;     }   }, } </script > 
 
5. 父子间直接访问: 
$parent :在子组件中使用该属性可直接调用父组件的方法或属性 
$root :直接访问根组件中的属性(一般是 App.vue) 
$children :vue2 中使用该属性访问子组件属性(vue3 已弃用 ) 
$refs : 访问子组件属性或获取 dom 元素: 
 
1 2 3 4 5 6 7 <my-component  ref ="com1" > </my-component > <my-component  ref ="com2" > </my-component > this.$refs.com1.msg="msg1"; this.$refs.com2.msg="msg2"; 
 
ref 作用在普通 html 标签中时获取到的是标签 dom 元素。
ref 与 v-for 一同使用时,vue2 时 $refs 会返回循环的 dom/组件 数组,vue3 中为了效率需要给 ref 绑定一个函数处理 ref 。 
 
1 2 3 4 5 <div  v-for ="i in 10"  :ref ="setItemRef" >  {{ i }} </div > setItemRef(el) { // 参1为循环得到的 dom/组件   this.divArr.push(el); } 
 
6. slot 插槽: 
基本使用: 
 
1 2 3 4 5 <template >   <div  class ="child" >      <slot > <span > 缺省内容(默认内容)</span > </slot >    </div >  </template > 
 
slot  标签表示一个插槽,当使用该组件未插入任何内容时,会将 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 <template >   <div  class ="child" >           <div > <slot  name ="one" > 1</slot > </div >      <div > <slot  name ="two" > 2</slot > </div >           <div > <slot > 3</slot > </div >    </div >  </templat > <my-component >      <template  v-slot:one >      <span > 插入 one 中</span >    </template >       <template  #two >      <span > 插入 two 中</span >    </template >       <template  v-slot:default >      <span > 插入 default 中</span >    </template >  </my-component > 
 
当子组件中有多个插槽时,插入的内容默认会插入所有没有 name 属性值的插槽中(name="" 不会)。
插槽传值: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <template >   <div  class ="child" >      <slot  :val ="msg" > 使用 v-bind 定义 val 属性绑定 msg,可以定义多个属性,绑定任意值</slot >    </div >  </template > <script > export  default  {  data ( ) {     return  {       msg : "this is child msg"      }   }, } </script > <my-component >          <template  v-slot:default ="child_val" >           <span > 插入 default 中 - {{child_val.val}}</span >      </template >  </my-component > 
 
动态插槽: 
 
1 2 3 4 5 6 7 8 9 10 11 12 <my-component >          <template  #[slotName ]>  {{ slotName }} </template >  </my-component > <script > export  default  {  data ( ) {     return  { slotName : "one"  }   }, } </script > 
 
7. keep-alive: 官方解释:  
<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。
当组件在 <keep-alive> 内被切换时,它的 mounted 和 unmounted 生命周期钩子不会被调用,取而代之的是 activated 和 deactivated。(这会运用在 <keep-alive> 的直接子节点及其所有子孙节点。)
主要用于保留组件状态或避免重新渲染。
1 2 3 <keep-alive >   <component  :is ="view" > </component >  </keep-alive > 
 
include / exclude (包含/排除): 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <KeepAlive  include ="a,b"  :max ="10" >   <component  :is ="view"  />  </KeepAlive > <KeepAlive  :include ="/a|b/" >   <component  :is ="view"  />  </KeepAlive > <KeepAlive  :include ="['a', 'b']" >   <component  :is ="view"  />  </KeepAlive > 
 
8. 组件生命周期函数: 1. 组件生命周期图: 
2. 组件生命周期方法: 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 beforeCreate ( ) {  console .log ("实例刚刚被创建" ); }, created ( ) {  console .log ("实例已经创建完成" ); }, beforeMount ( ) {  console .log ("模板编译之前" ); }, mounted ( ) {  console .log ("模板编译完成" ); }, beforeUpdate ( ) {  console .log ("数据更新之前" ); }, updated ( ) {  console .log ("数据更新完成" ); }, activated ( ) {  console .log ("keep-alive 缓存的组件激活时" ); }, deactivated ( ) {  console .log ("keep-alive 缓存的组件停用时" ); }, beforeUnmount ( ) {  console .log ("实例销毁之前" ); }, unmounted ( ) {  console .log ("实例销毁完成" ); } 
 
3. $nexTick : 官方解释: 将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。
1 2 3 this .$nextTick(function ( ) {     }) 
 
例如在 created  中 Dom 还未创建完成,这时我们可以使用 $nexTick  写入即将对 Dom 进行的操作,等待 Dom 更新创建完成后将执行该操作。 
列如一个很大的数组做了很大的改动你需要 Dom 更新完成之后做些什么,这时你可以在数组操作完的下一句使用 $nextTick 它会在 Dom 改动且渲染完成后做出操作。 
 
Axios 与 Vue 1. Vue 中使用 Axios: 
项目中安装 Axios:npm i axios -S 
在组件中直接使用: 
 
1 2 import  axios from  "axios" ;
 
新建 js 文件封装 axios(以 src 下新建 @/utils/request.js 文件为例): 
 
1 2 3 4 5 6 7 8 9 import  axios from  'axios'  const  request = axios.create ({     }) export  default  request
 
在组件中直接引入使用即可: import {request} from "@/utils/request"
2. Vue 封装 Axios 模板: 内容展示省略(TODO) 
VueRouter 1. 认识路由: 
1. 基础使用: 
vue 中使用路由参照 vue-cli 创建路由项目(src 下 router/index.js ): 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import  { createRouter, createWebHistory } from 'vue-router' import  HomeView from '../views/HomeView.vue' const  routes  =  [  {     path: '/' ,     name: 'home' ,     component: HomeView   },   {     path: '/about' ,     name: 'about' ,     component: () => import ('../views/AboutView.vue' )   } ] const  router  =  createRouter({  history: createWebHistory(process.env.BASE_URL),   routes }) export default  router 
 
路由中导入组件 import from 方式打包时会将所有组件与路由打包到同一个 js 中。
() => import('组件') 会将组件打包为单个 js 文件实现懒加载,推荐使用。
在 main.js  中注册路由: 
 
1 2 import  router from  './router' createApp (App ).use (router).mount ('#app' )
 
在 App.vue  中使用路由: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 <template >          <router-link  to ="/" > Home</router-link >      <router-link  to ="/about" > About</router-link >      <router-view />  </template > <style >          a .router-link-exact-active  {         color : #42b983 ;      } </style > 
 
2. 路由模式: Hash :使用 URL 的 hash值来作为路由,且 URL 中会带有 # 号 。
History :历史模式借助 HTML5 History API 实现。
切换 Hash 模式:
1 2 3 4 5 6 import  { createRouter, createWebHistory, createWebHashHistory} from  'vue-router' const  router = createRouter ({     history : createWebHashHistory (process.env .BASE_URL ),    routes }) 
 
3. 重定向与别名: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 {   path : '/' ,   name : 'home' ,   component : ()=>  import ('../views/HomeView.vue' ), },{   path : '/index' ,      alias : ['/index.html' ,'/home' ],      redirect : '/' ,        }, 
 
2. router-link: 
为路由激活状态的 router-link 设置 class: 
 
1 2 <router-link  to ="/"  active-class ="active"  target ="_blank" > Home</router-link > 
 
1 2 3 4 5 6 7 8 9 10 11 12 <router-link  to ="/about" >     <button > About</button >   </router-link > <button  @click ="$router.push('/user')" > 个人中心</button > <button  @click ="$router.replace('/about')" > 关于我们</button >  <button  @click ="$router.go(-1)" > 返回</button > <h3 > {{$route.path}}</h3 > 
 
可以为 router-view 设置 name  属性:
1 2 3 4 5 <router-view > </router-view > <router-view  name ="home" > </router-view > <router-view  name ="about" > </router-view > 
 
配置路由时使用 components 对象:
1 2 3 4 5 6 7 8 9 10 {   path : '/' ,   name : 'home' ,      components : {     default : ()=>  import ('../views/HomeView.vue' ),      home : ()=>  import ('../views/HomeView.vue' ),      about : ()=>  import ('../views/AboutView.vue' ),   } }, 
 
3. 嵌套路由: 
路由中定义子路由: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 {   path : '/about' ,   component : () =>  import ('../views/AboutView.vue' ),      children : [      {        path :'' ,        component : ()=>  import ('../components/Children1.vue' )     },{       path : 'children1' ,        component : ()=>  import ('../components/Children1.vue' )     },{       path : 'children2' ,       component : ()=>  import ('../components/Children2.vue' )     }   ] }, 
 
使用子路由(这里是 AboutView.vue 中): 
 
1 2 3 <router-link  to ="/about/children1" > Children1</router-link >  <router-link  to ="/about/children2" > Children1</router-link > <router-view > </router-view > 
 
注意 父级路由  不能设置 name  属性。
4. 动态路由与参数传递: 1. params 与 query 传参: 
路由定义: 
 
1 2 3 4 5 6 7 8 {   path : 'children1/:msg' ,   component : ()=>  import ('../components/Children1.vue' ) },{   path : 'children2' ,   name : 'children2' ,    component : ()=>  import ('../components/Children2.vue' ) } 
 
使用路由并传递参数: 
 
1 2 3 4 5 6 <router-link  to ="/about/children1/1" > 子路由页面 - 1</router-link > <router-link  to ="/about/children2?name=user&age=18" > 子路由页面 - 2</router-link > <router-link  :to ="{path:'/about/children2',query:{name:'user',age:18}}" > 子路由页面 - 2</router-link > <router-link  :to ="{name:'children2',query:{name:'user',age:18}}" > 子路由页面 - 2</router-link > 
 
在对应组件中接收参数值: 
 
2. props 传参: 将 props 传递给路由组件 | Vue Router (vuejs.org) 
在 params 传参基础之上定义路由时添加 props: true 属性值。 
 
1 2 3 4 5 6 {   path : '/user/:id' ,    name : 'user' ,   props : true ,    component : ()=>  import ('../views/UserView.vue' ), } 
 
在此对应路由组件 props  属性中定义一个与路径变量名同名的属性类接收此参数值。 
 
1 2 3 4 5 <script > export  default  {  props : ['id' ],  } </script > 
 
5. 导航守卫: 
1 2 3 4 5 6 7 {   path : '/' ,   component : ()=>  import ('../views/HomeView.vue' ),   meta : {      title : '首页' ,   } } 
 
1. 全局守卫: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 router.beforeEach ((to, from  )=> {      console .log (to.path ,from .fullPath );             }); router.beforeResolve (to =>  {    }); router.afterEach ((to, from , failure )=> {      document .title  = to.meta .title ; }); 
 
2. 路由守卫: 1 2 3 4 5 6 7 8 9 10 {   path : '/user/:id' ,   component : ()=>  import ('../views/UserView.vue' ),      beforeEnter : (to, from  ) =>  {        },       } 
 
3. 组件守卫: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script>  export  default  {  beforeRouteEnter (to, from , next ) {                    next (vm  =>  {            });   },   beforeRouteUpdate (to, from  ) {             },   beforeRouteLeave (to, from  ) {        }, } </script> 
 
4. 导航流程: 
导航被触发。 
在失活的组件里调用 beforeRouteLeave 守卫。 
调用全局的 beforeEach 守卫。 
在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。 
在路由配置里调用 beforeEnter。 
解析异步路由组件。 
在被激活的组件里调用 beforeRouteEnter。 
调用全局的 beforeResolve 守卫(2.5+)。 
导航被确认。 
调用全局的 afterEach 钩子。 
触发 DOM 更新。 
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。 
 
6. keep-alive 与 transition: 
vue2 时可以直接使用 transition 或 keep-alive 包裹组件。 
vue3 需使用下方式: 
 
1 2 3 4 5 6 7 <router-view  v-slot ="{ Component }" >   <transition >      <keep-alive >        <component  :is ="Component"  />      </keep-alive >    </transition >  </router-view > 
 
Vuex 1. 认识 Vuex: 
工作原理: 
引入方式: 
在 src/store  目录下新建 index.js  内容如下: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import  { createStore } from  'vuex' export  default  createStore ({  state : {   },   getters : {   },   mutations : {   },   actions : {   },   modules : {   } }); 
 
在 main.js  中注册 Vuex : 
 
1 2 import  store from  './store' createApp (App ).use (store).mount ('#app' ); 
 
2.核心概念: 1. State(状态): 
在 Vuex 的 state(src/store/index.js )中可以存储记录任意属性值,因此也被称之为 Vuex 的状态。 
 
1 2 3 4 5 export  default  createStore ({  state : {     name : 'ruoxijun' ,    }, }) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 <template >   <div > {{$store.state.name}} - {{name}}</div >   </template > <script >   export  default  {     data ( ) {       return  {         name : this .$store .state .name ,        }     },   } </script > 
 
2. Mutation(改变): 组件中使用 $store.state 访问状态属性,也可以直接修改属性值,但并不推荐。官方推荐我们使用 mutations  搭配 $store.commit  的方式来修改状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 mutations : {     setStateName (state, payload ) {     state.name  = payload;    }, } methods : {  setStateName ( ) {          this .$store .commit ('setStateName' ,this .name );   } }, 
 
使用对象风格 commit:
1 2 3 4 5 6 7 8 9 10 11 this .$store .commit ({    type : 'setStateName' ,      name : this .name   }); setStateName (state, payload ) {         state.name  = payload.name ; }, 
 
3. Getter(属性): 
Vuex 中 getters 与 vue 中的计算属性类似: 
 
1 2 3 4 5 6 7 getters : {         nameAndNum (state, getters ){                  return  state.name  + state.loginUser .number ;     } }, 
 
组件中使用 $store.getters 进行访问: 
 
1 <div >  geteer:{{$store.getters.nameAndNum}} </div > 
 
1 2 3 4 functionName (state, getters){         return  parameter  =>  {}; } 
 
4. Action(异步): Action 可以包含任意异步操作,常用做异步请求之类操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 actions : {     action1 (context ){     setTimeout (()=> console .log (context),1500 );   },      action2 ({state, commit, getters, dispatch}, payload ){                            } }, 
 
组件中使用 $store.dispatch 调用方法:this.$store.dispatch("action1")
使用大致与 commit 类似,它的参2也用来接收参数(同样支持对象风格的 dispatch)
context  中还包含了 rootState 和 rootGetters 两个属性,这是在下面模块中需要用到的。
5. Module(模块): Vuex 允许我们将 store 分割成 模块(module) ,每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
state(子模块状态): 
 
注册一个子模块 module1 : modules: { module1 }
1 2 3 4 5 6 7 8 const  module1 = {    state : ()=> {          return  {                          module1Name : 'module1Name'          }     }, } 
 
getters、mutations、actions: 
 
默认  情况下,模块内部  的 getters、mutations、actions  仍然是注册在 全局命名空间  的——这样使得多个模块能够对同一个 getters、mutations、actions 作出响应。
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 mutations : {     setName (state, payload ){          state.module1Name  = payload;   } }, getters : {     getModule1Name (state, getters, rootState, rootGetters ){     return  module1Name + rootState.name ;    } }, actions : {     module1Actions ({rootState, rootGetters}, payload ){} }, modules : {  }
 
命名空间: 
 
getters、mutations、actions 默认是与根模块整合的(同名属性根模块优先),如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const  module1 = {  namespaced : true ,       modules : {     myPage : {        getters : {         profile () {}        }     },     posts : {       namespaced : true ,        getters : {         popular () {}        }     }   } } 
 
访问方式(state 自带命名空间):
1 2 3 $store.getters ['module1/getModule1Name' ] $store.commit ('module1/setName' , payload) $store.dispatch ("module1/setName" , payload) 
 
Vuex 模块化编程: 
 
将 getters、mutations、actions、modules 抽取出来形成单独的 JS 文件,使用 export default 导出并在 Vuex 主文件导入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  { createStore } from  'vuex' ;import  getters from  './getters' ; import  mutations from  './mutations' ;import  actions from  './actions' ;import  module1 from  './module/module1' ;const  state = {}export  default  createStore ({  state,   getters,   mutations,   actions,   modules : {     module1,   } }) 
 
Composition API 1. setup: setup  函数是一个新的组件选项,作为组件中组合式 API 的入口,函数在 beforeCreate  生命周期钩子执行之前执行,实例还没生成,没有 this  。
setup 有两个可选参数 props  与 context  ,props 是访问组件 props 属性值的对象。context 包含 4 个属性 attrs、slots、emit、expose 。
1 2 3 4 5 <com-a-p-i  name ="hello"  title ="home"  @comfun ="comfun" >     <template  #a >          <h3 > slot</h3 >      </template >  </com-a-p-i > 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template >   <div > {{count}} <slot  name ="a" > </slot >  </div >  </template > <script > export  default  {  props : {     name : String ,   },   setup (props, { attrs, slots, emit, expose } ) {     console .log ("setup" );     console .log ("props" , props.name );           console .log ("attrs" , attrs.title );     console .log ("slots" , slots.a ());      emit ("comfun" , "向父组件传递数据" );          return  { count :1  };   },   beforeCreate ( ) {     console .log ("beforeCreate" );    } } </script > 
 
context 的 expose 能显式地限制该组件暴露出的 property,当父组件通过 模板 ref  访问该组件的实例时,将仅能访问 expose 函数暴露出的内容。 
setup 发返回值为一个对象,该对象中的属性值是暴露给组件访问的。属性值只有是 composition api 创建的响应式对象才能够像 data 中的属性一样响应式的改变。 
 
2. 常用 API: 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 import  { ref, reactive, toRef, toRefs, readonly, isRef, computed } from  'vue' ;export  default  {  setup ( ) {     let  num = 1 ;                const  count = ref (0 );          count.value ++;               const  user = reactive ({ name :"hi" , age :18  });     user.age ++;                const  name = toRef (user,"name" );     name.value  = "ha" ;                const  { age } = toRefs (user);                               const  userIsRef = isRef (user);               let  add  = ( )=> count.value ++;                    let  nameNum = computed ({       get : ()=>  user.name  + count.value ,       set : val =>  user.name  + count.value  + val      });     nameNum.value  = 1 ;           return  {       num,        count,               ...toRefs (user),        add,        nameNum,      }   }, } 
 
3. watch 与 watchEffect: 
监听属性值变化: 
 
监听属性方法同样需要导入:import { watch, watchEffect } from 'vue';
如下两方法初始时都会默认执行一次,且当方法内的某个属性发生改变时就会执行一次
1 2 3 4 5 6 7 8 9 10 11 watch (()=> {    console .log ("watch" , count.value ); }); const  stop = watchEffect ((oninvalidate )=> {    console .log ("watchEffect" , count.value );     oninvalidate (()=> {        console .log ("before" );      }); },{  }); stop (); 
 
指定监听: 
 
watch 参数1 指定要监听的响应式数据 
参数2 监听方法,newV 监听的新值, oldV 旧值(默认初始化时为 undefined) 
参数3 为监听配置对象,指定监听后默认初始化是不会执行一次的 immediate 为 true 时可以使监听方法初始化时执行一次。 
 
1 2 3 4 5 6 7 8 9 10 11 12 const  a = ref (9 );watch (a, (newV, oldV )=> {    console .log ("watch" , count.value );  }, { immediate : true  });  watch ([a,count],         ([newA, newCount], [oldA, oldCount] )=> {         console .log ("watch" , newA, oldA, newCount, oldCount); }, { immediate : true  }); 
 
监听对象响应式数据的某个属性或想监听响应式数据的 value 时需要使用函数方式返回: 
 
1 2 3 4 watch ([()=> a.value ,()=> user.name ], ([newA, newName], [oldA, oldName] )=> {    console .log ("watch" , newA, oldA, newName, oldName); }, { immediate : true  }); 
 
如果监听 ref 且 ref.value 是深层次对象时需要配置 deep: true 开启深层次监听,而 reactive 默认开启: 
 
1 2 3 4 5 6 watch (refV, (newV, oldV )=> {     }, {     deep : true ,      flush : "pre"   }); 
 
4. 生命周期: 注意使用以下生命周期 API 也需要导入如:import { onMounted } from 'vue'
选项式 API 
Hook inside setup 
 
 
 
beforeCreate 
Not needed*(setup) 
 
 
created 
Not needed*(setup) 
 
 
beforeMount 
onBeforeMount 
创建之前 
 
mounted 
onMounted 
创建完成 
 
beforeUpdate 
onBeforeUpdate 
更新之前 
 
updated 
onUpdated 
更新完成 
 
beforeUnmount 
onBeforeUnmount 
卸载(销毁)之前 
 
unmounted 
onUnmounted 
卸载完成 
 
errorCaptured 
onErrorCaptured 
 
 
renderTracked 
onRenderTracked 
 
 
renderTriggered 
onRenderTriggered 
 
 
activated 
onActivated 
keep-alive 缓存的组件激活 
 
deactivated 
onDeactivated 
keep-alive 缓存的组件停用 
 
当选项 API 与组合式 API 生命周期同时存在时会先执行 setup 中的,也就是先执行组合式 API 生命周期方法。
nextTick: $nextTick 在 setup 中使用 nextTick 代替:
1 2 3 4 5 import { nextTick } from "vue"; const change = async ()=>{   await nextTick(); // 同步调用 } 
 
5. provide 与 inject: 
provide、inject 是 vue 让父组件给子组件提供数据访问的一种方式,使多层子组件(如孙子组件)下依然可以访问到父组件的属性值。 
 
1. 选项式 API: 
如 App.vue 中: 
 
1 2 3 4 5 6 7 8 9 10 data ( ) {  return  {     msg : "this is App msg"    } }, provide ( ) {  return  {     msg : this .msg ,    } } 
 
在第 3 层的子组件中: 
 
1 2 3 4 export  default  {     inject : ['msg' ],  } 
 
2. 组合式 API: 
App.vue 中: 
 
1 2 3 4 5 6 7 8 9 10 import  { ref, provide } from  'vue' ; export  default  {  setup ( ) {     let  msg = ref ("this is App msg" );     provide ('msgStr' , msg);           return  {msg}   } } 
 
子组件中: 
 
1 2 3 4 5 6 7 import  { inject } from  'vue' ; export  default  {  const  msgStr = inject ('msgStr' );       return  {msgStr} } 
 
组合式 API 使用 provide、inject 注入的父属性是 响应式  的,即我们在这里修改 msgStr 的值父组件 msg 也会相应改变。 
 
6. VueRouter: Vue Router 和 组合式 API | Vue Router (vuejs.org) 
this.$router 与 this.$route 在 setup 中无法直接访问,因此 VueRouter 提供了 useRouter 和 useRoute 函数 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import  { useRoute, useRouter, onBeforeRouteLeave, onBeforeRouteUpdate } from  'vue-router' ;export  default  {  setup ( ) {          const  route = useRoute ();      const  router = useRouter ();           watch (()=> route.params , (newId, oldId )=> {       console .log (newId.id );     });          onBeforeRouteLeave ((to, from  )=>  {});     onBeforeRouteUpdate (async  (to, from )=> {});   } } 
 
注意 useRoute、useRouter(包括 Vuex 的 useStore) 等方法获取对象必须在 setup 中的最外层,如果在方法中使用 useRouter() 会是 undefined。 
 
7. Vuex: 组合式API | Vuex (vuejs.org) 
对于 this.$store Vuex 提供了 useStore 函数 
为了访问 state  和 getter ,需要创建 computed 引用以保留响应性,这与在选项式 API 中创建计算属性等效 
 
javascript 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import { computed } from 'vue'; import { useStore } from 'vuex'; // 引入 useStore export default {   setup () {     const store = useStore(); // 必须在 setup 最外层获取对象     // store.commit('increment') store.dispatch('asyncIncrement')     return {       // 在 computed 函数中访问 state       count: computed(() => store.state.count),       // 在 computed 函数中访问 getter       double: computed(() => store.getters.double)     }   } } 
 
Vite + TS 创建项目: 
 
 
Ref 全家桶: 
1 2 3 4 5 6 7 8 9 10 11 12 13 import  { ref, Ref , isRef } from  'vue' type T = {name : string} const  name :Ref <T> = ref ({name : 'ruoxijun' });console .log (isRef (name)); const  myDiv = ref<HTMLDivElement >(); myDiv.value   onMounted (()=> {  myDiv.value .innerText  = "myDiv" ; }); 
 
shallowRef 只支持 浅层次  数据响应,注意 ref 底层有调用 triggerRef(如同一方法中同时更改 ref 与 shallowRef 的值时,shallowRef 视图也会被更新)。
1 2 3 4 5 6 import  { shallowRef, triggerRef } from  'vue' const  o = shallowRef ({name : "haha" });o.value .name  = "xx" ;  triggerRef (o); o.value  = { name : "oo"  };  
 
Reactive 全家桶: 
1 2 3 4 5 6 7 8 import  { reactive, readonly } from 'vue' type  T  =  string[] const  o  =  reactive<T>([]);o.push("hh" ); const  read  =  readonly(o);
 
它与 shallowRef 类似提供浅层数据响应,且会被 ref 与 reactive 影响(每次对 ref 或者 reactive 的更改都会将所有的组件模板渲染更新为最新数据)。
1 2 3 4 5 import  { shallowReactive } from  'vue' const  obj = shallowReactive ({one : {two : "two" }});obj.one .two  = "hh" ;  obj.one  = {two : "xx" }  
 
To 全家桶: 
1 2 3 4 5 6 import  { reactive, toRef } from  'vue' const  o = reactive ({name : "ruoxijun" , age : 18 });const  name = toRef (o, "name" ); name.value  = "haha" ;  o.name  = "xx" ;  
 
toRefs 将响应式对象的每个属性都转为 ref 对象并解构: 
 
1 2 3 4 import  { reactive, toRefs } from  'vue' const  o = reactive ({name : "ruoxijun" , age : 18 });const  {name, age} = toRefs (o);
 
1 2 3 4 import  { reactive, toRaw } from  'vue' const  o = reactive ({name : "ruoxijun" , age : 18 });const  obj = toRaw (o);
 
computed: 
1 2 3 4 5 6 import  { ref, Ref , computed } from  'vue' const  count : Ref <number> = ref (0 );const  con = computed<number>(()=> {  return  count.value  + 1 ;  }); 
 
less: 
安装 less 到开发环境: 
 
1 npm install less less-loader -D 
 
在 style 标签上添加 lang="less" : 
 
1 <style lang="less" scoped></style> 
 
组件传值: 父传子: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 withDefaults (defineProps<{ msg : string  }>(),{  msg : ()=>  "默认值"   }); 
 
子传父: 
1 2 3 4 5 6 7 8 9 10 <script setup lang="ts"> // 定义接收参数的方法 const send = (msg:string)=> {   console.log(msg); } </script> <template> <!-- 绑定子组件的传参事件 -->   <HelloWorld @on-click="send" msg="Vite + Vue" /> </template> 
 
1 2 3 4 5 6 7 8 9 const  emit = defineEmits<{   (e : "on-click" , msg : String ):void  }>(); const  send  = ( )=>{     emit ("on-click" , "传参值" );  } 
 
暴露组件属性: 
1 2 3 4 5 defineExpose ({  count,   send }); 
 
1 2 3 4 5 6 const  hello = ref ();onMounted (()=>  {  console .log (hello.value .count );    hello.value .send (); }); 
 
递归组件: 
使用递归组件: 
 
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 <script setup lang="ts"> import { reactive } from "vue"; import Tree from "./components/Tree.vue"; // 引入递归组件 interface Tree { // 定义递归数据类型   name: string;   checked: boolean;   children?: Tree[]; } const data = reactive<Tree[]>([ // 递归数据   {     name: "1",     checked: false,     children: [       {         name: "1-1",         checked: true,         children: [],       },     ],   },   {     name: "2",     checked: false,     children: [],   },   {     name: "3",     checked: true,     children: [       {         name: "3-1",         checked: false,         children: [           {             name: "3-1-1",             checked: true,             children: [],           },         ],       },     ],   }, ]); </script> <template>   <div> <!-- 使用递归组件并传入递归数据 -->     <Tree :data="data"></Tree>   </div> </template> 
 
实现递归组件 Tree.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 <template>   <div v-for="item in data" @click.stop="treeClick($event)" class="tree">     <input type="checkbox" v-model="item.checked"><span>{{ item.name }}</span>     <!-- 当前组件递归,v-if 停止递归条件 -->     <Tree v-if="item?.children?.length" :data="item?.children"></Tree>   </div> </template> <script setup lang="ts">   interface Tree { // 递归数据类型     name: string;     checked: boolean;     children?: Tree[];   }   const props = defineProps<{     data?: Tree[] // 接收递归数据   }>();      const treeClick = (e: Event)=> {     console.log(e.currentTarget);   } </script> <script lang="ts"> export default {   name: "Tree" // 默认使用文件名作为组件名 } </script> <style scoped> .tree{   margin-left: 25px; } </style> 
 
1 2 3 4 ({}).a   ({}).a .b   ({})?.a ?.b   (null  || undefined ) ?? []  
 
v-model: 
父组件中: 
 
1 2 3 4 5 6 7 8 9 10 11 12 <script setup lang="ts"> import A from "./components/A.vue"; const isShow = ref<boolean>(true); const text = ref<string>("hello"); </script> <template>   <div> 父组件 - isShow:{{isShow}} - text:{{text}} </div>   <div> <button @click="isShow = !isShow"> 开关 </button> </div>   <A v-model="isShow" v-model:textVal="text"></A> </template> 
 
子组件中: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <template>   <div v-if="modelValue">     <div> A - isShow:{{modelValue}}</div>     <input :value="textVal" @input="textValChange">     <button @click="close"> 关闭 </button>   </div> </template> <script setup lang="ts"> const props = defineProps<{   modelValue: boolean, // v-model 默认为 modelValue 接收   textVal: string // 接收 v-model:textVal }>(); const emits = defineEmits(['update:modelValue', 'update:textVal']); const close = ()=>{   emits('update:modelValue', false); } const textValChange = (e:Event)=>{   // e.target 默认 EventTarget 类型需要断言为 HTMLInputElement 才能获取到 value 值   const el = e.target as HTMLInputElement;   emits('update:textVal', el.value); } </script> 
 
自定义修饰符: 
 
1 2 3 4 5 6 7 const  props = defineProps<{  modelValue : boolean,    textVal : string    textValModifiers?:{      isAdd : boolean    } }>(); 
 
组件上 v-model:textVal.isAdd="text" ,组件内 props?.textValModifiers?.isAdd 有使用修饰符值为 true 。
动态组件: 使用方式: 
对象方式实现动态组件: 
 
如果使用 ref 或者 reactive 包装组件 vue 会代理组件属性监听变化,而这会耗费性能且 vue 会在控制台给出警告。因此建议在对象中使用 markRaw 包裹组件而普通变量使用 shallowRef 包裹组件。 
 
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 <script setup lang="ts"> import { ref, reactive, markRaw, shallowRef } from "vue"; import A from "./components/A.vue"; import B from "./components/B.vue"; import C from "./components/C.vue"; interface o { name: string; com: any; } const comIndex = ref(0); const componentId = shallowRef(A); // 不建议使用 ref const data = reactive<o[]>([   { name: "A", com: markRaw(A), }, // 使用 markRaw 包裹   { name: "B", com: markRaw(B), },   { name: "C", com: markRaw(C), }, ]); const swTab = (com: any, index: number)=>{   componentId.value = com;   comIndex.value = index; } </script> <template>   <div class="tab">     <div :class="{ active: comIndex === index }" class="tab_item"       @click="swTab(item.com, index)"       v-for="(item, index) in data" >       {{ item.name }}     </div>   </div>   <component :is="componentId"></component> </template> 
 
字符串方式实现动态组件: 
 
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 <script setup lang="ts"> import { ref, reactive } from "vue"; interface o { name: string; com: any; } const comIndex = ref(0); const componentId = ref("A"); // 使用组件字符串名 const data = reactive<o[]>([   { name: "A", com: "A", },   { name: "B", com: "B", },   { name: "C", com: "C", }, ]); const swTab = (com: any, index: number)=>{   componentId.value = com;   comIndex.value = index; } </script> <script lang="ts"> import A from "./components/A.vue"; import B from "./components/B.vue"; import C from "./components/C.vue"; export default {   components:{ A, B, C }// 注册组件 } </script> 
 
异步组件: 
1 2 const  A = defineAsyncComponent (()=>  import ("./components/A.vue" ));
 
transition: 基础使用: 
使用 transition 标签包裹需要添加动画的元素, name  的值是定义动画 class 的前缀: 
 
1 2 3 <transition  name ="fade" >   <component  :is ="componentId" > </component >  </transition > 
 
定义动画: 
 
1 2 3 4 5 6 7 8 .fade-enter-from { opacity : 0 ; } .fade-enter-active { transition : all .5s  ease; } .fade-enter-to { opacity : 1 ; } .fade-leave-from { opacity : 1 ; }.fade-leave-active { transition : all .5s  ease; }.fade-leave-to { opacity : 0 ; }
 
animate.css: 
transition 也支持指定动画的 class 名: 
 
1 2 3 4 5 6 7 8 9 10 <transition      enter-active-class =""      enter-from-class =""      enter-to-class =""      leave-active-class =""      leave-from-class =""      leave-to-class =""    >     <component  :is ="componentId" > </component >    </transition >  
 
安装 animate.css: 
 
Animate.css 官网 
 
在组件中引入 animate.css: 
 
 
使用: 
 
新版中都需要添加 animate__animated 类, duration 属性可以指定执行时间(毫秒),还可通过对象属性方式单独设置显示和隐藏的动画时间 {enter: 500, leave: 500} 。
1 2 3 4 5 6 7 <transition    :duration ="500"    enter-active-class ="animate__animated animate__rubberBand"    leave-active-class ="animate__animated animate__swing"  >   <component  :is ="componentId" > </component >  </transition > 
 
生命周期: 
生命周期方法 
对应 Class 
时期 
 
 
@before-enter 
enter-from-class 
显示动画之前 
 
@enter 
enter-active-class 
显示过度 
 
@after-enter 
enter-to-class 
显示结束 
 
@enter-cancelled 
 
显示过度被打断 
 
@before-leave 
leave-from-class 
隐藏动画之前 
 
@leave 
leave-active-class 
隐藏过度 
 
@after-leave 
leave-to-class 
隐藏结束 
 
@leave-cancelled 
 
隐藏过度被打断 
 
所有生命周期方法参数 1 是动画元素对象,而 过度方法  接收参数 2  它是过度回调代表过度执行完成,默认它在动画结束后自动执行。 
 
1 2 3 const  enterActive  = (el: Element, done: Function  )=>{  done ();  } 
 
appear: 
appear 页面初次加载效果,只在页面初始化后执行一次: 
 
1 2 3 4 5 6 7 <transition      appear-from-class =""      appear-active-class =""      appear-to-class =""    >     <component  :is ="componentId" > </component >    </transition >  
 
TransitionGroup: 
为列表添加动画属性,使用方式与 transition 大体一致。 
默认不会给列表多包装一层,可使用 tag 属性指定包装一层元素。 
列表必须添加 key 属性。 
 
1 2 3 4 5 6 7 8 <transition-group    tag ="div"    enter-active-class ="animate__animated animate__wobble"    leave-active-class ="animate__animated animate__rubberBand"    move-class ="列表元素平移动画(元素位置改变)"  >   <div  class ="list"  v-for ="item in list"  key ="item" > {{ item }}</div >  </transition-group > 
 
gsap.js: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <script setup lang="ts"> import gsap from "gsap"; const number = reactive({   numberVal: 0,   showNumber: 0 }); const numberChange = (n: number)=>{   gsap.to(number, {     duration: 0.3, // 过度时间     showNumber: n // 注意该属性名与显示动画的属性值名要相同   }); } </script> <template>   <input type="number" v-model="number.numberVal" @input="numberChange(number.numberVal)" >   <span>{{ number.showNumber.toFixed(0) }}</span> </template> 
 
Mitt: 
vue 中 import 第一次加载文件时执行 js 并将获取到的内容放入缓存中,之后 import 文件都是从缓存中获取,因此 import 获取的对象实例都是 单例  。 
 
安装 mitt: 
 
Vue2 使用 EventBus 进行组件通信,而 Vue3 推荐使用 mitt 。
 
main.ts 中全局挂载: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import  mitt from  'mitt' ; const  app = createApp (App );const  Mitt  = mitt (); declare module  "vue"  {   export  interface ComponentCustomProperties  {     $Bus : typeof  Mitt ;   } } app.config .globalProperties .$Bus  = Mitt ; app.mount ('#app' ); 
 
组件中使用: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import  { getCurrentInstance } from  "vue" ;const  instance = getCurrentInstance ();const  emit  = ( )=>{     instance?.proxy ?.$Bus .emit ("on-emit" , "传递参数值(可多个)" ); } import  { getCurrentInstance } from  "vue" ;const  instance = getCurrentInstance ();instance?.proxy ?.$Bus .on ("on-emit" ,(data )=> {   console .log (data); }); instance?.proxy ?.$Bus .on ("*" ,(type,data )=> {   console .log (type, data); }); 
 
unplugin-auto-import: antfu/unplugin-auto-import 
安装: 
 
1 npm i -D unplugin-auto-import 
 
配置 vite.config.ts : 
 
1 2 3 4 5 6 7 8 import  AutoImport  from  'unplugin-auto-import/vite' export  default  defineConfig ({  plugins : [vue (), AutoImport ({     imports : ['vue' ],      dts : 'src/auto-import.d.ts'     }),] }) 
 
不用 import 在组件中直接使用 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 <script setup lang="ts"> import { Directive, DirectiveBinding, ref } from "vue"; import A from "./components/A.vue"; type Dir = { val: string } // 自定义指令 v-my,赋值函数方式是 mounted 与 updated 同时触发相同行为的简写 const vMy:Directive = (el, binding:DirectiveBinding<Dir>)=> {   console.log(binding.value.val); } /* 完整写法与生命周期: const vMy:Directive = {   created(){},   beforeMount(){},   mounted(){},   beforeUpdate(){},   updated(){},   beforeUnmount(){},   unmounted(){}, } */ </script> <template>   <A v-my="{val: 'hello'}"></A> </template> 
 
Hooks: Mixins: 在 vue2 中常使用 Mixins 抽取公共属性,mixins 中的属性会被组件中的同名属性覆盖,mixins 中的生命周期函数会比组件中同名生命周期函数先执行(不会覆盖)。
1 2 3 4 5 6 export  default {    data ( ){         return {...}     },     methods :{} } 
 
1 2 3 4 5 6 <script> import mixin from './mixins'; // 引入 mixins.js export default {     mixins:[mixin], // mixins 数组,属性自动注入组件中 } </script> 
 
Hooks: 在 vue3 中推荐使用 Hooks 使用函数导出与引入的方式。
hooks/index.ts (名称可自定义)中: 
 
1 2 3 4 5 6 import  { Ref , ref } from  "vue" ; export  default  function ( ):Ref <string >{  const  hello = ref ("hello" );   return  hello; } 
 
1 2 3 import  hook from  "./hooks" ; const  hello = hook (); 
 
定义全局变量: vue2: 1 Vue .prototype  .$my  = "hello" ;
 
vue3: 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const  app = createApp (App );declare  module  "vue"  {  export  interface  ComponentCustomProperties  {     $My : string ,     $MyFun : Function    } } app.config .globalProperties .$My  = "hello" ; app.config .globalProperties .$MyFun  = ()=> {} app.mount ('#app' ); 
 
1 2 3 4 5 6 7 8 9 <script setup lang="ts"> import { getCurrentInstance } from "vue"; const app = getCurrentInstance(); // 获取当前实例 app?.proxy?.$MyFun(); </script> <template>   <div> 直接调用 - {{ $My }} </div> </template> 
 
样式穿透: 
1 2 3 /deep/.el-switch__core {   background-color : black; } 
 
1 2 3 :deep (.el-switch__core) {   background-color : black; } 
 
:slotted() 插槽选择器
 
:global() 全局选择器
 
动态 css:
 
 
1 2 3 4 5 6 7 8 9 10 11 12 <script lang="ts" setup> import { ref } from 'vue' const color = ref<string>("red"); const style = ref({ color: "red" }); </script> <style lang="less" scoped> div{   color: v-bind(color);   background-color: v-bind('style.color'); // 对象需要引号方式包裹 } </style> 
 
Pinia 安装使用: Pinia 官网地址:Pinia (vuejs.org) 
安装 Pinia: 
 
 
在 main.ts 中注册引入: 
 
1 2 3 4 5 6 7 8 9 import  { createPinia } from  'pinia' ; const  store = createPinia (); const  app = createApp (App );app.use (store);  app.mount ('#app' ); 
 
在 store\index.ts 内: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import  { defineStore } from  "pinia" ;export  const  useStore = defineStore ('main' , {  state : ()=> ({          name : 'haha'    }),   getters : {        },   actions : {        } }); 
 
组件中使用: 
 
1 2 3 4 import  { useStore } from  './store' ;const  store = useStore ();console .log (store.name );
 
state: 1. 直接修改: 1 store.name  = "ruoxijun" ; 
 
2. $patch(推荐): 1 2 3 4 5 6 7 8 store.$patch({   name : "aaa"  }); store.$patch((state:any  )=> {   state.name  = "ooo" ; }); 
 
3. $state: 此方式需要修改 state 中的所有属性,才能使用。
1 store.$state  = { name : "ccc"  } 
 
4. actions(推荐): 
在 store 的 actions 中定义方法: 
 
1 2 3 4 5 6 7 8 9 export  const  useStore = defineStore ('main' , {  state : ()=> ({ name : "haha"  }),   actions : {          setName (name : string ): void {       this .name  = name;     }   } }); 
 
在组件中使用方法: 
 
 
5. storeToRefs: store 可以直接解构出 state 中的属性但不具有响应式,因此 Pinia 提供了 storeToRefs:
1 2 3 4 import  { storeToRefs } from  'pinia' ;const  { name } = storeToRefs (store);name.value  = "yyy" ; 
 
getters、actions: getters、actions 中定义的方法可以使用 this 调用 store 中的属性或方法。
API: $reset:  
$subscribe: 1 2 3 4 store.$subscribe((mutations, state )=> {   console .log (mutations, state); }, { detached : true  });   
 
$onAction: 1 2 3 4 store.$onAction((context )=> {   console .log (context); }, true );  
 
持久化存储: