從0到1搭建自己的元件(vue-code-view)庫(上)

Anduril發表於2021-11-02

0x00 前言

本文將從結構、功能等方面講解下專案 vue-code-view 的搭建過程,您可以瞭解以下內容:

  • 使用 vue cli 4從0搭建一個元件庫及細緻配置資訊。
  • 專案的多環境構建配置。
  • 專案網站的釋出部署、持續整合。
  • 專案NPM包釋出。
  • 專案元件的自定義 Markdown 解析 loader

本文算是 ?Element 2 原始碼學習系列 的擴充篇內容,通過之前的文章瞭解了開源元件庫的結構原理後,自己也搭建個元件專案怎麼辦?接下來就是實踐的過程,算是"知行合一"了吧! 耐心讀完,相信會對您有所幫助!


0x01 專案概述

建立專案

本專案使用 Vue CLI 4.x 進行專案建立。

// 安裝環境 
npm install -g @vue/cli 
// 建立專案 
vue create vue-code-view

在終端中輸入命令後,按照以下步驟操作建立專案:

  1. 選擇手動選擇功能 Manually select features
  2. 選中 Babel, Router, CSS Pre-processors,Linter / Formatter等功能 。
  3. 選擇 vue 版本 2.X
  4. 選擇路由是否使用history模式,預設 yes
  5. 選擇CSS 前處理器Sass/SCSS(with node-sass)
  6. 選擇程式碼風格、格式校驗 linter / formatter 配置ESLint + Prettier
  7. 選擇校驗時機儲存時檢測Lint on save
  8. 選擇 Babel, ESLint等配置檔案存放在專用配置檔案中 In dedicated config files

配置多環境變數

在專案根目錄中新建 .env, .env.deploy,.env.production等檔案。

VUE_APP 開頭的變數會被 webpack.DefinePlugin 靜態嵌入到客戶端側的包中,程式碼中可以通過 process.env.VUE_APP[xxx] 訪問。

NODE_ENVBASE_URL 是兩個特殊變數,在程式碼中始終可用。

.env
vue-cli-service serve 預設的本地開發環境配置

NODE_ENV = development
VUE_APP_PUBLIC_PATH = /

.env.production
vue-cli-service build 預設的環境配置(正式伺服器)

NODE_ENV = production 
VUE_APP_PUBLIC_PATH = /
VUE_APP_ENV = pub

.env.deploy
用於 github pages 構建部署的環境配置。VUE_APP_PUBLIC_PATH 設定 /vue-code-view 用於虛擬目錄。

NODE_ENV = production 
VUE_APP_PUBLIC_PATH = /vue-code-view
VUE_APP_ENV = deploy

目錄結構調整

預設的 src 目錄下存放專案原始碼及需要引用的資原始檔。根目錄下新建 examples 資料夾用於專案示例網站,將src 目錄下檔案移至 examples 檔案 。src 目錄存放專案元件原始碼。

調整後根目錄檔案結構如下:

├── examples   // 專案示例網站
|  ├── App.vue
|  ├── assets
|  ├── components
|  ├── main.js
|  ├── router
|  └── views 
├── src       // 專案元件原始碼 
|  ├── fonts
|  ├── index.js
|  ├── locale
|  ├── mixins
|  ├── src
|  ├── styles
|  └── utils
├── public
├── package.json

配置基礎 vue.config.js

專案預設入口./src/main.js,配置如下:

{
  entry: {
    app: [
      './src/main.js'
    ]
  }
} 

在根目錄下建立 vue.config.js 修改預設配置。

const path = require("path");
const resolve = (dir) => path.join(__dirname, dir);

module.exports = {
  configureWebpack: (config) => {
    // 專案入口
    config.entry.app = "./examples/main.js";
  },
  chainWebpack: (config) => {
    // 新增別名
    config.resolve.alias
      .set("vue$", "vue/dist/vue.esm.js")
      .set("@", resolve("examples"))
      .set("@assets", resolve("examples/assets"))
      .set("@src", resolve("src"))
      .set("@views", resolve("examples/views"))
      .set("@router", resolve("examples/router"))
      .set("@store", resolve("examples/store")); 
  },
}; 

執行 npm run serve , 專案網站正常執行。

0x02 專案構建

npm scripts 配置

調整 package.json 裡的 scripts 配置指令碼,並新增 --mode xxx 來選擇不同環境配置。

"scripts": { 
  // 開發環境 啟動專案示例網站
  "dev": "vue-cli-service serve ", 
  // 元件庫構建 
  "dist": "vue-cli-service build  --mode production --target lib --name vue-code-viewer --dest lib src/index.js",
  // 專案示例網站構建
  "deploy:build": "vue-cli-service build --mode deploy" 
}

元件構建

元件庫構建通過指定入口檔案src/index.js、設定引數選項。

用法:vue-cli-service build [options] [entry|pattern]

引數選項:
  --mode        指定環境模式 (預設值:production)
  --dest        指定輸出目錄 (預設值:dist) 
  --target      app | lib | wc | wc-async (預設值:app)
  --name        庫或 Web Components 模式下的名字 (預設值:package.json 中的 "name" 欄位或入口檔名) 

構建一個庫會輸出:

  • lib/vue-code-viewer.common.js:一個給打包器用的 CommonJS 包 。
  • lib/vue-code-viewer.umd.js:一個直接給瀏覽器或 AMD loader 使用的 UMD 包。
  • lib/vue-code-viewer.umd.min.js:壓縮後的 UMD 構建版本。
  • lib/vue-code-viewer.css:提取出來的 CSS 檔案。

元件NPM包釋出

配置 package.json 檔案中屬性值用於npm 釋出。

  • name: 包名,該名字是唯一的。需要去npm registry檢視名字是否已被使用。
  • version: 包版本號,版本號規則參考《語義化版本 2.0.0》。每次釋出至 npm 需要修改版本號,不能和歷史版本號相同。
  • description: 包的描述,描述這個包的主要功能以及用途。
  • main: 入口檔案,該欄位需指向專案編譯後的包檔案。
  • unpkg: UMD模式入口包檔案
  • keyword:關鍵字,陣列、字串。
  • author:包的作者。
  • private:是否私有,需要修改為 false 才能釋出到 npm
  • license: 開源協議。
  • repository:包的Git Repo資訊,包括type和URL。
  • homepage:專案官網的url。

更新 package.json 檔案內容:

{
  "name": "vue-code-view",
  "version": "0.3.9",
  "description": "A code editor component based on Vue.js 2 ",
  "main": "lib/vue-code-viewer.common.js",
  "unpkg": "lib/vue-code-viewer.umd.js",
  "scripts": {},
  "dependencies": {},
  "peerDependencies": {},
  "devDependencies": {},
  "keywords": [
    "vue",
    "components",
    "codemirror",
    "code editor"
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/andurils/vue-code-view.git"
  },
  "author": "andurils",
  "license": "MIT",
  "homepage": "https://andurils.github.io/vue-code-view"
}

.npmignore 檔案,設定忽略釋出檔案。釋出到 npm 中檔案,只保留有的 lib 目錄、package.json、README.md。

# 忽略目錄 
examples/
node_modules/
public/
build/
src/
dist/
deploy/
# 忽略指定檔案 
.browserslistrc 
.eslintignore 
.prettierignore 
.env
.env.*
*.js  

接下來 npmjs.com 上註冊賬號登入後,執行 npm publish 命令完成元件包釋出操作。

釋出成功後,進入元件的NPM首頁  npm/vue-code-view, 可以看到上面的專案配置資訊 。

image.png

專案示例網站構建

更新 vue.config.js,執行 npm run deploy:build 構建專案示例網站輸出至 deploy 目錄下。

const path = require("path"); 
const resolve = (dir) => path.join(__dirname, dir); 
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV);

module.exports = { 
  publicPath: process.env.VUE_APP_PUBLIC_PATH || "/",
  productionSourceMap: false,
  outputDir: process.env.VUE_APP_ENV === "deploy" ? "deploy" : "dist",  
  configureWebpack: (config) => {
    // 專案入口
    config.entry.app = "./examples/main.js"
  },
  ...
};

持續整合

使用 Travis CI的持續整合服務自動構建專案示例網站並部署至 gh-pages 分支 。

根目錄下新增 .travis.yml檔案,指定了 Travis 的行為。通過設定一旦程式碼倉庫有新的 Commit,Travis 就會去找這個檔案,執行裡面的命令(執行構建、部署等操作)。
y

sudo: false
language: node_js
node_js: 14
install:
  - yarn 

script: npm run deploy:build
after_script: 
  - mkdir temp_web 
  - cd temp_web
  - git clone --depth 1 -b gh-pages --single-branch https://${TravisCI_Token}@${GH_REF} && cd vue-code-view 
  - echo 'copy && commit' 
  - rm -rf js fonts
  - cp -rf ../../deploy/** .
  - git config user.name "${U_NAME}"
  - git config user.email "${U_EMAIL}"
  - git add -A .
  - git commit -m ":construction_worker:- Build & Deploy by Travis CI"
  - git push origin gh-pages  
  - echo "DONE, Bye~"
  - exit 0

Travis CI專案構建後臺:

image.png

開啟構建壓縮

安裝相關外掛。

# gzip webpack 4.x 對應 6.x版本 
npm i compression-webpack-plugin@6.1.1 -D 

# 程式碼壓縮
npm i uglifyjs-webpack-plugin -D

配置 vue.config.js,開啟外掛。

...
const IS_PROD = ["production", "prod"].includes(process.env.NODE_ENV); 
// gzip壓縮  webpack 4.x 對應 6.x版本 cnpm i compression-webpack-plugin@6.1.1 --save-dev
const CompressionWebpackPlugin = require("compression-webpack-plugin");
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
// 程式碼壓縮
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");

module.exports = { 
  ...
  configureWebpack: (config) => { 
    const plugins = []; 
    // 生產環境相關配置
    if (IS_PROD && process.env.VUE_APP_ENV === "pub") {
      plugins.push(
        new CompressionWebpackPlugin({
          filename: "[path][base].gz",
          algorithm: "gzip",
          minRatio: 0.99,
          test: productionGzipExtensions,
          deleteOriginalAssets: false,
        })
      );
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            // 刪除註釋
            output: {
              comments: false,
            },
            // 刪除console debugger
            compress: {
              drop_debugger: true,
              drop_console: true, // console
              pure_funcs: ["console.log"], // 移除console
            },
            // 刪除警告
            warnings: false,
          },
          sourceMap: false,
          parallel: true, //使用多程式並行執行來提高構建速度。預設併發執行數:os.cpus().length - 1。
        })
      );
    }
    config.plugins = [...config.plugins, ...plugins];
  },  
};

元件說明文件

參考element 2的實現,自定義 build/md-loderMarkdown 檔案進行解析渲染,將 examples\docs\zh-CN\example.md 編譯為 HTML。已在前文 04.封裝元件封裝、編寫說明文件 中詳細說明,不再過多贅述。

配置 vue.config.js ,新增 .md 文件的自定義 Markdown Loader

module.exports = { 
  ...
  configureWebpack: (config) => { 
    config.resolveLoader.modules = ["node_modules", "./build/"]; // 自定義loader
  },
  chainWebpack: (config) => { 
    // Markdown Loader
    config.module
      .rule("md")
      .test(/\.md/)
      .use("vue-loader")
      .loader("vue-loader")
      .end()
      // 自定義loader
      .use("md-loader")
      .loader("md-loader")
      .end(); 
  },
}; 

examples\router\index.js配置路由:

const routes = [  
  {
    path: "/md",
    name: "Markdown",
    component: (r) =>
      require.ensure([], () => r(require("../docs/zh-CN/example.md"))),
  },
];

0x03 結尾

本篇幅主要按照專案從簡到繁的順尋逐步講解了各操作項及細節配置。下文將就元件實現原理詳細講解。

未完待續~

相關文章