vue

 

区别

  • 在组件文件中
    • 标签顺序发生变化
    • script标签中加入setup关键字
      • 允许在script标签中书写组合式API
    • 不再限制唯一根元素
    • setupthis不再指向当前组件,而是一个undefined
  • main.js文件中通过createApp()创建实例与通过new Vue()创建实例

  • script标签内写setup和在script标签中写setup()的区别
    • 前者不需要将写的变量进行return,而后者需要
    • 前者可以更简单的使用组合式API
  • 生命周期
    • 生命周期可以多次执行,多次执行时,传入的回调会在对应的时机依次执行
    选项式 组合式
    beforeCreate / created setup
    beforeModunt onBeforeMount
    mounted onMounted
    beforeUpdate onBeforeUpdate
    updated onUpdated
    beforeUnmount onBeforeUnmount
    unmounted onUnmounted

标签属性

  • v-ifv-show的区别
    • v-if每次切换都会重新删除或创建元素,切换性能消耗较高,频繁切换不建议使用v-if,推荐使用v-show
    • 使用v-if值为false的组件,在进入时,不被渲染创建,而使用v-show的组件,会进入时就将所有组件创建了
    • 切换时,不会触发组件生命周期(进入时已经创建了)
    • v-show不会进行dom节点的删除和增添。切换时,只是进行元素的样式显隐修改:display的样式值,初始化渲染消耗较高
    • 使用场景
      • v-if多用于显示隐藏有数据刷新情况 - 初次加载较快
      • v-show多用于固定显隐,如某些按钮的显示隐藏
  • v-bindv-model的区别
    • v-bind
      • 用来绑定数据和属性以及表达式
      • 只能实现数据的单向绑定,从M(data)自动绑定到V(页面),无法实现数据的双向绑定。
      • 形式:v-bind:某属性="数据", v-bind:可以简写为“:”
    • v-model仅适用于输入框,实现数据双向绑定,
    • 形式:v-model="数据"

    注:v-model在表单元素外使用不起作用

  • v-on
    • 事件的表达可简写为@后跟事件名
    • 可通过@事件.属性可以对事件进行一定控制
      • .stop:阻止事件冒泡
      • .prevent:阻止默认行为;例如a的跳转
      • .capture:添加事件的监听捕获
      • .self:只有事件发生在自己身上时才触发
      • .once:只触发一次事件,串联情况下,前边的控件效果也只有一次效果
    • .stop.self的区别:
      • .stop写在被阻止冒泡的事件身上,阻止该事件引发的所有冒泡
      • .self写在冒泡途中的某事件身上,阻止冒泡行为在自己身上的触发,仅当事件发生在自己身上时才被触发,冒泡在他身上无效,但是当触发它时,也会产生冒泡
  • @keyup
    • .enter
    • .tab
    • .delete
    • .esc
    • .space
    • .up
    • .down
    • .left
    • .right

组件内属性

  • watch的使用
    • vue2
      • watch内监听的数据有两种书写格式

        1. 函数式:

           watch: {
             aaa(newName, oldName){
               // 第一个参数 : 新值
               // 第二个参数 : 旧值,之前的值
             }
             // 当值第一次绑定的时候,不会执行监听函数,只有值发生改变才会执行
             // 当监听一个对象时,不能监听其属性变化
           }
          
        2. 对象式:

             watch: {
               aaa: {
                 handler(newName, oldName) {}, // 发生变化便执行的函数
                 // 注意:属性值发生变化后,handler执行后获取的 newVal 值和 oldVal 值是一样的
                 immediate: true, // immediate值为true时,会监听第一次绑定
                 deep: true // 是否开启深度监听 - 优点:监听对象任意值发生变化都会触发执行 -- 缺点:所监听的对象有任意值变化,都会遍历监听所有属性,所有相对十分消耗性能
               } // 定向深度监听 - watch不能监听 aaa.b这样的值,但是可以将前面的那种写法改成字符串形式,即'aaa.b',如此便可以定向监听了
               // 示例
               'aaa.b': {}
             }
          
      • 路由监听

        watch:{
          '$route' (to, from){
            console.log(to, from)
            console.log('当监听到路由地址改变时,会触发,常用来监听复用同一个组件被多个不同路由复用的情况')
          }
        }
        
    • vue3
      • 使用需要引入
      • 一个组件内只能写一个
      • 书写变成了回调,接收三个参数
        • 参数一是监听的数据,一个时单独写,用函数返回形式书写,多个写在数组中
        • 参数二是发生响应时执行的回调,当只有一个参数时,是正常返回,多个参数时,是数组,回调有俩参数,第一个是新的,第二个是上一次的数据
        • 参数三是对监听的配置,通过immediatedeep俩属性来配置是否进入便执行和是否深度监听
      • deep开启有性能损耗,所以能不开启,就不开启。

        import { watch } from 'vue'
        // 监听一个值,开启深度监听和初始时监听
        watch(mes, () => {}, {
          immediate: true,
          deep: true
        })
        // 监听多个值
        watch([mes, mess], () => {})
        // 精确监听一个值
        watch(() => mes, () => {})
        // 精确监听多个值
        watch([() => mes, () => mess], () => {})
        
  • 计算属性
    • 不应该在computed中发生异步请求和修改dom
    • 是一个只读的属性,在非必要情况下,不应该出现对其进行修改
    • vue2
      • 函数内的一个属性
    • vue3
      • 使用时需要引入
      • 一个组件内可以写多个,以回调的形式返回想要的结果

        import { computed } from 'vue'
        const computyedState = compoted(() => {
          return 响应式数据计算之后的值
        })
        
    • 数据源
      • vue2
        • 对象方法(data(){return})的返回值,为避免多组件同址值,故而用函数形式而不是对象形式
      • vue3
        • 使用需引入,reactiveref配备俩可以函数来生成响应式数据
        • 生成响应式对象时,优先使用reactive,生成响应式普通数据或数组时使用ref
        • reactive不能处理简单类型的数据
        • ref参数类型支持简单类型数据,但是必须通过.value来访问及修改
        • ref函数的内部实现依赖于reactive函数
        • reactiveref函数都是用来生成响应式数据的
          • reactive():接受一个对象类型数据的参数传入,返回一个响应式的对象
          • ref():接受简单类型或者对象类型的参传入,返回一个响应式对象
            • 使用时,在模板中可以直接使用,在js代码部分,需要进行.value的形式经行使用
            <script setup>
              import { reactive, ref } from 'vue'
              const rea = reactive(复杂类型数据)
              const reff = ref(简单类型或者复杂类型)
            </script>
            

        小建议:由于vue3响应式书写方式是在一个script标签内,故而不涉及响应式变化的变量,尽量不使用reactiveref对其进行响应式包装

  • 计算属性和data的数据区别

    • data里边的数据只会在页面构建时进行获取,并将其变成静态数据,构建后边不会在改变了
    • computed计算属性里边的值,会时刻监督影响输出数值所涉及变量的变化,从而进行数据重渲染
    • 计算属性里边的值(不管是不是与输出值相关)发生变化,都会触发计算属性执行
    • 计算属性:基于某些数据或者某些计算属性,做一个二次处理,然后得到一份全新的数据,这份全新的数据可以直接当data中的数据使用,只是不能修改它

      计算属性里边要获取路由信息,需要写成函数的return形式

    • vue的计算属性中仅包含对返回值的计算,不能用于修改data里边的数据(此问题发生在对象和数组中,引用类型,引者改权)

    • 计算属性小案例

      在echarts图表中,通过生命周期mounted来调用实现图表构建函数,而图表调用值仅发生在生命周期调用时的一次,即对计算属性里边的值仅调用一次,计算属性在没有被调用,便将数据缓存里,后边改变计算属性里边的相关数据,由于计算属性值没有被调用,所以数据不会刷新

  • 数据优先级

    props > methods > data > computed > watch

路由

  • 路由模式:
    • hash模式: 带#
      • 路由跳转时,若找不到对应路由,不会发送请求
      • 打包自测时,使用hash,如果使用histor模式,会出现空白页问题
    • history模式: 不带#的 – 需要后端重定向
      • 路由跳转时,找不到对应路由,会将对应路由带在路径上,发送一次请求
  • 三种路由传参
    1. 字符串形式

       this.$router.push('/abc/' + this.aaa + '?k=' + this.bbb)
      
    2. 模板字符串

       this.$router.push(`/abc/${this.aaa}?k=${this.bbb}`)
      
    3. 对象传参

       this.$router.push({
         name: 'abc',
         params: {
           keyword: this.a
         },
         query: {
           k: '你好'
         }
       })
      
  • 路由传参误区
    1. 路由跳转传参时,对象的写法可以是name、path形式。但是,path不能与params传参一起使用(若一起使用,会报错)

    2. 配置路由时,使用params参数占位了但是跳转时却不传参数 - 此时路由路径会出现问题(路径参数消失 - 若有问号,则问好前的路由路径丢失), 若想解决,可以在params参数后面加一个问号

       {
         path: '/abb/:aaa?',
         name: 'abb',
         ...
       }
      
    3. 配置了params参数,传的是空值(空的字符串) - 路径会出现问题(同不传一样,路径消失),可以通过或运算,取undefined

       this.$router.push({
         name: 'aab',
         params: {
           keyword: '' || undefined,
           abc: '你好'
         }
       })
      
    4. 路由组件可以通过配置路由params参数及props来传props参数(只能传params参数),也可以直接配置路由的props项,将其配置为对象(传死值)来传递对应参数(额外传值),以及将props项写成函数的形式来传递参数(常用)

       // 路由文件
       // 形式一
       {
         path: '/aab/:abc',
         name: 'aab',
         props: true
       }
       // 使用
       this.$router.push({
         name: 'aab',
         params: '22'
       })
       // 形式二
       {
         path: '/aab',
         name: 'aab',
         props: {
           aa: '数据一',
           bb: '数据二'
         }
       }
       // 形式三
       {
         path: '/aab',
         name: 'aab',
         props: ($route) => {
           return {
             keywor: $route.params,
             ka: $route.query
           }
         }
       }
      
  • 路由编程式导航重复点击问题 原因:编程式导航引入了promise,而promise需要传递一个成功或者失败的回调 解决

      // 路由文件 - 对push进行二次封装
      let originPush = VueRouter.prototype.push // 将vueRouter原型对象的push存一份
      VueRouter.prototype.push = function(loction, resolve, reject) {
        if(resolve && reject) {
          originPush.call(this, loction, resolve, reject)
        } else {
           originPush.call(this, loction, ()=>{}, ()=>{})
        }
      }
    

导航守卫

  • 全局守卫

    • 全局前置守卫 beforeEach

        router.beforeEach((to, from, next) => {
       //  to: 代表去的路由
       //  form: 代表来着哪个路由
       //  next: 控制路由的跳转与取消  -- next()跳转  --- next(false)禁止跳转  ---next('要跳转的路径')   跳转到指定路径  ---若没有此选项,则不能进行路由页面跳转
      })
      
    • 全局后置守卫afterEach

  • 路由独享守卫
    • beforeEnter(to,form,next){}
    • 只有进入当前页面时才执行
    • 写于当前路由配置项中
  • 组件内守卫

    写于对应组件内,与生命周期同级 组件守卫里面不能访问当前组件实例的属性和方法

    • beforeRouteEnter(to, from, next){} 进入组件前执行的函数
    • beforeRouteUpdate(to, from, next){} 组件复用时执行的守卫函数 – 有条件的,不好用
    • beforeRouteLeave(to, from, next){} 离开组件前执行的守卫函数 – 不使用next()的话,不能离开当前页面
  • 复用组件数据刷新

      // 原理 - 复用同一个组件,不会触发生命周期,但是依然会离开当前组件,然后再进入当前组件,这时,就可以再守卫那里做事情了
      beforeRouteEnter (to, from, next) {
        console.log('进入')
        next((vm) => { // 所有数据都在vm上
          vm.aaa = true
        })
      },
      beforeRouteLeave (to, from, next) {
        console.log('离开')
        this.aaa = false
        next()
      }
      思路离开时使用 v-if = false 将组件关掉进入时等于true使组件重新渲染
    
  • 页面路由跳转,滚动清零

    在路由配置文件里边加上scrollBehavior(to, from, saved){return {x:0,y:0}}

  • 路由拆分

    // 路由源文件 router > index.jsx
    import Vue from 'vue'
    import Router from 'vue-router'
    // 拆分路由引入
    import VOTE from './modules/vote' 
    
    Vue.use(Router)
    // 定义路由
    const routes = [
    
    ]
    const router = new Router({  
      mode: 'history',  
      routes,
      VOTE
    })
    export default router
      
    // 拆分的文件
    export default [      
      {        
        path: '/vote/index',        
        name: 'VoteIndex',        
        component: resolve => require(['@/view/vote/index'], resolve) 
      },    
      。。。  
    ]
    

组件关系

  • 组件传值
    • 父传子
      • vue2
        • 子组件通过props接收父组件传来的值
        • type 也可以是一个自定义构造器函数,使用 instanceof 检测。
        • 在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用
        • 单向数据流,子组件可以通过计算属性或者watcha来监听修改

          // 传值校验
          poprs: {
            a: Number, // 基础类型校验
            b: [String, Number], // 多种类型校验
            c: {
              type: String,
              required: true, // 必传
              default: 100 // 有默认值 - 一般与必传不一起使用
            },
            d: { // 对象或数组的使用
              type: [Object, Array],
              validator: (val) => { // 自定义校验
                return val > 10
              }
              default: () => {
                return {}
                // 或 return []
              }
            }
          }
          
      • vue3
        • 使用defineProps()来接收
        • defineProps()不需要引入,是全局函数
        • 模板里可以直接使用defineProps()接收的值
        • js使用传来的值需要先用一个变量接收defineProps()函数的返回值,然后通过.的形式对其进行使用

          defineProps({
              mes: String
            })
            // defineProps(['mes'])
            // defineProps({
            //   mes: {
            //     type: String,
            //     required: true
            //   }
            // })
          
    • 子传父
      • vue2
        • 子组件通过this.$emit("方法名", 数据)向父组件传值 –> 触发父组件的@方法名对应的函数
      • vue3
        • 子组件需要通过defineEmits(['xxx', 'xxx'])来声明回调事件,然后通过其返回值加对应已经声明的事件名才能使用已经声明的回调事件来触发父组件的对应事件

            const emit = defineEmits(['aaa'])
            const abc = () => {
              emit('aaa', 传过去的值)
            }
          
    • 公共bus
      • vue2

        // gloublBus.js // 名字自定义
        import Vue from 'vue'
        export default new Vue()
        
        // 发送方
        import gloubs from '公共bus地址'
        // 某函数下
        gloubs.$emit('注册key名--注册车辆', data) // data为要穿的数据,可以是任何值吧
        
        // 使用方
        import gloubs from '公共bus地址'
        // 生命周期函数created()或 mounted()下
        gloubs.$on('使用对应的注册的车名', res => {})
        

        也可以

        // main.js中
        Vue.prototype.$bus = new Vue()
        
        // 发送
        this.$bus.$emit('xxx', data)
        // 接收
        this.$bus.$on('xxx', ()=>{})
        
      • vue3

        • 需要借助第三方依赖

          1. 安装mitt

            npm install --save mitt
            
          2. 引入及使用

            // 新建文件夹(bus)及文件(index.js/.ts)
            import mitt from 'mitt'
            export default mitt()
          
            // 注册事件处
            import $bus from '@/bus'
            import { onMounted } from 'vue'
            $bus.emit()
            // 接收事件
            onMounted( () => {
              $bus.on()
            })
          

插槽

一共有三种插槽:匿名、具名、作用域 语法:必须写在引用组件内,子组件必须有<slot></slot>

  • 匿名插槽

      <!-- 父组件  -->
      <demon>
        写于父组件
      </demon>
      <!-- 子组件 -->
      <template>
        <slot></slot>
      </template>
    

    特点:可以直接接收引用处写在组件内的内容,并用其替换子组件的<slot></slot>,无需特殊操作 缺点:单向广播,无差别替换 - 下边些几个<slot></slot>,将引用处内容重复几次

  • 具名插槽

    <!-- 父组件 -->
    <demon>
      <div slot="zhangsan"></div>
      <div slot="lisi"></div>
      <!-- 或者 -->
      <template slot="zhangsan"></template>
      <!-- 或者 - 注意:  v-slot 只能添加在 <template> 上,除非子组件是匿名插槽,此时可以直接写于组件上-->
      <template v-slot:zhangsan></template>
    </demon>
    <!-- 子组件 -->
    <template>
      <slot name="zhangsan"></slot>
      <slot name="lisi"></slot>
    </template>
    

    特点: 针对性放矢,对号入座式替换,可以根据需要对不同部分内容进行对应替换 缺点: 也是单向

  • 作用域插槽

     <!-- 父组件 -->
     <demon>
       <!-- 第一种写法 -->
       <!-- 匿名写法 -->
       <div slot-scope="nihao">
          
       </div>
       <!-- 或者 -->
       <!-- 具名写法 -->
       <div slot="zhangsan" slot-scope="nihao">
           
       </div>
       <!-- 或者 -->
       <!-- 插槽写法  --  一般都这样写 -->
       <template slot="zhangsan" slot-scope="nihao">
           
       </template>
       <!-- 第二种写法 -->
       <template v-slot:zhangsan="nihao">
           
       </template>
     </demon>
     <!-- 子组件 -->
     <template>
       <slot name="zhangsan" :nida="msg"></slot>
     </template>
     <script>
       export default {
         data () {
           return {
             msg: 'msg'
           }
         }
       }
     </script>
    
    • slot,slot-scope可以写在普通元素上,也可以写在template上,但是两个属性必须写在同一个元素标签上,不然无效 – 测试过,未写于同一标签,不显示内容
    • 特例: 在使用表格组件时,自封装表格,在引用组件上使用<el-table-column slot="setting"><template slot-scope="scope"></el-table-column>时,可以分开写 – 原因: 未知
    • v-slot替换了slot,slot-scope的分开写法,但是v-slot只能写于template标签上,不然报错
    • v-slot可以简写成’#’,只能写于template标签中

部分API学习

  • 全局 - API
    1. vue.directive(key, definition)
      • key {String}: 注册指令的名字 - 就一个字符串
      • definition {function object}: 对注册指令的的定义 - 调用该指令对应执行的事件,可以是一个函数或是一个对象

      解释: 自定义指令 范例 - 注册一个自定义指令v-focus

       // 全局注册自定义指令
           // 对象形式注册
           Vue.directive('focus', {
               // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
             bind: function () {}, 
             // 被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
             inserted: function () {}, 
             // 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。
             update: function () {}, 
             // 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
             componentUpdated: function () {}, 
             // 只调用一次,指令与元素解绑时调用
             unbind: function () {}
           })
           // 函数形式注册 - 只会调用bind和update  - 触发相同行为
           Vue.directive('my-directive', function (el) {})
       // 局部注册自定义指令
           directives: {
             focus: { // 指令的定义
               inserted: function (el) {}
             }
           }
       // 使用示例 - <input v-focus:foo.a.b="message">
       // 参数:
       /**
       * 1、 el : 指令所绑定的元素,可以用来直接操作 DOM。
       * 2、 binding: 一个对象
       *     {
       *       name:指令名,不包括 v- 前缀。
       *       value:指令的绑定值  - 解析执行后的具体值
       *       oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用     - 无论值是否改变都可用
       *       expression:字符串形式的指令表达式 - 绑定值解析执行前的字符串
       *       arg:传给指令的参数 - 例如: v-focus:foo --> v-focus:[argument]="value"
       *       modifiers:一个包含修饰符的对象
       *       vnode:Vue 编译生成的虚拟节点
       *      }
       */
      
    2. vue.filter(key, definition)
      • key {String}: 过滤器的名字 - 就一个字符串
      • definition {function}: 过滤的函数

      解释: 过滤器 范例 注: 可串联多个 - 筛选后,再筛选

       <!-- 使用一 - 双花括号中 -->
       <div></div>
       <!-- 使用二 -  v-bind 中 -->
       <div v-bind:id="message | capitalize"></div>
      
       // 全局注册过滤器
       Vue.filter('capitalize', function (value) {}) // 返回处理后的值
       // 局部注册过滤器
       filters: {
         capitalize: function (value) {}
       }
      
    3. vue.mixin({}) – 混入方法

      • 作用:给vue添加一些公共的方法和数据
      • 混入的生命周期高于原生的生命周期

项目配置

  • 环境变量配置
    1. 开发环境: 根目录下创建.env.development

         VUE_APP_ENV = 'dev'
         VUE_APP_BASH_API = '基础路径' # 必须以VUE_APP开头
      
    2. 生产环境: 根目录下创建.env.production

         VUE_APP_ENV = 'pro'
         VUE_APP_BASH_API = '基础路径' # 必须以VUE_APP开头
      
  • 环境变量使用

      // 使用判断
      process.env.VUE_APP_ENV  // 判断这个的值,取对应地址
    

特殊语法

  • v-model传值

    <!-- 父组件 -->
    <组件名 v-model="bcc"/>
    <!-- 子组件 -->
    <script>
      this.$emit('input', )
    </script>
    
    • v-model.sync传值不同
      • 前者仅可传一个,后者更灵活
      • 前者的输出是input,后者是update:值
    • vue3
      • 通过:后的内容来与子组件数据做关联同步
      • vue3中对v-model的扩展取代了.sync属性
      <!-- 父组件 -->
      <组件名 v-model:值1="xxx" v-model:值2="">
      <!-- 子组件 -->
      <script setup>
      import { defineProps, defineEmits } from 'vue'
        const prop = defineProps(['值1' '值2'])
        const emit = defineEmits(['update:值1', 'update:值2'])
        // 事件中
        emit('update:值1', 值1修改值)
      
      </script>
      
  • .sync学习 - vue3中没有此属性

    本质:是一个语法糖,简化了父子传值(具体来讲,是简化父组件传值写法)。 效果:不用父组件写自定义监听事件,便可以使子组件修改父组件的值

    • 格式:子组件传递给父组件值的格式:this.$emit(‘update:aaa’,值)
      • update+冒号是必须写的,后面跟的 aaa 是父组件传来的值的名称
      • 第二个便是传递给父组件的值
    • .sync使用示例

         <!-- 父组件传值变动写法 -->
         <组件名 :aaa.sync="bcc"/>
         <!-- 子组件返回值变动写法 -->
         <script>
           方法名() {
              this.$emit('update:.sync前边的值,其实也就是对应的方法', )
           }
         </script>
             
      
      • 原型:

        <text-document
          v-bind:title="doc.title"
          v-on:update:title="doc.title = $event"
        ></text-document>
        
      • 简化后:

        <text-document :title.sync="doc"></text-document>
        

        总体简化了一个v-on及对应的监听事件

      • 子组件里边要做改变

        // 原来的写法
         this.$emit('事件名',)
        //  现在的写法
         this.$emit('update:.sync前边的值,其实也就是对应的方法', )
              
          示例
          {
            this.$emit('update:title', 123)
          }
              
        
  • this.$nextTick()

    • 作用:用于获取vue更新之后的dom
    • 确保数据更新视图之后才执行(数据数据变了,DOM数据也变化后) – 属异步 – 可以理解为一个定时器
    • vue中使用的都是虚拟DOM,当对页面进行数据修改时,同步操作是获取不到DOM元素的内容的。因为虚拟DOM到真实的DOM是需要时间的,大型项目约在17~20ms之间。
    • 使用场景:当你想要在修改修改数据之后,马上操作数据影响的DOM
  • ref

    • vue中获取dom的方法
    • 可以通过ref来获取子组件内的方法或数据
    • vue3中通过const 变量名 = ref(null)与组件标签上的ref值相同来获取对应dom
      • 获取dom最早在onMounted生命周期阶段可用

        <script setup>
            import { ref,onMounted } from 'vue'
            cosnt href = ref(null)
            onMounted(()=> {
              console.log(href)
            })
          </script>
          <template>
            <子组件 ref='href'></xxx>
          </template>
        
      • <script setup>语法糖下,组件内部属性和方法是不公开给父组件访问,因此不能再直接通过dom访问子组件内的函数和变量,需要子组件通过defineExpose来指定哪些属性和方法允许访问

          defineExpose({
            暴露的属性或方法
          })
        
    • useAttrs传值

      <组件名 v-bind="$attrs"/>
      <script setup>
        import { useAttrs } from 'vue'
        let attrs = useAttrs() // 
      </script>
      
      • 通过useAttrs拿到的属性,就不能再通过defineProps拿到了。
      • useAttrs可以获取组件身上的属性与事件
    • <template></template>模板内可用访问vue实例属性$attrs$parent
    • provide/inject传值

      //父组件
      import { ref, provide } from 'vue'
      const text = ref('aaa')
      provide('key', text) // 注入
      // 子孙组件
      import { inject } from 'vue'
      const text = inject('key') // 获取
      

vuex and Pinia

  • 使用vuex的好处
    • 能够在vuex中集中管理共享数据,易于开发和后期维护
    • 能够高效的实现组件之间的数据共享,提高开发效率
    • 存储在vuex中的数据都是响应式,能够实时与各个页面保持同步

vuex对外只认store – 在全局中。 即: 在main.js中导出时,必须是store此时可以在this里面找到$store属性,若是其他的,则在全局中找不到你所公布的那个vuex属性,且也没有$store

  • 在main.js中错误的vuex导出

    // 引入
    import Vue from 'vue'
    import abcc from './自定义路径/vuex' // vuex那个文件名可以随意取名,无所谓
    // 导出
    new Vue({
      abcc,
      render: h => h(App)
    }).$mount('#app')
    

    这里边的abcc是随便取的vuex导入名,可以认为他可代替任何不是store的名字

    这样写的结果是在全局中找不到vuex属性,即你即找不到$store也找不到aabc这个属性

    伴随错误:

    Error in render: “TypeError: Cannot read property ‘state’ of undefined” found in 文件地址

  • 正确引入

    // 引入
    import Vue from 'vue'
    import store from './自定义路径/vuex' // vuex那个文件名可以随意取名,无所谓
    // 引入的名称发生了变化
    // 导出
    new Vue({
      store,
      render: h => h(App)
    }).$mount('#app')
      
    

    此时vuex使用便正常了

  • vuex 传参

  • vuex只能传递一个参数,若要传递多个参数,则可以以对象或者组织的形式传入

  • vuex之grtters

grtters中的数据,在没有使用的时候,是不会请求的

  • 使用

    1. state的使用
      1. 在state中,写入共享属性及值
      2. 在需要的地方通过import { mapState } from 'vuex'的方式引入
      3. 引入后,在计算属性computed中对数据进行导入使用:

        computed: {
           ...mapState(['对应state中的数据'])
        }
        
      4. 在其他组件中,也可以通过this.$store.state.要访问的数据名字来访问相应数据
    2. 数据操作 - 数据修改–方法引用:

      错误示例:可以通过对”this.$store.state.要访问的数据名字”数据进行改变达到对同步数据进行改变,当项目大了,不利于维护 正确示例:通过对mutatis中定义函数,用以数据改变,在通过this.$store.commit(‘函数名’, ‘参数一’)来调用对于函数

      • 也可以使用导入式引入
      • 在需要使用方法的地方通过import { mapState, mapMtations } from 'vuex'的方式引入
      • 在组件方法里边通过使用...mapMutations(['方法名'])

         methods{
           ...mapMutations(['方法名']), // 建议写在第一的位置,便于查找其他方法
         }
        
      • 后边调用时,可以通过this.方法名(参数)调用对应方法
      • 想要调用actions中的函数,需要通过this.$store.dispatch(‘函数名’)
    3. 同步异步:
      • 在mutations中写异步代码,效果可以在页面显示,但是违反规则,故不要再mutations函数中共湖南执行异步操作
      • 异步操作中,action无法直接操作state中的数据,需要调用间接调用mutations中的函数来执行修改

         abc(context){
         context.commit('mutations中的某个函数')
         }
        
        • gitter可以理解为对state中的数据进行加工形成新的数据
        • getter使用:可以通过”this.$sotore.getters.名称”来调用getter中的函数 也可以通过导入的方式将getter中的数据加载到相应组件 impor { mapGetters } from ‘vuex’// 数据引入 再计算属性里边导入:computed: { …mapGetters([‘对应getter中的数据’]) } getter中的数据需要return出来 –即有返回值
  • 状态管理器pinia(vuex的替代品)

    • 安装npm install pinia
    • 两种创建形式
    import { defineStore } from 'pinia'
    // 对象式, 通过this获取上下文数据
    export const useCounterStore = defineStore('唯一标识',{
      state: () => {
        return {}
      },
      actions: {
        increment () {
          执行内容
        }
      }
    })
    // 函数式
    export const useCounterStore = defineStore('唯一标识',() => {
      // 定义变量,定义方法
      return {} // 将东西导出去
    })
    
    • 解构变量会出现响应丢失,可以通过storeToRefs将值包裹后,再进行解构变量,这样就不会响应丢失了。
    • 方法不可以也不需要通过storeToRefs来解构,在原变量里直接解构就可以使用

扩展

  • 在vue中使用 require() 来获取”本地”静态图片 – require() - 文件加载

    原因:vue在DOM中直接引入的图片会被转为 base64 格式,使用变量引入的话,图片不会转为 base64 格式,因此引入的图片不会正常显示,使用require(‘./aaa.png’) 可以将引入的图片转为base64 :在标签中直接写上图片路径,路径会转为 base64,但是如果通过变量设置静态路径,则不会转为 base64,仍然显示变量值对应的路径,故图片无法正常显示

  • 读取文件夹下的文件名 require.context('../abc',false,/.\.js$/)

    参数一:文件夹的路径 参数二:是否查询子文件夹 参数三: 要匹配的文件后缀 – 是个正则表达式

    const file = require.context('../abc',false,/.\.js$/)
    console.log(file.keys()) // 通过keys()可以打印文件下的所有文件名 - 构成一个文件名数组
    
  • 组件的切换过渡
    • 将过渡事件写在vue定义的一个”域”内:<transition></transition>
    • 通过给<transition>里边加name="my"属性可以将相应的阶段属性的v-改成my-enter的样式
    • 在样式标签里边进行定义样式:
      • v-enter:时间点-进入之前/元素初始状态/还没进入页面
      • v-leave-to:时间点-动画离开后/离开的终止状态/动画已经结束
      • v-enter-active:入场时间段
      • v-leave-active:离场时间段
    • 在离开或进入的时间段里边写动画属性便可以产生动画效果
    // 
    .v-leave-active{
      transition: all 0.8s ease;
    }
    
    // 在给组件设置动画时,对它的外部(外置部位)加一个过渡组件包裹
    <transition mode="过渡效果">
      <component :is="key"/>
    </transition>
    
  • 定义指令
    • 定义全局指令:Vue.directive( "指令名称",{含有相关函数的对象})
      • 指令名称定义时前边不加v-前缀,但是,调用/使用时名称前边必须加v-前缀,以此进行调用。
      • 每个函数的第一个参数都是el,表示被绑定的那个元素,是一个原生的js对象
      • 第二个参数是一个对象,包含指令名(name)绑定值(value/expression)
      Vue.directive( "指令名称", { 
        bindfunction (el) {} // 每当指令绑定到元素上时,会立即执行bind函数,只执行一次。
        insertedfunction (el) {} // 元素插入到dom中时会执行inserted函数,仅执行一次
        updatedfunction (el) {}// 当VNode更新时执行updated,可能会触发多次
      })
      
    • 定义私有指令:
    // 与data同级
      directives{
        "指令名称": {
        // 对象加相应函数
        }
        // 简写:
        '指令名'function (elbinding) {
        // 执行的函数
        // 等同于把事件绑定在bind和update上
        }
        // 例如:
        "color":function (el, binding) {
          el.style.color = parseInt(binding.value)+"px"
        }
      }
    
  • 组件创建
    • 全局创建(注册)

      Vue.component('组件名',{
          template:'<h3>内容</h3>'
        })
      // 简写时,将Vue.extend()省略,直接换成对象,包含一个template属性名。
      
      
    • 外联创建

      <template id="对应的id名">
        <div> 里边写正常的HTML的内容但要被一个根元素包裹 </div>
      </template>
      
      Vue.component('组件名', {
          template:'#id名'
        })
      
    • 私有组件创建

      components{
        "组件名"{
          template"<div>组件内容</div>"
        }
        sss{
          template"id名"
        }
      }
      
    • 组件的data
      • 必须是一个方法,且必须是返回一个对象
      • 使用方法和全局data一样,但只能在自己的组件中使用。
      Vue.component('组件名',{
          template:'<h3>组件内容</h3>',
          data: function () {
            return{
              msg : 'nia'
            }
          }
        })
      
    • data方法的return{}和外联对象
      • 使用外联的对象,在同一个组件多次调用时,会发生所有组件互相影响的效果(有相应变化)
      • 原因:对象地址相同
      • 使用{}的好处:每次创建组件时,各自的对象分开,互不影响

对template属性值的介绍: 不论哪种创建方式创建的组件,组件的template属性指向的内容模板(值) 有且仅有唯一一个根元素 — 即一个根div或什么的元素,无并接属性。

  • SPA(单页面应用)缺点
    • SEO优化不好(SEO是基于多页面应用的)

vue优化

  1. 仅展示的数据,可以对其进行冻结操作,节省内部对其进行完全相应化操作。

     const obj = {a:1,b:2}
     Object.freeze(obj)
    
    • 检测对象是否被冻结

      Object.isfrozen(obj)