npm搭建项目
安装或升级@vue/cli
npm install -g @vue/cli
查看版本@vue/cli
vue --version
vue create hello-world
npm run serve
报错
Cannot find module 'vue-loader-v16/package.json'
//解决办法
//先卸载vue-loader-v16依赖
npm uninstall vue-loader-v16
//使用cnpm安装vue-loader-v16依赖
cnpm i vue-loader-v16
Vite 搭建项目
npm init vite-app <project name>
npm install
npm run dev
setup
Vue3中新的配置项,值为一个函数
组件中用到的数据,方法等,均要配置在setup中
setup函数的两种返回值
- 若返回一个对象,则对象中的属性,方法在模板中可直接使用
- 若返回一个渲染函数,则可以自定义渲染内容
import {h} from 'vue'
return ()=> h('h1','HelloWorld')
vue2.x配置(data,methods....)中可以访问setup中的属性,方法
但在setup中不能访问到vue2.x配置(data,methods....)
如果有重名setup优先
在beforeCreate之前执行一次,this是undifined:
- props:值为对象,包含:组件外部传递,组件内部声明接收
- context:上下文对象
- attrs:值为对象,包含:组件外部传递,但没有在props配置中声明的属性,相当于this.$attrs
- slots:插槽内容,相当于this.$slots
- emit:自定义事件的函数,相当于this.$emit
ref函数
定义一个响应式数据
js中操作数据:xxx.value
模板中读取数据:<div>{{xxx}}</div>
接收的数据可以是基本数据类型,也可以是对象类型
基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的
对象类型的数据:内部"求助"了Vue3.0中的reactive函数
reactive函数
定义一个对象类型的响应式数据
通过使用Proxy来实现响应式,并通过Reflect操作源对象内部的数据。
vue3.0中的响应式原理
vue2.x的响应式
实现原理:
- 对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截。
- 数组类型:通过重写更新数组的一系列方法来实现拦截。
Object.defineProperty(data,'count',{
get(){},
set(){}
})
vue3.0的响应式
实现原理:
- 通过proxy(代理):拦截对象中任意属性的变化,包括:属性的读写、添加、删除等
- 通过reflect(反射):对被代理对象的属性进行操作
// 源数据
let person = {
name: 'jan',
age: 18
}
const p = new Proxy(person, {
get(target, propName) {
// console.log(target, propName)
return target[propName]
},
set(target,propName,value) {
target[propName] = value
},
deleteProperty(target,propName){
return delete target[propName]
}
})
computed函数
与vue2中的配置一致
let fullName = computed(() => {
return person.firstName + '-' + person.lastName
})
let fullName = computed(() => {
get() {
return person.firstName + '-' + person.lastName
},
set(val) {
const nameArr = val.split('-')
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
watch函数
与vue2中的配置一致
监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启深度监听(deep配置失效)
监视reactive定义的响应式数据中某个属性时:deep配置有效
//监听ref定义的响应式数据
watch(count, (newVal, oldVal) => {
console.log(newVal, oldVal)
},{immediate:true})
// 监听ref定义的多个响应式数据
watch([count,count], (newVal, oldVal) => {
console.log(newVal, oldVal)
},{immediate:true})
//监听reactive定义的响应式数据的全部属性
watch(person, (newVal, oldVal) => {
console.log(newVal, oldVal)
},{deep:true})//此处deep配置无效
//监听reactive定义的响应式数据的某个属性
watch(()=>person.age, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
//监听reactive定义的响应式数据的某些属性
watch([()=>person.age,()=>person.name], (newVal, oldVal) => {
console.log(newVal, oldVal)
})
//特殊情况:监听reactive定义的对象中的某个属性
watch(()=>person.job, (newVal, oldVal) => {
console.log(newVal, oldVal)
},{deep:true})
watchEffect函数
watch:既要指明监视的属性,也要指明监视的回调
watchEffect:不用指明监视哪个属性,监视的回调中用到哪个属性
watchEffect注重的是过程(回调函数的函数体),不用写返回值
// watchEffect
watchEffect(()=>{
const x1 = count.value
console.log('watchEffect所指定的回调执行了')
})
生命周期
vue2:Vue 实例 — Vue.js
vue3:应用 & 组件实例 | Vue.js
组合式 API:
import {
onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted
} from 'vue'
onBeforeMount(()=>{
console.log('-- onBeforeMount --')
})
onMounted(()=>{
console.log('-- onMounted --')
})
onBeforeUpdate(()=>{
console.log('-- onBeforeUpdate --')
})
onUpdated(()=>{
console.log('-- onUpdated --')
})
onBeforeUnmount(()=>{
console.log('-- onBeforeUnmount --')
})
onUnmounted(()=>{
console.log('-- onUnmounted --')
})
其他Composition API
shallowReactive与shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)
shallowRef:只处理基本数据类型的响应式,不处理对象的响应式
如果有一个对象数据,结构比较深,但变化时只是外层属性变化,用shallowReactive
如果有一个对象数据,后续功能不会修改该对象中的属性,而是生成新的对象来替换,用shallowRef
readonly与shallowReadonly
readonly:让一个响应式数据变为只读(深只读)
shallowReadonly:让一个响应式数据变为只读(浅只读)
toRaw与markRaw
toRaw:将一个reactive的响应式对象转为普通对象
用于读取响应式对象的普通对象,对这个普通对象的所有操作,不会引起页面更新
markRaw:标记一个对象,使其永远不会再成为响应式对象
有些值不应被设置为响应式的,例如复杂的第三方类库
当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
customRef
创建一个自定义ref,并对其依赖项跟踪和更新触发进行显式控制
setup(){
let keyword = myRef('hello')//使用自定义ref
function myRef(val){
return customRef((track,trigger)=>{
return{
get(){
track()
return val
},
set(value){
val = value
trigger()
}
}
})
}
return {
keyword
}
}
provide与inject
实现祖孙组件间通信
父组件有一个provide选项来提供数据,子组件有一个inject选项来开始使用这些数据
//祖组件中
setup(){
let car = reactive({name:'特斯拉',price:'40W'})
provide('car',car)
}
//孙组件中
setup(){
let car = inject('car')
return {car}
}
响应式数据的判断
isRef //判断是否是ref对象
isReactive //判断是否是reactive创建的响应式代理
isReadonly //判断是否是readyonly创建的只读代理
isProxy //判断是否是reactive或readonly方法创建的代理
新的组件
Fragment
- 在vue2中,组件必须有一个根标签
- 在vue3中,组件可以没有根标签,内部会将多个标签包含在一个Fragment虚拟元素中
- 减少标签层级,减少内存占用
Teleport
能够将组件html结构移动到指定位置
<!-- to="移动位置" -->
<teleport to="">
<div v-if="isShow">
<div>
<h3>我是一个dialog</h3>
<button @click="isShow=false">关闭dialog</button>
</div>
</div>
</teleport>
Suspense
等待异步组件时渲染一些额外内容
//异步引入组件
import {defineAsyncComponent} from 'vue'
const HelloWorld = defineAsyncComponent(()=>{import('./components/HelloWorld.vue')})
//使用Suspense包裹组件,并配置好default与fallback
<Suspense>
<template v-slot:default>
<HelloWorld />
</template>
<template v-slot:fallback>
<h5>加载中...</h5>
</template>
</Suspense>
其他
全局API的转移
vue2有许多全局API和配置
//注册全局组件
Vue.component('myButton',{
data:()=>({count:0}),
template:`<button @click="count++">{{count}}</button>`
})
//注册全局指令
Vue.directive('focus',{
inserted:el=>el.focus()
})
vue3将全局API调整到应用实例(app)上
Vue.prototype -- app.config.globalProperties
其他改变
- data选项应始终声明为一个函数
- 过渡类名的更改
/*vue2.x写法*/
.v-enter,
.v-leave-to{
opacity:0;
}
.v-enter-to,
.v-leave{
opacity:1;
}
/*vue3.0写法*/
.v-enter-from,
.v-leave-to{
opacity:0;
}
.v-enter-to,
.v-leave-from{
opacity:1;
}
- 移除keyCode作为v-on的修饰符,同时也不再支持config.keyCodes
- 移除v-on.native修饰符
//父组件中绑定事件
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
//子组件中声明自定义事件
<script>
export default{
emits:['close']
}
</script>
- 移除过滤器(fillter)