Several projects in charge of the group all had some common components, so we set up a common component development scaffolding. It was the first time to develop library, so we built it by referring to the configuration of iView. How to use webpack4 to build a library scaffolding

preface

To use Webpack 4, you need to install WebPack and WebPack-CLI

yarn add webpack webpack-cli -D
Copy the code

The next step is to write the configuration file.

Project scaffolding structure

The directory structure of the library I wrote is as follows for reference only, mainly imitating the structure of iView. Part of the configuration refers to the webpack configuration file of VUe-CLI.

├─ Build │ build.js // Build │ ├─ build.js // build │ Check-versions. Mainly is to examine NPM versions and node │ webpack. Base. Conf., js / / general configuration │ webpack. Dev. Conf., js / / development environment │ webpack. Dist. Prod. Conf. Js / / Hbf.min.js │ webpack.prod.conf.js // Package code for example file, │ ├─dist │ ├─ example │ ├─ ├─ example │ ├─ ├─lib │ ├─ anywhere to preview │ ├─ hbf.min.js // Library file │ ├─example │ app.vue │ index.html │ main │ │ ├─ ├─package.json │ │ ├─package.json │ │ ├─ ├─ download.txtCopy the code

More detailed information can be found in the Github repository.

Webpack compiled code

To better understand, take a look at the compiled webPack code.

Webpack-processed code usually looks like this

// Webpack compiled code

/*
* @param {Array} modules
*/; (function(modules) {
  function __webpack_require__(moduleId) {
    var module = {
      i: moduleId, / / module ID
      l: false.exports: {}, // return as a result.
    }
    // Call an element of modules array (type function)
    modules[moduleId].call(
      module.exports,
      module.module.exports,
      __webpack_require__
    )
    return module.exports
  }

  return __webpack_require__(0)
})([
  /** omits the code. Each entry in this array represents a module, which is actually a function that takes three arguments: module object, module.exports object, __webpack_require__ function **/
])
Copy the code

The entire structure of webPack compiled code is an IIFE function that takes modules: Array.

For Module processing, both ES Module import and CommonJS require are converted to __webpack_require__ to import modules.

The __webpack_require__ function, which starts at the first element of the Modules array (moduleId is 0, which is the entry file), executes the logic of that module (which is really a function), using the data type of the passed module.exports as the reference type Object, Add attributes to module.exports indirectly.

return __webpack_require__(0)

Imports dependent modules one by one, starting with the import file, and returns the module.exports of the entry module

At this point, the compiled JS file is not referenced by other modules and is only valid in the current scope. Webpack provides a way to create library by defining library and libraryTarget in output. So that the completed JS can be used by other modules.

Setting the Library Configuration

For projects used as a library, the Output option requires the library to be set

// webpack.dist.pord.conf.js

output: {
  path: path.resolve(__dirname, '.. /dist'),
  publicPath: '/dist/'.filename: 'hbf.min.js'.library: 'hbf'.libraryTarget: 'umd'
},
Copy the code

A library can be a string or an object (objects are only used if the libraryTarget value is umD).

output: {
  library: {
    root:'Hbf'.// exposed to the global variable, window.hbf is called
    commonjs: 'hbf-public-components'
  },
  libraryTarget: 'umd'
}
Copy the code

Commonjs and CommonJs2.

The CommonJS specification defines an exports object, while nodeJS makes some extensions to the commonJS specification to define module.exports, which is also called commonJS2 specification.

When we refer to someone else’s library, we can usually introduce it in various ways, such as

When libraryTarget is set to UMD (common module specification), the library can be loaded in a variety of module loading methods after packaging, with high compatibility. Details about libraryTarget can be found in the official WebPack documentation

Library dependency issues

If our library is based on, say, a VUe-based UI component library, we need to import vUE while developing the component library. If the user of the component library has already imported VUE, the VUE will be imported and packaged twice. So when we develop a library, some of the modules that depend on it can be provided by the users who introduce it. So we need to remove the dependent modules from the library package build.

Externals prevents certain imported packages from being packaged into bundles and instead fetches these extension dependencies externally at run time.

Exclude vue and iView from the output bundle by setting externals.

These external dependencies can take any of the following forms.

  • rootGlobal variable access
  • commonjsAs acommonjsModule is introduced into
  • commonjs2commonjsIt’s similar, but it’s derived frommodule.exports
  • amduseamdModule specification introduction
// webpack.dist.pord.conf.js

externals: {
  vue: {
    root: 'Vue'.commonjs: 'vue'.commonjs2: 'vue'.amd: 'vue'
  },
  iview: {
    root: 'iView'.commonjs: 'iview'.commonjs2: 'iview'.amd: 'iview'}},Copy the code

In addition, add a peerDependencies field in package.json, which is used to specify the version number of the library that the library depends on. If the version number of the iView and vue is not correct when the library is downloaded and used, the peerDependencies field will be warned.

/ / package. Json "peerDependencies" : {" iview ":" > 2.0.0 ", "vue" : "> 2.0.0"},Copy the code

For these two dependencies, write to the development environment dependencies, otherwise the installation will install vue and iView in the library directory, which also does not fit the idea of library referees providing library dependencies.

Vue plug-in library/component library

For vue’s plug-in library/component library, there needs to be an install method if you want global import. The internal logic of Install is a Vue object passed in as a parameter to register all components. Then finally expose all the common components together with the install method as a new object.

// Introduce common components
import publicMenu from './components/public-menu'
import tablePage from './components/table-page'
import sliderCustom from './components/slider-custom'

const components = {
  publicMenu,
  tablePage,
  sliderCustom,
}
const Hbf = Object.assign({}, components)
const install = function(Vue, opts) {
  if (install.installed) return

  Object.keys(components).forEach(component= > {
    Vue.component(component, component)
  })
}

// For script tag introduction
if (typeof window! = ='undefined' && window.Vue) {
  install(window.Vue)
}

// Assign the install method to the Hbf object
Hbf.install = install

// Output the default variable for full import. You can also select * for full import
export default Hbf
// Outputs each component for import on demand
export { publicMenu, tablePage, sliderCustom }
Copy the code

Module. exports’ library ‘Uncaught TypeError: module.exports’ library’ Uncaught TypeError: Cannot assign to read only property ‘exports’ of object ‘#< object >’.

Because the project I tested turned Babel’s compilation of ES Modules off, Babel would normally convert ES6 Module compilation to the CommonJS specification without manually turning it off. When module conversion is turned off, the output of the library uses commonJS module. Exports, while the import of the library uses ES6 module import keyword, so there is no error. I wrote the export keyword of the library as export keyword, so there is no error.

We learned that most libraries now use the CommonJS specification, since webpack tree-shaking only works for ES Modules. Tree-shaking in Webpack is actually implemented by Uglylify.

The main and module fields of package.json are used to define entry files for the library’s two module specifications, respectively. Main is written using commonJS canonical syntax, module is written using ES6 Module syntax, and the Module field is currently a proposal. So for libraries that use ES2015 module syntax, when we only use part of the library code, we can use WebPack to tree-shaking, removing unreferenced code and reducing the size of packaged files.

Release NPM package

The first is to register an NPM account.

If you used taobao mirror before, you need to cut back to NPM official source. Otherwise, we can’t send the package.

Open command line

Switch the official source NPM config set registry at https://registry.npmjs.org/

Execute NPM login and enter your account information.

You can configure.npmignore to ignore files that do not need to be uploaded. It is written the same as.gitignore.

Make sure your project has the correct package.json file and readme.md file

Then execute NPM publish to send the packet.

After the package can be cut back to taobao mirror source

npm config set registry https://registry.npm.taobao.org

Reference component library

Script tag introduction

Unexpected token <, StackOverflow said the reason is that the import path is not correct, so I put the JS file on the CDN.

<script src="http://osuuzm0m8.bkt.clouddn.com/hbf.min.js"></script>
<script>  
  console.log(window.Hbf) // You will see the object you exported
</script>
Copy the code

The output

Full amount quoted

Can be used just like any other VUE plug-in/component library.

import hbf from 'hbf-public-components'

// Use the use method to trigger the INtall method of HBF to register all components
Vue.use(hbf)
Copy the code

If the default variable is not exported, use another way to import it in full

import * as hbf from 'hbf-public-components'
Copy the code

On-demand reference

import { publicMenu } from 'hbf-public-components'
Copy the code

Reference on demand, if the Library uses the ES2015 Module specification, no plug-ins need to be installed, webPack will tree-shaking it to remove unreferenced code.

As mentioned earlier, Tree-shaking for Webpack is implemented by the Uglylify plug-in. In my development environment, I did not enable Uglylify to compress the code, so when I look at the module packaging diagram, I see that the entire library has been brought in, even though I only brought in one component. Webpack4 is tree-shaking only in production. Set mode to production to enable optimization in production.

Libraries that use the CommonJS specification require a plugin support, babel-plugin-import. This plug-in is officially developed by Ant. The on-demand introduction of many UI component libraries also relies on this plug-in.

The installation

yarn add babel-plugin-import -D

Modify the.babelrc file,

"plugins": [
  ["import", {
    "libraryName": "hbf-public-components",
    "libraryDirectory": "lib/components"
  }]
],
Copy the code

conclusion

Actually if for a not very stable, need to keep an iterative update utility component library, using NPM package, will be more inconvenient, frequently updated public component code can use Git subtree (tutorial) to maintain, can wait until after a certain point, the public consider release a component library to stabilize NPM package.

Rollup. js is the ES2015 Module by default and can be used for static analysis and removal of unreferenced code. Tree-shaking is also proposed by Rollup. js. Rollup is a better way to build libraries than Webpack, and vue.js is built using Rollup. Webpack has an advantage in code splitting, so Webpack is relatively good for building applications, but it is also possible to use Webpack to build libraries.

This project can also be used as a generic scaffolding for webPack to build a Library. Next time, try Rollup to build the library

Have a question to discuss together.

Project address: Github

NPM address: NPM

If you’re not familiar with webpack and Babel’s handling of ES Modules, read the following article, which is nice.

In terms of module specifications, and webpack, how does Babel compile transformation modules