组件库建设--基本框架(从0到1)

290 阅读2分钟

开发环境

pc环境:windows webpack:5.3.9 webpack-cli:4.9.1 webpack-dev-server: 4.3.1 【全局安装webpack,webpack-cli,webpack-dev-server】

目录结构

  • components ui组件
  • docs 文档库

引入方式

全局引入 通过vue.use引入

创建组件

  • 在src/components目录下新建A目录,在A目录里面新建A.vue
  • 创建index.js,将A.vue导出

注意:name要与组件名称一致,比如A.vue,name也应该是A

import A from './A.vue'
export default A

全局引入

  • 在src目录下,新建index.js,通过install方法进行注册
import A from './components/A'

const components = {
    A,
}

const install = (Vue) => {
    Object.keys(components).map((key) => {
        const component = components[key]
        Vue.component(component.name, component)
    })
}

// auto install
if (typeof window !== 'undefined' && window.Vue) {
    install(window.Vue);
}

const HBirthDay = {
    install
}
export default HBirthDay
//module.exports.default = module.exports = HBirthDay

打包

这里的打包,我们参考element-ui,通过打包成一个js文件,我们在其他项目里面通过vue.use引入

  • webpack.common.js
/**
 * 公共配置
 */
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')
const webpack = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
	output: {
		path: path.join(__dirname, 'node_modules/hbirthday'),  // 打包生成文件地址
		filename: 'HBirthDay.min.js', // 指定输出文件的名称
		libraryTarget: 'umd',
		library: 'HBirthDay',
		umdNamedDefine: true
	},
	module: {
		rules: [
			{
				test: /\.js$/,
				use: {
					loader: 'babel-loader',
					options: {
						presets: ['@babel/preset-env'],
						plugins: ['@babel/plugin-transform-runtime']
					}
				},
				exclude: /node_modules/ // 不编译node_modules下的文件
			},
			{
				// *.vue
				test: /\.vue$/,
				loader: 'vue-loader'
			},
			{
				// 图片
				test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
				use: {
                    loader: 'url-loader',
					options: {
						limit: 10 * 1024 // 10kb
					}
				}
            },
            // {
            //     test: /\.css$/i,
            //     use: [MiniCssExtractPlugin.loader,"css-loader"],
            // },
            {
                test: /\.css$/i,
                 use: [
                     {
                         loader: MiniCssExtractPlugin.loader,
                         options: {
                             esModule: false,
                             publicPath: "../",
                         }
                     },
                     "css-loader",
                     {
                         loader: 'postcss-loader',
                         options: {
                             postcssOptions: {
                                 plugins: [
                                     [
                                         'postcss-preset-env',
                                     ],
                                 ],
                             },
                         },
                     }
                 ]
            },
		]
    },
	// 解析路径
	resolve: {
		// 设置src别名
        alias: {
            'vue': 'vue/dist/vue.esm.js',
			'@': path.resolve(__dirname, 'src'),
		},
		//后缀名 可以根据需要自由增减
        extensions: ['.js', '.vue']
	},
    plugins: [
		    new VueLoaderPlugin(),
		    new MiniCssExtractPlugin({
		        // 类似于 webpackOptions.output 中的选项
		        // 所有选项都是可选的
		        filename: 'assets/css/[name].css',
		        chunkFilename: 'assets/css/[id].css',
		    }),
				new HtmlWebpackPlugin({
					filename: 'index.html', // 生成的文件夹名
					template: 'public/index.html', // 模板html
					favicon: 'public/favicon.ico', // 图标
				}),
        new webpack.optimize.ModuleConcatenationPlugin()
	]
}

  • webpack.dev.js
const webpack = require('webpack')
const path = require('path')
const { merge } = require('webpack-merge')
const common = require('./webpack.common')

module.exports = merge(common, {
    mode: 'development',
    entry: './src/main.js', // 定义入口文件
	devServer: {
		contentBase: path.join(__dirname, 'dist'), // 告诉服务器内容的来源。仅在需要提供静态文件时才进行配置
		compress: true,  // 开启压缩
		port: 8081,  // 端口
		hotOnly: true,  // 热模块替换,保存页面状态
		hot: true,   // 自动刷新
        open: false,
        historyApiFallback: {
			index: '/index.html' //与output的publicPath有关(HTMLplugin生成的html默认为index.html)
		},
	},
	devtool: 'source-map',
	plugins: [
		new webpack.HotModuleReplacementPlugin(), // 配合 devServer:hot 使用,自动刷新页面
	]
})
  • webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

module.exports = merge(common, {
    mode: 'production',
    entry: './src/index.js', // 定义入口文件
    externals: {
		vue: {
			root: 'Vue',
			commonjs: 'vue',
			commonjs2: 'vue',
			amd: 'vue'
		}
	},
	plugins: [
        new CleanWebpackPlugin(),
        new CssMinimizerPlugin(),
	]
})
  • package.json
{
  "name": "hbirthday",
  "version": "1.0.0",
  "description": "生日",
  "main": "dist/HBirthDay.min.js",
  "scripts": {
    "dev": "webpack serve --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },
  "keywords": [],
  "author": "hhh",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.14.3",
    "@babel/plugin-transform-runtime": "^7.14.3",
    "@babel/preset-env": "^7.14.4",
    "@vue/cli-plugin-babel": "^4.5.14",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^4.0.0-alpha.0",
    "css-loader": "^5.2.6",
    "file-loader": "^6.2.0",
    "html-webpack-plugin": "^5.3.1",
    "less": "^4.1.1",
    "less-loader": "^9.1.0",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "vue-loader": "^15.9.7",
    "vue-style-loader": "^4.1.3",
    "vue-template-compiler": "^2.6.14",
    "webpack": "^5.38.1",
    "webpack-cli": "^4.7.2",
    "webpack-dev-server": "^3.11.2",
    "webpack-merge": "^5.8.0"
  },
  "dependencies": {
    "babel-plugin-components": "^1.0.4",
    "chalk": "^4.1.1",
    "compression-webpack-plugin": "^9.0.0",
    "css-minimizer-webpack-plugin": "^3.1.1",
    "fa": "^1.0.1",
    "hbirthday": "^1.0.0",
    "mini-css-extract-plugin": "^2.4.3",
    "path": "^0.12.7",
    "postcss-loader": "^6.2.0",
    "postcss-preset-env": "^6.7.0",
    "vue": "^2.6.14",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2"
  }
}

全局打包命令

npm run build

在这里插入图片描述

使用方式

在这里插入图片描述

npm发布

  • 任务
  1. 完成组件库打包,可通过全局引入组件
  2. 发布到npm公共库,可通过npm方式引入
  • 操作步骤

  • 在package.json main配置里面写入"dist/HBirthDay.min.js",也就是你刚才打包好的js文件,只一步必须,否则无法引入 在这里插入图片描述

  • npm config set registry registry.npmjs.org/

  • npm login

  • npm publish

426错误解决 :blog.csdn.net/weixin_4169…

注意:邮箱必须经过校验,否则会显示登录错误

package.json里面的name在发布时,必须全部小写,不能有大写字母,另外在每次发布需要更改json里面的version,必须与上一次version不一样 在这里插入图片描述

  • 检验是否发布成功 在这里插入图片描述

npm发布成功使用方法

在这里插入图片描述 在这里插入图片描述

局部引入

  • 新建webpack.lib.js文件
const fs = require('fs');
const path = require('path');
const { merge } = require('webpack-merge')
const common = require('./webpack.common')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const join = path.join;
const resolve = (dir) => path.join(__dirname, dir);
/**
 * @desc 大写转横杠
 * @param {*} str
 */
function upperCasetoLine(str) {
  let temp = str.replace(/[A-Z]/g, function (match) {
    return "-" + match.toLowerCase();
  });
  if (temp.slice(0, 1) === '-') {
    temp = temp.slice(1);
  }
  return temp;
}

/**
* @desc 获取组件入口
* @param {String} path
*/
function getComponentEntries(path) {
    let files = fs.readdirSync(resolve(path));

    const componentEntries = files.reduce((fileObj, item) => {
      //  文件路径
      const itemPath = join(path, item);
      //  在文件夹中
      const isDir = fs.statSync(itemPath).isDirectory();
      const [name, suffix] = item.split('.');
    
      //  文件中的入口文件
      if (isDir) {
        fileObj[upperCasetoLine(item)] = resolve(join(itemPath, 'index.js'))
      }
      //  文件夹外的入口文件
      else if (suffix === "js") {
        fileObj[name] = resolve(`${itemPath}`);
      }
      return fileObj
    }, {});
    
    return componentEntries;
}

module.exports = merge(common, {
    mode: 'production',
    entry: getComponentEntries('src/components'),
    //  输出配置
    output: {
      //  文件名称
      filename: '[name]/index.js',
	  path:path.resolve(__dirname,'node_modules/hbirthday/lib'),
	  umdNamedDefine: true,
      //  构建依赖类型
      libraryTarget: 'umd',
      //  库中被导出的项
      libraryExport: 'default',
      //  引用时的依赖名
      library: 'HBirthDay',
    },
    externals: {
		vue: {
			root: 'Vue',
			commonjs: 'vue',
			commonjs2: 'vue',
			amd: 'vue'
		}
	},
	plugins: [
        new CleanWebpackPlugin(),
				new MiniCssExtractPlugin({
					// 类似于 webpackOptions.output 中的选项
					// 所有选项都是可选的
					filename: '[name]/style.css',
					chunkFilename: '[id]/[id].css',
				}),
        new CssMinimizerPlugin(),
        // new MiniCssExtractPlugin()
	]
})

这个文件的作用是加载每个组件文件夹里面的index.js,把它们放置在lib文件夹下

  • 组件库里面index.js
import component1 from './src/component1.vue'

component1.install = function (Vue) {
  console.info('component1----install----')
  Vue.component(component1.name, component1)
}

export default component1

这里导出install方法,到时我们就可以通过use方法引入,当然还有第二种方式,代码如下:

import A from './A.vue'
export default A

通过这种方式,只能通过Vue.component(componentName, component)方式引入

  • package.json增加局部打包命令
"build:lib": "webpack --config webpack.lib.js"
  • 结果 在这里插入图片描述 在这里插入图片描述

我们将所有组件都打包在lib目录下,每一个组件目录下都有一个index.js

  • 接下来我们就可以在main.js单独引入这里的文件 在这里插入图片描述

注意这里用了use的方法,是因为我们在组件index.js导出时使用了组件.install方法 在这里插入图片描述

  • 接下来我们转变一下引入方式,改为如下方式
import {component1} from 'hbirthday'
// import '组件对应的css文件'
Vue.use(componnent1)
  • 在package.json里面增加babel配置,安装babel-plugin-component插件
"babel": {
   "plugins": [
     [
       "component",
       {
         "libraryName": "hbirthday"
       }
     ]
   ]
 }
  • 结果 在这里插入图片描述

文档库

gitee.com/huihui11239…

到这里我们就全部讲完了,现在组件库支持全局引入,局部引入 另外也分别讲了利用webpack从0到1搭建一个vue开发,上线环境