1、基础组件
- 首先安装
Vue3.0
包依赖。
pnpm i vue@"3.2.37"
- 接着尝试编写一个简单的 Button 组件
创建src/button/index.ts
目录文件
import { defineComponent, h } from "vue";
export default defineComponent({
name: "SButton",
// template:'<button>MyButton</button>'
render() {
return h("button", null, "MyButton");
},
});
写到这里大家可能有点疑问: 为什么是使用 render 函数,而不是熟悉的 template 语法编写呢?这是因为
Vue3.0
默认的包是不支持模板编译功能的。也就是说, template 语法现在还不能用。在Vue3.0
中编译功能推荐在构建阶段完成,而不是放到浏览器中运行。如果希望在浏览器中的话,可以选择./node_modules/vue/dist/vue.global.js
这个包。
- 在
index.html
中添加一个容器 , 用来展示组件
<div id="app"></div>
给容器添加id
属性,用于后面Vue
实例挂载。
- 在
src/index.ts
中启动Vue
实例
import { createApp } from "vue";
import SButton from "./button";
createApp(SButton).mount("#app");
- 启动项目
pnpm dev
- 浏览器访问项目地址
VITE v3.0.7 ready in 165 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
- 页面并没有显示
button
组件,且控制台报错
Uncaught SyntaxError: Cannot use import statement outside a module (at index.ts:1:1)
这里报错的原因是用了es6
的语法, 浏览器默认将它作为js
解析会出现问题,需要将它作为模块导入,script
标签默认type="text/javascript"
,需要改为type="module"
,更改后的index.html
- 修改
index.html
文件,再次查看
<script src="./src/index.ts" type="module"></script>
- 再次查看浏览器,可以看见
button
组件,但是控制台有以下警告
runtime-core.esm-bundler.js:4952 Feature flags __VUE_OPTIONS_API__, __VUE_PROD_DEVTOOLS__ are not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle.
For more details, see https://link.vuejs.org/feature-flags.
查阅资料——解决方案
- 安装插件
@vitejs/plugin-vue
pnpm i @vitejs/plugin-vue -D
- 创建
vite.config.ts
配置插件import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [ vue(), // VUE插件 ], })
- 再次启动项目
pnpm dev
- 再次查看浏览器控制台
组件正常显示,且控制台此时无警告报错。
2、单文件组件
由于Vue3.0
默认的包是不支持模板编译功能, 这里需要安装vite
的Vue
插件,也就是上面的解决方案步骤。
Vite
默认只能支持TS代码,而Vue
的模板需要在编译阶段转换为TypeScript
代码(渲染函数)才可以运行。Vue
插件不但提供了模板的编译,同时还支持Vue
单文件 (SFC
) 组件的编译。
- 安装插件
pnpm i @vitejs/plugin-vue -D
- 创建
vite.config.ts
配置插件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue(), // VUE插件
],
})
- 引入到
index.ts
中测试
import { createApp } from "vue";
import SFCButton from "./SFCButton.vue";
createApp(SFCButton)
.mount("#app");
此时报错
找不到模块“./SFCButton.vue”或其相应的类型声明。ts(2307)
这是因为Typescript 默认是不支持 .vue
类型的模块的。可以通过添加一个模块的类型定义来解决这个问题。
- 创建
src/shims-vue.d.ts
类型声明文件
declare module "*.vue" {
import { DefineComponent } from "vue";
const component: DefineComponent<{}, {}, any>;
export default component;
}
- 运行项目
pnpm dev
- 浏览器访问地址
VITE v3.0.7 ready in 297 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
此时可以看到按钮组件。
3、JSX
组件
JSX
是一种 Javascript
的语法扩展,最早运用于 React
架构中。JSX
也可以当作一种模板语言使用。虽然有人会质疑利用JSX
语法编写 Vue3
代码是否合理, 比如怀疑 JSX
语法是否兼容 Vue3
的静态提升特性。但是现在很多基于 Vue
的组件库都大量使用 JSX
语法,对于工程化搭建,还是以开发者的使用习惯优先,我们支持了再说。
- 在这里我们通过插件实现扩展
pnpm i @vitejs/plugin-vue-jsx@"2.0.0" -D
- 修改
vite.config.ts
文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
export default defineConfig({
plugins: [
vue(), // VUE插件
vueJsx({}), // JSX 插件
],
})
- 创建
src/JSXButton.tsx
文件
import { defineComponent, h } from "vue";
export default defineComponent({
name: "JSXButton",
render() {
return <button>JSX Button</button>;
},
});
这个时候TS
会报错
(property) button: ElementAttrs<ButtonHTMLAttributes>
找不到名称“React”。ts(2304)
这个提示的意思是不支持 JSX
语法造成的。而不是需要安装 React
。只需要在 tsconfig
中配置一下 jsx
语法支持就行了。
- 在根目录下创建
tsconfig.json
文件
{
"compilerOptions": {
"declaration": true, /* 生成相关的 '.d.ts' 文件。 */
"declarationDir": "./dist/types", /* '.d.ts' 文件输出目录 */
"jsx": "preserve",
},
"include": [
"./**/*.*",
"./src/shims-vue.d.ts"
],
"exclude": [
"node_modules"
],
"esModuleInterop": true,
"allowSyntheticDefaultImports": "true"
}
- 将组件引入
index.ts
中测试
import { createApp } from "vue";
import JSXButton from "./JSXButton";
createApp(JSXButton)
.mount("#app");
- 运行项目
pnpm dev
- 浏览器查看地址
VITE v3.0.7 ready in 466 ms
➜ Local: http://localhost:5173/
➜ Network: use --host to expose
查看浏览器显示的按钮组件是否正常。
4、库文件封装
参考一下 Element 的使用指南。可以看到组件库有两种引入形态:
- 完整引入 :一次性引入全部组件,使用
Vue.use
以Vue
插件的形式引入; - 按需引入 :按需引入,导出单个组件,使用
Vue.component
注册。
import Vue from 'vue'
import Element from 'element-ui'
// 完整引入
Vue.use(Element)
// or
import {
Select,
Button
// ...
} from 'element-ui'
// 按需引入
Vue.component(Select.name, Select)
Vue.component(Button.name, Button)
综上所述,组件库的形态应该是这样的结构:
可以满足以下的要求:
- 默认导出为
Vue
插件; - 每个组件可以单独导出。
首先设计一个入口,包含两个功能:
- 导出全部组件;
- 实现一个 Vue 插件,插件中编写 install 方法,将所有组件安装到 vue 实例中.
新建目录文件:src/entry.ts
import { App } from "vue";
import MyButton from "./button";
import SFCButton from "./SFCButton.vue";
import JSXButton from "./JSXButton";
// 导出单独组件
export { MyButton, SFCButton, JSXButton };
// 编写一个插件,实现一个install方法
export default {
install(app: App): void {
app.component(MyButton.name, MyButton);
app.component(SFCButton.name, SFCButton);
app.component(JSXButton.name, JSXButton);
},
};
默认 Vite 就是可以支持构建,使用 Vite 的 build 命令就可以打包输出。如果导出的是一个库文件的话,还需要配置【导出模块类型】并确定导出的文件名。配置如下:
修改文件 vite.config.ts
const rollupOptions = {
external: ["vue", "vue-router"],
output: {
globals: {
vue: "Vue",
},
},
};
export default defineConfig({
.....
// 添加库模式配置
build: {
rollupOptions,
minify:false,
lib: {
entry: "./src/entry.ts",
name: "SmartyUI",
fileName: "smarty-ui",
// 导出模块格式
formats: ["es", "umd","iife"],
},
},
});
修改包配置(package.json
)添加打包命令
"scripts": {
"build": "vite build"
},
执行命令
pnpm build
控制台打印
vite v3.0.7 building for production...
✓ 6 modules transformed.
dist/smarty-ui.mjs 1.02 KiB / gzip: 0.45 KiB
Entry module "src/entry.ts" is using named and default exports together. Consumers of your bundle will have to use `SmartyUI["default"]` to access the default export, which may not be what you want. Use `output.exports: "named"` to disable this warning
dist/smarty-ui.umd.js 1.56 KiB / gzip: 0.67 KiB
Entry module "src/entry.ts" is using named and default exports together. Consumers of your bundle will have to use `SmartyUI["default"]` to access the default export, which may not be what you want. Use `output.exports: "named"` to disable this warning
dist/smarty-ui.iife.js 1.26 KiB / gzip: 0.54 KiB
生成新的文件目录
dist
|----smart-ui.iife.js
|----smart-ui.mjs
|----smart-ui.umd.js
看到提示说明正常导出了。最后编写一个验证页面,测试一下打包结果是否正确。
验证的过程还是基于Vite。首先测试加载全部组件,引用构建完的 smarty-ui.mjs
文件。
创建目录文件:demo/esm/index.html
测试全量导入
<h1>全量加载组件</h1>
<div id="app"></div>
<script type="module">
import { createApp } from "vue/dist/vue.esm-bundler.js";
import SmartyUI from "../../dist/smarty-ui.mjs";
createApp({
template: `
<SButton/>
<JSXButton/>
<SFCButton/>
`}).use(SmartyUI).mount('#app')
</script>
创建目录文件:demo/esm/button.html
测试按需导入
<h1>按需加载组件</h1>
<div id="app"></div>
<script type="module">
import { createApp } from "vue/dist/vue.esm-bundler.js";
import {
SFCButton,
JSXButton,
MyButton,
} from "../../dist/smarty-ui.mjs";
createApp({
template: `
<SButton/>
<JSXButton/>
<SFCButton/>
`,
})
.component(SFCButton.name, SFCButton)
.component(JSXButton.name, JSXButton)
.component(MyButton.name, MyButton)
.mount("#app");
</script>
执行命令启动项目
pnpm dev
访问全量加载URL: http://localhost:5173/demo/esm/index.html
访问按需加载URL: http://localhost:5173/demo/esm/button.html
查看3种按钮组件是否都加载出来了,最后验证结果。