likes
comments
collection
share

【Vite插件】使用importmap添加cdn的一些思考

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

最近在优化项目的打包,一直有在想是否有办法将第三方库换成cdn,这样包的加载、缓存都可以得到很大的改善,并且也可以减少打包后项目的体积,节省服务器资源等

但由于项公司项目是 运行在沙箱 中,开发环境下无法加载外部资源

【Vite插件】使用importmap添加cdn的一些思考

一直都束手无策

后来偶然想到可以在开发环境使用npm包,然后在构建时替换成CDN;嗯,倒是有一点可行性

1、src="CDN" 添加CDN

构建工具:vite

思路:在构建时,将某些第三方包排除,不对其进行打包;编写一个vite插件,添加CDN到html文件

<script type="module" src="cdn"></script>

1.1 排除第三方模块

// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      external: ['vue']  // 将vue排除在bundle外部
    }
  }
})

1.2 编写vite插件

查阅文档vite提供了一个钩子:transformIndexHtml

  • 一个转化html文件的专用钩子
  • return 转换后的html字符串
function addCDNsPlugin(options) {
	const { cdnUrls } = options;

	return {
		name: 'vite-plugin-add-importmap',
		transformIndexHtml(html) {
      let scriptTag = ''
      cdnUrls.forEach(url => {
        scriptTag += `
    <script type="module" src="${url}"></script>
        `;
      })
      // 将其拼接在title标签后面
			const updatedHtml = html.replace('</title>', `</title>\n\t${scriptTag}`);

			return updatedHtml  // 返回新的html字符串
		}
	}
}

export default addCDNsPlugin

首先,vite插件是一个函数,它会返回一个对象,对象中必须要有一个name属性

将其在vite.config.js中应用起来:

import addCDNsPlugin from './vite-plugin-cdn.js'

export default defineConfig({
  plugins: [
    // 添加vue和Element Plus的cdn
    addCDNsPlugin({
      cdnUrls: [
        "https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.37/vue.esm-browser.prod.min.js",
        '//unpkg.com/element-plus'
      ]
    })
  ]
})
  • 构建:pnpm build

【Vite插件】使用importmap添加cdn的一些思考

  • 预览构建后的页面:pnpm preview

【Vite插件】使用importmap添加cdn的一些思考

可以看到已成功加载了vue,但也报错了

大概意思是:无法解析vue模块

很明显,这种方式不对的,因为它是在全局注册一个Vue,让我们可以直接访问Vue

const { ref } = Vue

const num = ref(0)

所以如果使用这种方式,我们在main.ts中就应该这么使用:

// main.ts
// 直接通过全局Vue对象导出而不是vue模块

/** 
	区别于: import { createApp } from 'vue'
*/
import { createApp } from Vue

import App from './App.vue'

createApp(App)

这很明显会导致在开发和生成的行为不一致,绝对不允许

2、启用import Map

如果使用ES模块构建开发版本,那就可以像我们使用vue库那样使用了

// 通过esm browser版本 + 原生ES模块

<script type="module">
  import { ref, createApp } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
  ...
</script>

这种方式就很像

import { ref, createApp } from 'vue'

只需要将vue = "https://unpkg.com/vue@3/dist/vue.esm-browser.js"即可,其实就是给CDN起一个别名

我们可以使用导入映射表来实现这个效果,即

<script type="importmap">
  {
    "imports": {
      "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
    }
  }
</script>

其中,这个 导入映射表的内容必须是一个标准的JSON

此时就可以通过import { ref, createApp } from 'vue'这种方式来访问vue了,这也是我们想要的结果

基于此,我们重写一下vite插件

2.1 vite插件 — 实现importmap

  1. 插件接收一个映射表
  2. 将其转化成标准的JSON
  3. 拼接在importmap中,并添加到html中
function addImportmapPlugin(options) {
  // importMap:映射表
  // isAdd:控制是否添加(可以用来控制dev不添加,如果是发版则添加)
  const { importMap, isAdd } = options

  return {
    name: 'vite-plugin-add-importmap',
    transformIndexHtml(html: string) {
      if (isAdd === false || !importMap) return html

      // 这里采用JSON.stringify转化成标准的json
      const cdnUrlStr = JSON.stringify(importMap)

      // 构造importmap
      const scriptTag =`
        <script type="importmap">
          { "imports": ` +
            cdnUrlStr +
           ` }
         </script>`

      const updatedHtml = html.replace('</title>', `</title>\n\t${scriptTag}`)

      return updatedHtml
    }
  }
}

vite中应用起来:

VitePluginCDN({
  // 传入importMap
  importMap: {
    "vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.37/vue.esm-browser.prod.min.js"
  }
})

打包后的结果:

【Vite插件】使用importmap添加cdn的一些思考

在网上找一个项目测试一下,成功加载出来了

【Vite插件】使用importmap添加cdn的一些思考

到这一步,已验证这种方式是行得通的

如此便可以将vue替换成cdn,减少了vue的体积

3、使用importmap的方式真的合适吗

先来看一下importmap的兼容性:

【Vite插件】使用importmap添加cdn的一些思考

桌面端除了IE浏览器不支持外,其它浏览器的兼容性还是不错的

移动端就稍微差一些

还要一个比较必要的条件,那就是:第三方包必须要支持浏览器端的ESM标准

这一点其实限制了很多包,像vue就有,而ElementPlus就没有,并且大多数都是没有的

这就导致它的应用面其实不广泛

另外,虽然CDN可以加速模块的加载速度,节约服务器资源,但是它也不无缺点:

  1. 可靠性:CDN依赖于第三方服务,如果CDN服务器不稳定,那么会导致应用出问题
  2. 隐私性:请求第三方资源可能会暴露用户的一些信息

综上,为了节省一点资源,而冒这么大的风险,显然有点不太讨好

为此,最终还是选择本地打包vue等第三方包,只不过把他们从业务模块中剥出来,打包成单独的chunk

【Vite插件】使用importmap添加cdn的一些思考


当然,也不是说就完全放弃了这种方式,假以时日也许会考虑将其应用到生产上

目前将其上传到npm仓库中,作为第一个版本,可以install下来尝尝

后面会继续迭代,例如增加兼容性等

npm地址

github完整代码


转载自:https://juejin.cn/post/7284261686608543800
评论
请登录