Действие по доставке книг: выберите 1 одноклассника и отправьте его бесплатно.
Крайний срок: 19:00 18 мая 2023 г.
Примите участие в области комментариев внизу и поговорите : пожалуйста, поделитесь своими успехами в чтении в комментариях.
Фронтенд-инжиниринг: дизайн и практика на основе Vue.js 3.0
Просмотры страниц (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
В виде автоматизации, как добавить эти закопанные точки?
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),
},
};
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
показано в распечатанных результатах в примере кода.
Интернационализация 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
( $attrs
attrs используется для получения параметров/методов, передаваемых в подкомпоненты в родительском компоненте, которые не являются пропсами, 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
хранит 代码标记:实际渲染的语言文案
взаимно-однозначные соответствующие данные в виде объектов
Front-end разработка использует вспомогательные плагины [@mango-scripts/i18n-scripts] для скачивания и импорта языковых пакетов в бизнес- 本地代码
систему в один клик
Поддержка использования вспомогательных плагинов [@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.
реквизит
$ излучать / v-on
.синхронизация
v-модель
ссылка
родитель
слушатели
предоставлять / вводить
EventBus
Вьюекс
$корень
слот
Связь между родительскими и дочерними компонентами может использоваться:
реквизит
$ излучать / 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