从打包结果浅析vue2/vue3/react细节(函数式vs面向对象)
打包工具:rollup(对比webpack无额外注入代码)
- 想进一步了解2者对比的话,可以看我另一篇:rollup打包产物解析及原理(对比webpack)
打包前的代码(保证渲染出来的内容是一样的)
vue2
// main.jsimport Vue from 'vue'import App from './App.vue'new Vue({el: '#app',render: h => h(App)})// './App.vue'<template><div id="app"><div>11 + {{a}} + {{count}}</div><button @click="count++">Click me</button><child></child></div></template><script>import child from './test.vue'export default {components: {child,},data () {return {a: 'aaa',count: 0}}}</script>// './test.vue'<template><div>11 + {{a}} + {{name}}</div></template><script>export default {data () {return {a: 'aaa',name: 'child'}}}</script>
vue3
// main.jsimport { createApp } from 'vue'import App from './App.vue'const app = createApp(App)app.mount('#app')// './App.vue'<template><div id="app"><div>11 + {{a}} + {{countProxy.count}}</div><button @click="countProxy.count++">Click me</button><child></child></div></template><script setup>import child from './child.vue'import { reactive } from 'vue'const a = 'aaa'const countProxy = reactive({count: 0})</script>// './child.vue'<template><div>11 + {{a}} + {{nameProxy.name}}</div></template><script setup>import { reactive } from 'vue'const a = 'aaa'const nameProxy = reactive({name: 'child'})</script>
react (17版本"react": "^17.0.0")
// main.jsimport React from 'react'import ReactDOM from 'react-dom'import App from './app'ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root'));// app.jsximport React, { useState } from 'react';import ChildTest from './testjsx'const App = () => {const a = 'aaa'const [count, setCount] = useState(0);return (<><div>11 + {a} + {count}</div><button onClick={() => setCount(count + 1)}>Click me</button><ChildTest /></>);};export default App;// './testjsx'import React, { useState } from 'react';const ChildTest = () => {const a = 'aaa'const [name, setName] = useState('child');return (<div>11 + {a} + {name}</div>);}export default ChildTest
打包后的结果
(下面的结果是用vite打包,底层是rollup 无额外代码注入。并且vite打包可以把vendor给分离开,让组件的结构更加清晰)
vue2
打包后目录结构dist├── assets│ ├── index.3a1d85d8.js│ └── vendor.28ac9a6d.js└── index.html// index.3a1d85d8.jsimport { V as Vue } from "./vendor.28ac9a6d.js";var render$1 = function() {var _vm = this;var _h = _vm.$createElement;var _c = _vm._self._c || _h;return _c("div", [_vm._v("11 + " + _vm._s(_vm.a) + " + " + _vm._s(_vm.name))]);};var staticRenderFns$1 = [];function normalizeComponent(scriptExports, render2, staticRenderFns2, functionalTemplate, injectStyles, scopeId, moduleIdentifier, shadowMode) {// ... 此处省略 50行}const __vue2_script$1 = {data() {return {a: "aaa",name: "child"};}};const __cssModules$1 = {};var __component__$1 = /* @__PURE__ */ normalizeComponent(__vue2_script$1, render$1, staticRenderFns$1, false, __vue2_injectStyles$1, null, null, null);function __vue2_injectStyles$1(context) {for (let o in __cssModules$1) {this[o] = __cssModules$1[o];}}var child = /* @__PURE__ */ function() {return __component__$1.exports;}();var render = function() {var _vm = this;var _h = _vm.$createElement;var _c = _vm._self._c || _h;return _c("div", { attrs: { "id": "app" } },[_c("div", [_vm._v("11 + " + _vm._s(_vm.a) + " + " + _vm._s(_vm.count))]),_c("button", { on: { "click": function($event) {_vm.count++;} } },[_vm._v("Click me")]),_c("child")], 1);};var staticRenderFns = [];const __vue2_script = {components: {child},data() {return {a: "aaa",count: 0};}};const __cssModules = {};var __component__ = /* @__PURE__ */ normalizeComponent(__vue2_script, render, staticRenderFns, false, __vue2_injectStyles, null, null, null);function __vue2_injectStyles(context) {for (let o in __cssModules) {this[o] = __cssModules[o];}}var App = /* @__PURE__ */ function() {return __component__.exports;}();new Vue({el: "#app",render: (h) => h(App)});// vendor.28ac9a6d.js是vue2的源代码
vue3
打包后目录结构dist├── assets│ ├── index.8a6206cf.js│ └── vendor.be99b281.js└── index.html// index.8a6206cf.jsimport { r as reactive, o as openBlock, c as createElementBlock, t as toDisplayString, u as unref, a as createBaseVNode, b as createVNode, d as createApp } from "./vendor.be99b281.js";const _sfc_main$1 = {setup(__props) {const a = "aaa";const nameProxy = reactive({name: "child"});return (_ctx, _cache) => {return openBlock(), createElementBlock("div", null, "11 + " + toDisplayString(a) + " + " + toDisplayString(unref(nameProxy).name), 1);};}};const _hoisted_1 = { id: "app" };const _sfc_main = {setup(__props) {const a = "aaa";const countProxy = reactive({count: 0});return (_ctx, _cache) => {return openBlock(),createElementBlock("div", _hoisted_1, [createBaseVNode("div", null, "11 + " + toDisplayString(a) + " + " + toDisplayString(unref(countProxy).count), 1),createBaseVNode("button", {onClick: _cache[0] || (_cache[0] = ($event) => unref(countProxy).count++)}, "Click me"),createVNode(_sfc_main$1)]);};}};const app = createApp(_sfc_main);app.mount("#app");// vendor.be99b281.js是vue3的源代码
react
打包后目录结构dist├── assets│ ├── index.66a13ace.js│ └── vendor.5719e053.js└── index.html// index.66a13ace.jsimport { _ as _react_17_0_2_react, R as React, a as ReactDOM } from "./vendor.5719e053.js";const ChildTest = () => {const a = "aaa";const [name, setName] = _react_17_0_2_react.exports.useState("child");return React.createElement("div", null, "11 + ", a, " + ", name);};const App = () => {const a = "aaa";const [count, setCount] = _react_17_0_2_react.exports.useState(0);return React.createElement(React.Fragment,null,React.createElement("div", null, "11 + ", a, " + ", count),React.createElement("button", {onClick: () => setCount(count + 1)}, "Click me"),React.createElement(ChildTest, null));};ReactDOM.render( React.createElement(React.StrictMode, null, React.createElement(App, null)), document.getElementById("root"));// vendor.5719e053.js是react的源代码
另外提一嘴,vue3和vue2都支持jsx,以下是vue3和react的jsx写法对比
结论:写法几乎已经很接近了,只有一些响应式api用法略不同
(以下举例,渲染出来是同样的内容)
// react ---> app.jsximport React, { useState } from 'react';import ChildTest from './testjsx'const App = () => {const a = 'aaa'const [count, setCount] = useState(0);return (<><div>11 + {a} + {count}</div><button onClick={() => setCount(count + 1)}>Click me</button><ChildTest /></>);};export default App;// vue3 jsx ---> app.jsximport { ref } from 'vue'import ChildTest from './testjsx'export default {setup () {const a = 'aaa'const count = ref(0)return () => (<><div>11 + {a} + {count}</div><button onClick={() => count.value++}>Click me</button><ChildTest /></>)}}
分析对比打包结果
vue2 vs vue3
直接说结论(大家可以对比一下 下面2个app.vue片段,在琢磨一下)
vue2是面向对象编程,vue3是函数式编程。
vue2内的数据,从实例(VueComponent实例,this指向的就是这个)上获得- vue2会把route和vuex之类的函数或对象和data都放到 对应组件的 VueComponent实例(this) 上去
所以我们可以直接用this.xx去访问 route store等。比如
data () {aaa: 123 // 在编译的时候,会被加到 VueComponent实例 上去},methods: {function someone () {console.log(this.aaa)console.log(this.$route.path)this.$router.push()console.log(this.$store.state.xx)// this就是指向当前组件的 VueComponent实例}}
vue3直接从函数作用域内获得(所以代码看上去很清晰,无需写this)
但使用 route 和 vuex 都需要额外引入了(按需引到函数作用域内去)。比如
import { useRoute, useRouter } from 'vue-router' // 这样可以按需引入import { useStore } from 'vuex' // 这样可以按需引入const route = useRoute()const router = useRouter()const store = useStore()const aaa = 123function someone () {console.log(aaa)console.log(route.path)router.push()console.log(store.state.xx)}someone()
最直接的好处:
- vue3更好的支持按需引入,利用tree shaking,可以使代码体积变的更小,加载和解析速度更快
- 代码的结构更加清晰,更利于维护 和 写大型项目(这也是vue3能写组合式api的原理)
大家可以对比一下 下面2个app.vue片段,在琢磨一下
// vue2 的app.vue片段var render = function() {var _vm = this;var _h = _vm.$createElement;var _c = _vm._self._c || _h;return _c("div", { attrs: { "id": "app" } },[_c("div", [_vm._v("11 + " + _vm._s(_vm.a) + " + " + _vm._s(_vm.count))]),_c("button", { on: { "click": function($event) {_vm.count++;} } },[_vm._v("Click me")]),_c("child")], 1);};// vue3 的app.vue片段const _sfc_main = {setup(__props) {const a = "aaa";const countProxy = reactive({count: 0});return (_ctx, _cache) => {return openBlock(),createElementBlock("div", _hoisted_1, [createBaseVNode("div", null, "11 + " + toDisplayString(a) + " + " + toDisplayString(unref(countProxy).count), 1),createBaseVNode("button", {onClick: _cache[0] || (_cache[0] = ($event) => unref(countProxy).count++)}, "Click me"),createVNode(_sfc_main$1)]);};}};
vue3 vs react
先说结论:
- 2者都是函数式编程,组件都是从自身的所用域内去获取数据
- 思路和结构都比较接近(vue3也有hooks的概念,支持jsx,整体和react已经很接近)
因为比较接近,所以这里就不在举例了,大家可以看上面给出的打包结果对比一下
总结
vue2 vs vue3
- vue2是面向对象编程,vue3是函数式编程。
- 函数式编程的好处:体现在编码上是可以使用组合式api,可以让逻辑更加集中和清晰。(组合式api介绍,官网)
- vue3有更好的按需引入支持,使得代码体积变得更小(可以让vue程序的体积和占的内存都变小,提升加载和渲染速度!)
vue3 vs react
- 2者都是函数式编程,组件都是从自身的所用域内去获取数据,都有很好的按需引入支持
- 打包后的组件的处理的思路 和 组件结构都比较接近(vue3也有hooks的概念,支持jsx,整体和react已经很接近)
还是想更多维度的了解,函数式编程 vs 面向对象编程的同学,可以看:https://zhuanlan.zhihu.com/p/158828668
码字不易,点赞鼓励!
