2022 обязательно изучит Vue3.0 (настоятельно рекомендуется)

Vue.js Vuex
2022 обязательно изучит Vue3.0 (настоятельно рекомендуется)

Предложения по обучению Vue3.0

Для студентов, изучающих vue2.0

Новые функции Vue3.0

китайский документ

  • Состав Api (самое ядро)
  • изменение v-модели
  • Изменения в использовании ключевых узлов v-for
  • v-if и v-for имеют более высокий приоритет для одного и того же элемента
  • ref внутри v-for больше не регистрирует ссылочные массивы
  • Функциональные компоненты могут быть созданы только с помощью обычных функций.
  • Асинхронные компоненты должны использоватьdefineAsyncComponentСоздать метод
  • Все слоты проходят$slots
  • существуетdestroyedПараметры жизненного цикла были переименованыunmounted
  • существуетbeforeDestroyПараметры жизненного цикла были переименованыbeforeUnmount
  • ...

Преимущества и недостатки Vue3.0

преимущество:

  1. Предоставьте доступ к большинству внутренних API-интерфейсов Vue, чтобы у Vue была возможность разрабатывать крупномасштабные проекты, такие как компиляция и компиляция API-интерфейсов и т. д.
  2. встряхивание дерева веб-пакета (встряхивание дерева - это способ DCE, который может игнорировать неиспользуемый код при сборке.) Дружественная поддержка
  3. Использование Proxy для адаптивного определения переменных повышает производительность в 1,2–2 раза.
  4. ssr в 2-3 раза быстрее
  5. Плагин композиции-api можно использовать отдельно в Vue2.0 или для непосредственной разработки плагинов.
  6. Более дружественная поддержка машинописного текста
  7. Глядя в будущее: недавно инновационный сервер разработки vite от Yuxi для вас (высокопроизводительный сервер разработки, который отказывается от веб-пакета и использует платформу Koa в нижней части), синтаксис Vue3.0 используется напрямую

недостаток:

  1. Vue3 больше не будет поддерживать IE11, Vue по-прежнему поддерживает IE11 в версии 2.X, если вы хотите использовать новые функции, такие как Vue 3, вы можете дождаться версии Vue 2.7. В этом RFC объявлено, что он будет обратно совместим с версией 2.7 и перенесет некоторые новые функции 3.x, чтобы обеспечить одинаковый опыт разработки между двумя версиями.
  2. Для разработчиков, привыкших к режиму разработки Vue2.0, это увеличивает умственную нагрузку и имеет опыт в организации кода разработчика.

В то же время это также возможность улучшить возможности.Мне особенно нравится первоначальный дизайнерский замысел автора Vue: позволить разработчикам расти вместе с фреймворком.

Испытайте четыре позы Vue3.0

Теперь есть четыре позы, чтобы испытать Vue3.0портал

  • Через CDN:<script src="https://unpkg.com/vue@next"></script>

  • пройти черезCodepenбраузерная площадка

  • строительные лесаVite:

    npm init vite-app hello-vue3 # OR yarn create vite-app hello-vue3
    

Новый инструмент, разработанный Youda, призван в будущем заменить веб-пакет.Первоначальный вариант заключается в использовании импорта, который браузеры уже поддерживают ES6; при обнаружении импорта будет отправлен http-запрос для загрузки соответствующего файла, а vite перехватит эти запросы. и выполните предварительную компиляцию. Посетите длительные мероприятия по пакетированию веб-пакетов, чтобы улучшить процесс разработки.

  • строительные лесаvue-cli

    npm install -g @vue/cli # OR yarn global add @vue/cli
    vue create hello-vue3
    # select vue 3 preset
    

Глобальный API

Новый глобальный API:createApp

передачаcreateAppВозвращает экземпляр приложения, что является новой концепцией Vue3.0:

Открытьsrc/main.js

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

Экземпляр приложения предоставляет подмножество текущего глобального API. Практическое правило заключается в том, что любой API, который глобально меняет поведение Vue, теперь будет перемещен в приложение в экземпляре приложения. Вот таблица текущих глобальных API и их соответствующие API экземпляра:

2.x Глобальный API API экземпляра 3.x (app)
Vue.config app.config
Vue.config.productionTip removedудаленный
Vue.config.ignoredElements app.config.isCustomElement
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use

изучение API композиции

Официальный сайт

setup


Функция настройки — это новая опция компонента. В качестве точки входа для использования Composition API внутри компонента.

Создайте экземпляр компонента, затем инициализируйте свойства, затем вызовитеsetupфункция. это будет вbeforeCreateВызывается до хука.

setup возвращает объект. то все свойства объекта (это ответные данные) можно использовать непосредственно в шаблоне. Эквивалентно объекту, возвращаемому функцией данных в vue2.0.

App.vue

<script>
export default {
  setup () {
    return {}
  }
}
</script>

отзывчивые данные

  • ref: вы можете передать любой тип значения и вернуть реактивный и изменяемый объект ref. Объекты ref имеют одно свойство, указывающее на внутреннее значение..value, вы должны использовать его атрибут value при изменении значения
  • реактивный: принимает простой объект и возвращает реактивный прокси для этого простого объекта. Эквивалент 2.xVue.obserable()

Вкратце: реактивный отвечает за сложные структуры данных, а ref может оборачивать базовые структуры данных в реактивный.

reactive


<template>
  <div>
    <h2>{{state.count}}</h2>
    <button @click="add">计算</button>
  </div>
</template>

<script>
import { reactive } from "vue";
export default {
  setup(){
    // 响应式变量声明 reactive负责复杂数据结构,
    const state = reactive({
      count: 1
    });
    function add() {
      state.count++;
    }
    return { state, add};
  }
};
</script>

ref


<template>
  <div>
    <h2>{{state.count}}</h2>
    <h3>{{num}}</h3>
    <button @click="add">计算</button>
  </div>
</template>
<script>
import { reactive, ref } from "vue";
export default {

  setup(){
    const state = reactive({
      count: 1
    });
    const num = ref(0);
    function add() {
      state.count++;
      num.value+=2
    }
    return { state, add, num };
  }
};
</script>

Число, упакованное по ссылке, можно использовать непосредственно в шаблоне, но оно работает при изменении в js..valueАтрибуты.

toRefs


Преобразование реактивного объекта в обычный объект, где каждое свойство результирующего объекта указывает на соответствующее свойство исходного объекта.

toRefs полезен при возврате реактивных объектов из составных функций, так что потребляющие компоненты могут разлагать/рассеивать возвращаемый объект без потери реактивности:

useFeatureX.js

import {reactive} from 'vue';
export function userFeatureX(){
  const state = reactive({
    foo: 1,
    bar: 2
  })

  // 逻辑运行状态

  // 返回时转换为ref
  return state;
}

App.vue

import {toRefs} from 'vue'
export default {
  setup(){
    const state = useFeatureX();
    return {
      ...toRefs(state)
    }
  }
}

computed

Передайте функцию получения, которая возвращает объект ref, который по умолчанию нельзя изменить вручную.

import { reactive, ref, computed } from "vue";
export default {

  setup() {
    // 1.响应式变量声明 reactive负责复杂数据结构,
    const state = reactive({
      count: 1
    });
    // 2.ref可以把基本的数据结构包装成响应式
    const num = ref(0);
    // 3.创建只读的计算属性
    const computedEven1 = computed(() => state.count % 2);
    // 4.创建可读可写的计算属性
    const computedEven2 = computed({
      get:()=>{
        return state.count % 2;
      },
      set: newVal=>{
        state.count = newVal;
      }
    })

    // 事件的声明
    function add() {
      state.count++;
      num.value += 2;
    }

    function handleClick() {
      computedEven2.value = 10;
    }



    return { state, add, num, computedEven1,computedEven2,handleClick };
  }
};

watchEffect

Немедленно выполняет переданную функцию, реактивно отслеживает ее зависимости и повторно запускает функцию при изменении ее зависимостей.

const num = ref(0)

watchEffect(() => console.log(count.value))
// -> 打印出 0

setTimeout(() => {
  count.value++
  // -> 打印出 1
}, 100)
  1. Хватит слушать

    Неявная остановка

    когдаwatchEffectв компонентеsetup()Когда вызывается функция или хук жизненного цикла, слушатель связывается с жизненным циклом компонента и автоматически останавливается, когда компонент размонтирован.

    показать остановку

    В некоторых случаях значение возврата вызова также может отображаться, чтобы прекратить прослушивание.

    const stop = watchEffect(()=>{
      /*...*/
    })
    //停止侦听
    stop()
    
  2. явные побочные эффекты

    Иногда функция побочного эффекта выполняет некоторые асинхронные побочные эффекты, и эти ответы необходимо очищать, когда они становятся недействительными (т. е. состояние изменилось до завершения). Вы можете принять функцию в функции, которая прослушивает побочные эффектыonInvalidateФункция в качестве параметра используется для регистрации обратного вызова в случае сбоя очистки. Когда происходит следующее, этоОбратный вызов недействительностибудет запущено:

    • Когда побочный эффект вот-вот повторится
    • слушатель останавливается (еслиsetup()или используется в функции хука жизненного циклаwatchEffect, то при удалении компонента)

    Пример официального сайта:

    watchEffect((onInvalidate) => {
      const token = performAsyncOperation(id.value)
      onInvalidate(() => {
        // id 改变时 或 停止侦听时
        // 取消之前的异步操作
        token.cancel()
      })
    })
    

Кейс: Реализуйте эффект «анти-тряски» при вводе данных пользователем.

<template>
  <div>
    <input type="text"
           v-model="keyword">
  </div>
</template>

<script>
  import { ref, watchEffect } from 'vue'

  export default {
    setup() {
      const keyword = ref('')
      const asyncPrint = val => {
        return setTimeout(() => {
          console.log('user input: ', val)
        }, 1000)
      }
      watchEffect(
        onInvalidate => {
          //用户输入的时间间隔小于1秒,都会立刻清除掉定时,不输入结果。正因为这个,实现了用户防抖的功能,只在用户输入时间间隔大于1秒,才做打印
          const timer = asyncPrint(keyword.value)
          onInvalidate(() => clearTimeout(timer))
          console.log('keyword change: ', keyword.value)
        },
        // flush: 'pre'  watch() 和 watchEffect() 在 DOM 挂载或更新之前运行副作用,所以当侦听器运行时,模板引用还未被更新。
        //flush: 'post' 选项来定义,这将在 DOM 更新后运行副作用,确保模板引用与 DOM 保持同步,并引用正确的元素。
        {
          flush: 'post' // 默认'pre',同步'sync','pre'组件更新之前
        }
      )

      return {
        keyword
      }
    }
  }
  // 实现对用户输入“防抖”效果
</script>

watch


watchAPI полностью эквивалентен 2.xthis.$watch(так же какwatchсоответствующие варианты).watchНеобходимо прослушивать определенный источник данных и выполнять побочные эффекты в функции обратного вызова. По умолчанию используется ленивый, что означает, что обратный вызов выполняется только при изменении прослушиваемого источника.

Первый параметр, полученный функцией watch(), называется «источник данных», который может быть:

  • Функция получения, которая возвращает произвольное значение
  • Объект-оболочка (может быть ссылкой или реактивным обернутым объектом)
  • Массив, содержащий два вышеуказанных источника данных

Второй параметр — это функция обратного вызова. Функция обратного вызова будет запущена только при изменении источника данных:

  1. Слушайте один источник данных

    const state = reactive({count: 1});
    
    //侦听一个reactive定义的数据,修改count值时会触发 watch的回调
    watch(()=>state.count,(newCount,oldCount)=>{
      console.log('newCount:',newCount);  
      console.log('oldCount:',oldCount);
    })
    //侦听一个ref
    const num = ref(0);
    watch(num,(newNum,oldNum)=>{
      console.log('newNum:',newNum);  
      console.log('oldNum:',oldNum);
    })
    
  2. Прослушивание нескольких источников данных (массивов)

    const state = reactive({count: 1});
    const num = ref(0);
    // 监听一个数组
    watch([()=>state.count,num],([newCount,newNum],[oldCount,oldNum])=>{
      console.log('new:',newCount,newNum);
      console.log('old:',oldCount,oldNum);
    })
    
  3. Слушайте сложные вложенные объекты

    В нашей реальной разработке сложные данные можно увидеть повсюду, например:

    const state = reactive({
      person: {
        name: '张三',
        fav: ['帅哥','美女','音乐']
      },
    });
    watch(
      () => state.person,
      (newType, oldType) => {
        console.log("新值:", newType, "老值:", oldType);
      },
      { deep: true }, // 立即监听
    );
    

Если третий параметр не используетсяdeep:true, не может отслеживать изменения данных. Ранее мы упоминали,часы ленивы по умолчанию, тогда при каких обстоятельствах не лениво и callback-функцию можно выполнить сразу? На самом деле использование тоже очень простое, задается в третьем параметреimmediate: trueТолько что

При этом watch и watchEffect ведут себя одинаково в плане прекращения прослушивания, сброса побочных эффектов (и соответственно onInvalidate передается третьим параметром callback'а) и т.д.

<template>
  <div>
    <input type="text"
      v-model="keyword">
  </div>
</template>

<script>
import { ref, watch } from 'vue'

export default {
  setup() {
    const keyword = ref('')
    const asyncPrint = val => {
      return setTimeout(() => {
        console.log('user input: ', val)
      })
    }

    watch(
      keyword,
      (newVal, oldVal, onCleanUp) => {
        const timer = asyncPrint(keyword)
        onCleanUp(() => clearTimeout(timer))
      },
      {
        lazy: true // 默认false,即初始监听回调函数执行了
      }
    )
    return {
      keyword
    }
  }
}
</script>

крючки жизненного цикла


Компонуемый API, соответствующий жизненному циклу версии 2.x

image-20220125101342545.png

Новый тестовый компонент/components/Test.vue

<template>
  <div id="test">
    <h3>{{a}}</h3>
    <button @click="handleClick">更改</button>
  </div>
</template>

<script>
import {
  ref,
  onMounted,
  onBeforeMount,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
} from "vue";
export default {
  // 初始化数据阶段的生命周期,介于beforeCreate和created之间
  setup() {
    const a = ref(0);
    console.log("👌");
    function handleClick() {
      a.value += 1;
    }
    onBeforeMount(() => {
      console.log("组件挂载之前");
    });
    onMounted(() => {
      console.log("DOM挂载完成");
    });
    onBeforeUpdate(() => {
      console.log("DOM更新之前", document.getElementById("test").innerHTML);
    });
    onUpdated(() => {
      console.log("DOM更新完成", document.getElementById("test").innerHTML);
    });
    onBeforeUnmount(() => {
      console.log("实例卸载之前");
    });
    onUnmounted(() => {
      console.log("实例卸载之后");
    });
    return { a, handleClick };
  }
};
</script>

Согласно официальной поговорке, вам не нужно сразу во всем разбираться, но по мере того, как вы продолжаете изучать и использовать его, его эталонное значение будет становиться все выше и выше.

внедрение зависимости


provideа такжеinjectОбеспечивает внедрение зависимостей, аналогично 2.x.provide/inject. Оба могут использоваться только в текущем компоненте.setup()вызывать

App.vueпредоставить источник данных

<template>
  <div>
    <Article></Article>
  </div>
</template>

<script>
import {
  ref,
  provide
} from "vue";
import Article from "./components/Article";
export default {
  setup() {
    const articleList = ref([
      { id: 1, title: "Vue3.0学习", author: "小马哥" },
      { id: 2, title: "componsition api", author: "尤大大" },
      { id: 3, title: "Vue-router最新", author: "vue官方" }
    ]);
    /* 
      provide 函数允许你通过两个参数定义 property:
      property 的 name (<String> 类型)
      property 的 value
    */
    provide("list",articleList);
    return {
      articleList
    };
  },
  components: {
    Article
  }
};
</script>

Article.vueвводить данные

<template>
  <div>
    {{articleList[0].title}}
  </div>
</template>

<script>
import { inject } from "vue";
export default {
  setup() {
    const articleList = inject('list',[]);
    return {articleList};
  },
};
</script>

ссылки на шаблоны


При использовании API композицииreactive refsа такжеtemplate refsКонцепция была унифицирована. Чтобы получить ссылку на экземпляр элемента или компонента в шаблоне, вы можете напрямуюsetup()объявить ссылку и вернуть ее

<template>
  <div>
    <div ref='wrap'>hello vue3.0</div>
    <Article ref='articleComp'></Article>
  </div>
</template>

<script>
import {
  ref,
  onMounted,
  provide
} from "vue";
import Article from "./components/Article";
export default {
  setup() {
    const isShow = ref(true);
    const wrap = ref(null);
    const articleComp = ref(null);

    const articleList = ref([
      { id: 1, title: "Vue3.0学习", author: "小马哥" },
      { id: 2, title: "componsition api", author: "尤大大" },
      { id: 3, title: "Vue-router最新", author: "vue官方" }
    ]);
    /* 
      provide 函数允许你通过两个参数定义 property:
      property 的 name (<String> 类型)
      property 的 value
    */
    provide("list", articleList);

    onMounted(() => {
      console.log(wrap.value); //获取div元素
      console.log(articleComp.value); //获取的article组件实例对象
      
    });
    return {
      articleList,
      wrap,
      articleComp
    };
  },
  components: {

    Article
  }
};
</script>

<style scoped>
</style>

Изображение эффекта:

компонент связи

  • props
  • $emit
  • expose /ref
  • attrs
  • v-model
  • provide/inject
  • vuex
  • mitt

props

// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2"></child>
<script>
import child from "./child.vue"
import { ref, reactive } from "vue"
export default {
    setup(){
        // 创建一个响应式数据
        const msg1 = ref("这是传级子组件的信息1")
        const msg2 = reactive(["这是传级子组件的信息2"])
        return {
            msg1
            msg2
        }
    }
}
</script>

// Child.vue 接收
<script>
export default {
  props: ["msg1", "msg2"],// 如果这行不写,下面就接收不到
  setup(props) {
    console.log(props) // { msg1:"这是传给子组件的信息1", msg2:"这是传给子组件的信息2" }
  },
}
</script>

$emit

// Child.vue 派发
<template>
  // 写法一
  <button @click="$emit('myClick',123)">按钮</buttom>
</template>
<script> 
 export default {
	emits:['myClick']
	//emits:{
  //myClick:null
  //}
}

</script>

// Parent.vue 响应
<template>
    <child @myClick="onMyClick"></child>
</template>
<script setup>
  import child from "./child.vue"
const onMyClick = (msg) => {
  console.log(msg) // 这是父组件收到的信息 123
}
</script>

Большие перемены

Teleport

Телепорт похож на «любую дверь» в Дораэмоне, функция любой двери — мгновенно телепортировать людей в другое место. С этим пониманием давайте посмотрим, почему нам нужно использовать функции Teleport.Давайте рассмотрим небольшой пример: В подкомпонентахHeaderиспользуется вDialogКомпоненты, мы часто используем фактическое развитие в аналогичных обстоятельствахDialog,В настоящее времяDialogОн визуализируется в виде слоя подкомпонентов для управления позиционированием вложенных компонентов.z-indexи стили стали трудными.DialogС точки зрения восприятия пользователем, это должен быть самостоятельный компонент, а DOM, смонтированный компонентом верхнего уровня Vue, должен быть полностью отделен от структуры dom, при этом также может использоваться состояние в компоненте Vue (dataилиprops) стоимость. Проще говоря,то есть хотите продолжать использовать внутри компонентаDialog, и надеемся, что визуализированная структура DOM не вложена в DOM компонента.. На данный момент нам нужен Телепорт для игры, мы можем использовать<Teleport>пакетDialog, в этот момент устанавливается портал, который можетDialogВизуализированный контент доставляется в любое указанное место. Далее, давайте возьмем небольшой пример, чтобы увидеть, как используется Телепорт.

Мы надеемся, что компоненты dom и верхнего уровня, визуализируемые Dialog, являются братьями и сестрами, т.е.index.htmlФайл определяет элемент для монтажа:

<body>
  <div id="app"></div>
  <div id="dialog"></div>
</body>

определитьDialogкомпонентыDialog.vue, обрати внимание наtoсвойства такие же, как и вышеidСелекторы те же:

<template>
  <teleport to="#dialog">
    <!-- 即希望继续在组件内部使用Dialog, 又希望渲染的 DOM 结构不嵌套在组件的 DOM 中。 此时就需要 Teleport 上场,
      我们可以用<Teleport>包裹Dialog, 此时就建立了一个传送门,可以将Dialog渲染的内容传送到任何指定的地方 -->
    <div class="dialog">
      <div class="dialog_wrapper">
        <div class="dialog_header">
          <h3>我是弹框 {{ count }}</h3>
        </div>
      </div>
    </div>
  </teleport>
</template>

<script>
import { reactive, toRefs } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0,
    })

    return {
      ...toRefs(state),
    }
  },
}
</script>

<style lang="less" scoped></style>

Suspense

экспериментальный

Приостановка — это экспериментальная новая функция, и ее API может быть изменен в любое время. Настоящим объявляется, чтобы сообщество могло оставить отзыв о текущей реализации.

Не используйте в производственной среде

Должен<suspense>Компоненты предоставляют еще одно решение, которое позволяет поднимать ожидающие процессы для обработки в дереве компонентов, а не в одном компоненте.

Принесите дваslotсоответственноdefault、fallback. Как следует из названия, когда загружаемый компонент не соответствует состоянию,Suspenseвернется кfallbackСостояние не отображается, пока загруженный компонент не удовлетворяет условию.

Suspense.vue

<template>
  <button @click="loadAsyncComponent">点击加载异步组件</button>
  <Suspense v-if="loadAsync">
    <template #default>
      <!-- 加载对应的组件 -->
      <MAsynComp></MAsynComp>
    </template>
    <template #fallback>
      <div class="loading"></div>
    </template>
  </Suspense>
</template>

<script>
import { ref, defineAsyncComponent } from 'vue'

export default {
  components: {
    MAsynComp: defineAsyncComponent(() => import('./AsynComp.vue')),
  },
  setup() {
    const loadAsync = ref(false)
    const loadAsyncComponent = () => {
      loadAsync.value = true
    }
    return {
      loadAsync,
      loadAsyncComponent,
    }
  },
}
</script>

<style lang="less" scoped>

button {
  padding: 12px 12px;
  background-color: #1890ff;
  outline: none;
  border: none;
  border-radius: 4px;
  color: #fff;
  cursor: pointer;
}
.loading {
  position: absolute;
  width: 36px;
  height: 36px;
  top: 50%;
  left: 50%;
  margin: -18px 0 0 -18px;
  background-image: url('../assets/loading.png');
  background-size: 100%;
  animation: rotate 1.4s linear infinite;
}
@keyframes rotate {
  from {
    transform: rotate(0);
  }
  to {
    transform: rotate(360deg);
  }
}
</style>

AsynComp.vue

<template>
  <h1>this is async component</h1>
</template>

<script>
import { setup } from 'vue'
export default {
  name: 'AsyncComponent',
  async setup() {
    const sleep = (time) => {
      return new Promise((reslove, reject) => {
        setTimeout(() => {
          reslove()
        }, time)
      })
    }
    await sleep(3000) //模拟数据请求
  },
}
</script>

Fragments

В компонентах Vue3.0 может быть разрешено несколько корневых компонентов, что позволяет избежать многократного ненужного рендеринга div.

<template>
  <div>头部</div>
  <div>内容</div>
</template>

Преимущества этого:

  • гораздо меньше бессмысленных div
  • Он может обеспечить горизонтальную рекурсию, что очень полезно для реализации древовидных компонентов.

emits

  • излучает может быть массивом или объектом
  • Инициировать пользовательские события
  • Позволяет нам настраивать и проверять события, если emits является объектом. Функция проверки должна возвращать логическое значение, чтобы указать, является ли параметр события допустимым.

Emits.vue

<template>
<div>
  <button @click="$emit('submit',{username:'xiaomage',password:'123'})">自定义事件</button>
  </div>
</template>

<script>
  export default {
    // emits:['submit'],//可以是数组
    emits: {
      submit: payload => {
        if(payload.username && payload.password){
          return true;
        }else{
          console.warn('无效的payload,请检查submit事件');
          return false
        }
      }
    },
    setup() {
      return {};
    }
  };
</script>

<style scoped>
</style>

App.vue

<Emits @submit="submitHandle"></Emits>
<script>
  import Emits from "./components/Emits";
  export default{
    components:{
      Emits
    },
    setup(){
      function submitHandle(payload) {
        console.warn("自定义事件触发",payload);
      }
      return {

      }
    }
  }

</script>

Показать результаты:

Global Vue API изменен на экземпляр приложения

Об этом было сказано выше, поэтому я не буду вдаваться в подробности.

API может выполнять оптимизацию Tree Shakeable

В vue2.0 есть много глобальных API, которые напрямую навешиваются на конструктор Vue как статические функции.Вы должны вручную манипулировать DOM, и вы столкнетесь со следующими паттернами. Если мы не использовали их в коде, мы сформируем то, что мы называем «мертвым кодом», а «мертвый код», вызванный таким глобальным API, не может использовать вебапки.tree-shakingСделайте «устранение мертвого кода».

import Vue from 'vue'
Vue.nextTick(()=>{
  //一些和DOM相关的东西
})

Поэтому в vue3.0 были внесены соответствующие изменения, чтобы выделить их в независимые функции, чтобы древовидная оптимизация инструмента упаковки могла исключить эти «мертвые коды». Глобальные API теперь доступны только как именованные экспорты сборок модуля ES. Например, наш предыдущий фрагмент теперь должен выглядеть так

import {nextTick} from 'vue'
nextTick(()=>{
  //一些和DOM相关的东西
})

Затронутый API


Эти глобальные API в Vue2.x затронуты этим изменением:

  • Vue.nextTick
  • Vue.observable (заменен на Vue.reactive)
  • Vue.version
  • Vue.compile (только при полной сборке)
  • Vue.set (только совместимая версия)
  • Vue.delete (только совместимая версия)

TreeShaking.vue

<template>
<div >
  <hr />摇树优化,把没引入的不必要的代码进行优化
  <div id='name'>小马哥</div>
  <h3 ref='myMsg'>{{msg}}</h3>
  <button @click="changeMsg('hai!')">改变</button>
  </div>
</template>

<script>
  import { ref, nextTick } from "vue";
  export default {
    setup() {
      const msg = ref("hello!");
      const myMsg = ref(null);
      async function changeMsg(newV) {
        msg.value = newV;
        // console.log(myMsg.value.innerText); //直接获取DOM还是以前的
        // nextTick返回了promise对象
        await nextTick();
        console.log(myMsg.value.innerText);
      }
      return {
        msg,
        myMsg,
        changeMsg
      };
    }
  };
</script>

Синтаксис именованного слота

В Vue2.x способ написания именованного слота:

<!--  子组件中:-->
<slot name="title"></slot>

В родительском компоненте используйте:

<template slot="title">
    <h1>歌曲:《孤勇者》</h1>
<template>

если мы хотимЧтобы привязать данные к слоту, вы можете использовать слоты с областью видимости., реализуется следующим образом:

// 子组件
<slot name="content" :data="data"></slot>
export default {
    data(){
        return{
            data:["走过来人来人往","不喜欢也得欣赏","陪伴是最长情的告白"]
        }
    }
}
<!-- 父组件中使用 -->
<template slot="content" slot-scope="scoped">
    <div v-for="item in scoped.data">{{item}}</div>
<template>

Именованные слоты и слоты с заданной областью используются отдельно в Vue2.x.slotа такжеslot-scopeДля достижения в Vue3.0 будетslotа такжеslot-scopeСогласие на использование суммируется. В Vue3.0v-slot:

<!-- 父组件中使用 -->
 <template v-slot:content="scoped">
   <div v-for="item in scoped.data">{{item}}</div>
</template>

<!-- 也可以简写成: -->
<template #content="{data}">
    <div v-for="item in data">{{item}}</div>
</template>

использование v-модели на компонентах


После выпуска Vue 2.0 разработчики используютv-modelДиректива должна использоваться какvalueопора Если разработчикам нужно использовать другие реквизиты для других целей, они должны использоватьv-bind.sync. Кроме того, посколькуv-modelа такжеvalueПричина этих жестко запрограммированных отношений между ними поднимает вопрос о том, как обрабатывать нативные и пользовательские элементы.

В Vue 2.2 мы представилиmodelПараметры компонента, позволяющие использовать настройку компонента дляv-modelреквизит и мероприятия. Тем не менее, это по-прежнему позволяет использовать только один компонентmodel.

В Vue 3 API двусторонней привязки данных был стандартизирован, что снижает потребность разработчиков в использованииv-modelОбфускация при директиве и использованииv-modelможет быть более гибким при заказе.

2.x синтаксис


В 2.x использовать на компонентахv-modelэквивалентно связываниюvalueопора иinputмероприятие:

<ChildComponent v-model="pageTitle" />

<!-- 简写: -->

<ChildComponent :value="pageTitle" @input="pageTitle = $event" />

Если вы хотите изменить имя свойства или события на другое, вам нужноChildComponentкомпонент добавленmodelОпции:

<!-- ParentComponent.vue -->

<ChildComponent v-model="pageTitle" />
// ChildComponent.vue

export default {
  model: {
    prop: 'title',
    event: 'change'
  },
  props: {
    // 这将允许 `value` 属性用于其他用途
    value: String,
    // 使用 `title` 代替 `value` 作为 model 的 prop
    title: {
      type: String,
      default: 'Default title'
    }
  }
}

Итак, в этом примереv-modelАббревиатура выглядит следующим образом:

<ChildComponent :title="pageTitle" @change="pageTitle = $event" />

использоватьv-bind.sync

В некоторых случаях нам может потребоваться «двухсторонний привязку» (кроме предыдущегоv-modelсвязанная опора). Для этого мы рекомендуем использоватьupdate:myPropNameВыбросить событие. Например, для предыдущего примера сtitleреквизитChildComponent, мы можем сообщить о намерении присвоить новое значение родителю следующим образом:

this.$emit('update:title', newValue)

Родитель может прослушивать это событие и при необходимости обновлять свойство локальных данных. Например:

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

Для удобства мы можем использовать.syncмодификатор для сокращения следующим образом:

<ChildComponent :title.sync="pageTitle" />

3.x синтаксис


В 3.x на пользовательских компонентахv-modelэквивалентно прохождениюmodelValueопора и получение брошенногоupdate:modelValueмероприятие:

<ChildComponent v-model="pageTitle" />

<!-- 简写: -->

<ChildComponent
  :modelValue="pageTitle"
  @update:modelValue="pageTitle = $event"
/>

Изменения API функции рендеринга

  • hТеперь импортируется глобально, а не передается в качестве параметра функции рендеринга.
  • Параметры функции рендеринга более согласованы между компонентами с отслеживанием состояния и функциональными компонентами.
  • vnode теперь является плоской опорной структурой

renderФункция автоматически получитhфункция (которая является псевдонимом для createElement) в качестве параметра

//vue2.x
export default{
  render(h){
    return h('div')
  }
}
//vue3 渲染
import { h } from 'vue'

export default {
  render() {
    return h('div')
  }
}

Например:

<template>
  <div>
    <RenderComp v-model='title'>
      <template v-slot:default>
        <!-- 默认插槽 -->
        头部
      </template>
      <template v-slot:content>
        <!-- 具名插槽 -->
        内容
      </template>
  </RenderComp>
  </div>
</template>
<script>
  import {
    ref,
    h
  } from "vue";
  export default {
    components: {
      RenderComp: {
        props: {
          modelValue: {
            type: String,
            default: ''
          },
        },
        setup(props,{attrs,slots,emit}) {
          // 以前得通过$scopedSlots获取对应的插槽
          console.log(slots.default()); //获取默认插槽
          console.log(slots.content()); //获取名字为content的插槽
          function changeTitle(newV) {
            emit('update:modelValue','哈哈哈');
          }
          return () => h("div", {}, [h("div", {
            onClick:changeTitle,
          },[
            `渲染函数api:${props.modelValue}`,
            slots.default(),
            slots.content()
          ])]);
        }
      }
    },
    setup(props) {
      const title = ref("双向数据绑定");
      return {
        title
      };
    }
  };
</script>

В то же время продемонстрировал$scopedSlotsсвойство удалено, все слоты пройдены$slotsвыставлен как функция

Создавайте функциональные компоненты, используя обычные функции

  • В 3.x прирост производительности функциональных компонентов 2.x незначителен, поэтому мы рекомендуем использовать только stateful-компоненты.
  • Функциональные компоненты могут использовать только приемpropsа такжеcontextнормальное создание функции (т.е.:slots,attrs,emit).
  • Критические изменения:functionalатрибут в компоненте одного файла (SFC)<template> удаленный
  • Критические изменения:{ functional: true }Добавлена ​​возможность создавать компонент через функцию.удаленный

В vue2.0 функциональные компоненты имеют две основные цели:

  • Улучшена оптимизация производительности, поскольку они инициализируются быстрее, чем компоненты с отслеживанием состояния.
  • Можно вернуть несколько корневых узлов

Однако в Vue 3 производительность компонентов с отслеживанием состояния улучшилась до незначительного уровня. Кроме того, компоненты с отслеживанием состояния теперь включают возможность возвращать несколько корневых узлов.

Таким образом, единственные оставшиеся варианты использования функциональных компонентов — это простые компоненты, такие как те, которые создают динамические заголовки. В противном случае рекомендуется использовать компоненты с отслеживанием состояния как обычно.

Резюме: в не особых случаях официальный сайт по-прежнему рекомендует использовать компоненты с отслеживанием состояния.

Functional.vue

import { h } from 'vue'

const DynamicHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']

export default DynamicHeading
<Functional level='3'>动态标题</Functional>

Вы можете пройти разные уровни, чтобы настроить разные названия серии h.

Асинхронные изменения компонентов

  • новыйdefineAsyncComponentВспомогательный метод, явно определяющий асинхронный компонент
  • componnetопция с именемloader
  • Функция загрузчика не принимается сама по себеresolveа такжеrejectпараметр, должен возвращать обещание

2.x


Раньше асинхронные компоненты создавались путем определения компонента как функции, возвращающей обещание, например:

const asyncPage = () => import('./NextPage.vue')

Для синтаксиса компонента более высокого порядка с параметрами:

const asyncPage = {
  component: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  error: ErrorComponent,
  loading: LoadingComponent
}

3.x


В vue3, поскольку функциональные компоненты определяются как чистые функции, необходимо обернуть определение асинхронного компонента в новыйdefineAsyncComponentПомощники для явного определения компонентов

import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'

// 不带选项的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))

// 带选项的异步组件
const asyncPageWithOptions = defineAsyncComponent({
  loader: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})

пользовательская директива

API переименован для лучшего согласования с жизненным циклом компонента.

  • связать →beforeMount
  • вставил →mounted
  • beforeUpdate:новый! Это вызывается перед обновлением самого элемента, очень похоже на хуки жизненного цикла компонента.
  • Обновление → удаление! Слишком много общего для обновления, так что это лишнее, пожалуйста, используйтеupdated
  • компонентОбновлено →updated
  • **beforeUnmount ** 新的Подобно хукам жизненного цикла компонента, он будет вызываться перед выгрузкой элемента.
  • unbind -> unmounted

Например:

main.js

const app = createApp(App);
// 创建自定义指令
app.directive('highlight',{
  // 指令 也拥有一组生命周期钩子
  // 1.在绑定元素的父组件挂载之前调用
  beforeMount(el,binding,vnode){
    el.style.background = binding.value;
  },
})

App.vue

<p v-highlight="'red'">自定义指令</p>

Изменения анимированных переходов

  • v-enter->v-enter-from
  • v-leave->v-leave-from

В версии vue2.x

Удалить API

2.x, поддержкаkeyCodesкак модификацияv-onметод метода

<!-- 键码版本 -->
<input v-on:keyup.13="submit" />

<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />

vue3.x

Для любых ключей, которые будут использоваться в качестве модификаторов, рекомендуется использовать имена с регистром кебаб (тире).

<!-- Vue 3 在 v-on 上使用 按键修饰符 -->
<input v-on:keyup.delete="confirmDelete" />

Так это значитconfig.keyCodesТакже теперь устарело и больше не поддерживается.

$on,$offа также$onceМетоды экземпляра были удалены, а экземпляры приложений больше не реализуют интерфейс запуска событий.

Фильтры были удалены из Vue 3.0 и больше не поддерживаются. Вместо этого мы рекомендуем заменить их вызовами методов или вычисляемыми свойствами.