目标
封装一个表单组件,包括 输入框 Input、下拉选择框 Select、查询按钮 Button。
实现
基础框架
因为我们要开发的是一个组件,而不是外部工程,所以我们在 TS + Vue:搭建开发环境 的基础上进行一些改造。
一、webpack:抽离入口文件
从公共配置(webpack.base.config.js)中抽离出入口文件配置,因为开发环境和生产环境需要不同的入口。
-
webpack.dev.config.js
module.exports = { entry: "./src/index.ts", }; -
webpack.pro.config.js
module.exports = { entry: "./src/main.ts", };
二、webpack:修改输出文件名
-
webpack.base.config.js
module.exports = { output: { filename: "employee-query.js", clean: true, }, // ... 其他配置 };
三、webpack: HtmlWebpackPlugin
因为调试时,需要页面运行组件,所以将 HtmlWebpackPlugin 从 base 移放到 dev。
-
webpack.dev.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { // ...其他配置 plugins: [ // ...plugins new HtmlWebpackPlugin({ template: "./src/tpl/index.html", }), ], };
四、webpack:生产环境的配置
把组件打包成 umd 模块,安装 nodeExternals 插件。
nodeExternals:能够排除
node_modules目录中所有模块,还提供一些选项,比如白名单package(whitelist package)。
- 安装
webpack-node-externals
$ npm i -D webpack-node-externals
-
webpack.dev.config.js
const nodeExternals = require("webpack-node-externals"); module.exports = { entry: "./src/main.ts", output: { libraryTarget: "umd", library: "EmployeeQuery", // 库名,在全局环境下被挂载在window下 }, externals: [nodeExternals()], };
五、package.json:Library 的入口
package.json 中的 main 字段指向的是 Library 的入口,通常有 3 个选择:
- 指向源代码入口文件,如
src/index.js; - 指向打包后的开发版本,如
dist/library.js; - 指向打包后的发布版本,如
dist/library.min.js。
这里我们将它指向打包后的开发版本。
- package.json
{
// ...
"main": "./dist/employee-query.js"
}
编写组件
装饰器模式
为什么需要 vue-class-component?
在 typescript 里写 vue 每次都需要写很多额外的形式代码:
而装饰器就是解决这些冗余代码的(实质上并没有减少,只是用一层函数包装了
$ npm i --save vue-property-decorator
components/EmployeeQuery.vue
v-model.trim:自动过滤用户输入的首尾空白字符,x!将从x值域中排除null和undefined
组件包含三个 Prop:name(员工姓名)、selected(部门选中值)、department(部门列表)
<template>
<div class="employee-query">
<input type="text" placeholder="姓名" v-model.trim="tempName" />
<select v-model.number="tempSelected">
<option value="0">部门</option>
<option
v-for="option in department"
:value="option.departmentId"
:key="option.departmentId"
>
{{ option.department }}
</option>
</select>
<button @click="query">查询</button>
</div>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
@Component
export default class EmployeeQuery extends Vue {
@Prop({ type: String, default: "" })
name?: string = "";
@Prop({ type: Number, default: 0 })
selected?: number = 0;
@Prop({ type: Array, default: () => [] })
department?: { department: string; departmentId: number }[];
tempName: string = this.name!;
tempSelected: number = this.selected!;
query() {
this.$emit("query", { name: this.tempName, selected: this.tempSelected });
}
}
</script>
<style scoped>
.employee-query {
display: flex;
}
input,
select {
margin-right: 10px;
}
</style>
开发环境入口(index.ts)
在 index.ts 注册组件,传入数据,处理 query 事件。
-
src/index.ts
import Vue from "vue"; import EmployeeQuery from "./components/EmployeeQuery.vue"; let app = new Vue({ el: ".app", components: { EmployeeQuery }, template: `<employee-query @query="getQuery" :department="department"/>`, data() { return { department: [ { department: "技术部", departmentId: 1 }, { department: "产品部", departmentId: 2 }, { department: "市场部", departmentId: 3 }, { department: "运营部", departmentId: 4 }, ], }; }, methods: { getQuery(param: any) { console.log(param); }, }, });
在 localhost:8080 调试组件。
$ npm start
生产环境入口(main.ts)
我们只是用了一个单文件组件,所以直接导出就可。
-
src/main.ts
import EmployeeQuery from "./components/EmployeeQuery.vue"; export default EmployeeQuery;
build 后,检查是否生成 src/employee-query.js
npm run build
坑儿汇总
experimentalDecorators
❌ ERROR: Experimental support for decorators is a feature that is subject to change in a future release. Set the 'experimentalDecorators' option in your 'tsconfig' or 'jsconfig' to remove this warning.
可以通过设置 tsconfig.json 中的 experimentalDecorators 来删除此警告。
- tsconfig.json
{
"compilerOptions": {
// ...other options
"experimentalDecorators": true /* Enables experimental support for ES7 decorators. */
}
}
DevTools failed to load SourceMap
⚠️ WARNING:DevTools failed to load SourceMap: Could not load content for webpack://vue-employee-query/node_modules/sockjs-client/dist/sockjs.js.map: HTTP error: status code 404, net::ERR_UNKNOWN_URL_SCHEME
webpack 5 中,可以用 devtool 选项控制是否生成,以及如何生成 source map。
- 原 webpack.dev.config.js
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.LoaderOptionsPlugin({
options: {
devtools: "cheap-module-eval-source-map",
},
}),
],
};
- 更改后
module.exports = {
devtool: "eval-cheap-source-map",
};
@Prop
如果不设置默认值,Vue 会报:
⚠️ [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value.
- src/components/EmployeeQuery.vue
// ❌
@Prop(String)
name?: string = "";
@Prop(Number)
selected?: number = 0;
// ✅
@Prop({ type: String, default: "" })
name?: string = "";
@Prop({ type: Number, default: 0 })
selected?: number = 0;