本文已参与「新人创作礼」活动,一起开启掘金创作之路
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
一、收获
通过阅读本次的源码学到了一些对文件操作的api及一些技巧,让我认识到了写代码其实不用那么的死板粘贴复制其实可以自己去写一些插件来简化自己的工作,提高自己的工作效率。
二、源码知识点
记录我自己比较认为比较重要的知识点
- process.on()
- process.argv
- process.exit()
- file-save 插件
- uppercamelcase 驼峰命名插件
- fileSave().write().end()
1.process.on()
监听进程事件,此次源码中该方法接收两个参数 一个事件名称,一个回调函数监听进程退出,退出了触发回调函数
process.on('exit', () => {
console.log('退出进程');
});
2.process.argv
属性返回一个数组,由命令行执行脚本时的各个参数组成。它的第一个成员总是node,第二个成员是脚本文件名,其余成员是脚本文件的参数。
执行命令
node build/bin/new.js 'hui3' '测试组件'
获取后面的参数
// 没有获取到组件名称推出当前进程
if (!process.argv[2]) {
console.error('[组件名]必填 - Please enter new component name');
process.exit(1);
}
process.argv[3]
console.log(process.argv.slice(1)) // ['hui3','测试组件']
3.process.exit()
退出进程或者进程执行成功 默认参数0代表进程执行成功,大于0代表退出进程
process.exit(1); //退出进程
4.file-save插件
此次源码中用到的是fileSave().write().end() 大概用法如下
fileSave接收一个地址.write接收一个字符串文本,第二个参数为什么格式
const fileSave = require('file-save');
fileSave(path.join(__dirname, '../../components.json'))
.write(JSON.stringify(componentsFile, null, ' '), 'utf8')
.end('\n');
5.uppercamelcase 驼峰命名插件
作用就是将我们传入的字符转换成驼峰命名的形式
官方示例
const upperCamelCase = require('uppercamelcase');
upperCamelCase('foo-bar');
//=> FooBar
upperCamelCase('foo_bar');
//=> FooBar
upperCamelCase('Foo-Bar');
//=> FooBar
upperCamelCase('--foo.bar');
//=> FooBar
...
三、阅读源码
第一块就是监听进程退出
process.on('exit', () => {
console.log('退出进程');
});
然后就是判断启动命令是否传入了组件名字 没有传入则退出进程
if (!process.argv[2]) {
console.error('[组件名]必填 - Please enter new component name');
process.exit(1); // 默认参数0表示进程执行成功,
}
下面就是引入一些插件及获取组件名称在转成驼峰命名的形式
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); // 转成驼峰命名
const PackagePath = path.resolve(__dirname, '../../packages', componentname); // 目录地址
定义模板文件及文件里面的内容
const Files = [
{
filename: 'index.js',
content: `import ${ComponentName} from './src/main';
/* istanbul ignore next */
${ComponentName}.install = function(Vue) {
Vue.component(${ComponentName}.name, ${ComponentName});
};
export default ${ComponentName};`
},
{
filename: 'src/main.vue',
content: `<template>
<div class="el-${componentname}"></div>
</template>
<script>
export default {
name: 'El${ComponentName}'
};
</script>`
},
{
filename: path.join('../../examples/docs/zh-CN', `${componentname}.md`),
content: `## ${ComponentName} ${chineseName}`
},
{
filename: path.join('../../examples/docs/en-US', `${componentname}.md`),
content: `## ${ComponentName}`
},
{
filename: path.join('../../examples/docs/es', `${componentname}.md`),
content: `## ${ComponentName}`
},
{
filename: path.join('../../examples/docs/fr-FR', `${componentname}.md`),
content: `## ${ComponentName}`
},
{
filename: path.join('../../test/unit/specs', `${componentname}.spec.js`),
content: `import { createTest, destroyVM } from '../util';
import ${ComponentName} from 'packages/${componentname}';
describe('${ComponentName}', () => {
let vm;
afterEach(() => {
destroyVM(vm);
});
it('create', () => {
vm = createTest(${ComponentName}, true);
expect(vm.$el).to.exist;
});
});
`
},
{
filename: path.join('../../packages/theme-chalk/src', `${componentname}.scss`),
content: `@import "mixins/mixins";
@import "common/var";
@include b(${componentname}) {
}`
},
{
filename: path.join('../../types', `${componentname}.d.ts`),
content: `import { ElementUIComponent } from './component'
/** ${ComponentName} Component */
export declare class El${ComponentName} extends ElementUIComponent {
}`
}
];
将组件添加到components.json添加的所有组件的地址都会存到这里面
1.先获取json里面的内容
2.再去判断当前组件名称是否已经存在存在则退出
3.没有就添加到json对象里面
4.重新写入到components.json文件里面
// 添加到 components.json
const componentsFile = require('../../components.json'); // 获取components.json 判断当前组件名称是否已经被创建
if (componentsFile[componentname]) {
console.error(`${componentname} 已存在.`);
process.exit(1);
}
// 没有则增加到json对象里面
componentsFile[componentname] = `./packages/${componentname}/index.js`;
// 重写写入到components.json文件
fileSave(path.join(__dirname, '../../components.json'))
.write(JSON.stringify(componentsFile, null, ' '), 'utf8')
.end('\n');
// 添加到 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');
添加到 element-ui.d.ts
- 首先读取文件的内容
这块代码的处理逻辑就是 element-ui.d.ts 代码分为两部分 一块是 imort开头 下面一块是export开头
2.找到export首次在字符串出现的位置 这样就区分出前半段和后半段
- 然后拼接号我们新建的组件导入字符串
4.然后组合起来elementTsText.slice(0, index) 前半段代码+新增组件的导入 + elementTsText.slice(index) 后半段
- 然后在执行写入
let elementTsText = `${fs.readFileSync(elementTsPath)}
/** ${ComponentName} Component */
export class ${ComponentName} extends El${ComponentName} {}`;
const index = elementTsText.indexOf('export') - 1; // 找到 export 在字符串中首次出现的位置
const importString = `import { El${ComponentName} } from './${componentname}'`;
elementTsText = elementTsText.slice(0, index) + importString + '\n' + elementTsText.slice(index);
fileSave(elementTsPath)
.write(elementTsText, 'utf8')
.end('\n');
创建 package
循环我们定义的模板文件数组在对应目录创建文件并在文件里添加对应的内容
Files.forEach(file => {
fileSave(path.join(PackagePath, file.filename))
.write(file.content, 'utf8')
.end('\n');
});
// 添加到 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!');
四、实践
通过读这篇源码及看若川的文章我想到了在我做后台管理系统的时候比如新增一个模块肯定会有首页首页就是搜索条件 及table的组件,我想着应该可以用此次源码中的方法可以实现每次运行一段命令就会创建好模块我们只需在里面写代码就好了,不用再去复制其他文件代码,然后在删除不用的代码。