前言
基于vite 搭建的vue3.0项目看上去更优化;vue版本自动3.0版本
npm create vite@latest
yarn create vite
pnpm create vite
在这里我们都使用vue-cli搭建项目,方便对比。
一、主要内容–初步
项目搭建
脚手架搭建 【vue@2、@3】 项目命令一样,在选择是选择对应预设;
vue create hello-vue2
vue create hello-vue3
入口文件
vue@2:
import Vue from 'vue'
new Vue({
render: h => h(App)
}).$mount('#app') //替换'#app'
vue@3:
import { createApp } from 'vue'
createApp(App).mount('#app') //挂载'#app'中
App.vue
- vue3.0中入口文件使用mount( 挂载 ),不同于vue@2.x的$mount( 替换 );
- template标签中不在需要一个根元素;为了便于区分,子组件还是推荐一个根元素。
// @2会整个替换,@3只会覆盖内部,虽然一般我们不会改动index.html
<div id="app">
<p>vue@3中只有我会被替换</p>
</div>
createApp(App)
createApp( ):第一个参数为根组件选项对象,如App;
- 这个方法在入口文件在传入的是App完整的组件选项对象;
- 也可以在子组件中只传入html标签以外的组件选项对象,比如给当前子组件,这样就不会替换当前子组件的html内容;
// 这个data会作用于'#list-rendering'
const ListRendering = {
data() {
return {
todos: [
{ text: 'Learn JavaScript' },
{ text: 'Learn Vue' },
{ text: 'Build something awesome' }
]
}
}
}
Vue.createApp(ListRendering).mount('#list-rendering')
生命周期
创建前后:beforeCreate、created
挂载前后:beforeMount、mounted
更新前后:beforeUpdate、updated
销毁前后:beforeDestroy、destroyed(vue@2)
移除前后:beforeUnmount、unmounted(vue@3)
二、主要内容–使用中
v-show
共同点:v-show 不支持 template 元素,也不支持 v-else
<template v-if="ok">
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</template>
不同点:当 v-if 与 v-for 一起使用时(都不推荐这样用)
- vue@2:v-for 具有比 v-if 更高的优先级
- vue@3:v-if 具有比 v-for 更高的优先级,v-if 将没有权限访问 v-for 里的变量;
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
v-for 与 in、of
效果都一样,习惯上v-for in 遍历普通对象,v-for of 遍历数组;
- 遍历数组:(item,index)只有两个参数对应:数组子项、索引;
- 遍历对象:(value,name,index)可有三个参数对应:属性值、属性名、索引;
非prop的属性在自定义组件中
1、
默认情况:
都是加在自定义组件的根元素上,可以通过inheritAttrs: false
与v-bind="$attrs"
实现将传入的属性加到指定元素上;
2、$attrs:
包含所有非prop的属性,包括 class、style等;
vue@2:
- 对于class、style属性:会合并;
- 对于非上述属性,外部传入会替换组件内属性值;
vue@3:
- 对于class、style属性:会合并;
- 对于非上述属性,以内部原有为优先;
特殊地,
因为vue@3支持组件中没有根元素,所以如果是自定义组件没有根元素,就不会有默认情况;必须借助$attrs
指定要绑到元素,否则警告。
多事件处理
vue@3:新增触发多个事件;
注意:多事件,必须是绑定方法,而不是方法名
<!-- 这两个 one() 和 two() 将执行按钮点击事件 -->
<button @click="one($event), two($event)">
Submit
</button>
<!-- 这里都不触发 -->
<button @click="one, two">
Submit
</button>
子组件事件抛出
vue@3:子组件中新增组件选项emits: ['enlargeText']
,用于检验抛出的事件;
app.component('blog-post', {
props: ['title'],
emits: ['enlargeText'],
template: `
<div class="blog-post">
<h4>{{ title }}</h4>
<button @click="$emit('enlargeText')">
Enlarge text
</button>
</div>
`
})
在组件中使用v-model
vue@2:与效果原生一致,v-model='[变量名]'
<custom-input v-model="searchText"></custom-input>
//解析后
<custom-input
v-bind:value="searchText"
v-on:input="searchText = $event"
></custom-input>
//子组件
Vue.component('custom-input', {
props: ['value'],
template: `
<input
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
})
vue@3:支持参数:v-model:[参数名]
,
- 不写参数的话,内部默认使用使用
modelValue
作为 prop 和update:modelValue
作为事件 - 可以通过
v-model:【参数名】
修改默认,这样就可以在自定义组件上绑定多个v-model:new、v-model:new2、...
, - 区分
v-model.capitalize
这是自定义的修饰符;
//默认解析后
<custom-input
:model-value="searchText"
@update:model-value="searchText = $event"
></custom-input>
//默认子组件应该这样写
app.component('custom-input', {
props: ['modelValue'],
emits: ['update:modelValue'],
template: `
<input
:value="modelValue"
@input="$emit('update:modelValue', $event.target.value)"
>
`
})
v-model的修饰符
vue@2:支持.trim、.number 和 .lazy
vue@3:
- 支持
.trim、.number 和 .lazy
- 还支持自定义修饰符,与在组件使用
v-model
一样需要子组件配合; - 同样也分为两种:
- 第一种:
v-model
,修饰符被加到modelModifiers
中,形成this.modelModifiers={capitalize:true}
,然后子组件通过判断修饰符来进行响应操作; - 第二种:带参数
v-model:title
,修饰符使用类似,modelValue
变为title
,modelModifiers
名称变为titleModifiers
- 第一种:
//默认情况下
<my-component v-model.capitalize="myText"></my-component>
//默认情况下
app.component('my-component', {
props: {
modelValue: String,
modelModifiers: {
default: () => ({})//自定义的修饰符以对象形式被填到这里,默认为{}
}
},
emits: ['update:modelValue'],
methods: {
emitValue(e) {
let value = e.target.value
if (this.modelModifiers.capitalize) {
value = value.charAt(0).toUpperCase() + value.slice(1)
}
this.$emit('update:modelValue', value)
}
},
template: `<input
type="text"
:value="modelValue"
@input="emitValue">`
})
Provide / Inject 详情
vue@2、vue@3都有
- 父组件提供,内部所有子孙组件都可以接收;
Provide
提供时有三种情况:对于传递普通字符串;对于传递组件内部数据;对于需要在子组件中传递可响应数据;
Provide提供:
app.component('todo-list', {
data() {
return {
todos: ['Feed a cat', 'Buy tickets']
}
},
------提供普通数据
provide: {
user: 'John Doe'
},
-----提供组件内部数据,后续子组件中不会从变化自动更新
provide() {
return {
todoLength: this.todos.length
}
},
------会响应到子组件中
provide() {
return {
todoLength: Vue.computed(() => this.todos.length)
}
}
template: `
<div>
{{ todos.length }}
<!-- 模板的其余部分 -->
</div>
`
})
Inject接收:
app.component('todo-list-statistics', {
inject: ['user'],
created() {
console.log(`Injected property: ${this.user}`) // > 注入 property: John Doe
}
})
关于过渡、动画中的硬件加速
诸如 perspective、backface-visibility 和 transform:translateZ(x) 等 property 将让浏览器知道你需要硬件加速。
//样式中使用一个即可
perspective: 1000px;
backface-visibility: hidden;
transform: translateZ(0);
自定义指令
1、全局注册指令
- vue@2:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
- vue@3:
const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
mounted(el) {
// Focus the element
el.focus()
}
})
2、局部注册指令
- vue@2:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
- vue@3:
directives: {
focus: {
// 指令的定义
mounted(el) {
el.focus()
}
}
}
3、钩子函数
vue@2 | vue@3 | 说明 |
---|---|---|
bind | beforeMount | 当指令第一次绑定到元素并且在挂载父组件之前调用 |
inserted | mounted | 在挂载绑定元素的父组件时调用。 |
update | 所在组件的 VNode 更新时调用 | |
beforeUpdate | 在更新包含组件的 VNode 之前调用 | |
componentUpdated | updated | 指令所在组件的 VNode 及其子 VNode 全部更新后调用。 |
beforeUnmount | 在卸载绑定元素的父组件之前调用 | |
unbind | unmounted | 只调用一次,指令与元素解绑时调用 |
4、具体参数
el、binding(略有不同)、vnode、oldVnode
5、函数简写
vue@2:默认是触发bind、update
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
vue@3:默认是触发mounted 和 updated
app.directive('pin', (el, binding) => {
el.style.position = 'fixed'
const s = binding.arg || 'top'
el.style[s] = binding.value + 'px'
})