1. 数据的双向绑定的原理

    重构响应式系统,使用Proxy替换Object.defineProperty
    Object.defineProperty的原理:通过使用 Object.defineProperty 来劫持对象属性的 geter 和 seter 操作,当数据发生改变发出通知。Vue2.x版本中的双向绑定不能检测到下标的变化

    <script>
    	     var obj = {};
    	    Object.defineProperty(obj, 'prop', {
    	       get: function () {
    	            return val;
    	         },
    	         set: function (newVal) {
    	           val = newVal;
    	             document.getElementById('text').innerHTML = val;
    	         }
    	     });
    	     document.addEventListener('keyup', function (e) {
    	         obj.prop = e.target.value;
    	     });
    	 </script>
    

    porxy原理:通过ES6的新特性proxy来劫持数据,当数据改变时发出通知。proxy可以劫持整个对象,并返回一个新对象

    	<script>
    	   var obj = {};
    	   var obj1 = new Proxy(obj, {
    	       // target就是第一个参数obj, receive就是返回的obj(返回的proxy对象)
    	      get: function (target, key, receive) {
    	        // 返回该属性值
    	          return target[key];
    	       },
    	       set: function (target, key, newVal, receive) {
    	            // 执行赋值操作
    	            target[key] = newVal;
    	           document.getElementById('text').innerHTML = target[key];
    	      }
    	     })
    	    document.addEventListener('keyup', function (e) {
    	        obj1[0] = e.target.value;
    	     });
    	 </script>
    
  2. 性能的提升

  • diff方法优化
    Vue2 中的虚拟dom是进行全量的杜比
    Vue3 新增了静态标记(PatchFlag),只比对带有 PF 的节点,并且通过 Flag 的信息得知
    当前节点要比对的具体内容。

  • 静态提升
    Vue2中无论元素是否参与更新, 每次都会重新创建, 然后再渲染
    Vue3中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可

  • cacheHandlers 事件侦听器缓存
    Vue2默认情况下onClick会被视为动态绑定, 所以每次都会去追踪它的变化
    Vue3但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可

  • ssr渲染
    Vue2当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面, 即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。
    Vue3当静态内容大到一定量级时候,会用_createStaticVNode方法在客户端去生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。

  • 按需编译,体积更小(Tree shaking)
    Tree-shaking 使编译更友好,比如,如果你的项目没有用到watch,那编译时就会把tree shaking掉。

  • Compostion API: 组合API/注入API
    Vue2的组件内,使用的是Option API风格(data/methods/mounted)来组织的代码,这样会让逻辑分散,举个例子就是我们完成一个计数器功能,要在data里声明变量,在methods定义响应函数,在mounted里初始化变量,如果在一个功能比较多、代码量比较大的组件里,你要维护这样一个功能,就需要在data/methods/mounted反复的切换到对应位置,然后进行代码的更改。
    Vue3中,使用setup函数。如下所示跟count相关的逻辑,都放到counter.js文件里,跟todo相关的逻辑放到todos.js里。

    import useCounter from './counter'
    import useTodo from './todos'
    setup(){
      let { val, todos, addTodo } = useTodo()
      let {count,add} = useCounter() 
      return {
        val, todos, addTodo,
        count,add,
      }
    }
    
  • 自定义渲染API(Custom Renderer API)
    Vue2.x最开始支持运行在浏览器中,渲染到浏览器的dom上,随着vue的流行,出现了weex和myvue。
    Vue2.x项目架构对于这种渲染到不同平台不太友好,
    Vue3.0推出了自定义渲染API解决了该问题。

    下面我们先看vue2和vue3的入口写法有所不同:

    // vue2
    import Vue from 'vue'
    import App from './App.vue'
    new Vue({ => h(App)}).$mount('#app')
    
    // vue3
    const { createApp }  from 'vue'
    import App from "./src/App"
    createApp(App).mount(('#app')
    

    vue官方实现的 createApp 会给我们的 template 映射生成 html 代码,但是要是你不想渲染生成到 html ,而是要渲染生成到 canvas 之类的不是html的代码的时候,那就需要用到 Custom Renderer API 来定义自己的 render 渲染生成函数了。

    // 你自己实现一个createApp,比如是渲染到canvas的。
    import { createApp } from "./runtime-render";
    import App from "./src/App"; // 根组件
    createApp(App).mount('#app');
    
  • vite开发构建工具
    Vue2是使用webpack作为开发构建工具的,npm run dev都要等一会,项目越大等的时间越长
    Vue3是使用vite来做开发构建工具。vite支持浏览器支持import关键字,启动项目时浏览器直接请求路由对应的代码文件,代理服务器针对单个文件进行编译并返回。如果请求的文件里还import了其他文件,同理浏览器继续发请求,代理服务器返回。就这样实现了npm run dev时无需编译,实时请求实时编译。

  • 支持TS语法
    Vue2结合ts的具体实践中,要用 vue-class-component 强化 vue 组件,让 Script 支持 TypeScript 装饰器,用 vue-property-decorator 来增加更多结合 Vue 特性的装饰器,最终搞的ts的组件写法和js的组件写法差别挺大。
    Vue3量身打造了defineComponent函数,使组件在ts下,更好的利用参数类型推断 。Composition API 代码风格中,比较有代表性的api就是 ref 和 reactive,也很好的支持了类型声明。

    import { defineComponent, ref } from 'vue' 
    const Component = defineComponent({
        props: {
            success: { type: String },
            student: {
              type: Object as PropType<Student>,
              required: true
           }
        },
        setup() {
          const year = ref(2020)
          const month = ref<string | number>('9')
         
          month.value = 9 // OK
         const result = year.value.split('') // => Property 'split' does not exist on type 'number'
     }
    
Logo

前往低代码交流专区

更多推荐