快速摸一個 Ant design vue 項目

初始化項目

  • 用 vue-cli 快速構建項目 vue create ant-design-vue-projavascript

  • cd ant-design-vue-pro/css

  • 安裝必要依賴 npm i ant-design-vue momenthtml

  • 刪除/初始化 不須要的文件vue

    // clear
    
    └──src/
        ├───router/
        │   └───index.js
        ├───views/
        │   └───Home.vue
        └───App.vue
    
    複製代碼
  • 引入 ant-design-vuejava

    import Antd from "ant-design-vue";
    import "ant-design-vue/dist/antd.css";
    複製代碼

    debuggernode

    import "ant-design-vue/dist/antd.less";
    
    // 報錯
    Syntax Error:
    
    // https://github.com/ant-design/ant-motion/issues/44
      .bezierEasingMixin();
    
    // 解決方案:開啓 javascript
    css: {
      loaderOptions: {
        less: {
          loader: "less-loader",
          options: {
            javascriptEnabled: true,
          },
        },
      },
    },
    複製代碼

按需引入 UI 組件

import Button from "ant-design-vue/lib/button";
import "ant-design-vue/lib/button/style";
複製代碼
  • babel-plugin-import
    • 修改 babel.config.js 文件,配置 babel-plugin-import
      module.exports = {
        presets: ["@vue/app"],
        +  plugins: [
        +    [
        +      "import",
        +      { libraryName: "ant-design-vue", libraryDirectory: "es", style: true }
        +    ]
        +  ]
        };
      複製代碼
    • src/main.js
      - import Button from 'ant-design-vue/lib/button';
      + import { Button } from 'ant-design-vue';
      - import 'ant-design-vue/dist/antd.css'
      複製代碼
    • bug
      // ❌ 沒法全局引入
      import Antd from 'antd-design-vue
      複製代碼

高擴展性的路由

  • 現有方案linux

    • 基於配置
    • 基於約定:輪子根據文件結構生成路由
  • componentwebpack

    const routes = [
      {
        path: "/user",
        component: () =>
          import(/* webpackChunkName: user */ "./component/RenderRouterView.vue"),
        children: [
          //...
        ],
      },
    ];
    複製代碼
    const routes = [
      {
        path: "/user",
        component: { render: (h) => h("router-view") },
        children: [
          //...
        ],
      },
    ];
    複製代碼
  • NProgressios

    • NProgress.start() — shows the progress bar
    • NProgress.set(0.4) — sets a percentage
    • NProgress.inc() — increments by a little
    • NProgress.done() — completes the progress

可動態改變的頁面佈局

  • 經過路由傳遞配置變量

如何將菜單和路由結合

  • 約定git

    • 在 routes 中添加 標誌位,篩選須要渲染到菜單的路由項。hideInMenu: true
    • 處理 routes 中的嵌套路由邏輯,約定name 字段才進行渲染
    • 隱藏子路由 hideChildrenMenu,處理 「頁面在子路由時,菜單依然高亮」 的邏輯
    • 添加顯示的元信息 meta,icon / title ...
  • 根據約定,生成動態菜單

    const menuData = getMenuData(this.$router.options.routes);
    getMenuData(routes){
    
    }
    複製代碼
  • 利用函數式組件(無狀態,只接受參數) + 組件遞歸,渲染處理後的 routes 對象。

  • .sync 修飾符

    • 在有些狀況下,咱們可能須要對一個 prop 進行「雙向綁定」。不幸的是,真正的雙向綁定會帶來維護上的問題,由於子組件能夠變動父組件,且在父組件和子組件都沒有明顯的變動來源。
    • 這也是爲何咱們推薦以 update:myPropName 的模式觸發事件取而代之。
    • 舉個例子,在一個包含 title prop 的假設的組件中,咱們能夠用如下方法表達對其賦新值的意圖:
      • this.$emit('update:title', newTitle)
      • 父組件
        <text-document
          v-bind:title="doc.title"
          v-on:update:title="doc.title = $event"
        ></text-document>
        複製代碼
    • 爲了方便起見,咱們爲這種模式提供一個縮寫,即 .sync 修飾符:
      <text-document v-bind:title.sync="doc.title"></text-document>
      複製代碼

如何使用路由進行權限管理

  • 權限驗證相關函數

    export async function getCurrentAuthority() {
      const { role } = await this.$axios.$get("/user");
      return ["admin"];
    }
    
    // some() 方法測試數組中是否是至少有1個元素經過了被提供的函數測試。它返回的是一個Boolean類型的值。
    export function check(authority) {
      const current = getCurrentAuthority();
      return current.some((item) => authority.includes(item));
    }
    
    export function isLogin() {
      const current = getCurrentAuthority();
      return current && current[0] !== "guest";
    }
    複製代碼
  • 路由守衛

    import findLast from "lodash/findLast";
    import { check, isLogin } from "utils/auth";
    router.beforeEach((to, from, next) => {
      // ...
      const record = findLast(to.matched, (item) => item.meta.authority);
      if (record && !check(record.meta.authority)) {
        if (!isLogin() && to.path !== "/user/login") {
          next({ path: "/user/login" });
        } else if (to.path !== "/403") {
          next({ path: "/403" });
        }
        // loading = false
        // ...
      }
      // ...
    });
    複製代碼
  • 側邊欄鑑權

    routes.forEach((item) => {
      if (item.meta && item.meta.authority && !check(item.meta.authority)) {
        return;
      }
    });
    複製代碼
  • 403 添加彈窗提醒

    import { notifiction } from "ant-deisgn-vue";
    if (to.path !== "/403") {
      notifiction.error({
        message: "403",
        description: "您沒有權限訪問該頁面,請聯繫管理員",
      });
      next({ path: "/403" });
    }
    複製代碼

更加精細的權限設計(權限組件、權限指令)

  • 權限組件 - 函數式組件

    export default {
      functional: true,
      render: function (h, context) {
        const { props, scopeSlots } = context;
        return check(props.authority) ? scopeSlots.default() : null;
      },
    };
    複製代碼
  • 權限指令 - 插件式

    export function install(Vue, options = {}) {
      const { name = "auth" } = options;
      Vue.directive(name, {
        // 當被綁定的元素插入到 DOM 中時……
        inserted: function (el, binding) {
          if (!check(binding.value)) {
            el.parentNode && el.parentNode.removeChild(el);
          }
        },
      });
    }
    複製代碼
  • 比較

    • 指令在 inserted 時 remove 後,當權限動態改變時,沒法從新添加 el。
    • 組件的響應更靈活,但使用須要嵌套目標 el。

如何在組件中使用EChartsAntv等其餘第三方庫

  • vue-echarts

    1. 插入所需的圖表組件。
    2. 抽象可配置參數。
    3. 優化(防抖)
    4. 添加更多需求(動態改變數據)
  • echart 渲染寬度超出容器?

    • 由於 echart 是在真正渲染完成前獲取高度。
    • 解決:
      • import { addListener, removeListener } from 'resize-detector'
  • resize 時,添加防抖

    • lodash/debounce
      • 該函數會從上一次被調用後,延遲wait毫秒後調用 func 方法。
      • 提供一個 cancel 方法取消延遲的函數調用以及 flush 方法當即調用
      • options.leading 與|或 options.trailing 決定延遲先後如何觸發(注:是 先調用後等待 仍是 先等待後調用)。
    • created(){
        this.resize = debounce(this.resize, 300)
      }
      複製代碼
  • 監聽 option 變化

    • 深度監聽: 耗性能( Vue3 劫持整個對象 )
      export default {
        watch: {
          option: {
            handler: () => {},
            deep: true,
          },
        },
      };
      複製代碼
    • 手動替換整個對象
      option = {...option}

如何高效使用 Mock 數據進行開發

  • 剝離 mock 數據和業務代碼
  • mock 數據不更新:清除指定模塊緩存
    • require.cache: 被引入的模塊將被緩存在這個對象中。
    • require.resolve:在 node 中,能夠使用 require.resolve 來查詢某個模塊的完整路徑
    • delete require.cache[require.resolve(name)]
  • module.exports = {
      devServer: {
        proxy: {
          "/api": {
            target: "http://localhost:8081",
            bypass: function (req, res) {
              if (req.headers.accept.indexOf("html") !== -1) {
                console.log("Skipping proxy for browser request.");
                return "/index.html";
              } else {
                // 根據約定尋找文件
                const name = req.path.split("/api/")[1].split("/").join("_");
                const mock = require(`./mock/${name}`);
                const result = mock(req.method);
                // 清理模塊緩存
                require.cache(require.resolve(`./mock/${name}`));
                return res.send(result);
              }
            },
          },
        },
      },
    };
    複製代碼

如何與服務端進行交互(Axios)

  • 添加環境變量 MOCK

    • cross-env

      • 是什麼?

        運行跨平臺設置和使用環境變量(Node 中的環境變量)的腳本。

      • 爲何須要?

        咱們在自定義配置環境變量的時候,因爲在不一樣的環境下,配置方式也是不一樣的。例如在 window 和 linux 下配置環境變量。

    • package.json

    • {
        "scripts": {
          "serve:no-mock": "cross-env MOCK=NONE "
        }
      }
      複製代碼
      const app = new (require("koa"))();
      const mount = require("koa-mount");
      
      app.use(
        mount("/api/dashboard/chart", async (ctx) => {
          ctx.body = [10, 20, 30, 40, 50];
        })
      );
      
      app.listen(8081);
      複製代碼
    複製代碼
  • axios 攔截:二次封裝,統一錯誤處理

    • request.js
      import axios from "axios";
      function request(options) {
        return axios(options)
          .then((res) => {
            return res;
          })
          .catch((error) => {
            const {
              response: { status, statusText },
            } = error;
            notifiction.error({
              message: status,
              describtion: statusText,
            });
            return Promise.reject(error);
          });
      }
      複製代碼
    • Vue.prototype.$request = request
    • jsx: @vue/babel-preset-jsx

建立一個分步表單

  • vuex: 臨時存儲表單數據
    • modules/form.js
      const state = () => ({ step: { payAccount: "" } });
      const mutation = {
        saveStepFormData(state, payload) {
          state.step = { ...state.step, ...payload };
        },
      };
      const actions = {
        async submitStepForm({ commit }, payload) {
          await request({ method: "POST", url: "", data: payload });
          // 不該該是清空表單嗎?
          commit("saveStepFormData", payload);
          router.push("");
        },
      };
      export default {
        namespaced: true,
        state,
        mutations,
        actions,
      };
      複製代碼

如何管理系統中的圖標

  • 來自 iconfont
    import { Icon } from "ant-design-vue";
    const IconFont = Icon.createFromIconfontCN({ scriptUrl: "" });
    複製代碼
    <icon-font type="icon-404" />
    複製代碼
  • svg
    • <image url>
    • 手動註冊 component / 利用 svg-loader 轉換成 component
  • 查看 vue cli 內部配置 vue inspect > output.js

如何定製主題及動態切換主題

  • 全局:config 配置

    module.exports = {
      css: {
        loaderOption: {
          less: {
            modifyVars: {
              "primary-color": "#1DA57A",
              "link-color": "#1DA57A",
              "border-radius-base": "2px",
            },
          },
        },
      },
    };
    複製代碼
  • 局部:深度做用選擇器

    若是你但願 scoped 樣式中的一個選擇器可以做用得「更深」,例如影響子組件,你能夠使用 >>> 操做符:

    <style scoped>
    .a >>> .b { /* ... */ }
    </style>
    複製代碼
  • 在線動態編譯主題色

    • 耗性能,
    • 若有需求,能夠在本地編譯好多個主題樣式文件,再從從服務端拉取
    • antd-theme-webpack-plugin
      • 該 webpack 插件用於生成特定於顏色的 less / css 並將其注入到 index.html 文件中,以便您能夠在瀏覽器中更改 Ant Design 特定的顏色主題。

國際化

  • antd-vue 組件庫國際化:localProvider -> configProvider

    <template>
      <div id="app">
        <a-config-provider :locale="locale"> </a-config-provider>
      </div>
    </template>
    複製代碼
    import zhCN from "ant-design-vue/lib/locale-provider/zh_CN";
    import enUS from "ant-design-vue/lib/locale-provider/en_US";
    
    export default = {
      data(){
        return {
          locale: enUS
        }
      },
      watch:{
        "$route.query.locale"(val){
          this.locale = val === 'enUS'? enUS : zhCN
        }
      }
    }
    複製代碼
  • moment 國際化

    import moment from 'moment';
     export default={
     watch:{
        "$route.query.locale"(val){
          moment.locale(val==='enUS'?'en':'zh_cn');
        }
      }}
    複製代碼
  • 業務代碼國際化:VueI18n

    • main.js

      import VueI18n from "vue-i18n";
      import zhCN from "./locale/zhCN";
      import enUS from "./locale/enUS";
      import queryString from "query-string";
      
      const i18n = new VueI18n({
        locale: queryString.parse(location.search).locale || "zhCN",
        message: {
          zhCN: {
            message: zhCN,
          },
          enUS: {
            message: enUS,
          },
        },
      });
      
      new Vue({
        router,
        store,
        i18n,
        render: (h) => h(App),
      }).$mount("#app");
      複製代碼
    • zhCN.js / enUS.js

      export default {
        "app.workspace.title": "時間",
      };
      複製代碼
      export default {
        "app.workspace.title": "TIME",
      };
      複製代碼
    • workspace.vue

      <template> {{$t('message')['app.workspace.title']}} </template>
      複製代碼
    • handleLocale

      export default {
        watch: {
          "$route.query.locale"(val) {
            this.$i18n.locale = val;
          },
        },
      };
      複製代碼

如何高效地構建打包方式

打包分析報告:( VUE CLI ) npm run build -- --report

  • UI 組件按需加載 / babel

  • router 中使用 webpackChunkName ,對路由進行懶加載和拆包

  • 按需引入 lodash

    1. import debounce from  'lodash/debounce'
      複製代碼
    2. 使用插件 lodash-webpack-plugin

      npm i lodash-webpack-plugin babel-plugin-lodash -D
      複製代碼

      babel.config.js

      module.exports = {
        presets: ["@vue/cli-plugin-babel/preset", "@vue/babel-preset-jsx"],
        plugins: ["lodash"],
      };
      複製代碼

      vue.config.js

      const LodashModuleReplacementPlugin = require("lodash-webpack-plugin");
      module.exports = {
        chainWebpack: (config) => {
          config
            .plugin("loadshReplace")
            .use(new LodashModuleReplacementPlugin());
        },
      };
      複製代碼
    3. lodash-es 結合 tree-shaking

      import { debounce } from 'lodash-es'
      複製代碼

      tree-shaking 的做用,即移除上下文中未引用的代碼(dead code)

      只有當函數給定輸入後,產生相應的輸出,且不修改任何外部的東西,才能夠安全作 shaking 的操做

      如何使用tree-shaking

      1. 確保代碼是 es6 格式,即 export,import

      2. package.json 中,設置 sideEffects

      3. 確保 tree-shaking 的函數沒有反作用

      4. babelrc 中設置presets [["env", { "modules": false }]] 禁止轉換模塊,交由 webpack 進行模塊化處理

      5. 結合 uglifyjs-webpack-plugin

如何構建可交互的組件文檔

  • raw-loader + highlightjs main.js
    import hljs from "highlight.js";
    import "highlight.js/styles/github.css";
    Vue.use(hljs.vuePlugin);
    複製代碼
    view.vue
    <highlightjs language="javascript" :code="ChartCode" />
    複製代碼
  • 本身編寫 loader:如 md-loader(成本高)

如何作好組件的單元測試

  • auth.spec.js

    import { authCurrent, check } from "@/utils/auth.js";
    
    describe("auth test", () => {
      it("empty auth", () => {
        authCurrent.splice(0, authCurrent.length);
        expect(check(["user"])).toBe(false);
        expect(check(["admin"])).toBe(false);
      });
    });
    複製代碼
  • jest.config.js

    module.exports = {
      preset: "@vue/cli-plugin-unit-jest",
      moduleNameMapper: {
        "^@/(.*)$": "<rootDir>/src/$1",
      },
      resolver: null,
      collectCoverage: process.env.COVERAGE === "true",
      collectCoverageFrom: ["src/**/*.{js,vue}", "!**/node_modules/**"],
    };
    複製代碼

如何發佈組件到 NPM

  • 註冊 npm 帳號,填寫 用戶名、密碼和郵箱;
  • 進入項目文件夾
  • 使用 npm login,登陸本身的 npm 帳號;
  • 使用 npm publish,·發佈本身的包到 npm;
  • 查看本身發佈的包是否成功,能夠去別的項目執行 npm install 你發佈的包名,下載成功。

注意

  1. 發佈本身包以前,應先去 npm 官網搜索本身要發佈的包名是否已經存在,已存在的包名會提交失敗;
  2. 本身發佈的包更新時,每次都要到 package.json, 將 version 修改,例如:從 1.0.0 改成 1.0.1。而後再執行 npm publish 更新;

GitHub相關生態應用(CI 持續集成、單車覆蓋率、文檔發佈、issue管理)

相關文章
相關標籤/搜索