import {h} from 'vue'
//向下兼容,可以写入vue2中的data配置项
module default {
name: 'App',
setup(){
//数据
let name = '张三',
let age = 18,
//方法
function sayHello(){
console.log(name)
},
//f返回一个对象(常用)
return {
name,
age,
sayHello
}
//返回一个函数(渲染函数)
//return () => {return h('h1','学习')}
return () => h('h1','学习')
}
}
//father
<template>
<HelloWorld :message="str" />
</template>
<script>
import { ref } from 'vue'
import HelloWorld from './components/HelloWorld.vue'
export default {
name: 'App',
components: {
HelloWorld
},
setup() {
const str = ref('helloword')
return {
str
}
}
}
</script>
//children
<script>
export default {
//必须在这里先定义props
props: {
message: {
type: String,
default: 'nihao'
}
},
setup(props) {
console.log(props.message) // 'helloword'
}
}
</script>
//语法糖写法
<script setup>
const props = defineProps({
foo: {
type: String,
default: '1212312312312312312'
}
})
console.log(props.message) // 'helloword'
}
</script>
<script setup>
import {reactive} from "vue"
let obj=reactive(
{
name:"aaa",
age:22,
family:[{relation: 'father',age:52}, { relation: 'mother', age: 55 }],
}
)
let fn=()=>{
let fn=()=>{
//直接用点语法,因为reactive使用的是Proxy代理了整个对象
obj.age++
obj.family[0].age++
obj.family[1].age++
}
监视reactive定义的响应式数据的时候:oldValue无法获取到正确的值,强制开启了深度监视(deep配置无效)
<template>
<h1>当前求和为: {{sum}}</h1>
<button @click="sum++">点我+1</button>
<hr>
<h1>当前信息为: {{msg}}</h1>
<button @click="msg+='!' ">修改信息</button>
<hr>
<h2>姓名: {{person.name}}</h2>
<h2>年龄: {{person.age}}</h2>
<button @click="person.name += '~' ">修改姓名</button>
<button @click="person.age++">增长年龄</button>
</template>
<script>
//使用setup的注意事项
import { watch,ref,reactive } from 'vue'
export default {
name: 'test5',
props: ['msg'],
emits:['hello'],
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name: '张三',
age: 18,
job:{
salary: '15k'
},
})
//情况一:监视ref所定义的一个响应式数据
watch(sum, (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
})
//情况二:监视ref所定义的多个响应式数据
watch([sum,msg], (newValue,oldValue)=>{
console.log('新的值',newValue); //['sum的newValue', 'msg的newValue']
console.log('旧的值',oldValue); //['sum的oldValue', 'msg的oldValue']
},{immediate: true,deep:true}) //这里vue3的deep是有点小问题的,可以不用deep,(隐式强制deep)
//情况三:监视reactive定义的所有响应式数据,
//1.此处无法获取正确的oldValue(newValue与oldValue是一致值),且目前无法解决
//2.强制开启了深度监视(deep配置无效)
/**
* 备注:
* 1. 当你监听一个响应式对象的时候,这里的newVal和oldVal是一样的,因为他们是同一个对象【引用地址一样】,
* 即使里面的属性值会发生变化,但主体对象引用地址不变。这不是一个bug。要想不一样除非这里把对象都换了
*
* 2. 当你监听一个响应式对象的时候,vue3会隐式的创建一个深层监听,即对象里只要有变化就会被调用。
* 这也解释了你说的deep配置无效,这里是强制的。
*/
watch(person, (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
})
//情况四:监视reactive对象中某一个属性的值,
//注意: 这里监视某一个属性的时候可以监听到oldValue
watch(()=>person.name, (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
})
//情况五:监视reactive对象中某一些属性的值
watch([()=>person.name,()=>person.age], (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
})
//特殊情况: 监视reactive响应式数据中深层次的对象,此时deep的配置奏效了
//注意: 这里监视某一个属性的时候可以监听到oldValue ????
watch(()=>person.job, (newValue,oldValue)=>{
console.log('新的值',newValue);
console.log('旧的值',oldValue);
},{deep:true}) //此时deep有用
return {
sum,
msg,
person,
}
},
}
</script>
备注:
<template>
<h2>工资: {{person.job.salary}}</h2>
<button @click="person.job.salary++">增长工资</button>
</template>
<script>
import { ref,reactive,watchEffect } from 'vue'
export default {
name: 'test5',
props: ['msg'],
emits:['hello'],
setup(){
let sum = ref(0)
let msg = ref('你好啊')
let person = reactive({
name: '张三',
age: 18,
job:{
salary: 15
},
})
//用处: 如果是比较复杂的业务,发票报销等,那就不许需要去监听其他依赖,只要发生变化,立马重新回调
//注重逻辑过程,你发生改变了我就重新执行回调,不用就不执行,只执行一次
watchEffect(() => {
//这里面你用到了谁就监视谁,里面就发生回调
console.log(person.job.salary, ' 111111');
})
return {
sum,
msg,
person,
}
},
}
</script>
computed是vue的计算属性,是根据依赖关系进行缓存的计算,只有在它的相关依赖发生改变时才会进行更新
<template>
<div>
<div>
姓:<input type="text" v-model="firstName" />
</div>
<div>
名:<input type="text" v-model="lastName" />
</div>
<!-- 调用两次fullName -->
<div>姓名:{{ fullName }}</div>
<div>姓名:{{ fullName }}</div>
</div>
</template>
<script>
export default {
data() {
return {
firstName: "张",
lastName: "三",
};
},
computed: {
fullName() {
console.log("这是fullName");
return this.firstName + this.lastName;
}
}
};
</script>
import { ref, reactive, computed } from 'vue'
export default {
name: 'HelloWorld',
setup() {
let msg = ref('你好啊')
let person = reactive({
name: '张三',
job: {
salary: 15
},
reverseMsg: computed(() => {
return msg.value.split('').reverse().join('')
})
})
let strings = computed(() => {
//返回一个带有value属性的对象
return msg.value.split('').reverse().join('')
})
console.log(strings.value, person.reverseMsg)
return {
msg,
person,
}
},
}
</script>
<template>
<h2>姓名: {{name2}}</h2>
<h2>年龄: {{age}}</h2>
<button @click="name += '~' ">修改姓名</button>
<button @click="age++">增长年龄</button>
</template>
<script>
//使用setup的注意事项
import { reactive, toRef, toRefs } from 'vue'
export default {
setup(){
let person = reactive({
name: '张三',
age: 18,
job:{
salary: '15k'
},
})
//toRef
const name2 = toRef(person,'name') //第一个参数是对象,第二个参数是键名
console.log('toRef转变的是',name2); //ref定义的对象
//toRefs,批量处理对象的所有属性
//const x = toRefs(person)
//console.log('toRefs转变的是',x); //是一个对象
return {
person,
name2,
...toRefs(person)
}
},
}
</script>
<script setup>
// 父组件
import { provide } from 'vue';
import ChildVue from './components/Child.vue';
let car = reactive({
name: '奔驰',
price: '40w'
})
provide('car',car) //给自己的后代组件传递数据
</script>
<script setup>
// 子组件/孙组件
import { inject } from 'vue';
et car = inject('car') //拿到父组件的数据
const {name, price} = car
</script>
<template>
<div>
<h1>姓名:{{name}}</h1> //响应式发生变化
<h2>年龄:{{age}}</h2> //响应式发生变化
<h3>喜欢的水果:{{likeFood.fruits.apple}}</h3> //普通对象不会发生变化
<button @click="name += '~'">修改姓名</button>
<button @click="age++">修改年龄</button>
<button @click="likeFood.fruits.apple += '!'">修改水果</button>
</div>
</template>
<script>
import {reactive, toRefs, shallowReactive} from 'vue'
export default {
name: "App",
setup() {
// 定义了一段数据
let person = shallowReactive({ // 只将第一层数据做了响应式处理
name: '张三',
age: 18,
likeFood: {
fruits:{
apple: '苹果' // 深层次的数据将会是一个普通的对象
}
}
})
// 将数据返回出去
return {
...toRefs(person)
}
}
};
</script>
// 我们调用了shallowRef方法传递了基本数据类型 我们可以看到 当前属性是有响应式的
<div>
<h1>姓名:{{ sum}}</h1> // 响应式数据变化
<button @click="sum++">点击+</button>
</div>
setup() {
// 定义了一段数据
let sum = shallowRef(0);
// 将数据返回出去
return {
sum,
};
},
//但是我们现在传递一个对象进去 我们可以看到 传递的对象将变成一个普通对象 不在具有响应式功能了
<div>
<h1>姓名:{{ sum.n}}</h1> // 数据不会发生变化
<button @click="sum.n++">点击+</button>
</div>
setup() {
// 定义了一段数据
let sum = shallowRef({ // sum将不在是一个响应式对象
n: 0
});
// 将数据返回出去
return {
sum,
};
},
<template>
<h4>当前求和为:{{ sum }}</h4>
<button @click="sum++">点我++</button>
<hr>
<h2>姓名:{{ name }}</h2>
<h2>年龄:{{ age }}</h2>
<h2>薪资:{{ job.j1.salary }}K</h2>
<button @click="name += '~'">修改姓名</button>
<button @click="age++">增长年龄</button>
<button @click="job.j1.salary++">涨薪</button>
</template>
<script>
//shallowReadonly
import { ref, reactive, toRefs, readonly, shallowReadonly } from 'vue'
export default {
setup() {
//数据
let sum = ref(0)
let person = reactive({
name: '张三',
age: 18,
job: {
j1: {
salary: 20
}
}
})
person = readonly(person) //全部都不能修改
person = shallowReadonly(person) //salary可以修改
sum = readonly(sum) //不能修改
sum = shallowReadonly(sum) //不能修改
//返回一个对象(常用)
return {
sum,
...toRefs(person)
}
}
}
</script>
import {reactive,toRaw,markRaw} from 'vue'
setup(){
let person = reactive({
name: '张三',
})
function showRawPerson(){
const p = toRaw(person)
p.age++
console.log(p)
}
function addCar(){
let car = {name: '奔驰'}
person.car = markRaw(car) //一旦这么做时候,他就永远不能当成响应式数据去做了
}
}
<script>
import { ref, reactive, toRefs, readonly, isRef, isReactive, isReadonly, isProxy } from 'vue'
export default {
name: 'App',
setup() {
let car = reactive({ name: '奔驰', price: '40W' })
let sum = ref(0)
let car2 = readonly(car)
console.log(isRef(sum)) //true
console.log(isReactive(car)) //true
console.log(isReadonly(car2)) //true
console.log(isProxy(car)) //true
console.log(isProxy(sum)) //false
return { ...toRefs(car) }
}
}
</script>
onMounted()
onUpdated()
onUnmounted()
onBeforeMount()
onBeforeUpdate()
onBeforeUnmount()
onActivated()
onDeactivated()
onServerPrefetch()
父组件通过ref定义子组件实例,通过调用实例获得子组件的数据和方法
<!-- 父组件 app.vue -->
<template>
<div class="itxst">
<!-- 使用 ref 指令关联子组件 -->
<child ref="childComp"/>
<button @click="onTry">点击试一试</button>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
import child from "./child.vue";
//定义子组件实例,名称要和上面的ref相同
const childComp = ref(null);
//访问demo组件的方法或对象
const onTry = () => {
//获取到子组件的 title 数据
let msg = childComp.value.state.title;
//调用子组件的 play方法
childComp.value.play();
};
</script>
子组件通过defineExpose暴露对象和方法
<!--子组件名称 child.vue -->
<template>
<div class="itxst">
{{ state.title }}
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
//定义一个变量
const state = reactive({
title: "www.itxst.com",
});
//定义一个方法
const play = () => {
state.title = "你调用了子组件的方法";
};
//暴露state和play方法
defineExpose({
state,
play,
});
</script>
父组件
<template>
<Child @sayHello="handle"></Child>
</template>
<script lang="ts" setup>
import Child from '../../components/child.vue';
const handle = () => {
console.log('子组件调用了父组件的方法')
}
</script>
子组件
<template>
<view>我是子组件</view>
<button @click="say">调用父组件的方法</button>
</template>
<script lang="ts" setup>
const emit = defineEmits(["sayHello"])
const say = () => {
emit('sayHello')
}
</script>
import Vue from 'vue'
Vue.nextTick(() => {})
Vue3中引入tree shaking特性,将全局API进行分块,如果不使用某些功能,就不会出现在打包文件中。
import { nextTick, observable } from 'vue'
nextTick(() => {})
在更快方面,Vue3主要做出的优化有:diff算法优化、静态提升、事件监听缓存、SSR优化。
diff算法详情
vue3在diff算法中相比于vue2增加了静态标记,作用是为会发生变化的地方添加一个flag标记,下次发生变化的时候直接找该地方进行比较。比如下述例子:
<template>
<div id="content">
<p class="text">静态文本</p>
<p class="text">静态文本</p>
<p class="text">{{ message }}</p>
<p class="text">静态文本</p>
...
<p class="text">静态文本</p>
</div>
</template>
这里的组件内部只有一个动态节点,剩余一堆都是静态节点,所以这里很多diff和遍历其实都是不需要的,会造成性能浪费。此时会给{{message}}标签标记一个flag=1,其他的静态文本会标记flag=-1,代表永远都不会进行diff操作。这样比较的时候可以直接选择flag=1的节点进行比较。
Vue3对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用,这样就可以免去了重复的创建节点。
默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化,但是因为是同一个函数,所以没必要去追踪它的变化,想办法将它直接缓存起来复用就会提升性能。因此要打开事件监听缓存,这样静态标记就不存在了,这部分内容也就不会进行比较了。
当静态内容达到一定量级的时候,会用createStaticVNode方法在客户端去生成一个static node,这些静态node,会直接innerHtml,就不需要创建对象,然后根据对象渲染。
vue3的更友好体现在,兼顾vue2的options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码的复用能力,同时优化了vue2中数据劫持,vue2中通过Object.defineProperty,这个API并不能检测对象属性的添加和删除,而Vue3通过proxy解决了这个问题。
Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个对象现有属性,并返回此对象。但是,如果存在深层的嵌套对象关系,需要深层次的进行监听,造成了性能的极大问题。
Proxy的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作,这就完全可以代理所有属性,包括新增属性和删除属性,并且Proxy可以监听数组的变化。