文章较长,建议收藏方便再看css
若是你对上面存在问题存在疑问,或者下面的内容,能够帮助到你一点点。html
├─build // 构建相关的nodejs脚本和webapck配置 ├─examples // 展现Element组件的官网项目 ├─lib // 打包出来的文件,发布到npm包 ├─packages // 组件代码 ├─src // 组件代码公共方法 ├─test // 测试代码 ├─Makefile // 构建工具文件 ├─components.json // 组件列表 └─package.json // 记录项目依赖包、script脚本 复制代码
先了解一下最简单的components.json
vue
这个文件记录了你全部组件的路径关系,最终以key,value
的形式保存在这个文件中,方便后续导出,批量操做组件node
咱们能够在不少地方应用了这个文件去参与Webpack、nodejs脚本的自动化构建流程,好比自动生成文件、自动生成多入口等等。。linux
// 为了避免增长文章长度,只截取关键部分,完整请看源码
{
"pagination": "./packages/pagination/index.js",
"dialog": "./packages/dialog/index.js",
"autocomplete": "./packages/autocomplete/index.js",
"dropdown": "./packages/dropdown/index.js",
"dropdown-menu": "./packages/dropdown-menu/index.js",
"dropdown-item": "./packages/dropdown-item/index.js",
"menu": "./packages/menu/index.js",
"submenu": "./packages/submenu/index.js",
"menu-item": "./packages/menu-item/index.js",
"menu-item-group": "./packages/menu-item-group/index.js",
"input": "./packages/input/index.js",
"input-number": "./packages/input-number/index.js",
"radio": "./packages/radio/index.js",
"radio-group": "./packages/radio-group/index.js",
"radio-button": "./packages/radio-button/index.js",
"checkbox": "./packages/checkbox/index.js",
}
复制代码
一个组件库,最重要的是组件库的源码。webpack
ElementUI的组件库源码主要存在于packages
、src
两个目录下面。git
// packages文件夹 每一个组件都有本身的文件夹 // 为了避免增长文章长度,只截取关键部分,完整请看源码 ├── alert ├── aside ├── button ├── button-group ├── calendar ├── checkbox ├── form ├── table ├── theme-chalk // 组件的样式都在这里 index.scss 里面包含了全部的样式文件 复制代码
packages下,每一个组件都是用一个独立的文件夹去管理github
. // packages/button文件夹 ├── index.js // 组件入口文件 └── src ├── button-group.vue // button-group源码 └── button.vue // button源码 复制代码
// packages/button/index.js // 引入组件button组件 import ElButtonfrom './src/button'; // 为组件提供install方法,让Vue能够经过Vue.use(Button)形式去使用 ElButton.install = function(Vue) { Vue.component(ElButton.name, ElButton); }; export default ElButton; 复制代码
theme-chalk文件夹是用来管理全部组件的样式的,文件夹大体结构以下:web
// 为了避免增长文章长度,只截取关键部分,完整请看源码 ├── packages // 组件入口文件 └── theme-chalk ├── alert.scss // alert组件的样式 └── button.scss // button组件的样式 └── aside.scss └── checkbox.scss └── index.scss // 该文件引入了全部样式,是总的样式入口文件 └── gulpfile.js // 样式的编译、压缩、输出是经过gulp工做流来完成的 复制代码
这里比较有趣的是,能够看到Element对样式的打包、编译、处理、输入,是经过gulp
来完成的,而不是Webpack。vue-router
另外还有一个比较流行的组件库IView
,也是经过这种gulp工做流的形式去处理的样式
packages是把每一个组件单独分开去管理的
Src的做用就是,把packages的全部公共方法、自定义指令、组件国际化都放在这里
每种不一样类型的公共方法的,都存在不一样的文件夹中
// src目录 ├── directives // 项目封装好的Vue自定义指令 ├── index.js // 组件的总体入口文件 ├── locale // 组件的国际化内容 ├── mixins // 一些组件的mixins ├── transitions // 动画的封装 └── utils // 工具函数集合 复制代码
这里,咱们重点关注一下index.js
// 为了避免增长文章长度,只截取关键部分,完整请看源码 /* Automatically generated by './build/bin/build-entry.js' */ // 一、导入了全部组件packages下的全部组件 import Pagination from '../packages/pagination/index.js'; import Dialog from '../packages/dialog/index.js'; import Autocomplete from '../packages/autocomplete/index.js'; import Dropdown from '../packages/dropdown/index.js'; const components = [ Pagination, Dialog, Autocomplete, Dropdown, DropdownMenu, DropdownItem, ]; // 二、提供了install方法,帮咱们挂载了一些组件与变量 const install = function(Vue, opts = {}) { locale.use(opts.locale); locale.i18n(opts.i18n); // 把全部的组件注册到Vue上面 components.forEach(component => { Vue.component(component.name, component); }); Vue.use(InfiniteScroll); Vue.use(Loading.directive); Vue.prototype.$ELEMENT = { size: opts.size || '', zIndex: opts.zIndex || 2000 }; Vue.prototype.$loading = Loading.service; Vue.prototype.$msgbox = MessageBox; }; // 导出install方法,能够理解为一个插件、以及一些功能好比国际化功能 export default { install, locale }; 复制代码
在这个文档中,咱们能够看到这个文件的最开头有这么一句话:
Automatically generated by './build/bin/build-entry.js'
能够知道这是经过build-entry
脚本去生成的,后续咱们看项目的构建脚本的时候会简单分析如何实现,这里简单了解一下便可。
另外这个自动生成的index.js文件,主要的实现的功能是如下三点:
$loading
、$Msgbox
等变量最后,由于这里由于引入了全部的组件源码,因此这也是一个Webpack的打包的入口文件,在看webpack配置的时候会具体说。
Examples,至关于一个独立的Vue项目,主要的功能是展现文档。
Element的官网展现的内容,就是在这个examples目录。
这里文件夹,咱们介绍一下比较主要的文件
├── app.vue // 根组件 ├── components // 项目公共组件 ├── docs // 每个页面的组件都存在在这 ├── entry.js // 项目入口 ├── route.config.js // 路由配置,经过脚本自动生成而来 复制代码
咱们能够看到这个文件中
经过这代码import Element from 'main/index.js'
,把以前全部写的组件js都引入了进来。
这里配置了Webpack的alias,main/index.js
,其实就是咱们上面提到的src/index.js
文件
另外还引入了全局的css import 'packages/theme-chalk/src/index.scss';
import Vue from 'vue'; import entry from './app'; import VueRouter from 'vue-router'; import Element from 'main/index.js'; import hljs from 'highlight.js'; import routes from './route.config'; import demoBlock from './components/demo-block'; import MainFooter from './components/footer'; import MainHeader from './components/header'; import SideNav from './components/side-nav'; import FooterNav from './components/footer-nav'; import title from './i18n/title'; import 'packages/theme-chalk/src/index.scss'; import './demo-styles/index.scss'; import './assets/styles/common.css'; import './assets/styles/fonts/style.css'; import icon from './icon.json'; Vue.use(Element); Vue.use(VueRouter); Vue.component('demo-block', demoBlock); Vue.component('main-footer', MainFooter); Vue.component('main-header', MainHeader); Vue.component('side-nav', SideNav); Vue.component('footer-nav', FooterNav); const globalEle = new Vue({ data: { $isEle: false } // 是否 ele 用户 }); Vue.mixin({ computed: { $isEle: { get: () => (globalEle.$data.$isEle), set: (data) => {globalEle.$data.$isEle = data;} } } }); Vue.prototype.$icon = icon; // Icon 列表页用 console.log(routes); const router = new VueRouter({ mode: 'hash', base: __dirname, routes }); router.afterEach(route => { // https://github.com/highlightjs/highlight.js/issues/909#issuecomment-131686186 Vue.nextTick(() => { const blocks = document.querySelectorAll('pre code:not(.hljs)'); Array.prototype.forEach.call(blocks, hljs.highlightBlock); }); const data = title[route.meta.lang]; for (let val in data) { if (new RegExp('^' + val, 'g').test(route.name)) { document.title = data[val]; return; } } document.title = 'Element'; ga('send', 'event', 'PageView', route.name); }); new Vue({ // eslint-disable-line ...entry, router }).$mount('#app'); 复制代码
Element官网支持4种语言,docs一共有4个文件夹,每一个文件夹里面的内容基本是同样的,只是根据语言不一样,翻译一下。
这里咱们重点关注zh-CN文件下的中文文档
咱们能够看到里面所有都是md文档,而每个md文档,分别对应着其官网的展现页面
用markdown文档编写展现文档,其实这是各大组件库写展现文档的一个主流方式
可是用这种文档展现,须要咱们去克服一个技术上的难题:
如何把md文档上写的Vue代码,编译成Vue组件呢?
这里咱们先把问题抛出来,不做回答,让你们带着问题看下去。
咱们先看button.md,就是官网展现Button组件的时候显示的内容
该文件里其实就是一些普通的md语法。
咱们聚焦到上图中,红色圈圈里的代码
它是用了特定的标识符给包裹起来,好比:::demo
、:::
就把一些Vue组件的代码给包裹起来
:::demo \```html 这里面的内容有两个做用: 一、以Vue组件的形式展示出来 二、以md的形式展示出来 \``` ::: 复制代码
为何要同:::demo
这样的标识符去给代码包裹起来呢?
实际上是由于,Element要把demo标识符里面的代码给单独提取出去,去给Vue-loader处理,从而生成Vue组件。
那么具体实施思路是怎样的呢?
在说这个以前,先复习一下Webpack中的loaders相关的三个知识:
这个功能的实现,底层就是Element本身编写了一个loader,把里面的内容抽出来,而后传给vue-loader去编译。
咱们先看看其webpack配置
module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false } } }, { test: /\.md$/, use: [ { loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false } } }, { loader: path.resolve(__dirname, './md-loader/index.js') } ] } ] } 复制代码
能够看到,当遇到*.md文档的时候
首先会用./md-loader/index.js
,去处理。
而后把处理的结果,再传给vue-loader.
最终Vue-loader就帮咱们编译成Vue组件
那么./md-loader/index.js
作了什么呢?
总结来讲,就是这个loader最终返回的是一段字符串
这段字符串就是符合Vue SFC 规范的字符串
那么这一串字符串,就能够被vue-loader去编译,为咱们生成Vue组件。
因此一个一个单独的md文档,经过路由+webpack的配置,其实跟一个个单独的Vue组件是差很少的,可是同时却提供了咱们写md文档的方便
当咱们跑起来项目的时候,修改这里的md文档,是会实时更新的。
咱们试试题目修改为 Ynet-button,那么在官方就能够看到
其实不少人都说,若是要理解一个项目的概要,首先要看的就是package.json
它记录了项目的依赖以及脚本,让你理解项目的一些主要功能点,咱们来看看它的大概样式
// 为了避免增长文章长度,只截取关键部分,完整请看源码 { "name": "element-ui", "version": "2.11.1", "description": "A Component Library for Vue.js.", // 发包上去后,项目的入口文件 // import Element from 'element-ui' 时候引入的就是main中的文件 "main": "lib/element-ui.common.js",// element-ui.common.js是commonsjs规范 lib/index.js是umd规范 // 当你npm publish 发包的时候,发什么到Npm上,由files决定,功能和.gitignore相似 "script": {}, // webpack配置,nodejs脚本,这部分单独在下文中介绍 "files": [ "lib", "src", "packages", "types" ], // TypeScript 入口文件 "typings": "types/index.d.ts", // 仓库信息 "repository": { "type": "git", "url": "git@github.com:ElemeFE/element.git" }, // 项目主页的URL地址 "homepage": "http://element.eleme.io", "keywords": [ "eleme", "vue", "components" ], // 使用MIT协议,具体见参考资料 "license": "MIT", // 提issuse、bugs的地方 "bugs": { "url": "https://github.com/ElemeFE/element/issues" }, // unpkg.com是一个源自 npm 的全球快速 CDN // 若是经过unpkg.com这种CDN方式引入库的话,默认文件路径 // 官方文档在CDN引入的时候有提 https://element.eleme.cn/#/zh-CN/component/installation // lib/index.js是umd规范,由webpack.conf.js生成 "unpkg": "lib/index.js", // 声明当前模块包含 style 部分,并指定入口文件。 "style": "lib/theme-chalk/index.css", "dependencies": { }, // 解决模块之间相互依赖的问题,具体见参考资料 "peerDependencies": { "vue": "^2.5.17" }, "devDependencies": { } } 复制代码
package.json中,script脚本的理解对我来讲难度是比较大的,因此这里单独拎出来去说。
{
"scripts": {
// 安装依赖,官方推荐优先用yarn
"bootstrap": "yarn || npm i",
// build/bin/iconInit.js 解析icon.scss。把全部的icon的名字放在icon.json里。最后挂到Vue原型上的$icon的上,方便循环出来.具体使用见【补充资料】
// build/bin/build-entry.js 根据components.json,生成src/index.js文件,核心就是json-templater/string插件的使用【插件使用见参考资料】
// build/bin/i18n.js 根据examples/i18n/page.json和模板,生成不一样语言的demo
// build/bin/version.js 根据package.json中的versions,生成examples/versions.json
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
// build/bin/gen-cssfile // 根据components.json,生成生成package/theme-chalk/index.scss
// gulp build --gulpfile packages/theme-chalk/gulpfile.js 用gulp构建工具,编译scss、压缩、输出css
// cp-cli 是一个跨平台的copy工具,和CopyWebpackPlugin相似、linux的cp差很少(不具备跨平台的功能),
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
// 把src目录下的除了index.js入口文件外的其余文件经过babel转译,而后移动到lib文件夹下。
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
// 跑起来demo
"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",
// 总体构建,打包出lib库
"dist": "npm run clean && npm run build:file && npm run lint && 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",
// 生成umd模块的语言包
"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 bui
// 对项目进行eslint检测
"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",
复制代码
该命令跑了4个任务
`node build/bin/iconInit.js` // 解析icon.scss。把全部的icon的名字放在icon.json里。最后挂到Vue原型上的$icon的上 & `node build/bin/build-entry.js` // 根据components.json,生成src/index.js文件,核心就是json-templater/string插件的使用【插件使用见参考资料】 & `node build/bin/i18n.js` // 根据examples/i18n/page.json和模板,生成不一样语言的demo,当选择语言不同,会跑不一样语言的文件夹。 & `node build/bin/version.js` // 根据package.json中的versions,生成examples/versions.json,而后在header中获取这个versions.json来使用 复制代码
iconInit.js
这文件是为了把icon.scss里面全部的icon名字,抽离出来,而后存放在 examples/icon.json文件上。
但是Element为何要把icon的名字提取出来呢?
咱们看一下,最终展现在官网上的demo就知道了
由于Element中Icon有太多了,一个一个去写就太蠢了,因此才把全部icon都存在一个数组里,而后经过v-for循环出来
icon.json
这个文件,咱们能够看到最终抽离出来的效果
最后使用,咱们能够看到入口文件entry.js、与icon.md中看到做者这么作的最终使用状况
// entry.js
import Vue from 'vue';
import icon from './icon.json';
Vue.prototype.$icon = icon; // Icon 列表页用
------------------------------------------------------------------
// icon.md
### 图标集合
<ul class="icon-list">
// 直接把全部的icon循环出来
<li v-for="name in $icon" :key="name">
<span>
<i :class="'el-icon-' + name"></i>
<span class="icon-name">{{'el-icon-' + name}}</span>
</span>
</li>
</ul>
复制代码
build-entry.js
在上面介绍src/index.js
的时候,咱们知道index.js文件,是经过这个脚本去自动化构建生成的,它究竟是如何实现的呢?
这里咱们结合源码来看:
// 把全部组件的依赖关系,引入进来 var Components = require('../../components.json'); var fs = require('fs'); // https://www.npmjs.com/package/json-templater 可让string与变量结合,输出一些内容 var render = require('json-templater/string'); // https://github.com/SamVerschueren/uppercamelcase 转化为驼峰 foo-bar >> FooBar var uppercamelcase = require('uppercamelcase'); var path = require('path'); var endOfLine = require('os').EOL;// os.EOL属性是一个常量,返回当前操做系统的换行符(Windows系统是\r\n,其余系统是\n) // 生成文件的名字和路径 var OUTPUT_PATH = path.join(__dirname, '../../src/index.js'); var IMPORT_TEMPLATE = 'import {{name}} from \'../packages/{{package}}/index.js\';'; var INSTALL_COMPONENT_TEMPLATE = ' {{name}}'; var MAIN_TEMPLATE = `/* Automatically generated by './build/bin/build-entry.js' */ {{include}} import locale from 'element-ui/src/locale'; import CollapseTransition from 'element-ui/src/transitions/collapse-transition'; const components = [ {{install}}, CollapseTransition ]; const install = function(Vue, opts = {}) { locale.use(opts.locale); locale.i18n(opts.i18n); components.forEach(component => { Vue.component(component.name, component); }); Vue.use(InfiniteScroll); Vue.use(Loading.directive); Vue.prototype.$ELEMENT = { size: opts.size || '', zIndex: opts.zIndex || 2000 }; Vue.prototype.$loading = Loading.service; Vue.prototype.$msgbox = MessageBox; Vue.prototype.$alert = MessageBox.alert; Vue.prototype.$confirm = MessageBox.confirm; Vue.prototype.$prompt = MessageBox.prompt; Vue.prototype.$notify = Notification; Vue.prototype.$message = Message; }; /* istanbul ignore if */ if (typeof window !== 'undefined' && window.Vue) { install(window.Vue); } export default { version: '{{version}}', locale: locale.use, i18n: locale.i18n, install, CollapseTransition, Loading, {{list}} }; `; delete Components.font; // 获取全部组件的名字,存放在数组中 var ComponentNames = Object.keys(Components); var includeComponentTemplate = []; var installTemplate = []; var listTemplate = []; ComponentNames.forEach(name => { // menu-item 转化为 MenuItem var componentName = uppercamelcase(name); // IMPORT_TEMPLATE = 'import {{name}} from \'../packages/{{package}}/index.js\';'; includeComponentTemplate.push(render(IMPORT_TEMPLATE, { name: componentName, package: name })); // 获得的数据结构是: includeComponentTemplate = ['import MenuItem from \'../packages/menu-item/index.js\';', ....] if (['Loading', 'MessageBox', 'Notification', 'Message', 'InfiniteScroll'].indexOf(componentName) === -1) { // INSTALL_COMPONENT_TEMPLATE = ' {{name}}'; installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, { name: componentName, component: name })); } // 获得的数据结构是: installTemplate = ['Pagination', 'Dialog', ...] if (componentName !== 'Loading') listTemplate.push(` ${componentName}`); }); // 这里若是理解include、install、version、list分别表明什么,下面的代码就容易理解了 // 其实就是用json-templater库,把 MAIN_TEMPLATE 中的字符串,和include、install、version、list这些变量结合出来,最终的样式就是src/index.js的样式 var template = render(MAIN_TEMPLATE, { include: includeComponentTemplate.join(endOfLine), install: installTemplate.join(',' + endOfLine), version: process.env.VERSION || require('../../package.json').version, list: listTemplate.join(',' + endOfLine) }); // 结果输出到src/index.js中 fs.writeFileSync(OUTPUT_PATH, template); 复制代码
大概实现逻辑就是,根据components.json,生成src/index.js文件
核心就是json-templater/string插件的使用【插件使用见参考资料】
build/bin/i18n.js
Element项目的国际化,分为两个部分,分别用两种不一样的方式去处理。
第一部分是,官网的Demo展现国际化
第二部分是,组件源码国际化
而build/bin/i18n.js
是帮咱们完成第一部分的国际化工做的
第一部分:Element的展现的官网是如何实现国际化的
首先官网的主页(index)页面的国际化,是以examples/pages/template
为公共的模板,再genuine不一样的语言,分别生成不一样的文件
咱们能够看看template里有什么
里面是一个个.tpl文件,每个文件都是一个模板
而每一个tpl文件,都是符合SFC规范的的Vue文件格式。
在tpl文件中,须要国际化的地方,会根据必定的格式,用数字标识出来
// guide.tpl <script> export default { data() { return { lang: this.$route.meta.lang, navsData: [ { path: '/design', name: '<%= 1 >' // 国际化标志位 }, { path: '/nav', name: '<%= 2 >'// 国际化标志位 } ] }; } }; </script> 复制代码
而后根据不一样的语言,把examples/i18n/page.json
(这个文件包含首页全部的国际化字段)里,提早写好的字段,按照标志位填充进去
最终生成的这一套组件,组合起来就是就是Element展现的首页
这个首页是支持多语言的,每一种语言都有对应的一个vue文件。
那么它是如何作到不一样的语言,有不一样的vue文件呢?
build/bin/i18n.js,就是帮咱们完成这个事项。
大概的工做流程就是,引入examples/i18n/page.json
,而后循环,根据不一样的数据结构
把tpl文件的标志标志位,经过正则匹配出来,并替换成本身预先设定好的字段
// 把标志位地方的字段,替换成page.json里预先写好的字段 content = content.replace(new RegExp(`<%=\\s*${ key }\\s*>`, 'g'), pairs[key]); // 最后再输出 fs.writeFileSync(outputPath, content); 复制代码
最终看到的结果就是examples里面的内容
这样就完成了官网首页的国际化,那么markdown文档的国际化呢?
个人答案是,本身翻译的,这部分是没有用自动化流程去构建,只是用脚本为咱们生成一个空的md文档,内容由咱们本身去编写。(若是错了,请指正)
第二部分:组件源码的国际化
在 packages/color-picker/src/components/picker-dropdown.vue
中,咱们在模板部分能够看到这个语言包的使用:
<el-button
size="mini"
type="text"
class="el-color-dropdown__link-btn"
@click="$emit('clear')">
{{ t('el.colorpicker.clear') }}
</el-button>
<el-button
plain
size="mini"
class="el-color-dropdown__btn"
@click="confirmValue">
{{ t('el.colorpicker.confirm') }}
</el-button>
复制代码
这个t('el.colorpicker.clear')
返回的是什么内容呢?
咱们再src/locale/lang
下面能够看到
export default { el: { colorpicker: { confirm: '肯定', clear: '清空' }, } 复制代码
因此t函数,实际上会根据你的语言,去获取不一样的语言包,而后再去加载去对于的字段出来,这一点跟普通项目的国际化没什么区别。
可是Element是如何取构建t函数的呢?是经过什么手段帮t绑定到Vue上面呢
这部分的操做是在src/locale/lang/index.js
完成的
若是你没有设置语言包,在index.js里面就会默认使用中文
这个命令帮咱们跑了3个命令
`node build/bin/gen-cssfile` // 根据components.json,生成生成package/theme-chalk/index.scss,把全部组件的样式都导入到index.scss && `gulp build --gulpfile packages/theme-chalk/gulpfile.js` // 用gulp构建工具,编译scss、压缩、输出css && `cp-cli packages/theme-chalk/lib lib/theme-chalk` //cp-cli 是一个跨平台的copy工具,和CopyWebpackPlugin相似、linux的cp差很少(不具备跨平台的功能) 把文件复制到lib/theme-chalk下 复制代码
首先咱们知道ElemntUI在使用的时候,是提供2种方式的。
一种是全局引入、一种是按需引入。
因此在样式的处理上,也有要两种处理方法,分别用于全局引入、按需引入的。
咱们先看看两种引入方式的具体使用:
全局引入
import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import App from './App.vue'; Vue.use(ElementUI); new Vue({ el: '#app', render: h => h(App) }); 复制代码
按需引入
import Vue from 'vue'; import { Button, Select } from 'element-ui'; // 上面这句会经过babel-plugin-component插件编译为 // var button = require('element-ui/lib/button') // require('element-ui/lib/theme-chalk/button.css') // 如何转义请看配置 https://element.eleme.cn/#/zh-CN/component/quickstart import App from './App.vue'; Vue.use(Button) Vue.use(Select) new Vue({ el: '#app', render: h => h(App) }); 复制代码
gulpfile.js 实现样式打包
既然有两种引入方式,那么Element在打包组件的样式,就必须打包两种方案。
还记得咱们上面介绍的packages/theme-chalk
吗?
其实只要把packages/theme-chalk下的全部scss都编译成css就能够了
当你须要全局引入的时候,就去引入index.scss文件,这样全部组件的样式都齐全了。
若是你想按需引入,好比按需引入button组件,那么只要引入button.scss文件,就能够了。
那么如何把packages/theme-chalk下的全部scss都编译成css?(Element是如何打包样式的?)
这一点webpack不容易办到(鉴于我使用webpack的渣渣经验,我没想到该如何用webpack去实现会比较优雅,若是小伙伴知道,欢迎分享),可是gulp却很容易
因此ElementUI的样式打包,并非用webpack的,是用了gulp,基于工做流去处理样式。
咱们看看Element如何结合gulp去使用
'use strict'; const { series, src, dest } = require('gulp'); const sass = require('gulp-sass'); // 编译gulp工具 const autoprefixer = require('gulp-autoprefixer');// 添加厂商名字工具 const cssmin = require('gulp-cssmin'); // 压缩css工具 function compile() { return src('./src/*.scss') // 引入src下全部的scss样式 .pipe(sass.sync()) // 把scss文件编译成css .pipe(autoprefixer({ // 基于目标浏览器版本,添加厂商前缀 browsers: ['ie > 9', 'last 2 versions'], cascade: false })) .pipe(cssmin()) // 压缩css .pipe(dest('./lib')); // 输出到lib下 } function copyfont() { return src('./src/fonts/**') // 读取src/fonts下的全部文件 .pipe(cssmin()) // 压缩 .pipe(dest('./lib/fonts')); // 输出到lib/fonts下 } exports.build = series(compile, copyfont); 复制代码
最终就会帮咱们打包出了样式文件。
根据components.json,生成生成package/theme-chalk/index.scss,把全部组件的样式都导入到index.scss
这样每次新增组件,就不用手动去引入新增组建的样式了
cp-cli 是一个跨平台的copy工具,和CopyWebpackPlugin相似、linux的cp差很少(不具备跨平台的功能) 把文件复制到lib/theme-chalk下
咱们能够看到E的webpack配置主要要由下面5中配置去分工
webpack.common.js
生成commonjs2规范的入口文件 /lib/element-ui.common.js,用于import的方法引入
webpack.component.js
把各组件以commonjs规范单独打包出来,提供按需引入的功能
webpack.conf.js
生成umd规范的入口文件/lib/index.js,用于CDN方法引入
webpack.test.js
webpack.test.js 跑单元测试的,这里咱们忽略。
webpack.demo.js
用于跑Element官网的基础配置
Element中还用了makefile为咱们编写了一些额外的脚本
当咱们在命令行中输入 make时,能够看到
这是已经帮咱们编写好的脚本
这里重点说一下 make new <component-name> [中文]
这个命令
这是用来建立一个新的组件的,而后这个命令会帮咱们基于新建好的组件,帮咱们进行一系列的自动化操做
当运行这个命令的时候,其实运行的是 node build/bin/new.js
new.js脚本主要作了下面几件事:
因此当咱们要在Elment架构上,新建一个elect-bill组件
只要执行 make new elect-bill 电子帐号,而后不用配置,就能够直接去开发了