lisonge / vite-plugin-monkey

A vite plugin server and build your.user.js for userscript engine like Tampermonkey, Violentmonkey, Greasemonkey, ScriptCat
MIT License
1.33k stars 70 forks source link

[功能请求] 新增一种可以让 resource 使用的 cdn 工具函数 #18

Closed Tsuk1ko closed 2 years ago

Tsuk1ko commented 2 years ago

我感觉在 resource 里添加 cdn 上的 css 这种使用方式挺常见的,能否让 resource 跟 externalGlobals 一样支持使用 cdn 工具函数

(想了下好像不太对,应该是需要一种新的 cdn 工具函数能直接返回 cdn 地址字符串,主要是为了可以动态获取版本号)

lisonge commented 2 years ago

不好意思,没完全懂,可否说一下具体应用场景?

lisonge commented 2 years ago

这种是否符合你说的场景呢?

// vite.config.ts
import { defineConfig } from 'vite';
import monkey, { cdn } from 'vite-plugin-monkey';

export default defineConfig(({ command, mode }) => ({
  plugins: [
    monkey({
      build: {
        externalGlobals: {
          'element-plus': cdn.jsdelivr('ElementPlus'),
          'element-plus/dist/index.css': [
            '',
            (version) => {
              const fn = ({ href = '' }) => {
                const style = document.createElement('style');
                style.innerHTML = `@import '${href}';`;
                document.head.appendChild(style);
              };
              return (
                'data:application/javascript,' +
                encodeURIComponent(
                  `;(${fn
                    .toString()
                    .replaceAll(/[\s]{2}/g, '')})(${JSON.stringify({
                    href: `https://unpkg.com/element-plus@${version}/dist/index.css`,
                  })});`,
                )
              );
            },
          ],
        },
      },
    }),
  ],
}));

main.ts

import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
console.log(ElementPlus);

dist.user.js

// ==UserScript==
// @name               example
// @namespace          https://github.com/lisonge
// @version            1.0.1
// @author             lisonge
// @description        default description zh
// @include            /^https:\/\/i\.songe\.li\/.*/
// @match              https://i.songe.li/
// @require            https://unpkg.com/element-plus@2.2.16/dist/index.full.js
// @require            data:application/javascript,%3B((%7B%20href%20%3D%20%22%22%20%7D)%20%3D%3E%20%7B%20const%20style%20%3D%20document.createElement(%22style%22)%3B%20style.innerHTML%20%3D%20%60%40import%20'%24%7Bhref%7D'%3B%60%3B%20document.head.appendChild(style)%3B%20%7D)(%7B%22href%22%3A%22https%3A%2F%2Funpkg.com%2Felement-plus%402.2.16%2Fdist%2Findex.css%22%7D)%3B
// ==/UserScript==
lisonge commented 2 years ago

@Tsuk1ko

这种也可以解决你代码里存在的问题

/src/utils/elementPlus.ts#L7-L14 image

import { defineConfig } from 'vite';
import monkey, { cdn } from 'vite-plugin-monkey';
import elementPlusPkg from 'element-plus/package.json';

export default defineConfig(({ command, mode }) => ({
  plugins: [
    monkey({
      userscript: {
        resource:
          command == 'build'
            ? {
                'element-plus': `https://unpkg.com/element-plus@${elementPlusPkg.version}/dist/index.css`,
              }
            : {},
        grant: ['GM_addStyle', 'GM_getResourceText'],
      },
      build: {
        externalGlobals: {
          'element-plus': [
            'ElementPlus',
            (version) =>
              `https://unpkg.com/element-plus@${version}/dist/index.full.js`,
          ],
          'element-plus/dist/index.css': [
            '',
            () => {
              // @ts-ignore
              const fn = () => GM_addStyle(GM_getResourceText('element-plus'));
              return (
                'data:application/javascript,' +
                encodeURIComponent(
                  `;(${fn.toString().replaceAll(/[\s]{2}/g, '')})();`,
                )
              );
            },
          ],
        },
      },
    }),
  ],
}));
lisonge commented 2 years ago

主要是为了可以动态获取版本号

代码里可以这样获取版本号

// vite.config.ts
import elementPlusPkg from 'element-plus/package.json';
// elementPlusPkg 在 vscode 中是自动类型提示的
console.log(elementPlusPkg.version);
lisonge commented 2 years ago

// @require data:application/javascript

在 greasyfork 里,现在它是已经允许的 cdn url,具体可以看 greasyfork#1084

Tsuk1ko commented 2 years ago

主要是为了可以动态获取版本号

代码里可以这样获取版本号

// vite.config.ts
import elementPlusPkg from 'element-plus/package.json';
// elementPlusPkg 在 vscode 中是自动类型提示的
console.log(elementPlusPkg.version);

对的,其实我是想要这样的效果,感谢思路,我都忘了还能直接导入 package.json 😂


这种也可以解决你代码里存在的问题

我需要将 element-plus 放在 resource 里的原因是他需要依赖 vue,很奇怪的一点是即使我已经确保它的 require 在 vue 后面,但它还是取不到脚本全局作用域里的 vue,不知是否和 vue 的导出方式和 element-plus 的取变量方式有关

image

image

所以我只能手动把 vue 挂载 window 上再 eval element-plus(不知道有没有更好的方式)


将 css 写到 externalGlobals 里这个操作学到了,在我这个场景里倒是不是很必要,因为 element-plus 组件我只会在极少数的情况下使用,所以弄成现在这样类似动态导入的效果也挺好的

Tsuk1ko commented 2 years ago

// @require data:application/javascript

在 greasyfork 里,现在它是已经允许的 cdn url,具体可以看 greasyfork#1084

说起来假如我需要一开始就在 require 里引入 element-plus,貌似可以利用这点去手动在 require vue 和 require element-plus 之间加入一段 js 来执行我上面所说的将 vue 塞到 window 里的操作,不过这就要求插件需要可以自定义 require 和 externalGlobals 内部的顺序,或者是让一个 externalGlobals 可以插入多个 require

lisonge commented 2 years ago

我需要将 element-plus 放在 resource 里的原因是他需要依赖 vue,很奇怪的一点是即使我已经确保它的 require 在 vue 后面,但它还是取不到脚本全局作用域里的 vue,不知是否和 vue UMD 的导出方式和 element-plus 的取变量方式有关

解决方法在 issues/5#issuecomment-1229716109

这也正是我发起 greasyfork#1084 的原因

lisonge commented 2 years ago

不过这就要求插件需要可以自定义 require 和 externalGlobals 内部的顺序,或者是让一个 externalGlobals 可以插入多个 require

插件 require 排列的顺序就是 externalGlobals 遍历 key 的顺序

当然,如果想自定义顺序,可以直接使用 pluginConfig.format , 输入参数是 string[][] 每项的第一项都是 userscript 的 key 后续项是它的值,对于 resource 这种,有两个后续项

Tsuk1ko commented 2 years ago

明白了,非常感谢

lisonge commented 2 years ago

如果你使用的 cdn 来自 jsdelivr, 它提供了 自动 mini js 的功能 可以把 https://cdn.jsdelivr.net/npm/element-plus@2.2.16/dist/index.full.js 换成 https://cdn.jsdelivr.net/npm/element-plus@2.2.16/dist/index.full.min.js


因为 element-plus 组件我只会在极少数的情况下使用

element-plus 是支持 tree shaking 的,其实可以直接打包进去的,少量使用 不会让 dist.user.js 增加多少大小

使用的时候不要用全量导入

import elementPlus from 'element-plus'
import  *  as elementPlus2 from 'element-plus'

要用

import { ElButton } from 'element-plus'

具体可以看 https://element-plus.org/zh-CN/guide/quickstart.html#%E6%8C%89%E9%9C%80%E5%AF%BC%E5%85%A5

Tsuk1ko commented 2 years ago

嗯,这个我是知道的

其实一开始我就是按需引用的,虽然用的很少,由于不允许缩小化所以还是会让脚本体积大幅增加(大约增加 600KB,虽然如果 gzip 的话也不算很多)

所以想了想索性就放 resource 里吧,而且也能让成品的可读性变高

lisonge commented 2 years ago

原来是这样

lisonge commented 2 years ago

@Tsuk1ko

new feat externalResource at v2.4.0

lisonge commented 2 years ago

example see external-source/vite.config.ts

https://github.com/lisonge/vite-plugin-monkey/blob/2639b710a1f0cadee64ac6560452c4ba0ff89e62/playground/external-source/vite.config.ts#L24-L30

lisonge commented 2 years ago

不过这就要求插件需要可以自定义 require 和 externalGlobals 内部的顺序,或者是让一个 externalGlobals 可以插入多个 require

perf: now externalGlobals is support Array at v2.4.1

example at playground/ex-vue-demi

https://github.com/lisonge/vite-plugin-monkey/blob/418ffb85dca2eb91260b3bc22bab110c7202bff7/playground/ex-vue-demi/vite.config.ts#L17-L38