Vue 是易学易用,性能出色,适用场景丰富的 Web 前端渐进式框架;常用来开发单页面应用。
主流开发方式-编译打包
用脚手架工具 create-vue 可以快速通过 npm create vue@latest
命令 来定制化新建一个 Vite 驱动的 Vue 单页面应用项目。
这是常规的使用 Vue 的方式。当然也可以从 Vite 那边入手。
我们新建一个项目 vue-demo
来试试,选上 Vue-Router 和 Pinia, 其余的不选: 访问 http://localhost:5173/
, 正常打开: 初始化的模板,用上了 Vue-Router,有两个路由, '/'
, '/about'
;那 Pinia 呢?可以看到依赖已经安装了引入了,给了一个 demo 了 我们来用一下 Pinia, 就在about路由组件里面用下吧:
html
<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>
这就是 Vue + Vue-Router + Pinia 全家桶在 打包构建工具 Vite 驱动下的开发方式。 Vite 开发阶段不打包,但会预构建项目的依赖,需要哪个资源会在请求的时候编译,而项目上线则需要打包。
完美对吧!但你有没有注意到,官网除了介绍这种方式,还介绍了 "Using Vue from CDN": 也就是说,可以 HTML 文件里面直接用上 Vue 的对吧?那我还想要 Vue-Router、 Pinia、Axios、 Element-Plus 呢?怎么全部直接用,而不是通过npm install xxx 在需要构建打包的项目里面用?
如何直接吃上 Vue 全家桶
我们将会从一个 HTML 文件开始,用浏览器原生的 JavaScript modules 来引入 Vue 、引入 Vue-Router,、引入 Pinia、引入 Axios, 并且构建一个类似工程化的目录结构,但不需要打包,JS 是 ES modules 语法;而项目的运行,只需要用npx serve -s
在当前项目目录起一个静态文件服务器,然后浏览器打开即可。
HTML 文件引入 Vue
找个空文件夹,我们新建一个 index.html
: 把 Vue 文档代码复制过来:
html
<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
打开看看
没问题。
但是经常写页面的朋友都知道,肯定得拆分组件,不然全写一个页面不好维护,这点官网也给了例子: 照猫画虎,我们拆分一下:
新建 src/app.js
文件,如下内容:
js
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
return { count }
},
template: `<div @click="count++">Count is: {{ count }}</div>`
}
然后在 index.html
引入:
js
<script type="module">
import { createApp, ref } from 'vue'
import App from './src/app.js'
createApp(App).mount('#app')
</script>
刷新下页面看看: Vue 成功引入并使用了。但还有遗憾,就是app.js
"组件"的 template 部分是字符串,没有高亮,不利于区分: 关于这点,官网也说了,如果你使用 VS Code, 那你可以安装插件 es6-string-html
,用 /*html*/实现高亮: 我们来试试看: 至此,我们可以相对舒服地使用 Vue 进行组件开发了。
HTML 文件引入、Vue 集成 Vue-Router
项目如果有不同的页面,就需要 Vue-Router 了, Vue-Router官网同样有网页直接引入的介绍: 我们来试一下,先在 Import Maps 添加 vue-router
的引入: 然后写个使用 Vue-router 的demo: 新建两个路由组件:src/view/home.js
, src/view/about.js
, 在 HTML 文件中引入: src/app.js
作为根组件,放个 RouterLink、RouterView组件: 然后我们刷新下页面,看看是否正常生效: 很遗憾,没有生效,控制台报错了: 意思是声明的vue-router模块,没有导出我们引用到的方法 createRouter
;这说明,Vue-Router打包的默认文件,并不是默认的 ES Modules 方式,我们得找找对应的构建产物文件才行;
这对比Vue的引入,Vue引入的是构建产物中的 "esm-browser" 后缀的文件: 那么斗胆猜测下,Vue-Router同样也有 esm 的构建产物,我们引入下该文件,应该就可以了。
但是怎么知道Vue-Router的构建产物有哪些?难道去翻官方的构建配置吗?不用,我们找个npm项目,然后npm install vue-router
,在 node_mudules/xxx
翻看就知道了。
我们上面正好有个 vue-demo, 使用了Vue-Router。我们看看: 我们改下 Import Maps 里面 vue-router
的映射: 刷新下页面看看: 还是有报错: @vue/devtools-api
我们并没有引入,报了这个错,斗胆猜测是 vue-router 中使用的,该模块应该是属于外部模块,我们看看网络里面响应的文件验证下: 确实如此,那么 Import Maps 也补充下引入这个模块,我们先翻看该模块的 npm 包看看,确定下路径: Import Maps 里面引入: 再刷新下页面试试: 至此,我们成功地在 HTML 文件中引入,在 Vue 中集成了 Vue-Router。
下面我们来看 Pinia 的
但在这之前,我们来整理下现在的目录划分吧。
新建 src/router/index.js
文件,将路由相关的逻辑放到这里: 在index.html
引入 router: 然后type=module
的 script 里面的内容也可以抽离出来到单独的文件里面: 新建 main.js
文件,将内容搬过去并引入: 页面刷新下,正常运行。
HTML 文件引入、Vue 集成 Pinia
有了上面引入 Vue-Router 的经验,我们就知道了,引入其他的库也是相同的套路。我们去之前的脚手架工具生成的项目 vue-demo 的依赖里面翻看一下,Pinia 包的构建产物是如何的,然后在现在的 esm 项目里面引入吧:
我们在项目里面使用一下 Pinia, 在main.js
里面引入 Pinia:
js
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
文件,填入如下内容:
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 }
})
即如下: 之后我们在 src/view/home.js
组件里面使用一下这个 store:
js
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
我们确认一下,在响应的 Pinia 库中确实有对这模块的引入 那么我们也引入一下吧,我们翻看需要的库的文件路径,注意这里的 esm 模块是 .mjs 后缀文件 再刷新看看: 至此,我们就在 HTML 文件中直接引入 Vue, 集成了 Vue-Router、Pinia。
HTML 文件引入 Axios
接下来,我们来看看网络请求库 Axios。
网络请求, 原生的 fetch API 可以胜任,但是对于项目的网络请求,最好有统一的拦截器处理,而Axios已经有了一套可行的方案,所以我项目开发一般会用 Axios。本节不讲Axios封装,只介绍在原生 HTML 文件中直接引入和使用 Axios。
要以 ESM 方式引入 Axios,我们得知道 Axios esm 模块的路径。我们在上述的工程化项目vue-demo中安装和查看路径 我们在 Import Maps 添加引入 我们添加 src/mock/test.json
文件,里面存放JSON 数据,然后用 axios 请求试试看: 我们在 src/view/about.js
组件里面使用一下 Axios 来获取 mock 数据,并且显示到页面上,代码如下:
js
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>`
}
刷新看看: 没有问题,可以正常使用,至于 Axios 如何封装得适合项目,这里就不展开了。
CSS 样式解决方案
但目前为止,我们几乎没有写样式,但这种纯 ESM 项目,我们应该怎么写样式呢?
用打包构建工具的项目,一般都有 CSS 的预构建处理工具,比如 Less, Scss等;但实际开发中,大部分就使用一下嵌套而已;
现在最新的浏览器已经支持 CSS 嵌套了: 还有 CSS 模块化的兼容性也完全没问题: 那么此 ESM 项目我这里给一个建议的方案,读者欢迎评论区留言提供其他方案。
新建 src/style/index.css
文件,键入如下样式:
css
body {
background-color: aqua;
}
在 index.html
文件中引入该样式: 刷新看看是否生效 项目中该怎么进行组件的 CSS 样式隔离呢?这里就建议 采用 ESM 的类名样式方案咯,这里不展开讲,只给一个样式目录参考。建议如下: 将样式放在 src/style
下面,按照组件的目录进行放置,然后在src/style/index.css
引入: 效果如下:
样式中,我使用了CSS模块化语法和嵌套语法,都生效了。
HTML 文件引入、Vue 集成 Element-Plus
最后,我们再引入组件库吧。我这里使用 Element-Plus
官网可以看到也是支持直接引入的,要注意的是得引入其样式 我们在上面工程化项目 vue-demo 里面安装下 Element-Plus 的 npm 包看看 esm 文件的位置(.mjs后缀文件一般就是esm模块): 在 index.html
文件里面引入样式,在 Import Maps 里面引入 element-plus: 然后在 main.js
里把所有 element-plus 组件注册为全局组件并在 src/view/home.js
使用下 Button 组件: 效果如下: 至此,我们在项目中集成了 Element-Plus 组件库了。
总结
我们先按照 Vue 官方文档使用了常规的项目开发方式创建了一个项目。
然后我们提出了一个想法:能否直接在 HTML
文件中使用 Vue 及其全家桶?
答案是可行的,因为几乎所有的库都提供了 ESM 的构建文件,而现今的浏览器也都支持 ESM 模块化了。
我们也探讨和实践了 CSS 模块化 和 CSS 嵌套,用在了 demo 中作为 esm 项目的样式方案。
最后我们在项目中集成了 Element-Plus 组件库。
至此,我们可以点题了:无打包构建,浏览器确实能吃上 Vue 全家桶了。但这并不是说,可以在真实项目中这样使用,兼容性就不说了,还有项目的优化,一般得打包构建中做:比如 Tree Shareing、代码压缩等。但如果是一些小玩具项目,可以试试这么玩。无构建和打包,浏览器跑的代码就是你写的源码了。
本文示例代码地址:gitee.com/GumplinGo/1...