11

Vue 3 组件开发:搭建基于SpreadJS的表格编辑系统(环境搭建)

 3 years ago
source link: https://segmentfault.com/a/1190000039808957
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Vue 3 组件开发:搭建基于SpreadJS的表格编辑系统(环境搭建)

发布于 4 月 12 日

image.png

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的地位,且真正做到了服务器随起随用。看来,连尤大神都难逃“真香”理论。

image.png

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. 执行下列命令:

image.png

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

image.png

报错的原因:浏览器的 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. 生成项目结构:

image.png

入口 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

image.png

6. 下载模块:

image.png

7. 启动项目:npm run dev

image.png

8. 进入地址,当我们看到这个页面时,说明项目已经成功启动了。

image.png

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=style
import { 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=template
import {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 框架支持


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK