最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 用webpack从0到1打包一个按需加载的vue组件库

    正文概述 掘金(soso)   2021-02-24   1099

    在vue项目开发中,我们会将经常用到的逻辑或模块抽象成组件,对于那些多个项目都有用到的组件,可以考虑封装成组件库,发布到npm。每次要到只需要npm install xx一下,就不用来回拷贝了。下面我们就从0开始来打包一个vue组件库。

    使用vue组件库的常见方式

    • 1、通过script标签引入
    <body>
        <div id="app">
            <hello></hello>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>  <!--在组件库之前引入vue-->
    <script src="../dist/my-lib.js"></script>
    <script>
        new Vue({
            el: '#app'
        })
    </script>
    
    • 2、通过import引入
    import Vue from "vue"
    import App from "./App.vue"
    import MyLib from "my-lib"
    Vue.use(MyLib)
    new Vue({
        name: "root",
        el: '#root',
        render: h => h(App)
    })
    

    这样引入后,在需要使用组件库的项目组件中,直接使用,比如:

    <template>
      <div id="app">
          <hello></hello>
      </div>
    </template>
    

    组件在使用之前都需要注册组件,注册组件可以局部注册和全局注册,从上面组件库的使用,我们可以看出,组件库的组件都是全局注册的。

    在第一种通过script标签引入的方式中,我们在组件库内部完成了全局注册,所以引入组件库就可以直接使用。

    在第二种通过import引入的方式中,我们通过Vue.use(MyLib)完成组件的全局注册。

    用webpack从0到1打包一个按需加载的vue组件库

    组件库的项目结构如下,在src/index.js中定义install方法

    用webpack从0到1打包一个按需加载的vue组件库

    //  src/index.js
    import Hello from "./components/Hello.vue"
    
    function install(Vue){   //外部的Vue.use(MyLib)会执行该方法,完成组件的全局注册。
      Vue.component(Hello.name, Hello)
    }
    
    if(window && window.Vue) {   //通过`script`标签引入的情况,在组件内部完成组件注册。
      Vue.use(install)
    }
    
    export default install
    

    webpack打包

    webpack的基础使用: webpack由浅入深系列(一) 、webpack由浅入深系列(二)

    现在我们的组件库就一个hello组件,我们将通过webpack,将它打包成可以通过上述两种方式使用的组件库。 用webpack从0到1打包一个按需加载的vue组件库 webpack打包vue组件库,跟打包普通的vue项目的配置绝大部分是一样的。只是output有所不同,如下所示,增加了libraryTarget等。

    //webpack.config.js
    const path = require('path')
    const { VueLoaderPlugin } = require('vue-loader')
    module.exports = {
      mode: 'none',
      entry: './src/index.js',
      output: {
        path: path.join(__dirname,"/dist"),
        filename: 'my-lib.js',  
        libraryTarget: 'umd',  //用到的模块定义规范
        library: 'myLib',   //库的名字
        libraryExport: 'default'
      },
      module: {
        rules: [
          {
            test: /\.vue$/,
            use:  ['vue-loader']
          },
          {
            test: /\.css$/,
            use:  ['style-loader','css-loader','postcss-loader']
          },
          {
              test: /\.s[ac]ss$/i,
              use:  ['style-loader','css-loader','postcss-loader','sass-loader']
          },
          {
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/,
          }
        ]
      },
      plugins: [
        new VueLoaderPlugin()
      ]
    }
    

    libraryTarget

    libraryTarget 规定了打包出的库文件所用的模块定义规范,主要有下面这些:var assign this window global jsonp commonjs commonjs2 amd umd。具体含义参考官方文档

    我们这里使用的是umd,它将在 CommonJS, AMD 环境下运行,或将模块导出到 global 下的变量。在umd模块规范下导出的库文件大体结构如下,我们经常在一些插件或库文件中看到,比如jquery。

    //官方示例。 'MyLibrary'就是library中定义的库名称, 对应我们自己demo中的myLib。
    
    (function webpackUniversalModuleDefinition(root, factory) {
      if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();    
      else if(typeof define === 'function' && define.amd)
        define([], factory);           
      else if(typeof exports === 'object')
        exports['MyLibrary'] = factory();  
      else
        root['MyLibrary'] = factory();  
    })(typeof self !== 'undefined' ? self : this, function() {
      return _entry_return_; // 此模块返回值,是入口 chunk 返回的值
    });
    

    写好了webpack.config.js,我们就来package.json中配置scripts

    "scripts": {
       "build": "webpack"  //默认会执行根目录下的webpack.config.js
    },
    

    npm run build

    一个乞丐版的vue组件库就打包了。

    externals

    在这个我们的vue组件库中,vue是作为参数传入的。我们也没有import其他库。当这个库的功能比较复杂时,往往会不可避免地import其他库,比如你要使用Vue的静态方法Vue.xxx,或者工具库,比如lodash。

    这时我们不可能把这些外部库打包到自己的组件库,因为,当用户自己也引入了这些外部库,再引入我们的组件库,就会对外部库进行两次引入和打包,这完全是冗余的。所以我们在开发一个库的时候,对于库依赖的外部模块,可以由引入库的使用者提供。

    这需要借助externals,它能防止将某些 import 的包打包到bundle中,而是在运行时再去从外部获取这些外部依赖模块。在上面的webpack.config.js中添加externals

    const path = require('path')
    const { VueLoaderPlugin } = require('vue-loader')
    module.exports = {
      ...
      externals : {
        vue: {
          root: "Vue",   //通过 script 标签引入,此时全局变量中可以访问的是 Vue
          commonjs: "vue",  //可以将vue作为一个 CommonJS 模块访问
          commonjs2: "vue",  //和上面的类似,但导出的是 module.exports.default
          amd: "vue"   //类似于 commonjs,但使用 AMD 模块系统
        }
      }
    }
    

    externals的配置可接受string、array、object、function、regex等各种语法。externals配置

    只有当libraryTarget: 'umd'时,才可以配置如上那样的包含 { root, amd, commonjs,... } 的对象,其它的libraryTarget的值不能这样配置。

    为了测试externals的配置,我们在src/index.js中加入下面两行代码:

    import Vue from "vue"
    console.log(Vue)
    

    怎样在本地调试组件库

    • 1、先来调试一下通过script标签引入的情况,新建一个html文档,引入打包好的组件库引入。
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <div id="app">
            <hello></hello>
        </div>
    </body>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="../dist/my-lib.js"></script>
    <script>
        new Vue({
            el: '#app'
        })
    </script>
    </html>
    

    用webpack从0到1打包一个按需加载的vue组件库

    • 2、import引入的情况。搭建一个用于测试的vue项目LibTest,或者,更方便快捷一点的,在已有的vue的项目中加一个测试组件,并添加到routes中。

    修改 myLib项目 的 package.json 中的 main 字段,main 字段定义了 npm 包的入口文件。

    "main": "./dist/my-lib.js",
    

    在myLib组件库项目根目录下执行 npm link

    cd myLib
    npm link
    

    在测试项目根目录下执行npm link my-lib

    cd LibTest
    npm link my-lib  //这个my-lib是在myLib项目的package.json中定义的"name": "my-lib"
    

    然后,就可以和普通npm安装的组件库一样使用了

    在测试项目LibTest的入口js中,引入my-lib,执行Vue.use()注册组件

    import MyLib from 'my-lib'
    Vue.use(MyLib)
    

    在LibTest的App.vue中使用组件:

    <template>
      <div>
        <hello></hello>
      </div>
    </template>
    

    用webpack从0到1打包一个按需加载的vue组件库

    按需加载

    基础的组件库我们已经跑通了,下面就把它改造成按需加载的组件库。

    对于组件库项目,支持按需加载需要满足:组件库以 es6 模块化方式导出。也就是说,导出文件得是这样de导出导入方式:

    import { xxx } from 'xxx'
    import yyy from yyy
    export default zzz;
    export { a, b, c };
    

    通过Tree-Shaking删除未使用的代码。

    Tree-Shaking的原理,墙裂建议认真阅读这两篇文章:

    Tree-Shaking性能优化实践 - 原理篇 、 你的Tree-Shaking并没什么卵用

    1、ES6的模块引入是静态分析的,故而可以在编译时正确判断到底加载了什么代码。
    2、分析程序流,判断哪些变量未被使用、引用,进而删除此代码。
    

    在基础打包中,我们知道webpack有多种导出模式(libraryTarget),但是webpack却没有支持导出ES模块的模式。大家通常都会选择的'umd'导出模式,它的导出文件也是一个立即执行函数,一点都不符合es6 模块化方式。按照上面那样的配置是没有办法按需加载的。

    有很多库为了按需引用,将每一个组件或者功能函数,都打包成单独的文件或目录。然后通过文件路径进行引用:

    import 'echarts/lib/chart/pie'
    import 'echarts/lib/component/title'
    

    这样写不能一次引入多个组件,也不够优雅。element-ui就专门开发了babel插件babel-plugin-component,使我们能像下面这样按需引入:

    import { Button, Select } from 'element-ui'
    Vue.use(Button)
    Vue.use(Select)
    

    参考element-ui的构建方式

    用webpack从0到1打包一个按需加载的vue组件库

    babel-plugin-component插件将:

    import { Button } from 'element-ui'
    

    转换成:

    var button = require('element-ui/lib/button')
    require('element-ui/lib/theme-chalk/button.css')
    

    在上面的路径中,element-uitheme-chalk都是可配置的,button是组件名,libbabel-plugin-component插件默认要去寻找组件的文件夹。

    如果我们也想借助babel-plugin-component插件实现比较优雅的按需引用,就把每个组件单独打包,放到组件库的lib文件

    实现按需引用

    创建一个webpack.component.js,作为组件单独打包的配置文件。再写两个组件Hello和Test,后面用于测试。 用webpack从0到1打包一个按需加载的vue组件库 因为是组件单独打包,所以每一个组件都导出一个函数,再Vue.use(xx)的时候执行这个函数,完成组件的全局注册。

    Hello/Hello.vueHello/index.js 用webpack从0到1打包一个按需加载的vue组件库

    //  src/index.js改成这样
    import Hello from "./components/Hello"
    import Test from "./components/Test"
    function install(Vue){
      Vue.use(Hello)
      Vue.use(Test)
    }
    if(window && window.Vue) {
      Vue.use(install)
    }
    export default install
    

    实现也很简单,核心就是多入口打包。这里有讲多入口打包

    我们将前面webpack.config.js中的内容复制粘贴到webpack.component.js中。

    • 第一步、修改entryoutput字段:
    entry: {
      'hello': './src/components/Hello/index.js',
      'test': './src/components/Test/index.js',
      'my-lib': './src/index.js'
    },
    output: {
      path: path.join(__dirname,"/lib"),
      filename: '[name].js',
      libraryTarget: 'umd',
      library: '[name]',
      libraryExport: 'default'
    },
    

    添加package.json的scripts:"component": "webpack --config webpack.component.js"

    npm run component

    在myLib组件的目录下就多了一个lib文件夹。 用webpack从0到1打包一个按需加载的vue组件库

    • 第二步、从上面的element-uibabel-plugin-component插件使用方式,我们看出组件库的js文件和css文件是分离开来。所以我们也将组件的css分离到单独的文件,这里我们用mini-css-extract-plugin插件。
    const MiniCssExtractPlugin = require('mini-css-extract-plugin')
    

    用webpack从0到1打包一个按需加载的vue组件库

    配置完成,再次npm run component

    用webpack从0到1打包一个按需加载的vue组件库 接下来,修改package.json中 "main": "./lib/my-lib.js",

    回到测试项目。在项目根目录下创建.babelrc (安装插件就根据命令行提示进行安装),重启项目。 babel-plugin-component配置参考

    {
      "presets": [["es2015", { "modules": false }]],
      "plugins": [
        [
          "component",
          {
            "libraryName": "my-lib",
            "styleLibrary": {
              "name": "lib-style", // same with styleLibraryName
              "base": false  // if theme package has a base.css
            }
          }
        ]
      ]
    }
    

    我们来粗暴地测试一下。只引用hello组件,同时使用hello和test。

    //   LibTest/src/index.js
    import { Hello } from "my-lib"
    Vue.use(Hello)
    
    //  LibTest/src/App.vue
    <template>
        <div class="cp">
            <hello></hello>
            <test></test>
        </div>
    </template>
    

    用webpack从0到1打包一个按需加载的vue组件库

    加上test组件的引用:

    import { Hello,Test } from "my-lib"
    Vue.use(Hello)
    Vue.use(Test)
    

    用webpack从0到1打包一个按需加载的vue组件库 我们再去掉test组件的引用。将测试项目打包。在打包后的文件中搜索两个组件的内容。

    用webpack从0到1打包一个按需加载的vue组件库

    按需加载的vue组件库我们就打包完成了。其实,开发组件库也可以使用 rollup.js来打包,它能很好地支持ES Module,tree-shaking 也是 rollup.js 先提出的,实现组件库地按需加载就简单很多。今天就到这里啦,下一篇文章将会讲到怎么用rollup打包一个库,下次见。


    起源地下载网 » 用webpack从0到1打包一个按需加载的vue组件库

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元