20230425----重返学习-vue项目-vue自定义指令-vue-cli的配置

53 阅读12分钟

day-057-fifty-seven-20230425-vue项目-vue自定义指令-vue-cli的配置

vue项目

vuex版

  • 普通版纯axios:切换页面,就会重新发送一次ajax请求

  • 普通版升级:vuex版

  • vuex的常用功能

    • vuex 数据通信

    • vuex 缓存数据 前进后退,切换页面,数据都不会重新加载

      • 除非刷新或关闭页面重新打开
      • 不用vuex版的:切换页面,就会重新发送一次ajax请求
  • 能用vuex版的缓存来做的特点

    • 不步骤更新的数据,如表格头或用户登录数据之类的
vuex版相对于纯axios的步骤
  • vuex版相对于纯axios的比较

    1. 获取数据,需要在vuex中获取,组件页面接收使用

      • 减少了请求次数
    2. 切换功能,可以自己写逻辑,来实现数据筛选

      • 减少了请求次数

        • 不用发送一次获取数据的请求
    3. 删除功能,还是发送删除请求,将数据库中的数据删除。

      • 接下来,不再发送重新获取数据更新页面请求,手动将vuex的数据删除,数据改变,页面使用数据,会自动更新页面

        • 减少了请求次数
    4. 完成功能,还是发送完成请求,将数据库中的数据改为完成状态。

      • 接下来,不再发送重新获取数据更新页面请求,手动将vuex的数据改为完成,数据改变,页面使用数据,会自动更新页面

        • 减少了请求次数
    5. 新增功能,还跟以前一样

      • 因为后台新增的id,前端并不确定数据是否已经改变了。
vuex版配合axios
  • vuex版配合axios步骤

    1. 操作数据类

      1. 获取数据,vuex发送一个axios请求用于获取表格数据,组件页面通过vuex拿到表格数据,之后渲染到页面上。

        1. /src/store/中创建一个目录/模块目录名/,里面有一个index.js文件,以便vuex主实例对象导入作为一个模块。

          • /src/store/模块目录名/index.js导出的一个对象,只不过它可以被/src/store/index.js这个vuex主实例对象导入并使用。
          • 这个/src/store/模块目录名/index.js依然可以导入其它模块或者是其它方法,以生成自己的模块。不过总体都在/src/store/index.js这个vuex主实例对象上。
        2. /src/store/模块目录名/index.js中的state属性里有一个变量,用于保存表格数据。

        3. /src/store/模块目录名/index.js中的mutations属性里有一个同步函数,用于修改表格数据。

        4. /src/store/模块目录名/index.js中的actions属性里有一个异步函数,用于发送axios请求获取表格数据,并通过mutations属性里的同步函数修改state属性里的表格数据变量。

        5. 在业务组件中从vuex中导入mapState与mapActions辅助函数,并用这两个辅助函数把/src/store/模块目录名/index.js中的state属性里的表格数据变量展开放置到业务组件的computed计算属性中,把/src/store/模块目录名/index.js中的actions属性里的异步函数放置到业务组件的methods方法函数中。

        6. 业务组件中定义一个计算属性,用于放置渲染表格用的表格数据,它主要对vuex中的表格数据变量进行处理,并用于渲染页面的表格DOM。

        7. 一开始让业务组件表格loading。

        8. 在业务组件的created()中调用vuex异步函数,发送一个axios请求表格数据。得到数据后,更新vuex中的表格数据。之后业务组件通过计算属性拿到并处理好vuex中的表格数据,更新业务组件中的表格数据。

        9. 停止表格loading。

      2. 切换功能,修改tab当前选中角标,业务组件中的表格数据计算属性也会重新计算,进而更新表格。

        1. 用一个变量保存tab的当前选中角标。
        2. 如果当前选中角标与当前点击id一致,就不做操作。
        3. 如果当前选中角标为当前点击id。
        4. 由于当前角标改变,业务组件中的表格数据计算属性也会重新计算,进而更新表格。
      3. 删除功能,直接发送一个axios请求用于删除数据,vuex中的同步修改数据,业务组件中的表格数据计算属性也会重新计算,进而更新表格。

        1. /src/store/模块目录名/index.js中的mutations属性里有一个同步函数,用于删除vuex表格数据中一项数据。
        2. 在业务组件中从vuex中导入mapMutations辅助函数,并用这个辅助函数把把/src/store/模块目录名/index.js中的mutations属性里的同步函数放置到业务组件的methods方法函数中。
        3. 直接发送一个axios请求用于删除后端的一项数据。
        4. 删除失败,做出提示后,不做操作。
        5. 删除成功,做出提示后,调用vuex中的同步函数,删除vuex表格数据中一项数据。
        6. 由于vuex表格数据改变,业务组件通过计算属性拿到并处理好vuex中的表格数据,更新业务组件中的表格数据。
      4. 完成功能,直接发送一个axios请求用于完成数据,vuex中的同步修改数据,业务组件中的表格数据计算属性也会重新计算,进而更新表格。

        1. /src/store/模块目录名/index.js中的mutations属性里有一个同步函数,用于修改vuex表格数据中一项数据变成完成状态。
        2. 在业务组件中从vuex中导入mapMutations辅助函数,并用这个辅助函数把把/src/store/模块目录名/index.js中的mutations属性里的同步函数放置到业务组件的methods方法函数中。
        3. 直接发送一个axios请求用于完成后端的一项数据。
        4. 完成操作失败,做出提示后,不做操作。
        5. 完成操作成功,做出提示后,调用vuex中的同步函数,修改vuex表格数据中一项数据变成完成状态。
        6. 由于vuex表格数据改变,业务组件通过计算属性拿到并处理好vuex中的表格数据,更新业务组件中的表格数据。
      5. 新增功能,对数据进行校验,发送一个axios请求用于新增数据,调用vuex异步函数,发送一个axios请求表格数据,更新vuex中的表格数据。业务组件通过计算属性拿到并处理好vuex中的表格数据,更新业务组件中的表格数据。

        1. 对数据进行校验,并处理数据让其符合后台需求的格式。
        2. 直接发送一个axios请求用于新增数据。
        3. 新增失败,做出提示后,不做操作。
        4. 新增成功,做出提示后,复用关闭弹窗的方法,之后调用vuex异步函数,发送一个axios请求表格数据。得到数据后,更新vuex中的表格数据。之后业务组件通过计算属性拿到并处理好vuex中的表格数据,更新业务组件中的表格数据。
    2. 非操作数据类的其它方法

      1. 格式化数据类的

        • 处理表格数据,格式化
      2. 让页面布局变动的

        • 关闭页面弹窗之类的,让页面出现提示框或隐藏之类的
        • 重置表单之类的功能
      3. 校验表单数据类的

工程化开发

  • 做功能的时候,分区块进行开发。

    • axios工程化

      • /src/api/index.js中引入同目录中其它文件夹,每个文件夹中的index.js就是它的一个模块。表现为api对象中的一个属性。

        • 使用表现为:this.$api.模块名.模块中的请求函数。
    • vuex工程化

      • /src/store/index.js中引入同目录中其它文件夹,每个文件夹中的index.js就是它的一个模块。表现为在它的Vuex实例对象中配置对象中modules对象中的一个属性。

        • 使用模块中的state及getters及mutations及actions等。

          • 一般通过辅助函数来进行。

查找页面与预期不符合的bug

vue自定义指令

vue自定义指令的类型

  • 全局指令

    • 一般指令都是全局的

      Vue.directive('focus', {
        // 当被绑定的元素插入到 DOM 中时……
        inserted: function (el) {
          // 聚焦元素
          el.focus()
        }
      })
      
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
        </head>
        <body>
          <div id="app">
            <h1 v-color:theArg1:theArg2.theModifiers1.theModifiers2="'red'+6">h1</h1>
          </div>
      
          <script>
            //全局指令: `v-color`;
            Vue.directive("color", {
              bind(el, binding, vnode, oldVnode) {
                console.log(`bind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
              inserted(el, binding, vnode, oldVnode) {
                console.log(`inserted()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
              update(el, binding, vnode, oldVnode) {
                console.log(`update()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
              componentUpdated(el, binding, vnode, oldVnode) {
                console.log(`componentUpdated()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
              unbind(el, binding, vnode, oldVnode) {
                console.log(`unbind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
            });
      
            //局部指令: `v-color`;
            let vm = new Vue({
              el: "#app",
            });
          </script>
        </body>
      </html>
      
  • 局部指令

    {
      el: "#app",
      directives: {
        focus: {
          // 指令的定义
          inserted: function (el) {
            el.focus()
          }
        }
      }
    }
    
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
      </head>
      <body>
        <div id="app">
          <h1 v-color:theArg1:theArg2.theModifiers1.theModifiers2="'red'+6">h1</h1>
        </div>
        <script>
          let vm = new Vue({
            el: "#app",
            directives: {
              //局部指令: `v-color`;
              color:{
                bind(el, binding, vnode, oldVnode) {
                  console.log(`bind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                },
                inserted(el, binding, vnode, oldVnode) {
                  console.log(`inserted()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                },
                update(el, binding, vnode, oldVnode) {
                  console.log(`update()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                },
                componentUpdated(el, binding, vnode, oldVnode) {
                  console.log(`componentUpdated()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                },
                unbind(el, binding, vnode, oldVnode) {
                  console.log(`unbind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                },
              }
            },
          });
        </script>
      </body>
    </html>
    

自定义指令的定义方式

  • 自定义指令的定义方式:

    1. 对象,有五个钩子函数

      1. bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

      2. inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

        • 虚拟DOM—真实DOM 插入页面的时候,执行
      3. update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。

      4. componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

      5. unbind:只调用一次,指令与元素解绑时调用。

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
        </head>
        <body>
          <div id="app">
            <h1 v-color:theArg1:theArg2.theModifiers1.theModifiers2="'red'+6" v-background="'blue'">h1</h1>
          </div>
      
          <script>
            //全局指令: `v-color`;
            Vue.directive("color", {
              bind(el, binding, vnode, oldVnode) {
                console.log(`bind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
              inserted(el, binding, vnode, oldVnode) {
                console.log(`inserted()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
      
                el.style.color=binding.value.slice(0,3)
              },
              update(el, binding, vnode, oldVnode) {
                console.log(`update()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
              componentUpdated(el, binding, vnode, oldVnode) {
                console.log(`componentUpdated()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
              unbind(el, binding, vnode, oldVnode) {
                console.log(`unbind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              },
            });
      
            let vm = new Vue({
              el: "#app",
              directives: {
                // bgColor(){},//函数(简写);
      
                // 对象
                bgColor: {
                  bind() {},
                },
      
                //局部指令: `v-background`;
                background:{
                  bind(el, binding, vnode, oldVnode) {
                    console.log(`bind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                  },
                  inserted(el, binding, vnode, oldVnode) {
                    console.log(`inserted()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
      
                    el.style.backgroundColor=binding.value;
                  },
                  update(el, binding, vnode, oldVnode) {
                    console.log(`update()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                  },
                  componentUpdated(el, binding, vnode, oldVnode) {
                    console.log(`componentUpdated()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                  },
                  unbind(el, binding, vnode, oldVnode) {
                    console.log(`unbind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                  },
                }
              },
            });
          </script>
        </body>
      </html>
      
    2. 函数(简写) ,相当于对象的inserted()和update()这两个钩子,常用的方式

      • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

        • 虚拟DOM—真实DOM 插入页面的时候,执行
      • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
        </head>
        <body>
          <div id="app">
            <h1 v-color:theArg1:theArg2.theModifiers1.theModifiers2="'red'+6" v-background="'blue'">h1</h1>
          </div>
      
          <script>
            //全局指令: `v-color`;
            Vue.directive("color", (el, binding, vnode, oldVnode)=> {
              console.log(`函数简写,相当于对象的inserted()和update(): el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
              console.log(`binding.value.slice(0,3)-->`, binding.value.slice(0,3));
              
              el.style.color=binding.value.slice(0,3)
            });
      
            //局部指令: `v-color`;
            let vm = new Vue({
              el: "#app",
              directives: {
                background(el, binding, vnode, oldVnode) {
                  console.log(`函数简写,相当于对象的inserted()和update(): el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
                  el.style.backgroundColor=binding.value
                }
              },
            });
          </script>
        </body>
      </html>
      

vue自定义指令钩子函数的参数

  • 钩子函数里面的参数

    • el:指令所绑定的元素,可以用来直接操作 DOM

    • binding:一个对象,包含以下 property:

      • name:指令名,不包括 v- 前缀。
      • rawName: 指令名,包括 v- 前缀。
      • value:指令的绑定值,计算后的,例如:v-my-directive=“1 + 1” 中,绑定值为 2。
      • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
      • expression:字符串形式的指令表达式。不能计算,例如 v-my-directive=“1 + 1” 中,表达式为 “1 + 1”。
      • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。
      • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
    • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。

    • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  </head>
  <body>
    <div id="app">
      <h1 v-color:theArg1:theArg2.theModifiers1.theModifiers2="'red'+6" v-background="'blue'">h1</h1>
    </div>

    <script>
      //全局指令: `v-color`;
      Vue.directive("color", {
        bind(el, binding, vnode, oldVnode) {
          console.log(`bind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
        },
        inserted(el, binding, vnode, oldVnode) {
          console.log(`inserted()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);

          el.style.color=binding.value.slice(0,3)
        },
        update(el, binding, vnode, oldVnode) {
          console.log(`update()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
        },
        componentUpdated(el, binding, vnode, oldVnode) {
          console.log(`componentUpdated()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
        },
        unbind(el, binding, vnode, oldVnode) {
          console.log(`unbind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
        },
      });

      let vm = new Vue({
        el: "#app",
        directives: {
          // bgColor(){},//函数(简写);

          // 对象
          bgColor: {
            bind() {},
          },

          //局部指令: `v-background`;
          background:{
            bind(el, binding, vnode, oldVnode) {
              console.log(`bind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
            },
            inserted(el, binding, vnode, oldVnode) {
              console.log(`inserted()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);

              el.style.backgroundColor=binding.value;
            },
            update(el, binding, vnode, oldVnode) {
              console.log(`update()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
            },
            componentUpdated(el, binding, vnode, oldVnode) {
              console.log(`componentUpdated()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
            },
            unbind(el, binding, vnode, oldVnode) {
              console.log(`unbind()钩子函数: el-->`,el,`\n binding-->`,binding,`\n vnode-->`,vnode,`\n oldVnode-->`,oldVnode);
            },
          }
        },
      });
    </script>
  </body>
</html>

vue-cli的配置

  • vue-cli的配置文件在/vue.config.js

    • 2023年4月vue2的/vue.config.js新默认配置

      //脚手架的默认配置
      const { defineConfig } = require('@vue/cli-service')//用于提示的一个函数,写在它里面,可以提示一些TypeScript数据类型。
      module.exports = defineConfig({
          transpileDependencies: true,
          //...
      })
      
    • 老vue2的/vue.config.js新默认配置

      //老配置
      module.exports = {
          //...
      }
      
  • vue-cli默认有一套配置规则,在/vue.config.js添加内容会覆盖默认配置信息,增加配置

vue-cli基础配置

  • publicPath:“/” 项目打包放到服务器上后,从服务器的根文件下读取文件(html)

    • “./” 项目打包放到服务器上后,根据html文件的位置读取相关文件
  • outputDir:打包的出口文件夹名称 dist

  • assetsDir: 把打包的文件放到对应文件夹里 (js、css、img、fonts)

  • indexPath: 打包后的html文件的路径和名称

  • pages:配置多入口和多出口

  • *lintOnSave: boolean | ‘warning’ | ‘default’ | ‘error’

    • 默认:‘default’

    • ‘default’/‘error’:控制台爆红,不会继续编译(弹窗)

    • ‘warning’/true:控制台爆红,继续编译

    • false:控制台不爆红,代码继续编译(eslint 失效)

    • 实际业务开发中:

      • 生产环境:false
      • 开发环境:true/‘warning’
  • productionSourceMap: true 打包js的时候,是否生成map文件 [代码调试文件]

  • runtimeCompiler:false 是否组件中使用 template 选项了,但是这会让你的应用额外增加 10kb 左右

  • transpileDependencies:false es6转成es5的过程中,会忽略所有node_modules中的文件

    • 想要转 :值 --> 数组 [‘bfj’,“base/index.js”]

进阶配置

  • configureWebpack 增加某些配置项(loader plugins)

  • chainWebpack 允许对内部的 webpack 配置进行更细粒度的修改

  • css.requireModuleExtension:true

    • 默认情况下,只有 *.module.[ext] 结尾的文件才会被视作 CSS Modules 模块。

      • [ext] ----》 *.(css|scss|sass|less|styl(us)?)
  • css.extract: 生产环境下是 true,开发环境下是 false

    • 是否将组件中的 CSS 提取至一个独立的 CSS 文件中
  • css.sourceMap:false

    • 是否为 CSS 开启 source map
  • css.loaderOptions 新增或修改css loader

  • *跨域配置

    • 开发环境 需要配置 跨域代理服务器 webpack-dev-server

    • 生产环境 就不需要通过webpack配置跨域代理服务器了,需要用过 nginx/apache

    • 配置代理服务器

      module.exports = {
        devServer: {
          host:"127.0.0.1",
          port:"8081",
          open:true,
          proxy:{
            "/zhihu":{
              target:"https://www.zhihu.com"
            },
            "/jianshu":{
              target:"https://www.jianshu.com",//目标服务器网址
              ws: true,//使用websocket
              changeOrigin: true,//是否伪装访问
              pathRewrite:{//网址重写
                "/jianshu":""
              }
            }
          }
        }
      }
      
  • 兼容配置

    1. browerslist(浏览器)

    2. babel-loader @vue/cli-plugin-babel/preset(vue版的babel-loader) es6–es5

    3. class 特色语法处理

    4. Promise API 重写—@babel/polyfill

      • 下载(webpack)

        yarn add @babel/polyfill 
        
      • main.js中导入

        import '@babel/polyfill';
        
      • 直接下载使用,会重写所有的API

      • 不要直接引入

        • babel.config.js

          • 使用 useBuiltIns: ‘usage’,根据你写的es6代码将使用的API进行重写,其余的不会重写

            module.exports = {
              presets: [
                '@vue/cli-plugin-babel/preset',
                {
                  useBuiltIns: 'usage' 
                }
              ]
            }
            

进阶参考