基于微前端qiankun写一个vue专用的插件
本次项目的目的,是基于qiankun这个微前端框架写一个vue专用的插件来方便在vue的项目里使用。
起初是想用js写的。但本着现在开发的是一个第三方类库,要考虑通用性。如果用js写,如果使用的项目时typescript的vue项目,就会遇到缺少类型定义文件的问题。于是采用ts开发。这样申明文件的生成也变得容易一些。
项目地址
https://github.com/Hades-li/qiankun-vue
安装
yarn add qiankun-vue
npm install qiankun-vue --save
从vue-cli到手写webpack配置
vue-cli是vue官方的脚手架工具,傻瓜式构建项目,非常好用。但也许它更适合去开发一整套应用,而不是一个小小的第三方库。
目录结构
- build: 存放webpack配置文件(后期手写)
- dist:最终文件输出目录
- example:用于展示用的列子。其实这个目录就是原来用vue-cli的生成的src目录,但由于我们要开发一个第三方库,src目录是用来放库源代码的地方,所以就把目录给换了。
- src:用于放源代码的地方。
- types:typescript类型定义文件
用vue-cli官方构建方式(弃用)
vue-cli提供了一个官方的构建库方法
vue-cli-service build --target lib --name myLib [entry]
这样构建的结果就和官方展示的一样,会同时生成多个js的库文件。而实际在项目引用时,我们只会用到一个。也就是说,我们根本不希望生成一堆我们不想要的东西。
修改vue.config.js构建(弃用)
放弃使用vue-cli官方自带的方法,剩下的方法就是改写vue.config.js配置来快速实现构建。以下是截取一些重要的代码段讲解
if (isProd) {
config.entry('index').clear().add('./src/index.ts')
config.output
.filename('index.js') // 输出文件名
.libraryTarget('umd') // 打包类型
.library('QiankunVue') // 全局变量名称
......
}
在生产环境下,入口文件为src路径下的入口文件。
输出文件设置成index.js,目标类型为umd格式,全局变量为QiankunVue
if (!isProd) {
config.entry('index').clear().add('./example/main.ts')
}
在开发环境下,目的不是把src的源代码打包输出,而是要运行example中的列子。所以就将入口文件设置成example下的main.ts文件。
// 排除掉Vue
config.externals({
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
})
由于我们开发的是vue的插件,所以需要将vue排除掉。
此时,执行yarn run build后,dist目录中就会只生成一个index.js文件,当然,由于我并没有屏蔽掉html模板插件,index.html还是会被生成。
最终我放弃了以上两种vue-cli的构建方式,因为我发现,打包出来的index.js文件大小,高达120+KB。这个大小明显偏大。但我却找不到原因。
webpack配置走起
这次改动,目的是做到完全不依赖vue-cli,开发,生产环境完全自定义。
build.js-采用函数式方式执行webpack打包。(暂时放弃)
以下是一些重点代码片段
webpack.config.base.js
基础配置,包含了生产和开发环境都需要配置
module.rules
// 预处理.ts文件
{
test: /\.ts$/,
use: [
'babel-loader',
{
loader: 'ts-loader',
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true
}
}
],
exclude: /node_modules/
},
// 预处理.vue文件
{
test: /\.vue$/,
loader: 'vue-loader'
},
这两段代码主要是预处理.ts文件和.vue文件。
babel-loader是可选的,作用是将es6代码转成es5用于兼容浏览器。如今的chrome,edge,firefox等现代浏览器,对于es6甚至es7支持已经很好了。不加babel-loader,打包文件尺寸能进一步缩小,运行效率还能更高一些,但本着也许还有人用ie的态度,顺手加一下吧。
// 预处理scss
{
test: /\.s[ac]ss$/,
use: [
env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
},
// 预处理css
{
test: /\.css$/,
use: [
env.NODE_ENV !== 'production' ? 'vue-style-loader' : MiniCssExtractPlugin.loader,
'css-loader'
]
},
预处理scss,和css样式。其实在vue-cli构建出来的项目中,还能支持less,stylu另外两种预编译样式文件,但我们现在是自定义webpack配置,本着,用到什么就配什么原则,只需要满足自己的需求即可。
// 预处理图片
{
test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 4096,
esModule: false, // 5.0版本以上要加
fallback: {
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
}
}
预处理图片,这里要着重说一下这个配置项的坑。此配置用于解析图片/文件路径,以及对图片进行base64转换。起初,我是直接用vue-cli审查出了一个webpack配置文件,将这部分拷贝过来,和以上代码相比,仅仅是没有esModule: false这个参数。运行的结果是,所有图片不展示,查看图片路径是这样<img src=[object module]>
。于是加上esModule:false就ok了。
以上原因是由于vue-loader在对.vue文件解析时,
<img src="../image.png">
标签会被编译成
createElement('img', {
attrs: {
src: require('../image.png') // commonJS语法导入函数 现在这是一个模块的请求了
}
})
require是commonJS规范的导入函数。而url-loader默认是识别es6的导入语法,即import。于是,最终转换出的代码就无法正常显示图片。esModule: false就是启用commonJS引入方式。
那为什么vue-cli构建出来的项目则不需要配置这个参数?
因为vue-cli所采用的url-loader版本号还停留在2.x.x。而最新版本已经到了4.x.x。旧版本url-loader是没有这个限制机制。新版url-loader是鼓励采用es6的标准规范来引入文件。commonJs是nodejs的规范。
plugins
plugins: [
new VueLoaderPlugin(), // 配合vue-loader
new ForkTsCheckerWebpackPlugin(), // 将ts-loader类型检查跑在一个独立线程加速编译
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"' + env.NODE_ENV + '"',
BASE_URL: '"/"'
}
}), // 给浏览器代码中添加全局变量
new FriendlyErrorsPlugin(), // 友好的错误提示
new MiniCssExtractPlugin(), // css提取
new CaseSensitivePathsPlugin() // 严格路径大小写
]
以上这些插件基本都是看着vue-cli审查加入的。有的可以不加,但加了,对于开发都是有极大帮助的。
webpack.config.prd.js
生产环境下的配置。
module.exports = merge(baseConf({ NODE_ENV: env }), {
mode: env,
entry: './src/index.ts',
output: {
path: resolve('dist'),
filename: 'index.js',
publicPath: '/',
chunkFilename: 'js/[name].[contenthash:8].js',
libraryTarget: 'umd',
library: 'QiankunVue'
},
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
},
plugins: [
// 清理dist文件夹
new CleanWebpackPlugin()
]
})
生产环境配置较为简单,首先通过webpack-merge
这个插件将webpack.config.base.js的配置合并过来,添加entry,和output的配置项,并且用cleanwebpackPlugin插件清理一下dist目录,就可以一键打包了。
webpack.config.dev.js
开发环境下的配置。
entry: './example/main.ts',
output: {
path: resolve('dist'),
filename: 'app.js',
publicPath: '/'
},
devServer: {
contentBase: resolve('dist'),
port: 8080,
hot: true,
progress: true, // 0-100%的进度提示
quiet: true // 去掉一堆告警信息
},
devtool: 'eval-source-map',
开发环境,同样合并webpack.config.base.js,改写一下entry指向example/main.ts。
配置一下开发服务器devServer,切记,别忘了安装webpack-dev-server
这个包。
plugins
plugins: [
new webpack.HotModuleReplacementPlugin(), // 支持热模块替换
new HtmlWebpackPlugin({
title: 'qiankun',
template: resolve('public/index.html'),
favicon: resolve('public/favicon.ico')
}), // 配置index.html模板
new CopyWebpackPlugin([
{
from: resolve('public'),
to: resolve('dist'),
toType: 'dir',
ignore: ['index.html']
}
]) // 拷贝插件,用于拷贝一些不参与打包的静态资源至dist目录
]
这些都是开发模式下必要的插件
以上的这些webpack配置,比起vue-cli提供的要少很多,比如我们甚至都没有针对.js文件编译loader配置项,因为我们的项目中纯玩ts。实际用vue-cli构建的ts项目是可以进行js,ts混合开发的。但纯手写webpack的目的,就是只配自己需要的。
本期重点介绍webpack配置,这也是此项目里难点之一,耗时费力。
遗留问题1-无法自动生成*.d.ts文件
开发一个标准的js库,不管你是用js开发还是ts,如今,都是要给自己的库写ts的类型申明文件。像类似lodash,jquery这些著名的库,早期都是没有申明文件。导致如果用于ts项目开发,类型检测机制就无法进行(早期没有typescript)。但现在,这些库都已经将类型申明补充进来了。
类型申明可以手写,但太费事。最好当然是自动生成。
按照官方说法,在tsconfig.js中配置了declaration: true
的属性时,就可以自动生成。但实际是没有任何反应。
当然,你如果直接用tsc --declaration命令行编译,则可以生成,目前这是个遗留问题。如果有大神知道解决方法忘指教。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。