<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>

          在不同 webpack 版本的 Vue 項目中配置 Storybook

          共 24564字,需瀏覽 50分鐘

           ·

          2021-04-20 23:51

          在之前的一篇文章中,介紹過組件化搭建工具 storybook 在 vue 項目中的安裝和配置。相比于其成文的時間,vue 項目依賴的工具多有發(fā)展;并且在實際應用中,多種歷史版本的項目并存的狀況比比皆是,用官方提供的 npx sb init 往往會出現(xiàn)配置失敗的情況,而較新或過舊的資料都在網上難覓 --

          所以在此特別補充一篇,記錄 新、舊 兩種典型配置下,storybook 可用的手動配置方法:

          1. babel7 + webpack5

          1.1 安裝過程

          diff --git a/.babelrc b/.babelrc
          index e1f4817..71b5fe8 100644
          --- a/.babelrc
          +++ b/.babelrc
          @@ -6,7 +6,8 @@
                   "modules"false,
                   "targets""last 2 years, ie > 8"
                 }
          -    ]
          +    ],
          +    "@vue/babel-preset-jsx"
             ],
             "plugins": [
               "@babel/plugin-transform-runtime",
          diff --git a/.eslintignore b/.eslintignore
          index e2192c5..0858135 100644
          --- a/.eslintignore
          +++ b/.eslintignore
          @@ -2,4 +2,5 @@
          +/test/*
          +*.spec.js
          diff --git a/.storybook/demo.css b/.storybook/demo.css
          new file mode 100644
          index 0000000..820f051
          --- /dev/null
          +++ b/.storybook/demo.css
          @@ -0,0 +1,12 @@
          +.demo {
          +    padding: 50px;
          +    display: inline-block;
          +}
          +.demo .block {
          +    margin-bottom: 30px;
          +}
          diff --git a/.storybook/main.js b/.storybook/main.js
          new file mode 100644
          index 0000000..ceca416
          --- /dev/null
          +++ b/.storybook/main.js
          @@ -0,0 +1,22 @@
          +const path = require("path");
          +const pathResolve = p => path.resolve(__dirname, "../", p);
          +
          +module.exports = {
          +  stories: ["../src/**/*.stories.mdx""../src/**/*.stories.@(js|jsx|ts|tsx)"],
          +  addons: ["@storybook/addon-links""@storybook/addon-essentials"],
          +  core: {
          +    builder: "webpack5"
          +  },
          +  webpackFinal: config => {
          +    config.resolve.alias = {
          +      ...config.resolve.alias,
          +      "@": pathResolve("src"),
          +      "~": pathResolve("node_modules")
          +    };
          +    config.module.rules.push({
          +      test: /\.scss$/,
          +      use: ["vue-style-loader""css-loader""sass-loader"]
          +    });
          +    return config;
          +  }
          +};
          diff --git a/.storybook/manager-head.html b/.storybook/manager-head.html
          new file mode 100644
          index 0000000..c5c2955
          --- /dev/null
          +++ b/.storybook/manager-head.html
          @@ -0,0 +1,14 @@
          +<style>
          +  .sidebar-header {
          +    text-align: center;
          +  }
          +  .sidebar-header > div > a {
          +    font-size: 28px;
          +  }
          +</style>
          diff --git a/.storybook/manager.js b/.storybook/manager.js
          new file mode 100644
          index 0000000..c2321a4
          --- /dev/null
          +++ b/.storybook/manager.js
          @@ -0,0 +1,10 @@
          +import { addons } from "@storybook/addons";
          +import { create } from "@storybook/theming";
          +
          +addons.setConfig({
          +  theme: create({
          +    base: "light",
          +    brandImage: null,
          +    brandTitle: "前端組件"
          +  })
          +});
          diff --git a/.storybook/preview.js b/.storybook/preview.js
          new file mode 100644
          index 0000000..55d75d7
          --- /dev/null
          +++ b/.storybook/preview.js
          @@ -0,0 +1,23 @@
          +import "./demo.css";
          +
          +export const parameters = {
          +    actions: { argTypesRegex: "^on[A-Z].*" },
          +    controls: {
          +        matchers: {
          +            color: /(background|color)$/i,
          +            date: /Date$/
          +        }
          +    },
          +    // https://github.com/storybookjs/storybook/pull/9090/commits/2db3faa727fed585fb5d9e6db23cc3835fc88829
          +    viewMode: "docs"
          +};
          diff --git a/package.json b/package.json
          index 673f3a9..c0b7b27 100644
          --- a/package.json
          +++ b/package.json
          @@ -1,19 +1,23 @@
          +    "storybook""start-storybook -p 6006 --no-manager-cache",
          +    "build-storybook""build-storybook"
             },
             "dependencies": {
          +    "@babel/plugin-syntax-jsx""^7.12.13",
          +    "@storybook/addon-actions""^6.2.7",
          +    "@storybook/addon-essentials""^6.2.7",
          +    "@storybook/addon-links""^6.2.7",
          +    "@storybook/builder-webpack5""^6.2.7",
          +    "@storybook/theming""^6.2.8",
          +    "@storybook/vue""^6.2.7",
          +    "@vue/babel-helper-vue-jsx-merge-props""^1.2.1",
          +    "@vue/babel-preset-jsx""^1.2.4",
               "babel-plugin-transform-vue-jsx""~3.7.0",
          diff --git a/src/utils/utils.js b/src/utils/utils.js
          new file mode 100644
          index 0000000..3e8413d
          --- /dev/null
          +++ b/src/utils/utils.js
          @@ -0,0 +1,103 @@
          +export const formatCode = jsCode =>
          +    JSON.stringify(
          +        jsCode,
          +        // eslint-disable-next-line
          +    (key, value) => {
          +            if (typeof value === 'function') {
          +                return value.toString();
          +            } else {
          +                return value;
          +            }
          +        },
          +        2
          +    ).replace(/\\n/g, '\n');
          diff --git a/src/utils/storyUtils.js b/src/utils/storyUtils.js
          new file mode 100644
          index 0000000..4ad5149
          --- /dev/null
          +++ b/src/utils/storyUtils.js
          @@ -0,0 +1,49 @@
          +import {formatCode} from './utils';
          +
          +/**
          + * 生成 storybook 用例輔助函數
          + * @param {String} path - 組件的層級式路徑,由 `/` 分割
          + * @param {Object} storyComponent - 用于展示的用例
          + * @param {String} markdown - 文檔
          + * @param {Object} [originComponent] - 可選,如果需要從原始組件內部的 jsdoc 自動生成 API,則傳入原始組件
          + * @return {Object} story - {metadata, named}
          + * @see https://github.com/storybookjs/storybook/issues/8527
          + */
          +export const storyOf = (path, storyComponent, markdown, originComponent) => {
          +    const pathArr = path.split('/').map(p => {
          +    // https://storybook.js.org/docs/vue/writing-stories/args#setting-args-through-the-url
          +        if (!/^[0-9a-zA-Z]/.test(p)) {
          +            return '#' + p;
          +        }
          +        return p;
          +    });
          +    const storyName = pathArr[pathArr.length - 1];
          +
          +    const metadata = {
          +        component: originComponent || void 0,
          +        title: pathArr.join('/'),
          +        decorators: [
          +            () => ({template: '<div class="demo"><story></story></div>'})
          +        ]
          +    };
          +
          +    const named = () => storyComponent;
          +    named.storyName = storyName;
          +    named.parameters = {
          +        docs: {
          +            description: {
          +                component: markdown || '&nbsp;'
          +            },
          +            source: {
          +                // type'dynamic'
          +                type'code',
          +                code: formatCode(storyComponent)
          +            }
          +        }
          +    };
          +
          +    return {
          +        metadata,
          +        named
          +    };
          +};

          1.2 組件和用例

          用例1:

          import { Meta, Preview, Description } from '@storybook/addon-docs/blocks'
          import readme from './README.md'
          import pathPrefix from './pathPrefix.js'

          <Meta title={pathPrefix + "/API"} />

          <Description markdown={readme} />

          組件2:

          <template functional>
          <x-table
          v-bind="{ ...data.attrs, ...props }"
          v-on="listeners"
          >
          <!-- ... -->
          </x-table>
          </template>

          <script>
          export default {
          props: {
          /**
          * 配置中增加了 __自定義 component__ 的能力,避免了在 template 中再分別寫 slot;
          * 自定義組件對象會默認接收 `row` 和 `column-config` 兩個屬性
          */
          columns: {
          type: Array,
          default: () => []
          }
          },
          emits: [
          /**
          * 自定義組件中如果發(fā)出同名事件,會被 table 的容器監(jiān)聽到;
          * 參數為 `key, ...values`
          */
          'column-action'
          ]
          };
          </script>

          用例2:

          import {storyOf} from '@/utils/storyUtils';
          import Comp from '@/components/SimpleTable';

          const title = 'Example/SimpleTable|全配置化表格';

          const markdown = `
            > 基于 \`x-table\` 的擴展,可以接收其原有屬性和事件
          `
          ;

          const comp = {
              template`
                  <x-simple-table
                      :width="600"
                      :columns="columns"
                      :data="list"
                      @column-action="onColAction"
                  />
              `
          ,
              data() {
                  return {
                      //...
                  };
              },
              methods: {
                  onColAction(act, value) {
                      alert([act, value]);
                  }
              }
          };

          const {metadata, named} = storyOf(title, comp, markdown, Comp);
          export default metadata;
          export const story = named;

          2. babel6 + webpack4

          2.1 安裝過程

          diff --git a/.storybook/addons.js b/.storybook/addons.js
          new file mode 100644
          index 0000000..7106272
          --- /dev/null
          +++ b/.storybook/addons.js
          @@ -0,0 +1,4 @@
          +import 'storybook-addon-vue-info/lib/register';
          +import '@storybook/addon-backgrounds/register';
          +import '@storybook/addon-viewport/register';
          +import '@storybook/addon-storysource/register';
          diff --git a/.storybook/config.js b/.storybook/config.js
          new file mode 100644
          index 0000000..f84ef78
          --- /dev/null
          +++ b/.storybook/config.js
          @@ -0,0 +1,34 @@
          +import {configure, addDecorator, addParameters} from '@storybook/vue';
          +import {create} from '@storybook/theming';
          +import {withInfo, setDefaults} from 'storybook-addon-vue-info';
          +import './demo.css';
          +
          +setDefaults({
          +    header: false,
          +    useDocgen: false
          +});
          +addDecorator(withInfo);
          +addParameters({
          +    options: {
          +        theme: create({
          +            base: 'light',
          +            brandImage: null,
          +            brandTitle: '前端組件'
          +        })
          +    }
          +});
          +const req = require.context('../src'true, /\.stories\.js$/);
          +function loadStories() {
          +    req.keys().forEach(filename => {
          +        try {
          +            req(filename);
          +        } catch (ex) {
          +            console.log('storybook-req', ex);
          +        }
          +    });
          +}
          +configure(loadStories, module);
          diff --git a/.storybook/manager-head.html b/.storybook/manager-head.html
          new file mode 100644
          index 0000000..c5c2955
          --- /dev/null
          +++ b/.storybook/manager-head.html
          @@ -0,0 +1,14 @@
          +<style>
          +  .sidebar-header {
          +    text-align: center;
          +  }
          +  .sidebar-header > div > a {
          +    font-size: 28px;
          +  }
          +</style>
          diff --git a/.storybook/demo.css b/.storybook/demo.css
          new file mode 100644
          index 0000000..820f051
          --- /dev/null
          +++ b/.storybook/demo.css
          @@ -0,0 +1,12 @@
          +.demo {
          +    padding: 50px;
          +    display: inline-block;
          +}
          +
          +.demo .block {
          +    margin-bottom: 30px;
          +}
          diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html
          new file mode 100644
          index 0000000..e69de29
          diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js
          new file mode 100644
          index 0000000..3186d15
          --- /dev/null
          +++ b/.storybook/webpack.config.js
          @@ -0,0 +1,32 @@
          +const path = require('path');
          +const pathResolve = p => path.resolve(__dirname, '../', p);
          +module.exports = ({config, mode}) => {
          +    config.resolve.alias = {
          +        ...config.resolve.alias,
          +        '@': pathResolve('src'),
          +        '~': pathResolve('node_modules')
          +    };
          +    config.module.rules.push({
          +        test: /\.scss$/,
          +        use: ['style-loader''css-loader''sass-loader']
          +    });
          +    config.module.rules.push({
          +        test: /\.vue$/,
          +        loader: 'storybook-addon-vue-info/loader',
          +        enforce: 'post'
          +    });
          +    config.module.rules.push({
          +        test: /\.stories\.js$/,
          +        loaders: [require.resolve('@storybook/addon-storysource/loader')],
          +        enforce: 'pre'
          +    });
          +    if (process.env.NODE_ENV === 'production') {
          +        config.output.filename = 'bundle.[name].js';
          +        config.optimization.splitChunks.automaticNameDelimiter = '.';
          +        config.optimization.runtimeChunk = {
          +            name: entrypoint => `runtime.${entrypoint.name}`
          +        };
          +    }
          +    // console.log(config);
          +    return config;
          +};
          diff --git a/package.json b/package.json
          index d83bbba..6027c58 100644
          --- a/package.json
          +++ b/package.json
          @@ -21,7 +21,9 @@
          +        "build-storybook""build-storybook",
          +        "storybook""start-storybook -p 6006"
               "devDependencies": {
          +        "@storybook/addon-backgrounds""^5.3.18",
          +        "@storybook/addon-info""^5.3.18",
          +        "@storybook/addon-storysource""^5.3.18",
          +        "@storybook/addon-viewport""^5.3.18",
          +        "@storybook/addons""^5.3.18",
          +        "@storybook/theming""^5.3.18",
          +        "@storybook/vue""^5.3.18",
          +        "storybook-addon-vue-info""^1.4.2",

          2.2 用例

          import {storiesOf} from '@storybook/vue';
          import CustomCols from './index';

          const totalColumns = [
              // ...
          ];

          const description = {
              CustomCols: {
                  props: {
                      choose'用于 v-model 的值',
                      totalColumns'可選擇的列',
                      storageName'本地存儲的key',
                  },
                  events: {
                      'on-change''選擇的列改變時觸發(fā)'
                  }
              }
          };

          const info = {
              useDocgenfalse,
              summary'基于 xxx'
          };

          storiesOf('增強的自定義表格列'module)
              .add(
                  '本機存儲上次選擇結果',
                  () => ({
                      components: {
                          CustomCols
                      },
                      template`
                      <div class="demo">
                              <div id="demo-result"></div>
                              <custom-cols
                                  :storage-name="selColsStoName"
                                  :total-columns="totalColumns"
                                  v-model="selectedCols"
                                  @on-change="onChange"
                              />
                      </div>
                      `
          ,
                      data() {
                          return {
                              totalColumns,
                              selColsStoName`${window.location.hostname}_overview_custom_cols`,
                              selectedCols: ['impression''click']
                          };
                      },
                      methods: {
                          onChange(v) {
                              //...
                          }
                      },
                      description
                  }),
                  {
                      info
                  }
              );


          --End--


          查看更多前端好文
          請搜索 云前端 或 fewelife 關注公眾號

          轉載請注明出處


          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  欧美成人一区二免费视频软件 | 成人午夜大香蕉 | 特级西西人体444.444人体聚色 | 天天射天玩天天日 | 黄色日批视频在线观看 |