文章目录
- 一、介绍
- 二、处理组件边界问题
- 二、attrs-listeners
- 三、快速原型开发
- 四、快速原型开发-ElementUI
- 五、组件开发-步骤条组件
- 六、组件开发-表单组件
- 七、Monorepo
- 八、Storybook
- 九、yarn workspaces
- 十、Lerna
- 十一、Vue组件的单元测试
- 十二、Rollup
- 十三、设置环境变量
- 十四、清理
- 十五、基于模板生成组件基本结构
- 十六、发布
一、介绍
- 处理组件的边界情况:关于组件的知识点
- vue-cli 中提供的快速原型开发:作用是方便我们进行组件开发,并能单独运行单文件组件
- 组件开发:步骤条、表单组件
- 开发组件的最佳方式 Storybook:隔离开发组件,方便管理组件和组件测试
- Monorepo:项目的一种组织方式,有利于管理组件库的项目结构
- Plop:为了快速开发组件,使用 plop 基于模板生成组件的基本结构
- Lerna + yarn workspaces 的工作方式:方便管理包的依赖以及提交发布所有组件
- 组件测试:使用 js 对组件进行测试
- Rollup 打包
二、处理组件边界问题
- $root.data访问到数据并修改,小型项目使用较方便,状态过多则难以维护
//main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
data: {
title: '根实例 - Root'
},
methods: {
handle () {
console.log(this.title)
}
}
}).$mount('#app')
// root.vue
<template>
<div>
<!--
小型应用中可以在 vue 根实例里存储共享数据
组件中可以通过 $root 访问根实例
-->
$root.title:{{ $root.title }}
<br>
<button @click="$root.handle">获取 title</button>
<button @click="$root.title = 'Hello $root'">改变 title</button>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
- $parent.data获取到父组件的数据并修改,同样可在子组件的子组件中通过parent.data获取到父组件数据并修改,而props是不允许修改的
此法嵌套过多,难以维护
// parent.vue
<template>
<div class="parent">
parent
<child></child>
</div>
</template>
<script>
import child from './02-child'
export default {
components: {
child
},
data () {
return {
title: '获取父组件实例'
}
},
methods: {
handle () {
console.log(this.title)
}
}
}
</script>
<style>
.parent {
border: palegreen 1px solid;
}
</style>
// child.vue
<template>
<div class="child">
child<br>
$parent.title:{{ $parent.title }}<br>
<button @click="$parent.handle">获取 $parent.title</button>
<button @click="$parent.title = 'Hello $parent.title'">改变 $parent.title</button>
<grandson></grandson>
</div>
</template>
<script>
import grandson from './03-grandson'
export default {
components: {
grandson
}
}
</script>
<style>
.child {
border:paleturquoise 1px solid;
}
</style>
// grandson.vue
<template>
<div class="grandson">
grandson<br>
$parent.$parent.title:{{ $parent.$parent.title }}<br>
<button @click="$parent.$parent.handle">获取 $parent.$parent.title</button>
<button @click="$parent.$parent.title = 'Hello $parent.$parent.title'">改变 $parent.$parent.title</button>
</div>
</template>
<script>
export default {
}
</script>
<style>
.grandson {
border:navajowhite 1px solid;
}
</style>
- $children是一个数组,可通过$children[0].data获取到第一个子组件数据并修改,此法不灵活,需用索引,一般用于获取所有子组件的数据
// PARENT.VUE
<template>
<div>
<children1></children1>
<children2></children2>
<button @click="getChildren">获取子组件</button>
</div>
</template>
<script>
import children1 from './02-children1'
import children2 from './03-children2'
export default {
components: {
children1,
children2
},
methods: {
getChildren () {
console.log(this.$children)
console.log(this.$children[0].title)
console.log(this.$children[1].title)
this.$children[0].handle()
this.$children[1].handle()
}
}
}
</script>
<style>
</style>
// children1.vue
<template>
<div>children1</div>
</template>
<script>
export default {
data () {
return {
title: 'children1 获取子组件 - title'
}
},
methods: {
handle () {
console.log(this.title)
}
}
}
</script>
<style>
</style>
// children2.vue
<template>
<div>children2</div>
</template>
<script>
export default {
data () {
return {
title: 'children2 获取子组件 - title'
}
},
methods: {
handle () {
console.log(this.title)
}
}
}
</script>
<style>
</style>
- $refs.***操作DOM,放在子组件上可获取子组件定义的值及方法
// parent.vue
<template>
<div>
<myinput ref="mytxt"></myinput>
<button @click="focus">获取焦点</button>
</div>
</template>
<script>
import myinput from './02-myinput'
export default {
components: {
myinput
},
methods: {
focus () {
this.$refs.mytxt.focus()
this.$refs.mytxt.value = 'hello'
}
}
// mounted () {
// this.$refs.mytxt.focus()
// }
}
</script>
<style>
</style>
// myinput.vue
<template>
<div>
<input v-model="value" type="text" ref="txt">
</div>
</template>
<script>
export default {
data () {
return {
value: 'default'
}
},
methods: {
focus () {
this.$refs.txt.focus()
}
}
}
</script>
<style>
</style>
- $inject:依赖注入 获取父组件中的成员,先在父组件中通过provide函数提供依赖的值,再在子组件中通过inject获取到值,但通过inject注入的值无法被修改
// PARENT.VUE
<template>
<div class="parent">
parent
<child></child>
</div>
</template>
<script>
import child from './02-child'
export default {
components: {
child
},
provide () {
return {
title: this.title,
handle: this.handle
}
},
data () {
return {
title: '父组件 provide'
}
},
methods: {
handle () {
console.log(this.title)
}
}
}
</script>
<style>
.parent {
border: palegreen 1px solid;
}
</style>
// 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>
<style>
.child {
border:paleturquoise 1px solid;
}
</style>
// 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>
<style>
.grandson {
border:navajowhite 1px solid;
}
</style>
二、attrs-listeners
$attrs把父组件中非prop属性绑定到内部组件
attrs",这样属性即可加到该元素上了,同理,若给该元素加上v-on="emit 适用用dom元素本身的事件
// parent.vue
<template>
<div>
<!-- <myinput
required
placeholder="Enter your username"
class="theme-dark"
data-test="test">
</myinput> -->
<myinput
required
placeholder="Enter your username"
class="theme-dark"
@focus="onFocus"
@input="onInput"
data-test="test">
</myinput>
<button @click="handle">按钮</button>
</div>
</template>
<script>
import myinput from './02-myinput'
export default {
components: {
myinput
},
methods: {
handle () {
console.log(this.value)
},
onFocus (e) {
console.log(e)
},
onInput (e) {
console.log(e.target.value)
}
}
}
</script>
<style>
</style>
// myinput.vue
<template>
<!--
1. 从父组件传给自定义子组件的属性,如果没有 prop 接收
会自动设置到子组件内部的最外层标签上
如果是 class 和 style 的话,会合并最外层标签的 class 和 style
-->
<!-- <input type="text" class="form-control" :placeholder="placeholder"> -->
<!--
2. 如果子组件中不想继承父组件传入的非 prop 属性,可以使用 inheritAttrs 禁用继承
然后通过 v-bind="$attrs" 把外部传入的非 prop 属性设置给希望的标签上
但是这不会改变 class 和 style
-->
<!-- <div>
<input type="text" v-bind="$attrs" class="form-control">
</div> -->
<!--
3. 注册事件
-->
<!-- <div>
<input
type="text"
v-bind="$attrs"
class="form-control"
@focus="$emit('focus', $event)"
@input="$emit('input', $event)"
>
</div> -->
<!--
4. $listeners
-->
<div>
<input
type="text"
v-bind="$attrs"
class="form-control"
v-on="$listeners"
>
</div>
</template>
<script>
export default {
// props: ['placeholder', 'style', 'class'] // 'style', 'class'是保留属性 设置会报错
// props: ['placeholder']
inheritAttrs: false
}
</script>
<style>
</style>
三、快速原型开发
VueCli提供了插件可以进行原型快速开发,全局安装@vue/cli-service-global
使用vue serve快速查看组件的运行效果,
- vue serve 如果不指定参数默认会在当前目录找main.js、index.js、APP.vue、app.vue作为入口文件
- 也可以指定要加载的组件 后面跟上路径
- vue serve ./src/login.vue
四、快速原型开发-ElementUI
安装 ElementUI
- 初始化package.json
- npm init -y
- 安装 ElementUI
- vue add element 除了安装elementUI以外 还会安装babel以及所依赖的一些插件,还会在package.json中生成必要的配置
- 加载elementUI,使用vue.use()安装插件
new Vue({…})
五、组件开发-步骤条组件
分类:第三方组件、基础组件、业务组件
// STEPS.VUE
<template>
<div class="lg-steps">
<div class="lg-steps-line"></div>
<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: 0
}
},
methods: {
next () {
this.active++
}
}
}
</script>
<style>
</style>
六、组件开发-表单组件
整体结构:Form、FormItem、Input、Button四类
1、各组件内部写上slot,调用时可在标签内添加内容,根据示例在props中添加相关属性
2、嵌套问题:因为stFormItem必须是stForm的子元素,所以stForm和stFormItem中可采用依赖注入方式,获取到stForm中的model和rules的值,从而完成表单验证;stInput既有可能是stFormItem的子元素,也可以单独使用,因此不适合依赖注入的方法获取父组件的值,可以使用while方法,获取到他的父元素,如果父元素有name为stInputItem的就获取该父组件的label和prop
<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 (evt) {
this.$emit('input', evt.target.value)
const findParent = parent => {
while (parent) {
if (parent.$options.name === 'LgFormItem') {
break
} else {
parent = parent.$parent
}
}
return parent
}
const parent = findParent(this.$parent)
if (parent) {
parent.$emit('validate')
}
}
}
}
</script>
<style>
</style>
3、验证问题:安装async-validator,通过validator调用rules中的验证规则
// 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() {
this.$on('validate', () => {
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.prop]: 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>
4、stForm中添加methods,默认stFormItem是stForm的第一级子组件,用this.$children获取所有子组件并找出有prop的子级,然后遍历,调用子组件的validate方法
...
methods: {
login () {
console.log('button')
this.$refs.form.validate(valid => {
if (valid) {
alert('验证成功')
} else {
alert('验证失败')
return false
}
})
}
}
...
// FORM.VUE
<template>
<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
.filter(child => child.prop)
.map(child => child.validate())
Promise.all(tasks)
.then(() => cb(true))
.catch(() => cb(false))
}
}
}
</script>
<style>
</style>
七、Monorepo
主要用于管理组件
两种项目的组织方式
Multirepo —— 每一个包对应一个项目(multiple repositories)
Monorepo —— 一个项目仓库管理多个模块/包
一般在项目中每个模块都放在packages文件夹中,每个模块独立一个文件夹,目录结构如下
其中index.js是入口文件,使用时可用Vue.use进行引入
import Button from './src/button.vue'
// 别人使用时可以直接使用Vue.use来注册插件
Button.install = Vue => {
// 注册一个全局的组件,并写上组件的名字
Vue.component(Button.name, Button)
}
export default Button
八、Storybook
可视化的组件展示平台
- 在隔离的开发环境中,以交互式方式展示组件
- 独立开发组件
- 支持多种框架,如Vue、React、Angular等
安装方法:
- npx -p @storybook/cli sb init --type vue
- yarn add vue
- yarn add vue-loader vue-template-compiler --dev
启动 yarn storybook
打包生成静态网站 yarn build-storybook
module.exports = {
"stories": [
"../stories/**/*.stories.mdx", // **匹配任意层次的文件夹
"../stories/**/*.stories.@(js|jsx|ts|tsx)"
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials"
]
}
.storybook/main.js相当与是storybook的配置文件,里面设置了stories的路径
storybook就是stories的集合,stories就是用来创建界面上要呈现的内容
addons是插件
使用:
路径中添加文件**.stories.js
.stories/main.js中修改stories文件的路径
module.exports = {
// 将来所有的story都存储在以.stories.js结尾的文件里面
stories: ['../packages/**/*.stories.js'],
addons: ['@storybook/addon-actions', '@storybook/addon-links'],
};
yarn storybook运行
cd packages
cd formitem
yarn add async-validator
packages/form/stories/form.stories.js
import LgForm from '../'
import LgFormItem from '../../formitem'
import LgInput from '../../input'
import LgButton from '../../button'
export default {
title: 'LgForm',
component: LgForm
}
export const Login = () => ({
components: { LgForm, LgFormItem, LgInput, LgButton },
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>
`,
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
}
})
}
}
})
九、yarn workspaces
项目依赖
npm 不支持workspaces的
多个模块中各自有依赖的插件,可以通过yarn workspaces进行统一管理
开启yarn的工作区:
- 项目根目录的package.json中加入
- “private”: true, // 工作区的根目录一般是脚手架不需要发布,这里防止意外把内容暴露出去
- “workspaces”: [“packages/*”] // 设置工作去的子目录,使用 * 指定packages目录下的任意包
yarn workspaces使用
- 给工作区根目录安装开发依赖
- yarn add jest -D -W jest是facebook出的单元测试工具
- 给指定的工作区安装依赖
- yarn workspace lg-button add loadsh@4 这里的包名是我们在package.json中设置的
- 给所有的工作区安装依赖
- yarn install
可使用yarn add ***给所有文件安装依赖,也可通过yarn workspace name add ***给单个名为name的组件安装依赖,这样可能造成各包内有重复的依赖组件,因此可以把packages中的node_modules删除,再在根目录通过yarn install,这样即可将多个包都需要的依赖安装到根目录,单个包需要的装到当前包
Monorepo的项目结构一般都会配合yarn workspaces来管理包的依赖。vue3和react的package.json都开启了workspaces目的就是为了方便管理依赖
十、Lerna
lerna是babel自己用来维护自己的Monorepo 并开源出的一个项目
- lerna是一个优化使用 git 和 npm 管理多包仓库的工作流工具
- 用于管理具有多个包的javascript项目
- 它可以一键把代码提交到git和npm仓库
也可以用来管理包的依赖,可以选择是使用npm还是yarn来管理依赖,需要单独配置,如果使用yarn的话也可以开启yarn的workspaces。一般会把lerna和yarn workspaces结合使用
用来管理多个包的javascript项目,可以一键把代码提交到git和npm仓库,一般用workspaces管理包的依赖,用Lerna发布包
安装使用
yarn add lerna -g
lerna init —— 初始化并执行git init
如果当前项目没有被git管理的话,它会进行git的初始化,在项目根目录创建一个lerna.json的配置文件(记录当前项目初始化的版本,以及所要管理的包的路径),在package.json中添加开发依赖
lerna publish —— 登录npm,发布到npm并传到git上
npm whoami 查看登录npm网站的用户名
npm config get registry 查看当前的镜像源
yarn lerna 执行lerna publish
十一、Vue组件的单元测试
单元测试就是对一个函数的输入和输出进行测试,使用断言的方式,根据输入判断实际的输出与预测的输出是否相同
组件单元测试的好处
参考:https://vue-test-utils.vuejs.org/zh/guides/#%E8%B5%B7%E6%AD%A5
提供描述组件行为的文档
节省手动测试的时间
减少研发新特性时产生的bug
改进设计
促进重构
安装依赖
Vue Test Utils vue官方提供的组件单元测试的官方库
Jest 单元测试框架,与vue结合比较方便配置最少,但是它并不支持但文件组件。所以需要一个预处理器,把vue的单文件组件编译之后的结果也就是js代码交给jest处理。vue官方提供了一个为jest服务的预处理器vue-jest
vue-jest 支持但文件组件的大多数功能,测试文件中会使用到esmodule语法和一些es新特性的语法。这时需要babel-jest插件对测试代码进行降级处理
babel-jest
yarn add jest @vue/test-utils vue-jest babel-jest -D -W
配置
根目录下的package.json
// package.json
"scripts": {
"test": "jest"
...
}
根目录下的 jest.config.js
// 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.config.js
module.exports = {
presets: [
[
'@babel/preset-env'
]
]
}
最后,当运行时,会提示找不到 babel。
原因:vue-test依赖的是babel6,当前是babel7
Babel 桥接 yarn add babel-core@bridge -D -W
-D是开发依赖 -W是安装在工作区的根目录中,不加会报错
Vue组件的单元测试
基本API
JEST 参考 https://www.jestjs.cn/
// packages/input/__tests__/input.test.js
import input from '../src/input.vue'
import { mount } from '@vue/test-utils' // 需要它里面提供的API挂载组件
// jest不需要导入,因为测试文件是被jest加载执行的
// describe 创建一个代码块,把input的相关测试都放到这个代码块中
// 测试是否包含
describe('lg-input', () => {
test('input-text', () => {
const wrapper = mount(input) // 将组件进行挂载
// 获取到这个组件所生成的html标签,判断是否包含...
expect(wrapper.html()).toContain('input type="text"')
})
// 设置type为password,同时测试
test('input-password', () => {
const wrapper = mount(input, {
propsData: { // 设置props
type: 'password'
}
})
expect(wrapper.html()).toContain('input type="password"')
})
// 测试value是否为admin
test('input-password', () => {
const wrapper = mount(input, {
propsData: {
type: 'password',
value: 'admin'
}
})
// wrapper.props 该方法能够获取生成的那个组件的props对象,如果传递value,它获取的是生成组件的props的value属性的值
expect(wrapper.props('value')).toBe('admin')
})
// 快照 是jest中提供的一个方法
// 第一次运行:将文本内容存到当前文件同目录下特定的文件中./__snapshots__
// 第二次运行:与第一次对比,相同则成功
// yarn test -u命令, 将快照删除重新生成
test('input-snapshot', () => {
const wrapper = mount(input, {
propsData: {
type: 'text',
value: 'admin'
}
})
// 把挂载的这个组件对应的dom对象给它拍个快照,第一次调用时会放到一个特定的文本文件中去记录起来
expect(wrapper.vm.$el).toMatchSnapshot()
})
})
十二、Rollup
Rollup 是一个模块打包器
Rollup 支持 Tree-shaking
打包的结果比Webpack小
开发框架/组件库的时候使用 Rollup 更合适
打包
安装依赖
Rollup
rollup-plugin-teser 压缩
rollup-plugin-vue@5.1.9 把vue2单文件组件编译成js
vue-template-compiler
在 input 目录中创建 rollup.config.js
import { terser } from 'rollup-plugin-terser'
import vue from 'rollup-plugin-vue'
module.exports = [
{
input: 'index.js',
output: [
{
file: 'dist/index.js',
format: 'es'//配置模块打包的方式 es表示es6,cjs表示Commonjs
}
],
plugins: [
vue({
css: true, //把单文件组件中的样式插入到html中的style标签里
compileTemplate: true // 把组件转换成render函数
}),
terser() // 对代码进行压缩
]
}
]
找到 input 包中的 package.json 的 scripts 配置
进入rollup这个命令的时候 默认去加载rollup.config.js这个文件
"build": "rollup -c"
运行打包/单个包
yarn workspace lg-input run build
打包多个包
除上述几个依赖外,还需
yarn add @rollup/plugin-json rollup-plugin-postcss @rollup/plugin-node-resolve -D -W
@rollup/plugin-json:让rollup可以把json文件作为模块加载,配置文件中会用到
rollup-plugin-postcss :
@rollup/plugin-node-resolve:打包的过程中把依赖的第三方包打包进来
项目根目录创建 rollup.config.js 这个配置文件本质上是一个node程序,作用是为packages目录下所有包生成rollup配置
import 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({
// Dynamically inject css as a <style> tag
css: true,
// Explicitly convert template to render function
compileTemplate: true
}),
json(),
nodeResolve(),
postcss({
// 把 css 插入到 style 中
// inject: true,
// 把 css 放到和js同一目录
extract: true
})
]
// 如果是开发环境,把压缩插件添加到插件数组中开启压缩
isDev || plugins.push(terser())
// packages 文件夹路径 。作为处理的跟路径
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')) // 找到每一个目录中的package.json文件
return { // 每个包对应的rollup的配置
input: path.resolve(root, item, 'index.js'),
output: [
{
exports: 'auto',
file: path.resolve(root, item, pkg.main),
format: 'cjs'
},
{
exports: 'auto',
file: path.join(root, item, pkg.module),
format: 'es'
},
],
plugins: plugins // 配置插件,当map结束之后,它返回一个数组,数组中每一个对象就代表一个包的配置
}
})
在每一个包中设置 package.json 中的 main 和 module 字段
"main": "dist/cjs/index.js",
"module": "dist/es/index.js"
根目录的 package.json 中配置 scripts
"build": "rollup -c" **
十三、设置环境变量
删除项目中没有用的内容
stories 初始化storybook时生成的
storybook-static storybook打包时生成的
- yarn add cross-env -D -W
- 根目录的 package.json中scripts build命令修改为
“build:prod”: “cross-env NODE_ENV=production rollup -c”,
“build:dev”: “cross-env NODE_ENV=development rollup -c” - 运行时则会发现build:prod模式下代码时压缩的
十四、清理
删除指定目录
yarn add rimraf -D -W
每个包都配置
"scripts": {
"del": "rimraf dist"
}
yarn workspaces run del
清除所有包的node_modules
根目录下配置
"scripts": {
"clean": "lerna clean",
}
十五、基于模板生成组件基本结构
yarn add plop -W -D
十六、发布
yarn build:prod
npm whoami
yarn lerna