1.创建组件实例
在vue2中我们是通过new Vue() 的方法进行创建,而在vue3通过vue里面的一个全局方法createApp进行创建,可以通过链式调用
例子:
const Counter = {
data() {
return {
counter: 0
}
}
}
const app = Vue.createApp(Counter)
app.mount('#root')
该应用实例是用来在应用中注册“全局”组件的。
const app = Vue.createApp({})
app.component('SearchInput', SearchInputComponent)
app.directive('focus', FocusDirective)
app.use(LocalePlugin)
2.自定义指令
之前全局通过Vue.directive(),现在通过定义的全局变量,例如上面的app.directive()
例子:定义一个v-focus
<input type="text" v-focus /> //用的时候,在刚打开页面就会获取焦点
app.directive('focus', {
mounted(el) {
el.focus()
}
})
(2) 里面钩子函数的改变
在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使钩子的目标事件十分相似。我们现在把它们统一起来了:
- **created** - 新增!在元素的 attribute 或事件监听器被应用之前调用。
- bind → **beforeMount**
- inserted → **mounted**
- **beforeUpdate**:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
- update → 移除!该钩子与 `updated` 有太多相似之处,因此它是多余的。请改用 `updated`。
- componentUpdated → **updated**
- **beforeUnmount**:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
- unbind -> **unmounted**
例子:
<div id="root">
<h1>scroll down the page</h1>
<input type="range" min="0" max="500" v-model="pinPadding">
<p v-pin:[direction]="pinPadding">text</p>//:[direction]是building参数的arg
</div>
<script>
const app = Vue.createApp({
data() {
return {
pinPadding: 200,
direction: 'right'
}
}
})
// app.directive('pin', {
// mounted(el, binding) {
// // console.log(binding)
// el.style.position = 'fixed'
// const s = binding.arg || 'top'
// el.style[s] = binding.value + 'px'
// },
// updated(el, binding) {
// const s = binding.arg || 'top'
// el.style[s] = binding.value + 'px'
// },
// })
app.directive('pin', (el, binding) => {
el.style.position = 'fixed'
const s = binding.arg || 'top'
el.style[s] = binding.value + 'px'
})
app.mount('#root')
</script>
</body>
3.自定义插件
首先在vue2通过install函数进行创建插件才能进行vue.use,在vue3中用app.use
例子;
i18n.js,在改文件夹下导出install方法
知识点: app.config.globalProperties.$translate是在vue实例下面创建一个$translate属性,在实例里直接通过this.$translate就能获取到该值
export default {
install: (app, options) => {
// 挂载一个全局属性
app.config.globalProperties.$translate = key => {
console.log(key.split('.'));
return key.split('.').reduce((o, i) => {
if (o) {
console.log(o);//第一次是options对象,第二次是 greetings: {
hello: '你好'
}对象
return o[i]
}
}, options)
}
}
}
在挂载的时候,用的时候,在刚打开页面就会打印“你好”
<div id="app">
<my-component></my-component>
</div>
<script type="module">
import i18nPlugin from './i18n.js'
const app = Vue.createApp({
mounted() {
console.log(this.$translate('greetings.hello'))
}
})
// 全局定义的组件
app.component('my-component', {
template: `
<h1>自定义全局组件</h1>
`
})
// 使用插件
const i18nString = {
greetings: {
hello: '你好'
},
oparator: {
open: '打开',
close: '关闭'
}
}
app.use(i18nPlugin, i18nString)
app.mount('#app')
</script>
4.生命周期的改变
改变了卸载的生命周期,之前是destory,变成了unmount,还可以通过app.unmount()进行卸载组件,在vue2通过v-if=false
5.在根组件里面datab必须是一个函数,在vue2中是一个对象
防抖案例,是通过lodash里面的方法
首先需要引入lodash
然后
<div id="root">
<!-- {{title}} -->
<save-button></save-button>
</div>
<script>
const app = Vue.createApp({
})
app.component('save-button', {
created() {
this.deboucedClick = _.debounce(this.click, 500) //这里是_.debounce是lodash里面的防抖发方法,第一个参数是返回出来的一个函数,也就是在methods里面定义的click,第二个是这个函数需要执行的时间
},
template: `
<button @click="deboucedClick">
Save
</button>
`,
methods: {
click() {
console.log('clicked.')
}
}
unmounted() {
// 移除组件时,取消定时器
this.debouncedClick.cancel()
},
})
app.mount('#root')
6.v-if与v-for
(1)在vue3 当 `v-if` 与 `v-for` 一起使用时,`v-if` 具有比 `v-for` 更高的优先级。
在vue2中v-for优先级高,当你循环遍历[2,3,4,5,6]的时候你v-if=item>2,在vue2中只有2b不渲染,而在vue3中所以的都不渲染,相当于数组中every只要一个是false就返回false,因为v-if优先级高,所以都不会渲染
(2)
在 Vue 2 中,在 v-for 中使用的 `ref` attribute 会用 ref 数组填充相应的 `$refs` property。当存在嵌套的 `v-for` 时,这种行为会变得不明确且效率低下。
例如:
<div id="root">
<ul>
<li v-for="li in lists" :ref="li">{{ li }}</li> //通过这种方式绑定ref,会自动给你创建一个$refs,的数据挂载到当前实例下面
</ul>
</div>
<script>
const vm = new Vue({
el: '#root',
data: {
lists: ['🍒', '🍊', '🍇', '🥕'],
refs: 'lists'
},
mounted() {
console.log(this.$refs); //在这里可以打印出来,但是在vue3中就不会给你自动创建$refs的数组了
},
methods: {
setItemRef(el) {
// if (el) {
// this.itemRefs.push(el)
// }
console.log(0)
}
},
})
</script>
在 Vue 3 中,此类用法将不再自动创建 `$ref` 数组。要从单个绑定获取多个 ref,请将 `ref` 绑定到一个更灵活的函数上 (这是一个新特性)
思路:在要v-for的节点上,用:ref绑定到自己实例里面methods里面的一个方法setItemRef,在data里面创建一个空数组,用来存ref,而setItemRef方法进行把每次的ref进行push到data定义的数组里面
例子:
<div id="root">
<ul >
<!-- <li v-for="li in lists" v-if="li!=='🥕'">{{ li }}</li> -->
<li v-for="li in lists" :ref="setItemRef">{{ li }}</li>//这里把:ref绑定到自己实例里面methods里面的一个方法,
</ul>
<!-- <my-component v-for="i in 3">{{i}}</my-component> -->
</div>
<script>
const app = Vue.createApp({
data() {
return {
lists: ['🍒', '🍊', '🍇', '🥕'],
refs: 'lists',
itemRefs: [] //在data里面创建一个数组用来存ref
}
},
methods: {
setItemRef(el) {
if (el) {
this.itemRefs.push(el)//这里el是绑定这个方法的dom元素
}
}
},
mounted() {
console.log(this.itemRefs)
},
})
app.mount('#root')
</script>
</body>
7.多事件处理器
事件处理程序中可以有多个方法,这些方法由逗号运算符分隔:
```html
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<button @click="one($event), two($event)">
Submit
</button>
```js
// ...
methods: {
one(event) {
// 第一个事件处理器逻辑...
},
two(event) {
// 第二个事件处理器逻辑...
}
}
```
8.按键修饰符keyup
在vue2中
```html
<!-- 键码版本 -->
<input v-on:keyup.13="submit" />
<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />
此外,也可以通过全局的 `config.keyCodes` 选项定义自己的别名。
```js
Vue.config.keyCodes = {
f1: 112
}
```
```html
<!-- 键码版本 -->
<input v-on:keyup.112="showHelpText" />
<!-- 自定义别名版本 -->
<input v-on:keyup.f1="showHelpText" />
在vue3中全局的 `config.keyCodes` 选项定义自己的别名已经废弃,直接.enter就行
```html
<!-- Vue 3 在 v-on 上使用按键修饰符 -->
<input @keyup.page-down="nextPage">
<!-- 同时匹配 q 和 Q -->
<input @keypress.q="quit">
9.$attrs
在vue2中,要是在组件上面写上属性<date-picker data-status="activated" class="a"></date-picker> 不管是自定义属性还是,class,style,都会传递到组件实例最外层div上面,但是要是加上
inheritAttrs: false,这个属性,只有class与style传递到组件实例,自定义属性不会传递
而在vue3中书写inheritAttrs: false,class与style也不会传递到组件实例,所有属性都不会传递
,虽然不会传递但是都存在this.$attrs下面,如果想让某个节点要有某个属性可以这样写
<body>
<div id="root">
<date-picker data-status="activated" class="a"></date-picker>
</div>
<script>
const app = Vue.createApp({
})
app.component('date-picker', {
inheritAttrs: false,
template: `
<div v-bind:class="$attrs.class"> //直接拿$attrs下面存的值
<input type="datetime-local" v-bind="$attrs"/>//这样写会拿到$attrs所有的属性
</div>
`,
created() {
// console.log(this.$attrs.class)
}
})
app.mount('#root')
10.自定义事件
首先在vue2中,要是字组件想要使用父组件的方法的时候需要@click.native=" ",而在vue3中已经不用.native,直接写时间就行,但是注意它只适合浏览器自定的事件,自定义事件不可以
例如:
<div id="app">
<my-component @click.native="handleClick"></my-component> //点击就会触发父组件的方法
</div>
<script>
Vue.component('my-component', {
template: `
<button>按钮</button>
`
})
const vm = new Vue({
el: '#app',
methods: {
handleClick() {
console.log('click.')
}
},
})
</script>
自定义事件的改变,在vue2中自定义事件,通过子组件的实例mounted里面自定义事件,在组件上这个事件触发父元素的方法,而在vue3中并不需去操作父元素,前提是父元素必须有一个值来接收,然后只需要在子元素进行操作就行,
知识点:新增了emits,update:
例子:
<div id="root">
{{counted}}
<counter v-model:total="counted"></counter> //首先通过v-model:一个自定义属性绑定的是要传到父元素里面的值
</div>
<script>
const app = Vue.createApp({
data() {
return {
counted: 0,
}
}
})
app.component('counter', {
props: ['total'], //子组件通过props接收属性
emits: ['update:total'], //这里update是必须写的后面跟props接收的total属性
data() {
return {
count: 0,
}
},
methods: {
handleClick() {
this.count++
this.$emit('update:total', this.count) //这里同样要抛出这个update:total,第二个参数是传给父元素的值,前提是父元素必须有一个值来接收,
}
},
template: `
<button @click="handleClick">click</button>
`
})
app.mount('#root')
</script>
emits:里面的事件必须是自定义事件,不可以是浏览器自带的事件名
还可以写一个对象的形式。里面是一个回调函数,返回true才会触发,返回false会出现一个警告
emits: {
// click: null,
myEvent: (num) => {
// 返回值是个true/false
// true 可以触发事件
// false 不触发事件
if (num > 10) {
return true
} else {
return false
}
}
},
methods: {
handleClick() {
this.$emit('myEvent', 9)//这里传的参数是9,会出现一个警告
}
}
11.插槽
一个不带 `name` 的 `<slot>` 出口会带有隐含的名字“default”。
#list是插槽相当于v-slot:list
不带name的插槽相当于v-slot:defult
作用域插槽
有时让插槽内容能够访问到组件的数据是很有用的。当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式
例子:
实现:这里的插槽就会访问到组件中的数据,并进行使用,之前写个span标签里面的内容是自己写死的,并不能访问到组件data里面的数据
<body>
<div id="root">
<todo-list #list="{ index, item }"> //这里#list是插槽相当于v-slot:list
<span>{{index}}</span> -
<span>{{item}}</span> //这里的插槽就会访问到组件中的数据,并进行使用,之前写个span标签里面的内容是自己写死的,并不能访问到组件data里面的数据
</todo-list>
</div>
<script>
const app = Vue.createApp({
})
app.component('todo-list', {
data() {
return {
items: ['feed a cat', 'Buy milk']
}
},
template: `
<ul>
<li v-for="(item, index) in items">
<slot name="list" :item="item" :index="index"></slot>
</li>
</ul>
`
})
app.mount('#root')
</script>
12.provider 与inject
相当于react里面context上下文,在上层组件使用provider,在各个子组件使用inject接收父组件传递过来的值,如果上层组件中传递过来的state的值改变了,那么子组件通过inject接收的值是不变的,要想变的话要从Vue下面去一个方法computed,这个计算属性与options中API的computed不一样
<script>
const { createApp, computed } = Vue
const app = createApp({
})
// app.config.unwrapInjectedRef = true
app.component('TodoList', {
template: `
<div>{{str}}</div>
<slot></slot>
`,
data() {
return {
str: 'hello'
}
},
// provide: {
// msg: 'hello'
// }
provide() {
return {
msg: Vue.computed(() => this.str) //通过computed进行传递,当str通过下面的方法改变之后,在通过inject的数据也会改变
}
},
mounted() {
setTimeout(() => {
this.str = 'world'
}, 2000)
},
})
app.component('TodoItem', {
template: `
<div>TodoItem</div>
<slot></slot>
`
})
app.component('TodoListFooter', {
template: `
<div>TodoListFooter</div>
<slot></slot>
`
})
app.component('ClearTodosButton', {
template: `
<div>ClearTodosButton</div>
`
})
app.component('TodoListStatistics', {
inject: ['msg'], //在这里进行接收
template: `
<div>{{msg}} TodoListStatistics</div> //渲染到页面
`,
mounted(){
console.log(this.msg._value);
this.msg._value=2222
console.log(this.msg._value);
}
},
)
app.mount('#root')
13.异步加载组件
defineAsyncComponent 需要用到vue里面的这个方法,返回的是一个promise对象
模拟一个懒加载,先加载loading......两秒之后在加载异步组件
知识点: <suspense>组件,懒加载的组件
<div id="root">
<suspense>
<template #default> //这个是默认加载
<async-example></async-example>
</template>
<template #fallback> //这里是首先加载
<div>
loading...
</div>
</template>
</suspense>
</div>
<script>
const { createApp, defineAsyncComponent } = Vue
const app = createApp({})
const AsyncComp = defineAsyncComponent(
() =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
template: '<div>I am async!</div>'
})
}, 3000)
})
)
app.component('async-example', AsyncComp)
app.mount('#root')
</script>
14.$children在vue3中已经废弃
15.is属性的改变
在vue2中的动态组建中:is后面是跟的该渲染哪个组件,但是有个问题就是,如果你传的不是组件是一个自定义属性呢,这样的话就会出现冲突,所以在vue3中通过 <tr is="vue:mytr"></tr>组件前面加一个vue:来进行写动态组件,is也不需要写冒号
例子
<body>
<div id="root">
<div :is="myDiv"></div>
<table>
<tr is="vue:mytr"></tr>
</table>
</div>
<script>
const app = Vue.createApp({
data() {
return {
myDiv: 'abc'
}
}
})
app.component('mytr', {
template: `
<tr><td>abc</td></tr>
`
})
app.mount('#root')
</script>