Vue3 + web component 实现跨技术栈组件

Vue3 + web component 实现跨技术栈组件

Vite脚手架

创建vue项目

pnpm init @vitejs/app + 项目名

安装路由

 pnpm i vue-router@4
新建router文件夹index.ts
import { createRouter, createWebHistory } from "vue-router";

const routes = [
  {
    path: "/",
    redirect: "/home",
  },
  {
    path: "/home",
    name: "home",
    component: () => import("../view/home/index.vue"),
    meta: {
      title: "home",
    },
  },
];

export default createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior() {
    // 始终滚动到顶部
    return { top: 0 };
  },
});

main.ts

在main.ts里导入,使得整个应用支持路由。

import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";

const app = createApp(App);
app.use(router);

app.mount("#app");


App.vue
<script setup lang="ts">script>

<template><router-view>router-view>template>

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


项目跑起来以后,进行Web Component组件封装

Web Component组件封装

src下新建文件夹lib,新建button组件,封装组件所用语法与Vue2/3常用语法一致,这里使用的是vue3.x+ts

lib>demo>demo.vue

<template>
  <div class="demoValue">{{ demoValue }}</div>
  <div class="button" @click="clickHandler">+</div>
</template>

<script lang="ts" setup>
import { ref } from "vue";
const demoValue = ref<number>(0);
const clickHandler = () => {
  demoValue.value += 1;
};
</script>

<style scoped lang="less">
.demoValue {
  color: royalblue;
  font-size: 100px;
}
.button {
  width: 100px;
  height: 30px;
  border: 1px solid pink;
  border-radius: 5px;
  margin: 0 auto;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>

导入到home页面展示效果

Vue3 + web component 实现跨技术栈组件_第1张图片

组件可用开始导出工作,将组件样式单独新建一个css文件剪切出来。

demo.css
.demoValue {
    color: royalblue;
    font-size: 100px;
  }
  .button {
    width: 100px;
    height: 30px;
    border: 1px solid pink;
    border-radius: 5px;
    margin: 0 auto;
    display: flex;
    justify-content: center;
    align-items: center;
  }

demo组件新建index.ts,新建一个CustomElement。

index.ts
import { defineCustomElement } from "vue"
import Demo from "./demo.vue";
import demoStyle from './demo.css'

export default defineCustomElement({...Demo, styles: [
    demoStyle
]})

lib根目录下新建index.ts ,进行相关组件配置

lib>index.ts

import { App } from "vue";

import ComDemo from "./demo";

const components = [ComDemo];

// 导出普通vue组件
export { ComDemo };

// 注册普通VUE组件
export function registerComUIVue3(app: App): void {
  for (const component of components) {
    app.component(component.name, component);
  }
}

// 注册跨技术栈组件
export function registerComUI(): void {
  customElements.define("com-demo", ComDemo);
}

export default registerComUI;

打包

需安装rollup,node版本需在12.x以上

为了方便使用,这里将rollup打包出口文件配置在了dist目录中,先需使用vite进行打包后,方可使用rollup打包组件,rollup配置参考官网。

指令

vite
npm run build
rollup
rollup -c  
根目录新建rollup.config.js
import esbuild from "rollup-plugin-esbuild";
import vue from "rollup-plugin-vue";
import scss from "rollup-plugin-scss";
import dartSass from "sass";
import { terser } from "rollup-plugin-terser";
import alias from "@rollup/plugin-alias";
import path from "path";
import resolve from "rollup-plugin-node-resolve";
import commonjs from "rollup-plugin-commonjs";
import json from "@rollup/plugin-json";
import styles from "rollup-plugin-styles";

export default {
  input: "src/lib/index.ts",
  output: [
    {
      globals: {
        vue: "Vue",
      },
      name: "boriska-ui",
      file: "dist/lib/boriska-ui.js",
      format: "umd",
      plugins: [terser()],
    },
    {
      name: "boriska-ui",
      file: "dist/lib/boriska-ui.esm.js",
      format: "es",
      plugins: [terser()],
    },
  ],
  plugins: [
    json(),
    resolve({
      browser: true,
      preferBuiltins: true,
    }),
    commonjs(),
    scss({ include: /\.scss$/, sass: dartSass }),
    styles({
      mode: "extract",
    }),
    esbuild({
      include: /\.[jt]s$/,
      minify: process.env.NODE_ENV === "production",
      target: "es2015",
    }),
    vue({
      include: /\.vue$/,
    }),
    alias({
      entries: [
        {
          find: "@", // 别名名称,作为依赖项目需要使用项目名
          replacement: path.resolve(__dirname, "src"),
          customResolver: resolve({
            extensions: [".js", ".jsx", ".vue", ".sass", ".scss"],
          }),
        },
      ],
    }),
  ],
};

package.json
{
  "name": "boriskaui",
  "version": "0.2.0",
  "license": "MIT",
  "author": "sxzq",
  "files": [
    "dist/lib/*"
  ],
  "main": "dist/lib/boriska-ui.esm.js",
  "module": "dist/lib/boriska-ui.esm.js",
  "scripts": {
    "dev": "vite --host",
    "build": "vite build",
    "preview": "vite preview",
    "prepare": "husky install"
  },
  "dependencies": {
    "@vitejs/plugin-vue-jsx": "^1.3.4",
    "@vue/babel-preset-jsx": "^1.2.4",
    "axios": "^0.21.4",
    "crypto-js": "^4.1.1",
    "github-markdown-css": "^5.1.0",
    "less": "^4.1.3",
    "path": "^0.12.7",
    "prismjs": "^1.26.0",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-jsx": "^1.0.3",
    "rollup-plugin-typescript2": "^0.31.2",
    "vite-plugin-markdown": "^2.0.2",
    "vue": "^3.2.25",
    "vue-router": "4"
  },
  "devDependencies": {
    "@commitlint/cli": "^16.2.1",
    "@commitlint/config-conventional": "^16.2.1",
    "@rollup/plugin-alias": "^3.1.9",
    "@rollup/plugin-json": "^4.1.0",
    "@vicons/antd": "^0.11.0",
    "@vicons/carbon": "^0.11.0",
    "@vicons/fa": "^0.11.0",
    "@vicons/fluent": "^0.11.0",
    "@vicons/ionicons4": "^0.11.0",
    "@vicons/ionicons5": "^0.11.0",
    "@vicons/material": "^0.11.0",
    "@vicons/tabler": "^0.11.0",
    "@vicons/utils": "^0.1.4",
    "@vitejs/plugin-vue": "^2.0.1",
    "cross-env": "^7.0.3",
    "husky": "^7.0.4",
    "marked": "^4.0.9",
    "mockjs": "^1.1.0",
    "rollup-plugin-commonjs": "^10.1.0",
    "rollup-plugin-esbuild": "^4.8.2",
    "rollup-plugin-node-resolve": "^5.2.0",
    "rollup-plugin-scss": "^3.0.0",
    "rollup-plugin-styles": "^4.0.0",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-vue": "^6.0.0",
    "sass": "^1.47.0",
    "vite": "^2.7.2",
    "vite-plugin-mock": "^2.9.6"
  }
}


vite.config.ts基本配置
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // 将所有包含短横线的标签作为自定义元素处理
          isCustomElement: (tag) => tag.startsWith("jw-"),
        },
      },
    }),
  ],
});


打包完成

打包完成后,在dist>lib找到打包好的文件。
Vue3 + web component 实现跨技术栈组件_第2张图片

使用

vite.config.ts同样需要添加web component配置。

    plugins: [
      vue({
        template: {
          compilerOptions: {
            // 将所有包含短横线的标签作为自定义元素处理
            isCustomElement: (tag) => tag.includes("jw-"),
          },
        },
      }),

      visualizer(),
    ],
在Vue中使用

复制该文件到vue的组件库中。导入到页面文件中,组件的命名需与到出的文件名保持一致。自定义标签的前缀,需要与vite中的配置对应。

 isCustomElement: (tag) => tag.includes("jw-"),






使用效果

Vue3 + web component 实现跨技术栈组件_第3张图片

react使用
import { useState } from 'react'
import logo from './logo.svg'
import './App.css'
import { ComDemo } from "./Comui/boriska-ui.esm.js";
function App() {
  const [count, setCount] = useState(0)
  const initHandler = async () => {
    const res = await customElements.get("JwButton");
    return res;
  };
  initHandler().then((res: any) => {
    customElements.define("jw-button", ComDemo);
  });
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>Hello Vite + React!</p>
        <jw-button />

      </header>
    </div>
  )
}

export default App

踩坑

封装组件时,不管有没有参数,都要声明defineProps。

const props = defineProps({
  size: {
    type: [String, Number],
    default: 10,
  },
});

否则打包后引用组件时会报错

tsconfig.json添加配置
"skipLibCheck": true,

你可能感兴趣的:(Vue3,前端,vue.js,javascript)