源码学习目录

1. 前言

1.1 环境

  1. 操作系统: macOS 11.5.2
  2. 浏览器: Chrome 94.0.4606.81
  3. element-ui 2.15.7

    1.2 阅读该文章可以get以下知识点

  4. element-ui 如何初始化新的组件

    2. 开始

    2.1 如何创建新组件

    1. make new xxx

    运行上面的命令会创建响应的资源文件

  5. packages/xxx

  6. test/unit/specs/xxx.spec.js
  7. types/element-ui.d.ts

    2.2 Makefile

    ```makefile .PHONY: dist test default: help

install: npm install

new:

这里会执行 build/bin/new.js下面的文件

  1. node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))

help: @echo “ \033[35mmake\033[0m \033[1m命令使用说明\033[0m” @echo “ \033[35mmake install\033[0m\t\033[0m\t\033[0m\t\033[0m\t—- 安装依赖” @echo “ \033[35mmake new [中文名]\033[0m\t—- 创建新组件 package. 例如 ‘make new button 按钮’” @echo “ \033[35mmake dev\033[0m\t\033[0m\t\033[0m\t\033[0m\t—- 开发模式” @echo “ \033[35mmake dist\033[0m\t\033[0m\t\033[0m\t\033[0m\t—- 编译项目,生成目标文件” @echo “ \033[35mmake deploy\033[0m\t\033[0m\t\033[0m\t\033[0m\t—- 部署 demo” @echo “ \033[35mmake pub\033[0m\t\033[0m\t\033[0m\t\033[0m\t—- 发布到 npm 上” @echo “ \033[35mmake new-lang \033[0m\t\033[0m\t\033[0m\t—- 为网站添加新语言. 例如 ‘make new-lang fr’”

  1. 挺有意思的写法,直接通过make new再去执行脚本<br />$(filter-out $@,$(MAKECMDGOALS))这段代码是将参数传入到new.js脚本中
  2. 1. $@
  3. 1. 会返回自身,例如 这里的new:,会返回new
  4. 2. $(MAKECMDGOALS)
  5. 1. 返回所有的参数,例如 make new xxx,会返回 new xx
  6. 3. filter-out反过滤函数
  7. 1. 有两个参数,第一个参数是匹配的字符串,第二个参数是需要处理的字符串
  8. 2. 过滤掉所有匹配的字符串,保留不匹配的,例如; make new xxx, 返回xxx,过滤掉了new
  9. <a name="ezz5E"></a>
  10. ## 2.3 new.js
  11. ```javascript
  12. 'use strict';
  13. console.log();
  14. process.on('exit', () => {
  15. console.log();
  16. });
  17. // 默认0和1是node环境变量和当前运行文件,第三个就是组件名
  18. if (!process.argv[2]) {
  19. console.error('[组件名]必填 - Please enter new component name');
  20. process.exit(1);
  21. }
  22. const path = require('path');
  23. const fs = require('fs');
  24. // 文件存储
  25. const fileSave = require('file-save');
  26. // 驼峰转化
  27. const uppercamelcase = require('uppercamelcase');
  28. // 组件吗
  29. const componentname = process.argv[2];
  30. // 如果有中文名,传入第四个参数,没有的话,直接去组件吗
  31. const chineseName = process.argv[3] || componentname;
  32. // 转化成大驼峰
  33. const ComponentName = uppercamelcase(componentname);
  34. // 获取packages的路径
  35. const PackagePath = path.resolve(__dirname, '../../packages', componentname);
  36. // 文件名和模板语法
  37. const Files = [
  38. {
  39. filename: 'index.js',
  40. content: `import ${ComponentName} from './src/main';
  41. /* istanbul ignore next */
  42. ${ComponentName}.install = function(Vue) {
  43. Vue.component(${ComponentName}.name, ${ComponentName});
  44. };
  45. export default ${ComponentName};`
  46. },
  47. {
  48. filename: 'src/main.vue',
  49. content: `<template>
  50. <div class="el-${componentname}"></div>
  51. </template>
  52. <script>
  53. export default {
  54. name: 'El${ComponentName}'
  55. };
  56. </script>`
  57. },
  58. {
  59. filename: path.join('../../examples/docs/zh-CN', `${componentname}.md`),
  60. content: `## ${ComponentName} ${chineseName}`
  61. },
  62. {
  63. filename: path.join('../../examples/docs/en-US', `${componentname}.md`),
  64. content: `## ${ComponentName}`
  65. },
  66. {
  67. filename: path.join('../../examples/docs/es', `${componentname}.md`),
  68. content: `## ${ComponentName}`
  69. },
  70. {
  71. filename: path.join('../../examples/docs/fr-FR', `${componentname}.md`),
  72. content: `## ${ComponentName}`
  73. },
  74. {
  75. filename: path.join('../../test/unit/specs', `${componentname}.spec.js`),
  76. content: `import { createTest, destroyVM } from '../util';
  77. import ${ComponentName} from 'packages/${componentname}';
  78. describe('${ComponentName}', () => {
  79. let vm;
  80. afterEach(() => {
  81. destroyVM(vm);
  82. });
  83. it('create', () => {
  84. vm = createTest(${ComponentName}, true);
  85. expect(vm.$el).to.exist;
  86. });
  87. });
  88. `
  89. },
  90. {
  91. filename: path.join('../../packages/theme-chalk/src', `${componentname}.scss`),
  92. content: `@import "mixins/mixins";
  93. @import "common/var";
  94. @include b(${componentname}) {
  95. }`
  96. },
  97. {
  98. filename: path.join('../../types', `${componentname}.d.ts`),
  99. content: `import { ElementUIComponent } from './component'
  100. /** ${ComponentName} Component */
  101. export declare class El${ComponentName} extends ElementUIComponent {
  102. }`
  103. }
  104. ];
  105. // 添加到 components.json
  106. const componentsFile = require('../../components.json');
  107. if (componentsFile[componentname]) {
  108. console.error(`${componentname} 已存在.`);
  109. process.exit(1);
  110. }
  111. componentsFile[componentname] = `./packages/${componentname}/index.js`;
  112. // 存储新的json
  113. fileSave(path.join(__dirname, '../../components.json'))
  114. .write(JSON.stringify(componentsFile, null, ' '), 'utf8')
  115. .end('\n');
  116. // 添加到 index.scss
  117. const sassPath = path.join(__dirname, '../../packages/theme-chalk/src/index.scss');
  118. const sassImportText = `${fs.readFileSync(sassPath)}@import "./${componentname}.scss";`;
  119. fileSave(sassPath)
  120. .write(sassImportText, 'utf8')
  121. .end('\n');
  122. // 添加到 element-ui.d.ts
  123. const elementTsPath = path.join(__dirname, '../../types/element-ui.d.ts');
  124. let elementTsText = `${fs.readFileSync(elementTsPath)}
  125. /** ${ComponentName} Component */
  126. export class ${ComponentName} extends El${ComponentName} {}`;
  127. const index = elementTsText.indexOf('export') - 1;
  128. const importString = `import { El${ComponentName} } from './${componentname}'`;
  129. elementTsText = elementTsText.slice(0, index) + importString + '\n' + elementTsText.slice(index);
  130. fileSave(elementTsPath)
  131. .write(elementTsText, 'utf8')
  132. .end('\n');
  133. // 创建 package
  134. Files.forEach(file => {
  135. fileSave(path.join(PackagePath, file.filename))
  136. .write(file.content, 'utf8')
  137. .end('\n');
  138. });
  139. // 添加到 nav.config.json
  140. const navConfigFile = require('../../examples/nav.config.json');
  141. Object.keys(navConfigFile).forEach(lang => {
  142. let groups = navConfigFile[lang][4].groups;
  143. groups[groups.length - 1].list.push({
  144. path: `/${componentname}`,
  145. title: lang === 'zh-CN' && componentname !== chineseName
  146. ? `${ComponentName} ${chineseName}`
  147. : ComponentName
  148. });
  149. });
  150. fileSave(path.join(__dirname, '../../examples/nav.config.json'))
  151. .write(JSON.stringify(navConfigFile, null, ' '), 'utf8')
  152. .end('\n');
  153. console.log('DONE!');

3. 总结

  1. 熟悉了一些Makefile的语法
  2. 通过Makefile的方式,去执行一些脚本,代码很简洁,如 make new, make help,不需要去写package.json scripts,减少了script的脚本,但是也有一定的理解成本,有些前端同学不懂Makefile,windows环境需要gcc环境
  3. 组件初始化主要是通过文件读写来处理,没什么难度的东西

    4. 参考文档

  4. https://juejin.cn/post/7031331765482422280

  5. https://www.npmjs.com/package/element-ui