乐趣区

关于vue.js:vue3-和-vue2的比较

一、vue3 绝对于 vue2 的优化

  1. 类型判断的优化:由 flow.js 改为 typescript。
  2. 数据劫持的优化:由 Object.defineProperty 改为 Proxy, 因为:

    • 对于比拟深的对象,Object.defineProperty 须要递归遍历所有属性,将所有属性变为响应对象,升高了性能
    • Proxy 对于拜访到对象外部的才会变为响应对象
  3. 编译阶段的优化:模版编译的优化、slot 的编译优化、事件监听函数的缓存优化及 diff 算法的重写

    • slot 优化,将 slot 编译为 lazy 函数,将 slot 的渲染的决定权交给子组件
    • 模版编译的优:通过编译阶段对动态模板的剖析,编译生成了 Block TreeBlock Tree 是一个将模版基于动静节点指令切割的嵌套区块,每个区块外部的节点构造是固定的,而且每个区块只须要以一个 Array 来追踪本身蕴含的动静节点
    • diff 算法重写是因为:vue2 中单个组件内,VDOM diff 须要遍历整个 DOM 树,更新须要从新生成 DOM 树;vue3 的 VDOM 减少动态标记、动态晋升、事件缓存,会事后缓存 DOM 树,通过上下文拿到缓存的 DOM 树,只 diff 动静节点
<div id="content">
  <p>text</p>           // 动态节点
  <p>text</p>           // 动态节点
  <p>{{message}}</p>    // 动静节点
  <p>text</p>           // 动态节点
  <p>text</p>           // 动态节点
</div>
  1. 逻辑复用的优化:由 mixins 改为 hook 钩子函数,因为:

    • 定义的变量名容易抵触
    • 变量数据起源不清
// hook
import {ref, onMounted, onUnmounted} from 'vue'

export default () => {const width = ref(window.innerWidth);
  const height = ref(window.innerHeight);
  
  const update = () => {
    width.value = window.innerWidth;
    height.value = window.innerHeight;
  }
  
  onMounted(() => {window.addEventListener("resize", update)
  })
  
  onUnmounted(() => {window.removeEventListener("resize", update)
  })
  
  return {width, height}
}

二、vue3 绝对于 vue2 的变动

  1. 生命周期的变动
vue3 vue2
setup() 开始创立组件 beforeCreate() + created()
onBeforeMount() 组件挂载到页面之前 beforeMount()
onMounted() 组件挂载到页面之后 Mounted()
onBeforeUpdate() 组件更新之前 beforeUpdate()
onUpdated() 组件更新之后 updated()
onBeforeUnmount() 组件卸载之前 beforeDestroy()
onUnmount() 组件卸载之前 destroyed()
onActivated() activated()
onDeactivated() deactivated()
  1. 全局 API 的变动
## Before
import Vue from 'vue'
import App from './App.vue'

Vue.use(...)
Vue.component(...)
Vue.prototype.customProperty = ...

new Vue({render: h => h(App)
}).$mount('#app')


## After
import {createApp} from 'vue'
import App from './App.vue'

const app = createApp(App)

app.use(...)
app.component(...)
app.config.globalProperties.customProperty = ...

app.mount('#app')
  1. 全局弹窗
## Before
import vue from 'vue'
import toastComponent from './toast.vue'

const ToastConstructor = vue.extend(toastComponent)

function showToast() {const toastDom = new ToastConstructor({...})  
}

vue.prototype.$toast = showToast

                                        
## After
<div>
  <teleport to="body">
    ...
  </teleport>
</div>
                                    
  1. 子组件向父组件传值
## Before
export default {
  methods: {change() {this.$emit("todata", params);
    }
  }
}

## After
export default defineComponent({emits: ["todata"],
     setup(props, {emit}) {const change = () => {
         const params = {user:"ylw"};
         emit("todata", params);
       };
    }
 })

三、组件内的应用

// sfc 组件
<script lang="ts">
  import {defineComponent, ref, Ref, computed, watchEffect, PropType} from 'vue'

  interface Config {name: string;}

  const Iprops = {
      age: {type: Number as PropType<number>},
      config: {
        type: Object as PropType<Config>
        required: true
      }
  } as const

  export default defineComponent({
    name: 'App',
    props: Iprops,
    data() {return {}
    },
    mounted() {},
    setup(props, { slots, attrs, emit}) {const nameRef: Ref<number> = ref(0)
      
      const setName = () => {nameRef.value++}
      
      const computedNameRef: Computed<number> = computed(() => {return nameRef.value + 2})
      
      watchEffect(() => {console.log(nameRef.value)
      })
      
      return {
        name: nameRef,
        name2: computedNameRef,
        setName
      }
    }
  })
</script>
// jsx 组件
import {defineComponent, ref, computed, watchEffect} from 'vue'

export default defineComponent({const numberRef: Ref<number> = ref(1)

  setup() {
    const number = numberRef.value
    
    return () => {
      return (<div>
                <p>{number}</p>
      </div>)
    }
  }
})

四、Vite 和 webpack

vite 的个性
  • vite 是一个基于浏览器原生 ES imports 的开发服务器
  • 利用浏览器去解析 imports,在服务器端按需编译返回,齐全跳过了打包这一步,服务器随起随用;而 webpack 开发环境,须要编译打包 es6、es7 等,而后启动开发服务器
  • vite 反对热更新:vite 是按需加载,webpack 是全副加载
  • vite 依赖 es module 的个性
vite 的打包原理
打包过程 原理
webpack 辨认入口 -> 逐层辨认依赖 -> 剖析 / 转换 / 编译 / 输入代码 -> 打包后的代码 逐级递归辨认依赖,构建依赖图谱 -> 转化 AST 语法树 -> 解决代码 -> 转换为浏览器可辨认的代码
vite 基于浏览器原生 ES module,利用浏览器解析 imports,服务器端按需编译返回
vite 的改良点
webpack 毛病 vite 改良点
服务器启动迟缓 将利用模块辨别为依赖 和 源码 两类; 应用 esbuild 构建; 在浏览器申请源码时进行转换并按需提供源码
基于 nodejs esbuild(Go 编写) 预构建依赖,比 node 快 10-100 倍
热更新效率低下; 编辑单个文件会从新构建整个包;HMR 更新速度随规模增大降落 HMR 基于原生 ESM 上,更新速度与利用规模无关; 利用 http2 的缓存 + 压缩劣势
vite 的毛病
  • 生态不迭 webpack,加载器、插件不够丰盛
  • 生产环境 esbuild 构建对于 css 和代码宰割不够敌对
  • 没被大规模重度应用,会暗藏一些问题
退出移动版