序言
ElementUI 作为当前运用的最广的 Vue PC 端组件库,很多 Vue 组件库的架构都是参照 ElementUI 做的。作为一个有梦想的前端(咸鱼),当然需要好好学习一番这套比较成熟的架构。
同时Element是一款Vue的UI框架,它可以将CSS,JavaScript,Vue知识糅合在一起。
本篇文章简单的分析一下Element安装 和 脚本命令!
(1) Element-ui 源码地址
element-ui 2.0 版本地址:github.com/ElemeFE/ele…
(2) Element-ui 脚本命令解析
对于项目来说,除了对整体结构的浏览,第一步要做的事,就是查看项目的核心文件,package.json。
如果说package.json是学习框架的第一部分,那么其中scripts对象则是重中之重.script里边指定了项目的生命周期个各个环节需要执行的命令,体现了项目的开发、测试、打包、部署等架构关系
从下面的代码块可以看出,scripts 下很多内容,我们需要关心的主要有两个,一个dist(项目构建,生成打包文件,类似vue-cli 的 npm run build),一个dev(同 vue-cli 的 npm run dev)
"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",
// 编译icon文件,编译源码入口文件,编译i18n文件,编译版本信息文件
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-chalk/gulpfile.js && cp-cli packages/theme-chalk/lib lib/theme-chalk",
// SCSS生成CSS并创建入口文件
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
// 编译工具文件
"build:umd": "node build/bin/build-locale.js",
// 编译umd风格国际化文件
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage",
// 清除生成的文件
"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 && 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",
// 打包生成最终文件
"i18n": "node build/bin/i18n.js",
// 生成 examples/pages下国际化相关vue文件
"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",
// 发布版本
"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",
// CI环境的单元测试,会启动浏览器
"test:watch": "npm run build:theme && cross-env BABEL_ENV=test karma start test/unit/karma.conf.js"
// 仅单元测试
},
复制代码
首先咱们先看dist
"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"
复制代码
这里发现element在 dist 这一步做了好多事情呀 有很多的脚本 这里我们大致的分析一下
1.npm run clean : 执行clean命令 会删除掉之前打包的文件
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage"
复制代码
2.npm run build:file :build后会执行以下四个脚本
node build/bin/iconInit.js & //生成 examples/icon.json
node build/bin/build-entry.js & //生成 src/index.js
node build/bin/i18n.js & //生成 examples/pages下国际化相关vue文件
node build/bin/version.js //生成 examples/versions.json
复制代码
node build/bin/iconInit.js
通过 postcss 解析 icon.scss ,筛选出类名并最终导出到 icon.json 文件
var postcss = require('postcss');
var fs = require('fs'); // Nodejs内置的fs模块用于文件系统模块,负责读写文件
var path = require('path'); // Nodejs内置的path模块用于处理文件路径
var fontFile = fs.readFileSync(path.resolve(__dirname, '../../packages/theme-chalk/src/icon.scss'), 'utf8');
//同步的读取到icon.scss这个文件
var nodes = postcss.parse(fontFile).nodes;
//解析源 css 并返回一个新的根Document节点,其中包含源 CSS 节点。
var classList = [];
nodes.forEach((node) => {
var selector = node.selector || '';
var reg = new RegExp(/\.el-icon-([^:]+):before/);
var arr = selector.match(reg);
if (arr && arr[1]) {
classList.push(arr[1]);
}
});
//通过postcss解析后样式表后,通过正则匹配el-icon-(x):before中的内容,并存入数组中
classList.reverse(); // 希望按 css 文件顺序倒序排列
fs.writeFile(path.resolve(__dirname, '../../examples/icon.json'), JSON.stringify(classList), () => {});
// 把转换为json后的样式表 放到了examples/icon.json下
// icon.json文件格式如下
// ["info","error","success","warning",......]
复制代码
node build/bin/i18n.js
生成 examples/pages下国际化相关vue文件
var fs = require('fs');
var path = require('path');
var langConfig = require('../../examples/i18n/page.json');
//以 i18n/page.json 作为数据,再以 pages/templates 作为模版来生成 pages 目录下的多语言版本
langConfig.forEach(lang => {
try {
// 判断当前文件是否存在
fs.statSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
} catch (e) {
// 创建新的文件
fs.mkdirSync(path.resolve(__dirname, `../../examples/pages/${ lang.lang }`));
}
Object.keys(lang.pages).forEach(page => {
var templatePath = path.resolve(__dirname, `../../examples/pages/template/${ page }.tpl`);
// 对应国际化版本的vue文件
var outputPath = path.resolve(__dirname, `../../examples/pages/${ lang.lang }/${ page }.vue`);
// 读取
var content = fs.readFileSync(templatePath, 'utf8');
// 获取内容对象
var pairs = lang.pages[page];
Object.keys(pairs).forEach(key => {
// 将内容,按照键进行替换
content = content.replace(new RegExp(`<%=\\s*${ key }\\s*>`, 'g'), pairs[key]);
});
// 往对应的文件里面写入
fs.writeFileSync(outputPath, content);
});
});
复制代码
node build/bin/version.js
记录 Element 版本号到examples/version.json,这个需要再官方网站上切换展示
var fs = require('fs');
var path = require('path');
// 获取版本信息
var version = process.env.VERSION || require('../../package.json').version;
// 比较重要的版本
var content = { '1.4.13': '1.4', '2.0.11': '2.0', '2.1.0': '2.1', '2.2.2': '2.2', '2.3.9': '2.3', '2.4.11': '2.4', '2.5.4': '2.5', '2.6.3': '2.6', '2.7': '2.7.2' };
// 写入文件
if (!content[version]) content[version] = '2.8';
// 写入文件
fs.writeFileSync(path.resolve(__dirname, '../../examples/versions.json'), JSON.stringify(content));
写入完后 会再 examples/components/header里面渲染出来
复制代码
node build/bin/build-entry.js
构建 src/index.js 这个文件可能随着组件的增加删除会经常变动,故用脚本来产生
var Components = require('../../components.json');// 所有可用组件的映射表 组件
var fs = require('fs');
var render = require('json-templater/string');// z字符串模板生成工具
var uppercamelcase = require('uppercamelcase'); // 转驼峰 a-bc =>ABc
var path = require('path');
var endOfLine = require('os').EOL;
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(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 => {
var componentName = uppercamelcase(name);
includeComponentTemplate.push(render(IMPORT_TEMPLATE, {
name: componentName,
package: name
}));
if (['Loading', 'MessageBox', 'Notification', 'Message'].indexOf(componentName) === -1) {
installTemplate.push(render(INSTALL_COMPONENT_TEMPLATE, {
name: componentName,
component: name
}));
}
if (componentName !== 'Loading') listTemplate.push(` ${componentName}`);
});
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)
});
fs.writeFileSync(OUTPUT_PATH, template);
console.log('[build entry] DONE:', OUTPUT_PATH);
复制代码
3.npm run lint :执行脚本中的lint命令,eslint检查
"lint": "eslint src/**/* test/**/* packages/**/* build/**/* --quiet",
复制代码
4.webpack
webpack --config build/webpack.conf.js :
webpack打包,lib目录下打包 index.js文件 (浏览器使用的js包).
webpack --config build/webpack.common.js :
webpack打包,lib目录下生成element-ui.common.js 文件)
webpack --config build/webpack.component.js :
webpack打包,lib下生成组件的 js 文件
复制代码
7.npm run build:utils :执行 build:utils 命令 ,babel 打包src目录下文件至lib,忽略index.js
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js"
复制代码
8.npm run build:umd :执行 build:umd 命令 ,lib/locale下生成国际化相关文件
"build:umd": "node build/bin/build-locale.js"
复制代码
9.npm run build:theme:执行 build:theme 命令 ,生成样式
"build:theme": "
node build/bin/gen-cssfile &&
gulp build --gulpfile packages/theme-chalk/gulpfile.js &&
cp-cli packages/theme-chalk/lib lib/theme-chalk"
复制代码
首先咱们先看div
"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",
复制代码
1.npm run bootstrap : 安装依赖包 同理 “yarn || npm i”
2.npm run build:file : 和上面的dist的一样 4个步骤
3.node build/bin/template.js 监听template下文件的变化并且重新生成多语言版本的pages
const path = require('path');
const templates = path.resolve(process.cwd(), './examples/pages/template');
// process.cwd() 运行 node 命令时所在的文件夹的绝对路径,保证了文件在不同的目录下执行时,路径始终不变
// 常用的 __dirname 获得当前执行文件所在目录的完整目录名 类似./
const chokidar = require('chokidar');
// 监听文件变化
let watcher = chokidar.watch([templates]);
watcher.on('ready', function() {
watcher
.on('change', function() {
exec('npm run i18n');
});
});
function exec(cmd) {
return require('child_process').execSync(cmd).toString().trim();
子进程 转字符串 去空格
}
复制代码
(3) 启动成功
注意事项
这里有一点 要注意了 Element这里抛出了一个http://0.0.0.0:8085 直接访问这个地址是访问不到
必须访问http://localhost:8085/ 才行! 这个坑我踩了好久才踩掉的?
复制代码
招聘广告
言重式招聘 寻人!!!寻志同道合之人、寻竭忠尽智之人、寻深思远虑之人、寻勤恳至诚之人
浙江大华技术股份有限公司-软研-智慧城市产品研发部招聘高级前端!!!!!
欢迎大家来聊,有意向可发送简历到chen_zhen@dahuatech.com
一个人的价值,应该看他贡献什么,而不应当看他取得什么 ———— 无奖竞猜
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END