精华内容
下载资源
问答
  • 学习Vue3.x方便c Vue 3 新增内容的整理文档(经过验证的) 重点内容: ViteComposition API新的响应式机制计算属性和侦听器teleport (传送)Fragment(碎片)自定义事件($emit 验证)组件的 v-model 升级 ...

    本博客会长期更新(在 Vue 的中文官方文档没有发布前)。

    博客的边界:本博客只会带来 Vue 3 新增内容的整理文档(经过验证的)


    重点内容:

    • Vite
    • Composition API
    • 新的响应式机制
    • 计算属性和侦听器
    • teleport (传送)
    • Fragment(碎片)
    • 自定义事件($emit 验证)
    • 组件的 v-model 升级

    利用 vite 创建下一代的 Vue 项目

    两个重要的点:

    1. Vue CLI v4.5 之上的版本
    2. Vite

    Vue CLI

    想要构建 Vue 3.x 项目,那么需要使用 Vue CLI v4.5 之上的版本

    yarn global add @vue/cli@next
    # or
    npm install -g @vue/cli@next
    
    • 1
    • 2
    • 3

    如果想要在现有的项目中(Vue 2.x),升级构建工具,则需要执行以下代码

    vue upgrade --next
    
    • 1

    Vite

    Vite 是一个想要用来代替 webpackWeb 开发构建工具,也是官方推荐使用的构建工具,使用 vite 将脱离 webpack

    // ----------npm----------
    npm init vite-app <project-name>
    cd <project-name>
    npm install
    npm run dev
    // ----------yarn----------
    yarn create vite-app <project-name>
    cd <project-name>
    yarn
    yarn dev
    

    Composition API

    简介

    为什么会有 Composition API

    创建Vue组件使我们能够将接口的可重复部分及其功能提取到可重用的代码段中。就维护性和灵活性而言,仅此一项就可以使我们的应用程序发展得相当远。

    但是,经过大量的开发经验证明,仅凭这一项可能还不够,特别是当你的应用程序变得非常大时。

    想象一下数百个组件的项目,在处理如此大的应用程序时,共享和重用代码变得尤为重要。

    摘要自:https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api

    但是因为 Vue 2.x 中,以 组件 为最小颗粒的原因(PS:mixin 虽然可以达到复用的效果,但是它依然缺少独立性。比如:命名冲突、隐式依赖的问题),导致组件中的逻辑无法被进行 共享重用

    Composition API 概念

    Composition API组合式 API) 是一个概念,而非具体的实现。

    在组件中,实际使用 Composition API 的地方,是 setup 函数。在它内部,我们可以描述一个组件所需要的 所有逻辑

    setup

    简介

    setup 函数是Composition API 的入口函数,也是它的核心函数。

    但是要注意:setup 函数执行时,尚未创建组件实例,所以 this 中没有任何内容。这意味着,除了props,你将无法访问通过 this 来访问组件中声明的任何属性、本地状态、计算属性、方法。

    参数

    setup 函数有两个参数:

    1. {Data} props
      1. props 是响应式的
      2. 由于props是反应性的,你不能使用ES6解构,详见 对象的响应式
    2. {SetupContext} context
      1. context 是普通的 js 对象,所以他不是响应式的
      2. context 下有三个属性:
        1. attrs:包含父组件属性绑定、未识别出的为组件属性或自定义事件的事件
        2. slots:插槽
        3. emit:通知(发出的事件)

    下面是官方的示例代码:

    // Data 为  key:value 结构的对象
    interface Data {
      [key: string]: unknown
    }
    // context 为上下文,包含:
    // attrs:包含父组件属性绑定、未识别出的为组件属性或自定义事件的事件
    // slots:插槽
    // emit:通知
    interface SetupContext {
      attrs: Data
      slots: Slots
      emit: (event: string, ...args: unknown[]) => void
    }
    function setup(props: Data, context: SetupContext): Data
    

    可访问的组件属性

    setup被执行时,该组件实例尚未建立。如此一来,您将只能访问以下属性:

    • props
    • attrs
    • slots
    • emit

    换句话说,您将无权访问以下组件选项:

    • data
    • computed
    • methods

    模板使用 setup 中的数据

    如果setup返回对象,则可以在组件的模板中访问对象的属性(注意代码中的备注):

    <!-- MyBook.vue -->
    <template>
      <div>{{ readersNumber }} {{ book.title }}</div>
    </template>
    <script>
      import { ref, reactive } from 'vue'
      export default {
        setup() {
          // 通过 ref 或 reactive 创建响应式数据,现在你可以先不需要理解什么是 ref 和 reactive。
          // 你只需要知道此时 readersNumber 和 book 为响应式的
          const readersNumber = ref(0)
          const book = reactive({ title: 'Vue 3 Guide' })
    	 // return 的对象,可以直接在 模板中直接访问(不需要 .value)
          return {
            readersNumber,
            book
          }
        }
      }
    </script>
    

    渲染功能的实现

    setup 还可以返回一个渲染函数,该函数可以直接使用在同一作用域中(setup 函数中)声明的反应状态 :

    import { h, ref, reactive } from 'vue'
    export default {
      setup() {
        const readersNumber = ref(0)
        const book = reactive({ title: 'Vue 3 Guide' })
        // Please note that we need to explicitly expose ref value here
        return () => h('div', [readersNumber.value, book.title])
      }
    }
    

    this 指向问题

    setup 函数中,thisundefined 而不是 组件的引用。因为 setup 函数执行时,尚未创建组件实例。所以 thissetup 中无法正常使用。

    setup 函数和其他的 Options API 一起使用时,可能会出现无法预知的错误,所以一定要谨慎。

    生命周期钩子

    setup 中,可以通过 onX 的方式注册 生命周期钩子

    import { onMounted, onUpdated, onUnmounted } from 'vue'
    const MyComponent = {
      setup() {
        onMounted(() => {
          console.log('mounted!')
        })
        onUpdated(() => {
          console.log('updated!')
        })
        onUnmounted(() => {
          console.log('unmounted!')
        })
      }
    }
    

    Options API(Vue 2.x) 生命周期选项和 Composition API(Vue 3.x) 之间的映射

    • beforeCreate ->使用 setup()
    • created ->使用 setup()
    • beforeMount -> onBeforeMount
    • mounted -> onMounted
    • beforeUpdate -> onBeforeUpdate
    • updated -> onUpdated
    • beforeUnmount -> onBeforeUnmount
    • unmounted -> onUnmounted
    • errorCaptured -> onErrorCaptured
    • renderTracked -> onRenderTracked
    • renderTriggered -> onRenderTriggered

    提供 provide / 注入 inject

    首先必须要明确:提供 provide / 注入 inject 的功能都只能在 setup 函数中进行使用。

    如果你希望在 父组件 中提供一个值,然后在 子组件 中可以使用。那么你就应该考虑 提供 provide / 注入 inject 的功能。

    // 父组件
    setup() {
        // 为子组件提供一个值,并且这个值是响应式的(ref)
        // 则代表:子组件可以修改该值(book.value),并且会 响应式的 作用于父组件的变量中
        let book = ref('父组件的book');
        // 为子组件提供一个值,并且这个值不是响应式的
        // 则代表:子组件获取到了 非响应式的 数据。
        // 此时:子组件可以通过 ref(inject('book', '默认值')) 的方式,把该数据变为 响应式的 ,但是要注意,此时的响应式仅在 子组件中生效
        let book = '父组件的book';
        provide('book', book);
        return {
            book,
        };
    },
    // 子组件
    setup () {
        // 利用 inject 函数,获取父组件提供的值
        let book = inject('book', '默认值');
        // 利用 inject 函数,获取父组件提供的值,同时把它变为响应式的。但是要注意,此时的响应式仅在 子组件中生效
        let book = ref(inject('book', '默认值'))
    }
    

    新的响应式

    代理对象

    当我们把 JavaScript 对象作为 data 选项传递给 Vue 实例的时候,Vue 会遍历对象的所有属性,并且会把它们转化成带有 gettersetter 函数的 Proxies(代理对象)

    同时为了兼顾低版本的浏览器,对于较久的版本,Vue 依然使用 Object.defineProperty 来进行支持。

    两者之间在使用中并没有区别,只不过 代理对象 会提供更好的性能。

    响应式数据的声明 - reactive 函数

    想要在 setup函数中 创建响应式数据,可以使用 reactive方法(注意:需要主动导入 reactive

    <template>
      <div>
      	<h1>Vue 3</h1>
      	<button @click="state.count++">count is: {{ state.count }}</button>  
      </div>
    </template>
    <script>
    import { reactive } from 'vue';
    export default {
      name: 'HelloWorld',
      setup() {
         // 创建的响应式数据对象
        const state = reactive({
          count: 0,
        });
        return {
          // 直接放回创建的响应式对象
          state,
        };
      },
    };
    </script>
    

    注意:Vue 3 中并没有取消 data 声明,也就是说,我们依然可以使用 data 函数来创建响应式数据。以下两种写法在响应式效果 中等效

    // setup() {
      //   const state = reactive({
      //     count: 0,
      //   });
      //   return {
      //     state,
      //   };
      // },
      // 效果等效
      data() {
        return {
          state: {
            count: 0,
          },
        };
      },
    

    将一个非响应式数据转化为响应式数据 - ref 函数

    Vue 3 对 ref 函数进行了改变,使 ref 具备了两种能力:

    1. 使用 ref 获取 元素 或 组件实例
    2. 可以把一个非响应式的数据改变为响应式的

    我们在这里探讨的就是 ref 函数的第二个能力:

    <template>
      <h1>Vue 3</h1>
      <button @click="count++">count is: {{ count }}</button>
    </template>
    <script>
    import { ref } from 'vue';
    export default {
      setup() {
        // ----- 利用 setup 函数返回 ------
        let count = 0;
         return {
          count,
        };
        // ----- 利用 reactive 函数 ------
        const state = reactive({
          count: 0,
        });
        return {
          // count 值的改变,将不会影响视图的变化,原因请查看 《对象的响应式》  
          count: state.count,
        };
        // ----- 利用 ref 函数 ------
        let count = ref(0);
        return {
          count,
        };
      }
    };
    </script>
    

    以上三种方式,貌似可以达到同样的效果。

    那么 ref 的作用是什么呢?再来回顾一下:ref 函数可以将一个非响应式数据转化为响应式数据。即:当数据发生了变化的时候,视图也理应发生变化。我们来看下面的代码:

    setup() {
        // ----- 利用 setup 函数返回 ------
        let count = 0;
        setTimeout(() => {
        // 视图并没有发生变化
          count = 1;
        }, 1000);
        return {
          count,
        };
        // ----- 利用 reactive 函数 ------
        et state = reactive({
          count: 0,
        });
        setTimeout(() => {
        // 视图并没有发生变化
           state.count = 1;
        }, 1000);
        return {
          count: state.count,
        };
        // ----- 利用 ref 函数 ------
        let count = ref(0);
        setTimeout(() => {
          // 访问 count 的值,需要使用 value 属性
          // 注意:在模板中,我们不需要手动写入 value
          // 视图发生了变化
          count.value = 1;
        }, 1000);
        return {
          count,
        };
      },
    

    在上面的代码中,我们通过 setTimeout 延迟 1 秒钟之后改变数据源 count 的值,从结果中,我们可以发现,只有被 ref 函数声明的 count 才会直接影响视图的变化。

    对象的响应式

    通常情况下,我们希望利用 解构赋值 的方式来获取到响应式数据:

    <template>
      <h1>Vue 3</h1>
      <button @click="count++">count is: {{ count }}</button>
    </template>
    <script>
    import { reactive } from 'vue';
    export default {
      setup() {
        // ----- 利用 reactive 函数 ------
        let state = reactive({
          count: 0,
        });
        let { count } = state;
        setInterval(() => {
          // 视图并没有发生变化
          count++;
        }, 1000);
        return {
          count,
        };
      },
    };
    </script>
    

    但是很遗憾,这种方式将会让 count 失去响应性

    如果我们想要解决这个问题,那么需要借助一个函数 toRefs。同样的,我们需要先 导入 这个函数。

    // 不要忘记,我们需要导入 toRefs
    import { toRefs } from 'vue';
    let state = reactive({
        count: 0,
    });
    // 借助 toRefs 函数,让数据保持响应性
    let { count } = toRefs(state);
    setInterval(() => {
        // 同样需要是使用 value 来访问到数据源
        count.value++;
    }, 1000);
    return {
        count,
    };
    

    只读的响应式数据

    有时我们希望 数据为响应式 的同时,该数据不被其他的组件所修改 (即:我们想要其他组件 享受 响应数据(refreactive )的变化,同时也想要防止其他的组件修改响应式数据),那么我们需要给其他组件 只读的响应式数据 - readonly

    <!-- state.count 发生改变,readonlyState.count 会同步跟随变化 -->
    <button @click="state.count++">count is: {{ state.count }}</button> 
    <!-- 如果想要直接修改 readonlyState.count 的值,那么会得到一个警告:Set operation on key "count" failed: target is readonly -->
    <button @click="readonlyState.count++">count is: {{ readonlyState.count }}</button>
    <script>
    import { reactive, readonly } from 'vue';
    export default {
    setup() {
          // 响应式数据对象 state
          const state = reactive({
            count: 0
          })
          // 只读数据 readonlyState
          // state 称为 readonlyState 源数据
          const readonlyState = readonly(state)
          return {
            state,
            readonlyState
          }
        }
    </script>
    

    这样我们就获得了一个只读的响应式数据 readonlyState 和它的源数据 state 。如果我们想要修改 readonlyState 的值,那么唯一的方式是 修改state

    对于 readonly 来说,源数据的变化会响应式的作用于 readonly

    计算属性与侦听器

    计算属性

    基本用法

    Vue 计划使用 computed 函数 来定义 计算属性 (这并不代表 Options API 被完全丢弃),看下面的示例:

    <template>
      <input type="text" v-model="str" />
      <h2>{{ comStr }}</h2>
    </template>
    <script>
    import { ref, computed } from 'vue';
    export default {
      setup() {
        const str = ref('Hello');
        // Vue 3 中推荐写法,完全等效于 computed 属性中函数定义
        const comStr = computed(() => str.value.split('').reverse().join(''));
        return {
          str,
          comStr,
        };
      },
      /*  Vue 2.x 的写法并没有被废弃,只是不再被推荐使用。
      以下写法完全等效于 Vue 3 中的 computed 函数 */
      computed: {
        comStr() {
          return this.str.split('').reverse().join('');
        },
      },
    };
    </script>
    

    set 和 get

    computed 函数 包含了 getset 方法:

    const count = ref(1)
    // 此代码等于使用了 getter 函数
    const plusOne = computed(() => count.value++)
    // 当我们获取 plusOne 的值时,等于调用了 get
    console.log(plusOne.value) // 2
    // 但是因为没有实现 setter 函数,所以无法设置新值
    // err:setter is not a function
    plusOne.value++ 
    

    我们可以使用具有 getset 功能的对象来创建可写的 ref 对象

    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
    

    此时对 plusOneset 操作会回调到 传入对象的 set 方法中。

    监听函数:watchEffect

    如果你想要监听响应式数据的变化,那么可以使用 watchEffect 函数

    watchEffect 函数 的运行时机有两个:

    1. 调用函数时,立刻运行
    2. 依赖项的数据发生变化时,会立刻运行
    const count = ref(0);
    // 立刻打印:0
    watchEffect((v) => {
        // 依赖数据发生变化时,依次打印 1.2.3.4....
        console.log(count.value);
    });
    setInterval(() => {
        count.value++;
    }, 1000);
    return {
        count,
    };
    

    如果你想要停止 watcher 的话,那么可以返回方法进行停止

    const stop = watchEffect(() => {
      /* ... */
    })
    // 调用返回的停止函数
    stop()
    

    watch

    watch API完全等同于组件 watch 属性。

    watch需要查看特定的数据源,并在单独的回调函数中应用副作用。

    默认情况下,它也是惰性的。即仅在监视的源已更改时才调用回调。

    参数:

    • {string | Function} source
    • {Function | Object} callback
    • {Object} [options]
      • {boolean} deep
      • {boolean} immediate

    监听单一的数据源变化

    watcher 的数据源可以有两种:

    1. 返回值的 getter 函数
    2. ref
    // 监听 返回值的 getter 函数
    const state = reactive({ count: 0 })
    watch(
      () => state.count,
      (count, prevCount) => {
        /* ... */
      }
    )
    // 监听 ref
    const count = ref(0)
    watch(count, (count, prevCount) => {
      /* ... */
    })
    

    监听多个数据源的变化

    使用数组的方式可以监听多个数据源

    watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
      /* ... */
    })
    

    options 参数对象

    options 参数对象主要有两个属性:

    1. deep:监听对象内部嵌套值的改变。注意:你不需要制定 deep 既可以监听到 Array 的变化
    2. immediate:立刻触发回调

    teleport (传送)

    在 Vue 的技术体系中,我们一直以来都是根元素中做事情(<div id="app" ></div>),这个逻辑是没有问题的。

    但是在 Vue 3 中提出了一个新的概念 teleport

    它是一个组件,使用它可以让我们的渲染区 脱离 根元素。这也是 传送 所代表的意思:将视图传送出渲染区(同时视图将保持响应式的渲染)

    <body>
      <!-- 渲染区 -->
      <div id="app">
      </div>
      <!-- 
        转移区。
        注意:目标元素必须在组件挂在前就已经存在。
        即目标不能由组件本身渲染,并且理想情况下应位于整个Vue组件树之外。 
      -->
      <div id="endofbody"></div>
    </body>
    <script>
      const app = Vue.createApp({
        template: `
        <h1>根组件</h1>
        <parent-component />
      `
      })
      app.component('parent-component', {
        template: `
        <h2>父组件</h2>
        <teleport to="#endofbody">
          <child-component :name="name" />
        </teleport>
      `,
        data() {
          return {
            name: '张三'
          }
        },
        created: function () {
          setTimeout(() => {
            this.name = '李四'
          }, 1000)
        }
      })
      app.component('child-component', {
        props: ['name'],
        template: `
        <h4>被转移的子组件,它将出现在 app 之外</h4>
        <h4>同时享受响应式数据改变带来的视图变化,不信?你刷新页面,等一秒钟看一下</h4>
        <div>Hello, {{ name }}</div>
      `
      })
      app.mount('#app')
    </script>
    

    并且,你需要知道,传送是可以支持多个的。即:可以存在多个 teleport 组件指向同一个 目标区域

    <teleport to="#modals">
      <div>A</div>
    </teleport>
    <teleport to="#modals">
      <div>B</div>
    </teleport>
    <!-- 结果-->
    <div id="modals">
      <div>A</div>
      <div>B</div>
    </div>
    

    Fragment

    组件不再强制单个根元素,这个特性 Vue 3 称为 Fragment(碎片)

    以下代码可以正常运行(虽然会有 ESLint 警告)

    <template>
      <header>...</header>
      <main v-bind="$attrs">...</main>
      <footer>...</footer>
    </template>
    

    自定义事件

    Vue 3 建议对组件中所有发出的事件 emit 进行记录 emits 。如果你声明了 emits 选项,并且发送的事件没有被 emits 记录,则会收到一个警告:

    app.component('custom-form', {
        template: `
          <form>
    		<!-- 此处会收到警告:
        [Vue warn]: Component emitted event "submit" but it is neither declared in the emits option nor as an "onSubmit" prop 
    -->
            <input type="button" value="submi" @click="$emit('submit')"/>
          </form>
        `,
        emits: {
        }
      })
    

    记录的形式分为两种:

    1. 数组:如果使用数组进行记录,那么可以只可以进行声明,而不可以进行验证(参考 prop 机制)
    2. 对象:对象语法可以对 发送的事件 进行验证

    以下为对象验证:

      app.component('custom-form', {
        template: `
          <form>
            <input type="button" value="submi" @click="$emit('submit', {email: 'aa@163.com', password: 'aaa'})"/>
          </form>
        `,
        emits: {
          // 无验证
          click: null,
          // 为 submit 事件添加验证规则
          // 如果不符合验证规则,你会收到一个警告
          submit: ({ email, password }) => {
            if (email && password) {
              console.log('验证通过');
              return true
            } else {
              console.warn('这是一个无效的提交事件!')
              return false
            }
          }
        }
      })
    

    组件中的 v-model

    Vue2.2.0 的版本中新增了 自定义组件的 v-model2.3.0 中新增了 .sync 修饰符。 在功能上 sync 更像是 自定义组件的 v-model 的增强。

    Vue 3 中,自定义组件的 v-model 进行了进一步的增强,使其更像是 v-bind:foo.sync 的另一种写法:

    // 子组件声明
    app.component('my-component', {
      props: {
        foo: String
      },
      template: `
        <input 
          type="text"
          :value="foo"
          @input="$emit('update:foo', $event.target.value)">
      `
    })
    // 父组件调用
    <my-component v-model:foo="bar"></my-component>
    

    同时,自定义组件的 v-model 支持了 多重绑定

    // 子组件声明
    app.component('user-name', {
      props: {
        firstName: String,
        lastName: String
      },
      template: `
        <input 
          type="text"
          :value="firstName"
          @input="$emit('update:firstName', $event.target.value)">
        <input
          type="text"
          :value="lastName"
          @input="$emit('update:lastName', $event.target.value)">
    })
    // 父组件调用
    <user-name
      v-model:first-name="firstName"
      v-model:last-name="lastName"
    ></user-name>
    
    展开全文
  • element_vue3.0 / VE-Admin 项目简介 基于vue3和element-plus开发的企业后台管理模板。 功能特性 项目使用了最新的vue3全家桶+元件加+ mockjs + Axios公司+ eChart5。项目继成了mockServe,可脱离后端自主开发测试...
  • 画画的baby~ 今天又是划水的一天~不过遇到一个新的功能开发,记录一下 ... 使用vue2.0是可以放在static目录下,使用vue3.0时可放在public目录下(注意: 模板文档最好存放在此位置,不然可能会报路径错误等问题) 注意: 模

    画画的baby~
    在这里插入图片描述
    今天又是划水的一天~不过遇到一个新的功能开发,记录一下
    功能需求: 需要根据点击的单位数据,下载一个记录该单位数据的登记证word文档的下载功能~

    1. 安装docxtemplater等需要的包

    npm i docxtemplater pizzip file-saver --save

    1. 创建需要的模板docx文档; 使用vue2.0是可以放在static目录下,使用vue3.0时可放在public目录下(注意: 模板文档最好存放在此位置,不然可能会报路径错误等问题)
      注意: 模板中如果需要使用到遍历时,使用 {#数组名称}{/数组名称} 包裹即可
      在这里插入图片描述
      在这里插入图片描述

    2. 在需要的模块中引入使用

    <template>
      <div>
        <button @click="generate">导出</button>
      </div>
    </template>
    
    <script>
      import 'docxtemplater/build/docxtemplater.js'
      import 'pizzip/dist/pizzip.js'
      import 'pizzip/dist/pizzip-utils.js'
      import 'file-saver'
    
      export default {
        data() {
          return {
            wordData: {
              name: '导出word',
              nameList: [{
                  name: "张三",
                  age: 16,
                  hobby: ['吃饭', '睡觉', '打豆豆']
                },
                {
                  name: "李四",
                  age: 19,
                  hobby: ['抽烟', '喝酒', '打麻将']
                },
              ]
    
    
            },
          }
        },
    
        methods: {
          // 导出word
          loadFile(url, callback) {
            PizZipUtils.getBinaryContent(url, callback);
          },
          generate() {
            var that = this;
            this.loadFile("/word.docx", function (error, content) {  //url模板存放的位置
              if (error) {
                throw error
              };
              var zip = new PizZip(content);
              var doc = new window.docxtemplater().loadZip(zip)
              doc.setData({
                ...that.wordData    //导入到模板文档的数据对象
              });
              try {
                // render the document (replace all occurences of {first_name} by John, {last_name} by Doe, ...)
                doc.render()
              } catch (error) {
                var e = {
                  message: error.message,
                  name: error.name,
                  stack: error.stack,
                  properties: error.properties,
                }
                console.log(JSON.stringify({
                  error: e
                }));
                // The error thrown here contains additional information when logged with JSON.stringify (it contains a property object).
                throw error;
              }
              var out = doc.getZip().generate({
                type: "blob",
                mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
              }) //Output the document using Data-URI
              saveAs(out, "output.docx")
            })
          },
    
        }
      }
    
    </script>
    
    <style scoped>
    
    </style>
    

    好啦,这样就完成了啦啦啦~

    可能出现的问题报错:
    在这里插入图片描述
    出现此问题是因为模板url写的不对,找不到模板文档~
    这里需要将文档存放在static(Vue2.0)或者public(Vue3.0)下,然后使用模板文档的url写成 “/模板文件名.docx” 即可
    在这里插入图片描述
    参考博客: https://blog.csdn.net/weixin_43753330/article/details/103461870

    展开全文
  • 开发要点-Vue3.0通用知识学习 2020-09-18 发布的Vue3.0正式版 建议先学ts;因为Vue3.0框架多数都是用ts重写的; API文档仔细阅读; Vue3亮点: Performance:性能更快; Tree shaking support:按需编译,体积更...

    开发要点-Vue3.0通用知识学习

    2020-09-18 发布的Vue3.0正式版

    建议先学ts;因为Vue3.0框架多数都是用ts重写的;
    API文档仔细阅读;

    Vue3亮点:

    • Performance:性能更快;
    • Tree shaking support:按需编译,体积更小;
    • Composition API:组合API(类似React Hooks);
    • Better TypeScript support:更好的ts支持;
    • Custom Renderer API:暴露了自定义渲染API;
    • Fragment Teleport(Protal) Suspense:更先进的组件;

    Vue3是如何变快的:

    • diff优化:
      • Vue2中虚拟Dom是进行全量对比;
      • Vue3新增静态标记(PatchFlag),虚拟dom比对时,只对比带有patch flag的节点,而且可以通过flag的信息得知当前接待你要对比的具体内容;
    • hoistStatic 静态提升:
      • Vue2中无论元素是否参与更新,每次都会重新创建;
      • Vue3中对不参与更新的元素,只会创建一次,之后会在每次渲染时候被不停的复用;
    • cacheHandlers 事件侦听器缓存
      • 默认情况下onClick会被视为动态绑定(静态标记是8),所以每次都回去追踪它的变化;使用缓存后,就不会进行静态标记,也就不会再追踪了,因为是同一函数缓存复用即可;
    • ssr渲染:
      • 当有大量静态的内容时,这些内容会被当做纯字符串推进一个buffer里;即使存在动态绑定,也会通过模板插值嵌入;比通过虚拟Dom来渲染的快很多;
      • 当静态内容大到一定程度时,会用_createStaticVNode方法在客户端生成一个static node,它们会被直接innerHtml,无需创建对象;

    PatchFlags 静态标记

    Vue3.0的diff在创建虚拟Dom的时候会根据DOM中的内容会不会发生变化,添加静态标记;

    PatchFlags:

    export const enum PatchFlags {
        TEXT = 1, // 动态文本节点
        CLASS = 1 << 1, // 2 动态class
        STYLE = 1 << 2, // 4 动态style
        PROPS = 1 << 3, // 8 动态属性,但不包含类名和样式
        FULL_PROPS = 1 << 4, // 16 具有动态key属性, 当key改变时,需要进行完整的diff比较;
        HYDRATE_EVENTS = 1 << 5, // 32 带有监听事件的节点
        STABLE_FRAGMENT = 1 << 6, // 64 一个不会改变子节点顺序的fragment
        KEYED_FRAGMENT = 1 << 7, // 128 带有key属性的fragment 或 部分子节点有key
        UNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有key的fragment
        NEED_PATCH = 1 << 9, // 512 一个节点只会进行非props比较
    }
    

    可以通过探测Vue代码转换为js之后的内容来观察Vue3中添加的静态标记;

    创建Vue3

    创建方式:Vue-Cli、Webpack、Vite

    基于Webpack快速创建的项目:

    git clone https://github.com/vuejs/vue-next-webpack-preview.git projectName
    cd projectName
    npm install
    npm run dev
    

    基于Vue-Cli快速创建的项目:

    npm install -g @vue/cli
    vue create projectName
    cd projectName
    vue add vue-next
    npm run serve
    

    什么是Vite:

    • Vite是vue作者开发的意图取代webpack的工具;
    • 原理:利用ES6的import会发送请求去加载文件的特性,拦截请求,做一些预编译,省去webpack冗长的打包时间;
    // 安装Vite
    npm install -g create-vite-app
    // 通过Vite创建Vue3项目
    create-vite-app projectName
    // 安装运行时依赖
    cd projectName
    npm install
    npm run dev
    

    Vue3 兼容 Vue2:

    • 新的->按需导入
    • 新的->组合API
      • Vue2中每新建一个业务逻辑,一般需要在data中加数据,在methods/computed/watch中处理相应的逻辑;
      • 问题是,当需要处理的业务逻辑很多时,就会维护困难;
      • 未解决这个问题,Vue3引入可组合API

    Object.assign(newObj, oldObj)API可以实现数据的浅拷贝;

    组合API —— Composition API

    Vue2中的API一般叫做,Option API;

    Vue3的Composition API:(也叫注入API)

    • 将封装好的具有独立功能的函数组合在一起使用;
    • 对组合API的理解 参看如下案例:
    <script>
    // ref 函数只能监听简单数据类型的响应式变化
    import { ref } from 'vue';
    // reactive 函数则可以监听 对象/数组 这样的复杂数据类型的响应式变化
    import { reactive } from 'vue'
    
    import useStuAdd from './useStuAdd.js'
    
    export default {
      name:'App',
      // 组合API的入口函数(这是一个同步函数)
      setup(){
        // 组合1
        let {
          count,
          addCount,
        } = useCount();
        // 组合2
        let {
          states,
        } = useStus();
        // 组合3
        let {
          stuInfo,
          addStu
        } = useStuAdd(states);
    
        return {
          //
          count,// 暴露出的变量 将被注入到 Option API的 data中
          addCount,// 暴露出的方法 将被注入到 Option API的 methods中
          //
          states,
          //
          stuInfo,
          addStu
        }
      }
    }
    // 组合1
    function useCount(){
        // 定义变量 值使用ref函数包裹 获取响应式(响应双向绑定)
        // count实际是一个对象 使用value属性获取值
        let count = ref(0);
    
        // 定义方法
        function addCount(){
          count.value += 1;
        }
    
        // 组合API中定义的变量或方法 必须通过 return {xxx,xxx}暴露出去
        return {
          count,
          addCount,
        };
    }
    // 组合2
    function useStus(){
        // 定义复杂数据类型变量
        let states = reactive({
          stus:[],
        })
    
        // 组合API中定义的变量或方法 必须通过 return {xxx,xxx}暴露出去
        return {
          states,
        };
    }
    </script>
    

    这些组合还可以定义到其他单独文件模块中:

    import { reactive } from 'vue'
    
    // 组合3 
    function useStuAdd(state){
      let stuInfo = reactive({
        id:'',
        name:'',
        age:0
      });
      function addStu(){
        let stuOne = Object.assign({}, stuInfo);
        state.stus.push(stuOne);
      }
      return {
        stuInfo,
        addStu
      }
    }
    export default useStuAdd;
    

    关于setup函数

    setup函数执行时机:

    • setup -> beforeCreate -> created;
    • setup:在setup函数中,无法使用data和methods数据;
      • setup中的this被修改为undefined
      • setup只能是同步函数;
    • beforeCreate:组件被创建出来,组件的data和methods还没有初始化;
    • created:组件的data和methods已经初始化;

    Vue3中的reactive函数

    reactive是Vue3中提供的实现响应式数据的方法:

    • 在Vue2中响应式数据是通过defineProperty修改属性标识符来诗选的;
    • Vue3中响应式数据 则是通过ES6的Proxy来实现;

    reactive函数的注意点:

    • 参数为对象/json/arr;即将传入的对象包装在Proxy中;
    • 如果不传对象,那么数据改变时,不会触发响应式的页面变化;
    • 传入的数组,通过下标修改数据也是支持的;
    • 值得注意的是,如果传入的对象/数组,包含其他对象,比如new Date()传入;
      • 这类的值需要重新赋值才能触发响应式,直接修改则无效;
    setup(){
      let date = reactive({
        time:new Date()
      });
      function changeDate(){
        // 直接修改(无法触发页面变化)
        date.time.setDate(date.time.getDate() + 1);
        // 重新赋值
        const newTime = new Date(date.time.getTime());
        date.time = newTime.setDate(date.time.getDate() + 1);
      }
      return {date,changeDate};
    }
    
    

    Vue3中的ref函数

    ref和reactive一样,也是用来实现响应式数据的方法:

    • reactive必须传递一个对象;ref方法,则实现对简单值的监听(也可以是对象);
    • ref底层还是reactive,系统会根据传入的值进行转换:let age = ref(18) ->let age = reactive({value:18})
      • 如果在template里使用ref类型数据(__v_ifRef:true),会自动添加.value;
      • 如果在template里使用reactive类型数据,不会自动添加.value;
    • 在Vue template模板中使用ref的值,直接用即可,无需通过value获取;
    • 在js中使用ref的值,则必须通过value获取(ref返回的实际是一个对象);

    对于setup函数中的数据,可以使用vue提供的isRef、isReactive函数进行类型判断:

    import {ref,reactive} from 'vue';
    import {isRef,isReactive} from 'vue';
    
    setup(){
      // ...
      isRef(age);// true
      isReactive(state);// true
    }
    
    

    递归监听

    通过ref和reactive处理的对象,都是递归监听的:

    • 包含对象嵌套的state数据,每一层都是被监听的;如果数据量比较大,会很耗性能;
      • 外层对象包装成一个Proxy对象,里边的每一层嵌套,也都递归进行Proxy包装;

    非递归监听

    仅能监听一层:

    • 创建非递归监听,使用shallowReactiveshallowRef;
    • 需要注意的是,如果通过shallowRef创建的数据是嵌套对象,那么Vue监听的是.value的变化,而不是第一层数据,.value发生变化会触发页面重新渲染;shallowReactive监听的是第一层数据,第一层数据变化会触发页面的重新渲染;

    如果在非递归监听下修改了嵌套的对象值,还想触发页面的重新渲染,可以使用triggerRef函数:

    • 注意,vue只提供了triggerRef函数,而没有提供triggerReactive函数,也就是说,没有办法主动触发reactive类型数据变化之后的页面渲染;
    • 虽然没有提供triggerReactive函数,但这个函数确实存在,shallowRef类型数据实际就会转换为triggerReactive类型数据去处理;
    import { shallowRef, triggerRef } from 'vue'
    let state = shallowRef({...});
    // ...
    state.value.a.b = 'b';
    trggierRef(state);
    // ...
    

    注意:正常使用递归监听即可,只有数据嵌套很多时,才会考虑使用非递归监听;

    toRaw方法

    Proxy对象的变化,会引起界面响应式的修改;但如果直接修改引用对象,并不会触发;

    toRaw的作用就是,获取响应式数据引用的原始数据:

    • 在某些不需要响应式变化的地方,还想修改响应式的数据,就可以通过toRaw拿到原始数据,来进行操作;
    // reactive类型的原始数据获取
    import {reactive, toRaw} from 'vue'
    
    setup(){
      let obj1 = {name:'haha',age:18};// 原始数据
      let state = reactive(obj1);// Proxy对象
      let obj2 = toRaw(state);// state引用的原始对象
      obj1 === obj2 // true
    
      return {state};
    }
    
    // ref类型的原始数据获取
    import {ref, toRaw} from 'vue'
    
    setup(){
      let obj1 = {name:'haha',age:18};// 原始数据
      let state = ref(obj1);// Proxy对象
      let obj2 = toRaw(state.value);// state引用的原始对象
      obj1 === obj2 // true
    
      return {state};
    }
    

    markRaw方法

    markRaw 处理之后的数据 再也不会被追踪 也不会产生响应式的变化

    // reactive类型的原始数据获取
    import {reactive, markRaw} from 'vue'
    
    setup(){
      let obj = {name:'haha',age:18};// 原始数据
      obj = markRaw(obj);// markRaw 处理之后的数据 再也不会被追踪 也不会产生响应式的变化
      let state = reactive(obj);// Proxy对象
    
      function changeState(){
        state.name = 'heihei';// 这个赋值操作既不会生效 也不会引起页面响应式的变化;
      }
    
      return {state, changeState};
    }
    

    toRef 和 toRefs

    toRef:可以把响应式数据与以前的数据关联起来,而且修改响应式数据还不会更新UI;

    ref:

    • 复制,修改响应式数据state.value,obj中name不会改变;
    • 修改响应式数据state.value,页面自动更新;

    toRef、toRefs:

    • 引用,修改响应式数据state.value,obj中name也会改变;
    • 修改响应式数据state.value,页面不会自动更新;
    // ref
    let obj = {name:'haha'};
    let state = ref(obj.name);//注意 这里响应式处理的只是obj的一个属性 相当于复制了obj.name的值
    state.value = 'heihei';//原始数据 obj.name 不受影响,界面上绑定的数据显示也会改变
    
    // toRef(也是一种变成响应式数据的方式)
    let obj = {name:'haha'};
    let state = toRef(obj, 'name');
    state.value = 'heihei';// 原始数据 obj.name 也会发生变化,界面上绑定的数据显示不受影响
    
    // toRefs
    let obj = {name:'haha', age:18};
    let state = toRefs(obj);
    state.name.value = 'heihei';
    state.age.value = 20;
    
    

    customRef

    自定义一个Ref:myRef

    import { customRef } from 'vue'
    
    function myRef(value, delay = 200){
      let timeout//这个延时可以不要
      return customRef((track, trigger) => {
        retrun {
          get(){
            track()//当前值需要被追踪
            return value
          },
          set(newValue){
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()// 触发响应式变化
            }, delay)
          }
        }
      })
    }
    
    ...
    
    setup(){
      return {
        text:myRef('DM')// 用起来和Ref一样 只是自定义的
      }
    }
    
    

    自定义一个Ref的意义:

    • 对于需要异步获取的数据A,如果要把它对应成响应式的数据,正常需要在setUp中编写相应的业务网络请求代码,将接口返回数据用准备暴露出去的响应式对象接收;
    let state = ref([])
    state.value = A
    return { state }
    
    • 使用自定义的Ref可以进一步将业务分离,直接将异步获取的对象转换成响应式的对象
    import { customRef } from 'vue'
    
    function myRef(value, delay = 200){
      // 传入的value是一个接口请求的url
    
      let timeout//这个延时可以不要
      return customRef((track, trigger) => {
        // 这里使用es6的fetch进行网络请求
        fetch(value).then(res => {
          value = data
          trigger()
        }).catch(err => {
          console.log(err)
        })
    
        retrun {
          get(){
            track()//当前值需要被追踪
            return value
          },
          set(newValue){
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()// 触发响应式变化
            }, delay)
          }
        }
      })
    }
    
    ...
    
    setup(){
      return {
        A:myRef('../list')// 通过接口获取的服务端对象 通过自定义Ref直接被处理成响应式的数据了
      }
    } 
    

    理解更多

    ref获取元素

    Vue2.0

    <div ref="box">test</div>
    
    ...
    //  获取组件
    this.$ref.box
    

    Vue3.0

    <div ref="box">test</div>
    
    ...
    import { ref, onMounted } from 'vue';
    setUp(){
      let box = ref(null);
      // 抽离的生命周期函数 需要传入一个回调函数
      onMounted(() => {
        console.log(box.value)
      })
      // 直接在setUp方法中打印组件信息是无效的 因为此时处于的生命周期在created之前
    
      return { box } 
    }
    
    

    readonly isReadonly shallowReadonly

    用于把对象处理成只读的响应式数据:

    • readonly:递归只读处理
    • shallowReadonly:第一层的只读处理(嵌套数据变化 不会引起页面显示变化)
    • isReadonly:判断是不是只读数据;
    展开全文
  • Web开发-Vue3.0 CompositionAPI 通用知识学习 2020-09-18 发布的Vue3.0正式版 建议先学ts;因为Vue3.0框架多数都是用ts重写的; API文档仔细阅读; Vue3亮点: Performance:性能更快; Tree shaking support:按...

    Web开发-Vue3.0 CompositionAPI 通用知识学习

    2020-09-18 发布的Vue3.0正式版

    建议先学ts;因为Vue3.0框架多数都是用ts重写的;
    API文档仔细阅读;

    Vue3亮点:

    • Performance:性能更快;
    • Tree shaking support:按需编译,体积更小;
    • Composition API:组合API(类似React Hooks);
    • Better TypeScript support:更好的ts支持;
    • Custom Renderer API:暴露了自定义渲染API;
    • Fragment Teleport(Protal) Suspense:更先进的组件;

    Vue3是如何变快的:

    • diff优化:
      • Vue2中虚拟Dom是进行全量对比;
      • Vue3新增静态标记(PatchFlag),虚拟dom比对时,只对比带有patch flag的节点,而且可以通过flag的信息得知当前接待你要对比的具体内容;
    • hoistStatic 静态提升:
      • Vue2中无论元素是否参与更新,每次都会重新创建;
      • Vue3中对不参与更新的元素,只会创建一次,之后会在每次渲染时候被不停的复用;
    • cacheHandlers 事件侦听器缓存
      • 默认情况下onClick会被视为动态绑定(静态标记是8),所以每次都回去追踪它的变化;使用缓存后,就不会进行静态标记,也就不会再追踪了,因为是同一函数缓存复用即可;
    • ssr渲染:
      • 当有大量静态的内容时,这些内容会被当做纯字符串推进一个buffer里;即使存在动态绑定,也会通过模板插值嵌入;比通过虚拟Dom来渲染的快很多;
      • 当静态内容大到一定程度时,会用_createStaticVNode方法在客户端生成一个static node,它们会被直接innerHtml,无需创建对象;

    PatchFlags 静态标记

    Vue3.0的diff在创建虚拟Dom的时候会根据DOM中的内容会不会发生变化,添加静态标记;

    PatchFlags:

    export const enum PatchFlags {
        TEXT = 1, // 动态文本节点
        CLASS = 1 << 1, // 2 动态class
        STYLE = 1 << 2, // 4 动态style
        PROPS = 1 << 3, // 8 动态属性,但不包含类名和样式
        FULL_PROPS = 1 << 4, // 16 具有动态key属性, 当key改变时,需要进行完整的diff比较;
        HYDRATE_EVENTS = 1 << 5, // 32 带有监听事件的节点
        STABLE_FRAGMENT = 1 << 6, // 64 一个不会改变子节点顺序的fragment
        KEYED_FRAGMENT = 1 << 7, // 128 带有key属性的fragment 或 部分子节点有key
        UNKEYED_FRAGMENT = 1 << 8, // 256 子节点没有key的fragment
        NEED_PATCH = 1 << 9, // 512 一个节点只会进行非props比较
    }
    

    可以通过探测Vue代码转换为js之后的内容来观察Vue3中添加的静态标记;

    创建Vue3

    创建方式:Vue-Cli、Webpack、Vite

    基于Webpack快速创建的项目:

    git clone https://github.com/vuejs/vue-next-webpack-preview.git projectName
    cd projectName
    npm install
    npm run dev
    

    基于Vue-Cli快速创建的项目:

    npm install -g @vue/cli
    vue create projectName
    cd projectName
    vue add vue-next
    npm run serve
    

    什么是Vite:

    • Vite是vue作者开发的意图取代webpack的工具;
    • 原理:利用ES6的import会发送请求去加载文件的特性,拦截请求,做一些预编译,省去webpack冗长的打包时间;
    // 安装Vite
    npm install -g create-vite-app
    // 通过Vite创建Vue3项目
    create-vite-app projectName
    // 安装运行时依赖
    cd projectName
    npm install
    npm run dev
    

    Vue3 兼容 Vue2:

    • 新的->按需导入
    • 新的->组合API
      • Vue2中每新建一个业务逻辑,一般需要在data中加数据,在methods/computed/watch中处理相应的逻辑;
      • 问题是,当需要处理的业务逻辑很多时,就会维护困难;
      • 未解决这个问题,Vue3引入可组合API

    Object.assign(newObj, oldObj)API可以实现数据的浅拷贝;

    组合API —— Composition API

    Vue2中的API一般叫做,Option API;

    Vue3的Composition API:(也叫注入API)

    • 将封装好的具有独立功能的函数组合在一起使用;
    • 对组合API的理解 参看如下案例:
    <script>
    // ref 函数只能监听简单数据类型的响应式变化
    import { ref } from 'vue';
    // reactive 函数则可以监听 对象/数组 这样的复杂数据类型的响应式变化
    import { reactive } from 'vue'
    
    import useStuAdd from './useStuAdd.js'
    
    export default {
      name:'App',
      // 组合API的入口函数(这是一个同步函数)
      setup(){
        // 组合1
        let {
          count,
          addCount,
        } = useCount();
        // 组合2
        let {
          states,
        } = useStus();
        // 组合3
        let {
          stuInfo,
          addStu
        } = useStuAdd(states);
    
        return {
          //
          count,// 暴露出的变量 将被注入到 Option API的 data中
          addCount,// 暴露出的方法 将被注入到 Option API的 methods中
          //
          states,
          //
          stuInfo,
          addStu
        }
      }
    }
    // 组合1
    function useCount(){
        // 定义变量 值使用ref函数包裹 获取响应式(响应双向绑定)
        // count实际是一个对象 使用value属性获取值
        let count = ref(0);
    
        // 定义方法
        function addCount(){
          count.value += 1;
        }
    
        // 组合API中定义的变量或方法 必须通过 return {xxx,xxx}暴露出去
        return {
          count,
          addCount,
        };
    }
    // 组合2
    function useStus(){
        // 定义复杂数据类型变量
        let states = reactive({
          stus:[],
        })
    
        // 组合API中定义的变量或方法 必须通过 return {xxx,xxx}暴露出去
        return {
          states,
        };
    }
    </script>
    

    这些组合还可以定义到其他单独文件模块中:

    import { reactive } from 'vue'
    
    // 组合3 
    function useStuAdd(state){
      let stuInfo = reactive({
        id:'',
        name:'',
        age:0
      });
      function addStu(){
        let stuOne = Object.assign({}, stuInfo);
        state.stus.push(stuOne);
      }
      return {
        stuInfo,
        addStu
      }
    }
    export default useStuAdd;
    

    关于setup函数

    setup函数执行时机:

    • setup -> beforeCreate -> created;
    • setup:在setup函数中,无法使用data和methods数据;
      • setup中的this被修改为undefined
      • setup只能是同步函数;
    • beforeCreate:组件被创建出来,组件的data和methods还没有初始化;
    • created:组件的data和methods已经初始化;

    Vue3中的reactive函数

    reactive是Vue3中提供的实现响应式数据的方法:

    • 在Vue2中响应式数据是通过defineProperty修改属性标识符来诗选的;
    • Vue3中响应式数据 则是通过ES6的Proxy来实现;

    reactive函数的注意点:

    • 参数为对象/json/arr;即将传入的对象包装在Proxy中;
    • 如果不传对象,那么数据改变时,不会触发响应式的页面变化;
    • 传入的数组,通过下标修改数据也是支持的;
    • 值得注意的是,如果传入的对象/数组,包含其他对象,比如new Date()传入;
      • 这类的值需要重新赋值才能触发响应式,直接修改则无效;
    setup(){
      let date = reactive({
        time:new Date()
      });
      function changeDate(){
        // 直接修改(无法触发页面变化)
        date.time.setDate(date.time.getDate() + 1);
        // 重新赋值
        const newTime = new Date(date.time.getTime());
        date.time = newTime.setDate(date.time.getDate() + 1);
      }
      return {date,changeDate};
    }
    
    

    Vue3中的ref函数

    ref和reactive一样,也是用来实现响应式数据的方法:

    • reactive必须传递一个对象;ref方法,则实现对简单值的监听(也可以是对象);
    • ref底层还是reactive,系统会根据传入的值进行转换:let age = ref(18) ->let age = reactive({value:18})
      • 如果在template里使用ref类型数据(__v_ifRef:true),会自动添加.value;
      • 如果在template里使用reactive类型数据,不会自动添加.value;
    • 在Vue template模板中使用ref的值,直接用即可,无需通过value获取;
    • 在js中使用ref的值,则必须通过value获取(ref返回的实际是一个对象);

    对于setup函数中的数据,可以使用vue提供的isRef、isReactive函数进行类型判断:

    import {ref,reactive} from 'vue';
    import {isRef,isReactive} from 'vue';
    
    setup(){
      // ...
      isRef(age);// true
      isReactive(state);// true
    }
    
    

    递归监听

    通过ref和reactive处理的对象,都是递归监听的:

    • 包含对象嵌套的state数据,每一层都是被监听的;如果数据量比较大,会很耗性能;
      • 外层对象包装成一个Proxy对象,里边的每一层嵌套,也都递归进行Proxy包装;

    非递归监听

    仅能监听一层:

    • 创建非递归监听,使用shallowReactiveshallowRef;
    • 需要注意的是,如果通过shallowRef创建的数据是嵌套对象,那么Vue监听的是.value的变化,而不是第一层数据,.value发生变化会触发页面重新渲染;shallowReactive监听的是第一层数据,第一层数据变化会触发页面的重新渲染;

    如果在非递归监听下修改了嵌套的对象值,还想触发页面的重新渲染,可以使用triggerRef函数:

    • 注意,vue只提供了triggerRef函数,而没有提供triggerReactive函数,也就是说,没有办法主动触发reactive类型数据变化之后的页面渲染;
    • 虽然没有提供triggerReactive函数,但这个函数确实存在,shallowRef类型数据实际就会转换为triggerReactive类型数据去处理;
    import { shallowRef, triggerRef } from 'vue'
    let state = shallowRef({...});
    // ...
    state.value.a.b = 'b';
    trggierRef(state);
    // ...
    

    注意:正常使用递归监听即可,只有数据嵌套很多时,才会考虑使用非递归监听;

    toRaw方法

    Proxy对象的变化,会引起界面响应式的修改;但如果直接修改引用对象,并不会触发;

    toRaw的作用就是,获取响应式数据引用的原始数据:

    • 在某些不需要响应式变化的地方,还想修改响应式的数据,就可以通过toRaw拿到原始数据,来进行操作;
    // reactive类型的原始数据获取
    import {reactive, toRaw} from 'vue'
    
    setup(){
      let obj1 = {name:'haha',age:18};// 原始数据
      let state = reactive(obj1);// Proxy对象
      let obj2 = toRaw(state);// state引用的原始对象
      obj1 === obj2 // true
    
      return {state};
    }
    
    // ref类型的原始数据获取
    import {ref, toRaw} from 'vue'
    
    setup(){
      let obj1 = {name:'haha',age:18};// 原始数据
      let state = ref(obj1);// Proxy对象
      let obj2 = toRaw(state.value);// state引用的原始对象
      obj1 === obj2 // true
    
      return {state};
    }
    

    markRaw方法

    markRaw 处理之后的数据 再也不会被追踪 也不会产生响应式的变化

    // reactive类型的原始数据获取
    import {reactive, markRaw} from 'vue'
    
    setup(){
      let obj = {name:'haha',age:18};// 原始数据
      obj = markRaw(obj);// markRaw 处理之后的数据 再也不会被追踪 也不会产生响应式的变化
      let state = reactive(obj);// Proxy对象
    
      function changeState(){
        state.name = 'heihei';// 这个赋值操作既不会生效 也不会引起页面响应式的变化;
      }
    
      return {state, changeState};
    }
    

    toRef 和 toRefs

    toRef:可以把响应式数据与以前的数据关联起来,而且修改响应式数据还不会更新UI;

    ref:

    • 复制,修改响应式数据state.value,obj中name不会改变;
    • 修改响应式数据state.value,页面自动更新;

    toRef、toRefs:

    • 引用,修改响应式数据state.value,obj中name也会改变;
    • 修改响应式数据state.value,页面不会自动更新;
    // ref
    let obj = {name:'haha'};
    let state = ref(obj.name);//注意 这里响应式处理的只是obj的一个属性 相当于复制了obj.name的值
    state.value = 'heihei';//原始数据 obj.name 不受影响,界面上绑定的数据显示也会改变
    
    // toRef(也是一种变成响应式数据的方式)
    let obj = {name:'haha'};
    let state = toRef(obj, 'name');
    state.value = 'heihei';// 原始数据 obj.name 也会发生变化,界面上绑定的数据显示不受影响
    
    // toRefs
    let obj = {name:'haha', age:18};
    let state = toRefs(obj);
    state.name.value = 'heihei';
    state.age.value = 20;
    
    

    customRef

    自定义一个Ref:myRef

    import { customRef } from 'vue'
    
    function myRef(value, delay = 200){
      let timeout//这个延时可以不要
      return customRef((track, trigger) => {
        retrun {
          get(){
            track()//当前值需要被追踪
            return value
          },
          set(newValue){
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()// 触发响应式变化
            }, delay)
          }
        }
      })
    }
    
    ...
    
    setup(){
      return {
        text:myRef('DM')// 用起来和Ref一样 只是自定义的
      }
    }
    
    

    自定义一个Ref的意义:

    • 对于需要异步获取的数据A,如果要把它对应成响应式的数据,正常需要在setUp中编写相应的业务网络请求代码,将接口返回数据用准备暴露出去的响应式对象接收;
    let state = ref([])
    state.value = A
    return { state }
    
    • 使用自定义的Ref可以进一步将业务分离,直接将异步获取的对象转换成响应式的对象
    import { customRef } from 'vue'
    
    function myRef(value, delay = 200){
      // 传入的value是一个接口请求的url
    
      let timeout//这个延时可以不要
      return customRef((track, trigger) => {
        // 这里使用es6的fetch进行网络请求
        fetch(value).then(res => {
          value = data
          trigger()
        }).catch(err => {
          console.log(err)
        })
    
        retrun {
          get(){
            track()//当前值需要被追踪
            return value
          },
          set(newValue){
            clearTimeout(timeout)
            timeout = setTimeout(() => {
              value = newValue
              trigger()// 触发响应式变化
            }, delay)
          }
        }
      })
    }
    
    ...
    
    setup(){
      return {
        A:myRef('../list')// 通过接口获取的服务端对象 通过自定义Ref直接被处理成响应式的数据了
      }
    } 
    

    理解更多

    ref获取元素

    Vue2.0

    <div ref="box">test</div>
    
    ...
    //  获取组件
    this.$ref.box
    

    Vue3.0

    <div ref="box">test</div>
    
    ...
    import { ref, onMounted } from 'vue';
    setUp(){
      let box = ref(null);
      // 抽离的生命周期函数 需要传入一个回调函数
      onMounted(() => {
        console.log(box.value)
      })
      // 直接在setUp方法中打印组件信息是无效的 因为此时处于的生命周期在created之前
    
      return { box } 
    }
    
    

    readonly isReadonly shallowReadonly

    用于把对象处理成只读的响应式数据:

    • readonly:递归只读处理
    • shallowReadonly:第一层的只读处理(嵌套数据变化 不会引起页面显示变化)
    • isReadonly:判断是不是只读数据;
    <template>
    ...
      {{state.name}}
    
      @click="myFn"
    ...
    <template>
    import {readonly, isReadonly, shallowReadonly} from 'vue'
    
    export default {
      name:'App',
      setip(){
        let state  = readonly({name:'haha'});
    
        function myFn(){
          state.name = 'hehe'//这个修改是无效的
    
          isReadonly(state)//readonly和shallowReadonly经isReadonly返回的都是true
        }
        return {
          state
        }
      }
    }
    
    

    代码示例

    ant-design-vue Table

    表格编辑时的一段数据操作示例:

    <template>
      <a-table :columns="columns" :data-source="dataSource" bordered>
        <template v-for="col in ['name', 'age', 'address']" #[col]="{ text, record }" :key="col">
          <div>
            <a-input
              v-if="editableData[record.key]"
              v-model:value="editableData[record.key][col]"
              style="margin: -5px 0"
            />
            <template v-else>
              {{ text }}
            </template>
          </div>
        </template>
        <template #operation="{ record }">
          <div class="editable-row-operations">
            <span v-if="editableData[record.key]">
              <a @click="save(record.key)">Save</a>
              <a-popconfirm title="Sure to cancel?" @confirm="cancel(record.key)">
                <a>Cancel</a>
              </a-popconfirm>
            </span>
            <span v-else>
              <a @click="edit(record.key)">Edit</a>
            </span>
          </div>
        </template>
      </a-table>
    </template>
    <script>
    import { cloneDeep } from 'lodash-es';
    import { defineComponent, reactive, ref } from 'vue';
    const columns = [
      {
        title: 'name',
        dataIndex: 'name',
        width: '25%',
        slots: {
          customRender: 'name',
        },
      },
      {
        title: 'age',
        dataIndex: 'age',
        width: '15%',
        slots: {
          customRender: 'age',
        },
      },
      {
        title: 'address',
        dataIndex: 'address',
        width: '40%',
        slots: {
          customRender: 'address',
        },
      },
      {
        title: 'operation',
        dataIndex: 'operation',
        slots: {
          customRender: 'operation',
        },
      },
    ];
    const data = [];
    
    for (let i = 0; i < 100; i++) {
      data.push({
        key: i.toString(),
        name: `Edrward ${i}`,
        age: 32,
        address: `London Park no. ${i}`,
      });
    }
    
    export default defineComponent({
      setup() {
        const dataSource = ref(data);
        const editableData = reactive({});
    
        const edit = key => {
          editableData[key] = cloneDeep(dataSource.value.filter(item => key === item.key)[0]);
        };
    
        const save = key => {
          Object.assign(dataSource.value.filter(item => key === item.key)[0], editableData[key]);
          delete editableData[key];
        };
    
        const cancel = key => {
          delete editableData[key];
        };
    
        return {
          dataSource,
          columns,
          editingKey: '',
          editableData,
          edit,
          save,
          cancel,
        };
      },
    });
    </script>
    <style scoped>
    .editable-row-operations a {
      margin-right: 8px;
    }
    </style>
    
    展开全文
  • vue3.0快速上手

    2019-10-17 21:33:36
    vue3.0为了更好的类型推断,(避免使用装饰器),完全使用普通函数,用TS重写了源码,所以在如果你使用Vue3.0开发,最好和TS搭配使用。 数据通信之前使用高阶组件(会导致没有更新的组件也进行重新渲染),mixin...
  • 当使用组合式 API 时,reactive refs 和 template refs 的概念已经是统一的。为了获得对模板内元素或组件实例的引用,...vue 没有做特殊处理,需要使用函数型的 ref(3.0 提供的新功能)来自定义处理方式: {{ item }}
  • Vue 3.0中jsx语法的使用

    2021-01-21 10:54:12
    尝试看了下文档,发现支持 jsx 语法,由于这段时间都是在使用 react 来开发项目,jsx 完全不同的体验,更加的纯粹与灵活。 项目创建 npm init vite-app vite-vue cd vite-vue npm install npm run dev 我们发现创建...
  • vue3.0拖拽缩放组件

    2021-01-24 15:19:26
    本人开发vue3.0拖拽缩放组件,持续更新中 github地址: https://github.com/zzz0908/vue3-resize-drag npmjs地址: https://www.npmjs.com/package/vue3-resize-drag demo: 文档 vue3.0拖拽缩放插件 下载并引入 npm...
  • Vue3.0脚手架搭建

    2019-09-25 03:39:08
    vue3.0官网:https://cli.vuejs.org/zh/guide/ 介绍: notice: 这份文档是对应 @vue/cli 3.x 版本的。老版本的 vue-cli 文档请移步这里 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供: 1.通过 @vue/...
  • vue3.0初体验

    2021-01-12 16:18:57
    vue3.0初体验 文档网址: https://www.vue3js.cn/docs/zh/guide/migration/introduction.html#%E6%A6%82%E8%A7%88 1、体积比vue2.0更小 使用vite,利用es6的import会发送请求去加载文件的特性,拦截这些请求。做...
  • vue 3.0 官方文档学习 基础 开发概览 安装介绍 应用&组件实例 模板语法 Data Property 计算属性和侦听器 Class 与 Style 绑定 条件渲染 列表渲染 事件处理 表单输入绑定 组件基础 深入组件 组件注册 ...
  • vue3.0学习记录(1)

    2020-12-12 00:21:31
    vue3.0官方文档地址 https://v3.vuejs.org/ 使用 vue-cli 配置 vue3 开发环境 卸载旧版本 vue-cli npm uninstall vue-cli -g 如果出错,有可能是npm的全局路径遭到了更改,则运行以下命令:npm config set prefix /...
  • Vue3.0由单页面应用改为多页面开发 一、使用vue cli创建一个单页面应用程序 1、在命令行窗口敲如下图命令,输入完成敲回车会自动打开一个vue的管理界面 2、在管理界面中创建项目 选择好配置开始...
  • vue-next-renting​github.comvue-next-renting简介vue-next-renting 是一个基于 vite + vue3 构建的移动端视频课程项目。我们可以通过下面的 gif 图片来对 vue-next-renting 有...dev:开发分支。始终保持最新完成...
  • 安装卸载老版本的vue cli:NPM安装vue cli v4.5:创建vue3.0项目:按需安装项目必须的插件 尤大大的vue 3.0来了,那肯定抓紧学起来,照着官方文档学习,也会记录一下学习过程! 卸载老版本的vue cli: npm ...
  • 尝鲜 Vue 3.0 组合式API

    2020-09-06 17:46:46
    Vue 3.0 即将发布,我们已经可以尝试一些新版本中带来的新功能,比如 Composition-API 组合式API。如果你了解过 React 的 Hooks,那么当你看到组合式API时一定不会感到陌生。组合式API受到了 React Hooks 很大的启发...
  • 这是vue3.0文档地址:https://v3.vuejs.org/guide/installation.html(也可以进入2.x版本选择3.0进入); 下面开始说安装步骤 一、介绍 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统。有三个组件: CLI:@vue/cli ...
  • Vue 3.0:初窥Vite

    2020-10-24 13:38:12
    Vue 3.0的又一亮点是带来了一个新的开发构建工具:Vite。根据官方文档的描述,它是一个在开发环境下利用现代浏览器原生ES模块进行代码文件的按需加载,并在生产环境下将其与Rollup进行绑定的一个web开发构建工具。 ...
  • Vue 3.0 正式发布了,喜大普奔?。新的语法又要学习一阵阵,不过需要在生产环境下大...尝试看了下文档,发现支持 jsx 语法,由于这段时间都是在使用 react 来开发项目,jsx 完全不同的体验,更加的纯粹与灵活。项目...
  • Vue.js 3.0 "One Piece" 已正式发布,此框架新的主要版本提供了更好的性能、更...3.0 版本的开发周期长达两年多,期间产生了30+ RFCs、2600+ commits、628 pull requests,以及核心仓库之外的大量开发文档工作。Vu...
  • Vue 3.0 VueRouter 威克斯 打字稿 元素UI Plus 尾风CSS Axios 进度安排 原型设计 :large_orange_diamond: 配合文档改善数据库的结构和类型 样式编写 项目技术型的初始建造 :check_mark: 恶毒 打字稿 Vue路由器...
  • Vue3.0与Vue2.x的差异

    2021-01-23 09:46:44
    但是必须要承认在实际开发中对于Vue的使用不尽人意,很多地方并没有能够利用Vue的优点来简化开发,比如父子组件的传值和修改,多个组件总是用到同一个方法,对象数据的响应式处理等,所以回炉重看了Vue文档,...
  • 15分钟上手vue3.0(小结)

    2021-01-19 14:42:25
    嘴上喊着老子学不动了,双手还是很诚实的打开了 Vue 3 文档 创建项目 Vue 官方很贴心的提供了一个 github 仓库,让我们能快速体验Vue 3的新特性: git clone ...
  • 一、使用vue-cli搭建项目(可使用vue ui图形化界面搭建项目,... // 翻阅文档https://cli.vuejs.org/zh/config/#vue-config-js  配置publicPath    设置反向代理,用于本地跨域使用,仅使用于开发环境  ...
  • vue-cli3.0开发上路

    2019-02-22 14:20:05
    Vue Cli3.0项目搭建Vue Cli 3.0Vue cli 安装初始化项目引入element引入bootstrap引入jquery Vue Cli 3.0 参看文档https://cli.vuejs.org/zh/guide/ Vue cli 安装 node版本使用新的版本,10.0.x。 npm install -g @...
  • 目录一、环境配置1.1 安装脚手架([Vue CLI](https://cli.vuejs.org/zh/))1.2 创建项目1.3 VScode设置二、基础语法2.1 setup2.2 ref2.3 ...借鉴使用 Typescript + Vue3 开发高仿知乎专栏文档站点 一、环境配置 1
  • 这种错误是因为vue.config.js的配置里面 publicPath写了绝对路径,生产环境改为./相对路径即可,vue.config.js详细配置参考官方文档 第二种错误:无报错,js加载了,但是页面空白(router-view没有渲染任何内容) ...
  • 想学Vue的小伙伴们,小编这几个月陆陆续续收集整理了几十篇关于Vue的学习相关文章以及项目开发中的总结,相关Vue学习资料文章请见本篇文章底部 1. 全新文档RFCs Vue.js 3.0 Beta发布后的工作重点是保证稳定性和...
  • Vue3.0来啦

    2020-11-13 22:51:25
    Vue3.0—— 两年开发 Vue3.0终于在九月发布啦,但是目前官方文档还未发布中文文档 一.Vue3的新特性 性能提升:打包大小减少41% 初次渲染加快55%,更新快133% 内存使用减少54% Composition API: ref和reactive ///...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 140
精华内容 56
关键字:

vue3.0开发文档

vue 订阅