【若川视野 x 源码共读】 | 怎么在初始化页面上摆脱床上施床、提高效率、获得更多摸鱼时间?

382 阅读3分钟

本文参加了由公众号@若川视野发起的每周源码共读活动,点击了解详情一起参与。

1. 前言

一下就四号了,祝大家五四青年节快乐~😛 以及假期结束快乐~😢

1.1 这个库,是干啥的

每次新增一个页面的时候,你是怎么操作的?每次复制粘贴?还是用vscode自定义代码块?这样方便了但不完全方便..
今天我们站在巨人 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",
},

image.png

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

npm 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. 学习资源

5. 总结 & 收获

5.1 总结 new.js 流程

image.png

5.2 知识点

  • process 相关知识点
  • npm file-save
  • 本来繁琐的操作,如何通过总结与规范让一行代码减去大量重复的工作

5.3 React相关的

我技术栈主要是 React,没用过 vue,所以本来想看看 antd 系列有没有类似的东西,但是并没有找到...当然本文的知识点基本都还是原生JS
但我还是想看看🤣如果你有看到 React 技术栈有用到类似东西的话,欢迎评论区留言,让我去瞅瞅😛

🌊如果有所帮助,欢迎点赞关注,一起进步⛵