vue-cli v4.5/ vite
vite为何启动快?
开发环境使用ES6 Module,无需打包;但是生产环境使用rollop,并不会快很多。
webpack需要转es5。
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
this 为 undefined,可通过 getCurrentInstance() 获取组件实例。
export default {
name:'App',
components:{Demo},
props:['msg','school'],//props如果不声明接收,值会在$attrs(vue2)
emits:['hello'],//子组件向父组件传值要声明emit(vue2)
//在beforeCreate之前执行一次,this是undefined
setup(props,context){//context为上下文对象,包含attrs/slots/emit(同vue2)
//数据
let name = '张三'
let age = 18
//方法
function sayHello(){
alert(`我叫${name},我${age}岁了,你好啊!`)
}
//返回一个对象(常用)
return {
name,
age,
sayHello,
}
}
}
实现原理:
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作。
//proxy增删改属性都能获取到;Object移植到Reflect:
//#region
const p = new Proxy(person,{
//有人读取p的某个属性时调用
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`)
//return target[propName]
return Reflect.get(target,propName)
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去更新界面了!`)
//target[propName] = value
Reflect.set(target,propName,value)
},
//有人删除p的某个属性时调用
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新界面了!`)
//return delete target[propName]
return Reflect.deleteProperty(target,propName)
}
})
//#endregion
ref函数
ref函数用来定义一个响应式的数据。(基本数据)通过.value修改值
基本类型的数据:响应式依然是靠Object.defineProperty()的get与set完成的。
对象类型的数据:内部求助了Vue3.0中的一个新函数—— reactive函数。
一个人的信息
姓名:{{name}}
年龄:{{age}}
工作种类:{{job.type}}
工作薪水:{{job.salary}}
why .value
vue3用了proxy实现响应式,那如果用reactive定义引用类型的话是正常的,如果用ref是一个基本类型的值,就没办法实现proxy的拦截,所以vue3中对ref定义的值进行了包装,变成一个对象实现proxy进行拦截达到响应式。
reactive函数
reactive函数定义一个对象类型的响应式数据(深层次的)。
接收一个对象或数组,返回一个proxy对象。
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
一个人的信息
姓名:{{person.name}}
年龄:{{person.age}}
工作种类:{{person.job.type}}
工作薪水:{{person.job.salary}}
爱好:{{person.hobby}}
测试的数据c:{{person.job.a.b.c}}
toRef:创建一个 ref 对象,返回ObjectRefImpl对象,用于将响应式对象中的某个属性单独提供给外部使用。两者保持引用关系
toRefs:批量创建多个 ref 对象。
{{person}}
姓名:{{name}}
年龄:{{age}}
薪资:{{job.j1.salary}}K
一个人的信息
姓:
名:
全名:{{person.fullName}}
全名:
age1具有响应式
watch
监视reactive定义的响应式数据时:oldValue无法正确获取、强制开启了深度监视(deep配置失效)。
监视reactive定义的响应式数据中某个属性时:deep配置有效。
当前求和为:{{sum}}
当前的信息为:{{msg}}
姓名:{{person.name}}
年龄:{{person.age}}
薪资:{{person.job.j1.salary}}K
watchEffect
watch:既要指明监视的属性,也要指明监视的回调。
watchEffect:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性。初始化时执行一次,用于收集要监听的数据。
当前求和为:{{sum}}
姓名:{{person.name}}
年龄:{{person.age}}
薪资:{{person.job.j1.salary}}K
销毁阶段的两个名字改了,创建阶段的2个钩子在setup中调用。
当前求和为:{{sum}}
//祖组件
setup(){
......
let car = reactive({name:'奔驰',price:'40万'})
provide('car',car)
......
}
//后代组件
setup(props,context){
......
const car = inject('car')
return {car}
......
}
本质是一个函数,把setup函数中使用的Composition API进行了封装,类似于vue2.x中的mixin。
import {reactive,onMounted,onBeforeUnmount} from 'vue'
export default function (){
//实现鼠标“打点”相关的数据
let point = reactive({
x:0,
y:0
})
//实现鼠标“打点”相关的方法
function savePoint(event){
point.x = event.pageX
point.y = event.pageY
console.log(event.pageX,event.pageY)
}
//实现鼠标“打点”相关的生命周期钩子
onMounted(()=>{
window.addEventListener('click',savePoint)
})
onBeforeUnmount(()=>{
window.removeEventListener('click',savePoint)
})
return point
}
当前求和为:{{sum}}
当前点击时鼠标的坐标为:x:{{point.x}},y:{{point.y}}
shallowReactive 与 shallowRef
shallowReactive:只处理对象最外层属性的响应式(浅响应式)。
shallowRef:只处理基本数据类型的响应式, 不进行对象的响应式处理。
readonly 与 shallowReadonly
readonly: 让一个响应式数据变为只读的(深只读)。
shallowReadonly:让一个响应式数据变为只读的(浅只读)。
toRaw 与 markRaw
toRaw:将一个由reactive生成的响应式对象转为普通对象
markRaw:标记一个对象,使其永远不会再成为响应式对象。
customRef
{{keyWord}}
响应式数据的判断
isRef: 检查一个值是否为一个 ref 对象
isReactive: 检查一个对象是否是由 `reactive` 创建的响应式代理
isReadonly: 检查一个对象是否是由 `readonly` 创建的只读代理
isProxy: 检查一个对象是否是由 `reactive` 或者 `readonly` 方法创建的代理
Fragment
在Vue2中: 组件必须有一个根标签
在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中
好处: 减少标签层级, 减小内存占用
Teleport
`Teleport` 是一种能够将我们的组件html结构移动到指定位置的技术。
我是一个弹窗
Suspense
异步渲染子组件:通过插槽控制显示
我是App组件
稍等,加载中...
子组件需要声明自定义事件;
可以用computed实现
按需实现深度监听
2. PatchFlag & hoistStatic & cacheHandler
https://vue-next-template-explorer.netlify.app
PatchFlag:编译模板时,给动态节点做标记,如TEXT/ CLASS/ PROPS。diff算法根据标记做对比。
hoistStatic:将静态节点的定义,提升到父作用域,缓存起来;多个相邻的静态节点合并。
cacheHandler:事件缓存。
3.SSR优化
静态节点直接输出,绕过了vdom
4. tree shaking
编译时,根据不同的情况,引入不同的API
//.jsx文件
import { defineComponent } from 'vue'
export default defineComponent({//传递setup函数或组件配置
props: ['a'],
setup(props) {
const render = () => {
return Child {props.a}
}
return render
}
})
JSX本质是js代码,可以使用js的任何能力
template只能嵌入简单的js表达式,其他需要指令,如v-if
template | JSX | |
插值 | {{ msg }} | { msg } |
组件定义 | 大小写 | 大写 |
属性 | :name="name" | name={name} |
事件 | @click="demo(123)" | onClick={()=>demo(123)} |
条件 | v-if | {flagRef.value && |
循环 | v-for | {state.list.map(item => |
插槽 | v-slot | 通过函数传参实现 |
import { defineComponent } from 'vue'
import Child from './Child'
export default defineComponent(() => {
function render(msg) {
return msg: {msg} 123123
}
return () => {
return <>
Demo - JSX
>
}
})
import { defineComponent, ref } from 'vue'
export default defineComponent({
props: ['render'],
setup(props) {
const msgRef = ref('作用域插槽 Child - JSX')
return () => {
return {props.render(msgRef.value)}
}
}
})
定义属性defineProps;定义事件defineEmits;defineExpose暴露数据给父组件
{{countRef}}
{{name}}
Child2.vue
Child2 - name: {{props.name}}, age: {{props.age}}
Child3.vue
Child3