利用webpack打包自己的第一個Vue組件庫


  先說一下這篇文章的誕生原因。我們有一個這樣的項目,類似或者說就是一個儀表板-Dashboard,其中的各個部分可能不是一個部門寫的……我們需要提供拖拽布局(大小和位置)和展示的能力。要實現這樣一個功能,想了好幾種方式實現(后面的筆記詳說),最后選擇了這篇筆記的實現方式:寫整個項目的,算是使用方;寫每個組件的,算是vue類庫(UI、組件庫)的提供方。之后就是我們如何使用這些類庫的問題了,就像我們使用element-ui一樣,這樣說就明白了吧!這里不說父子之間如何通信以及如何使用類庫,只說如何打包類庫。

  之前總是使用別人的類庫了,沒有自己寫過,今天就着這個機會研究了有了一下,demo算是跑通了,深入的就需要之后慢慢學習了。

  開始還是看了一下element-ui是如何將所有的組件打包到一個JS文件,並且可以CDN方式使用的,發現和我們寫一個單獨的.vue單文件組件沒有什么區別,主要是webpack的output和入口文件的寫法有些不同,其他的都大同小異,先看一下output

var path = require('path');
var VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
    mode: 'development', // production|development  // https://segmentfault.com/a/1190000013712229
    entry: "./index.js",
    output: {
        path: path.resolve(__dirname, './dist'),
        publicPath: '/dist/',
        filename: 'ddztestlib01.js',
        library: 'ddztestlib01',
        libraryTarget: 'umd',
        libraryExport: 'default',
        umdNamedDefine: true,
        // globalObject: `(typeof self !== 'undefined' ? self : this)`, // https://stackoverflow.com/questions/49111086/webpack-4-universal-library-target
        globalObject: 'typeof self !== \'undefined\' ? self : this' // element-ui 寫法
    },
    module: {
        rules: [{
            test: /\.vue$/,
            loader: 'vue-loader'
        }, {
            test: /\.css$/,
            loader: 'css-loader'
        }, {
            test: /\.less$/,
            loader: 'style-loader!css-loader!less-loader'
        }]
    },
    devtool: "source-map",
    resolve: {
        alias: {
            'vue': 'vue/dist/vue.js'
        }
    },
    plugins: [
        new VueLoaderPlugin()
    ]
}
webpack.config.js

  這里主要說一下,libraryTarget、libraryExport、umdNamedDefine和globalObject

  1、libraryTarget:打包類庫的發布格式,這里使用UMD,其他選項不解釋(其實是……)

  2、libraryExport:這個選項同樣不知道干什么的,但是我遇到了一個問題就是開始沒有添加這個選項(雖然看了element-ui的打包,但是我給過濾了),導致使用的時候發現有雙層的“default”,因為不是很了解,所以查了一些資料先看看,卻發現和這篇文章說的一樣:webpack組織模塊打包Library的原理及實現,后來發現該文中使用的選項過期了,之后還是又看了一遍element-ui 才搞定,這一大圈

  3、umdNamedDefine:這個還是同上,但是添加和不添加這個選項比較一下生成文件你就知道了

  4、globalObject:這個是真不知道了,但是在stackoverflow中無意發現說這是個Bug,地址:https://stackoverflow.com/questions/49111086/webpack-4-universal-library-target

  現在看來webpack配置文件處理output某些屬性和我們正常開發沒有什么區別,下面看一下他的入口文件:

//  1、這里導入需要導出的組件,統一處理
import DDZComponent01 from './src/components/DDZComponent01.vue';
import DDZComponent02 from './src/components/DDZComponent02.vue';
//      1.1、書寫Vue插件(保證只引入某一個組件時可用),https://cn.vuejs.org/v2/guide/plugins.html
DDZComponent01.install = function (Vue) {
    Vue.component(DDZComponent01.name, DDZComponent01);
};
DDZComponent02.install = function (Vue) {
    Vue.component(DDZComponent02.name, DDZComponent02);
};

//  2、遍歷注冊所有的組件(依賴),全局時使用
const components = [
    DDZComponent01,
    DDZComponent02
];
const install = function (Vue, opts = {}) {
    components.forEach(component => {
        Vue.component(component.name, component);
    });
    // 這里除了注冊組件,還可以做一些其他的東西
    // 你可以在Vue的原型上擴展一些方法
    // eg:element-ui
    //      Vue.prototype.$message = Message;
    //      使用:this.$message({message:"xxxxx",type:"success"});
};

//      可以根據實際情況,是否需要這段代碼(CDN引入,便可使用所有組件)
if (typeof window !== 'undefined' && window.Vue) {
    install(window.Vue);
}
//  3、導出類庫的版本、組件、Vue插件需要暴露的install方法
export default {
    version: '0.0.1',
    install,
    DDZComponent01,
    DDZComponent02
};

//    4、使用方式
//      4.1、使用部分組件
//            4.1.1、
//                import { DDZComponent01 } from '……/ddztestlib01.js';
//                局部注冊:components: { ddzcomponent01: DDZComponent01 },
//                全局注冊:Vue.use(DDZComponent01); //這種寫法需要對應的組件暴露install方法
//            4.1.2、
//                import * as ddztestlib01 from '……/ddztestlib01.js'; // 這里的書寫方式應該和導出的寫法有關系
//                局部注冊:components: { ddzcomponent01: ddztestlib01.DDZComponent01 },
//                全局注冊:Vue.use(ddztestlib01.DDZComponent01); //這種寫法需要對應的組件暴露install方法
//        4.2、使用類庫中的所有組件
//          4.2.1、
//                import * as ddztestlib01 from '……/ddztestlib01.js'; // 這里的書寫方式應該和導出的寫法有關系
//                Vue.use(ddztestlib01); //這里的使用就是調用對象的install方法
//          4.2.2、cdn方式使用
//              <script src="……/ddztestlib01.js"></script> //如果window.Vue存在,則自動注冊全部組件
//      4.3、使用systemjs異步加載(測試版本:SystemJS 3.1.6)
//          加載之后,返回的是該類庫的默認導出對象:{default:{version:,install:,……}}。這種加載方式和CDN類似,如果window.Vue存在,則自動注冊全部組件。所以如果window.Vue存在,返回的對象意義不大;除非window.Vue不存在。注意:組件注冊成功之后在顯示
//          代碼示例:
//              System.import("……/ddztestlib01.js").then((result) => {
//                   // 成功加載之后,顯示組件
//                   // 如果window.Vue存在,並且存在類似上面的install方法,則這里的返回結果沒有什么意思
//                   // 至於如何使用,則可以根據具體情況而定,選擇自己合適的
//              });
//      4.4、使用requirejs異步加載(測試版本:requirejs 2.3.6)
//          和systemjs類似,只是使用方式不同
//          代碼示例:
//              requirejs.config({
//                  paths: {
//                     "ddztestlib01": tempUrl
//                  }
//              });
//              requirejs(["ddztestlib01"], (result) => {
//                  // 成功加載之后,顯示組件
//              });
//      4.5、……使用模塊加載器加載JS和CDN方式差不多,只是不同的加載器返回的結果不同(有支持UMD,有的不支持)
index.js

  入口文件就不解釋了,在里面我覺得解釋的夠清楚了,還有類庫中包含的組件這里就不說了,也沒有什么好說的就是“Hello World”。

  最后在附上使用類庫的代碼:

<!DOCTYPE html>
<html lang="zh-cmn-Hans">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>測試我的Vue組件庫:ddztestlib01</title>
    <style>
        *,
        *::before,
        *::after {
            box-sizing: border-box;
        }

        html,
        body {
            height: 100%;
            width: 100%;
            margin: 0;
        }

        [v-cloak] {
            display: none;
        }
    </style>
</head>

<body>
    <div id="myApp" v-cloak>
        <h3>{{msg}}</h3>
        <div>下面是組件1的內容</div>
        <ddzcomponent01 :prop1="ddzcomponent01prop1"></ddzcomponent01>
        <div>下面是組件2的內容</div>
        <ddzcomponent02 :prop1="ddzcomponent02prop1"></ddzcomponent02>
    </div>
    <script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
    <script src="https://xiaodu114.github.io/vue/vue2.x/PackFirstVueLibrary/dist/ddztestlib01.js"></script>
    <script>
        var myApp = new Vue({
            el: "#myApp",
            data() {
                return {
                    msg: "測試我的Vue組件庫:ddztestlib01",
                    ddzcomponent01prop1: "我在這里為組件1的屬性1賦值:" + new Date(),
                    ddzcomponent02prop1: "我在這里為組件2的屬性1賦值:" + Math.random(),
                }
            }
        });
    </script>
</body>

</html>
View Code

  預覽地址

  源碼地址

  這篇文章就到這里吧!有寫的不對的地方,敬請指正,非常感謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM