CDD
全称:(Component-Driven Development)
- 自下而上
- 从组件级别开始,到页面级别结束
CDD好处
组件在最大程度被重用;
并行开发;
可视化测试;
处理组件的边界情况
- root 访问根实例【一般情况不推荐这样做】**)
- children(children获取子组件【作为了解】$children是个数组)
- $refs
-
依赖注入 provide / inject
-
- 子组件要访问父组件的title和handle()
-
-
- 先在父组件提供title和handle()
-
-
- 在子组件要使用这两个成员的位置,要通过inject注入进来
- inject: ['title', 'handle']
01-parent.vue
<template>
<div class="parent">
parent
<child></child>
</div>
</template>
<script>
import child from './02-child'
export default {
components: {
child
},
// 子组件要访问父组件的title 和handle()
// 先在父组件提供title 和handle()
// 在子组件要使用这两个成员的位置
// 要通过inject注入进来
// inject: ['title', 'handle']
provide () {
return {
title: this.title,
handle: this.handle
}
},
data () {
return {
title: '父组件 provide'
}
},
methods: {
handle () {
console.log(this.title)
}
}
}
</script>
02-child.vue
<template>
<div class="child">
child<br>
title:{{ title }}<br>
<button @click="handle">获取 title</button>
<button @click="title='xxx'">改变 title</button>
<grandson></grandson>
</div>
</template>
<script>
import grandson from './03-grandson'
export default {
components: {
grandson
},
inject: ['title', 'handle']
}
</script>
03-grandson.vue
<template>
<div class="grandson">
grandson<br>
title:{{ title }}<br>
<button @click="handle">获取 title</button>
<button @click="title='yyy'">改变 title</button>
</div>
</template>
<script>
export default {
inject: ['title', 'handle']
}
</script>
listeners(开发自定义组件的时候会用到)
. $attrs
把父组件中非prop属性绑定到内部组件
$listeners
把父组件中的DOM对象的原生事件绑定到内部组件
快速原型开发
- VueCLI中提供了一个插件可以进行原型快速开发
- 需要先额外安装- -个全局的扩展
- npm install -g @vue/cli-service- global
- npm i @vue/cli-service-global
- 使用vue serve快速查看组件的运行效果
vue serve
- vue serve如果不指定参数默认会在当前目录找以下的入口文件
- main.js、 index.js、 App.vue、app.vue
- 可以指定要加载的组件
- vue serve ./src/login.vue
运行该组件
新建一个app.vue
运行:vue serve即可
快速原型开发-element
安装element
- 初始化package.json
- npm init -y
- 安装ElementUl
- vue add element
- 加载ElementUl,使用Vue.use()安装插件
组件开发
表单组件
步骤条组件开发
steps.css
.lg-steps {
position: relative;
display: flex;
justify-content: space-between;
}
.lg-steps-line {
position: absolute;
height: 2px;
top: 50%;
left: 24px;
right: 24px;
transform: translateY(-50%);
z-index: 1;
background: rgb(223, 231, 239);
}
.lg-step {
border: 2px solid;
border-radius: 50%;
height: 32px;
width: 32px;
display: flex;
justify-content: center;
align-items: center;
font-weight: 700;
z-index: 2;
background-color: white;
box-sizing: border-box;
}
Steps.vue
<template>
<div class="lg-steps">
<div class="lg-steps-line"></div>
<!-- :style="{ color: active >= index (active跟index进行对比)? activeColor(完成之后的样式) : defaultColor(默认样式【或者完成之前的样式】) }" -->
<div
class="lg-step"
v-for="index in count"
:key="index"
:style="{ color: active >= index ? activeColor : defaultColor }"
>
{{ index }}
</div>
</div>
</template>
<script>
import './steps.css'
export default {
name: 'LgSteps',
// 接收父组件传递过来的数据
props: {
// 总共有多少步
count: {
type: Number,
default: 3
},
// 控制当前完成第几步
active: {
type: Number,
default: 0
},
// 完成之后的样式
activeColor: {
type: String,
default: 'red'
},
// 默认的字体颜色
defaultColor: {
type: String,
default: 'green'
}
}
}
</script>
<style>
</style>
Steps-test.vue
<template>
<!-- 使用该组件 -->
<div>
<steps :count="count" :active="active"></steps>
<button @click="next">下一步</button>
</div>
</template>
<script>
import Steps from './Steps.vue'
export default {
components: {
Steps
},
data () {
return {
count: 4,
active: 1
}
},
methods: {
next () {
this.active++
}
}
}
</script>
form表单开发
form->Button.vue
<template>
<div>
<button @click="handleClick"><slot></slot></button>
</div>
</template>
<script>
export default {
name:'LgButton',
methods:{
// evt 事件对象
handleClick(evt){
this.$emit('click',evt)
// 默认会提交表单 要阻止默认行为
evt.preventDefault()
}
}
}
</script>
form->Form.vue
<template>
<!-- 直接form标签 因为渲染的也是form标签 -->
<form>
<slot></slot>
</form>
</template>
<script>
export default {
name: 'LgForm',
// 直接返回一个对象
provide () {
return {
form: this //当前组件对象
}
},
props: {
model: {
type: Object
},
rules: {
type: Object
}
},
methods: {
validate (cb) {
const tasks = this.$children //拿到所有itme组件
.filter(child => child.prop)//child 所有子组件
.map(child => child.validate())
// 查看是否都执行成功
Promise.all(tasks)
.then(() => cb(true))
.catch(() => cb(false))
}
}
}
</script>
<style>
</style>
form->FormItem.vue
<template>
<div>
<!-- 显示提示的文本 -->
<label>{{ 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() {
// 当组件渲染完成之后注册validate事件
this.$on('validate', () => {
this.validate()
})
},
data () {
return {
errMessage: ''
}
},
methods: {
// 验证的方法
validate () {
// 如果没有传递prop这个选项就不做验证
if (!this.prop) return
const value = this.form.model[this.prop]//要验证的数据
const rules = this.form.rules[this.prop]//要验证的规则
const descriptor = { [this.prop]: rules } //要验证的属性名和验证对象
const validator = new AsyncValidator(descriptor)
// { [this.prop]: value } 要验证的属性以及值
// 验证失败的回调函数(errors)
return validator.validate({ [this.prop]: value }, errors => {
if (errors) {
this.errMessage = errors[0].message //验证失败的信息
} else {
this.errMessage = '' //验证失败信息进行清空
}
})
}
}
}
</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' //默认值是text
}
},
methods: {
// evt 事件对象
handleInput (evt) {
// 触发父组件自定义事件
// evt.target.value 文本框的值
this.$emit('input', evt.target.value)
const findParent = parent => {
// 查看父组件是否存在
while (parent) {
if (parent.$options.name === 'LgFormItem') {
break
} else {
// 如果找不到父组件 就继续找
parent = parent.$parent
}
}
return parent
}
//$parent触发当前组件的父组件
const parent = findParent(this.$parent)
if (parent) {
parent.$emit('validate') //如果找到了父组件中的父组件 然后触发validate
}
}
}
}
</script>
<style>
</style>
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>
storybook安装
自动安装
npx -p @storybook/cli init --type vue(vue 强制使用vue的方式)
npx -p @storybook/cli sb init --type vue(跟上面是一样的)
yarn add vue
vue yarn add vue-loader vue-template-compiler --dev
Lerna
Lerna 是一个工具,它优化了使用 git 和 npm 管理多包仓库的工作流工具
用于管理有多个包的JavaScript项目
他可以一键把代码提交到Git和npm仓库
Lerna的使用
全局安装
- yarn global add lerna
- 初始化
- lerna init
- 发布
- lerna publish
Lerna Script
- lerna create [loc]
- 创建一个包,name 包名,loc 位置可选;
- lerna add [@version] [--dev] [--exact]
- 增加本地或远程 package 做为当前项目 packages 里面的依赖;--exact 安装准确版本;
- lerna bootstrap
- 把所有包的依赖安装到根 node_modules;
- lerna list
- 列出所有的包,如果与你文件夹里面的不符,进入那个包运行 yarn init -y;
- lerna run
- 运行所有包里面的有这个 script 的命令;
- lerna exec
- 运行任意命令在每个包;
- lerna link
- 项目包建立软链,类似 npm link:
- lerna clean
- 删除所有包的 node_modules 目录;
- lerna changed
- 列出下次发版 lerna publish 要更新的包;
- lerna publish
- 会打 tag,上传 git,上传 npm;
- yarn workspaces run del
- 运行所有文件中的 del 命令;