精华内容
下载资源
问答
  • 本篇文章主要介绍了Vue2 SSR 缓存 Api 数据,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • /** * 封装 get、post 请求 * 集成接口缓存过期机制 * 缓存过期将重新请求获取最新数据,并更新缓存 * 数据存储在localstorage * { * cache: true * cacheTime: 1000 * 60 * 3 -- 默认缓存3分钟 * } */ const ...

    注:本篇博客借鉴了https://segmentfault.com/a/1190000022758406
    在此基础上,又根据我的项目https://blog.csdn.net/fmtzy1991/article/details/109065167
    做了部分改变;

    1. 目录结构
      在这里插入图片描述
    2. 用到了apiBefor,cryptoHelper,storage三个文件
    3. apiBefor 是对接口的再封装
      import axios from 'axios';
      import storage from './storage';
      import cryptoHelper from './cryptoHelper';
      
      const root = process.env.API_ROOT;
      
      if (process.env.NODE_ENV === "development") {
        axios.defaults.baseURL = root
      }else {
        axios.defaults.baseURL = root
      }
      axios.defaults.headers.post['Content-Type']='application/x-www-form-urlencoded';
      
      const CANCELTTYPE = {
        CACHE: 1,
        REPEAT: 2,
      };
      
      const pendingRequests = [];
      
      const http = axios.create();
      
      http.interceptors.request.use((config) => {
        /**
         * 为每一次请求生成一个cancleToken
         */
        const source = axios.CancelToken.source();
        config.cancelToken = source.token;
        /**
         * 缓存命中判断
         * 成功则取消当次请求
         */
        //处理FormData生成详细key存储数据
        let objData = {};
        for (let entry of config.data.entries()){
          objData[entry[0]] = entry[1];
        }
        //尝试获取缓存数据
        const data = storage.get(cryptoHelper.encrypt(
          config.url + JSON.stringify(objData) + (config.method || '')
        ));
        if (data && (Date.now() <= data.exppries)) {
          console.log(`接口:${config.url} 参数:${JSON.stringify(objData)}缓存命中 -- ${Date.now()} -- ${data.exppries}`);
          source.cancel(JSON.stringify({
            type: CANCELTTYPE.CACHE,
            data: data.data,
          }));
        }
        return config;
      });
      
      http.interceptors.response.use((res) => {
        if (res.data) {
          const dataParse = res.data;
          //添加缓存时间
          if (!dataParse.cacheTime) {
            dataParse.cacheTime = 1000 * 60 * 5;
          }
          //处理FormData生成详细key存储数据
          let objData = {};
            for (let entry of res.config.data.entries()){
              objData[entry[0]] = entry[1];
            }
            //设置缓存数据
            storage.set(cryptoHelper.encrypt(res.config.url + JSON.stringify(objData) + (res.config.method || '')), {
                data: res.data,
                exppries: Date.now() + dataParse.cacheTime,
              });
            console.log(`接口:${res.config.url} 参数:${JSON.stringify(objData)} 设置缓存,缓存时间: ${dataParse.cacheTime}`);
          return res.data;
        } else if(res.data == ''){
          return res.data;
        }else {
          return Promise.reject('接口报错了!');
        }
      });
      
      /**
       * 封装 get、post 请求
       * 集成接口缓存过期机制
       * 缓存过期将重新请求获取最新数据,并更新缓存
       * 数据存储在localstorage
       * {
       *      cache: true
       *      cacheTime: 1000 * 60 * 3  -- 默认缓存3分钟
       * }
       */
      const httpHelper = {
        get(url, params) {
          return new Promise((resolve, reject) => {
            http.get(url, params).then(async (res) => {
              resolve(res);
            }).catch(error => {
              if (axios.isCancel(error)) {
                const cancle = JSON.parse(error.message);
                if (cancle.type === CANCELTTYPE.REPEAT) {
                  return resolve([]);
                } else {
                  return resolve(cancle.data);
                }
              } else {
                return reject(error);
              }
            });
          });
        },
        post(url, params) {
          return new Promise((resolve, reject) => {
            http.post(url, params).then(async (res) => {
              resolve(res);
            }).catch(error => {
              if (axios.isCancel(error)) {
                const cancle = JSON.parse(error.message);
                if (cancle.type === CANCELTTYPE.REPEAT) {
                  return resolve(null);
                } else {
                  return resolve(cancle.data);
                }
              } else {
                return reject(error);
              }
            });
          });
        },
      };
      
      export default httpHelper;
      
      
    4. storage是数据存储
      const Storage = {
         get(key) {
          if (!key) { return; }
          const text = localStorage.getItem(key);
          if (text) {
            return JSON.parse(text);
          } else {
            localStorage.removeItem(key);
            return null;
          }
        },
         set(key, data) {
          if (!key) {
            return;
          }
          localStorage.setItem(key, JSON.stringify(data));
        },
         remove(key) {
          if (!key) {
            return;
          }
          localStorage.removeItem(key);
        }
      }
      
      export default Storage;
      
      
    5. cryptoHelper生成加密串
      import cryptoJs from 'crypto-js';
      
      const CryptoHelper = {
        constructor(key) {
          /**
           * 如需秘钥,可以在实例化时传入
           */
          this.key = key;
        },
        /**
         * 加密
         * @param word
         */
          encrypt(word) {
          if (!word) {
            return '';
          }
          const encrypted = cryptoJs.MD5(word);
          return encrypted.toString();
        }
      }
      
      export default CryptoHelper;
      
      
    6. 注意地方:因为我之前是用fromdata方式传参,所以在生成存储变量时要做一下数据处理,将参数转化为字符串,否则在调取缓存数据时不能精准调取;然后返回数据量过多的接口也不建议使用缓存;
    展开全文
  • Vue2.x与@vue/compositio-api上手指南

    千次阅读 2020-07-10 21:41:10
    @vue/composition-api分享 该API现已稳定! ???? 当迁移到 Vue 3 时,只需简单的将 @vue/composition-api 替换成 vue 即可。你现有的代码几乎无需进行额外的改动。 动机与目的 更好的逻辑复用与代码组织 随着功能...

    @vue/composition-api分享

    API现已稳定!本文主要是按照官方文档加上自己的理解编写,如若有误,烦请指正!

    💡 当迁移到 Vue 3 时,只需简单的将 @vue/composition-api 替换成 vue 即可。你现有的代码几乎无需进行额外的改动。

    动机与目的

    • 更好的逻辑复用与代码组织
      1. 随着功能的增长,复杂组件的代码变得越来越难以阅读和理解。
      2. 目前缺少一种简洁且低成本的机制来提取和重用多个组件之间的逻辑。
    • 更好的类型推导
      Vue 当前的API 在集成 TypeScript 时遇到了不小的麻烦,其主要原因是 Vue 依靠一个简单的this 上下文来暴露property,我们现在使用this 的方式是比较微妙的。(比如 methods 选项下的函数的 this 是指向组件实例的,而不是这个 methods 对象)。
    • mixins 带来的问题
      1. 渲染上下文中暴露的 property 来源不清晰。例如在阅读一个运用了多个 mixin 的模板时,很难看出某个 property 是从哪一个 mixin 中注入的。
      2. 命名空间冲突。Mixin 之间的 property 和方法可能有冲突,同时高阶组件也可能和预期的 prop 有命名冲突。
      3. 性能方面,高阶组件和无渲染组件需要额外的有状态的组件实例,从而使得性能有所损耗。

    安装与使用

    • vue create app创建vue 2.x项目或现有vue 2.x项目

    • npm / yarn

      1. 安装

        npm install @vue/composition-api
        # or
        yarn add @vue/composition-api
        
      2. 使用

        import Vue from 'vue'
        import VueCompositionAPI from '@vue/composition-api'
        
        Vue.use(VueCompositionAPI)
        
        // 在组件中
        import { ref, reactive } from '@vue/composition-api'
        
    • cdn

      1. 安装

        <!-- 在 Vue 之后引入 @vue/composition-api ,插件将会自动完成安装。 -->
        
        <script src="https://cdn.jsdelivr.net/npm/vue@2.6"></script>
        <script src="https://cdn.jsdelivr.net/npm/@vue/composition-api@1.0.0-beta.3"></script>
        
        <!-- @vue/composition-api 将会暴露在全局变量 window.VueCompositionAPI 中。 -->
        
      2. 使用

        1. 方式1
        const { ref, reactive } = window.VueCompositionAPI;
        
        1. 方式2
          vue.config.js中,配置externals,之后与npm的使用方式相同
          只不或引入是import VueCompositionAPI from "VueCompositionAPI";
        module.exports = {
          configureWebpack: config => {
            config.externals = {
              vue: "Vue",
              VueCompositionAPI: "VueCompositionAPI"
            };
          }
        };
        

    与现有的 API 配合

    组合式 API 完全可以和现有的基于选项的 API 配合使用。

    • 组合式 API 会在 2.x 的选项 (datacomputedmethods) 之前解析,并且不能提前访问这些选项中定义的 property。
    • setup() 函数返回的 property 将会被暴露给 this。它们在 2.x 的选项中可以访问到。

    setup

    setup 函数是一个新的组件选项。作为在组件内使用 Composition API 的入口点。

    • 调用时机
      创建组件实例,然后初始化 props ,紧接着就调用setup 函数。从生命周期钩子的视角来看,它会在 beforeCreate 钩子之前被调用 但是在vue2.x版本中,是先执行beforeCreatd 之后是setup再是created

    • 模板中使用
      如果 setup 返回一个对象,则对象的属性将会被合并到组件模板的渲染上下文

    • 参数

      该函数接收 props 作为其第一个参数, 不能结构props,会失去响应式。

      export default {
        props: {
          name: String,
        },
        setup(props) {
          console.log(props.name)
        },
      }
      

      第二个参数提供一个上下文对象,可以解构。这些API大家都知道就不一一介绍了,root根组件的实例。attrsslots 都是内部组件实例上对应项的代理,可以确保在更新后仍然是最新值。所以可以解构,无需担心后面访问到过期的值。
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WymPLQn0-1595397952389)(/Users/liqiankun/Library/Application Support/typora-user-images/image-20200710173426685.png)]

    API介绍

    reactive

    先看一个简单的单页面文件

    <template>
      <div @click="increment">{{ state.count }}</div>
    </template>
    
    <script>
    import { reactive } from "@vue/composition-api";
    
    export default {
      setup() {
        // 依赖收集
        const state = reactive({
          count: 0
        });
    
        /**
         * 增加state.count的值
         */
        function increment() {
          state.count++;
        }
    		
        return {
          state,
          increment
        };
      }
    };
    </script>
    

    reactive 几乎等价于 2.x 中现有的 Vue.observable() API,且为了避免与RxJS 中的 observable 混淆而做了重命名。这里返回的 state 是一个所有 Vue 用户都应该熟悉的响应式对象。

    注意

    1. reactive() 会返回一个修改过的原始的对象,此行为与 Vue 2 中的 Vue.observable 一致,而在vue3中,reactive() 会返回一个新的的代理对象。

    2. 使用组合函数时必须始终保持对这个所返回对象的引用以保持响应性。这个对象不能被解构或展开,可以使用toRefsApi去解决这个问题

      function useMousePosition() {
        const pos = reactive({
          x: 0,
          y: 0,
        })
      
        // ...
        return toRefs(pos)
      }
      
      // x & y 现在是 ref 形式了!
      const { x, y } = useMousePosition()
      

    ref

    还是一个简单的单页面文件,功能与reactive的例子一样

    <template>
      <div @click="increment">{{ count }}</div>
    </template>
    
    <script>
    import { ref } from "@vue/composition-api";
    
    export default {
      setup() {
        // 依赖收集
        const count = ref(0);
    
        /**
         * 增加count的值
         */
        function increment() {
          count.value++;
        }
    
        return {
          count,
          increment
        };
      }
    };
    </script>
    

    看出来差别了吗?count的增加有个value

    • 为什么会有这个value
      由于JavaScript 中基础类型是值传递而非引用传递,一个响应式的值一旦作为 property 被赋值或从一个函数返回,而失去了响应性之后,也就失去了用途。为了确保始终可以读取到最新的计算结果,我们需要将这个值上包裹到一个对象中再返回。另外我们同样需要劫持对这个对象 .value property 的读/写操作,现在我们可以通过引用来传递计算值,也不需要担心其响应式特性会丢失了。当然代价就是:为了获取最新的值,我们每次都需要写 .value

    • 那为什么在template模板中,又不需要写.value了呢?
      在渲染过程中,Vue 会直接使用其内部的值,也就是说在模板中你可以把 {{ count.value }} 直接写为 {{ count }}

    注意

    1. 不要使用数组直接存取ref对象

      const state = reactive({
        list: [ref(0)],
      })
      // 不会自动展开, 须使用 `.value`
      state.list[0].value === 0 // true
      
      state.list.push(ref(1))
      // 不会自动展开, 须使用 `.value`
      state.list[1].value === 1 // true
      
    2. 不要在数组中使用含有 ref 的普通对象

      const a = reactive({
        count: ref(0),
      })
      const b = reactive({
        list: [a],
      })
      // 自动展开
      b.list[0].count === 0 // true
      
      b.list.push(
        reactive({
          count: ref(1),
        })
      )
      // 自动展开
      b.list[1].count === 1; // true
      
    3. 在数组中,应该总是将 ref 存放到 reactive 对象中

      const a = reactive({
        count: ref(0),
      })
      const b = reactive({
        list: [a],
      })
      // 自动展开
      b.list[0].count === 0 // true
      
      b.list.push(
        reactive({
          count: ref(1),
        })
      )
      // 自动展开
      b.list[1].count === 1; // true
      

    以上的场景应该很少会遇到,但是也不能不排除,一般来说在业务中,这样使用数组就行了。

    • 使用ref定义数组

      const list = ref([]);
      list.value.push('xxx');
      list.value[3] = 'xxx';
      
    • 使用reactive定义数组

      const state = reactive({
        list: []
      });
      state.list.push('xxx');
      state.list[3] = 'xxx';
      

    Ref VS Reactive

    1. 就像你在普通 JavaScript 中区别声明基础类型变量与对象变量时一样区别使用 refreactive。我们推荐你在此风格下结合 IDE 使用类型系统。
    2. 所有的地方都用 reactive,然后记得在组合函数返回响应式对象时使用 toRefs。这降低了一些关于 ref 的心智负担,但并不意味着你不需要熟悉这个概念。

    笼统的来讲就是两种风格,看个人爱好。

    computed

    传入一个 getter 函数,返回一个默认不可手动修改的 ref 对象。

    import { computed } from "@vue/composition-api";
    // ...
    const count = ref(1)
    const plusOne = computed(() => count.value + 1)
    
    console.log(plusOne.value) // 2
    
    plusOne.value++ // 错误!
    

    或者传入一个拥有 getset 函数的对象,创建一个可手动修改的计算状态。

    import { computed } from "@vue/composition-api";
    // ...
    const count = ref(1)
    const plusOne = computed({
      get: () => count.value + 1,
      set: (val) => {
        count.value = val - 1
      },
    })
    
    plusOne.value = 1
    console.log(count.value) // 0
    

    传参

    import { computed } from "@vue/composition-api";
    // ...
    const getActiveStatus = computed(() => index => {
      return activeIndex.value === index;
    });
    

    watch

    watch API 完全等效于 2.x this.$watch (以及 watch 中相应的选项)。watch 需要侦听特定的数据源,并在回调函数中执行副作用。默认情况是懒执行的,也就是说仅在侦听的源变更时才执行回调。

    • 侦听单个数据源

      侦听器的数据源可以是一个拥有返回值的 getter 函数,也可以是 ref:

      // 侦听一个 getter
      const state = reactive({ count: 0 })
      watch(
        () => state.count,
        (count, prevCount) => {
          /* ... */
        }
      )
      
      // 直接侦听一个 ref
      const count = ref(0)
      watch(count, (count, prevCount) => {
        /* ... */
      })
      
    • 侦听多个数据源

      watcher 也可以使用数组来同时侦听多个源:

      watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
        /* ... */
      })
      
    • immediate等
      监听路由变化

      import { watch } from "@vue/composition-api";
      
      export default {
        setup(props, { refs, root }) {
          const { $route } = root;
      
          watch(
            () => $route,
            (to, from) => {
              console.log(to, from);
            },
            { immediate: true }
          );
      
          return {};
        }
      };
      
    • watch是可以停止的

      import { watch } from "@vue/composition-api";
      
      export default {
        setup() {
      
          const stopWatch = watch('xxxx');
          
          // 执行即可停止监听
          // watch返回一个函数 function(){ stop() }
          stopWatch()
      
          return {};
        }
      };
      

    watchEffect

    立即执行传入的一个函数,并响应式追踪其依赖,并在其依赖变更时重新运行该函数。

    import { ref, watchEffect } from "@vue/composition-api";
    export default {
      setup() {
        const count = ref(0);
    
        watchEffect(() => console.log(count.value)); // -> 打印出 0
    
        setTimeout(() => {
          count.value++; // -> 打印出 1
        }, 100);
    
        return {};
      }
    };
    

    停止侦听

    watchEffect 在组件的 setup() 函数或生命周期钩子被调用时, 侦听器会被链接到该组件的生命周期,并在组件卸载时自动停止。

    在一些情况下,也可以显式调用返回值以停止侦听:

    const stop = watchEffect(() => {
      /* ... */
    })
    
    // 之后
    stop()
    

    清除副作用

    有时副作用函数会执行一些异步的副作用, 这些响应需要在其失效时清除(即完成之前状态已改变了)。所以侦听副作用传入的函数可以接收一个 onInvalidate 函数作入参, 用来注册清理失效时的回调。当以下情况发生时,这个失效回调会被触发:

    • 副作用即将重新执行时
    • 侦听器被停止 (如果在 setup() 或 生命周期钩子函数中使用了 watchEffect, 则在卸载组件时)
    watchEffect((onInvalidate) => {
      const token = performAsyncOperation(id.value)
      onInvalidate(() => {
        // id 改变时 或 停止侦听时
        // 取消之前的异步操作
        token.cancel()
      })
    })
    

    副作用刷新时机

    Vue 的响应式系统会缓存副作用函数,并异步地刷新它们,这样可以避免同一个 tick 中多个状态改变导致的不必要的重复调用。在核心的具体实现中, 组件的更新函数也是一个被侦听的副作用。当一个用户定义的副作用函数进入队列时, 会在所有的组件更新后执行:

    <template>
      <div>{{ count }}</div>
    </template>
    
    <script>
    	import { ref, watchEffect } from "@vue/composition-api"';
      export default {
        setup() {
          const count = ref(0)
    
          watchEffect(() => {
            console.log(count.value)
          })
    
          return {
            count,
          }
        },
      }
    </script>
    

    在这个例子中:

    • count 会在初始运行时同步打印出来
    • 更改 count 时,将在组件更新后执行副作用。

    请注意,初始化运行是在组件 mounted 之前执行的。因此,如果你希望在编写副作用函数时访问 DOM(或模板 ref),请在 onMounted 钩子中进行:

    onMounted(() => {
      watchEffect(() => {
        // 在这里可以访问到 DOM 或者 template refs
      })
    })
    

    如果副作用需要同步或在组件更新之前重新运行,我们可以传递一个拥有 flush 属性的对象作为选项(默认为 'post'):

    // 同步运行
    watchEffect(
      () => {
        /* ... */
      },
      {
        flush: 'sync',
      }
    )
    
    // 组件更新前执行
    watchEffect(
      () => {
        /* ... */
      },
      {
        flush: 'pre',
      }
    )
    

    响应式系统工具集

    unref

    如果参数是一个 ref 则返回它的 value,否则返回参数本身。它是 val = isRef(val) ? val.value : val 的语法糖。

    function useFoo(x: number | Ref<number>) {
      const unwrapped = unref(x) // unwrapped 一定是 number 类型
    }
    

    toRef

    toRef 可以用来为一个 reactive 对象的属性创建一个 ref。这个 ref 可以被传递并且能够保持响应性。

    当您要将一个 prop 中的属性作为 ref 传给组合逻辑函数时,toRef 就派上了用场:

    export default {
      setup(props) {
        useSomeFeature(toRef(props, 'foo'))
      },
    }
    

    toRefs

    把一个响应式对象转换成普通对象,该普通对象的每个 property 都是一个 ref ,和响应式对象 property 一一对应。

    当想要从一个组合逻辑函数中返回响应式对象时,用 toRefs 是很有效的,该 API 让消费组件可以 解构 / 扩展(使用 ... 操作符)返回的对象,并不会丢失响应性:

    function useFeatureX() {
      const state = reactive({
        foo: 1,
        bar: 2,
      })
    
      // 对 state 的逻辑操作
    
      // 返回时将属性都转为 ref
      return toRefs(state)
    }
    
    export default {
      setup() {
        // 可以解构,不会丢失响应性
        const { foo, bar } = useFeatureX()
    
        return {
          foo,
          bar,
        }
      },
    }
    

    isRef

    检查一个值是否为一个 ref 对象。

    高级响应式系统 API

    customRef

    customRef 用于自定义一个 ref,可以显式地控制依赖追踪和触发响应,接受一个工厂函数,两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回一个一个带有 getset 属性的对象

    • 使用自定义 ref 实现带防抖功能的 v-model

      <input v-model="text" />
      
      function useDebouncedRef(value, delay = 200) {
        let timeout
        return customRef((track, trigger) => {
          return {
            get() {
              track()
              return value
            },
            set(newValue) {
              clearTimeout(timeout)
              timeout = setTimeout(() => {
                value = newValue
                trigger()
              }, delay)
            },
          }
        })
      }
      
      export default {
        setup() {
          return {
            text: useDebouncedRef('hello'),
          }
        },
      }
      

    生命周期

    可以直接导入 onXXX 一族的函数来注册生命周期钩子

    import {
      onMounted,
      onUpdated,
      onUnmounted
    } from "@vue/composition-api";
    
    export default {
      beforeRouteLeave(to, from, next) {
        //可以获取this
        //路由跳转,不适用此组件时触发
        console.log("beforeRouteLeave", this);
        next();
      },
      setup() {
        onMounted(() => console.log("onMounted"));
    
        onUpdated(() => console.log("onUpdated"));
    
        onUnmounted(() => console.log("onUnmounted"));
    
        return {};
      }
    };
    
    

    与 2.x 版本生命周期相对应的组合式 API

    • beforeCreate -> 使用 setup()
    • created -> 使用 setup()
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeDestroy -> onBeforeUnmount
    • destroyed -> onUnmounted
    • errorCaptured -> onErrorCaptured

    依赖注入

    • provideinject 提供依赖注入,功能类似 2.x 的 provide/inject。两者都只能在当前活动组件实例的 setup() 中调用。

    • inject 接受一个可选的的默认值作为第二个参数。如果未提供默认值,并且在 provide 上下文中未找到该属性,则 inject 返回 undefined

    父组件

    <template>
      Dom结构
    </template>
    
    <script>
    import { provide, ref } from "@vue/composition-api";
    export default {
      setup() {
        // ref注入响应式对象
        provide("globalMsg", ref("给我的子孙后代捎句话"));
      }
    };
    </script>
    

    孙组件

    <template>
      Dom结构
    </template>
    
    <script>
    import { inject } from "@vue/composition-api";
    export default {
      setup() {
        // 默认值只有父级没有定义 provide("globalMsg") 的时候才有效,定义了没传值也不会显示默认值
        const globMsg = inject("globalMsg", '默认消息');
      }
    };
    </script>
    

    模板Refs

    Virtual DOM patch 算法中,如果一个VNoderef 对应一个渲染上下文中的 ref,则该 VNode 对应的元素或组件实例将被分配给该 ref。 这是在 Virtual DOMmount / patch 过程中执行的,因此模板 ref 仅在渲染初始化后才能访问。

    ref 被用在模板中时和其他 ref 一样:都是响应式的,并可以传递进组合函数(或从其中返回)。

    <template>
      <div class="about">
        <h1 ref="h1Dom">This is an about page</h1>
      </div>
    </template>
    
    <script>
    import { ref, onMounted } from "@vue/composition-api";
    export default {
      setup() {
        const h1Dom = ref(null);
    
        onMounted(() => console.log(h1Dom.value)); // 打印出了DOM
    
        return {
          h1Dom
        };
      }
    };
    </script>
    

    也可以通过以下这种方式获得refs引用,早期的composition-api只能通过这种方式获得引用~

    <template>
      <div class="about">
        <h1 ref="h1Dom">This is an about page</h1>
      </div>
    </template>
    
    <script>
    import { onMounted } from "@vue/composition-api";
    export default {
      setup(props, { refs }) {
    
        onMounted(() => console.log(refs.h1Dom)); // 打印出了DOM
    
      }
    };
    </script>
    

    在列表中使用

    <template>
      <div class="about">
        <ul>
          <li v-for="i in 3" ref="liList" :key="i">{{ i }}</li>
        </ul>
      </div>
    </template>
    
    <script>
    import { ref, onMounted } from "@vue/composition-api";
    
    export default {
      setup() {
        const liList = ref([]);
    
        onMounted(() => console.log(liList.value));
    
        return { liList };
      }
    };
    </script>
    
    

    另一种方式

    <template>
      <div class="about">
        <ul>
          <li v-for="i in 3" ref="liList" :key="i">{{ i }}</li>
        </ul>
      </div>
    </template>
    
    <script>
    import { onMounted } from "@vue/composition-api";
    
    export default {
      setup(props, { refs }) {
        onMounted(() => console.log(refs.liList));
    
        return {};
      }
    };
    </script>
    
    

    在setup中使用Vue-Router的API

    export default {
      setup(props, { root }) {
        
        const { $router, $route } = root;
        console.log($router, $route);
        
        // $router.push(...)
        // $route.path ...
        
      }
    };
    

    在setup中使用$nextTick等

    export default {
      setup(props, { root }) {
        
        const { $nextTick } = root;
        console.log($nextTick);
        
      }
    };
    

    在setup中使用Vuex的API

    最简化版本的

    假如我们的vuex是这样定义的

    import Vue from "vue";
    import Vuex from "vuex";
    
    Vue.use(Vuex);
    
    export default new Vuex.Store({
      state: { count: 0 },
      mutations: {
        ["CHAGNE_COUNT"](state) {
          state.count++;
        }
      },
      actions: {},
      modules: {}
    });
    
    

    setup中,我们可以这样使用

    <template>
      <div class="about">
        {{ $store.state.count }}
      </div>
    </template>
    
    <script>
    export default {
      setup(props, { root }) {
        const { $store } = root;
    
        $store.commit("CHAGNE_COUNT");
    
        return {};
      }
    };
    </script>
    
    

    如何使用mapStatemapGettersmapActionsmapMutations?

    直接在return的对象中解耦即可

    <template>
      <div class="about" @click="handleAddCount">
        {{ $store.state.count }}
      </div>
    </template>
    
    <script>
    import { mapMutations } from "vuex";
    export default {
      setup() {
        return { ...mapMutations({ handleAddCount: "CHAGNE_COUNT" }) };
      }
    };
    </script>
    
    

    组合式API

    命名规范

    我们建议使用 use 作为函数名的开头,以表示它是一个组合函数。

    逻辑的提取与复用

    我们创建一个src/hooks/useMousePosition.js的文件,内容如下

    import { ref, onMounted, onUnmounted } from "@vue/composition-api";
    
    export default function useMousePosition() {
      const x = ref(0);
      const y = ref(0);
    
      function update(e) {
        x.value = e.pageX;
        y.value = e.pageY;
      }
    
      onMounted(() => {
        window.addEventListener("mousemove", update);
      });
    
      onUnmounted(() => {
        window.removeEventListener("mousemove", update);
      });
    
      return { x, y };
    }
    
    

    在单页面文件中使用

    <template>
      <div class="about">当前位置x: {{ x }} - y:{{ y }}</div>
    </template>
    
    <script>
    import useMousePosition from "../hooks/useMousePosition";
    export default {
      setup() {
        const { x, y } = useMousePosition();
        return { x, y };
      }
    };
    </script>
    
    

    类似的逻辑复用也可以通过诸如 mixins、高阶组件或是 (通过作用域插槽实现的) 无渲染组件的模式达成。网上已经有很多解释这些模式的信息了所以我们不再赘述。更高层面的想法是,相比于组合函数,这些模式都有各自的弊端:

    • 渲染上下文中暴露的 property 来源不清晰。例如在阅读一个运用了多个 mixin 的模板时,很难看出某个 property 是从哪一个 mixin 中注入的。
    • 命名空间冲突。Mixin 之间的 property 和方法可能有冲突,同时高阶组件也可能和预期的 prop 有命名冲突。
    • 性能方面,高阶组件和无渲染组件需要额外的有状态的组件实例,从而使得性能有所损耗。

    相比而言,组合式 API:

    • 暴露给模板的 property 来源十分清晰,因为它们都是被组合逻辑函数返回的值。
    • 不存在命名空间冲突,可以通过解构任意命名
    • 不再需要仅为逻辑复用而创建的组件实例。

    setup() 函数现在只是简单地作为调用所有组合函数的入口

    export default {
      setup() {
        // 网络状态
        const { networkState } = useNetworkState()
    
        // 文件夹状态
        const { folders, currentFolderData } = useCurrentFolderData(networkState)
        const folderNavigation = useFolderNavigation({
          networkState,
          currentFolderData,
        })
        const { favoriteFolders, toggleFavorite } = useFavoriteFolders(
          currentFolderData
        )
        const { showHiddenFolders } = useHiddenFolders()
        const createFolder = useCreateFolder(folderNavigation.openFolder)
    
        // 当前工作目录
        resetCwdOnLeave()
        const { updateOnCwdChanged } = useCwdUtils()
    
        // 实用工具
        const { slicePath } = usePathUtils()
    
        return {
          networkState,
          folders,
          currentFolderData,
          folderNavigation,
          favoriteFolders,
          toggleFavorite,
          showHiddenFolders,
          createFolder,
          updateOnCwdChanged,
          slicePath,
        }
      },
    }
    
    • 每个逻辑关注点的代码现在都被组合进了一个组合函数。这大大减少了在处理大型组件时不断“跳转”的需要。
    • 你还可以根据传递的参数清楚地看到组合函数之间的依赖关系。
    • 最后的 return 语句作为单一出口确认暴露给模板的内容。
    • 当然这只是举个例子,项目中实际情况随机应变

    缺失的 API

    以下在 Vue 3 新引入的 API ,在本插件中暂不适用:

    • readonly
    • shallowReadonly
    • defineAsyncComponent
    • onRenderTracked
    • onRenderTriggered
    • isProxy
    • isReadonly
    • isVNode

    JSX

    使用babel插件babel-preset-vca-jsx

    使用前提

    已安装@vue/composition-api@vue/cli-plugin-babel的项目

    如何使用?

    1. 安装

      npm install babel-preset-vca-jsx --save-dev
      
    2. 配置 babel.config.js

      module.exports = {
          presets: [
              "vca-jsx",
              "@vue/cli-plugin-babel/preset"
          ]
      };
      

    注意

    • 这里需要区分默认的 functional组件和基于 composition-apifunctional组件的概念

      • 默认的 functional组件实质是 render函数,jsx中的简写写法如下

        const Test = ({ props, children, data, ... }) => {
            return <h1>Hello World!</h1>;
        };
        

        注:变量名首字母必须为大写,具体回调参数见详情

      • 基于本插件的 composition-api functional实质是 setup函数,jsx中的简写写法如下

        const Test = (props, { refs, emit, ... }) => {
            return () => <h1>Hello World!</h1>;
        };
        

        注:与默认functional的区别是返回了一个render函数

      • 如果出现 "export 'createElement' (imported as 'h') was not found in '@vue/composition-api'

        删除node_modules
        然后npm install
        找到node_modules里面的babel-preset-vca-jsx/src/babel-sugar-inject-h.js,修改里面的代码
        requests-#12

        // t.isImportSpecifier(s) && s.imported.name === 'h' && s.local.name === 'h'
           t.isImportSpecifier(s) && s.local.name === 'h'
        

    demo

    jsx与vue-router

    写一个jsx文件 比如名字叫Page.jsx

    import { ref } from "@vue/composition-api";
    
    const Hello = () => {
      const state = ref("Hello World!");
      return () => <h1>{state.value}</h1>;
    };
    
    export default {
      setup(props, ctx) {
        console.log(ctx);
        return () => (
          <h1>
           // jsx中组件不用注册 直接使用 不会注册到全局中
            page1 <Hello></Hello>
          </h1>
        );
      }
    };
    
    

    在路由中这样引入

    {
        path: "/page",
        name: "page",
        component: () => import(/* webpackChunkName: "page" */ "../views/Page.jsx")
      }
    

    jsx与vue文件混用

    import Page from './Page.jsx'
    export default {
      components:{
        Page
      } 
    }
    

    jsx与props

    const Button = {
      props: {
        type: {},
        size: {}
      },
      setup(props, ctx) {
        return () => (
          <el-button props={props}>
            {ctx.slots.default()}---{props.size}
          </el-button>
        );
      }
    };
    

    Babel的设置

    如果您引入完整的babel了,可跳过该章。如果是vue-cli默认的按需引入,需要在vue.config.js中增加如下配置,用以兼容IE。

    const path = require('path');
    const resolve = function(dir) {
      return path.join(__dirname, dir);
    };
    
    module.exports = {
    	// ...
      transpileDependencies: ['@vue/composition-api'],
      chainWebpack: config => {
        config.module
          .rule('js')
          .include.add(resolve('node_modules/@vue/composition-api'))
      }
      // ...
    }  
    ```## todoList
    
    目录结构
    
    ```shell
    todoList
    |____index.vue
    |____hooks
    | |____useDataList.js
    | |____useLiEvent.js
    

    index.vue

    <template>
      <div>
        <div v-if="dataListLoadingStatus">加载中...</div>
        <ul v-else>
          <li v-for="item in dataList" :key="item.id" @click="handleClickLi(item)">
            {{ item.text }}
          </li>
        </ul>
      </div>
    </template>
    
    <script>
    import useDataList from "./hooks/useDataList";
    import useLiEvent from "./hooks/useLiEvent";
    
    export default {
      setup() {
        const { dataList, loadingStatus: dataListLoadingStatus } = useDataList();
        const { handleClick: handleClickLi } = useLiEvent();
    
        return {
          dataList,
          dataListLoadingStatus,
          handleClickLi
        };
      }
    };
    </script>
    
    <style></style>
    
    

    useDataList.js

    import { ref, onMounted } from "@vue/composition-api";
    
    /**
     * 获取模拟的数据列表
     * @returns {Array} dataList - 数据列表
     * @returns {Boolean} loadingStatus - 请求状态
     */
    function useDataList() {
      /**
       * 加载状态
       */
      const loadingStatus = ref(false);
      /**
       * 数据列表
       */
      const dataList = ref([]);
    
      /**
       * 模拟获取数据列表
       */
      function getDataList() {
        changeLoadingStatus(true);
    
        setTimeout(() => {
          dataList.value = [
            { id: 1, text: "小红" },
            { id: 2, text: "小明" },
            { id: 3, text: "小黄" }
          ];
    
          changeLoadingStatus(false);
        }, 3000);
      }
    
      /**
       * 更改loading状态
       * @param {boolean} status 状态值
       * @returns {void}
       */
      function changeLoadingStatus(status) {
        loadingStatus.value = status;
      }
    
      onMounted(() => getDataList());
    
      return {
        dataList,
        loadingStatus
      };
    }
    
    export default useDataList;
    
    

    UseLiEvent.js

    /**
     * 点击li的回调
     */
    function useLiEvent() {
      /**
       * 点击之后的回调
       * @param {HTMLElement} item
       */
      function handleClick(item) {
        console.log(item);
      }
    
      return {
        handleClick
      };
    }
    
    export default useLiEvent;
    
    

    鸣谢

    展开全文
  • Vue SSR服务端渲染之数据缓存

    千次阅读 2018-07-20 18:02:00
    当我们在做vue的服务器端渲染时,可能会碰到各种各样的坑,内存泄露就是其中的一种。当然,导致内存泄露的原因有很多,不合理使用Axios也是其中一种,那下面我...2. api 配置文件 config-server.js var LRU = req...

    当我们在做vue的服务器端渲染时,可能会碰到各种各样的坑,内存泄露就是其中的一种。当然,导致内存泄露的原因有很多,不合理使用Axios也是其中一种,那下面我给大家介绍一下如何有效的避免请求中的内存泄露。

    1. 安装缓存依赖: lru-cache

    npm install lru-cache --dev

    2. api 配置文件

    config-server.js

    var LRU = require('lru-cache')
    
    let api
    if (process.__API__) {
        api = process.__API__
    } else {
        api = process.__API__ = {
            api: 'http://localhost:8181/api/',
            //配置缓存
            cached: LRU({
                max: 1000,
                maxAge: 1000 * 60 * 15
            }),
            cachedItem: {}
        }
    }
    
    module.exports = api

    3. 封装下 api

    import axios from 'axios'
    import qs from 'qs'
    import md5 from 'md5'
    import config from './config-server.js'
    
    export default {
        post(url, data) {
            const key = md5(url + JSON.stringify(data))
            if (config.cached && config.cached.has(key)) {
                return Promise.resolve(config.cached.get(key))
            }
            return axios({
                method: 'post',
                url: config.api + url,
                data: qs.stringify(data),
                // 其他配置
            }).then(res => {
                if (config.cached && data.cache) config.cached.set(key, res)
                return res
            })
        }
    }

    ajax 库我们用axios, 因为axios在 nodejs 和 浏览器都可以使用,并且将 node 端和浏览器端分开封装

    import config from './config-server.js'
    const key = md5(url + JSON.stringify(data))

    通过 url 和参数, 生成一个唯一的 key

    if (config.cached && config.cached.has(key)) {
        return Promise.resolve(config.cached.get(key))
    }
    if (config.cached && data.cache) config.cached.set(key, res)

    判断下是不是开启了缓存, 并且接口指定缓存的话, 将 api 返回的数据, 写入缓存

    注意:这个 api 会处理所有的请求, 但是很多请求其实是不需要缓存的, 所以需要缓存可以在传过来的 data 里, 添加个 cache: true, 如:

    api.post('/api/test', {a: 1, b:2, cache: true})

    不需要缓存的直接按正常传值即可

    展开全文
  • Vue页面级缓存解决方案feb-alive (上) 在剖析feb-alive实现之前,希望大家对以下基本知识有一定的了解。 keep-alive实现原理 history api vue渲染原理 vue虚拟dom原理 feb-alive与keep-alive差异性 1. 针对...
  • Vue3.x API

    2020-09-11 16:46:50
    config是一个包含Vue应用程序全局配置的对象。可以在挂载应用程序之前修改下面列出的属性: const app = Vue.createApp({}) app.config = {...} 1、devtools 类型: boolean 默认值: true (false in production ...

    一、Application Config(app.config)

    config是一个包含Vue应用程序全局配置的对象。可以在挂载应用程序之前修改下面列出的属性:

    const app = Vue.createApp({})
    app.config = {...}
    

    1、devtools

    • 类型: boolean
    • 默认值: true (false in production builds)
    • 使用:
    app.config.devtools = true
    

    配置是否允许vue-devtools检查。此选项的默认值在开发版本中为true,在生产版本中为false。您可以将其设置为true以启用对生产版本的检查。

    2、errorHandler

    • 类型: Function
    • 默认值: undefined
    • 使用:
    app.config.errorHandler = (err, vm, info) => {
      // handle error
      // info是Vue特定的错误信息,例如 在哪个生命周期挂钩中发现错误
    }
    

    为组件渲染功能和观察程序期间的未捕获错误分配处理程序。

    3、warnHandler

    • 类型: Function
    • 默认值: undefined
    • 使用:
    app.config.warnHandler = function(msg, vm, trace) {
      // trace是组件层次结构跟踪
    }
    

    为运行时Vue警告分配自定义处理程序。这仅在开发期间有效,在生产中将被忽略。

    4、globalProperties 全局属性

    • 类型: [key: string]: any
    • 默认值: undefined
    • 使用:
    app.config.globalProperties.foo = 'bar'
    
    app.component('child-component', {
      mounted() {
        console.log(this.foo) // 'bar'
      }
    })
    

    冲突时,组件的属性将具有优先权

    在 vue2.x 中是 Vue.prototype

    // 2.x
    Vue.prototype.$http = () => {}
    
    // 3.x
    const app = Vue.createApp({})
    app.config.globalProperties.$http = () => {}
    

    5、isCustomElement

    • 类型: (tag: string) => boolean
    • 默认值: undefined
    • 使用:
    app.config.isCustomElement = tag => tag.startsWith('ion-')
    

    指定一种方法来识别在Vue之外定义的自定义元素(例如,使用Web组件API)。 如果component符合此条件,则不需要本地或全局注册,并且Vue不会发出有关Unknown自定义元素的警告。

    此功能不需要匹配所有HTML和SVG标签,Vue解析器会自动执行此检查

    6、optionMergeStrategies

    • 类型: { [key: string]: Function }
    • 默认值: {}
    • 使用:
    const app = Vue.createApp({
      mounted() {
        console.log(this.$options.hello)
      }
    })
    app.config.optionMergeStrategies.hello = (parent, child, vm) => {
      return `Hello, ${child}`
    }
    app.mixin({
      hello: 'Vue'
    })
    // 'Hello, Vue
    

    自定义选项定义合并策略。

    合并策略将分别在父实例和子实例上定义的该选项的值作为第一个参数和第二个参数。 上下文应用程序实例作为第三个参数传递。

    7、performance

    • 类型: boolean
    • 默认值: false
    • 使用:
    app.config.performance = true
    

    将其设置为true可在浏览器devtool性能/时间线面板中启用组件初始化,编译,渲染和补丁性能跟踪。 仅在开发模式和支持Performance.mark API的浏览器中工作。

    二、Application API(app.xxx)

    在Vue 3.x,全局改变Vue行为的API都在createApp方法所创建的应用程序实例中

    import { createApp } from 'vue'
    const app = createApp({})
    

    1、component

    • 参数:
      • {string} name
      • {Function | Object} definition (optional)
    • 返回值:
      • 如果传递定义参数,则为应用程序实例
      • 如果没有传递定义参数,则为组件
    • 使用:
      • 注册或检索全局组件。 注册还会使用给定的name参数自动设置组件的名称。
    import { createApp } from 'vue'
    const app = createApp({})
    // 注册一个 options 对象
    app.component('my-component', {
      /* ... */
    })
    
    // 检索注册的组件
    const MyComponent = app.component('my-component')
    

    2、config

    包含应用程序配置的对象。(往上翻)

    3、directive

    • 参数:

      • {string} name
      • {Function | Object} definition (optional)
    • 生命周期:

    app.directive('my-directive', {
      // 挂载前
      beforeMount() {},
      // 挂载后
      mounted() {},
      // 更新前
      beforeUpdate() {},
      // 更新后
      updated() {},
      // 卸载前
      beforeUnmount() {},
      // 卸载后
      unmounted() {}
    })
    

    生命周期钩子函数所具有的参数

    1、el

    绑定的DOM元素

    2、binding
    • instance:使用伪指令的组件的实例。
    • value:传递给指令的值。
    • oldValue:以前的值,仅在beforeUpdate和updated中可用。
    • arg:传递给指令的参数。
    • modifiers:包含修饰符的对象。
    • dir:一个对象,在注册指令时作为参数传递。
    3、vnode

    绑定的DOM所对应的虚拟节点

    4、prevNode

    之前的虚拟节点,仅在beforeUpdate和updated中可用

    除el之外,您应该将这些参数视为只读,并且永远不要修改它们。

    示例 自定义 drag 拖动指令

    import { createApp } from 'vue'
    import App from './App.vue'
    let app = createApp(App)
    
    app.directive('drag', {
      beforeMount (el, binding) {
        console.log(el, binding)
        let oDiv = el;   //当前元素
        oDiv.onmousedown = function (e) {
         //鼠标按下,计算当前元素距离可视区的距离
          let disX = e.clientX - oDiv.offsetLeft;
          let disY = e.clientY - oDiv.offsetTop;
          document.onmousemove = function (e) {
            //通过事件委托,计算移动的距离 
            let l = e.clientX - disX;
            let t = e.clientY - disY;
            //移动当前元素  
            oDiv.style.left = l + 'px';
            oDiv.style.top = t + 'px';
             //将此时的位置传出去
            binding.value({x:e.pageX,y:e.pageY}, el)
          };
          document.onmouseup = function () {
            document.onmousemove = null;
            document.onmouseup = null;
          };
        };
      }
    })
    app.mount('#app')
    

    template中使用

    <template>
        <div v-drag="dragFun" class="div1"></div>
    </template>
    <script>
    export default {
        setup() {
            let dragFun = (val, e) => {
                console.log(val, e)
                // val 是 在binging.value 的值
                // e 是对应的element元素
            }
            return {
                dragFun
            }
        }
    }
    </script>
    

    4、mixin

    • 参数: {Object} mixin
    • 使用:

    在整个应用范围内应用mixin。一旦注册,它们就可以在当前应用程序中任何组件的模板中使用。

    5、mount

    • 参数:
      • {Element | string} rootContainer
      • {boolean} isHydrate (optional)

    挂载到跟组件上

    import { createApp } from 'vue'
    const app = createApp({})
    app.mount('#app')
    

    6、provide

    • 参数:
      • {string | Symbol} key
      • value

    设置一个可以注入到应用程序内组件中的值。组件应使用注入来接收提供的值。

    provide 和 inject 不是反应性的

    import {provide} from 'vue'
    setup() {
        provide('city', '杭州')
        provide('person', {
            name: 'Bob',
            age: 18
        })
    }
    

    7、inject

    • 参数:
      • {string | Symbol} key
      • 没有获取到时的默认值
    import {inject} from 'vue'
    setup() {
        let city = inject('city')
        let person = inject('person')
    	let noProvide = inject('noProvide', 'noProvide hah')
    }
    

    8、unmount

    • 参数: {Element | string} rootContainer

    卸载

    import { createApp } from 'vue'
    const app = createApp({})
    app.mount('#app')
    // 5 秒钟之后卸载
    setTimeout(() => app.unmount('#app'), 5000)
    

    9、use

    • 参数:
      • {Object | Function} plugin
      • …options (optional)

    使用 Vue 其他插件

    //2.x
    Vue.use(router)
    
    //3.x
    import { createApp } from 'vue'
    const app = createApp({})
    app.use(router)
    

    三、Global API(全局API)

    1、createApp

    返回一个组件实例

    const app = Vue.createApp({})
    

    接收根组件选项对象作为第一个参数:

    const app = Vue.createApp({
      data() {
        return {}
      },
      methods: {},
      computed: {}
    })
    

    第二个参数,我们可以将根 props 传递给应用程序

    const app = Vue.createApp(
      {
        props: ['username']
      },
      { username: 'Evan' }
    )
    
    <div id="app">
      {{ username }}
    </div>
    

    2、h

    返回一个返回的“虚拟节点”,通常缩写为VNode。

    render() {
      return Vue.h('h1', {}, 'Some title')
    }
    

    参数

    • tag
      • 类型:String | Object | Function | null
      • 具体值:一个HTML的标签名、组件、异步组件、null;此参数是必须的。
    • props
      • 类型:Object
      • 具体值:模版中将使用到的属性、props或者元素
    • children
      • 类型:String | Array | Object
      • 具体值:子虚拟节点,使用h()返回的值,或者一个插槽
    h('div', {}, [
      'Some text comes first.',
      h('h1', 'A headline'),
      h(MyComponent, {
        someProp: 'foobar'
      })
    ])
    

    3、defineComponent

    参数

    • 类型 一个 component options 的 对象
    import { defineComponent } from 'vue'
    const MyComponent = defineComponent({
      data() {
        return { count: 1 }
      },
      methods: {
        increment() {
          this.count++
        }
      }
    })
    

    4、defineAsyncComponent

    异步组件

    参数

    对于基本用法,defineAsyncComponent可以接受返回Promise的工厂函数。
    import { defineAsyncComponent } from 'vue'
    
    const AsyncComp = defineAsyncComponent(() =>
        import('./components/AsyncComponent.vue')
    )
    
    app.component('async-component', AsyncComp)
    
    使用本地注册时,您还可以直接提供一个返回Promise的函数:
    components: {
        AsyncComponent: defineAsyncComponent(() =>
            import('./components/AsyncComponent.vue')
        )
    }
    
    对于高级用法,defineAsyncComponent可以接受一个对象:
    const AsyncComp = defineAsyncComponent({
      // 工厂函数
      loader: () => import('./Foo.vue')
      // 加载异步组件时要使用的组件
      loadingComponent: LoadingComponent,
      // 加载失败时使用的组件
      errorComponent: ErrorComponent,
      // 显示加载组件之前的延迟。 默认值:200ms。
      delay: 200,
      // 如果提供并超过了超时,则将显示错误组件。 默认值:无穷大
      timeout: 3000,
      // 一个返回布尔值的函数,该值指示加载程序承诺拒绝时异步组件是否应重试
      retryWhen: error => error.code !== 404,
      // 允许的最大重试次数
      maxRetries: 3,
      // 定义组件是否
      suspensible: false
    })
    

    5、resolveComponent

    resolveComponent 只能在 render 或者 setup 中使用

    参数

    一个 component

    import { resolveComponent } from 'vue'
    render() {
      const MyComponent = resolveComponent('MyComponent')
    }
    

    6、resolveDynamicComponent

    resolveDynamicComponent 只能在 render 或者 setup 中使用

    参数

    一个 component

    import { resolveDynamicComponent } from 'vue'
    render () {
      const MyComponent = resolveDynamicComponent('MyComponent')
    }
    

    7、resolveDirective

    resolveDirective 只能在 render 或者 setup 中使用

    参数

    接收一个 指令名称

    const app = Vue.createApp({})
    app.directive('highlight', {})
    
    import { resolveDirective } from 'vue'
    render () {
      const highlightDirective = resolveDirective('highlight')
    }
    

    8、withDirectives

    withDirectives 只能在 render 或者 setup 中使用

    参数

    • 虚拟节点 使用h()创建的虚拟节点
    • 自定义指令 一个自定义指令数组
    import { withDirectives, resolveDirective } from 'vue'
    const foo = resolveDirective('foo')
    const bar = resolveDirective('bar')
    
    return withDirectives(h('div'), [
      [foo, this.x],
      [bar, this.y]
    ])
    
    const MyDirective = resolveDirective('MyDirective')
    const nodeWithDirectives = withDirectives(
      h('div'), 
      [ [MyDirective, 100, 'click', { prevent: true }] ]
    )
    

    9、createRenderer

    自定义渲染平台

    参数

    • HostNode
    • HostElement
    import { createRenderer } from 'vue'
    const { render, createApp } = createRenderer<Node, Element>({
      patchProp,
      ...nodeOps
    })
    

    10、nextTick

    异步更新队列

    import { createApp, nextTick } from 'vue'
    
    const app = createApp({
      setup() {
        const message = ref('Hello!')
        const changeMessage = async newMessage => {
          message.value = newMessage
          await nextTick()
          console.log('Now DOM is updated')
        }
      },
      methods: {
        this.$nextTick(() => {
          // ...
        })
      }
    })
    

    四、Options

    1、Data

    1. data

    类型:Function

    const data = { a: 1 }
    const vm = Vue.createApp({
      data() {
        return data
      }
    }).mount('#app')
    
    //或者
    
    data: vm => ({ a: vm.myProp })
    

    2. props

    类型:Array | Object

    当使用 Object 的时候:

    • type: 传入的类型;[String, Number, Boolean, Array, Object, Date, Function, Symbol]
    • default: 默认值;[any]
    • required: 是否必须;[Boolean]
    • validator: 验证是否满足;[Function] 返回true和false
    props: {
        age: {
            type: Number,
            default: 0,
            required: true,
            validator: value => {
                return value >= 0
            }
        }
    }
    

    3. computed

    计算属性

    类型:{ [key: string]: Function | { get: Function, set: Function } }

    计算的属性的值会被缓存,并且仅在响应性依赖项更改时重新计算。

    computed: {
        aDouble() {
            return this.a * 2
        }
    }
    

    4. methods

    组件方法

    类型:{ [key: string]: Function }

    methods: {
        checkVal () {
            return true
        }
    }
    

    5. watch

    监听

    类型:{ [key: string]: string | Function | Object | Array}

    watch: {
        dataA (val, oldVal) {
            console.log(val, oldVal)
        },
        // 在 methods 里面的方法
        dataB: 'someMethod',
        // 深度监听
        dataC: {
            handler(val, oldVal) {
                console.log('c changed')
            },
            deep: true
        },
        // 立即执行
        dataD: {
            handler(val, oldVal) {
                console.log('d changed')
            },
            immediate: true
        },
        // 传入一个数组
        dataE: [
            'handle1',
            function handle2(val, oldVal) {
                console.log('handle2 triggered')
            },
            {
                handler: function handle3(val, oldVal) {
                    console.log('handle3 triggered')
                }
            }
        ]
    },
    methods: {
        someMethod() {
            console.log('b changed')
        },
        handle1() {
            console.log('handle 1 triggered')
        }
    }
    

    不能使用箭头函数来定义watch

    6. emits

    自组件通过 emit 的事件列表

    类型:Array | Object

    当为对象时可以验证传入的值是否有效

    const app = Vue.createApp({})
    // 当为数组时
    app.component('todo-item', {
      emits: ['check'],
      created() {
        this.$emit('check')
      }
    })
    
    // 当为一个对象时
    app.component('reply-form', {
      emits: {
        click: null,
        submit: payload => {
          if (payload.email && payload.password) {
            return true
          } else {
            console.warn(`Invalid submit event payload!`)
            return false
          }
        }
      }
    })
    

    2、生命周期钩子函数

    1. beforeCreate

    组件实例创建之前,data、event、watcher 不能调用

    2. created

    组件实例创建之后,data、event、watcher可以使用

    $el属性不能调用,不能操作DOM

    3. beforeMount

    组件实例挂载之前立即被调用:render函数将被首次调用

    在服务器端渲染期间不会调用此钩子函数。

    4. mounted

    组件实例挂载之后调用

    mounted不能保证所有子组件也都已安装。 如果要等到整个视图渲染完毕,可以在mount的内部使用vm.$nextTick

    在服务器端渲染期间不会调用此钩子函数。

    5. beforeUpdate

    组件实例更新之前

    在服务器端渲染期间不会调用此钩子函数。

    6. updated

    组件更新之后

    更新并不能保证所有子组件也都已重新呈现。如果要等到整个视图重新渲染后,可以在更新后使用vm.$nextTick

    在服务器端渲染期间不会调用此钩子函数。

    7. activated

    kept-alive 组件激活时的钩子函数

    在服务器端渲染期间不会调用此钩子函数。

    8. deactivated

    kept-alive 组件卸载时的钩子

    在服务器端渲染期间不会调用此钩子函数

    9. beforeUnmount

    卸载之前调用

    在服务器端渲染期间不会调用此钩子函数

    10. unmounted

    卸载之后调用,调用此钩子后,组件实例的所有指令均已解除绑定,所有事件侦听器均已删除,所有子组件实例也已卸载。

    在服务器端渲染期间不会调用此钩子函数

    11. errorCaptured

    类型: (err: Error, instance: Component, info: string) => ?boolean

    当捕获到任何后代组件的错误时调用。挂钩接收三个参数:错误,触发错误的组件实例以及包含有关捕获错误的位置信息的字符串。 挂钩可以返回false,以阻止错误进一步传播。

    • 默认情况下,所有错误仍将发送到全局config.errorHandler(如果已定义)。
    • 如果组件的继承链或父链上存在多个errorCaptured挂钩,则将在同一错误上调用所有它们。
    • 如果errorCaptured挂钩本身抛出错误,则此错误和原始捕获的错误都将发送到全局config.errorHandler。
    • errorCaptured挂钩可以返回false,以防止错误进一步传播。

    12. renderTracked

    虚拟dom 被 tracked 时调用

    renderTracked({ key, target, type }) {}
    

    13. renderTriggered

    触发虚拟DOM重新呈现时调用

    renderTriggered({ key, target, type }) {}
    

    五、示例方法

    1、$watch

    参数

    • {string | Function} source
    • {Function | Object} callback
    • {Object} options (optional)
      • {boolean} deep
      • {boolean} immediate

    $watch返回一个unwatch函数,该函数停止触发监听

    const unwatch = vm.$watch('a', cb)
    // 5秒钟之后停止监听
    setTimeout(() => {
        unwatch()
    }, 5000)
    

    2、$emit

    参数

    • {string} eventName
    • …args (optional)
    <button v-on:click="$emit('give-advice', adviceText)">
        Click but
    </button>
    

    3、$forceUpdate

    强制重新渲染组件实例。它不会影响所有子组件,只会影响实例本身和插入了插槽内容的子组件。

    触发之后 会触发 updated() 钩子函数

    4、$nextTick

    参数

    • {Function} callback (optional)

    在下一个DOM更新周期后执行的回调。

    methods: {
        example() {
            this.$nextTick(() => {})
        }
      }
    

    六、反应性API

    1、Reactivity APIs

    1. reactive

    返回一个对象的反应性拷贝数据

    const person = reactive({
        name: 'Bob',
        age: 18
    })
    

    基于 ES6 的Proxy代理实现

    2. readonly

    返回一个只读的对象,或者只读的ref

    const ref1 = ref(0)
    const read1 = readonly({
    	read: 'only'
    })
    const read2 = readonly(ref1)
    
    read1.read = 'change'
    // ⚠️ reactivity.esm-bundler.js?a1e9:301 Set operation on key "read" failed: target is readonly
    read2.value = 10
    // ⚠️ Set operation on key "value" failed: target is readonly.
    

    3. isProxy

    检查代理对象是由 reactive 还是 readonly 创建的,返回 true 和 false

    const ref1 = ref(0)
    const book = reactive({
    	title: 'this is vue3'
    })
    const read1 = readonly({
    	read: 'only'
    })
    console.log(isProxy(ref1), isProxy(book), isProxy(read1))
    // false true true 
    

    4. isReactive

    检查代理对象是否由 reactive 创建的,返回 true 和 false

    const ref1 = ref(0)
    const book = reactive({
    	title: 'this is vue3'
    })
    const read1 = readonly({
    	read: 'only'
    })
    console.log(isReactive(ref1), isReactive(book), isReactive(read1))
    // false true false
    

    如果一个对象是由 readonly 创建的 reactive 对象,则返回的也是 true

    let react = reactive({
    	name: 'Bob'
    })
    let react1 = readonly(react)
    console.log(isProxy(react1), isReactive(react1))
    // true true 
    

    5. isReadonly

    检查代理对象是不是 readonly,返回 true 和 false

    const ref1 = ref(0)
    const read1 = readonly({
    	read: 'only'
    })
    let react = reactive({
    	name: 'Bob'
    })
    let react1 = readonly(react)
    const ref2 = readonly(ref1)
    console.log(isReadonly(react1), isReadonly(read1), isReadonly(ref2))
    // true true true
    

    6. toRaw

    返回反应式或只读代理的原始原始对象。可用于临时读取而不会产生代理访问/跟踪开销,也可用于写入而不会触发更改。 不建议保留对原始对象的持久引用。 请谨慎使用。

    let obj = {a: 1}
    let reactObj = reactive(obj)
    
    let toRawObj = toRaw(obj)
    
    let toRawObj1 = toRaw(reactObj)
    
    console.log(obj, reactObj, toRawObj, toRawObj1, toRawObj === obj, toRawObj1 === obj, toRawObj === toRawObj1)
    // {a: 1} Proxy {a: 1} {a: 1} {a: 1} true true true
    
    setTimeout(() => {
    	obj.a = 2
    	console.log(obj, reactObj, toRawObj, toRawObj1, toRawObj === obj, toRawObj1 === obj, toRawObj === toRawObj1)
    	// {a: 2} Proxy {a: 2} {a: 2} {a: 2} true true true
    }, 1000)
    
    setTimeout(() => {
    	reactObj.a = 3
    	console.log(obj, reactObj, toRawObj, toRawObj1, toRawObj === obj, toRawObj1 === obj, toRawObj === toRawObj1)
    	// {a: 3} Proxy {a: 3} {a: 3} {a: 3} true true true
    }, 2000)
    

    7. markRaw

    标记一个对象,使其永远不会转换为代理。 返回对象本身。

    let obj = {a: 1}
    let reactObj = reactive(obj)
    
    let toRawObj = markRaw(obj)
    
    let toRawObj1 = markRaw(reactObj)
    
    console.log(obj, reactObj, toRawObj, toRawObj1, toRawObj === obj, toRawObj1 === obj, toRawObj === toRawObj1)
    // {a: 1, __v_skip: true}a: 3__v_skip: true__proto__: Object Proxy {a: 1, __v_skip: true} {a: 1, __v_skip: true} Proxy {a: 1, __v_skip: true} true false false
    
    setTimeout(() => {
    	obj.a = 2
    	console.log(obj, reactObj, toRawObj, toRawObj1, toRawObj === obj, toRawObj1 === obj, toRawObj === toRawObj1)
    	// {a: 2, __v_skip: true}a: 3__v_skip: true__proto__: Object Proxy {a: 2, __v_skip: true} {a: 2, __v_skip: true}a: 3__v_skip: true__proto__: Object Proxy {a: 2, __v_skip: true} true false false
    }, 1000)
    
    setTimeout(() => {
    	reactObj.a = 3
    	console.log(obj, reactObj, toRawObj, toRawObj1, toRawObj === obj, toRawObj1 === obj, toRawObj === toRawObj1)
    	// {a: 3, __v_skip: true} Proxy {a: 3, __v_skip: true} {a: 3, __v_skip: true} Proxy {a: 3, __v_skip: true} true false false
    }, 2000)
    

    8. shallowReactive

    创建一个反应式代理,该反应式代理跟踪其自身属性的反应性,但不执行嵌套对象的深度反应式转换。

    const book = reactive({
    	title: 'this is vue3',
    	address: {
    		city: 'hz',
    		area: 'bj'
    	}
    })
    console.log(isReactive(book.address))
    // true
    const obj = shallowReactive({
    	name: 'Bob',
    	age: 18,
    	address: {
    		city: 'hz',
    		area: 'bj'
    	}
    })
    console.log(isReactive(obj.address))
    // false
    

    9. shallowReadonly

    创建一个代理,使其自身的属性为只读,但不执行嵌套对象的深度只读转换。

    const book = readonly({
    	title: 'this is vue3',
    	address: {
    		city: 'hz',
    		area: 'bj'
    	}
    })
    console.log(isReadonly(book.address))
    // true
    const obj = shallowReadonly({
    	name: 'Bob',
    	age: 18,
    	address: {
    		city: 'hz',
    		area: 'bj'
    	}
    })
    console.log(isReadonly(obj.address))
    // false
    

    2、Refs

    1. ref

    接受一个值并返回一个反应性且可变的ref对象。ref对象具有指向内部值的单个属性.value。

    const ref1 = ref(0)
    
    __v_isRef: true
    _rawValue: 0
    _shallow: false
    _value: 0
    value: 0
    
    const ref2 = ref({
    	name: 'Bob',
    	age: 18
    })
    
    __v_isRef: true
    _rawValue: {name: "Alice", age: 18}
    _shallow: false
    _value: Proxy {name: "Alice", age: 18}
    value: Proxy
    [[Handler]]: Object
    [[Target]]: Object
    age: 18
    name: "Alice"
    __proto__: Object
    [[IsRevoked]]: false
    

    一般使用 ref 定义基本类型

    2. unref

    如果参数是 ref 则返回内部的 value 值,否则返回 参数本身,是 val = isRef(val) ? val.value : val 的语法糖

    const ref1 = ref(0)
    const ref2 = ref({
    	name: 'Bob',
    	age: 18
    })
    const obj = reactive({
    	name: 'Alice',
    	age: 20
    })
    console.log(unref(ref1), unref(ref2), unref(obj))
    // 0
    // Proxy
    // Proxy
    

    3. toRef

    创建一个 reactive 的对象的属性 做为 ref 的引用,保持反应性连接

    const obj = reactive({
    	name: 'Alice',
    	age: 20
    })
    
    const toR1 = toRef(obj, 'name')
    const notoR2 = obj.name
    
    setTimeout(() => {
    	obj.name = 'Jack'
    }, 1000)
    
    // 1000 毫秒之后 toR1.value 值为 Jack, notoR2 的值为 Alice
    

    4. toRefs

    将反应对象转换为普通对象,其中所得对象的每个属性都是指向原始对象相应属性的ref。

    const obj = reactive({
    	name: 'Alice',
    	age: 20
    })
    
    const refs1 = toRefs(obj)
    console.log(refs1)
    {name: ObjectRefImpl, age: ObjectRefImpl}
        age: ObjectRefImpl
            __v_isRef: true
            _key: "age"
            _object: Proxy {name: "Jack", age: 20}
            value: 20
            __proto__: Object
        name: ObjectRefImpl
            __v_isRef: true
            _key: "name"
            _object: Proxy {name: "Jack", age: 20}
            value: "Jack"
            __proto__: Object
    
    setTimeout(() => {
    	obj.name = 'Jack'
    }, 1000)
    // 1000 毫秒之后 refs1.name.value 的值也变为 Jack
    

    5. isRef

    检查是否是 ref 对象

    const ref1 = ref(0)
    const ref2 = ref({
    	name: 'Bob',
    	age: 18
    })
    const obj = reactive({
    	name: 'Alice',
    	age: 20
    })
    
    console.log(isRef(ref1), isRef(ref2), isRef(obj))
    // true true false
    

    6. customRef

    <template>
    	<div class="my-com2">
    		<input v-model="text" />
    	</div>
    </template>
    
    <script>
    import {customRef} from 'vue'
    
    	function useDebouncedRef(value, delay = 200) {
    		let timeout
    		return customRef((track, trigger) => {
    			return {
    				get() {
    					console.log(value)
    					track()
    					return value
    				},
    				set(newValue) {
    					console.log(newValue)
    					clearTimeout(timeout)
    					timeout = setTimeout(() => {
    						value = newValue
    						trigger()
    					}, delay)
    				}
    			}
    		})
    	}
    	export default{
    		setup () {
    			return {
    				text: useDebouncedRef('hello')
    			}
    		}
    	}
    </script>
    
    <style scoped>
    	
    </style>
    

    7. shallowRef

    const ref1 = ref(0)
    const ref2 = ref({
    	name: 'Bob',
    	age: 18
    })
    const obj = reactive({
    	name: 'Alice',
    	age: 20
    })
    
    const a1 = shallowRef(ref1)
    const a2 = shallowRef(ref2)
    const a3 = shallowRef(obj)
    
    console.log(isReactive(a1), isReactive(a2), isReactive(a3))
    // false false false
    

    8. triggerRef

    const s1 = shallowRef({
    	name: 'Bob'
    })
    s1.value.name = 'Alice'
    triggerRef(s1)
    console.log(s1.value.name)
    // Alice
    
    展开全文
  • 解决Vue常见的数据渲染或缓存问题

    千次阅读 2019-07-07 23:56:18
    一、vue路由加载页面时,数据返回慢的问题 vue路由加载页面时,数据返回慢的时候页面会有闪动的效果,数据加载前和加载后的区别。(特别是el-table表格数据) 解决方案: 使用vue-router的路由守卫...
  • Vue + Axios + Nginx 实现调用企业微信API详细过程一、思路2.读入数据二、思路2.读入数据总结 一、思路 先捋清楚整体的步骤: 1、首先需要从企业微信管理后台拿到企业ID和应用的Secret 2.读入数据 代码如下(示例)...
  • vue项目实现缓存的最佳方案

    万次阅读 2019-07-11 13:13:14
    在开发vue的项目中有遇到了这样一个需求:一个视频列表页面,展示视频名称和是否收藏,点击进去某一项观看,可以收藏或者取消收藏,返回的时候需要记住列表页面的页码等状态,同时这条视频的收藏状态也需要更新, ...
  • vue里提供了keep-alive组件用来缓存状态。 可以用以下几种方案解决问题; 一、利用meta标签 1、首先在路由中的meta标签中记录keepAlive的属性为true path: '/classify', name: 'classify', component: () => ...
  • vue-router 常用API

    千次阅读 2018-03-28 17:18:54
    一.Router 实例---方法1.增加全局的导航守卫 参考导航守卫。 router.beforeEach(guard) router.beforeResolve(guard) (2.5.0+...2.动态的导航到一个新 URL。参考编程式导航。 router.push(location, onComplete?,...
  • vue面试六一 http缓存机制(强缓存、协商缓存)二 Webpack构建流程三 WebpackDevServer代理APIVue 常用修饰符(v-on v-bind v-model) 一 http缓存机制(强缓存、协商缓存) 背景:从输⼊URL到⻚⾯加载完中间发⽣了...
  • 防止用户过渡刷新或切换界面,不断的请求后端接口,所以在前端对用户拿到的部分数据进行缓存。 目录结构 WebSql.js 创建数据库表,储存key,value,过期时间 import Vue from 'vue' import {executeSql} from '@...
  • Vue的Option API

    2021-06-14 14:41:02
    Vue的Option API方法 methodsmethods的基本使用methods方法绑定this计算属性 computedcomputed的基本使用computed的 setter 和 gettercomputed VS methods侦听器Watch的使用基本使用侦听器的配置选项侦听器watch的...
  • 关于vue store 全局缓存的问题

    千次阅读 2019-09-09 11:02:57
    state :对数据的全局存储 getter: 可以理解为computed ,对数据进行计算 mutations :对数据的同步更改 actions:对数据的异步更改(实现异步操作) module: 将 store 分割成模块 state,getter,mutations,...
  • 插槽是Vue实现的一套内容分发的API,将<slot></slot>元素作为承载分发内容的出口。插槽就是子组件提供的可替换模板,父组件可以根据需求改变模板的内容。也就是说,没有插槽的情况下,在组件标签内写...
  • 尤雨溪:Vue Function-based API RFC

    千次阅读 2019-06-12 09:06:57
    2.x 中与组件逻辑相关的选项以 API 函数的形式重新设计。 基本例子 import { value, computed, watch, onMounted } from 'vue' const App = { template: ` <div> <span>cou...
  • 详谈Vue缓存方法

    千次阅读 2021-03-11 17:08:11
    最近新做了个需求“前端缓存” ...也称会话缓存,当用户关闭浏览器窗口后,数据就会被删除; sessionStorage.setItem("key","value");//存储 sessionStorage.getItems("key");//按可以进行取值 sessionStorage.re
  • 学习内容:Vue+Vuex+Router+ElementUi+Webpack全家桶,VUE3.0体验版API,组件化开发,生命周期,路由权限,Sass,Axios拦截器,缓存,项目部署,Nginx,域名,服务器,GIT,原型,接口联调,性能,缓存VUE学习:...
  • vue的set api

    2020-10-17 21:57:10
    vue2.x里面数组与对象的属性变化 没办法 进行响应式 所以推出了 this.$set(obj,key,value)方法或者this.set(arr,index,value) 这样可以注册到data里面的响应式对象,来触发改变 watcher 观察者 是每一个组件 特有...
  • 前两天做个刷新二维码的东西,发现url用的是缓存数据,即使url调用的二维码图片内容已经变化,但是vue这边还是用的缓存,必须手动刷新,用下面的方法解决: data() { return { qrcode_url:"/api/qr_code", //我...
  • vue api

    千次阅读 2018-05-21 17:06:40
    1.全局配置Vue.config时一个对象有以下属性: ··· Vue.config.solent = true; 取消Vue所有的日志和警告 ··· Vue.config.optionMergeStrategies._my_option = function (parent, child, vm) { return child...
  • 缓存 虽然 Vue 的服务器端渲染(SSR)相当快速,但是由于创建组件实例和虚拟 DOM 节点的开销,无法与纯基于字符串拼接...vue服务区缓存分为页面缓存、组建缓存和接口缓存 页面缓存:  在server.js中设置 ...
  • 这三类页面都共享一个完整的数据model,从上级页面进入下一级页面时,能够加载相应数据;回到上一级时,数据有更新。举个栗子,从故事页点击“编辑”按钮,进入故事编辑页则默认填充点击的“编辑”按钮所对应的故事...
  • :high_voltage: Vue 3 Composition API用于数据获取,支持SWR,轮询,错误重试,缓存请求,分页等。 特征 :rocket: 所有数据都是React性的 :counterclockwise_arrows_button: 间隔轮询 :robot: 自动错误重试 :...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,572
精华内容 7,828
关键字:

vue2数据缓存的api

vue 订阅