资讯专栏INFORMATION COLUMN

vue控件库环境搭建(1)--统一打包

freecode / 1614人阅读

摘要:当然如果使用的话,使用来效验提交的代码最后,需要修改演示环境在该环境下创建一个文件这边主要和英文版中文版框架库框架库选择语言简体中文主页面配置别名路径配置别名路径

背景:为了避免重复造轮子,很有必要开发一个通用组件库,方便重复利用。
本文是采用vue-lic3.0脚手改造而成的,使用vuepress作为演示环境。

首先通过vue脚手架生产目录如下:


然后我们需要修改一下:删除public目录,添加packages,utils和lib文件,见如下:

packages是用于放控件库, utils是用于放一些通用工具(方法等),src用于整个控件的引用,lib用于放打包后的文件:
src下放如下内容


index.js是将所有控件引入,作为打包的入口

import locale from "hui-pro/src/locale"; // 语言包

import "hui-pro/packages/theme/index.scss"; // 皮肤包,将主题抽离出来
const components = [] // 放控件库
const install = function(Vue, opts = {}) {
  /* istanbul ignore if */
  if (install.installed) return;
  locale.use(opts.locale);
  locale.i18n(opts.i18n);

  components.map(component => {
    Vue.component(component.name, component);
  });

  Vue.use(EllipsisDirective);
};

/* istanbul ignore if */
if (typeof window !== "undefined" && window.Vue) {
  install(window.Vue);
}

export default {
  version: "0.1.0",
  locale: locale.use,
  i18n: locale.i18n,
  install,
  ... //控件
}

src下locale是用于管理多语言的,我们在开发控件的时候是不能讲中文写死,所以需要做一层多语言处理,而单纯的控件库有是没有引入vue实例,那就更不用说i18n了,所以这边就是通过自己翻译,引用项目工程中的i18n或者直接自己做切割翻译。如下:
index.js

import defaultLang from "hui-pro/src/locale/lang/zh-CN";
import Vue from "vue";
import deepmerge from "deepmerge";
import Format from "./format";

const format = Format(Vue);
let lang = defaultLang;
let merged = false;
let i18nHandler = function() {
  const vuei18n = Object.getPrototypeOf(this || Vue).$t;
  if (typeof vuei18n === "function" && !!Vue.locale) {
    if (!merged) {
      merged = true;
      Vue.locale(
        Vue.config.lang,
        deepmerge(lang, Vue.locale(Vue.config.lang) || {}, { clone: true })
      );
    }
    return vuei18n.apply(this, arguments);
  }
};

export const t = function(path, options) {
  let value = i18nHandler.apply(this, arguments);
  if (value !== null && value !== undefined) return value;

  const array = path.split(".");
  let current = lang;

  for (let i = 0, j = array.length; i < j; i++) {
    const property = array[i];
    value = current[property];
    if (i === j - 1) return format(value, options);
    if (!value) return "";
    current = value;
  }
  return "";
};

export const use = function(l) {
  lang = l || lang;
};

export const i18n = function(fn) {
  i18nHandler = fn || i18nHandler;
};

export default { use, t, i18n };

format.js 这个是通过自己本地切割字符串来找对应的翻译: "xxx.yyy.ccc" => xxx: {yyy: {ccc: "翻译次"}}

const RE_NARGS = /(%|){([0-9a-zA-Z_]+)}/g;
/**
 *  String format template
 *  - Inspired:
 *    https://github.com/Matt-Esch/string-template/index.js
 */
export default function() {
  /**
   * template
   *
   * @param {String} string
   * @param {Array} ...args
   * @return {String}
   */

  function template(string, ...args) {
    if (args.length === 1 && typeof args[0] === "object") {
      args = args[0];
    }

    if (!args || !args.hasOwnProperty) {
      args = {};
    }

    return string.replace(RE_NARGS, (match, prefix, i, index) => {
      let result;

      if (string[index - 1] === "{" && string[index + match.length] === "}") {
        return i;
      } else {
        result = Object.prototype.hasOwnProperty.call(args, i) ? args[i] : null;
        if (result === null || result === undefined) {
          return "";
        }

        return result;
      }
    });
  }

  return template;
}

然后在mixins中加入一个使用多语言的方法,主要是这个方法是一样的,所以提取到mixins
mixins/locale.js

import { t } from "hui-pro/src/locale";

export default {
  methods: {
    t(...args) {
      return t.apply(this, args);
    }
  }
};

之后控件库中可以直接引用mixis,t(), this.t()的形式使用了

import Locale from "hui-pro/src/mixins/locale";
mixins: [Locale]

使用
html中
{{t("aa.bb")}}
js中使用
test () {
    this.t("aa.bb")
}

接下来我们还需要修改一些配置:
babel.config.js

const utilTypeList = []// 对外暴露工具库的列表

module.exports = function(api) {
  let presets = ["@vue/app"];
  let plugins = [];

  if (api.env("js")) {
    presets = [["@babel/preset-env", { loose: true }]];
    plugins = [
      [
        "module-resolver",
        {
          root: ["hui-pro"],
          alias: {
            "hui-pro/src": "hui-pro/lib"
          }
        }
      ]
    ];
  } else if (api.env("node")) {
    presets = [
      [
        "@babel/preset-env",
        {
          targets: {
            node: true
          }
        }
      ]
    ];
  }
  // 按需加载utils
  for (let type of utilTypeList) {
    plugins.push([
      "import",
      {
        libraryName: `hui-pro/packages/utils/${type}`,
        libraryDirectory: ""
      },
      `${type}`
    ]);
  }
  return {
    presets,
    plugins
  };
};

需要安装eslintrc.js
增加配置文件

var isDev = process.env.NODE_ENV === "development";

module.exports = {
  root: true,
  env: {
    mocha: true,
    es6: true,
    node: true,
    browser: true
  },
  parserOptions: {
    parser: "babel-eslint"
  },
  plugins: ["vue"],
  extends: ["plugin:vue/strongly-recommended", "@vue/prettier"],
  rules: {
    "vue/html-indent": 1,
    "no-console": isDev
      ? 0
      : [
          "error",
          {
            allow: ["warn", "error"]
          }
        ],
    "no-debugger": isDev ? 0 : 2
  },
  globals: {
    expect: true,
    sinon: true
  }
};

还需要增加stylelintrc效验

{
  "plugins": ["stylelint-prettier", "stylelint-scss"],
  "extends": [
    "stylelint-config-idiomatic-order",
    "stylelint-config-standard",
    "stylelint-config-prettier"
  ],
  "rules": {
    "at-rule-no-unknown": null,
    "scss/at-rule-no-unknown": true,
    "prettier/prettier": true
  }
}

最后增加一下vue的配置

const path = require("path");
const nodeExternals = require("webpack-node-externals");

module.exports = {
  publicPath: "",
  outputDir: "lib",
  assetsDir: "",
  filenameHashing: false,
  css: {
    extract: true,
    sourceMap: false
  },
  productionSourceMap: false,

  pages: {
    index: {
      // page 的入口
      entry: "preview/main.js",
      // 模板来源
      template: "preview/index.html"
    }
  },

  devServer: {
    port: 8999
  },

  configureWebpack() {
    if (process.env.LIB_TYPE === "common") {
      return {
        externals: [
          {
            vue: "vue",
            "hui-pro/src/locale": "hui-pro/lib/locale"
          },
          nodeExternals()
        ]
      };
    }
  },

  chainWebpack(webpackConfig) {
    webpackConfig.when(process.env.LIB_TYPE === "umd", config => {
      config.output.umdNamedDefine(true);
    });

    webpackConfig.resolve.alias.set("hui-pro", path.resolve(__dirname));

    //svg
    const svgRule = webpackConfig.module.rule("svg");
    svgRule.uses.clear();
    svgRule
      .oneOf("svg")
      .resourceQuery(/svg/)
      .use("vue-svg-loader")
      .loader("vue-svg-loader")
      .end()
      .end()
      .oneOf("img")
      .resourceQuery(/img/)
      .use("url-loader")
      .loader("url-loader")
      .options({
        name: "img/[name].[hash:8].[ext]"
      })
      .end()
      .end()
      .oneOf()
      .use("file-loader")
      .loader("file-loader")
      .options({
        name: "fonts/[name].[ext]"
      });

    webpackConfig.module
      .rule("fonts")
      .use("url-loader")
      .tap(options => Object.assign(options, { limit: 10 }));

    // map-picker控件中,map/下的代码是openlayer相关代码,无需babel编译。
    webpackConfig.module
      .rule("js")
      .test(/.js$/)
      .exclude.add(path.resolve(__dirname) + "packages/map-picker/src/map")
      .end()
      .use("babel-loader");
    //stylelint
    webpackConfig
      .plugin("stylelint")
      .use("stylelint-webpack-plugin")
      .tap(() => {
        return [
          {
            configFile: ".stylelintrc",
            files: ["packages/**/*.scss"],
            emitErrors: true
          }
        ];
      });
  }
};

当然如果使用git的话,使用commitlint来效验提交的代码

{
  "printWidth": 80,
  "tabWidth": 2,
  "singleQuote": true,
  "trailingComma": "none",
  "bracketSpacing": true,
  "semi": true,
  "useTabs": false,
  "proseWrap": "never",
  "overrides": [
    {
      "files": [
        "*.json",
        ".eslintrc",
        ".babelrc",
        ".stylelintrc",
        ".prettierrc"
      ],
      "options": {
        "parser": "json",
        "tabWidth": 2
      }
    }
  ]
}

最后,需要修改package.json

{
  "name": "hui-pro",
  "version": "0.1.0-alpha.1",
  "scripts": {
    "lint": "vue-cli-service lint packages src && stylelint packages/**/*.scss --fix",
    "cz:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
    "dev": "vue-cli-service serve",
    "lib:all": "npm run lib:clean && npm run lib:i18n && npm run lib:common && npm run lib:umd && npm run lib:utils",
    "lib:clean": "rimraf lib",
    "lib:common": "cross-env LIB_TYPE=common vue-cli-service build --no-clean --target lib --formats commonjs --name hui-pro src/index.js",
    "lib:i18n": "cross-env NODE_ENV=js babel src/locale --out-dir lib/locale",
    "lib:utils": "cross-env NODE_ENV=node babel-node bin/generateIndex && cross-env NODE_ENV=js babel packages/utils --out-dir utils",
    "lib:umd": "cross-env LIB_TYPE=umd vue-cli-service build --no-clean --target lib --formats umd-min --name hui-pro src/index.js",
    "vuepress:dev": "vuepress dev docs"
  },
  "dependencies": {
    "jsencrypt": "^2.3.1",
    "hui": "^2.0.0-alpha.4",
    "moment": "^2.24.0",
    "qs": "^6.5.2"
  },
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/node": "^7.2.2",
    "@commitlint/cli": "^7.2.0",
    "@commitlint/config-conventional": "^7.5.0",
    "@vue/cli-plugin-babel": "^3.3.0",
    "@vue/cli-plugin-eslint": "^3.3.0",
    "@vue/cli-service": "^3.3.0",
    "@vue/eslint-config-prettier": "^4.0.1",
    "babel-eslint": "^10.0.1",
    "babel-plugin-import": "^1.11.0",
    "babel-plugin-module-resolver": "^3.1.3",
    "commitizen": "^3.0.5",
    "conventional-changelog": "^3.0.5",
    "cross-env": "^5.2.0",
    "cz-customizable": "^5.2.0",
    "eslint": "^5.8.0",
    "eslint-plugin-vue": "^5.0.0",
    "highlightjs": "^9.12.0",
    "husky": "^1.1.1",
    "ip": "^1.1.5",
    "lint-staged": "^8.1.3",
    "node-sass": "^4.11.0",
    "prettier-eslint": "^8.8.2",
    "prettier-stylelint": "^0.4.2",
    "sass-loader": "^7.1.0",
    "stylelint": "^9.10.1",
    "stylelint-config-idiomatic-order": "^6.2.0",
    "stylelint-config-prettier": "^5.0.0",
    "stylelint-config-standard": "^18.2.0",
    "stylelint-prettier": "^1.0.6",
    "stylelint-scss": "^3.5.1",
    "stylelint-webpack-plugin": "^0.10.5",
    "vue": "^2.5.21",
    "vue-cli-plugin-changelog": "^1.1.9",
    "vue-cli-plugin-lint-staged": "^0.1.1",
    "vue-router": "^3.0.1",
    "vue-svg-loader": "^0.12.0",
    "vue-template-compiler": "^2.5.21",
    "webpack-node-externals": "^1.7.2"
  },
  "postcss": {
    "plugins": {
      "autoprefixer": {}
    }
  },
  "browserslist": [
    "Chrome > 48",
    "Edge > 16",
    "Firefox > 62",
    "IE > 9",
    "Safari > 11"
  ],
  "commitlint": {
    "extends": [
      "@commitlint/config-conventional"
    ]
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"
    }
  },
  "files": [
    "lib",
    "src",
    "packages",
    "utils"
  ],
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "post-merge": "npm install",
      "pre-commit": "lint-staged"
    }
  },
  "main": "lib/hui-pro.common.js"
}
vuepress演示环境

在该环境下创建一个docs文件


这边主要config.js和enhanceApp.js
config.js:

var path = require("path");
var ip = require("ip");
var enNav = require("./links/en.nav.json");
var zhNav = require("./links/zh.nav.json");
var enSidebar = require("./links/en.sidebar.json"); // 英文版
var zhSidebar = require("./links/zh.sidebar.json"); // 中文版

var webpackConfig = {
  module: {
    rules: [
      {
        test: /.js$/,
        loader: "babel-loader",
        include: [path.resolve(__dirname, "../../packages")]
      }
    ]
  }
};

module.exports = {
  base: "/lib/",
  host: ip.address(),
  port: "8099",
  title: "lib",
  description: "框架库",
  locales: {
    "/zh/": {
      lang: "zh-CN",
      title: "test-lib",
      description: "框架库"
    },
    "/en/": {
      lang: "en-US",
      title: "test-lib",
      description: "Library"
    }
  },
  head: [
    [
      "link",
      {
        rel: "icon",
        href: `favicon.ico`
      }
    ]
  ],
  themeConfig: {
    editLinks: true,
    docsDir: "docs",
    locales: {
      "/zh/": {
        selectText: "选择语言",
        label: "简体中文",
        nav: zhNav,
        sidebar: zhSidebar
      },
      "/en/": {
        selectText: "Languages",
        label: "English",
        nav: enNav,
        sidebar: enSidebar
      }
    }
  },
  dest: "./docs/.vuepress/dist",
  demo: {
    menu: [
      {
        title: "主页面",
        router: "/layout/page.html",
        icon: "h-icon-menu_app"
      }
    ]
  },
  scss: {
    sourceMap: true
  },
  sass: {
    indentedSyntax: true
  },
  configureWebpack: webpackConfig,
  overlay: {
    warnings: true,
    errors: true
  },
  chainWebpack: (webpackConfig, isServer) => {
    webpackConfig.resolve.alias.set(
      "name1",
      path.resolve(__dirname, "../../")
    ); // 配置别名路径

    webpackConfig.resolve.alias.set(
      "name2",
      path.resolve(__dirname, "../../src")
    ); // 配置别名路径

    webpackConfig.module
      .rule("eslint")
      .pre()
      .exclude.add(/node_modules/)
      .end()
      .include.add(path.resolve(__dirname, "../../src"))
      .add(path.resolve(__dirname, "../../packages"))
      .end()
      .test(/.(vue|(j|t)sx?)$/)
      .use("eslint-loader")
      .loader("eslint-loader")
      .options({
        extensions: [".js", ".jsx", ".vue", ".ts", ".tsx"],
        cache: true,
        emitWarning: true,
        emitError: true,
        formatter: require("eslint/lib/formatters/codeframe")
      });

    //svg
    const svgRule = webpackConfig.module.rule("svg");
    svgRule.uses.clear();
    svgRule
      .oneOf("svg")
      .resourceQuery(/svg/)
      .use("vue-svg-loader")
      .loader("vue-svg-loader")
      .end()
      .end()
      .oneOf("img")
      .resourceQuery(/img/)
      .use("url-loader")
      .loader("url-loader")
      .options({
        name: "img/[name].[hash:8].[ext]"
      })
      .end()
      .end()
      .oneOf()
      .use("file-loader")
      .loader("file-loader")
      .options({
        name: "fonts/[name].[ext]"
      });

    //stylelint
    webpackConfig
      .plugin("stylelint")
      .use(require.resolve("stylelint-webpack-plugin"))
      .tap(() => {
        return [
          {
            configFile: ".stylelintrc",
            files: ["packages/**/*.scss"],
            emitErrors: true
          }
        ];
      });
  }
};

enhanceApp.js:

import name from "name2"
import routerGuard from "./router"

import *.css"

export default ({
  Vue,
  router
}) => {
  routerGuard.use(router)
  Vue.use(name)
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/53875.html

相关文章

  • vue控件环境搭建(1)--统一打包

    摘要:当然如果使用的话,使用来效验提交的代码最后,需要修改演示环境在该环境下创建一个文件这边主要和英文版中文版框架库框架库选择语言简体中文主页面配置别名路径配置别名路径 背景:为了避免重复造轮子,很有必要开发一个通用组件库,方便重复利用。本文是采用vue-lic3.0脚手改造而成的,使用vuepress作为演示环境。 首先通过vue脚手架生产目录如下: showImg(https://seg...

    Scorpion 评论0 收藏0
  • 一张脑图看懂BUI Webapp移动快速开发框架【上】--框架与工具、资源

    摘要:后续我们还会增加一些实战类的移动开发案例,欢迎关注专栏。进入官网新版预览在线预览需要使用开启设备模拟,效果更佳。 前言 之前写过一篇 2018开发最快的Webapp框架--BUI交互框架 ,如果你还没看过,可以简单看一下,主要介绍了BUI的基本功能,有多少控件,以及实现的思路,BUI 1.5版本以后变化很大,统一新的风格,新的规范750,新增基于Dom的数据驱动,完善了页面的生命周期等...

    wuyumin 评论0 收藏0
  • 第一集: 从零开始实现一套pc端vue的ui组件(环境搭建)

    摘要:第一集从零开始实现环境的搭建工程定位本套工程定位在端针对的组件库名字的由来是我从年养到现在的一直大金毛是我的吉祥物原因本人上一份工作参与了大型的保险公司后台管理系统的搭建对的端框架有过一定的了解感受到了他们真的很强大同时也存在少许的不足其实 第一集: 从零开始实现(环境的搭建) 工程定位: 本套工程, 定位在pc端针对vue的ui组件库 名字的由来 cc是我从2015年养到现在的...

    Ashin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<