注册
web

无构建和打包,浏览器直接吃上Vue全家桶?

Vue 是易学易用,性能出色,适用场景丰富的 Web 前端渐进式框架;常用来开发单页面应用。


主流开发方式-编译打包


用脚手架工具 create-vue 可以快速通过 npm create vue@latest命令 来定制化新建一个 Vite 驱动的 Vue 单页面应用项目。


image.png
这是常规的使用 Vue 的方式。当然也可以从 Vite 那边入手。


我们新建一个项目 vue-demo来试试,选上 Vue-Router 和 Pinia, 其余的不选:
image.png
访问 http://localhost:5173/, 正常打开:
image.png
初始化的模板,用上了 Vue-Router,有两个路由, '/', '/about';那 Pinia 呢?可以看到依赖已经安装了引入了,给了一个 demo 了
image.png
我们来用一下 Pinia, 就在about路由组件里面用下吧:
image.png


<script setup>
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
const { count, doubleCount } = storeToRefs(store)
const { increment } = store
</script>

<template>
<div class="about">
<h1>{{ count }}</h1>
<h1>{{ doubleCount }}</h1>
<button @click="increment">+1</button>
</div>
</template>

<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

image.png
录屏2024-07-31 23.29.34.gif
这就是 Vue + Vue-Router + Pinia 全家桶在 打包构建工具 Vite 驱动下的开发方式。
Vite 开发阶段不打包,但会预构建项目的依赖,需要哪个资源会在请求的时候编译,而项目上线则需要打包。


完美对吧!但你有没有注意到,官网除了介绍这种方式,还介绍了 “Using Vue from CDN”:
image.png
image.png
image.png
也就是说,可以 HTML 文件里面直接用上 Vue 的对吧?那我还想要 Vue-Router、 Pinia、Axios、 Element-Plus 呢?怎么全部直接用,而不是通过npm install xxx 在需要构建打包的项目里面用?


allwant.gif


如何直接吃上 Vue 全家桶


我们将会从一个 HTML 文件开始,用浏览器原生的 JavaScript modules 来引入 Vue 、引入 Vue-Router,、引入 Pinia、引入 Axios, 并且构建一个类似工程化的目录结构,但不需要打包,JS 是 ES modules 语法;而项目的运行,只需要用npx serve -s在当前项目目录起一个静态文件服务器,然后浏览器打开即可。


HTML 文件引入 Vue


找个空文件夹,我们新建一个 index.html:
image.png
把 Vue 文档代码复制过来:


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

<div id="app">{{ message }}</div>

<script type="module">
import { createApp, ref } from 'vue'

createApp({
setup() {
const message = ref('Hello Vue!')
return {
message
}
}
}).mount('#app')
</script>

当前目录下执行下npx serve -s打开看看
image.png
image.png


没问题。


但是经常写页面的朋友都知道,肯定得拆分组件,不然全写一个页面不好维护,这点官网也给了例子:
image.png
照猫画虎,我们拆分一下:


新建 src/app.js文件,如下内容:


import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
},
template: `<div @click="count++">Count is: {{ count }}</div>`
}

然后在 index.html引入:


<script type="module">
import { createApp, ref } from 'vue'
import App from './src/app.js'

createApp(App).mount('#app')
</script>

image.png
刷新下页面看看:
录屏2024-08-01 23.21.39.gif
Vue 成功引入并使用了。但还有遗憾,就是app.js"组件"的 template 部分是字符串,没有高亮,不利于区分:
image.png
关于这点,官网也说了,如果你使用 VS Code, 那你可以安装插件 es6-string-html,用 /*html*/实现高亮:
image.png
image.png
我们来试试看:
录屏2024-08-01 23.33.43.gif
至此,我们可以相对舒服地使用 Vue 进行组件开发了。


HTML 文件引入、Vue 集成 Vue-Router


项目如果有不同的页面,就需要 Vue-Router 了, Vue-Router官网同样有网页直接引入的介绍:
image.png
我们来试一下,先在 Import Maps 添加 vue-router 的引入:
image.png
然后写个使用 Vue-Router 的demo: 新建两个路由组件:src/view/home.js, src/view/about.js, 在 HTML 文件中引入:
image.png
src/app.js作为根组件,放个 RouterLink、RouterView 组件:
image.png
然后我们刷新下页面,看看是否正常生效:
录屏2024-08-03 18.23.45.gif
很遗憾,没有生效,控制台报错了:
image.png
意思是声明的 vue-router 模块,没有导出我们引用到的方法 createRouter;这说明,Vue-Router 打包的默认文件,并不是默认的 ES Modules 方式,我们得找找对应的构建产物文件才行;


这对比 Vue 的引入,Vue 引入的是构建产物中的 “esm-browser” 后缀的文件:
image.png
那么斗胆猜测下,Vue-Router 同样也有 esm 的构建产物,我们引入下该文件,应该就可以了。


但是怎么知道 Vue-Router 的构建产物有哪些?难道去翻官方的构建配置吗?不用,我们找个 npm 项目,然后npm install vue-router,在 node_mudules/xxx翻看就知道了。


我们上面正好有个 vue-demo, 使用了 Vue-Router。我们看看:
image.png
我们改下 Import Maps 里面 vue-router 的映射:
image.png
刷新下页面看看:
录屏2024-08-03 18.43.44.gif
还是有报错:
image.png
@vue/devtools-api我们并没有引入,报了这个错,斗胆猜测是 vue-router 中使用的,该模块应该是属于外部模块,我们看看网络里面响应的文件验证下:
image.png
确实如此,那么 Import Maps 也补充下引入这个模块,我们先翻看该模块的 npm 包看看,确定下路径:
image.png
Import Maps 里面引入:
image.png
再刷新下页面试试:
录屏2024-08-03 18.55.41.gif
至此,我们成功地在 HTML 文件中引入,在 Vue 中集成了 Vue-Router。


下面我们来看 Pinia 的


但在这之前,我们来整理下现在的目录划分吧。


新建 src/router/index.js 文件,将路由相关的逻辑放到这里:
image.png
index.html引入 router:
image.png
然后type=module 的 script 里面的内容也可以抽离出来到单独的文件里面:
image.png
新建 main.js 文件,将内容搬过去并引入:
image.png
页面刷新下,正常运行。


HTML 文件引入、Vue 集成 Pinia


有了上面引入 Vue-Router 的经验,我们就知道了,引入其他的库也是相同的套路。我们去之前的脚手架工具生成的项目 vue-demo 的依赖里面翻看一下,Pinia 包的构建产物是如何的,然后在现在的 esm 项目里面引入吧:


image.png
image.png
我们在项目里面使用一下 Pinia, 在main.js里面引入 Pinia:


import { createApp, ref } from 'vue'
import App from './src/app.js'
import router from './src/router/index.js'
import { createPinia } from 'pinia'

const app = createApp(App)
app.use(createPinia())
app.use(router)
.mount('#app')

新建 src/stores/useCounterStore.js文件,填入如下内容:


import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export default defineStore('counter', () => {

const count = ref(0)
const doubleCount = computed(() => count.value * 2)
const increment = () => {
count.value++
}
return { count, doubleCount, increment }
})

即如下:
image.png
之后我们在 src/view/home.js组件里面使用一下这个 store:


import useCounterStore from "../stores/useCounterStore.js"
import { storeToRefs } from 'pinia'

export default {
setup() {
const store = useCounterStore()
const { count, doubleCount } = storeToRefs(store)
const { increment } = store
return { count, doubleCount, increment }
},
template: /*html*/`<div>
<h1>Home</h1>
<p>{{ count }}</p>
<p>{{ doubleCount }}</p>
<button @click="increment">+1</button>
</div>`

}

我们刷新页面看看,报错了, 缺了一个模块 vue-demi
image.png
我们确认一下,在响应的 Pinia 库中确实有对这模块的引入
image.png
那么我们也引入一下吧,我们翻看需要的库的文件路径,注意这里的 esm 模块是 .mjs 后缀文件
image.png
image.png
再刷新看看:
录屏2024-08-04 15.42.18.gif
至此,我们就在 HTML 文件中直接引入 Vue, 集成了 Vue-Router、Pinia。


HTML 文件引入 Axios


接下来,我们来看看网络请求库 Axios


网络请求, 原生的 fetch API 可以胜任,但是对于项目的网络请求,最好有统一的拦截器处理,而 Axios 已经有了一套可行的方案,所以我项目开发一般会用 Axios。本节不讲Axios封装,只介绍在原生 HTML 文件中直接引入和使用 Axios。


要以 ESM 方式引入 Axios,我们得知道 Axios esm 模块的路径。我们在上述的工程化项目 vue-demo 中安装和查看路径
image.png
我们在 Import Maps 添加引入
image.png
我们添加 src/mock/test.json文件,里面存放JSON 数据,然后用 axios 请求试试看:
image.png
我们在 src/view/about.js组件里面使用一下 Axios 来获取 mock 数据,并且显示到页面上,代码如下:


import axios from 'axios'
import { ref } from 'vue'

export default {
setup() {
const mockData = ref(null)
axios.get('/src/mock/test.json').then(res => {
mockData.value = res.data
})
return { mockData }
},
template: /*html*/`<div>
<h1>About</h1>
<pre>
{{ mockData }}
</pre>
</div>`

}

刷新看看:
录屏2024-08-04 16.20.30.gif
没有问题,可以正常使用,至于 Axios 如何封装得适合项目,这里就不展开了。


CSS 样式解决方案


但目前为止,我们几乎没有写样式,但这种纯 ESM 项目,我们应该怎么写样式呢?


用打包构建工具的项目,一般都有 CSS 的预构建处理工具,比如 Less, Scss等;但实际开发中,大部分就使用一下嵌套而已;


现在最新的浏览器已经支持 CSS 嵌套了:
image.png
还有 CSS 模块化的兼容性也完全没问题:
image.png
那么此 ESM 项目我这里给一个建议的方案,读者欢迎评论区留言提供其他方案。


新建 src/style/index.css 文件,键入如下样式:


body {
background-color: aqua;
}

index.html文件中引入该样式:
image.png
刷新看看是否生效
image.png
项目中该怎么进行组件的 CSS 样式隔离呢?这里就建议 采用 ESM 的类名样式方案咯,这里不展开讲,只给一个样式目录参考。建议如下:
image.png
将样式放在 src/style下面,按照组件的目录进行放置,然后在src/style/index.css引入:
image.png
效果如下:
录屏2024-08-04 16.45.19.gif


样式中,我使用了CSS模块化语法和嵌套语法,都生效了。


HTML 文件引入、Vue 集成 Element-Plus


最后,我们再引入组件库吧。我这里使用 Element-Plus


官网可以看到也是支持直接引入的,要注意的是得引入其样式
image.png
我们在上面工程化项目 vue-demo 里面安装下 Element-Plus 的 npm 包看看 esm 文件的位置(.mjs后缀文件一般就是esm模块):
image.png
index.html 文件里面引入样式,在 Import Maps 里面引入 element-plus:
image.png
然后在 main.js 里把所有 element-plus 组件注册为全局组件并在 src/view/home.js使用下 Button 组件:
image.png
效果如下:
image.png
至此,我们在项目中集成了 Element-Plus 组件库了。


其他优化


以上所有的库,都可以在网络的响应里面,复制到本地,作为本地文件引入,这样加载速度更快,没有网络延迟问题。


总结


我们先按照 Vue 官方文档使用了常规的项目开发方式创建了一个项目。


然后我们提出了一个想法:能否直接在 HTML文件中使用 Vue 及其全家桶?


答案是可行的,因为几乎所有的库都提供了 ESM 的构建文件,而现今的浏览器也都支持 ESM 模块化了。


我们也探讨和实践了 CSS 模块化 和 CSS 嵌套,用在了 demo 中作为 esm 项目的样式方案。


最后我们在项目中集成了 Element-Plus 组件库。


至此,我们可以点题了:无打包构建,浏览器确实能吃上 Vue 全家桶了。但这并不是说,可以在真实项目中这样使用,兼容性就不说了,还有项目的优化,一般得打包构建中做:比如 Tree Shaking、代码压缩等。但如果是一些小玩具项目,可以试试这么玩。无构建和打包,浏览器跑的代码就是你写的源码了。


本文示例代码地址:gitee.com/GumplinGo/1…


作者:小江大浪
来源:juejin.cn/post/7399094428343959552

0 个评论

要回复文章请先登录注册