Vue3学习笔记

Vue3快速上手

1-vue3介绍

  • Vue.js 3.0 版版在2020年9月份发布.
  • Vue3支持vue2的大多数特性

性能提升

  • 项目打包体积更小
  • 初次渲染更快, 更新渲染更快
  • 需要的运行内存更小
  • 使用Proxy代替Object.defineProperty实现数据响应式
  • 重写虚拟DOM的实现和Tree-Shaking
  • 更好的支持Typescript

2-获取vue3.0

官网:

https://v3.cn.vuejs.org/guide/introduction.html
  1. cdn方式获取

    https://unpkg.com/vue@next
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <!-- 导入vue3源码包 -->
        <script src="./vue.global.js"></script>
    </head>
    <body>
        <!-- 应用程序的挂载点 -->
        <div id="app">
            <h1></h1>
        </div>
    </body>
    <script>
        /*
                vue2:  
                   
                new Vue({
                    el:'#app',
                    data:{
       
                    },
                    methods:{
       
                    }
                });
        */ 
        // 1-创建vue实例对象
        const app=Vue.createApp({
            // data属性节点必须采用函数的写法
            data(){
                return {
                    msg:'hello vue3'
                }
            }
        });
        // 2-挂载应用实例
        app.mount('#app');
    </script>
    </html>
    
  2. vue cli脚手架

    • 安装最新版本的vue cli 脚手架

      npm i @vue/cli -g
      
    • 创建项目

      vue create 项目名称
      
    • 选择vue3版本的默认选项

    • 启动项目

      npm run serve
      
  3. vite构建工具

    vite是vue官方发布的一款构建工具

    vite底层不是基于webpack

    vite底层是基于浏览器对ES模块语法的支持来进行项目构建, 所有构建速度很快

    1. 创建项目

      npm init vite 项目名称
      
    2. 安装项目依赖

      在项目的根目录运行命令

      npm install
      // 或者
      npm i
      
    3. 启动项目

      项目根目录运行启动命令

      npm run dev
      
    4. 默认监听端口号3000

      http://localhost:3000
      

3-新增特性

Fragment模板碎片

  • vue2中组件的模板必须有一个唯一的根标签
  • vue3中组件模板可以有多个根标签

编辑器禁用插件 vetur ,安装插件 Vue Language Features (Volar)

<template>
    <span></span>
    <span></span>
</template>
<script setup>
//一旦script上加上了setup,那么就不再需要注册子组件,引入即可使用
import HelloWorld from './components/HelloWorld.vue'
import Home from "./components/Home.vue"
</script>

Composition (组合) API

  • 作用: 将不同组件的相同部分抽离出来, 单独维护, 目的是提高代码复用率

  • setup

    • 用法: 在组件对象中添加setup方法
    • 该方法在组件创建完成之前自动调用, 所用不能在其内部使用this访问组件对象
    • 该方法中返回的数据会自动和组件对象中的data合并
    • 该方法中返回的方法会自动和组件对象中的methods和并
    <template>
        <span></span>
        <span></span>
        <button @click="changeName">李四</button>
    </template>
      
    <script>
    export default {
        // 1.类似混入
        // 2.返回一个对象,属性会合并到data,方法会合并到methods
        // 3.组件创建之前执行,所以没有this
        setup() {
            console.log(this);//undefined
            return {
                name: "张三",
                age: 20,
                changeName() {
                    this.name = "李四"
                    console.log(this.name);//此刻数据会变,页面是不渲染的
                }
            }
        },
        data() {
            return {
                msg: "hello",
                text: "vue3"
            }
        }
    }
    </script>
    
  • reactive

    • 作用: 帮助我们创建响应式数据对象

    • 语法:

      import {reactive} from 'vue';
      export default {
          setup(){
              // 创建响应式数据对象
              const obj=reactive({
          
              });
              return obj;
          }
      }
      
    • 案例:

      import {reactive} from "vue"
      export default {
             
          setup() {
              let obj=reactive({
                  name:"zs",
                  age:20,
                  //该函数的改变会引起页面的重新渲染
                  changeName(){
                      this.name='ls'
                  }
              })
              return obj
          },
             
          methods:{ 
              //该函数的改变会引起页面的重新渲染
              changeAge(){
                  this.age=30
              }
          }
      }
      
  • ref

    • 作用: 基于基本数据类型(字符串, 布尔, 数值) 创建一个响应式的数据对象

    • 语法

      import {ref} from 'vue';
      
      export default {
          setup(){
              // 使用ref基于基本数据类型创建响应式数据对象
             	const name=ref('张三');
              const age=ref(20);
                 
          	return {
                  name,age,
                  changeName(){
                      this.name='ls'
                  }
              }
          }
      }
      
    • 视图中引用直接通过数据名称引用即可

          
      
  • toRefs

    • 作用: 让reactvie方法创建的响应式数据对象, 支持ES6的解构赋值操作( reactive创建的响应式数据对象默认不支持ES6解构赋值, 因为会使其失去响应式 )

    • 语法: 参数必须是reactive创建的响应式数据对象

      import {torRefs} from 'vue';
      
      export default {
          setup(){
              let {name,age,changeName}=toRefs(reactive({
                  name:"zs",
                  age:20,
                  changeName(){
                      this.name='ls'
                  }
              }))
              return {
                  name,age,changeName
              }
          },
      }
      

组合API中调用生命周期钩子函数

  • 如果要在setup方法中调用组件生命周期钩子函数: 在原来的生命周期钩子函数名称前加on关键, 并且需要保持小驼峰的命名方式
  • 不能在setup方法中调用beforeCreate,created者2个生命周期钩子函数 ( 因为beforeCreate,created这两个生命周期钩子函数的执行时间和setup方法接近 )

生命周期

  • 语法

    // 导入
    import {onMounted,onUpdated,onUnmounted} from 'vue';
    export default {
        setup(){
            onMounted(function(){
                console.log('组件视图挂载完成');
            });
            onUpdated(function(){
                console.log('组件更新完成');
            });
            onUnmounted(function(){
                console.log('组件卸载完成');
            });
        }
    }
    

computed计算属性

  • vue2

    export default {
        computed:{
            // 计算属性方法
        }
    }
    
  • vue3

    • 使用方式1

      export default {
          computed:{
              // 计算属性方法
          }
      }
      
    • 使用方式2

      import {computed} from 'vue';
      
      const computedData=computed(()=>{})
      
    • 案例:

      import { ref,computed } from "vue"
      
       setup() {
                 
              let msg = ref("hello")
              let reverse=computed(()=>msg.value.split("").reverse().join(""))
              return {
                  name, age, changeName,msg,reverse
              }
          },
      

留言板 案例

<template>
  <div class="con">
      <h3>留言板</h3>
      <div>user:</div>
      <div>昵称:<input type="text" v-model="user.nickname"></div>
      <div>内容:<input type="text" v-model="user.con"></div>
      <button @click="add">添加</button>
      <hr>
      <div class="item" v-for="(item,index) in list" :key="item.id">
          <h4>用户:</h4>
          <div>内容:</div>
          <button @click="del(index)">删除</button>
      </div>
  </div>
</template>

<script>
import {reactive,toRefs} from "vue"
export default {
    setup(){
        //初始化数据
        const {user,list}=toRefs(reactive({
            user:{
                nickname:"",
                con:""
            },
            list:[
                {id:1,nickname:"jack",con:"con1"},
                {id:2,nickname:"rose",con:"con2"}
            ]
        }))
        //添加
        const add=()=>{
            list.value.push({
                id:new Date().getTime(),
                //注意:此处读取user的值需要使用user.value
                ...user.value
            })
        }
        //删除
        const del=index=>{
            //注意:此处使用list需要使用list.value
            list.value.splice(index,1)
        }
        return {
            user,
            list,
            del,
            add
        }
    }
}
</script>

<style scoped>
.con{
    width: 500px;
    margin: 20px auto;
}
.item{
    padding: 10px;
    margin: 10px;
    border: 1px solid #ccc;
}
</style>

watch侦听器

  • vue2

    export default {
        // 侦听器
        watch:{
              
        }
    }
    
  • vue3

    • 使用方式1

      export default {
          // 侦听器
          watch:{
                  
          }
      }
      
    • 使用方式2

      • 通过watch方法创建的普通侦听器无法侦听到数组元素的变化, 必须使用深度侦听器
      import {watch} from 'vue';
      export default {
      	setup(){
              // 普通侦听器
              watch(被侦听的数据,function(value,oldValue){
                      
              });
              // 深度侦听器
              watch(被侦听的数据,function(value,oldValue){
                      
              },{deep:true});
          }
      }
      
    • 自己笔记:

      // watch()是可以监听到对象和普通数据的改变的, 这种是在setup中使用的
      // watch:{}监听对象还是需要深度监听
      
      import { ref, watch, reactive } from "vue"
      export default {
          setup(){
              let name = ref("")
                  
              // 普通侦听器
              /*watch(name,(n,o) => {
                  console.log(n,o);
              })*/
          
              // 深度监听
              watch(name, (n, o) => {
                  console.log(n, o);
              }, { deep: true })
          
          
              // 可以监听到json的改变
              let user = reactive({
                  name: "妲己"
              })
              watch(user,(n,o)=>{
                  console.log(n,o);
              })
              return {
                  name,
                  user
              }
          }
      }
      

h渲染函数

  • vue2中

    系统会自动在render中注入渲染函数

    import App from './App.vue';
    new Vue({
        // h是系统自动注入的渲染函数
        render(h){
           return h(App); 	
        }
    });
    
  • vue3中

    vue3中提供了一个h渲染函数, 我们需要手动导入才能使用, 系统不会自动将其注入到render中

    import { createApp,h} from 'vue'
    import App from './App.vue'
    // createApp(App).mount('#app')
    createApp({
        render(){
            // 调用渲染函数h进行组件渲染
            // return h(App);
            // h创建元素对象: h('元素名称',{/*属性集合*/},['子节点'])
            return h('h1',{style:{color:'red'}},'hello vue3');
        }
    }).mount('#app')
      
    

teleport: 瞬移组件的位置

  • teleport是一个vue3中提供的系统组件

  • 作用: 可以让开发者动态指定组件内的某个视图结构的挂载位置

  • 语法

    <teleport to="挂载节点">
    	<div></div>
    </teleport>
    
  • 案例

    <!--to后跟css选择器 to="body" to=".haha" to="#aa" -->
    <teleport to=".haha">
        <div class="modal" v-show="isshow">模态框</div>
    </teleport>
    

4-更新特性

生命周期钩子函数

  • 销毁阶段生命周期钩子函数名称发生了变换

  • vue2中

    export default {
        // 销毁前
        beforeDestroy(){
              
        },
        // 销毁后
        destroyed(){
              
        }
    }
    
  • vue3中

    • vue3中从组件对象中移除了销毁组件实例的方法$destroy
    export default {
        // 销毁前
        beforeUnmount(){
              
        },
        // 销毁后
        unmounted(){
              
        }
    }
    
  • 自己的代码

    export default {
        data(){
            return {
                msg:"hello"
            }
        },
        beforeCreate(){
            console.log("创建之前");
        },
        created(){
            console.log("创建完成");
        },
        beforeMount(){
            console.log("挂载之前");
        },
        mounted(){
            console.log("挂载完成");
        },
        beforeUpdate(){
            console.log("更新之前");
        },
        updated(){
            console.log("更新完成");
        },
        beforeUnmount(){
            console.log("卸载之前");
        },
        unmounted(){
            console.log("卸载完成");
        }
    }
    

全局组件注册方式

  • vue2中注册全局组件

    Vue.component('组件名称',{
        /* 组件对象 */
    });
    
  • vue3中注册全局组件

    import {createApp} from 'Vue';
    // 创建实例对象
    const app=createApp();
    // 注册全局组件
    app.component('组件名称',{
        // 组件对象
    })
    
  • 案例:

    import MyTeleport from "./components/05-Teleport.vue"
    //创建app实例
    let app = createApp(App)
    //通过app实例注册全局组件
    app.component("MyTeleport", MyTeleport)
      
    //挂载到节点上
    app.mount("#app")
    

函数式组件

  • 通过函数方式定义的组件

  • 特点: 函数组件默认没有状态数据和生命周期

  • 语法

    • 定义

      import { h } from 'vue';
      export default function FunctionComponent(props){
          // console.log(props);
          return h('h1',{style:{color:'red'}},props.msg);
      }
      
    • 调用

      <script>
      // 导入函数组件
      import FunctionComponent from './pages/FunctionComponent';
      export default {
        // 注册组件
        components: { FunctionComponent }
      };
      </script>
      <template>
          <!--调用函数组件-->
        <FunctionComponent msg="hello function component"/>
      </template>
          
      <style>
      </style>
          
      

###

异步组件

  • vue2中
const Home=()=>import('./pages/Home.vue');
const asyncModal = {
  // vue2中通过component属性节点指定目标组件
  component: () => import('./Modal.vue'),
  // 延时时长
  delay: 200,
  // 超时时间  
  timeout: 3000,
  // 错误处理组件 
  error: ErrorComponent,
  // 加载中的组件  
  loading: LoadingComponent
}
  • vue3中
import {defineAsyncComponent} from 'vue';
const Home=defineAsyncComponent(()=>import('./pages/Home.vue'));
const asyncModalWithOptions = defineAsyncComponent({
  // vue3中通过loader属性节点指定目标组件  
  loader: () => import('./Modal.vue'),
   // 延时时长
  delay: 200,
  // 超时时间  
  timeout: 3000,
  // 错误处理组件 
  error: ErrorComponent,
  // 加载中的组件  
  loading: LoadingComponent
})

v-for指令中使用ref属性

  • vue2中

    <ul>
    	<li ref="list" v-for="item in list" :key="item"></li>
    </ul>
    
    this.$refs.list[0];
    this.$refs.list[1];
    
  • vue3中: 需要手动维护一个数组用来保存每一个子元素对象

    <ul>
    	<li :ref="addRef" v-for="item in list" :key="item"></li>
    </ul>
    
    export default {
        data(){
            return {
              	// 保存列表元素的元素对象   
           		refList:[]     
            }
        },
        methods:{
            // el: 系统自动注入的参数, 子元素的元素对象
            addRef(el){
        			this.refList.push(el);
    				}
        },
        // 在组件更新之前, 清空数组中的元素, 否则该数组中的元素会越来越多
        beforeUpdate(){
            this.refList=[];
        }
    }
    

v-if和v-for结合使用

  • vue2中

    v-if和v-for指令同时应用于同一个元素之上, v-for指令优先级会更高, v-if会作用域v-for循环遍历产生的每一个子元素之上

  • vue3中

    v-if指令的优先级高于v-for指令, v-if指令执行, 然后执行v-for指令

  • vue3.0案例

    <template>
      <div>
        <ul>
          <!-- 此刻index会报错为未定义,原因是v-if的优先级高于v-for -->
          <!-- <li v-for="(item,index) in arr" :key="item" v-if="index<6"></li> -->
          <li v-for="(item) in showArr" :key="item"></li> 
        </ul>
      </div>
    </template>
      
    <script>
    import { toRefs, reactive, computed } from "vue";
    export default {
      setup() {
        const { arr } = toRefs(
          reactive({
            arr: [
              "html",
              "css",
              "js",
              "jq",
              "node",
              "express",
              "vue",
              "react",
              "angular",
              "小程序"
            ]
          })
        );
        //设计一个计算属性,用来展示,解决v-if+v-for
        let showArr = computed(() => arr.value.slice(0, 6));
        return { arr, showArr };
      }
    };
    </script>
      
    <style>
    </style>
    

自定义指令定义方式 [暂未代码演练,使用较少]

  • vue2中

    Vue.directive('指令名称',function(el, binding, vnode){
          
    });
    
  • vue3中

    然而,在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使钩子的目标事件十分相似。我们现在把它们统一起来了:

    • created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
    • bind → beforeMount
    • inserted → mounted
    • beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
    • update → 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated
    • componentUpdated → updated
    • beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
    • unbind -> unmounted
    const app=createApp();
    app.directive('指令名称',function(el, binding, vnode){
          
    });
    

$attrs包含class&style

  • vue2中
    • this.$attrs包含调用组件的时候通过自定义属性所传递的数据, 但是不包含class属性和style属性
  • vue3中
    • this.$attrs: 调用组件的时候通过自定义属性所传递的所有数据, 包含class属性和style属性
      • 当子组件只有一个唯一的根元素的时候, 系统会将所有的自定义属性平移到组件视图的根元素上
      • 如果组件视图存在多个根元素, 则不会处理
<div class="box">
  <h3>$attr包含了style和class</h3>
  <!-- vue2.0中,$attrs中没有包含styleclass,但是classstyle也会作用在子组件的根元素上。 -->
  <!-- vue3.0中,$attrs 直接包含styleclass,而且classstyle会直接作用在子组件的根节点上。 -->
  <Child a="10" b="20" class="lime" style="color:#fff;"></Child>
</div>

emits属性节点

  • vue3中针对组件对象新增的属性节点

  • 作用:

    • 告诉系统组件调用标签上添加的事件是一个自定义事件, 需要通过自定义事件的方式进行触发( vue3中默认会将组件调用标签上的事件作为原生事件进行触发)
    • 避免当我们的定义的自定义事件名称和系统事件名称出现冲突的时候, vue3直接把自定义事件当做系统事件来触发
    <template>
      <div>
          <h1>EmitsTest子组件</h1>
          <button @click="$emit('click')">触发自定义事件click</button>
      </div>
    </template>
      
    <script>
    export default {
      // 通过emits属性节点告诉vue, 这是一个自定义事件, 需要通过自定义事件的方式进行触发
      emits:['click']
    }
    </script>
    
 <div class="box">
    <h3>$emit对于原生事件的改写</h3>
    <!-- 
            vue2.0中,组件上的 @click 是一个自定义事件,需要子组件通过$emit("click")来触发;
            vue2.0中,如果希望组件的@click是原生的click事件,那么需要通过使用@click.native 
        -->
    <!-- 
          对于Vue3.0来说,组件上绑定@click,此刻,组件中通过原生的click和$emit('click')都可以触发;
          如果希望只是自定义事件的click,需要设置 emits:['click']

       -->
    <Child @click="log"></Child>
  </div>

过渡动画

过渡动画

  1. 过渡动画选择器名称的变化
    • v-enter=>v-enter-from: 元素进入之前的过渡动画选择器名称
    • v-leave=>v-leave-from: 元素离开之前的过渡动画选择器名称
  2. transition组件属性的变化
    • enter-class=>enter-from-class: 指定元素进入之前需要的过渡动画选择器

    • leave-class=>leave-from-class: 指定元素离开之前需要的过渡动画选择器

    • 案例:

       <div>
            <button @click="isshow=!isshow">切换</button>
            <transition 
            enter-active-class="animate__animated animate__bounceInLeft" 
            leave-active-class="animate__animated animate__bounceOutRight"
            >
                <div v-show="isshow">内容</div>
            </transition>
        </div>
      
  3. transition-group组件默认不再自动渲染一个span标签, 但是依然可以通过tag属性指定transition-group渲染的元素名称.

    <transition-group
        tag="ul"
        appear
        enter-active-class="animate__animated animate__bounceInLeft"
        leave-active-class="animate__animated animate__bounceOutRight"
        >
        <li v-for="(item,index) in arr" :key="item" @click="arr.splice(index,1)"></li>
    </transition-group>
    

全局属性app.config.globalProperties

  • 作用: 给Vue注册全局功能( 给每一个vue组件提供全局的属性或者方法 )

  • vue2中创建vue应用

    const app=new Vue({
          
    });
    
    • vue2中给Vue注册全局功能: 在Vue的原型对象添加属性或者方法, 这样每一个vue组件都可以访问, 每一个vue组件都是Vue的实例对象( 利用面向对象的继承性 )
  • vue3中创建应用对象

    const app=Vue.createApp({
          
    });
    
    • vue3中给vue注册全局功能: 必须借助全局属性app.config.globalProperties
  • 需求: 希望在每一个vue组件中都可以通过组件对象访问axios

    • 下载安装axios

      npm i axios
      
    • 必须使用app.config.globalProperties全局注册axios

      app.config.globalPropperties.$axios=axios;
      
  • 案例:

    import { createApp } from "vue"
    const app=createApp(App)
      
    // 给vue所有的实例添加一些属性和方法 
    app.config.globalProperties.msg="这是一个全局的数据";
    app.config.globalProperties.fn=()=>{
        console.log("这是一个全局的方法");
    }
      
    app.mount("#app")
    

插件注册

  • vue2中插件注册

    Vue.use();
    
  • vue3中插件注册

    // app: 是通过Vue.createApp()方法创建的应用对象
    app.use();
    
    • 功能: 将axios自动注册成全局对象( 在每一个组件中都可以通过this来访问axios )

    • 开发vue插件

      • 函数方式

        // app: 系统自动注入, 应用对象
        // options: 调用者通过app.use()方法手动传入的配置对象(可选)
        function MyAxios(app,options){
                  
        }
        
      • 对象方式

        const MyAxios={
            // app: 系统自动注入的应用对象
            // options: 调用者通过app.use()方法手动传递的配置对象(可选)
            install(app,options){
                      
            }
        }
        
        // 导入axios
        import axios from 'axios';
        export default {
            // app: 系统自动注入的应用对象
            install(app){
                // 通过app.config.globalProperties全局注册axios
                app.config.globalProperties.$http=axios;
            }
        }
        

v-model实现组件双向数据绑

  • vue2中使用v-model实现组件双向数据绑定

    <Modal v-model="isShow"></Modal>
    
    1. 在组件的调用标签上添加了一个自定义属性value, 通过value属性向组件内部传递数据

    2. 在组件的调用标签上通过v-on监听了input事件

      <Modal :value="isShow" v-on:input="isShow=$event"></Modal>
      
    3. 希望对v-model指令背后使用的自定义属性名称和自定义事件名称进行修改

      • 在组件内部通过model属性节点对自定义属性名称和自定义事件名称进行修改
      export default{
          model:{
              prop:'',		// 修改自定义属性名称
              event:''		// 修改自定义事件名称
          }
      }
      
  • vue3中使用v-model指令实现组件双向数据绑定

    <Modal v-model="isShow"></Modal>
    
    1. 在组件的调用标签上添加了一个自定义属性modelValue, 通过modelValue向组件内部传递数据

    2. 在组件的调用标签上通过v-on监听了update:modelValue

    3. 希望对v-model指令背后的自定义属性名称和自定义事件名称进行修改

      <Modal v-model:isShow="isShow"></Modal>
      
      1. 在组件内部要通过isShow来接收数据
      2. 在组件内部要通过$emit()触发事件update:isShow
    4. vue3中允许在同一个组件的调用标签上多次使用v-model指令

       <Modal v-model:isShow="isShow" v-model:msg="msg"/>
      
      1. 相当于在组件的调用标签上添加了自定义属性isShow, msg, 同时监听了两个自定义事件update:isShowupdate:msg

5-移除特性

$children属性

  • vue2中可以通过组件属性$children获取子组件的组件对象
  • vue3中建议使用ref属性来操作子组件

native事件修饰符

  • vue2中可以通过native事件修饰符修饰某个事件, 被修饰的事件将被作为原生事件处理, 可以在子组件的内部使用原生方式对其进行触发

  • vue3中移除了native事件修饰符

  • vue3中如果希望某个事件可以在子组件的内部被当做原生事件来处理?

    • 在vue3中组件调用标签上注册的事件, 默认都会当做原生的事件进行触发

    • 在vue3中如果我们希望组件调用标签上所注册的事件是一个自定义事件, 可以在组件内部通过属性节点emits来进行声明, 在emits属性节点中生命过的事件就不会当做系统事件来处理

      export default {
          // 此处声明过的事件, 会认为是自定义事件, 必须通过自定义事件的方式进行触发, 即使事件明层和系统事件名称冲突, 也不会通过系统事件的触发方式触发该事件
          emits:['click']
      }
      

filter过滤器

  • vue2中过滤器的使用

    • 作用: 对视图中即将输出的数据进行预处理

    • 定义

      • 全局过滤器: 在全局作用域内都可以使用
      // data: 系统自动注入, 待处理的数据
      Vue.filter('过滤器名称',function(data){
         // 必须返回处理结果 		
      });
      
      • 私有过滤器: 只能在定义地方使用
      export default {
          filters:{
              // data: 系统自动注入, 待处理的数据
              过滤器名称:function(data){
                  // 必须返回处理结果 			
              }
          }
      }
      
    • 使用: 只能在插值表达式和v-bind指令中使用

          
      
  • vue3中建议使用自定义方法或者computed计算属性来代替过滤器

vm.$set(Vue.set)

  • vue2中可以使用vm.$set方法解决通过数组下标方式向数组中添加元素, 视图不更新的问题

    vm.$set(目标数组,数组下标,数组元素);
    
    vm.$set(目标对象,属性名,属性值);
    
  • vue3中移除实例方法vm.$set和静态方法Vue.set

vm.$destroy

  • vue2中, 可以通过实例方法$destroy销毁vue组件
  • vue3中移除实例方法$destroy,

vm.$on

  • vue2中, 可以通过$on方法监听自定义事件
  • vue3中, 移除了$on方法, 但是$emit方法没有移除

vm.$once

  • vue2中, 可以通过$once方法监听自定义事件, 但是只能监听一次
  • vue3中, $once方法被移除了

vm.$off

  • vue2中可以通过$off方法注销一个自定义事件
  • vue3中$off方法被移除了

6-vue-router路由模块

vue2中路由的使用流程

  1. 下载安装

    npm i vue-router
    
  2. 导入路由模块

    import Router from 'vue-router';
    
  3. 注册插件

    Vue.use(Router);
    
  4. 创建路由实例对象

    const router=new Router({
        routes:[],	// 引用路由规则数组
        mode:'hash'		// 指定路由模式, 可选值hash(默认路由模式),history
    });
    
  5. 挂载路由实例对象

    new Vue({
        el:'#app',
        router		// 挂载路由实例对象
    });
    
  6. 在根组件中添加路由占位符

    <router-view/>
    

vue3中路由模块的使用流程

  1. 下载安装

    npm i vur-router@next
    
  2. 导入路由模块

    import {createRouter,createWebHashHistory,createWebHistory} from 'vue-router';
    
  3. 创建路由实例

    const router=createRouter({
        routes:[],		// 路由规则
        //必须手动指定路由模式, createWebHashHistory: 提供的是访问地址带#的路由模式
        // createWebHistory: 提供的是访问地址不带#的路由模式
        history:createWebHashHistory()
    });
    
  4. 注册路由实例

     //app: 通过Vue.createApp()创建的应用实例
    app.use(router);
    
  5. 在根组件中添加路由占位符

    <router-view/>
    

路由传参

动态路由

  • 定义动态路由规则

    const routes=[
        {
            path:'/goods/:id',
            component:Goods
        }
    ]
    
  • 进行路由传参

    <router-link to="/goods/1"></router-link>
    
  • 获取动态路由参数

    this.$route.params.id
    
  • 动态路由参数参数(Vue3.0)

    import { useRoute } from 'vue-router'
      
    setup() {
    	const Route = useRoute()
    	const id = Route.params.id
    }
    

查询字符串方式

  • 在页面访问地址中拼接查询字符串

    <router-link to="/news?id=1&title=娱乐新闻">新闻详情</router-link>
    
  • 在目标页面中获取查询字符串参数

    this.$route.query.id
    this.$route.query.title
    
  • 在目标页面中获取查询字符串参数(Vue3.0)

    import { useRoute } from 'vue-router'
      
    setup() {
    	const Route = useRoute()
    	const id = Route.query.id
    }
    

嵌套路由

  • 配置嵌套路由规则

    const routes=[
        {
            path:'/ucenter',
            component:Ucenter,
            // 嵌套路由规则
            children:[
                {
                    name:'order',
                    path:'/order',
                    component:Order
                },
                {
                    name:'address',
                    path:'/Address',
                    component:Address
                }
            ]
        }
    ]
    
  • 父级路由对应的视图中添加二级路由占位符

    <router-view/>
    

7-element-plus组件库

下载安装

npm i element-plus

完整导入

  • 将element-plus组件库中提供的所有组件全部导入我们自己的vue3的项目中

  • 缺点: 导致项目最终构建出来的文件体积过大, 性能不好

    main.js

    // 导入element-plus核心文件
    import Element from 'element-plus';
    // 导入element-plus的样式文件
    import 'element-plus/dist/index.css';
    // 全局注册组件库
    const app=Vue.createApp(App);
    app.use(Element);
    

按需导入(vite)

  1. 安装一个开发依赖

    npm i unplugin-vue-components
    
  2. 在项目配置文件中添加配置项

    vite.config.js

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'
       
    +import Components from 'unplugin-vue-components/vite'
    +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
       
    // https://vitejs.dev/config/
    export default defineConfig({
      plugins: [
        vue(),
       + Components({
       +   resolvers: [ElementPlusResolver()],
       + }),
      ]
    })
       
    
  3. 重启开发服务器

    npm run dev
    

按需导入(vue-cli)

  • 安装插件

    npm install unplugin-vue-components
    
  • 修改项目配置文件(webpack进行配置)

    vue.config.js

    const Components = require('unplugin-vue-components/webpack')
    const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
    module.exports={
        // webpack的配置节点
        configureWebpack:{
            plugins: [
                Components({
                  resolvers: [ElementPlusResolver()],
                }),
              ]
        }
    }
    
  • 重启开发服务器

    npm run serve
    

8-vuex状态管理工具

vue2中使用vuex状态管理工具

  1. 下载安装

    npm i vuex
    
  2. 导入

    import Vuex from 'vuex'
    
  3. 注册

    Vue.use(Vuex)
    
  4. 创建数据仓储对象

    const store=new Vuex.Store({
        state:{
            // 状态数据
        },
        mutaionts:{
            // 操作state状态数据的同步方法
        },
        actions:{
            // 发送数据请求的异步方法
        },
        getters:{
            // 计算属性方法
        },
        modules:{
            // 子模块
        }
    })
    
  5. 挂载数据仓储对象

    /mian.js

    new Vue({
        // 注册数据仓储对象
        store
    });
    
  6. 在组件中使用数据仓储对象

    this.$store
    

vue3中使用vuex状态管理工具

  1. 下载安装

    npm i vuex@next
    
  2. 导出

    import {createStore} from 'vuex';
    
  3. 创建数据仓储对象

    const store=createStore({
        state:{
            // 状态数据
        },
        mutations:{
            // 操作state状态数据的同步方法
        },
        actions:{
            // 发送数据请求的异步方法
        },
        getters:{
            // 计算属性方法
        },
        modules:{
            // 子模块
        }
    });
    
  4. 注册仓储对象

    // app:Vue.createApp()创建的应用对象
    app.use(store);
    

9-案例

留言板案例

case-message

<template>
  <div class="container">
    <h1>留言板</h1>
    <div class="line"></div>
    <div class="well">
      <div class="form-group">
        <input v-model="nickname" class="form-control" type="text" placeholder="昵称" />
      </div>
      <div class="form-group">
        <textarea v-model="content" class="form-control" placeholder="请输入留言内容" cols="30" rows="3"></textarea>
      </div>
      <div class="fotm-group">
        <div @click="messageAdd" class="btn success">提交留言</div>
      </div>
    </div>
    <div class="line"></div>
    <div class="well success" v-for="(item,index) in list" :key="item.id">
      <i @click="messageDel(index)" class="close">x</i>
      <p>昵称: </p>
      <p>留言内容: </p>
    </div>
   
  </div>
</template>

<script>
import { reactive, toRefs } from "vue";
export default {
  setup() {
    // 创建响应式数据对象
    const data = toRefs(
      reactive({
        nickname: "", // 昵称
        content: "", // 留言内容
        // 留言列表
        list: [
        //   {
        //     id: 1,
        //     nickname: "jack",
        //     content: "留言内容",
        //   },
        //   {
        //     id: 2,
        //     nickname: "rose",
        //     content: "留言内容",
        //   },
        ],
      }) 
    );

    // 删除留言内容
    // index: 将要被删除的留言内容在留言列表数组中的索引
    const messageDel=(index)=>{
        // 根据数组索引号通过splice()方法删除数组元素
        data.list.value.splice(index,1);
    }   

    // 添加留言

    const messageAdd=()=>{
        if(data.nickname.value===''){
            return alert('请输入昵称');
        }
        if(data.content.value===''){
            return alert('请输入留言内容');
        }
        // 手动计算留言id
        const id=data.list.value.length>0?data.list.value[data.list.value.length-1].id+1:1;
        // 手动构造留言对象
        const msg={id,nickname:data.nickname.value,content:data.content.value}
        // 将留言对象追加到留言列表数组的末尾
        data.list.value.push(msg);
        // 重置表单元素
        data.nickname.value='';
        data.content.value='';

    }

    return {
      ...data,
      messageDel,
      messageAdd
    };
  },
};
</script>

<style>
* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

.container {
  width: 800px;
  margin: 20px auto;
}

.form-group {
  margin: 15px 0;
}

.form-control {
  padding: 10px;
  width: 100%;
  border: 1px solid #ddd;
  border-radius: 4px;
  outline: none;
  resize: none;
}

.btn {
  border: 0;
  min-width: 70px;
  max-width: 120px;
  line-height: 35px;
  text-align: center;
  color: #555;
  background: #eee;
  border-radius: 4px;
  cursor: pointer;
  outline: 0;
  margin: 0 2px;
  padding: 4px;
}

.btn.danger {
  background-color: #d9534f;
  color: #fff;
}

.btn.success {
  background-color: #337ab7;
  color: #fff;
}

.line {
  border-bottom: 1px solid #ddd;
  margin: 10px 0;
}

.well {
  position: relative;
  padding: 10px;
  border-radius: 5px;
  background: #f0f0f0;
  margin: 10px 0;
  color: #444;
}
.well.success {
  background: #dff0d8;
}

.well .close {
  position: absolute;
  top: 10px;
  right: 10px;
  cursor: pointer;
  font-style: normal;
}

.well p {
  padding: 6px;
}
</style>

参考文档