<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從 Element UI 源碼中學(xué)到的 5 個(gè)技巧

          共 13194字,需瀏覽 27分鐘

           ·

          2021-08-01 10:26


          導(dǎo)語Element UI[1] 是世界級(jí)最優(yōu)秀的UI框架之一。這個(gè)優(yōu)秀的框架有哪些我們能學(xué)習(xí)的優(yōu)點(diǎn)呢?這篇文章將分享作者在查看這個(gè)框架倉庫源碼中認(rèn)為值得參考的技巧,建議配合element[2]源碼食用更佳。

          技巧一:組件腳手架

          腳手架在創(chuàng)建新組件的應(yīng)用:規(guī)范代碼目錄,減少搬磚工作量 ,腳手架源碼實(shí)現(xiàn):build/bin/new.js[3]

           # 執(zhí)行命令,參數(shù)說明    
           # componentname 組件名 必填   
           # chineseName 組將中文名 選填 ,不填默認(rèn)取值componentname  
           # node build/bin/new.js componentname [chineseName] 命令說明
           
           #
           在element項(xiàng)目下執(zhí)行
           node build/bin/new.js helloworld 示例

          腳手架處理結(jié)果:

          • 1、組件樣式處理:

            1.1 生成組件樣式 packages/theme-chalk/src/${componentname}.scss

            1.2 樣式入口文件packages/theme-chalk/src/index.scss 導(dǎo)入改組件樣式

          • 2、組件代碼處理:

            2.1 生成組件代碼文件 :packages/componentname/index.js和packages/{componentname}/index.js和packages/componentname/index.js和packages/{componentname}/src/main.vue

            2.2 新增組件的路徑信息導(dǎo)入到 components.json ,該文件是json對(duì)象,存放了組件的名字和組件入口路徑

          • 3、生成組件文檔:

            3.1 生成examlpes/docs/{i18n}/component.md, 其中,i18n=['en-US','es','fr-FR','zh-CN']

            3.2 將新增的組件文檔的標(biāo)題和路徑添加到 examples/nav.config.json,該文件是Element UI的組件文檔的目錄,保存了組件文檔的標(biāo)題和路由

          • 4、生成單元測(cè)試:

            4.1 生成單元測(cè)試文件:test/unit/specs/component.spec.js

          • 5、生成組件接口定義:

            5.1 生成組件的描述文件:types/component.d.ts

            5.2在types/element-ui.d.ts新增新組件的接口定義

          一個(gè)優(yōu)秀的組件,除了需要要把組件的代碼寫好,還有有單元測(cè)試、文檔說明,最好,也有有接口定義(這編譯器就有友好的使用提示),Element的組件起步就已經(jīng)配齊了??

          技巧二:用代碼來生成代碼

          源碼入口文件生成:Element UI 目前一共有80個(gè)組件,如果要導(dǎo)出這80個(gè)組件,那么引入、導(dǎo)出和聲明Vue組件的代碼都要寫240次,而且,組件的增刪都要去修改入口文件。為了減少這部分工作量,基于components.json[4]來生成入口文件的組件引入和導(dǎo)出。

          components.json內(nèi)容如下:

          入口文件有三處代碼(引入,導(dǎo)出和聲名組件)都要重復(fù)了80次,下面以引入代碼語句作為說明示例:

          引入代碼示例

          /* Automatically generated by './build/bin/build-entry.js' */

          import Pagination from '../packages/pagination/index.js';
          import Dialog from '../packages/dialog/index.js';
          import Autocomplete from '../packages/autocomplete/index.js';
          import Dropdown from '../packages/dropdown/index.js';
          import DropdownMenu from '../packages/dropdown-menu/index.js';
          import DropdownItem from '../packages/dropdown-item/index.js';
          import Menu from '../packages/menu/index.js';
          /**省略 70多個(gè)組件導(dǎo)入**/
          import CollapseTransition from 'element-ui/src/transitions/collapse-transition';

          生成上述代碼,主要邏輯:讀入components.json作為數(shù)據(jù),然后模版拼接起來,具體實(shí)現(xiàn)如下:


          var Components = require('../../components.json');
          var render = require('json-templater/string');
          var uppercamelcase = require('uppercamelcase');
          var endOfLine = require('os').EOL;

          // import 語句語法模版
          var IMPORT_TEMPLATE = 'import {{name}} from \'../packages/{{package}}/index.js\';';

          var ComponentNames = Object.keys(Components);
          var includeComponentTemplate = [];

          ComponentNames.forEach(name => {
            var componentName = uppercamelcase(name);
            includeComponentTemplate.push(
                // 單條import生成語句
                render(IMPORT_TEMPLATE, {
                  name: componentName,
                  package: name
                })
            );
          });

          console.info(includeComponentTemplate.join(endOfLine))

          生成代碼的實(shí)現(xiàn):build/bin/build-entry.js[5]

          代碼位置:src/index.js[6]

          技巧三:用md去寫組件文檔和示例

          十分優(yōu)雅的文檔和示例書寫方式:文檔和示例統(tǒng)一寫到markdown文件中,在通過編寫md-loader,先轉(zhuǎn)成html文件,再轉(zhuǎn)成vue的組件,再渲染,這種做法筆者認(rèn)為是太有意思。

          先看一下 Element UI 的文檔頁面的框架:

          路由組件邏輯(詳細(xì)代碼[7]):

          // 截取組件文檔的路由代碼片段
          const LOAD_DOCS_MAP = {
            'zh-CN'path => {
              return r => require.ensure([], () =>
                r(require(`./docs/zh-CN${path}.md`)),
              'zh-CN');
            },
            'en-US'path => {
              return r => require.ensure([], () =>
                r(require(`./docs/en-US${path}.md`)),
              'en-US');
            },
            'es'path => {
              return r => require.ensure([], () =>
                r(require(`./docs/es${path}.md`)),
              'es');
            },
            'fr-FR'path => {
              return r => require.ensure([], () =>
                r(require(`./docs/fr-FR${path}.md`)),
              'fr-FR');
            }
          };

          注意到,路由加載的組件不 vue 組件,而是,一個(gè) markdown 文件。這個(gè)過程,是在 webpack 打包過程中自定義一個(gè) loader 來實(shí)現(xiàn):markdown 轉(zhuǎn)成 vue 來實(shí)現(xiàn)的。

          詳細(xì)實(shí)現(xiàn):build/md-loader/index.js[8]

          接下來,將詳細(xì)分享文檔如何實(shí)現(xiàn)組件演示的效果:

          第一步:擴(kuò)展了markdown的container格式:demo

          :::demo ${content}:::

          實(shí)現(xiàn)代碼:

          const md = require('markdown-it')();
          const fs = require('fs');
          const path = require('path');
          const mdContainer = require('markdown-it-container');

          module.exports = md => {
          md.use(mdContainer, 'demo', {
          validate(params) {
          return params.trim().match(/^demo\s*(.*)$/);
          },
          render(tokens, idx) {
          const m = tokens[idx].info.trim().match(/^demo\s*(.*)$/);
          if (tokens[idx].nesting === 1) {
          const description = m && m.length > 1 ? m[1] : '';
          const content = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : '';
          return `<demo-block>
          ${description ? `<div>${md.render(description)}</div>` : ''}
          <!--element-demo: ${content}:element-demo-->
          `;
          }
          return '</demo-block>';
          }
          });

          md.use(mdContainer, 'tip');
          md.use(mdContainer, 'warning');
          };

          const inputPath = path.resolve(__dirname, './alert.md');
          const outputPath = path.resolve(__dirname, './alert.html');

          const input = fs.readFileSync(inputPath, {encoding: 'utf8'});
          const ounput = md.render(input);
          fs.writeFileSync(outputPath, ounput, {encoding: 'utf8'});}

          執(zhí)行結(jié)果:

          提取注釋內(nèi)容轉(zhuǎn)成組件的實(shí)例實(shí)現(xiàn)

          第二步:將html轉(zhuǎn)成ComponentDoc.vue

           <template>    
             <section class="content element-doc">        
              <h2>Alert 警告</h2>        
              <p>用于頁面中展示重要的提示信息。</p>        
              <h3>自定義關(guān)閉按鈕</h3>        
              <p>自定義關(guān)閉按鈕為文字或其他符號(hào)。</p>  
              
              <!--示例展示 這個(gè)步是最關(guān)鍵-->    
              <demo-block>            
                  <template name="source">              
                  <element-demo0 />            
                  </template>            
                  <template name="default"></template>            
                  <slot name="highlight"></slot>        
              </demo-block>  
              
            </section>
           </template>  

          組件:<demo-block>,源碼位置:examples/components/demo-block.vue[9]

          DemoBlockComponent效果展示如下圖:

          第三步:示例效果展示

          • 第一種,組件僅僅只有模板,沒有其他屬性,就跟描述內(nèi)容插槽一樣,直接以插槽透傳就行;
          • 第二種,是組件有script內(nèi)容,怎么處理呢?如下圖

          組件代碼是調(diào)用vue-template-compiler模塊生成的,參照:build/md-loader/util.js:L30[10]

          技巧四:icon組件示例

          Element UI 提供了280個(gè)icon,人工搬磚,寫文檔,又得加不少班呀。對(duì)于有追求的程序員,當(dāng)然要有想法了。處理技巧:使用postcss模塊解析icons的樣式文件,提取出el-icon-XXX的className,將所有icon的className組裝成數(shù)組中,保存到examples/icon.json[11]

          通過樣式class提取icon名字,代碼實(shí)現(xiàn)如下:

          'use strict';
          var postcss = require('postcss');
          var fs = require('fs');
          var path = require('path');
          var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/src/icon.scss'), 'utf8');
          // 借助postcss解析css文件
          var nodes = postcss.parse(fontFile).nodes;
          var classList = [];

          // 通過正則獲取icon的name
          nodes.forEach((node) => {  
              var selector = node.selector || '';  
              var reg = new RegExp(/\.el-icon-([^:]+):before/);  
              var arr = selector.match(reg);  
              if (arr && arr[1]) {    
              classList.push(arr[1]);  
          }});
          classList.reverse(); 
          // 希望按 css 文件順序倒序排列
          console.info(classList.length);

          fs.writeFile(
               path.resolve(__dirname, '../../examples/icon.json'), 
               JSON.stringify(classList)
           );  

          技巧三種中是將icons的icons.md生成的ComponentDoc.vue組件,無法編寫代碼來傳入icons數(shù)組,那就直接注入 Vue中原型鏈中

           // 文檔入口引入
           import Vue from 'vue'
           import icon from './icon.json';
           Vue.prototype.$icon = icon;

          icon文檔書寫

          圖標(biāo)集合
          <ul class="icon-list">
            <!--直接從原型鏈獲取全部icon名字-->
            <li v-for="name in $icon" :key="name">
              <span>
                <i :class="'el-icon-' + name"></i>
                <span class="icon-name">{{'el-icon-' + name}}</span>
              </span>
            </li>
          </ul>

          效果:element.eleme.io/#/zh-CN/com…[12]

          一下子解決這么多的重復(fù)工作??

          技巧五:文檔多語言

          文檔多語言,使用腳本生成每中語言,都單獨(dú)生成一個(gè)vue模板,和技巧二類似,詳細(xì)參考:build/bin/i18n.js[13]

          參考資料

          [1]

          https://element.eleme.io/: https://link.juejin.cn/?target=https%3A%2F%2Felement.eleme.io%2F

          [2]

          https://github.com/ElemeFE/element: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement

          [3]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/build/bin/new.js: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fbuild%2Fbin%2Fnew.js

          [4]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/components.json: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fcomponents.json

          [5]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/build/bin/build-entry.js: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fbuild%2Fbin%2Fbuild-entry.js

          [6]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/src/index.js: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fsrc%2Findex.js

          [7]

          https://github.com/ElemeFE/element/blob/dev/examples/route.config.js: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2Fdev%2Fexamples%2Froute.config.js

          [8]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/build/md-loader/index.js: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fbuild%2Fmd-loader%2Findex.js

          [9]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/examples/components/demo-block.vue: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fexamples%2Fcomponents%2Fdemo-block.vue

          [10]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/build/md-loader/util.js#L30: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fbuild%2Fmd-loader%2Futil.js%23L30

          [11]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/examples/icon.json: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fexamples%2Ficon.json

          [12]

          https://element.eleme.io/#/zh-CN/component/icon#tu-biao-ji-he: https://link.juejin.cn/?target=https%3A%2F%2Felement.eleme.io%2F%23%2Fzh-CN%2Fcomponent%2Ficon%23tu-biao-ji-he

          [13]

          https://github.com/ElemeFE/element/blob/45c0ef46f298cc9dfbe72d20039795184a8ddab0/build/bin/i18n.js: https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FElemeFE%2Felement%2Fblob%2F45c0ef46f298cc9dfbe72d20039795184a8ddab0%2Fbuild%2Fbin%2Fi18n.js


          轉(zhuǎn)自:azuo

          https://juejin.cn/post/6966491047257964575


          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产成人精品小电影 | 日本高潮视频在线观看 | 亚洲日韩国产精品 | 一区二区三区韩国av夜夜骚 | 美女抽插网站 |