赞
踩
最近萌发要做一个自己的基于
Vue
的组件库的想法,尝试下自己去造一些轮子;于是翻了下业内的标杆element-ui
组件库的设计,在此基础上去构建自己的ui库(站在巨人的肩膀上成功)。注:项目是基于vue-cli@3.x的;
element
的目录设计- ├─build // 构建相关的脚本和配置
- ├─docs // 打包后的静态文件
- ├─examples // 用于展示Element组件的demo
- ├─lib // 构建后生成的文件,发布到npm包
- ├─packages // 组件代码
- | ├── button
- | | ├── button.vue //组件文件
- | | └── index.js // 导出Button组件以及处理供按需加载的方法
- | ├── .. 各个组件
- | └── theme-chalk // 这里存放所有的组件样式.scss
- ├─public // index.html
- └─package.json
注:这里的设计将组件跟样式进行了分离,这样做的目的是为了更好的去做按需加载
我们都知道在vue中组件的安装要使用Vue.use(install)
,install
是向外暴漏的一个方法,里面调用Vue.component(component.name, component)
完成组件的注册;具体的源码如下:
- /* eslint-disable */
- // This file is auto gererated by build/build-entry.js
- import FeButton from './button'
- import FeInput from './input'
- const version = '0.0.46'
- const components = [
- FeButton,
- FeInput
- ]
- const install = Vue => {
- components.forEach(Component => {
- Vue.use(Component);
- // Vue.component(component.name, component) 也可使用这个进行注册
- })
-
- Vue.prototype.$message = Message
- };
- /* istanbul ignore if */
- if (typeof window !== 'undefined' && window.Vue) {
- install(window.Vue)
- }
- export {
- install,
- version,
- FeButton,
- FeInput
- }
- export default {
- install,
- version
- }
install
内部之所以能够使用Vue实例是Vue.use
中进行了Vue实例的参数的合并,有兴趣的可以去看看源码这里就提上一嘴;
以上基本的组件构造就完成了,我们也就很好的完成了第一步可在main.js
全局导入one-piece-ui的组件进行测试;这时候我们应该思考下如何去实现按需加载的问题啦!
- import FeUI from '../packages';
- import '../packages/theme-chalk/index.css';
- Vue.use(FeUI);
element-ui
的介绍,借助 babel-plugin-component
,我们可以只引入需要的组件,以达到减小项目体积的目的。然后,将 .babelrc 修改为:
- {
- "presets": [["es2015", { "modules": false }]],
- "plugins": [
- [
- "component",
- {
- "libraryName": "element-ui",
- "styleLibraryName": "theme-chalk"
- }
- ]
- ]
- }
这个插件的作用是什么呢?就是将引用路径进行了变换,如下:
import { Button } from 'one-piece-ui'
转换为:
- var button = require('one-piece-ui/lib/button')
- require('one-piece-ui/lib/theme-chalk/button.css')
这样我们就精准地引入了对应 lib 下的 Button 组件的 JS 和 CSS 代码了,也就实现了按需引入 Button 组件。
element-ui
的package.json
的scripts
构建命令- "bootstrap": "yarn || npm i",
- "build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
- "build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
- "build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
- "build:umd": "node build/bin/build-locale.js",
- "clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
- "deploy:build": "npm run build:file && cross-env NODE_ENV=production webpack --config build/webpack.demo.js && echo element.eleme.io>>examples/element-ui/CNAME",
- "deploy:extension": "cross-env NODE_ENV=production webpack --config build/webpack.extension.js",
- "dev:extension": "rimraf examples/extension/dist && cross-env NODE_ENV=development webpack --watch --config build/webpack.extension.js",
- "dev": "npm run bootstrap && npm run build:file && cross-env NODE_ENV=development webpack-dev-server --config build/webpack.demo.js & node build/bin/template.js",
- "dev:play": "npm run build:file && cross-env NODE_ENV=development PLAY_ENV=true webpack-dev-server --config build/webpack.demo.js",
- "dist": "npm run clean && npm run build:file && webpack --config build/webpack.conf.js && webpack --config build/webpack.common.js && webpack --config build/webpack.component.js && npm run build:utils && npm run build:umd && npm run build:theme",
- "i18n": "node build/bin/i18n.js",
- "lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",
- "pub": "npm run bootstrap && sh build/git-release.sh && sh build/release.sh && node build/bin/gen-indices.js && sh build/deploy-faas.sh",
- "test": "npm run lint && npm run build:theme && cross-env CI_ENV=/dev/ BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
- "test:watch": "npm run build:theme && cross-env BABEL_ENV=test karma start test/unit/karma.conf.js"
是不是有点懵逼,这都是啥咋这么多??? 我的内心其实也是很这啥都啥...
我对其中的一些命令进行了删除保留了一些 暂时我们能用到的,如下:
- "init": "npm install commitizen -g && commitizen init cz-conventional-changelog --save-dev --save-exact && npm run bootstrap",
- "bootstrap": "npm install && cd ./packages/theme-chalk && npm install",
- "build:style": "gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
- "build:docs": "vue-cli-service build",
- "build:lib": "node build/build-lib.js",
- "build:entry": "node build/build-entry.js ",
- "serve": "vue-cli-service serve",
- "clean": "rimraf lib && rimraf packages/*/lib",
- "deploy": "sh build/deploy.sh",
- "lint": "vue-cli-service lint",
- "lib": "vue-cli-service build --target lib --name feui --dest lib packages/index.js && webpack --config ./build/webpack.component.js"
首先解释一下npm run init
做的那几件事:
git commit
的相关规范插件,主要是在功能提交上做一些规定npm run bootstrap
npm run bootstrap
做的几件事:
cd ./packages/theme-chalk
目录下,安装gulp相关的依赖;注:
element-ui
采用了gulp对scss
文件进行打包;个人觉得还是挺好的,gulp比较简单不像webpack的配置那么复杂,能够很简单的处理scss
build:style
做的几件事:
lib
目录下cp-cli
插件将打包好的lib
文件夹内容复制到lib/theme-chalk
目录下到此我们就完成了样式的打包及输出到指定的目录啦,此处有掌声...
npm run lib
命令时候,你会发现组件的js都被单独打包出来了,那这是如何实现的呢?我们看下这条命令webpack --config ./build/webpack.component.js
,对,就是他了;源码如下:- const path = require('path');
- const ProgressBarPlugin = require('progress-bar-webpack-plugin');
- const VueLoaderPlugin = require('vue-loader/lib/plugin');
-
- const Components = require('./get-components')();
- const entry = {};
- Components.forEach(c => {
- entry[c] = `./packages/${c}/index.js`;
- });
- const webpackConfig = {
- mode: 'production',
- entry: entry,
- output: {
- path: path.resolve(process.cwd(), './lib'),
- filename: '[name].js',
- chunkFilename: '[id].js',
- libraryTarget: 'umd'
- },
- resolve: {
- extensions: ['.js', '.vue', '.json']
- },
- performance: {
- hints: false
- },
- stats: 'none',
- module: {
- rules: [{
- test: /\.js$/,
- loader: 'babel-loader',
- exclude: /node_modules/
- }, {
- test: /\.vue$/,
- loader: 'vue-loader'
- }]
- },
- plugins: [
- new ProgressBarPlugin(),
- new VueLoaderPlugin()
- ]
- };
-
- module.exports = webpackConfig;
很熟悉是吧,对!就是你想的那样---多文件入口打包;这里里面有个get-components
工具方法就是返回所有的packages
下的组件文件名称;这样我们就可以通过命令自动注入组件的js文件啦~
- const fs = require('fs');
- const path = require('path');
-
- const excludes = [
- 'index.js',
- 'theme-chalk',
- 'mixins',
- 'utils',
- 'fonts',
- '.DS_Store'
- ];
-
- module.exports = function () {
- const dirs = fs.readdirSync(path.resolve(__dirname, '../packages'));
- return dirs.filter(dirName => excludes.indexOf(dirName) === -1);
- };
npm run build:entry
这里会执行build/build-entry.js
,源码如下:- const fs = require('fs-extra');
- const path = require('path');
- const uppercamelize = require('uppercamelcase');
-
- const Components = require('./get-components')();
- const packageJson = require('../package.json');
-
- const version = process.env.VERSION || packageJson.version;
- const tips = `/* eslint-disable */
- // This file is auto gererated by build/build-entry.js`;
-
- function buildPackagesEntry() {
- const uninstallComponents = ['Message'];
- const importList = Components.map(
- name => `import ${uppercamelize(name)} from './${name}'`
- );
-
- const exportList = Components.map(name => `${uppercamelize(name)}`);
-
- const installList = exportList.filter(
- name => !~uninstallComponents.indexOf(`${uppercamelize(name)}`)
- );
-
- const content = `${tips}
- ${importList.join('\n')}
- const version = '${version}'
- const components = [
- ${installList.join(',\n ')}
- ]
- const install = Vue => {
- components.forEach(Component => {
- Vue.use(Component)
- })
-
- Vue.prototype.$message = Message
- };
- /* istanbul ignore if */
- if (typeof window !== 'undefined' && window.Vue) {
- install(window.Vue)
- }
- export {
- install,
- version,
- ${exportList.join(',\n ')}
- }
- export default {
- install,
- version
- }
- `;
- fs.writeFileSync(path.join(__dirname, '../packages/index.js'), content);
- }
-
- buildPackagesEntry();
-
不难发现,这里给我们自动化生成了一个packages/index.js
文件,是不是感觉技术减少人力? 心里:"卧槽..."
3. npm run build:lib
,通过上面的学习,到此这个命令其实也就不神秘啦,里面也就是做了一件汇总的事情:
- /**
- * Build npm lib
- */
- const shell = require('shelljs');
- const signale = require('signale');
-
- const { Signale } = signale;
- const tasks = [
- 'bootstrap',
- 'lint',
- 'clean',
- 'build:entry',
- 'lib',
- 'build:style'
- ];
-
- tasks.forEach(task => {
- signale.start(task);
- const interactive = new Signale({ interactive: true });
- interactive.pending(task);
- shell.exec(`npm run ${task} --silent`);
- interactive.success(task);
- });
好了,基本的组件js分离功能也就完成了;
这里就不介绍了,具体请戳项目如何发布到NPM;
静态页面如何发布到GithubPages
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。