写点什么

Vite for Vue 是什么?

用户头像
꯭🇫꯭
关注
发布于: 2020 年 05 月 29 日
Vite for Vue 是什么?

vite 是一个基于 Vue3 单文件组件的非打包开发服务器

这和传统基于打包(例如 Webpack)的开发服务器有什么区别


vite 在开发的时候没有打包的过程,ES 模块源码直接传输给浏览器,浏览器使用自带的 <script module> 进行解析支持,通过 HTTP 请求进行每次 import,开发服务器拦截请求和对需要转换的代码进行转换。


例如:*.vue 文件会在发回浏览器之前进行编译


这样操作有许多优势:


  • 开发服务器启动后不需要进行打包操作,启动会变得非常迅速


  • 代码在需要的时候进行编译,所以只有代码真正在屏幕上展现的时候才进行编译。开始开发的时候再也不需要等待整个应用编译完成,这对大型应用是一个巨大的改变


  • 热模块替换的性能和模块的数量之间的关系解耦,热模块替换变得非常快


导入本地 ES 模块可能会引发深层的导入链路,整个页面重新加载会比依赖打包的开发服务器略慢。然而这是一个本地开发服务器,这部分增加的时间和实际编译的时间相比应该非常小(编译的文件会被缓存在内存中)


vite 的编译本质上还是的 Node.js 中进行,从技术上讲它可以支持打包工具能支持的各种代码转换,没有什么可以阻止你将代码包用于生产,实际上,vite 提供了vite build的脚本用于这个操作,因此不会在生产环境中遭遇到网络流爆炸的问题


当前 vite 尚处于实验性阶段,不适合用于生产环境,但希望有一天能做到这个目标


特性


模块解析


本地 ES 模块导入不支持如下的导入方式


import { createApp } from 'vue'


默认情况下将会导致一个错误,vite 在 js 文件中检测到这种情况将会将其改写为@modules/{package-name},在这些特殊的路径下,vite 执行以下的方式找到正确的文件


  • vue 有特殊的处理,你不需要安装这个模块,如果需要使用特殊的版本,vite 将会使用node_modules内部的模块包


  • 如果web_modules目录存在,将会使用它


  • 如果其它方式都没有定位到模块,将会在node_modules中查找


热模块替换


  • 对于 *.vue 文件将会得到开箱即用的替换功能



import { foo } from "./foo.js";import { hot } from "@hmr";
foo();
hot.accept("./foo.js", ({ foo }) => { // the callback receives the updated './foo.js' module foo();});
复制代码


CSS 预处理器


安装模块即可在 *.vue 中使用


<style lang="scss">/* use scss */</style>
复制代码


生产构建


执行 vite build,当前支持 --root--cdn 两个参数


API


可以使用 API 定制开发服务器,vite 支持插件形式扩展,可以定制化访问 vite 内部的 koa 实例和增加相关的中间件


下一步开发计划


  • Source Map 支持

  • 自动加载 postcss 配置


解析


启动一个 vite 开发服务器


  • http://localhost:3000/ 首屏页面


<div id="app"></div><script type="module">  import { createApp } from "/@modules/vue"; // 此模块中包含相关热加载逻辑  import App from "./App.vue"; // 此文件为SFC主模板
createApp(App).mount("#app"); // 渲染模版</script>
复制代码


  • http://localhost:3000/App.vue 主模板


import { updateStyle } from "/@hmr"; // 加载更新style方法
const __script = { data: () => ({ count: 0 })};
updateStyle("c44b8200-0", "/App.vue?type=style&index=0");__script.__scopeId = "data-v-c44b8200";import { render as __render } from "/App.vue?type=template"; // 加载template模板__script.render = __render;__script.__hmrId = "/App.vue";__script.__file = "/Users/shoyuf/work/vite-app/App.vue";export default __script;
复制代码


  • /@hmr 更新逻辑


console.log("[vite] connecting...");const socket = new WebSocket(`ws://${location.host}`);// Listen for messagessocket.addEventListener("message", ({ data }) => {  const { type, path, id, index, timestamp } = JSON.parse(data);  switch (type) {    case "connected": // 连接成功      console.log(`[vite] connected.`);      break;    case "vue-reload": // 当script改变的情况下,需要重新加载      import(`${path}?t=${timestamp}`).then(m => {        __VUE_HMR_RUNTIME__.reload(path, m.default);        console.log(`[vite] ${path} reloaded.`);      });      break;    case "vue-rerender": // 当template改变的情况下,需要重新渲染      import(`${path}?type=template&t=${timestamp}`).then(m => {        __VUE_HMR_RUNTIME__.rerender(path, m.render);        console.log(`[vite] ${path} template updated.`);      });      break;    case "vue-style-update": // 当css改变情况下更新style      updateStyle(id, `${path}?type=style&index=${index}&t=${timestamp}`);      console.log(        `[vite] ${path} style${index > 0 ? `#${index}` : ``} updated.`      );      break;    case "vue-style-remove": // css改变后移除旧的css引用      const link = document.getElementById(`vite-css-${id}`);      if (link) {        document.head.removeChild(link);      }      break;    case "js-update": // js 模块更新重新加载      const update = jsUpdateMap.get(path);      if (update) {        update(timestamp);        console.log(`[vite]: js module reloaded: `, path);      } else {        console.error(          `[vite] got js update notification but no client callback was registered. Something is wrong.`        );      }      break;    case "full-reload": // 导入链进入死胡同,需要进行页面重新加载      location.reload();  }});// ping serversocket.addEventListener("close", () => {  console.log(`[vite] server connection lost. polling for restart...`);  setInterval(() => {    new WebSocket(`ws://${location.host}`).addEventListener("open", () => {      location.reload();    });  }, 1000);});export function updateStyle(id, url) {  const linkId = `vite-css-${id}`;  let link = document.getElementById(linkId);  if (!link) {    link = document.createElement("link");    link.id = linkId;    link.setAttribute("rel", "stylesheet");    link.setAttribute("type", "text/css");    document.head.appendChild(link);  }  link.setAttribute("href", url);}const jsUpdateMap = new Map();export const hot = {  accept(importer, deps, callback) {    jsUpdateMap.set(importer, timestamp => {      if (Array.isArray(deps)) {        Promise.all(deps.map(dep => import(dep + `?t=${timestamp}`))).then(          callback        );      } else {        import(deps + `?t=${timestamp}`).then(callback);      }    });  }};
复制代码


  • /App.vue?type=template 主模板 HTML 部分


import {  createVNode as _createVNode,  toDisplayString as _toDisplayString,  Fragment as _Fragment,  openBlock as _openBlock,  createBlock as _createBlock,  withScopeId as _withScopeId,  pushScopeId as _pushScopeId,  popScopeId as _popScopeId} from "/@modules/vue";const _withId = _withScopeId("data-v-c44b8200");
_pushScopeId("data-v-c44b8200");const _hoisted_1 = _createVNode( // 创建Virtual DOM "h1", null, "Hello Vite + Vue 3!", -1 /* HOISTED */);const _hoisted_2 = _createVNode( "p", null, "Edit ./App.vue to test hot module replacement (HMR).", -1 /* HOISTED */);_popScopeId();
export const render = _withId(function render(_ctx, _cache) { // 渲染函数 return ( _openBlock(), _createBlock( _Fragment, null, [ _hoisted_1, _hoisted_2, _createVNode("p", null, [ _createVNode( "span", null, "Count is: " + _toDisplayString(_ctx.count), 1 /* TEXT */ ), _createVNode( "button", { onClick: _cache[1] || (_cache[1] = $event => _ctx.count++) }, "increment" ) ]) ], 64 /* STABLE_FRAGMENT */ ) );});
复制代码


  • /App.vue?type=style&index=0 主模板 css 部分,包括 scopedId


h1[data-v-c44b8200] {  color: #4fc08d;}h1[data-v-c44b8200],p[data-v-c44b8200] {  font-family: Arial, Helvetica, sans-serif;}
复制代码


  • ws://localost:3000/ 执行热替换的数据交互,与/@hmr相联


Example:


{  path: "/App.vue",  timestamp: 1588242356511,  type: "vue-reload"}
复制代码


type 与@hmr的相关方法一致


  • vue-reload

  • vue-rerender

  • vue-style-update

  • vue-style-remove

  • js-update

  • full-reload


webpack 很慢



参考


  1. vuejs/vite: Experimental no-bundle dev server for Vue SFCs


发布于: 2020 年 05 月 29 日阅读数: 155
用户头像

꯭🇫꯭

关注

还未添加个人签名 2018.05.30 加入

还未添加个人简介

评论

发布
暂无评论
Vite for Vue 是什么?