v-if 与 v-show 有什么区别
- v-if切换会创建/删除元素,v-show切换只是元素的展示/隐藏(display: none)
- v-if切换会创建/销毁组件,v-show切换在创建完组件后只会隐藏(display: none)
- 对于多个元素的控制可以用
<template>
包裹
列表循环时key的作用?
- v-for可基于数组渲染列表,也可基于对象渲染列表
- 可以使用值的范围
- 可在组件上循环渲染
- v-for默认使用“就地更新”策略,数据项的顺序被改变,Vue将不会移动DOM元素来匹配数据项的顺序,而是就地更新每个元素。
- 为能跟踪每个节点的身份,重用和重新排序现有元素,提升性能,需要使用key
父子组件如何传递数据?
通过props传递数据,我们不应该在一个子组件内部改变 prop。
这里有两种常见的试图变更一个 prop 的情形:
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data property 并将这个 prop 作为其初始值:
props: ['initialCounter'],
data() {
return {
counter: this.initialCounter
}
}
这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
单向数据流指的是什么?有什么好处?
什么是单向数据流?
很多框架都使⽤单向数据流,指的是⽗组件能直接传递数据给⼦组件,⼦组件不能随意修改⽗组件状态。
为什么要单向?
单向数据流的⽬的是让数据的传递变得简单、可控、可追溯。假设都是⽤双向数据流,⽗组件维护⼀个状态,并且传递给所有的⼦组件。当其中⼀个⼦组件通过双向数据流直接修改⽗组件的这个状态时,其他⼦组件也会被修改。当这个结果产⽣时我们⽆法得知是拿个⼦组件修改了数据,在项⽬庞⼤时数据的管理和追溯变得更复杂。
如果要双向如何实现?
⼀般来说,⽗组件可⽤通过设置⼦组件的props直接传递数据给⼦组件。⼦组件想传递数据给⽗组件时,可以在内部emit⼀个⾃定义事件,⽗组件可在⼦组件上绑定该事件的监听,来处理⼦组件emit的事件和数据。
在Vue⾥,v-model实现的所谓双向绑定,本质上就这种⽅式。
v-text 和 v-html 有什么区别
v-text是直接插入文本
v-html是将它作为html片段插入的
<div id="app">
<fieldset>
<legend>v-html&&v-text</legend>
<div v-html="pushHTML"></div>
<div v-text="pushHTML"></div>
</fieldset>
</div>
<script>
const App = {
data() {
return {
visible: true,
pushHTML: `<h1 style="color:red;">你好我的世界</h1>`,
};
},
};
Vue.createApp(App).mount('#app');
</script>
data 为什么要是函数
data() {
return {
...
}
}
data选项是一个函数,返回的是一个对象,相对于是一个给定初始值的地方,如果不给初始值,可以使用null或者undefined代替。
这里面的数据都是不响应式的,一般在实例挂载后就不会再次读取里面的数据。
计算属性缓存是什么
计算属性有级存机制,如果依赖的数据未发生改变,则不会重新计算而是直接使用缓存值
注意methods和computed里面的方法不要使用箭头函数,否则this就不是vm对象了
<div id="computed-basics" class="demo">
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
</div>
<script>
Vue.createApp({
data() {
return {
author: {
name: 'John Doe',
books: ['Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery']
}
}
},
computed: {
// a computed getter
publishedBooksMessage() {
// `this` points to the vm instance
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
}).mount('#computed-basics')
</script>
计算属性只在相关响应式依赖发生改变时它们才会重新求值。这就意味着只要 author.books
还没有发生改变,多次访问 publishedBookMessage
计算属性会立即返回之前的计算结果,而不必再次执行函数。
如果是方法的,他就会在每次调用时都会执行。
watch、计算属性有什么区别
用Watch的时候
- 当只需要根据data中某个property的变化做出反应,但不一定需要结果时
- 当有异步操作时
- 当需要用旧值时
watch会监控data中某个property的变化,执行函数。
而计算属性是根据监控某个property变化得到的一个返回值。
在Vue中组件的全局注册和局部注册有什么区别,如何局部注册组件?
全局组件注册
const app = Vue.createApp({})
app.component('my-component-name', {
// ... 选项 ...
})
局部组件的注册
注意注册的组件要在调用之前,先注册后调用。
const ComponentA = {
/* ... */
}
const ComponentB = {
/* ... */
}
const ComponentC = {
/* ... */
}
const app = Vue.createApp({
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
app.moun("#app")
两者的区别
- 局部注册的组件在其子组件中不可用。
- 全局注册的组件可以在应用中的任何组件的模板中使用。
如何传递一个字符串类型的prop给子组件?数字类型呢?如何动态给prop赋值?
自定义传值的方式来解决上面的问题。。这时,你可以以对象形式列出 prop,这些 property 的名称和值分别是 prop 各自的名称和类型:
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其他构造函数
}
静态传值
<blog-post title="我的世界"></blog-post>
我们不仅可以静态的赋值,还可以动态的赋值
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
// 下面是 v-bind的语法糖
<blog-post :title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post :title="post.title + ' by ' + post.author.name"></blog-post>
同时还可以传入数字、布尔值、数组、对象,等等。
对于组件来说非prop的attribute怎么处理?
我们可以通过 $attrs
来给需要的元素添加事件
<date-picker data-status="activated"></date-picker>
app.component('date-picker', {
inheritAttrs: false,
template: `
<div class="date-picker">
<input type="datetime" v-bind="$attrs" />
</div>
`
})
v-model:foo="bar”与:foo="bar"有什么区别?如何实现v-model:foo="bar” ?
v-model:foo="bar"
// 等价于
:foo="bar" @updata:foo="bar=$event"
:foo="bar"
相当于给子组件传递一个props,它的值是bar里面的内容
v-model:foo="bar"
相当于在:foo="bar"
上添加了事件响应
Vue中的插槽是什么?
插槽就是slot,说通俗一点就是占位置,当使用该组件标签时候,组件标签里面的内容就会自动填坑
例如下面代码
<div>
<todo-button>
Add todo
</todo-button>
</div>
<!-- todo-button 组件模板 -->
<button class="btn-primary">
<slot></slot>
</button>
上面的代码就会被翻译成
<button class="btn-primary">
Add todo
</button>
如何实现多层级嵌套的父子组件数据传递?
使用Provide / Inject
父组件有一个 provide
选项来提供数据,子组件有一个 inject
选项来开始使用这些数据。
传递大范围的data和method共用。
<div id="app">
<div :class="`app theme-${themeName} fontSize-${fontSizeName}` ">
<Child></Child>
</div>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
// 孙子组件
const ChangeThemeButton = {
inject: ["changeTheme", "changeFontSize"],
template: `
<div>
<button @click="changeTheme">换肤</button>
<button @click="changeFontSize('big')">大</button>
<button @click="changeFontSize('small')">小</button>
<button @click="changeFontSize('normal')">正常</button>
</div>
`,
};
// 子组件
const Child = {
components: {
ChangeThemeButton,
},
template: `
<div>
你好
<ChangeThemeButton/>
</div>
`,
};
// 父组件
Vue.createApp({
components: {
Child,
},
data() {
return {
themeName: "blue",
fontSizeName: "normal",
};
},
provide() {
return {
changeTheme: this.changeTheme,
changeFontSize: this.changeFontSize,
};
},
methods: {
changeTheme() {
if (this.themeName === "blue") {
this.themeName = "red";
} else {
this.themeName = "blue";
}
},
changeFontSize(name) {
if (["big", "small", "normal"].indexOf(name) >= 0) {
this.fontSizeName = name;
}
},
},
}).mount("#app");
</script>
<style>
.app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
.app.theme-blue button {
background: blue;
color: white;
}
.app.theme-blue {
color: darkblue;
}
.app.theme-red button {
background: red;
color: white;
}
.app.theme-red {
color: darkred;
}
.app button {
font-size: inherit;
}
.app.fontSize-small {
font-size: 12px;
}
.app.fontSize-big {
font-size: 20px;
}
</style>
keep-alive有什么作用
保持组件的状态,以避免反复渲染导致的性能问题。
执行下面的代码,在input标签里面输入东西,跳转到其他组件 在跳转回来,你会发现,之前输入的东西是没有改变的,并不会清除。
<div id="app">
<button v-for="tab in tabs" :key="tab" @click="currentTab = tab">{{tab}}</button>
<keep-alive>
<component :is="currentTabComponent" class="tab"></component>
</keep-alive>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
currentTab: "Home",
tabs: ["Home", "Input"],
};
},
computed: {
currentTabComponent() {
return "tab-" + this.currentTab.toLowerCase();
},
},
});
app.component("tab-home", {
template: `<div>Home component</div>`,
});
app.component("tab-input", {
template: `<div><input type="text"/></div>`,
});
app.mount("#app");
</script>
动态组件是什么?
在不同组件之间进行动态切换,上面的一问中就是动态组件。
<!-- 组件会在 `currentTabComponent` 改变时改变 -->
<component :is="currentTabComponent"></component>
Vue里有哪些方法实现过渡或者动画效果
使用class实现动画
也就是先写好CSS3的动画样式,然后再需要的标签上添加class形成动画
<div id="app">
<div :class="['box', {active: isActive}]">box</div>
<button @click="isActive = !isActive">Click me</button>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data() {
return {
isActive: false
}
}
}).mount('#app')
</script>
<style>
.box {
width: 300px;
height: 200px;
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.active {
transform: translateX(100px);
}
</style>
使用style动画过渡
和class类似,将样式卸载标签上,适合添加的样式较少的情况下使用。
<div id="app">
<div class="box" :style="{backgroundColor:`rgb(${R},${G},${B})`}"></div>
<input type="range" min="0" max="255" v-model="R" />
R:{{R}}<br>
<input type="range" min="0" max="255" v-model="G" />
G:{{G}}<br>
<input type="range" min="0" max="255" v-model="B" />
B:{{B}}<br>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
Vue.createApp({
data() {
return {
R:0,
G:0,
B:0
};
},
}).mount("#app");
</script>
<style>
.box{
width: 200px;
height: 200px;
}
</style>
使用transition来制作动画过渡
将需要制作动画的标签放入 transition里面。然后使用 name属性制作动画。
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>
,则 v-
是这些class名的默认前缀。如果你使用了 <transition name="fade">
,那么 v-enter-from
会替换为 fade-enter-from
。
我们可以用 <transition>
组件上的 duration
来定制一个显性的过渡持续时间 (以毫秒计):
<div id="demo">
<button @click="show = !show">Toggle</button>
<transition name="fade">
<p v-if="show">hello</p>
</transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const Demo = {
data() {
return {
show: true,
};
},
};
Vue.createApp(Demo).mount("#demo");
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
多个元素过渡
同时生效的进入和离开的过渡不能满足所有要求,所以 Vue 提供了过渡模式
in-out
: 新元素先进行过渡,完成之后当前元素过渡离开。out-in
: 当前元素先进行过渡,完成之后新元素过渡进入。
<div id="app">
<button v-for="tab in tabs" :key="tab" :class="{ active: currentTab === tab }" @click="currentTab = tab">
{{ tab }}
</button>
<transition mode="out-in" name="fade">
<keep-alive>
<component :is="currentTab" class="tab"></component>
</keep-alive>
</transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
currentTab: 'Tab1',
tabs: ['Tab1', 'Tab2']
}
},
})
app.component('Tab1', {
template: `<div>Tab1 content</div>`,
})
app.component('Tab2', {
template: `<div>
<input v-model="value" /> {{value}}
</div>`,
data() { return { value: 'hello' } },
})
app.mount('#app')
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css" rel="stylesheet" />
<style>
.active {
background: #ccc;
}
.fade-enter-active,
.fade-leave-active {
transition: all .3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
多个组件的过渡
<div id="app">
<button
v-for="tab in tabs"
:key="tab"
:class="{active:currentTab === tab}"
@click="currentTab = tab"
>
{{tab}}
</button>
<transition
mode="out-in"
enter-active-class="animate__animated animate__fadeInLeft"
leave-active-class="animate__animated animate__fadeOutRight"
>
<keep-alive>
<component :is="currentTab" class="tab"></component>
</keep-alive>
</transition>
</div>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
data() {
return {
currentTab: "Tab1",
tabs: ["Tab1", "Tab2"],
};
},
});
app.component("Tab1", {
template: `<div>Tab1 content</div>`,
});
app.component("Tab2", {
template: `<div>
<input v-model="value" />
.{{value}}
</div>`,
data() {
return { value: "hello" };
},
});
app.mount("#app");
</script>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.0/animate.min.css"
rel="stylesheet"
/>
<style>
:root {
--animate-duration: 0.3s;
--animate-delay: 0;
}
.active {
background-color: #ccc;
}
</style>
transition组件实现过渡效果怎么使用
- v-enter-from:在元素被插入之前生效,在元素被插入之后的下一帧移除
- v-enter-active:定义进入过渡生效时的状态
- v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效,在过渡/动画完成之后移除
- v-leave-from:在离开过渡被触发时立刻生效,下一帧被移除
- v-leave-active:定义离开过渡生效时的状态
- v-leave-to:离开过渡的结束状态。在离开过渡被触发之后下一-帧生效,在过渡/动画完成之后移除
列表过渡怎么做
在需要过渡的列表外面添加 transition-group
,记住必加一个 tag
让他成为一个便签包裹在列表过渡外面。
<transition-group name="list" tag="div">
<div v-for="(value, index) in news" :key="value">
{{value}}
<button @click="news.splice(index, 1)">删除</button>
</div>
</transition-group>
什么是组合式 API
当我们在面临大型应用是,共享和重用代码变得尤为重要。
我们将这些重用的放在 setup
注意:由于在执行 setup
时,组件实例尚未被创建,因此在 setup
选项中没有 this
。这意味着,除了 props
之外,你将无法访问组件中声明的任何属性——本地状态、计算属性或方法。