vue3学习

150 阅读4分钟
Composition API + <script setup>
  • 可以任意拆分组件的功能,抽离出独立的工具函数,大大提高了代码的可维护性。
  • Composition API都是从 Vue 中单独引入,可以脱离对this 上下文的依赖。
<template>
  <div>
    <input type="text" v-model="title" @keydown.enter="addTodo" />
    <button v-if="active < all" @click="clear">清理</button>
    <ul v-if="todos.length">
      <li v-for="todo in todos">
        <input type="checkbox" v-model="todo.done" />
        <span :class="{ done: todo.done }"> {{ todo.title }}</span>
      </li>
    </ul>
    <div v-else>暂无数据</div>
    <div>
      全选<input type="checkbox" v-model="allDone" />
      <span> {{ active }} / {{ all }} </span>
    </div>
  </div>
</template>

<script>
import { ref, computed } from "vue";
function useTodos() {
  let title = ref("");
  let todos = ref([{ title: "学习Vue", done: false }]);
  function addTodo() {
    todos.value.push({
      title: title.value,
      done: false,
    });
    title.value = "";
  }
  function clear() {
    todos.value = todos.value.filter((v) => !v.done);
  }
  let active = computed(() => {
    return todos.value.filter((v) => !v.done).length;
  });
  let all = computed(() => todos.value.length);
  let allDone = computed({
    get: function () {
      return active.value === 0;
    },
    set: function (value) {
      todos.value.forEach((todo) => {
        todo.done = value;
      });
    },
  });
  return { title, todos, addTodo, clear, active, all, allDone };
}
</script>

<script setup>
let { title, todos, addTodo, clear, active, all, allDone } = useTodos();
</script>
style标签内使用js变量
  • 在样式中可以使用v-bind绑定script中声明的js变量
<template>
    <h1 @click="add">点击变色</h1>
</template>

<script setup>
import { ref } from 'vue'
let color = ref('red')
const add = () =>{
    color.value = Math.random() > 0.5? 'blue': 'red'
}
</script>

<style>
h1 {
    color: v-bind(color);
}
</style>
各种拦截方式对比(后续补充)
  • Proxy 是针对对象来监听,而不是针对某个具体属性,所以不仅可以代理那些定义时不存在的属性,还可以代理更丰富的数据结构,比如 Map、Set 等,并且我们也能通过 deleteProperty 实现对删除操作的代理。 image.png
watchEffect与watch监听的区别
  • watchEffect 不需要指定监听的属性,他会在初始化的时候就执行一次自动收集依赖,只要我们回调中引用到了响应式的属性,那么当这些属性变更的时候,这个回调都会执行,而 watch 只能监听指定的属性而做出变更(v3开始可以同时指定多个)。
  • 就是 watch 可以获取到新值与旧值(更新前的值),而 watchEffect 是拿不到的。
  • watchEffect如果存在的话,在组件初始化的时候就会执行一次用以收集依赖(与computed同理),而后收集到的依赖发生变化,这个回调才会再次执行,而 watch 不需要,因为他一开始就指定了依赖
和本地存储配合一起保存数据
<template>
  <div>
    <input type="text" v-model="title" @keydown.enter="addTodo" />
    <ul v-if="todos.length">
      <li v-for="todo in todos">
        <input type="checkbox" v-model="todo.done" />
        <span :class="{ done: todo.done }"> {{ todo.title }}</span>
      </li>
    </ul>
  </div>
</template>

<script>
import { ref, watchEffect } from "vue";
const useStorage = (name, value=[]) => {
    value = localStorage.getItem(name) ? JSON.parse(localStorage.getItem(name)) : value;
    let data = ref(value)
    watchEffect(()=>{
        localStorage.setItem(name,JSON.stringify(data.value))
    })
    return data
}
function useTodos() {
  let title = ref("");
  let todos = useStorage('todos',[{ title: "学习Vue", done: false }]);
  function addTodo() {
    todos.value.push({
      title: title.value,
      done: false,
    });
    title.value = "";
  }
  return { title, todos, addTodo };
}
</script>

<script setup>
let { title, todos, addTodo } = useTodos();
</script>
v-model更改

vue3中,默认情况下,组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件。而vue2中是使用value作为prop

jsx支持

需要安装@vitejs/plugin-vue-jsx插件来支持jsx转化为createVNode 函数,从而得到jsx的虚拟dom。
npm install @vitejs/plugin-vue-jsx -D

// vue.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(),vueJsx()]
})
template和jsx的区别
  • template 的语法是固定的,只有 v-if、v-for 等等语法。也就是说,template 遇见条件渲染就是要固定的选择用 v-if。我们按照这种固定格式的语法书写,这样 Vue 在编译层面就可以很方便地去做静态标记的优化。
  • 而 JSX 只是 h 函数的一个语法糖,本质就是 JavaScript,想实现条件渲染可以用 if else,也可以用三元表达式,还可以用任意合法的 JavaScript 语法。也就是说,JSX 可以支持更动态的需求。而 template 则因为语法限制原因,不能够像 JSX 那样可以支持更动态的需求。这是 JSX 相比于 template 的一个优势。
  • JSX 相比于 template 还有一个优势,是可以在一个文件内返回多个组件。
  • 相比于我们自己去写 h 函数,在 template 解析的结果中,有以下几个性能优化的方面:
  1. 静态的标签和属性会放在 _hoisted 变量中,并且放在 render 函数之外。这样,重复执行 render 的时候,代码里的纯静态的标签,就不需要进行额外地计算,并且静态标签在虚拟 DOM 计算的时候,会直接越过 Diff 过程。
  2. @click 函数增加了一个 cache 缓存层,这样实现出来的效果也是和静态提升类似,尽可能高效地利用缓存。
  3. 动态属性会打标签与静态属性区分出来,因而存在使用一个数字去标记标签的动态情况。比如在 p 标签上,使用 8 这个数字标记当前标签时,只有 props 是动态的。而在虚拟 DOM 计算 Diff 的过程中,可以忽略掉 class 和文本的计算,这也是 Vue 3 的虚拟 DOM 能够比 Vue 2 快的一个重要原因。
  • 在 template 和 JSX 这两者的选择问题上,只是选择框架时角度不同而已。我们实现业务需求的时候,也是优先使用 template,动态性要求较高的组件使用 JSX 实现,尽可能地利用 Vue 本身的性能优化。
vite环境变量
  • 客户端源码中环境变量的读取与webpack有很大差别,可以根据官方文档来进行配置。
  • 只有VITE_为前缀的变量才会暴露给客户端源码,当然可以通过envPrefix自定义前缀。 image.png image.png
  • vite.config.js的环境变量获取 process.env只能获取到
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx';
import { viteMockServe } from 'vite-plugin-mock'

export default ({mode}) => {
  process.env = {...process.env, ...loadEnv(mode, process.cwd())};
  const localEnabled: boolean = (process.env.USE_MOCK as unknown as boolean) || true;
  const prodEnabled: boolean = (process.env.USE_CHUNK_MOCK as unknown as boolean) || false;
  return defineConfig({
    plugins: [vue(),vueJsx(),
      viteMockServe({
        // ↓解析根目录下的mock文件夹
        mockPath: "mock",
        localEnabled: localEnabled,  // 开发打包开关
        prodEnabled: prodEnabled, // 生产打包开关
        supportTs: true, // 打开后,可以读取 ts 文件模块。 请注意,打开后将无法监视.js 文件。
        watchFiles: true, // 监视文件更改
      }),],
    resolve: {
      alias: {
        '@/': new URL('./src/', import.meta.url).pathname
      }
    },
  })
}
mock配置
  • vite.config.js配置见上面例子
  • mock.js配置
import { MockMethod } from 'vite-plugin-mock'
export default [
    {
        url: '/api/getUserInfo',
        method: 'post',
        response: () => {
            return {
                code: 200,
                data: {
                    nickname: '@cname',
                    age: '@integer(10-100)',
                    uid: '@id',
                    url: '@image',
                    city: '@city',
                    country: '@county(true)',
                    province: '@province',
                    mobile_phone: '@phone',
                    email: '@email',
                    region: '@region',
                    menus: [
                        {
                            menu_name: '一级导航',
                            id: '@id',
                            code: 'Nav1',
                            children: [
                                {
                                    code: 'about',
                                    menu_url: 'views/about',
                                    access_permissions: '["about"]',
                                    children: [],
                                    menu_name: '测试1',
                                    id: '@id'
                                },
                                {
                                    code: 'home',
                                    menu_url: 'views/home',
                                    access_permissions: '["home"]',
                                    children: [],
                                    menu_name: '测试2',
                                    id: '@id'
                                }
                            ]
                        },

                    ]
                },
            }
        },
    }
] as MockMethod[]