按需加載,本質上是把一個組件庫的不一樣組件拆分紅不一樣文件,按照須要引用對應的文件,而該文件暴露一個install方法,供Vue.use使用。 好比:我只想引用element庫裏的一個Button組件css
import Button from 'element-ui/lib/Button.js'
import Button from 'element-ui/lib/theme-chalk/Button.css'
Vue.use(Button);
複製代碼
上面的寫法比較繁瑣,並且須要知道每一個組件的實際路徑,使用起來並不方便,因此咱們還須要藉助一個轉換插件。vue
先來看看element是怎麼作的,官方的的「快速手上」: webpack
element使用一個了babel插件,做用就是代碼轉換:git
import { Button } from 'components'
// 轉換爲
var button = require('components/lib/button')
require('components/lib/button/style.css')
複製代碼
到這咱們能夠知道,要搭建一個按需加載的組件庫。主要工做須要兩點:github
咱們在項目的跟目錄建一個文件夾packages,下面放咱們的組件: web
packages下每個文件夾對應一個組件所須要的資源,在index.js定義組件的install方法。而packages/index.js存放了在全量加載時用的install方法vue-cli
import Button from './src/main';
Button.install = function(Vue) {
Vue.component(Button.name, Button);
};
export default Button;
複製代碼
<template>
<div>
我是一個Button組件
</div>
</template>
複製代碼
import Button from './Button';
import Loading from './Loading';
import LoadMore from './LoadMore';
const components = [
Button,
LoadMore,
Loading
];
const install = function(Vue) {
components.forEach(component => {
Vue.component(component.name, component);
});
}
if (typeof window !== 'undefined' && window.Vue) {
install(window.Vue)
}
export default {
install, // 全量引入
Button,
LoadMore,
Loading
};
複製代碼
組件代碼寫好了,接下來須要配置一下webpack的打包邏輯。咱們複用vue-cli生成的模板,在上面作一些必要更改:npm
每一個組件獨立生成一個對應的js和css,這就須要咱們在入口處就把組件的引用定義好:element-ui
const entrys = {
Button: path.resolve(__dirname, '../packages/Button'),
index: path.resolve(__dirname, '../packages')
};
const webpackConfig = merge(baseWebpackConfig, {
entry: entrys,
// ......
});
複製代碼
上述配置每增長一個組件都須要修改entrys,咱們能夠優化一下,使其動態生成:bash
const entrys = require(./getComponents.js)([組件目錄入口]);
const webpackConfig = merge(baseWebpackConfig, {
entry: entrys,
......
});
複製代碼
const fs = require('fs');
const path = require('path');
/**
* 判斷剛路徑是否含有index.js
* @param {String} dir
*/
function hasIndexJs(dir) {
let dirs = [];
try {
dirs = fs.readdirSync(dir);
} catch(e) {
dirs = null;
}
return dirs && dirs.includes('index.js');
}
/**
* 獲取指定入口和入口下包含index.js的文件夾的路徑
* @param {String} entryDir
*/
const getPath = function(entryDir) {
let dirs = fs.readdirSync(entryDir);
const result = {
index: entryDir
};
dirs = dirs.filter(dir => {
return hasIndexJs(path.resolve(entryDir, dir));
}).forEach(dir => {
result[dir] = path.resolve(entryDir, dir);
});
return result;
}
module.exports = getPath;
複製代碼
默認生成的js文件並不支持ES6引入,在這裏咱們設置成umd
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('[name].js'),
library: 'LoadOnDemand',
libraryTarget: 'umd'
},
複製代碼
上面的組件庫打包發佈到npm上以後。咱們在使用的時候npm install babel-plugin-component -D以後,修改一下.babelrc.js:
"plugins": [
[
"component",
{
"libraryName": "load-on-demand", // 組件庫的名字
"camel2Dash": false, // 是否把駝峯轉換成xx-xx的寫法
"styleLibrary": {
"base": false, // 是否每一個組件都默認引用base.css
"name": "theme" // css目錄的名字
}
}
]
],
複製代碼
這裏提一下屬性camel2Dash,默認是開啓的,開啓狀態下假如你的組件名是vueCompoent,引用的css文件會變成vue-component.css。
上面demo的代碼放在了我的github github.com/jmx16449196… 你們若是有更好的實現方法,或者我上面還有什麼須要更正的錯誤,歡迎交流。