大家好,我是wo不是黄蓉,今年学习目标从源码共读开始,希望能跟着若川大佬学习源码的思路学到更多的东西。
本文是为了学习组件库搭建思路而衍生的一篇文章,最近在思考搭建项目业务组件库,发现公司封装的命令也是基于vue2
的,因此开始了学习ElementUI
源码学习之路。
调试环境准备
在package.json
的scripts
中配置
"dev:sourcemap": "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 --sourcemap",
配置sourcemap
就可以开启源码调试了,在项目中打好断点
入口文件 examples\entry.js
找到build\webpack.demo.js
发现就是一些基础的webpack
配置,查看webpack
配置文件,发现入口为examples\entry.js
这正是一个vue项目,因此npm run dev
就是启动一个vue
项目,而上面build
生成的那些文件,是运行时所需的一些配置
npm run dev
首页内容
examples\components\header.vue
页面导航部分代码,最终生成的路径:/zh-CN/component/installation
<li class="nav-item">
<router-link
active-class="active"
:to="`/${ lang }/component`">{{ langConfig.components }}
</router-link>
</li>
router-link
的内容应该在路由配置表里面可以找到,从examples\entry.js
可以找到路由配置相关信息
import VueRouter from 'vue-router';
import routes from './route.config';
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'hash',
base: __dirname,
routes
});
router
配置来自examples\route.config.js
//这边import使用到的内容就是上节我们说的从npm run build中打包过来的内容
import navConfig from './nav.config';
const LOAD_MAP = {
'zh-CN': name => {
return r => require.ensure([], () =>
r(require(`./pages/zh-CN/${name}.vue`)),
'zh-CN');
},
'en-US': name => {
return r => require.ensure([], () =>
r(require(`./pages/en-US/${name}.vue`)),
'en-US');
},
};
const LOAD_DOCS_MAP = {
"zh-CN": (path) => {
return (r) =>
require.ensure([], () => r(require(`./docs/zh-CN${path}.md`)), "zh-CN")
},
"en-US": (path) => {
return (r) =>
require.ensure([], () => r(require(`./docs/en-US${path}.md`)), "en-US")
},
}
//组件懒加载-这边加载的是语言包下的导航类的组件
const load = function(lang, path) {
return LOAD_MAP[lang](path);
};
//加载文档组件-这边加载的才是真正的组件
const loadDocs = function(lang, path) {
return LOAD_DOCS_MAP[lang](path)
}
//注册路由
const registerRoute = (navConfig) => {
let route = [];
Object.keys(navConfig).forEach((lang, index) => {
let navs = navConfig[lang];
route.push({
path: `/${ lang }/component`,
redirect: `/${ lang }/component/installation`,
component: load(lang, 'component'),
children: []
});
navs.forEach(nav => {
if (nav.href) return;
if (nav.groups) {
nav.groups.forEach(group => {
group.list.forEach(nav => {
addRoute(nav, lang, index);
});
});
} else if (nav.children) {
nav.children.forEach(nav => {
addRoute(nav, lang, index);
});
} else {
addRoute(nav, lang, index);
}
});
});
function addRoute(page, lang, index) {
//判断是否是changelog,不是使用loadDocs加载组件
const component = page.path === '/changelog'
? load(lang, 'changelog')
: loadDocs(lang, page.path);
let child = {
path: page.path.slice(1),
meta: {
title: page.title || page.name,
description: page.description,
lang
},
name: 'component-' + lang + (page.title || page.name),
component: component.default || component
};
route[index].children.push(child);
}
return route;
};
let route = registerRoute(navConfig);
export default route;
load
函数,这个函数主要是让组件懒加载,LOAD_MAP[lang]
返回的是一个函数,即
(name) => {
return (r) =>
require.ensure([], () => r(require(`./pages/zh-CN/${name}.vue`)), "zh-CN")
}
调用LOAD_MAP[lang]
这个函数,返回内容即
(r) =>require.ensure([], () => r(require(`./pages/zh-CN/${name}.vue`)), "zh-CN")
registerRoute
函数会根据examples\nav.config.json
这个json里面配置的内容为各种语言的映射,包括导航和菜单组织信息,将导航和菜单信息都提前配置好在这个文件中,后续组件导航就根据这个文件来生成的。
在addRoute
函数中,最终组件使用的是load
和loadDocs
来导入组件的,这两个的区别就是,load加载的组件是导航类的,这个可以在examples\pages\zh-CN
中进行验证
loadDocs
加载的组件是examples\docs\zh-CN
的
为什么这边加载的组件是md文件呢?
因为在build\webpack.demo.js
中有这么一段配置,这个是用来解析md
文件的loader,先将md
文件解析成vue
文件,再使用vue-loader
解析vue
文件,再进行展示页面
{
test: /\.md$/,
use: [
{
loader: "vue-loader",
options: {
compilerOptions: {
preserveWhitespace: false,
},
},
},
{
loader: path.resolve(__dirname, "./md-loader/index.js"),
},
],
},
总结:
看一点头绪都没有的代码不妨先看降级的代码,比如我一开始是想学习element-plus
源码的,但是新的结构让我觉得陌生,学习摸索大概有一两个星期,也就大概看了个架子,内心其实也是比较受打击的。
就像我之前说的,我不是一个学习技术特别厉害的人,因此学习跨度更大的代码让我提不起兴趣,想要逃避。但是我当我换成看element
源码后完全不会有这样的感觉,即使有不太懂的,也可以在网上找到答案,解决我的疑惑。
网友评论