<template>
<div>App: {{numRef}}</div>
<hr>
<Minus :state="state"/>
</template>
<script setup>
import {ref, watchEffect} from "vue";
import Minus from "./Minus.vue";
const numRef = ref(0);
const state = {numRef};
watchEffect(() => {
console.log(numRef.value);
});
</script>
<template>
<div>{{state.numRef}} - <button @click="onClick">Minus</button></div>
<Plus :state="state"/>
</template>
<script setup>
import Plus from "./Plus.vue";
const props = defineProps(["state"]);
function onClick() {
props.state.numRef.value--;
}
</script>
<template>
<div>{{state.numRef}} - <button @click="onClick">Plus</button></div>
</template>
<script setup>
const props = defineProps(["state"]);
function onClick() {
props.state.numRef.value++;
}
</script>
.github/workflows/blank.yml
.v-model
это не то, что по-умолчанию есть во все компонентах. Это как раз наоборот.v-model
, то скорее всего об это будет явно сказано в доках.TransformStream
. ReadableStream
. Единственное отличие, что TransformStream
"ленивый". Т.е. если закомментировать await newResponse.blob();
onProgress
не будет вызываться, хотя данные были загружены.const response = await fetch("https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg");
total.value = parseInt(response.headers.get("content-length")) || 0;
function onProgress(chunk) {
loaded.value += chunk.length;
}
const ts = new TransformStream({
transform(chunk, controller) {
onProgress(chunk);
controller.enqueue(chunk);
},
});
response.body.pipeThrough(ts);
const newResponse = new Response(ts.readable);
const blob = await newResponse.blob();
"content-length"
может отсутствовать. "content-encoding"
, который, к слову, по-умолчанию будет недоступен для кода при кросс-доменных запросах."content-encoding"
в "content-length"
значение всегда указано для сжатого респонса, а в коллбеке будут уже не сжатые данные. Т.е. loaded
в таком случае будет больше значения total
("content-length"
)ReadableStream
:const reader = response.body.getReader();
const readableStream = new ReadableStream({
async start(controller) {
while (true) {
const {done, value} = await reader.read();
if (done) { break; }
onProgress(value);
controller.enqueue(value);
}
controller.close();
reader.releaseLock();
},
cancel() {
void reader.cancel();
}
});
const newResponse = new Response(readableStream);
export default {
data() {
return {
dateNow: new Date(),
timerId: null
};
},
mounted() {
this.timerId = setInterval(() => {
this.dateNow = new Date();
}, 1000);
},
beforeUnmount() {
clearInterval(this.timerId);
}
};
import {ref, onUnmounted} from "vue";
const dateNow = ref(new Date());
const timerId = setInterval(() => {
dateNow.value = new Date();
}, 1000);
onUnmounted(() => {
clearInterval(timerId);
});
ref
, reactive
и т.д.:const a = [1, 2, 3];
toRaw
, чтобы получить оригинальный объект из реактивного:const a = [1, 2, 3];
const r = ref(a);
const b = toRaw(r.value);
a
и/или b
не будут вызывать рендер, а изменения r.value
— будут.markRaw
над a
, то даже r.value
перестанет быть реативным.Поисковик выдает много всего
<template>
<div v-for="i of items" :key="i">{{i}}</div>
</template>
<script setup>
import {ref, computed, onMounted} from "vue";
const data = ["1q", "2w", "3e", "4r", "5t", "6y"];
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const items = ref([]);
onMounted(async () => {
for (const value of data) {
items.value.push(value);
await sleep(1000);
}
});
</script>
const limit = ref(0);
const items = computed(() => {
return data.slice(0, limit.value);
});
onMounted(async () => {
while (limit.value < data.length) {
limit.value++;
await sleep(1000);
}
});
script setup
Spread types may only be created from object types.
undefined
.useSomething
) делается так — возвращается объект, чьи поля присваиваются другому объкту путем оператора спреад.useModal()
создаются свои личные isShowModal
, openModal
, closeModal
переменные.modal-state.js
нужно оформить в виде класса, и передавать экземпляр* этого класса компоненту в виде пропса.modal-state.js
по сути синглтон.Как установить и настроить Vue.js?
npm create vite@latest my-vue-app -- --template vue
cd my-vue-app
npm install
npm run dev
yarn
(npm install --global yarn
) вместо npm
это будет заметно быстрее:yarn create vite my-vue-app --template vue
cd my-vue-app
yarn install
yarn run dev
server.open
в vite.config.js
import {defineConfig} from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [
vue(),
],
server: {
open: "/"
}
});
HelloWorld.vue
, в случае VSCode следует установить соответствующий плагин для код ассистанса, подсветки синтаксиса Vue 3. arrayBuffer
у это объекта. event.target.files[0]
— это объект класса File
, который в свою очередь наследуется от класса Blob
. import {ref, readonly} from "vue";
const todo = ref(null);
const todoId = ref(1);
export async function fetchTodo() {
const resp = await fetch("https://jsonplaceholder.typicode.com/todos/" + todoId.value++);
todo.value = await resp.json();
};
// Using of `readonly` is optional, I use it just in case.
// You can just export `todoId`, `todo` (above) as is.
const _todoId = readonly(todoId);
const _todo = readonly(todo);
export {
_todoId as todoId,
_todo as todo
};
<template>
<Button/>
<div v-if="todo">
{{todo}}
</div>
</template>
<script setup>
import Button from "./Button.vue";
import {todo} from "./core.js";
</script>
<template>
<button @click="fetchTodo">fetchTodo({{todoId}})</button>
</template>
<script setup>
import {fetchTodo, todoId} from "./core.js";
</script>
<script setup>
import {ref, computed, watchEffect} from "vue";
const phones = ref([
{ title: "iPhone 12", company: "Apple", price: 65000 },
{ title: "Galaxy S20", company: "Samsung", price: 63000 },
{ title: "Galaxy A10", company: "Samsung", price: 38000 },
{ title: "iPhone 10", company: "Apple", price: 45000 },
{ title: "Xiaomi Redmi 8", company: "Xiaomi", price: 42000 },
]);
const companySearch = ref("");
const filteredList = computed(() => {
if (!companySearch.value) {
return phones.value;
}
const search = companySearch.value.trim().toLowerCase();
return phones.value.filter(elem => elem.company.toLowerCase().startsWith(search)); // .includes(search)
});
const sortParam = ref("");
const sortByTitle = (p1, p2) => p1.title.localeCompare(p2.title, undefined, {sensitivity: "base"});
const sortByCompany = (p1, p2) => p1.company.localeCompare(p2.company, undefined, {sensitivity: "base"});
const sortByPrice = (p1, p2) => p1.price - p2.price;
const sortedList = ref([]);
watchEffect(() => {
const array = filteredList.value; //const array = [...filteredList.value];
if (sortParam.value === "title") {
sortedList.value = array.sort(sortByTitle);
} else if (sortParam.value === "company") {
sortedList.value = array.sort(sortByCompany);
} else if (sortParam.value === "price") {
sortedList.value = array.sort(sortByPrice);
} else {
sortedList.value = array;
}
});
</script>
<script setup>
import {ref, computed} from "vue";
const phones = ref([
{ title: "iPhone 12", company: "Apple", price: 65000 },
{ title: "Galaxy S20", company: "Samsung", price: 63000 },
{ title: "Galaxy A10", company: "Samsung", price: 38000 },
{ title: "iPhone 10", company: "Apple", price: 45000 },
{ title: "Xiaomi Redmi 8", company: "Xiaomi", price: 42000 },
]);
const companySearch = ref("");
const filteredList = computed(() => {
if (!companySearch.value) {
return phones.value;
}
const search = companySearch.value.trim().toLowerCase();
return phones.value.filter(elem => elem.company.toLowerCase().startsWith(search)); // .includes(search)
});
/** @type {import("vue").Ref<("title"|"company"|"price")>} */
const orderBy = ref("title");
const orders = ref({ // if `true` — an order is reversed
title: false,
company: false,
price: false,
});
const isOrderReversed = computed(() => orders.value[orderBy.value]);
function toggleOrder() {
orders.value[orderBy.value] = !orders.value[orderBy.value];
}
/** @param {"title"|"company"|"price"} value */
function setOrderBy(value) {
if (orderBy.value === value) {
toggleOrder();
}
orderBy.value = value;
};
const {compare} = new Intl.Collator(undefined, {sensitivity: "base"});
function comparator(pre, cur) {
const k = isOrderReversed.value ? -1 : 1;
if (orderBy.value === "title") {
return compare(pre.title, cur.title) * k;
} else if (orderBy.value === "company") {
return compare(pre.company, cur.company) * k;
} else if (orderBy.value === "price") {
return (pre.price - cur.price) * k;
}
return 0;
}
const sortedList = computed(() => {
return filteredList.value.sort(comparator);
});
</script>
Копирую его в проект и при вызове метода на кнопку получаю в консоли:
Нашел пример в документации. Копирую его в проект
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="mount-point"></div>
<script>
const Profile = {
template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
data() {
return {
firstName: 'Walter',
lastName: 'White',
alias: 'Heisenberg'
}
}
}
Vue.createApp(Profile).mount('#mount-point')
</script>
</body>
</html>
Что это и почему?
Как в Vue3 создать глобальную переменную?
import {computed, ref} from "vue";
export const todos = ref([]);
export const activeTodos = computed(() => {
return todos.value.filter(todo => !todo.done);
});
let id = 0;
export function addTodo(name, done = false) {
todos.value.push({name, done, id: id++});
};
export function removeTodo(todo) {
todos.value = todos.value.filter(_todo => _todo.id !== todo.value.id);
};
export function toggleTodo(todo) {
todo.value.done = !todo.value.done;
};
export function clearTodos() {
todos.value = [];
};
<script setup>
import {computed, ref, onMounted} from "vue";
import Todo from "./Todo.vue";
import {todos, activeTodos, clearTodos, addTodo} from "./main.js";
const showAll = ref(true);
const selectedTodos = computed(() => {
return showAll.value ? todos.value : activeTodos.value;
});
function onEnter(event) {
addTodo(event.currentTarget.value);
event.currentTarget.value = "";
}
onMounted(() => {
addTodo("Task 01", true);
addTodo("Task 02");
addTodo("Task 03");
});
</script>
<script setup>
import {toRefs} from "vue";
import {toggleTodo, removeTodo} from "./main.js";
const props = defineProps(["todo"]);
const {todo} = toRefs(props);
function onClick() {
toggleTodo(todo);
}
function onRightClick() {
removeTodo(todo);
}
</script>
<template>
<label>
<input v-model="checked" type="checkbox">
Show input
</label>
<br>
<input v-if="checked" v-model="msg">
</template>
<script setup>
import {ref} from "vue";
const msg = ref("Hello World!");
const checked = ref(false);
</script>