likes
comments
collection
share

使用vite打包单个组件和直接使用浏览器打开打包的方式

作者站长头像
站长
· 阅读数 13

这篇文章主要分享一下vite打包两种方式

  • vite打包支持浏览器直接打开
  • 使用vite打包单个组件并发布到npm

由于都属于vite打包相关就将其整合到一篇文章中了,有相关需求的朋友可直接查看对应章节即可

vite打包支持浏览器直接打开

最近遇到一个需求,为公司客户端写一个pdf打印模块。由于客户端同事坚持要使用静态文件的方式打开。。。那没办法,我得满足他

  • 配置base为./相对路径
import { UserConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default ({ mode }): UserConfig => {
  return {
    base: mode === 'development' ? '/' : './',
    plugins: [vue()]
  }
}

可是打包后打开直接用index.html页面却呈现出空白状态,我小心翼翼打开控制台发现是跨域问题

使用vite打包单个组件和直接使用浏览器打开打包的方式

官方给出的解释是你得起个服务 cn.vitejs.dev/guide/troub…

无奈,只能另寻它法

方式一:将所有资源打包到index.html中

使用vite-plugin-singlefile插件将所有资源打包到index.html

  • 安装插件
npm i vite-plugin-singlefile -D
  • 修改vite.config.ts配置
import { UserConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteSingleFile } from 'vite-plugin-singlefile'

// https://vitejs.dev/config/
export default ({ mode }): UserConfig => {
  return {
    base: mode === 'development' ? '/' : './',
    plugins: [vue(), viteSingleFile()]
  }
}

缺陷:项目过大会导致index.html越大,加载很慢,用不用视情况而定

方式二:这也是作者推荐的方式

  • 使用@vitejs/plugin-legacy为打包后的文件提供传统浏览器兼容性支持
npm i @vitejs/plugin-legacy terser -D
  • 配置vite.config.ts
import { UserConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import legacy from '@vitejs/plugin-legacy'
// https://vitejs.dev/config/
export default ({ mode }): UserConfig => {
  return {
    base: mode === 'development' ? '/' : './',
    plugins: [
      vue(),
      legacy({
        targets: ['defaults', 'not IE 11']
      })
    ]
  }
}
  • npm run build 打包后将index.html中所有的 标签中的 type="module"、crossorigin、nomodule 移除

   <script type="module" crossorigin src="./assets/index-993358c3.js"></script>
   <script nomodule crossorigin id="vite-legacy-polyfill" src="./assets/polyfills-legacy-2fc23d6e.js"></script>

   <!-- 移除后 -->
   <script src="./assets/index-993358c3.js"></script>
   <script id="vite-legacy-polyfill" src="./assets/polyfills-legacy-2fc23d6e.js"></script>
  • 手动替换显然不是明智之举,我们可以写个脚本,运行完打包后再运行脚本替换
// scripts/build.cjs

const fs = require('fs')
const path = require('path')

const srcDir = path.resolve(__dirname, '../dist')

function mian() {
  const url = path.join(srcDir, 'index.html')
  // 读取dist/index.html
  let htmlStr = fs.readFileSync(url, { encoding: 'utf-8' })
  // 匹配script标签
  const reg = /<script.*>/g
  
  htmlStr = htmlStr.replace(reg, (str) => {
    // 替换关键字
    return str.replace(/\snomodule|\scrossorigin|\stype="module"/g, '')
  })
  // 输出文件
  fs.writeFileSync(url, htmlStr)
}

mian()
  • 修改package.json打包命令
{
   "scripts": {
    "build": "vue-tsc && vite build && node ./scripts/build.cjs",
  }
}
  • 运行 npm run build 查看效果

使用vite打包单个组件并发布到npm

前段时间写了两个独立的vue组件,es-drager拖拽组件es-calendar日历组件,这里记录一下使用vite打包单个组件的方式

  • 支持单文件引入
  • 支持script标签方式引入
  • 支持ts类型提示

关于组件库的打包以及如何开发vue3组件库请参考我的这篇文章 手把手从零搭建一个 vue3 组件库 (一)

组件准备

  • drager组件 src/drager/drager.vue
<template>
  <div class="es-drager"></div>
</template>

<script setup lang="ts"></script>
<style scoped>
.es-drager {
  width: 100px;
  height: 100px;
  border: 1px solid red;
}
</style>
  • 导出组件,添加install方法
// src/drager/index.ts
import { App, Plugin } from 'vue'
import Drager from './drager.vue'

export const install = (app: App) => {
  app.component('es-drager', Drager)
}

Drager.install = install

export {
  Drager as ESDrager
}
export default Drager as Plugin & typeof Drager
  • src/vite-env.d.ts
/// <reference types="vite/client" />
declare module '*.vue' {
  import type { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

打包配置

  • 安装 vite-plugin-dts
npm i vite-plugin-dts -D

vite.config.ts配置

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import dts from 'vite-plugin-dts'
import { resolve } from 'path'

export default defineConfig({
  plugins: [dts(), vue()],
  build: {
    outDir: 'lib',
    lib: {
      entry: resolve(__dirname, './src/drager/index.ts'),
      name: 'ESDrager', // 使用script标签引入时全局名称
      fileName: format => `index.${format}.js`,
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ['vue'],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          vue: 'Vue'
        }
      }
    }
  }
})

vite库模式默认支持es 和 umd格式,根据需求可自行修改

package.json 配置,参考 cn.vitejs.dev/guide/build…

// package.json
{
   "name": "es-drager",
   "version": "0.0.0",
   "files": [
      "lib" // 将lib目录发布到npm
   ],
   "main": "./lib/index.umd.js", // script标签引入umd
   "module": "./lib/index.es.js", // 单文件引入
   "exports": {
      ".": {
         "import": "./lib/index.es.js",
         "require": "./lib/index.umd.js"
      },
      "./*": "./*"
   },
   "typings": "lib/drager/index.d.ts", // ts类型声明文件
   // ... 其它配置
}
  • typings 设置打包后的类型声明入口文件

  • 相对路径导入,例如导入样式: import 'es-drager/lib/style.css',如果没有配置在vite项目中会报错

{
   "exports": {
      "./*": "./*"
   },
}

添加发布脚本

{
   "scripts": {
      "dev": "vite",
      "build": "vue-tsc && vite build",
      "release": "npm run build && npm publish", // 新增
      "preview": "vite preview"
   }
}

运行 npm run release打包并发布

完整package.json配置

{
  "name": "es-drager",
  "version": "0.2.9",
  "description": "A drag、resizable、rotatable component based on vue3",
  "keywords": [
    "drag",
    "resize",
    "rotate",
    "vue-drager",
    "vue-drag-resize",
    "vue-drager",
    "es-drager"
  ],
  "homepage": "https://github.com/vangleer/es-drager",
  "bugs": {
    "url": "https://github.com/vangleer/es-drager/issues"
  },
  "license": "MIT",
  "author": "vangleer",
  "files": [
    "lib"
  ],
  "main": "./lib/index.umd.js",
  "module": "./lib/index.es.js",
  "exports": {
    ".": {
      "import": "./lib/index.es.js",
      "require": "./lib/index.umd.js"
    },
    "./*": "./*"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/vangleer/es-drager.git"
  },
  "scripts": {
      "dev": "vite",
      "build": "vue-tsc && vite build",
      "release": "npm run build && npm publish",
      "preview": "vite preview"
  },
  "dependencies": {
    "es-drager": "^0.2.8",
    "vue": ">=3.2.0"
  },
  "devDependencies": {
    "@types/node": "^18.15.13",
    "@vitejs/plugin-vue": "^4.1.0",
    "typescript": "^4.9.3",
    "vite": "^4.2.0",
    "vite-plugin-dts": "^2.3.0",
    "vue-tsc": "^1.2.0"
  },
  "peerDependencies": {
    "vue": ">=3.2.0"
  },
  "typings": "lib/drager/index.d.ts"
}

使用说明

全局注册

import { createApp } from 'vue'
import App from './App.vue'

import 'es-drager/dist/style.css'
import Drager from 'es-drager'

createApp(App)
  .component('es-drager', Drager)
  .mount('#app')
  • 使用
<template>
  <es-drager>
    drager
  </es-drager>
</template>

组件中直接使用

<template>
  <Drager>
    drager
  </Drager>
</template>

<script setup lang='ts'>
import Drager from 'es-drager'
</script>

浏览器直接引入

直接通过浏览器的 HTML 标签导入 es-drager,然后就可以使用全局变量 ESDrager 了。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link rel="stylesheet" href="https://unpkg.com/es-drager/lib/style.css">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <es-drager>drager</es-drager>
  </div>

  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <script src="https://unpkg.com/es-drager"></script>
  <script>
    const { createApp } = Vue
    const app = createApp({})
    app.use(ESDrager)
    app.mount('#app')
  </script>
</body>
</html>