Vue2.x:https://template-explorer.vuejs.org/#
Vue3.0:https://vue-next-template-explorer.netlify.app/#
export const enum PatchFlags {
TEXT = 1, // 动态文本节点
CLASS = 1 << 1, // 2 // 动态 class
STYLE = 1 << 2, // 4 // 动态 style
PROPS = 1 << 3, // 8 // 动态属性,但不包含类名和样式
FULL_PROPS = 1 << 4, // 16 // 具有动态 key 属性,当 key 改变时,需要进行完整的 diff 比较。
HYDRATE_EVENTS = 1 << 5, // 32 // 带有监听事件的节点
STABLE_FRAGMENT = 1 << 6, // 64 // 一个不会改变子节点顺序的 fragment
KEYED_FRAGMENT = 1 << 7, // 128 // 带有 key 属性的 fragment 或部分子字节有 key
UNKEYED_FRAGMENT = 1 << 8, // 256 // 子节点没有 key 的 fragment
NEED_PATCH = 1 << 9, // 512 // 一个节点只会进行非 props 比较
DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot
HOISTED = -1, // 静态节点
// 指示在 diff 过程应该要退出优化模式
BAIL = -2
}
<div>
<p>hellop>
<p>hellop>
<p>hellop>
<p>{
{msg}}}p>
div>
编译之后:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("p", null, "hello"),
_createVNode("p", null, "hello"),
_createVNode("p", null, "hello"),
_createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */)
]))
}
<div>
<p>hellop>
<p>hellop>
<p>hellop>
<p>{
{msg}}}p>
div>
没有静态提升时:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("p", null, "hello"),
_createVNode("p", null, "hello"),
_createVNode("p", null, "hello"),
_createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */)
]))
}
静态提升之后:
const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "hello", -1 /* HOISTED */)
const _hoisted_2 = /*#__PURE__*/_createVNode("p", null, "hello", -1 /* HOISTED */)
const _hoisted_3 = /*#__PURE__*/_createVNode("p", null, "hello", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1,
_hoisted_2,
_hoisted_3,
_createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */)
]))
}
默认情况下onClick会被视为动态绑定,所以每次都会去追踪它的变化。但是因为是同一个函数,所以没有追踪变化,可以直接缓存起来复用即可
<div>
<button @click="onClick">按钮button>
div>
没有开启事件监听缓存时:
转换之后的代码,可能还看不懂, 但是不要紧,我们只需要观察有没有静态标记即可。因为我们知道在Vue3.0的diff算法中,只有有静态标记的才会进行比较,才会进行追踪
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("button", {
onClick: _ctx.onClick }, "按钮", 8 /* PROPS */, ["onClick"])
]))
}
开启事件监听缓存之后:
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("button", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))
}, "按钮")
]))
}
npm install -g @vue/cli
vue create projectName
cd projectName
vue add vue-next
npm run serve
git clone https://github.com/vuejs/vue-next-webpack-preview.git projectName
cd projectName
npm install ...
npm run dev
Vite是Vue作者开发的一款意图取代webpack的工具
实现原理:利用ES6的import会发送请求去加载文件的特性,拦截这些请求,做一些预编译,省去webpack冗长的打包时间
npm install -g create-vite-app
create-vite-app projectName
cd projectName
npm install
npm install ...
npm run dev
Vue2.x数据与业务逻辑太分散了,不利于管理和维护。Vue3.0针对该问题推出了组合API(注入API)
data () {
return {
//新增功能1的数据
//新增功能2的数据
}
},
methods: {
//新增功能1的业务逻辑
//新增功能2的业务逻辑
},
computed: {
//新增功能1的业务逻辑
//新增功能2的业务逻辑
},
watch: {
//新增功能1的业务逻辑
//新增功能2的业务逻辑
}
组合API(注入API)将
return {}
暴露出去的变量和方法分别注入到data和methods中
setup()执行时机
reactive()
ref()
Vue是如何决定是否需要自动添加.value的?
Vue在解析数据之前,会自动判断这个数据是否是ref类型的,如果是就自动添加.value,否则不自动添加.value
Vue是如何判断当前的数据是否是ref类型?
Vue通过当前数据的__v_ref
来判断的,如果有__v_ref
这个私有属性并且取值为true,那么就代表是一个ref类型的数据
自己如何判断当前的数据是ref类型还是reactive类型?
Vue提供了对应的方法,isRef()
和isReactive()
App.vue
<template>
<div>
<form>
<input type="text" v-model="state1.stu.id">
<input type="text" v-model="state1.stu.name">
<input type="text" v-model="state1.stu.age">
<input type="button" value="添加" @click="addStu">
form>
<ul>
<li v-for="(stu,index) in state.stus" :key="stu.id" @click="remStu(index)">
{
{ stu.name }}-{
{ stu.age }}
li>
ul>
div>
template>
<script>
import useRemoveStudent from "./rem"
import useAddStudent from "./add"
export default {
name: "App",
// setup函数是组合API的入口函数
setup() {
let {
state, remStu } = useRemoveStudent()
let {
state1, addStu } = useAddStudent(state)
// 在组合API中定义的变量或方法,要想在外界使用,必须通过return {}暴露出去
return {
state, remStu, state1, addStu }
}
}
script>
<style>
style>
remove.js
import {
reactive} from 'vue'
function useRemoveStudent() {
let state = reactive({
stus:[
{
id:1, name:'zs', age:10},
{
id:2, name:'ls', age:20},
{
id:3, name:'ww', age:30},
]
})
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => return idx !== index);
}
return {
state, remStu}
}
export default useRemoveStudent
add.js
import {
reactive} from 'vue'
function useAddStudent(state) {
let state1 = reactive({
stu:{
id:'',
name:'',
age:''
}
})
function addStu(e) {
const stu = Object.assign({
}, state1.stu)
state.stus.push(stu)
state1.stu.id = ''
state1.stu.name = ''
state1.stu.age = ''
}
return {
state1, addStu}
}
export default useAddStudent
默认情况下,无论是通过ref还是reactive都是递归监听,内部做了递归,将每一层包装成了Proxy对象,如果数据量比较大时,非常消耗性能
<template>
<div>
<p>{
{state.a}}p>
<p>{
{state.b.c}}p>
<p>{
{state.b.d.e}}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
reactive} from 'vue'
export default {
name: 'App',
setup(){
let state = reactive({
a: 'a',
b: {
c: 'c',
d: {
e: 'e'
}
}
})
function handle(){
state.a = '1',
state.b.c = '2',
state.b.d.e = '3'
console.log(state) //Proxy {a: "1", b: {…}}
console.log(state.b) //Proxy {c: "2", d: {…}}
console.log(state.b.d) //Proxy {e: "3"}
}
return {
state,handle}
}
}
script>
<template>
<div>
<p>{
{state.a}}p>
<p>{
{state.b.c}}p>
<p>{
{state.b.d.e}}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
ref} from 'vue'
export default {
name: 'App',
setup(){
let state = ref({
a: 'a',
b: {
c: 'c',
d: {
e: 'e'
}
}
})
function handle(){
state.value.a = '1',
state.value.b.c = '2',
state.value.b.d.e = '3'
console.log(state.value) //Proxy {a: "1", b: {…}}
console.log(state.value.b) //Proxy {c: "2", d: {…}}
console.log(state.value.b.d) //Proxy {e: "3"}
}
return {
state,handle}
}
}
script>
使用shallowReactive和shallowRef可以实现非递归监听
<template>
<div>
<p>{
{state.a}}p>
<p>{
{state.b.c}}p>
<p>{
{state.b.d.e}}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
shallowReactive} from 'vue'
export default {
name: 'App',
setup(){
let state = shallowReactive({
a: 'a',
b: {
c: 'c',
d: {
e: 'e'
}
}
})
function handle(){
state.a = '1',
state.b.c = '2',
state.b.d.e = '3'
console.log(state) //Proxy {a: "1", b: {…}}
console.log(state.b) //{c: "2", d: {…}}
console.log(state.b.d) //{e: "3"}
}
return {
state,handle}
}
}
script>
从上面代码可以看出,shallowReactive只将第一层包装成了Proxy对象(只监听第一层),即只要改变了第一层的数据就会去更新UI,如果没有改变第一层的数据而改变的是第二层或第三层不会更新UI
<template>
<div>
<p>{
{state.a}}p>
<p>{
{state.b.c}}p>
<p>{
{state.b.d.e}}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
shallowRef} from 'vue'
export default {
name: 'App',
setup(){
let state = shallowRef({
a: 'a',
b: {
c: 'c',
d: {
e: 'e'
}
}
})
function handle(){
state.value.a = '1',
state.value.b.c = '2',
state.value.b.d.e = '3'
console.log(state.value) //{a: "1", b: {…}}
console.log(state.value.b) //{c: "2", d: {…}}
console.log(state.value.b.d) //{e: "3"}
}
return {
state,handle}
}
}
script>
从上面代码可以看出,shallowRef并不是监听的第一层并且没有一层被包装成RefImpl对象(即改变每层的数据也不会更新UI)
<template>
<div>
<p>{
{ state.a }}p>
<p>{
{ state.b.c }}p>
<p>{
{ state.b.d.e }}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
shallowRef } from "vue"
export default {
name: "App",
setup() {
let state = shallowRef({
a: "a",
b: {
c: "c",
d: {
e: "e",
},
},
})
function handle() {
//修改.value值
state.value = {
a: "1",
b: {
c: "2",
d: {
e: "3",
},
},
}
console.log(state) //RefImpl {_rawValue: {…}, _shallow: true, __v_isRef: true, _value: {…}}
console.log(state.value) //{a: "1", b: {…}}
console.log(state.value.b) //{c: "2", d: {…}}
console.log(state.value.b.d) //{e: "3"}
}
return {
state, handle }
}
}
script>
从上面代码可以看出,shallowRef其实监听的是.value的变化,并不是第一层的变化。只有修改.value的值才会更新UI
从底层看shallowRef(xxx)—>shallowReactive( {value: xxx} ),通过shallowRef创建的数据,它其实监听的是.value的变化,本质上value才是第一层
<template>
<div>
<p>{
{ state.a }}p>
<p>{
{ state.b.c }}p>
<p>{
{ state.b.d.e }}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
shallowRef,triggerRef } from "vue"
export default {
name: "App",
setup() {
let state = shallowRef({
a: "a",
b: {
c: "c",
d: {
e: "e",
},
}
})
function handle() {
//修改第二层数据只更新第二层对应的UI
state.value.b.c="2"
triggerRef(state)
}
return {
state, handle }
}
}
script>
通过triggerRef()来触发非递归监听属性更新UI界面。Vue3.0只提供了triggerRef方法,没有提供triggerReactive方法。如果是reactive类型的数据,那么是无法触发UI界面更新的
reactive和ref数据类型的特点:每次修改都会被追踪,都会更新UI界面,但这样是非常消耗性能的。如果我们有一些操作不需要追踪,不需要更新UI界面,那么这个时候就可以通过toRaw方法拿到它的原始数据,对原始数据进行修改,这样就不会被追踪和更新UI界面,性能提高了
state本质是一个Proxy对象(响应式数据),Proxy对象引用了obj
<template>
<div>
<p>{
{ state.id }}p>
<p>{
{ state.name }}p>
<p>{
{ state.age }}p>
div>
template>
<script>
import {
reactive,toRaw } from "vue"
export default {
name: "App",
setup() {
let obj ={
id: 1,name: "张三",age: 18}
let state = reactive(obj)
console.log(obj) //{id: 1, name: "张三", age: 18}
console.log(state) //Proxy {id: 1, name: "张三", age: 18}
console.log(obj===state) //false
console.log(toRaw(state)) //{id: 1, name: "张三", age: 18}
return {
state }
}
}
script>
如果想要通过toRaw拿到ref类型的原始数据,toRaw(state.value)
,.value中保存的才是当初创建时传入的原始数据
reactive和ref是否会影响原始数据:
reactive(引用obj),修改响应式数据会影响到原始数据
ref(复制obj),修改响应式数据不会影响到原始数据
<template>
<div>
<p>{
{ state.id }}p>
<p>{
{ state.name }}p>
<p>{
{ state.age }}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
reactive, toRaw } from "vue"
export default {
name: "App",
setup() {
let obj = {
id: 1, name: "张三", age: 18 }
let state = reactive(obj)
function handle() {
state.name = "李四"
console.log(obj) //{id: 1, name: "李四", age: 18}
console.log(state) //Proxy {id: 1, name: "李四", age: 18}
console.log(toRaw(state)) //{id: 1, name: "李四", age: 18}
}
return {
state, handle }
}
}
script>
<template>
<div>
<p>{
{ state.id }}p>
<p>{
{ state.name }}p>
<p>{
{ state.age }}p>
<button @click="handle">修改数据button>
div>
template>
<script>
import {
ref, toRaw } from "vue"
export default {
name: "App",
setup() {
let obj = {
id: 1, name: "张三", age: 18 }
let state = ref(obj);
function handle() {
state.name = "李四"
console.log(obj) //{id: 1, name: "张三", age: 18}
console.log(state) //RefImpl {_rawValue: {…}, _shallow: false, __v_isRef: true, _value: Proxy, name: "李四"}
console.log(toRaw(state.value)) //{id: 1, name: "张三", age: 18}
}
return {
state, handle }
}
}
script>
添加不可转为响应式数据的标记(永远不会被追踪),函数返回这个对象本身
let obj ={
id: 1,name: "张三",age: 18}
obj = markRaw(obj)
let state = reactive(obj)
ref(复制),修改响应式数据不会影响原始数据,数据发生改变UI界面就会自动更新
toRef(引用),修改响应式数据会影响原始数据,数据发生改变UI界面也不会自动更新
toRefs的用法:
let obj = {
name: '张三',age: 18}
//let state = toRef(obj, 'name')
//let state = toRef(obj, 'age')
let state = toRefs(obj)
customRef用于自定义返回一个ref对象,可以显式地控制依赖追踪和触发响应,两个参数分别是用于追踪的 track 与用于触发响应的 trigger,并返回一个带有 get 和 set 属性的对象
import {
customRef} from 'vue'
function myRef(value) {
return customRef((track,trigger)=>{
return {
get(){
//告诉Vue这个数据是需要追踪变化的
track()
return value
},
set(newValue){
value = newValue
//告诉Vue触发界面更新
trigger()
}
}
})
}
Vue2.x中我们可以给元素添加ref="xxx"
,然后通过this.$refs.xxx
的方式来获取元素
在Vue3.0中我们也可以通过ref来获取元素:
<template>
<div ref="box">我是divdiv>
template>
<script>
import {
ref,onMounted } from "vue"
export default {
name: "App",
setup() {
let box = ref(null)
onMounted(()=>{
console.log(box.value) //我是div
})
return {
box}
}
}
script>
readonly:用于创建一个只读的数据,并且是递归只读
let state = readonly({
name: '张三',
attr: {
age: 18,
height: 1.88
}
})
shallowReadonly:用于创建一个只读的数据,但不是递归只读的(第一层只读)
let state = shallowReadonly({
name: '张三',
attr: {
age: 18,
height: 1.88
}
})
isReadonly:判断是否是只读数据
console.log(isReadonly(state))
const和readonly的区别:
let obj ={
name: '张三',age: 18}
let state = new Proxy(obj,{
get(obj,key){
console.log(obj,key) //{name: "张三", age: 18} "name"
return obj[key]
},
set(obj,key,value){
console.log(obj,key,value) //{name: "张三", age: 18} "name" "李四"
obj[key]=value
return true
}
})
console.log(state.name) //张三
state.name = "李四"
console.log(obj) {
name: "李四", age: 18}