0
点赞
收藏
分享

微信扫一扫

浅谈vue3.0和vue2.0的区别

微言记 2022-04-13 阅读 138

前题

vue3.0的变化可以总结为以下几点:

  • 更小
  • 更快
  • 加强typescript支持
  • Api一致性
  • 提高可维护能力
  • 开放更多底层功能

其中前三点是最主要的变化。

一、编程说明

vue2.0采用面向对象编程的思想,vue3.0则采用函数式编程的思想。

详细介绍:

面向对象编程

什么是对象

1、对象是单个事物的抽象,是一个具体的事物(一本书、一辆汽车、一个人都可以是对象,一个数据库、一张网页、一个与远程服务器的连接也可以是对象。当实物被抽象成对象,实物之间的关系就变成了对象之间的关系,从而就可以模拟现实情况,针对对象进行编程)。

2、对象是一个容器,封装了属性(property;属性是对象的状态特征)和方法(method;方法是对象的行为。例:完成了)。

3、在实际开发中,对象是一个抽象的概念,可以将其简单理解为:数据集或功能集。

4、ECMAScript-262 把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。

5、严格来讲,这就相当于说对象是一组没有特定顺序的值。对象的每个属性或方法都有一个名字,而每个名字都映射到一个值。

面向对象的特征

  • 封装性(就是包装,把一些重用的内容进行包装,在需要的时候,直接使用把一个值,存放在一个变量中,把一些重用的代码放在函数中,把好多相同功能的函数放在一个对象中,把好多功能的对象,放在一个文件中,把一些相同的内容放在一个对象中)
  • 继承性(类与类之间的关系,js中没有类的概念,js中有构造函数的概念,是可以有继承的,是基于原型)
  • 多态性(同一个行为,针对不同的对象,产生了不同的效果)

编程思想:
面向对象:提出需求,找对象,对象解决,注重的是结果 

原型prototype

构造函数、实例、原型三者之间的关系

在这里插入图片描述

函数式编程

1、vue3 英文版说明

vue3 API相关的RFC,地址:https://github.com/vuejs/rfcs/blob/function-apis/active-rfcs/0000-function-api.md

2、函数式编程是一种编程范式,主要是利用函数把运算过程封装起来,通过组合各种函数来计算结果。

例子,假设我们要把字符串 functional programming is great 变成每个单词首字母大写,我们可以这样实现:

var string = 'functional programming is great';
var result = string
  .split(' ')
  .map(v => v.slice(0, 1).toUpperCase() + v.slice(1))
  .join(' ');

上面的例子先用 split 把字符串转换数组,然后再通过 map 把各元素的首字母转换成大写,最后通过 join 把数组转换成字符串。 整个过程就是 join(map(split(str))),体现了函数式编程的核心思想: 通过函数对数据进行转换。
由此我们可以得到,函数式编程有两个基本特点:
• 通过函数来对数据进行转换
• 通过串联多个函数来求结果

3、对比声明式与命令式(见二中e3.0例子容易理解)

  •  命令式:我们通过编写一条又一条指令去让计算机执行一些动作,这其中一般都会涉及到很多繁杂的细节。命令式代码中频繁使用语句,来完成某个行为。比如 for、if、switch、throw 等这些语句。
  • 声明式:我们通过写表达式的方式来声明我们想干什么,而不是通过一步一步的指示。表达式通常是某些函数调用的复合、一些值和操作符,用来计算出结果值。
//命令式
var CEOs = [];
//----------for(var i = 0; i < companies.length; i++){
for(var i in companies){
  CEOs.push(companies[i].CEO)
}

//声明式
var CEOs = companies.map(c => c.CEO);

 4、函数式编程常见特性

无副作用,指调用函数时不会修改外部状态,即一个函数调用 n 次后依然返回同样的结果。

var a = 1;
// 含有副作用,它修改了外部变量 a
// 多次调用结果不一样
function test1() {
  a++
  return a;
}

// 无副作用,没有修改外部状态
// 多次调用结果一样
function test2(a) {
  return a + 1;
}
透明引用
指一个函数只会用到传递给它的变量以及自己内部创建的变量,不会使用到其他变量。
var a = 1;
var b = 2;
// 函数内部使用的变量并不属于它的作用域
function test1() {
  return a + b;
}
// 函数内部使用的变量是显式传递进去的
function test2(a, b) {
  return a + b;
}

 不可变变量
指的是一个变量一旦创建后,就不能再进行修改,任何修改都会生成一个新的变量。使用不可变变量最大的好处是线程安全。多个线程可以同时访问同一个不可变变量,让并行变得更容易实现。 由于 JavaScript 原生不支持不可变变量,需要通过第三方库来实现。 (如 Immutable.js,Mori 等等)

var obj = Immutable({ a: 1 });
var obj2 = obj.set('a', 2);
console.log(obj);  // Immutable({ a: 1 })
console.log(obj2); // Immutable({ a: 2 })


函数是一等公民
我们常说函数是JavaScript的"第一等公民",指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。下文将要介绍的闭包、高阶函数、函数柯里化和函数组合都是围绕这一特性的应用

5、常见的函数式编程模型

1.闭包(Closure)

2.高阶函数

map
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。map 不会改变原数组。
假设我们有一个包含名称和种类属性的对象数组,我们想要这个数组中所有名称属性放在一个新数组中

// 使用高阶函数
var animals = [
  { name: "Fluffykins", species: "rabbit" },
  { name: "Caro", species: "dog" },
  { name: "Hamilton", species: "dog" },
  { name: "Harold", species: "fish" },
  { name: "Ursula", species: "cat" },
  { name: "Jimmy", species: "fish" }
];
var names = animals.map(x=>x.name);
console.log(names); //["Fluffykins", "Caro", "Hamilton", "Harold", "Ursula", "Jimmy"]

filter
filter() 方法会创建一个新数组,其中包含所有通过回调函数测试的元素。filter 为数组中的每个元素调用一次 callback 函数, callback 函数返回 true 表示该元素通过测试,保留该元素,false 则不保留。filter 不会改变原数组,它返回过滤后的新数组。
假设我们有一个包含名称和种类属性的对象数组。 我们想要创建一个只包含狗(species: “dog”)的数组。

// 使用高阶函数
var animals = [
  { name: "Fluffykins", species: "rabbit" },
  { name: "Caro", species: "dog" },
  { name: "Hamilton", species: "dog" },
  { name: "Harold", species: "fish" },
  { name: "Ursula", species: "cat" },
  { name: "Jimmy", species: "fish" }
];
var dogs = animals.filter(x => x.species === "dog");
console.log(dogs); // {name: "Caro", species: "dog"}
// { name: "Hamilton", species: "dog" }

 reduce
reduce 方法对调用数组的每个元素执行回调函数,最后生成一个单一的值并返回。 reduce 方法接受两个参数:1)reducer 函数(回调),2)一个可选的 initialValue。
假设我们要对一个数组的求和:

// 不使用高阶函数
const arr = [5, 7, 1, 8, 4];
let sum = 0;
for (let i = 0; i < arr.length; i++) {
  sum = sum + arr[i];
}
console.log(sum);//25
// 使用高阶函数
const arr = [5, 7, 1, 8, 4];
const sum = arr.reduce((accumulator, currentValue) => accumulator + currentValue,0);
console.log(sum)//25

 函数组合 (Composition)
函数式编程的一个特点是通过串联函数来求值。然而,随着串联函数数量的增多,代码的可读性就会不断下降。函数组合来解决这个问题。
假设有一个 compose 函数,它可以接受多个函数作为参数,然后返回一个新的函数。当我们为这个新函数传递参数时,该参数就会「流」过其中的函数,最后返回结果。

//两个函数的组合
var compose = function(f, g) {
    return function(x) {
        return f(g(x));
    };
};

//或者
var compose = (f, g) => (x => f(g(x)));
var add1 = x => x + 1;
var mul5 = x => x * 5;
compose(mul5, add1)(2);// =>15 

二、例子作比

vue3 -- DEMO

<template>
  <div>
    <span>count is {{ count }}</span>
    <span>plusOne is {{ plusOne }}</span>
    <button @click="increment">count++</button>
  </div>
</template>

<script>
import { value, computed, watch, onMounted } from 'vue'

export default {
  setup() {
    // reactive state
    const count = value(0)
    // computed state
    const plusOne = computed(() => count.value + 1)
    // method
    const increment = () => { count.value++ }
    // watch
    watch(() => count.value * 2, val => {
      console.log(`count * 2 is ${val}`)
    })
    // lifecycle
    onMounted(() => {
      console.log(`mounted`)
    })
    // expose bindings on render context
    return {
      count,
      plusOne,
      increment
    }
  }
}
</script>

转换为vue2 -- DEMO

<template>
    <div>
        <span>count is {{ count }}</span>
        <span>plusOne is {{ plusOne }}</span>
        <button @click="increment">count++</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                count: 0
            };
        },
        methods: {
            increment: function () {
                this.count++;
            }
        },
        computed: {
            plusOne: function () {
                return this.count + 1;
            }
        },
        watch: {
            count: function (val) {
                console.log(`count * 2 is ${val * 2}`)
            }
        },
        mounted() {
            console.log(`mounted`);
        }
    };
</script>

1、

对比一下,我们不难发现:
vue2是将mounted,data,computed,watch之类的方法作为一个对象的属性进行导出。
vue3新增了一个名为setup的入口函数,value, computed, watch, onMounted等方法都需要从外部import。

函数式编程为组件的编写提供了更高的灵活度与可读性,并且更符合一个前端编写者的习惯(或者叫做“编程直觉”)。

2、

在vue2中,watch、computed、data、method等API都是直接作为对象的属性,传给vue实例的。这意味着,我们开发者在开发时,脑中需要给这个对象的不同属性(data、method、mounted之类的)建立联系。但一旦代码规模变大,这种联系就非常吃力了,这集中表现在大型vue组件代码的可读性很低。我想,每个维护过1000+行vue组件的开发者都会有所体会

而在vue3中,我们可以像写一个方法一样去写这个组件的JS逻辑部分,使用import来按需引入。这样的好处显而易见,首先就是我们需要写的代码量少了,其次就是我们可以封装更多的子函数引用更多的公共函数去维护我们的代码,第三就是代码的可读性变高了。(当然,我们的打包体积也会变小)----  此处需要调研

三、扩展vue和react

vue和react最大的区别有两个,一是双向数据绑定 VS 单向数据流,二是模板语法 VS JSX

vue3.0在函数式编程方面,使其在一些API的使用、组件JS层的逻辑上更像react,但其仍为模板语法。

举报

相关推荐

0 条评论