迁移原因
某些项目由于种种原因,之前采用的requirejs框架开发,由于其所有资源都是通过xhr请求获取,会导致用户等待时间过长等性能问题,又由于项目整体重构采用脚手架成本过大,风险过高,于是需自定义webpack配置,进行整体打包,这样再首次加载系统的时候代码体量会增大,但后续所有的请求都无需通过xhr,再一定程度上可提高用户体验
可行性分析
requirejs项目采用的AMD规范,而webpack也支持这种模块语法,这样webpack就能够动态分析项目中的依赖项,这是项目迁移成功的关键所在。
requirejs项目特点
- 使用amd语法
- js依赖直接写依赖地址
- html文本类依赖使用 text! 前缀引用
- css依赖使用css!前缀引用
迁移目标
- 不改变项目原有代码结构
- 所有依赖全部成功打包
- 引入vue文件解析
- vue文件与老代码格式共存
解决方案
- 编写js脚本,将所有text! css! 依赖加载前缀去除
- 配置webpack 中对应的loader,并增加vue-loader
- 对requirejs中的配置内容映射为webpack中的 alias,以解决路径解析问题
- 删除引入requirejs的script
配置文件
采用webpack4的配置文件如下,仅供参考
const path = require('path'),
WebpackNotifierPlugin = require('webpack-notifier'),
HtmlWebpackPlugin = require('html-webpack-plugin'),
CopyWebpackPlugin = require('copy-webpack-plugin'),
{VueLoaderPlugin} = require('vue-loader'),
MiniCssExtractPlugin = require("mini-css-extract-plugin");
const webpack = require('webpack');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
let base = {
index: './main.js'
}
const SRCPATH = "src";
const isDev = process.env.NODE_ENV !== 'production';
let desPath = "./";
let proxyTarget = "";
let copyFiles = ['default.conf'].map((el)=>{
return {
// 从public中复制文件
from: path.resolve(__dirname, el),
// 把复制的文件存放到dis里面
to: path.resolve(desPath, 'dist')
}
})
module.exports = {
devtool: isDev ? 'source-map' : "eval", //配合 target 用来生成source-map 文件,以便 开发调试 debug 等, devtool 意指 开发工具
target: 'web',
entry: base,
devServer: {
// port: 8080,
static: path.resolve(desPath, '/dist'),
hot: true,
proxy: {
'/api': {
target: proxyTarget,
// secure: false, // 如果是https接口,需要配置这个参数
ws: true,
changeOrigin: true, // 是否跨域
pathRewrite: {
'^/api': '', // 重写接口
}
},
},
},
output: { //指定输出目录,和文件名
path: path.resolve(desPath, 'dist'),
filename: '[name]_[contenthash].js',
assetModuleFilename: 'assets/[name]_[contenthash][ext][query]',
clean: true
},
optimization: {
minimizer: [
`...`,
new CssMinimizerPlugin(),
],
},
externals: {
Vue: 'Vue',
Vuex: 'Vuex',
'vue-router': 'VueRouter',
echarts: 'echarts',
moment: 'moment',
'element-ui': 'ELEMENT',
vuedraggable: 'vuedraggable',
vant: 'vant'
},
resolve: { //resolve字段最常用的就是 alias (别名)属性,用来把一些冗长的路径替换为简单的字符,以便js中引入模块时更简洁
extensions: ['.js', '.vue', '.css', '.html', '...'],
alias: {
'@':path.resolve(__dirname, './mini'),
gotop: path.resolve(__dirname, 'src/js/com/gotop.js'),
carousel: path.resolve(__dirname, 'src/js/com/carousel.js'),
waterfall: path.resolve(__dirname, 'src/js/com/waterfall.js'),
biz: path.resolve(__dirname, './mini/js/biz'),
dao: path.resolve(__dirname, './mini/js/dao'),
common: path.resolve(__dirname, './mini/js/common'),
template: path.resolve(__dirname, './mini/js/template'),
widget: path.resolve(__dirname, './mini/js/widget'),
text: path.resolve(__dirname, './mini/js/lib/text'),
css: path.resolve(__dirname, './mini/js/lib/css'),
store: path.resolve(__dirname, './mini/js/common/store'),
config: path.resolve(__dirname, './mini/js/common/config'),
util: path.resolve(__dirname, './mini/js/common/util'),
Util: path.resolve(__dirname, './mini/js/common/util'),
constance: path.resolve(__dirname, './mini/js/common/constance'),
listmix: path.resolve(__dirname, './mini/js/common/listmix'),
treemix: path.resolve(__dirname, './mini/js/common/treemix'),
formmix: path.resolve(__dirname, './mini/js/common/formmix'),
workflowmix: path.resolve(__dirname, './mini/js/common/workflowmix'),
routermap: path.resolve(__dirname, './mini/js/common/routermap'),
}
},
resolveLoader: {
alias: {
"text": "raw-loader",
"css": ["style-loader", "css-loader"]
}
},
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
new VueLoaderPlugin(),
new MiniCssExtractPlugin(),
new WebpackNotifierPlugin({
title: 'Webpack 编译成功',
alwaysNotify: false,
onlyOnError: true
}), new HtmlWebpackPlugin({
template: "./mini/index.html", // 要打包输出哪个文件,可以使用相对路径
filename: "index.html" // 打包输出后该html文件的名称
}),
new CopyWebpackPlugin({
patterns: [
{
// 从public中复制文件
from: path.resolve(__dirname, 'mini/lib'),
// 把复制的文件存放到dis里面
to: path.resolve(desPath, 'dist/lib')
},
{
// 从public中复制文件
from: path.resolve(__dirname, 'mini/config.js'),
// 把复制的文件存放到dis里面
to: path.resolve(desPath, 'dist')
},
{
// 从public中复制文件
from: path.resolve(__dirname, 'mini/css'),
// 把复制的文件存放到dis里面
to: path.resolve(desPath, 'dist/css')
}
].concat(copyFiles),
})
],
module: {
unknownContextCritical: false,
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
include: path.resolve(__dirname, SRCPATH),
},
{
test: /\.m?js$/,
exclude: /(node_modules)|(lib)/,
include: path.resolve(__dirname, SRCPATH+"/js"),
use: [{
loader: 'babel-loader'
}]
},
{
test: /\.(css)|(less)$/i,
include: path.resolve(__dirname, SRCPATH),
// 'vue-style-loader',
use: [MiniCssExtractPlugin.loader, {
loader: "css-loader"
}, 'less-loader']
},
{
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/i,
include: path.resolve(__dirname, SRCPATH),
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 20 * 1024 // 16kb 64k
}
}
// use: [
// {
// loader: 'url-loader',
// options: {
// esModule: true,
// limit: 8196 * 2,
// name: "[name]_[contenthash:6].[ext]",
// // generator: (content, mimetype, encoding, resourcePath) => {
// // return `data:${mimetype}${
// // encoding ? `;${encoding}` : ''
// // },${content.toString(encoding)}`;
// // },
// },
// },
// ],
},
{
test: /\.html$/i,
include: path.resolve(__dirname, SRCPATH),
use: [
{
loader: 'raw-loader',
options: {
esModule: false,
},
},
]
},
// {
// test: /\.(png|jpg|gif)$/i,
// use: [
// {
// loader: 'url-loader',
// options: {
// esModule: false,
// limit: 8196 * 2,
// name: "img/[name]_[contenthash:6].[ext]",
// },
// },
// ],
// },
],
}
}