[Доставка книги] Интерфейсный сериал, 14 серий - Vue3-setup

Действие по доставке книг: выберите 1 одноклассника и отправьте его бесплатно.
Крайний срок: 19:00 18 мая 2023 г.
Примите участие в области комментариев внизу
и поговорите : пожалуйста, поделитесь своими успехами в чтении в комментариях.

Фронтенд-инжиниринг: дизайн и практика на основе Vue.js 3.0

f55af1f836ff577901e4c36fe12ba380.jpeg

Просмотры страниц (Page View, PV) и посетители (уникальные посетители, UV)

// index.vue
<template>
  <a-button @click="onClick">查询</a-button>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'

@Component({})
export default class Page extends Vue {
  // PV 埋点
  mounted() {
    window.DATracker.track('page_id', {})
  }

  // 交互埋点
  onClick() {
    window.DATracker.track('event_id', {})
  }
}
</script>

Для скрытой точки взаимодействия со страницей  trackEvent метод может быть инкапсулирован унифицированным способом:

// utils/track.ts

/**
 * 日志上报
 * @param id 事件id
 * @param data 上报数据
 */
export const trackEvent = (id, data = {}) => {
  window.DATracker.track(id, data)
}

Обработка событий щелчка относительно проста, и каждый щелчок запускает отчет о данных:

// src/directives/track/click.js
import { sendUBT } from "../../utils"

export default class Click {
  add(entry) {
    // console.log("entry", entry);
    const traceVal = entry.el.attributes["track-params"].value
    const traceKey = entry.el.attributes["trace-key"].value
    const { clickAction, detail } = JSON.parse(traceVal)
    const data = {
      action: clickAction,
      detail,
    }
    entry.el.addEventListener("click", function() {
      console.log("上报点击埋点", JSON.parse(traceVal))
      console.log("埋点key", traceKey)
      sendUBT(traceKey, data)
    })
  }
}
  • v-track:click|exposure

  • v-track:exposure

В виде автоматизации, как добавить эти закопанные точки?

8785d5cf8a7eeedc95fe7aafeb6d2ff86.png
изображение.png
SysLoginController
SysLoginService
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author
 *
 * 跨域访问配置
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .maxAge(3600);
    }

}
server: {
        // host: '0.0.0.0',
        // 反向代理解决跨域
        proxy: {
            [env.VITE_APP_PORT]: {
                target: 'htts://',
                changeOrigin: true,
                rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_PORT), '')
            }
        }
        // open: true, // 运行是否自动打开浏览器
        // port: Number(env.VITE_APP_PORT),
    },
// vue.config.js

const env = process.env;

module.exports = {
  devServer: {
    proxy: {
      [env.VITE_APP_PORT]: {
        target: 'htts://example.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_PORT), '')
      }
    },
    open: true, // 运行是否自动打开浏览器
    port: Number(env.VITE_APP_PORT),
  },
};
89f16d3062a4262176013d3f453ace0c.png
изображение.png
37c273ccc6bb0a78badddc4be5cf59f8.png
изображение.png
server: {
        // host: '0.0.0.0',
        // 反向代理解决跨域
        proxy: {
            '/api': {
                target: 'http://xxx:1606',
                changeOrigin: true,
                secure: false,
                rewrite: (path) => path.replace(/^\/api/, '')
            }
        }
        // open: true, // 运行是否自动打开浏览器
        // port: Number(env.VITE_APP_PORT),
    },
css: {
        // CSS 预处理器
        preprocessorOptions: {
            //define global scss variable
            scss: {
                javascriptEnabled: true,
                additionalData: `@use "@/styles/variables.scss" as *;`
            }
        }
    },

Это элемент конфигурации для предварительной обработки кода CSS. В этой конфигурации мы можем указать используемый препроцессор и параметры его конфигурации. Среди них scssпопулярный препроцессор CSS. javascriptEnabled: trueУказывает, что синтаксис JavaScript доступен в SCSS. additionalDataАтрибуты позволяют нам определять глобальные переменные SCSS, которые будут включены во все файлы SCSS. В этом примере @useдиректива импортирует variables.scssфайл и делает все его содержимое доступным в виде глобальных переменных.

Метод Vue.js toRaw— это метод глубокого обхода реактивного объекта и возврата его нереактивного примитивного значения. Его можно использовать для доступа к необработанным данным в реактивных объектах, созданных с помощью reactive, refи в Vue 3 .computed

В Vue.js при работе с отзывчивым объектом Vue.js заключает его в прокси-объект, чтобы отслеживать изменения в свойствах объекта и при необходимости обновлять представление. Но бывают случаи, когда нам нужно напрямую обращаться к исходному значению объекта, а не к его прокси-объекту. Затем вы можете использовать toRawметод, чтобы получить исходное значение.

Например:

import { reactive, toRaw } from 'vue'

const state = reactive({ count: 0 })

console.log(state.count) // 0
console.log(toRaw(state).count) // 0

state.count++

console.log(state.count) // 1
console.log(toRaw(state).count) // 0

В приведенном выше примере мы создали реактивный объект, содержащий счетчик state. Затем мы получаем доступ к значению счетчика, используя state.countи соответственно toRaw(state).count. Когда мы увеличиваем счетчик, state.countзначение обновляется до 1, но toRaw(state).countостается при начальном значении 0, потому что оно не управляется системой реактивности.

В этом коде переменная paramsкопируется с помощью toRawфункции, а затем Object.assignприсваивается новому объекту с помощью метода . Полученный объект paramsInitявляется paramsмелкой копией .

Цель использования toRaw— создать копию объекта без соответствующих свойств. Другими словами, если paramsэто адаптивный объект (например, reactiveсозданный с помощью функции Vue.js), результирующий paramsInitобъект не будет реагировать.

В JavaScript использование Object.assign({}, obj)— это распространенный способ создания поверхностных копий объектов. Он создает новый пустой объект и objкопирует все перечисляемые собственные свойства исходного объекта ( ) в целевой объект (который является пустым объектом). Object.assignПервый параметр {}указывает, что мы создаем новый пустой объект, который будет использоваться в качестве целевого объекта.

В целом, этот код создает paramsнеотзывчивую копию и присваивает ее новой переменной paramsInit.

events
size-change page-size 改变时触发
current-change current-page 改变时触发
prev-click 用户点击上一页按钮改变当前页时触发
next-click 用户点击下一页按钮改变当前页时触发
background 是否为分页按钮添加背景色 
page-size / v-model:page-size 每页显示条目个数
default-page-size 每页显示条目数的初始值
total 总条目数
page-count 总页数, total 和 page-count 设置任意一个就可以达到显示页码的功能;如果要支持 page-sizes 的更改,则需要使用 total 属性
pager-count 设置最大页码按钮数。 页码按钮的数量,当总页数超过该值时会折叠
current-page / v-model:current-page 当前页数
default-current-page 当前页数的初始值
layout 组件布局,子组件名用逗号分隔
page-sizes 每页显示个数选择器的选项设置
popper-class 每页显示个数选择器的下拉框类名
prev-text 替代图标显示的上一页文字
prev-icon 上一页的图标, 比 prev-text 优先级更高
next-text 替代图标显示的下一页文字
next-icon 下一页的图标, 比 next-text 优先级更高
disabled 是否禁用分页
hide-on-single-page 只有一页时是否隐藏
warning
total 和 page-count 必须传一个,不然组件无法判断总页数;优先使用 page-count;
如果传入了 current-page,必须监听 current-page 变更的事件(@update:current-page),否则分页切换不起作用;
如果传入了 page-size,且布局包含 page-size 选择器(即 layout 包含 sizes),必须监听 page-size 变更的事件(@update:page-size),否则分页大小的变化将不起作用。

Если вы хотите отслеживать изменения текущей страницы и размера страницы, лучше использовать двустороннюю привязку v-model.

определяет pagerконстанту с именем .

computed()Функции используются для создания вычисляемых свойств. В этом примере computed()вычисляемое свойство создается путем передачи объекта в функцию. Этот объект имеет два метода: getи set.

get()Метод возвращает props.modelValue, свойство компонента modelValue, как значение вычисляемого свойства.

set()метод принимает один параметр valueи при вызове использует emit()метод для запуска события для обновления modelValueсвойства. Имя события 'update:modelValue', которое defineEmits()определяется функцией.

При изменении значения вычисляемого свойства set()будет вызываться метод и 'update:modelValue'инициироваться событие для обновления modelValueсвойства, поэтому элементы, привязанные к pagerвычисляемому свойству в шаблоне компонента, будут автоматически реагировать на обновление.

определяет emitконстанту с именем .

defineEmits()Функции используются для безопасного определения событий, которые может генерировать компонент. В этом примере defineEmits()определены два события: 'change'и 'update:modelValue', которые представляют changeсобытие триггера компонента и modelValueсобытие обновления свойства соответственно.

defineEmits()Функция принимает общий параметр, который указывает все определенные события и их типы параметров. В этом примере общий параметр указывает, что у changeсобытия нет параметров, а update:modelValueу события есть valueпараметр типа any.

Поэтому, когда компоненты используют emit()метод для запуска этих событий, TypeScript или Vue.js проверяют имя события и тип параметра, тем самым повышая надежность и удобство сопровождения кода.

Константа с именем определяется функциями withDefaults()и .defineProps()props

defineProps()Функция создает новый Propsобъект типа, где Propsпредставлены свойства (то есть реквизиты) компонента Vue.js.

withDefaults()Функция принимает два аргумента: объект, в который должны быть включены значения по умолчанию, и второй объект, содержащий значения по умолчанию. В этом случае свойство withDefaults()по умолчанию modelValue, pageSizesмассив по умолчанию и layoutстрока по умолчанию объединяются в propsобъект.

modelValueЗначение по умолчанию для — это {}функция, которая возвращает пустой объект, а это означает, что если никакое свойство не передается явно modelValue, по умолчанию будет пустой объект.

Опять же, pageSizesзначением по умолчанию для является [20, 50, 100, 150]функция, возвращающая массив. Это означает, что если никакое свойство не передано явно pageSizes, по умолчанию будет массив [20, 50, 100, 150].

Наконец, layoutзначение по умолчанию для — это строка 'total, sizes, prev, pager, next, jumper', представляющая собой список компонентов, разделенных запятыми, которые составляют пользовательский интерфейс подкачки.

разбиение на страницы

Пагинация

withDefaultsэто служебная функция в Vue 3 для слияния объектов опций. Он может получить объект опций по умолчанию и новый объект опций, а затем объединить их в новый объект опций.

Образец кода:

import { withDefaults } from 'vue';

const defaultOptions = {
  color: 'blue',
  fontSize: 14,
};

const options = withDefaults(defaultOptions, {
  color: 'red',
});

console.log(options); // 输出 { color: 'red', fontSize: 14 }

В приведенном выше примере мы определили defaultOptionsобъект с именем по умолчанию. Затем мы вызываем withDefaultsфункцию, передавая ее в качестве первого аргумента и новый объект параметров в качестве второго аргумента. Поскольку новый объект параметров имеет свойство, идентичное объекту параметров по умолчанию color, он переопределяет значение свойства в объекте параметров по умолчанию. Однако, поскольку новый объект параметров не имеет определенного свойства fontSize, он получит это свойство из объекта параметров по умолчанию. Окончательный объект параметров будет содержать эти два свойства, как console.logпоказано в распечатанных результатах в примере кода.

d700efe55b226eb5e2f77d1637772dd1.png
изображение.png

Интернационализация Vue3

npm я vue-i18n --сохранить

npm i vue-i18n@next

Создайте новый файл zh-CN.js, например, для хранения китайского языка.

const Settings = {
  settings: '设置',
  search: '搜索',
  airplaneMode: '飞行模式',
  wlan: '无线局域网',
  bluetooth: '蓝牙'
}

const Maps = {
  searchPlaceHolder: '搜索地点或地址'
}
// 按功能模块导出
export default {
  Settings,
  Maps
}

Создайте новый en-US.js для хранения английского языка, например

const Settings = {
  settings: 'Settings',
  search: 'Search',
  airplaneMode: 'Airplane Mode',
  wlan: 'WLAN',
  bluetooth: 'Bluetooth',
}

const Maps = {
  searchPlaceHolder: 'search'
}

export default {
  Settings,
  Maps
}
// 国际化多语言
import i18n from './locales/index'
app.use(i18n).mount('#app')

<div class="page-title">{
    
    { $t("Settings.settings") }}</div>

<template>
  <button @click="changeLanguage">change language</button>
</template>

<script lang="ts">
import { defineComponent  } from "vue";
import { useI18n } from "vue-i18n";

export default defineComponent({
  name: "App",
  components: {},
  setup() {
    const { locale } = useI18n({ useScope: "global" });
    function changeLanguage() {
      locale.value = "en-US"; // 切换成英文
    }
    return {
      changeLanguage,
    };
  },
});
</script>

Использовать синтаксический сахар настройки

<script setup> Это синтаксический сахар времени компиляции, использующий комбинированный API в однофайловом компоненте (SFC)
для решения проблемы, заключающейся в том, что установка в Vue3.0 должна быть громоздкой для предоставления объявленных переменных, функций и контента, введенного импортом. наружу через возврат, прежде чем его можно будет <template/>использовать.

<script setup>
//import引入的内容
import { getToday } from './utils'  
// 变量
const msg = 'Hello!'
// 函数
function log() {
  console.log(msg)
}
</script>
//在template中直接使用声明的变量、函数以及import引入的内容
<template>
  <div @click="log">{
    
    { msg }}</div>
   <p>{
    
    {getToday()}}</p>
</template>

<script setup>Код в синтаксическом сахаре будет скомпилирован в  setup() содержимое функции компонента, его можно использовать без раскрытия объявленных переменных, функций и содержимого, введенного импортом через возврат, <template/>и нет необходимости писатьexport default{}

<script setup>Код в синтаксическом сахаре будет скомпилирован в  setup() содержимое функции компонента. Это означает, что  <script> вместо того, чтобы выполняться только один раз при первом введении компонента, <script setup> код будет выполняться каждый раз при создании экземпляра компонента.

<script>
  console.log('script');//多次实例组件,只触发一次
  export default {
      setup() {
          console.log('setupFn');//每次实例化组件都触发和script-setup标签一样
      }
  }
  </script>

Тег script-setup в конечном итоге будет скомпилирован в setup()содержимое функции.Каждый раз, когда создается экземпляр компонента, функция установки создается один раз. Функция настройки в теге скрипта также одинакова.Каждый раз, когда вы создаете экземпляр компонента, вы создаете экземпляр функции настройки один раз, но настройку тега скрипта необходимо записать в экспорт по умолчанию {}, а снаружи выполняется только один раз, когда он вводится впервые.

<script setup>Импортированные компоненты будут автоматически зарегистрированы

<script setup>
import MyComponent from './MyComponent.vue'
//components:{MyComponent}  不需要注册直接使用
</script>
<template>
  <MyComponent />
</template>

Коммуникация компонентов: <script setup>необходимо использовать  defineProps и  defineEmits API вместо реквизита и испускает в

defineProps и  defineEmitsимеют полный вывод типа и  <script setup> доступны непосредственно в

defineProps вместо props получает данные, переданные родительским компонентом (родительский компонент передает параметры дочернему компоненту)

<template>
  <div>父组件</div>
  <Child :title="msg" />
</template>
<script setup>
import {ref} from 'vue'
import Child from './child.vue'
const msg = ref('父的值')  //自动返回,在template直接解套使用
</script>

<template/> Реквизиты, переданные родительским компонентом, могут быть напрямую использованы в

<script-setup> Необходимо получить реквизиты, переданные родительским компонентом через props.xx.

<template>
  <div>子组件</div>
  <div>父组件传递的值:{
    
    {title}}</div>
</template>
<script setup>
//import {defineProps} from 'vue'   不需要引入
//语法糖必须使用defineProps替代props
const  props = defineProps({
  title: {
    type: String
  }
});
//script-setup 需要通过props.xx获取父组件传递过来的props
console.log(props.title) //父的值
</script>

defineEmit заменяет emit, дочерний компонент передает данные родительскому компоненту (дочерний компонент выставляет данные наружу)

<template>
  <div>子组件</div>
  <button @click="toEmits">子组件向外暴露数据</button>
</template>
<script setup>
import {ref} from 'vue'
const name = ref('我是子组件')
//1、暴露内部数据
const  emits = defineEmits(['childFn']);
const  toEmits = () => {
  //2、触发父组件中暴露的childFn方法并携带数据
  emits('childFn',name)
}
</script>

父组件代码:

<template>
  <div>父组件</div>
  <Child  @childFn='childFn' />
  <p>接收子组件传递的数据{
    
    {childData}} </p>
</template>
<script setup>
import {ref} from 'vue'
import Child from './child.vue'
    
const childData = ref(null)    
const childFn=(e)=>{
    console.log('子组件触发了父组件childFn,并传递了参数e')
    childData=e.value
}    
       
</script>

<pagination v-model="pager" @change="getLists" />

<script setup>Необходимо активно раскрывать свойства дочернего компонента родительскому компоненту:

Для используемых  компонентов <script setup> родительский компонент не может передать ref или  $parent получить данные ответа, такие как ref дочернего компонента, и должен быть активно представлен через defineExpose.

<script setup>
import { ref } from 'vue'
const a = 1
const b = ref(2)
//主动暴露组件属性
defineExpose({
  a,
  b
})
</script>

父组件代码:

<template>
  <div>父组件</div>
  <Child  ref='childRef' />
  <button @click='getChildData'>通过ref获取子组件的属性 </button>
</template>
<script setup>
import {ref} from 'vue'
import Child from './child.vue'
const childRef= ref()  //注册响应数据  
const getChildData =()=>{
  //子组件接收暴露出来得值
  console.log(childRef.value.a) //1
  console.log(childRef.value.b) //2  响应式数据
}    
</script>

useSlots И  useAttrs ( используйте меньше , так как большинство людей разрабатывает в режиме SFC, слот можно отрисовать <template/>через метку)<slot/>

Если вам нужно script-setupиспользовать slotsи attrsв необходимости заменить на useSlotsиuseAttrs

Необходимо импортировать:import { useSlots ,useAttrs } form 'vue'

<template/>Доступ удобнее через и $slots( $attrsattrs используется для получения параметров/методов, передаваемых в подкомпоненты в родительском компоненте, которые не являются пропсами, attrs используется для получения параметров/методов, передаваемых в подкомпоненты, которые являются не реквизит в родительском компоненте, attrs используется для получения параметров/методов, не являющихся реквизитами, переданных дочерним компонентам в родительском компоненте, слоты могут получить объект виртуального дома, переданный слотом в родительском компоненте, это не должно быть полезно в режиме SFC, и он больше используется в JSX/TSX)

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

<template>
  <Child msg="非porps传值子组件用attrs接收" >
    <!-- 匿名插槽 -->
    <span >默认插槽</span>
    <!-- 具名插槽 -->
    <template #title>
      <h1>具名插槽</h1>
    </template>
    <!-- 作用域插槽 -->
    <template #footer="{ scope }">
      <footer>作用域插槽——姓名:{
    
    { scope.name }},年龄{
    
    { scope.age }}</footer>
    </template>
  </Child>
</template>
<script setup>
// 引入子组件
import Child from './child.vue'
</script>

Сборка:

<template>
  <!-- 匿名插槽 -->
  <slot />
  <!-- 具名插槽 -->
  <slot name="title" />
  <!-- 作用域插槽 -->
  <slot name="footer" :scope="state" />
  <!-- $attrs 用来获取父组件中非props的传递到子组件的参数 -->
  <p>{
    
    { attrs.msg == $attrs.msg }}</p>
  <!--true  没想到有啥作用... -->
  <p>{
    
    { slots == $slots }}</p>
</template>
<script setup>
import { useSlots, useAttrs, reactive, toRef } from 'vue'
const state = reactive({
  name: '张三',
  age: '18'
})
const slots = useSlots()
console.log(slots.default()); //获取到默认插槽的虚拟dom对象
console.log(slots.title());   //获取到具名title插槽的虚拟dom对象
// console.log(slots.footer()); //报错  不知道为啥有插槽作用域的无法获取
//useAttrs() 用来获取父组件传递的过来的属性数据的(也就是非 props 的属性值)。
const attrs = useAttrs()
</script>

useSlots может получить виртуальный объект dom слота, переданный родительским компонентом, который можно использовать для рендеринга содержимого слота.

<script lang='jsx'>
import { defineComponent, useSlots } from "vue";
export default defineComponent({
  setup() {
    // 获取插槽数据
    const slots = useSlots();
    // 渲染组件
    return () => (
      <div>
        {slots.default?slots.default():''}
        {slots.title?slots.title():''}
      </div>
    );
  },
});
</script>

Доступ к информации компонента экземпляра маршрутизации: маршрут и маршрутизатор

setupбольше не может быть доступен thisнапрямую this.$routerили this.$route. (getCurrentInstance может заменить это, но не рекомендуется)

Рекомендация: Используйте useRouteфункции и useRouterфункции вместо this.$routeиthis.$router

<script setup>
import { useRouter, useRoute } from 'vue-router'
    const route = useRoute()
    const router = useRouter()
    
    function pushWithQuery(query) {
      router.push({
        name: 'search',
        query: {
          ...route.query,
        },
      })
    }
  <script/>
import router from './router'
router.beforeEach((to,from,next)=>{

})

Вы также можете использовать навигационную защиту комбинированного APIonBeforeRouteLeave, onBeforeRouteUpdate

<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'

    // 与 beforeRouteLeave 相同,无法访问 `this`
    onBeforeRouteLeave((to, from) => {
      const answer = window.confirm(
        'Do you really want to leave? you have unsaved changes!'
      )
      // 取消导航并停留在同一页面上
      if (!answer) return false
    })

    const userData = ref()

    // 与 beforeRouteUpdate 相同,无法访问 `this`
    onBeforeRouteUpdate(async (to, from) => {
      //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
      if (to.params.id !== from.params.id) {
        userData.value = await fetchUser(to.params.id)
      }
    })
 <script/>

Composite API Guards также можно использовать в любом <router-view>компоненте, визуализируемом с помощью , их не обязательно использовать непосредственно в компонентах маршрутизации, как это делают внутрикомпонентные Guards.

<template>
  <div class="watch-test">
    <div>ref定义数组:{
    
    {arrayRef}}</div>
    <div>reactive定义数组:{
    
    {arrayReactive}}</div>
  </div>
  <div>
    <button @click="changeArrayRef">改变ref定义数组第一项</button>
    <button @click="changeArrayReactive">改变reactive定义数组第一项</button>
  </div>
</template>

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

  export default {
    name: 'WatchTest',
    setup() {
      const arrayRef = ref([1, 2, 3, 4])
      const arrayReactive = reactive([1, 2, 3, 4])

      //ref not deep
      const arrayRefWatch = watch(arrayRef, (newValue, oldValue) => {
        console.log('newArrayRefWatch', newValue, 'oldArrayRefWatch', oldValue)
      })

      //ref deep
      const arrayRefDeepWatch = watch(arrayRef, (newValue, oldValue) => {
        console.log('newArrayRefDeepWatch', newValue, 'oldArrayRefDeepWatch', oldValue)
      }, {deep: true})

      //reactive,源不是函数
      const arrayReactiveWatch = watch(arrayReactive, (newValue, oldValue) => {
        console.log('newArrayReactiveWatch', newValue, 'oldArrayReactiveWatch', oldValue)
      })

      // 数组监听的最佳实践- reactive且源采用函数式返回,返回拷贝后的数据
      const arrayReactiveFuncWatch = watch(() => [...arrayReactive], (newValue, oldValue) => {
        console.log('newArrayReactiveFuncWatch', newValue, 'oldArrayReactiveFuncWatch', oldValue)
      })

      const changeArrayRef = () => {
        arrayRef.value[0] = 6
      }
      const changeArrayReactive = () => {
        arrayReactive[0] = 6
      }
      return {
        arrayRef,
        arrayReactive,
        changeArrayRef,
        changeArrayReactive
      }
    }
  }
</script>

Автоинструменты интернационализации

В проекте vue используется [vue-i18n], в то время как в проекте реакции используется [react-i18next]. Принцип работы этих плагинов основан на наборе json-файлов языкового пакета, предоставляемых внешним интерфейсом (например, en-US. json, zh-CN.json), каждый языковой пакет key:valueхранит 代码标记:实际渲染的语言文案взаимно-однозначные соответствующие данные в виде объектов

3b8d441f6b8e0ce65056a2385c68f1a9.png
изображение.png
bac66e5a4f2cf4012fe968ddb489ab56.png
изображение.png

Front-end разработка использует вспомогательные плагины [@mango-scripts/i18n-scripts] для скачивания и импорта языковых пакетов в бизнес- 本地代码систему в один клик

521fbc9215eb1711294cd126c2808a30.png
изображение.png
980422e711eb4cf2309e1e05d7875170.png
изображение.png

Поддержка использования вспомогательных плагинов [@mango-scripts/i18n-scripts] для загрузки и импорта языковых пакетов в бизнес- 本地代码систему одним щелчком мыши.

Парсер копирайтинга: [@mango-scripts/i18n-utils] AST-анализ целевого исходного кода на основе babel, vue-template-compiler, hyntax, pug, svelte/compiler и т. д.

Вспомогательные плагины: [@mango-scripts/i18n-scripts] на базе commander, fs-extra, glob, inquirer и т.д.

https://github.com/AlbertLin0923/манго-i18n-система

https://github.com/AlbertLin0923/mango-scripts/tree/main/packages/i18n-scripts

Общаются 8 типов Vue3 и 12 типов компонентов Vue2

// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2"></child>
<script>
import child from "./child.vue"
import { ref, reactive } from "vue"
export default {
    data(){
        return {
            msg1:"这是传级子组件的信息1"
        }
    },
    setup(){
        // 创建一个响应式数据
        
        // 写法一 适用于基础类型  ref 还有其他用处,下面章节有介绍
        const msg2 = ref("这是传级子组件的信息2")
        
        // 写法二 适用于复杂类型,如数组、对象
        const msg2 = reactive(["这是传级子组件的信息2"])
        
        return {
            msg2
        }
    }
}
</script>

// Child.vue 接收
<script>
export default {
  props: ["msg1", "msg2"],// 如果这行不写,下面就接收不到
  setup(props) {
    console.log(props) // { msg1:"这是传给子组件的信息1", msg2:"这是传给子组件的信息2" }
  },
}
</script>
// Parent.vue 传送
<child :msg2="msg2"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg2 = ref("这是传给子组件的信息2")
    // 或者复杂类型
    const msg2 = reactive(["这是传级子组件的信息2"])
</script>

// Child.vue 接收
<script setup>
    // 不需要引入 直接使用
    // import { defineProps } from "vue"
    const props = defineProps({
        // 写法一
        msg2: String
        // 写法二
        msg2:{
            type:String,
            default:""
        }
    })
    console.log(props) // { msg2:"这是传级子组件的信息2" }
</script>
// Child.vue 派发
<template>
    // 写法一
    <button @click="emit('myClick')">按钮</buttom>
    // 写法二
    <button @click="handleClick">按钮</buttom>
</template>
<script setup>
    
    // 方法一 适用于Vue3.2版本 不需要引入
    // import { defineEmits } from "vue"
    // 对应写法一
    const emit = defineEmits(["myClick","myClick2"])
    // 对应写法二
    const handleClick = ()=>{
        emit("myClick", "这是发送给父组件的信息")
    }
    
    // 方法二 不适用于 Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const { emit } = useContext()
    const handleClick = ()=>{
        emit("myClick", "这是发送给父组件的信息")
    }
</script>

// Parent.vue 响应
<template>
    <child @myClick="onMyClick"></child>
</template>
<script setup>
    import child from "./child.vue"
    const onMyClick = (msg) => {
        console.log(msg) // 这是父组件收到的信息
    }
</script>
// Child.vue
<script setup>
    // 方法一 不适用于Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const ctx = useContext()
    // 对外暴露属性方法等都可以
    ctx.expose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
    
    // 方法二 适用于Vue3.2版本, 不需要引入
    // import { defineExpose } from "vue"
    defineExpose({
        childName: "这是子组件的属性",
        someMethod(){
            console.log("这是子组件的方法")
        }
    })
</script>

// Parent.vue  注意 ref="comp"
<template>
    <child ref="comp"></child>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    import child from "./child.vue"
    import { ref } from "vue"
    const comp = ref(null)
    const handlerClick = () => {
        console.log(comp.value.childName) // 获取子组件对外暴露的属性
        comp.value.someMethod() // 调用子组件对外暴露的方法
    }
</script>
// Parent.vue 传送
<child :msg1="msg1" :msg2="msg2" title="3333"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const msg1 = ref("1111")
    const msg2 = ref("2222")
</script>

// Child.vue 接收
<script setup>
    import { defineProps, useContext, useAttrs } from "vue"
    // 3.2版本不需要引入 defineProps,直接用
    const props = defineProps({
        msg1: String
    })
    // 方法一 不适用于 Vue3.2版本,该版本 useContext()已废弃
    const ctx = useContext()
    // 如果没有用 props 接收 msg1 的话就是 { msg1: "1111", msg2:"2222", title: "3333" }
    console.log(ctx.attrs) // { msg2:"2222", title: "3333" }
    
    // 方法二 适用于 Vue3.2版本
    const attrs = useAttrs()
    console.log(attrs) // { msg2:"2222", title: "3333" }
</script>
// Parent.vue
<child v-model:key="key" v-model:value="value"></child>
<script setup>
    import child from "./child.vue"
    import { ref, reactive } from "vue"
    const key = ref("1111")
    const value = ref("2222")
</script>

// Child.vue
<template>
    <button @click="handlerClick">按钮</button>
</template>
<script setup>
    
    // 方法一  不适用于 Vue3.2版本,该版本 useContext()已废弃
    import { useContext } from "vue"
    const { emit } = useContext()
    
    // 方法二 适用于 Vue3.2版本,不需要引入
    // import { defineEmits } from "vue"
    const emit = defineEmits(["key","value"])
    
    // 用法
    const handlerClick = () => {
        emit("update:key", "新的key")
        emit("update:value", "新的value")
    }
</script>
// Parent.vue
<script setup>
    import { provide } from "vue"
    provide("name", "沐华")
</script>

// Child.vue
<script setup>
    import { inject } from "vue"
    const name = inject("name")
    console.log(name) // 沐华
</script>
// store/index.js
import { createStore } from "vuex"
export default createStore({
    state:{ count: 1 },
    getters:{
        getCount: state => state.count
    },
    mutations:{
        add(state){
            state.count++
        }
    }
})

// main.js
import { createApp } from "vue"
import App from "./App.vue"
import store from "./store"
createApp(App).use(store).mount("#app")

// Page.vue
// 方法一 直接使用
<template>
    <div>{
    
    { $store.state.count }}</div>
    <button @click="$store.commit('add')">按钮</button>
</template>

// 方法二 获取
<script setup>
    import { useStore, computed } from "vuex"
    const store = useStore()
    console.log(store.state.count) // 1

    const count = computed(()=>store.state.count) // 响应式,会随着vuex数据改变而改变
    console.log(count) // 1 
</script>

Межкомпонентная связь EventBus

В Vue3 нет межкомпонентной связи EventBus.

сначала установить npm i mitt -S

mitt.js
import mitt from 'mitt'
const mitt = mitt()
export default mitt
// 组件 A
<script setup>
import mitt from './mitt'
const handleClick = () => {
    mitt.emit('handleChange')
}
</script>

// 组件 B 
<script setup>
import mitt from './mitt'
import { onUnmounted } from 'vue'
const someMethed = () => { ... }
mitt.on('handleChange',someMethed)
onUnmounted(()=>{
    mitt.off('handleChange',someMethed)
})
</script>

Существует 12 типов взаимодействия компонентов Vue2.x.

  1. реквизит

  2. $ излучать / v-on

  3. .синхронизация

  4. v-модель

  5. ссылка

  6. родитель

  7. слушатели

  8. предоставлять / вводить

  9. EventBus

  10. Вьюекс

  11. $корень

  12. слот

Связь между родительскими и дочерними компонентами может использоваться:

  • реквизит

  • $ излучать / v-on

  • слушатели

  • ссылка

  • .синхронизация

  • v-модель

  • родитель

Связь компонентов родственного компонента может использоваться:

  • EventBus

  • Вьюекс

  • $родитель

Межуровневая связь между компонентами может осуществляться с помощью:

  • предоставить/ввести

  • EventBus

  • Вьюекс

  • слушатели

  • $корень

Данные, обернутые с помощью readonly, доступны только для чтения на глубоком уровне, и только самый внешний слой мелкого чтения доступен только для чтения.

toRaw и markRaw

toRaw может преобразовывать общий объект, преобразованный из реактивной функции или функции только для чтения, в отзывчивый прокси-сервер, и интерфейс представления не будет обновляться, если значение свойства общего объекта будет изменено. Обычно используется для рендеринга больших списков с неизменяемыми источниками данных. Пропуск прокси-преобразования может повысить производительность.

markRaw помечает объект, чтобы он никогда не был преобразован в адаптивные данные и мог возвращать только сам объект.Обычно он используется для определенных значений, которые не следует устанавливать как адаптивные, например экземпляры сторонних классов или объекты vue. .

markRaw помечает данные, которые никогда не реагируют, даже если они позже преобразуются в реактивные, они не реагируют

toRef создает ссылку Ref для свойства в отзывчивом объекте. При обновлении эталонный объект будет обновляться синхронно. Обратите внимание, что если данные, созданные toRef, изменены, это не вызовет обновление интерфейса представления, потому что сущность toRef — ссылка, исходные данные связаны.

Возьмите атрибут объекта и манипулируйте этим атрибутом

Используйте toRef, чтобы сделать его реактивным

let count =toRef(objA,'count')
          count.value++

Добавьте группу для связи с автором vx: xiaoda0423

Адрес склада: https://github.com/webVueBlog/WebGuideInterview

рекомендация

отblog.csdn.net/qq_36232611/article/details/130716887
рекомендация