npm install -g @vue/cli-service-global
、Form-test.vue
<template>
<lg-form class="form" ref="form" :model="user" :rules="rules">
<lg-form-item label="用户名" prop="username">
<!-- <lg-input v-model="user.username"></lg-input> -->
<lg-input :value="user.username" @input="user.username=$event" placeholder="请输入用户名"></lg-input>
</lg-form-item>
<lg-form-item label="密码" prop="password">
<lg-input type="password" v-model="user.password"></lg-input>
</lg-form-item>
<lg-form-item>
<lg-button type="primary" @click="login">登 录</lg-button>
</lg-form-item>
</lg-form>
</template>
<script>
import LgForm from './form/Form'
import LgFormItem from './form/FormItem'
import LgInput from './form/Input'
import LgButton from './form/Button'
export default {
components: {
LgForm,
LgFormItem,
LgInput,
LgButton
},
data () {
return {
user: {
username: '',
password: ''
},
rules: {
username: [
{
required: true,
message: '请输入用户名'
}
],
password: [
{
required: true,
message: '请输入密码'
},
{
min: 6,
max: 12,
message: '请输入6-12位密码'
}
]
}
}
},
methods: {
login () {
console.log('button')
this.$refs.form.validate(valid => {
if (valid) {
alert('验证成功')
} else {
alert('验证失败')
return false
}
})
}
}
}
</script>
<style>
.form {
width: 30%;
margin: 150px auto;
}
</style>
form/Form.vue
<template>
<div>
<form>
<slot></slot>
</form>
</div>
</template>
<script>
export default {
name: 'LgForm',
provide () {
return {
form: this
}
},
props: {
model: {
type: Object
},
rules: {
type: Object
}
},
methods: {
validate (cb) {
const tasks = this.$children
.filter(child => child.prop)
.map(child => child.validate())
Promise.all(tasks)
.then(() => cb(true))
.catch(() => cb(false))
}
}
}
</script>
<style>
</style>
form/FormItem.vue
<template>
<div>
<label :for="prop">{
{
label}}</label>
<div>
<slot></slot>
<p v-if="errMessage">{
{
errMessage}}</p>
</div>
</div>
</template>
<script>
import AsyncValidator from 'async-validator'
export default {
name: 'LgFormItem',
inject: ['form'],
props: {
label: {
type: String
},
prop: {
type: String
}
},
mounted () {
this.$on('validator', () => {
this.validate()
})
},
data () {
return {
errMessage: ''
}
},
methods: {
validate () {
if (!this.prop) return
const value = this.form.model[this.prop]
const rules = this.form.rules[this.prop]
const descriptor = {
[this.props]: rules }
const validator = new AsyncValidator(descriptor)
return validator.validate({
[this.prop]: value }, errors => {
if (errors) {
this.errMessage = errors[0].message
} else {
this.errMessage = ''
}
})
}
}
}
</script>
<style>
</style>
form/Button.vue
<template>
<div>
<button @click="handleClick">
<slot></slot>
</button>
</div>
</template>
<script>
export default {
name: 'LgFButton',
methods: {
handleClick (event) {
this.$emit('click', event)
event.preventDefault()
}
}
}
</script>
<style>
</style>
form/Input.vue
<template>
<div>
<input v-bind="$attrs" :type="type" :value="value" @input="handleInput">
</div>
</template>
<script>
export default {
name: 'LgInput',
inheritAttrs: false,
props: {
value: {
type: String
},
type: {
type: String,
default: 'text'
}
},
methods: {
handleInput (event) {
this.$emit('input', event.target.value)
const findParent = parent => {
while (parent) {
if (parent.$options.name === 'LgFormItem') {
break
}
parent = parent.$parent
}
return parent
}
const parent = findParent(this.$parent)
if (parent) {
parent.$emit('validator')
}
}
}
}
</script>
<style>
</style>
可以把每个组件中重复的依赖安装到根目录下,如果有不同的依赖或者依赖的版本不同,就单独安装到该组件下
"private": true,
"workspaces": [
"./packages/*"
]
yarn add jest @vue/test-utils vue-jest babel-jest -D -W
"scripts": {
"test": "jest"
}
Jest配置文件 jest.config.js
module.exports = {
"testMatch": ["**/__tests__/**/*.[jt]s?(x)"],
"moduleFileExtensions": [
"js",
"json",
// 告诉Jest处理`*.vue`文件
"vue"
],
"transform": {
// 用`vue-jest`处理`*.vue`文件
".*\\.(vue)$": "vue-jest",
// 用`babel-jest`处理js
".*\\.(js)$": "babel-jest"
}
}
Babel配置文件 babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env'
]
]
}
Babel桥接 yarn add babel-core@bridge -D -W
执行yarn test进行测试
生成快照
生成的快照会存到同级目录的__snapshots__/input.test.js.snap文件中
执行yarn test -u可以把快照文件删掉重新生成一个快照
yarn add rollup rollup-plugin-terser [email protected] vue-template-compiler -D -W
import {
terser } from 'rollup-plugin-terser'
import vue from 'rollup-plugin-vue'
module.exports = [
{
input: 'index.js',
output: [
{
file: 'dist/index.js',
format: 'es'
}
],
plugins: [
vue({
css: true,
compileTemplate: true
}),
terser()
]
}
]
然后在每个组件的package.json中配置脚本命令"build": “rollup -c”yarn workspace je-button run build
在根目录下配置统一打包yarn add @rollup/plugin-json rollup-plugin-postcss @rollup/plugin-node-resolve -D -W
根目录创建rollup.config.jsimport fs from 'fs'
import path from 'path'
import json from '@rollup/plugin-json'
import vue from 'rollup-plugin-vue'
import postcss from 'rollup-plugin-postcss'
import {
terser } from 'rollup-plugin-terser'
import {
nodeResolve } from '@rollup/plugin-node-resolve'
const isDev = process.env.NODE_ENV !== 'production'
// 公共插件配置
const plugins = [
vue({
css: true,
compileTemplate: true
}),
json(),
nodeResolve(),
postcss({
// 把css插入到style中
// inject: true,
// 把css放到和js同一级目录
extract: true
})
]
// 如果不是开发环境,开启压缩
isDev || plugins.push(terser())
// pacakges 文件夹路径
const root = path.resolve(__dirname, 'packages')
module.exports = fs.readdirSync(root)
// 过滤,只保留文件夹
.filter(item => fs.statSync(path.resolve(root, item)).isDirectory())
// 为每一个文件夹创建对应额配置
.map(item => {
const pkg = require(path.resolve(root, item, 'package.json'))
return {
input: path.resolve(root, item, 'index.js'),
output: [
{
exports: 'auto',
file: path.resolve(root, item, pkg.main),// 读取package.json中的main属性
format: 'cjs'
},
{
exports: 'auto',
file: path.resolve(root, item, pkg.module), // 读取package.json中的module属性
format: 'es'
}
],
plugins: plugins
}
})
在package.json中配置脚本命令"build": “rollup -c” "main": "dist/cjs/index.js",
"module": "dist/es/index.js",
执行yarn buildyarn add cross-env -D -W
修改package.json中的打包命令 "build:prod": "cross-env NODE_ENV=production rollup -c",
"build:dev": "cross-env NODE_ENV=development rollup -c"
执行yarn build:prod生成的代码是压缩过的yarn add rimraf -D -W