本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
1. 前言
1.1 这个库,是干啥的
每次新增一个页面的时候,你是怎么操作的?每次新增页面复制粘贴?今天我们从 element-ui
中学习一下如何初始化新的组件。
1.2 你能学到
element-ui
如何初始化新的组件- 学以致用应用到自己开发的项目中,比如新增页面等~
2. 准备
2.1 环境准备
2.1.1 在线环境
通过 github1s 在线 vscode 打开:new.js
2.1.2 git clone
# 克隆官方项目
git clone https://github.com/ElemeFE/element.git
# npm i -g yarn
cd element && npm run dev
3 看看 源码
3.1 经典三部曲
3.1.1 经典README
README 中好像没什么,只要安装方式和快速上手和其他的一些目前用不到的东西,但是里面有个贡献指南。我们等会进去看看~
3.1.2 经典package.json
先看看是怎么启动项目的
从 package.json 中即可得知
"script": {
"bootstrap": "yarn || npm i",
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
"dev": "npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js",
},
3.1.3 贡献指南
贡献指南中提到本文关键
组件开发规范
- 通过
**make new**
创建组件目录结构,包含测试代码、入口文件、文档- 如果包含父子组件,需要更改目录结构,参考 Button
- 组件内如果依赖了其他组件,需要在当前组件内引入,参考 Select
make命令
这个make new
命令是啥?怎么在 package.json 中 script 模块没看到嘞?
我是真第一次见哇😶然后搜索了一下找到了阮一峰老师的这篇文章~
Make
这个词,英语的意思是”制作”。Make命令直接用了这个意思,就是要做出某个文件。并且需要有人告诉它,如何调用其他命令完成这个目标。
而这个人就是 Makefile
文件~
MakeFile
我们去到 MakeFile 里看看,就能找到对应的new
命令
# element/Makefile
new:
node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))
这行命令又指向了一个地方 build/bin/new.js 看来这里就是关键岛屿了~
3.3 理解源码
在 new.js 设置断点后,命令行输入make new zhou 舟测试
,zhou 是 组件名字,也是后面路径什么的所用的,后面那个则是组件中文名。然后就跟着调试就好了~
3.3.1 命令初始判断 L1-L11
'use strict';
console.log();
process.on('exit', () => {
console.log();
});
if (!process.argv[2]) {
console.error('[组件名]必填 - Please enter new component name');
process.exit(1);
}
process.on
process
对象部署了EventEmitter
接口,可以使用on
方法监听各种事件,并指定回调函数。
这里监听了 exit 事件,退出时输出一个空行来使得终端显示更为美观
process.argv
process.argv
属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node
,第二个成员是脚本文件名,其余成员是脚本文件的参数。
这里对参数进行判断,如果没有传入必填的参数——即组件名,就会报错并 exit
3.3.2 引入相关依赖 L13-L20
// 路径模块
const path = require('path');
// 文件模块
const fs = require('fs');
// 保存文件
const fileSave = require('file-save');
// 转驼峰
const uppercamelcase = require('uppercamelcase');
// 第一个参数 组件名
const componentname = process.argv[2]; //*
// 第二个参数 组件中文名
const chineseName = process.argv[3] || componentname;
// 转驼峰
const ComponentName = uppercamelcase(componentname);
// package 路径
const PackagePath = path.resolve(__dirname, '../../packages', componentname);
file-save
file-save
模块会为文件建立一个写入流,如果目录不存在需要创建,则自动创建目录。
后面添加文件配置就用到了这个
3.3.3 文件模板 L21-L95
定义了多个文件模板方便分情况进行使用
const Files = [
{
filename: 'index.js',
content: `import ${ComponentName} from './src/main';
/* istanbul ignore next */
${ComponentName}.install = function(Vue) {
Vue.component(${ComponentName}.name, ${ComponentName});
};
//...
]
3.3.4 添加配置到对应文件中 L97-L130
把 componentname
添加到 components.json
// 添加到 components.json
const componentsFile = require('../../components.json');
if (componentsFile[componentname]) { //判断zhou是否已经存在
console.error(`${componentname} 已存在.`);
process.exit(1);
}
componentsFile[componentname] = `./packages/${componentname}/index.js`; //设置为对应的字符串
//建立对应文件
fileSave(path.join(__dirname, '../../components.json'))
.write(JSON.stringify(componentsFile, null, ' '), 'utf8')
.end('\n');
把 componentname.scss 添加到 index.scss
// 添加到 index.scss
const sassPath = path.join(__dirname, '../../packages/theme-chalk/src/index.scss');
const sassImportText = `${fs.readFileSync(sassPath)}@import "./${componentname}.scss";`;
fileSave(sassPath)
.write(sassImportText, 'utf8')
.end('\n
把 componentname.d.ts 添加到 element-ui.d.ts
// 添加到 element-ui.d.ts
const elementTsPath = path.join(__dirname, '../../types/element-ui.d.ts');
let elementTsText = `${fs.readFileSync(elementTsPath)}
/** ${ComponentName} Component */
export class ${ComponentName} extends El${ComponentName} {}`;
const index = elementTsText.indexOf('export') - 1;
const importString = `import { El${ComponentName} } from './${componentname}'`;
elementTsText = elementTsText.slice(0, index) + importString + '\n' + elementTsText.slice(index);
fileSave(elementTsPath)
.write(elementTsText, 'utf8')
.end('\n');
3.3.5 创建 package L131-L136
// 创建 package
Files.forEach(file => {
fileSave(path.join(PackagePath, file.filename))
.write(file.content, 'utf8')
.end('\n');
});
forEach
遍历前面的文件模板,并写入 packages 文件夹路径下对应文件,新增 element/packages/zhou/index.js``element/packages/zhou/src/main.vue
等文件
3.3.6 把新增的组件添加到 nav.config.json L138-L155
// 添加到 nav.config.json
const navConfigFile = require('../../examples/nav.config.json');
Object.keys(navConfigFile).forEach(lang => {
let groups = navConfigFile[lang][4].groups;
groups[groups.length - 1].list.push({
path: `/${componentname}`,
title: lang === 'zh-CN' && componentname !== chineseName
? `${ComponentName} ${chineseName}`
: ComponentName
});
});
fileSave(path.join(__dirname, '../../examples/nav.config.json'))
.write(JSON.stringify(navConfigFile, null, ' '), 'utf8')
.end('\n');
console.log('DONE!');
修改json文件中对应的配置,如添加
{
"path": "/zhou",
"title": Zhou"
}
4. 学习资源
- 阮一峰老师:make 命令
- 阮一峰老师:process
-
5. 总结 & 收获
5.1 总结 new.js 流程
5.2 知识点
process
相关知识点- npm file-save 库
- 本来繁琐的操作,如何通过总结与规范让一行代码减去大量重复的工作
5.3 React相关的
我技术栈主要是React
,没用过vue
,所以本来想看看antd
系列有没有类似的东西,但是并没有找到…当然本文的知识点基本都还是原生JS
但我还是想看看🤣如果你有看到 Re``act 技术栈有用到类似东西的话,欢迎评论区留言,让我去瞅瞅😛🌊如果有所帮助,欢迎点赞关注,一起进步⛵