Vue是一套用于构建用户界面的渐进式框架,与其它大型 JS 框架不同,Vue 被设计为能够自底向上逐层利用,更易上手,还便于与第三方库或既有我的项目整合,因而,Vue齐全可能为简单的单页利用提供驱动。

2020年09月18日,Vue.js 3.0 正式公布,作者尤雨溪将其形容为:更快、更小、更易于保护。

Vue 3都退出了哪些新性能?

本次公布, Vue框架自身迎来了多项更新,如Vue 此前的反馈零碎是应用 Object.defineProperty 的 getter 和 setter。 然而,在 Vue 3中,将应用 ES2015 Proxy 作为其观察者机制,这样做的益处是打消了以前存在的正告,使速度加倍,并节俭了一半的内存开销。

除了基于 Proxy 的观察者机制,Vue 3的其余新个性还包含:

1. Performance(性能晋升)

在Vue 2中,当某个DOM须要更新时,须要遍历整个虚构DOM树能力判断更新点。而在Vue 3中,无需此项操作,仅需通过动态标记,比照虚构节点上带有patch flag的节点,即可定位更新地位。

比照Vue 2和Vue 3的性能差别,官网文档中给出了具体数据阐明:

· SSR速度进步了2~3倍

· Update性能进步1.3~2倍

2. Composition API(组合API)

Vue 2中有data、methods、mounted等存储数据和办法的对象,咱们对此应该不生疏了。比如说要实现一个轮播图的性能,首先须要在data里定义与此性能相干的数据,在methods里定义该性能的办法,在mounted里定义进入页面主动开启轮播的代码…… 有一个不言而喻的问题,就是同一个性能的代码却要扩散在页面的不同中央,保护起来会相当麻烦。

为了解决上述问题,Vue 3推出了具备清晰的代码构造,并可打消反复逻辑的 Composition API,以及两个全新的函数setup和ref。

Setup 函数可将属性和办法返回到模板,在组件初始化的时候执行,其成果相似于Vue 2中的beforeCreate 和 created。如果想应用setup里的数据,须要将值return进去,没有从setup函数返回的内容在模板中不可用。

Ref函数的作用是创立一个援用值,次要是对String、Number、Boolean的数据响应做援用。

绝对于Vue 2,Vue 3的生命周期函数也产生了变更,如下所示:

· beforeCreate -> 请应用 setup()

· created -> 请应用 setup()

· beforeMount -> onBeforeMount

· mounted -> onMounted

· beforeUpdate -> onBeforeUpdate

· updated -> onUpdated

· beforeDestroy -> onBeforeUnmount

· destroyed -> onUnmounted

· errorCaptured -> onErrorCaptured

须要留神的是,Vue 2应用生命周期函数时是间接在页面中写入生命周期函数,而在Vue 3则间接援用即可:

import {reactive, ref, onMounted} from 'vue'

3. Tree shaking support(按需打包模块)

有人将“Tree shaking”  称之为“摇树优化”,其实就是把无用的模块进行“剪枝”,剪去没有用到的API,因而“Tree shaking”之后,打包的体积将大幅度缩小。

官网将Vue 2和Vue 3进行了比照,Vue 2若只写了Hello World,且没有用到任何的模块API,打包后的大小约为32kb,而Vue 3 打包后仅有13.5kb。         

4. 全新的脚手架工具:Vite

Vite 是一个由原生 ESM 驱动的 Web 开发构建工具。在开发环境下基于浏览器原生 ES imports 开发,在生产环境下基于 Rollup 打包。

和 Webpack相比,具备以下特点:

· 疾速的冷启动,不须要期待打包

· 即时的热模块更新

· 真正的按需编译,不必期待整个我的项目编译实现

因为齐全跳过了打包这个概念,Vite的呈现大大的撼动了Webpack的位置,且真正做到了服务器随起随用。看来,连尤大神都难逃“真香”实践。

Vite到底有什么魔力?无妨让咱们通过理论搭建一款基于Vue 3 组件的表格编辑零碎,亲自体验一把。

一、环境搭建

应用 Vite 初始化一个 Vue 3 我的项目

1. 执行代码:

$ npm init vite-app <project-name>$ cd <project-name> //进入我的项目目录$ npm install //装置我的项目所需依赖$ npm run dev //启动我的项目

咱们来看下生成的代码, 因为 vite 会尽可能多地镜像 vue-cli 中的默认配置, 所以,这段代码看上去和 vue-cli 生成的代码没有太大区别。

├── index.html├── package.json├── public│ └── favicon.ico└── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── index.css └── main.js

2. 执行下列命令:

此时如果不通过 npm run dev 来启动我的项目,而是间接通过浏览器关上 index.html, 会看到上面的报错:

报错的起因:浏览器的 ES module 是通过 http 申请拿到模块的,所以 vite 的一个工作就是启动一个 web server 去代理这些模块,在 vite 里是借用了 koa 来启动的服务。

export function createServer(config: ServerConfig): Server {  // ...  const app = new Koa<State, Context>()  const server = resolveServer(config, app.callback())    // ...  const listen = server.listen.bind(server)  server.listen = (async (...args: any[]) => {    if (optimizeDeps.auto !== false) {      await require('../optimizer').optimizeDeps(config)    }    return listen(...args)  }) as any    return server}

因为浏览器中的 ESM 是获取不到导入的模块内容的,须要借助Webpack 等工具,如果咱们没有援用相对路径的模块,而是援用 node_modules,并间接 import xxx from 'xxx',浏览器便无奈得悉你我的项目里有 node_modules,只能通过相对路径或者绝对路径去寻找模块。

这便是vite 的实现外围:拦挡浏览器对模块的申请并返回解决后的后果(对于vite 的实现机制,文末会深刻解说)。

3. 生成我的项目构造:

入口 index.html 和 main.js 代码构造为:

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <link rel="icon" href="/favicon.ico" />  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>Vite App</title></head><body>  <div id="app"></div>  <script type="module" src="/src/main.js"></script></body></html>// main.js// 只是援用的是最新的 vue3 语法,其余雷同import { createApp } from 'vue'import App from './App.vue'import './index.css'createApp(App).mount('#app')

4. 进入我的项目目录:cd myVue3 

5. 装置相干模块:npm install

6. 下载模块:

7. 启动我的项目:npm run dev

8. 进入地址,当咱们看到这个页面时,阐明我的项目曾经胜利启动了。

Vite 的实现机制

1. /@module/ 前缀

比照工程下的 main.js 和开发环境下理论加载的 main.js,能够发现代码产生了变动。

工程下的 main.js:

import { createApp } from 'vue'import App from './App.vue'import './index.css'createApp(App).mount('#app')

理论加载的 main.js:

import { createApp } from '/@modules/vue.js'import App from '/src/App.vue'import '/src/index.css?import'createApp(App).mount('#app')

为了解决 import xxx from 'xxx' 报错的问题,vite 对这种资源门路做了对立解决,即增加一个/@module/前缀。

在 src/node/server/serverPluginModuleRewrite.ts 源码的 koa 中间件里能够看到 vite 对 import 做了一层解决,其过程如下:

· 在 koa 中间件里获取申请 body

· 通过 es-module-lexer 解析资源 ast 拿到 import 的内容

· 判断 import 的资源是否是绝对路径,相对视为 npm 模块

· 返回解决后的资源门路:"vue" => "/@modules/vue"

2. 反对 /@module/

在 /src/node/server/serverPluginModuleResolve.ts 里能够看到大略的解决逻辑:

· 在 koa 中间件里获取申请 body

· 判断门路是否以 /@module/ 结尾,如果是取出包名

· 去node_module里找到这个库,基于 package.json 返回对应的内容

3. 文件编译

通过前文,咱们晓得了 js module 的处理过程,对于vue、css、ts等文件,其又是如何解决的呢?

以 vue 文件为例,在 webpack 里应用 vue-loader 对单文件组件进行编译,在这里 vite 同样拦挡了对模块的申请并执行了一个实时编译。

通过工程下的 App.vue 和理论加载的 App.vue,便发现扭转。

工程下的 App.vue:

<template>  ![](./assets/logo.png)  <HelloWorld msg="Hello Vue 3.0 + Vite" /></template><script>import HelloWorld from './components/HelloWorld.vue';export default {  name: 'App',  components: {    HelloWorld,  },};</script><style>#app {  font-family: Avenir, Helvetica, Arial, sans-serif;  -webkit-font-smoothing: antialiased;  -moz-osx-font-smoothing: grayscale;  text-align: center;  color: #2c3e50;  margin-top: 60px;}</style>

理论加载的 App.vue:

import HelloWorld from '/src/components/HelloWorld.vue';const __script = {    name: 'App',    components: {        HelloWorld,    },};import "/src/App.vue?type=style&index=0&t=1592811240845"import {render as __render} from "/src/App.vue?type=template&t=1592811240845"__script.render = __render__script.__hmrId = "/src/App.vue"__script.__file = "/Users/wang/qdcares/test/vite-demo/src/App.vue"export default __script

可见,一个 .vue 文件被拆成了三个申请(别离对应 script、style 和template) ,浏览器会先收到蕴含 script 逻辑的 App.vue 的响应,而后解析到 template 和 style 的门路后,再次发动 HTTP 申请来申请对应的资源,此时 Vite 对其拦挡并再次解决后返回相应的内容。

// App.vue?type=styleimport { updateStyle } from "/vite/hmr"const css = "\n#app {\n  font-family: Avenir, Helvetica, Arial, sans-serif;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  text-align: center;\n  color: #2c3e50;\n  margin-top: 60px;\n}\n"updateStyle("7ac74a55-0", css)export default css// App.vue?type=templateimport {createVNode as _createVNode, resolveComponent as _resolveComponent, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock} from "/@modules/vue.js"const _hoisted_1 = /*#__PURE__*/_createVNode("img", {    alt: "Vue logo",    src: "/src/assets/logo.png"}, null, -1 /* HOISTED */)export function render(_ctx, _cache) {    const _component_HelloWorld = _resolveComponent("HelloWorld")    return (_openBlock(),    _createBlock(_Fragment, null, [_hoisted_1, _createVNode(_component_HelloWorld, {        msg: "Hello Vue 3.0 + Vite"    })], 64 /* STABLE_FRAGMENT */    ))}

vite对于其余的类型文件的解决简直都是相似的逻辑,即依据申请的不同文件类型,做出不同的编译处理结果。

扩大浏览

· Vue 3 组件开发实战:搭建基于SpreadJS的表格编辑零碎(组件集成篇)

· Vue 3 组件开发实战:搭建基于SpreadJS的表格编辑零碎(性能拓展篇)

· SpreadJS Vue 框架反对