123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- const path = require('path')
- const { SourceMapDevToolPlugin, DefinePlugin } = require('webpack')
- function resolve (dir) {
- return path.join(__dirname, dir)
- }
- const { getPages, changeProjectContext, getDirectories, checkProjectConfig, combineStyle } = require('./webpackUtils')
- const existsPages = getDirectories('./public')
- // 获取多页面文件夹
- const pages = getPages()
- // 校验新建页面是否与已有页面重复
- const pageNameDuplicated = existsPages.filter(name => pages[name])
- if (pageNameDuplicated.length > 0) {
- throw Error(`名为${pageNameDuplicated.join('、')}的页面已经存在,不能重复创建`)
- }
- const pageNames = Object.keys(pages)
- // 指定平台生成文件夹
- const platformPages = existsPages.filter(name => name.indexOf('-') === -1)
- // 开发服务器需要的配置
- const config = require('./public/common-assets/js/project.config')
- const { version: projectVersion, projectContext, backServerRoot, backServerType, proxyPath, assetsContext, loginPath, staticDir = 'static' } = config
- // 配置校验
- checkProjectConfig(config)
- // 获取单体后端上下文,供兼容JSP页面请求使用
- let soaBackServerContext = '__msaNeverIn__'
- if (backServerType === 'soa') {
- let root = backServerRoot.endsWith('/') ? backServerRoot.substring(0, backServerRoot.length - 1) : backServerRoot
- soaBackServerContext = root.substring(root.lastIndexOf('/'), root.length)
- }
- // 输出路径
- const outputDir = `dist${projectContext}/${projectVersion}${projectContext}`
- // 工程可以指定自己的目录
- const assetsDir = process.env.NODE_ENV === 'production' ? staticDir : ''
- module.exports = {
- // 多页面配置
- pages,
- // 工程上下文
- publicPath: projectContext,
- // 编译输出目录
- outputDir,
- // 静态文件父级目录
- assetsDir,
- // 默认在生成的静态资源文件名中包含hash以控制缓存
- filenameHashing: true,
- // 是否使用包含运行时编译器的 Vue 构建版本
- runtimeCompiler: false,
- // 如果这个值是一个对象,则会通过 webpack-merge 合并到最终的配置中
- // 如果你需要基于环境有条件地配置行为,或者想要直接修改配置,那就换成一个函数 (该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。在函数内,你可以直接修改配置,或者返回一个将会被合并的对象
- configureWebpack () {
- let config = {
- output: {},
- plugins: [],
- }
- // 运行时配置
- if (process.env.NODE_ENV === 'production') {
- // 输出主文件放置在对应页面目录
- config.output.filename = '[name]/[name].[contenthash].js'
- // 关闭默认配置,使用插件添加source map
- config.devtool = false
- const timestamp = (new Date()).toISOString().replace(/\D/g, '')
- config.plugins.push(
- new SourceMapDevToolPlugin({
- // 暂不将引用加到文件中
- append: false,
- // 指定sourceMap的生成路径
- filename: `${assetsDir}/source-map/${timestamp}/[file].map`,
- // 公共服务地址
- publicPath: 'http://127.0.0.1/',
- fileContext: '',
- }),
- )
- }
- config.plugins.push(
- new DefinePlugin({
- ASSETS_CONTEXT: `'${assetsContext}'`,
- DIREWOLF_VERSION: `'${process.env.npm_package_version}'`,
- }),
- {
- apply (compiler) {
- // 运行时插件,编译前判断工程上下文是否正确
- compiler.hooks.beforeCompile.tap('changeProjectContext', () => changeProjectContext(projectContext))
- },
- }, {
- apply (compiler) {
- // 运行时插件,编译完成后创建库文件
- if (process.env.NODE_ENV === 'production') {
- compiler.hooks.done.tap('combineCss', () => combineStyle(outputDir, assetsDir, pageNames))
- }
- },
- })
- config.optimization = {
- splitChunks: {
- cacheGroups: {
- 'async-commons': {
- chunks: 'async',
- minChunks: 2,
- name: 'async-commons',
- priority: 80,
- },
- },
- },
- }
- config.externals = [{
- 'vue': 'Vue',
- 'element-ui': 'ELEMENT',
- 'vue-router': 'VueRouter',
- 'vuex': 'Vuex',
- 'axios': 'axios',
- 'clipboard': 'ClipboardJS',
- 'vue-echarts': 'VueECharts',
- 'vue-slimscroll': 'VueSlimScroll',
- 'vue-grid-layout': 'VueGridLayout',
- 'qs': 'Qs',
- }, /\/(echarts)\//]
- return config
- },
- // 对内部的 webpack 配置(比如修改、增加Loader选项)(链式操作)
- chainWebpack (config) {
- // 目录别名
- config.resolve.alias.set('@', resolve('src/pages'))
- .set('@assets', resolve('src/assets'))
- /**
- * 删除懒加载模块的prefetch,降低带宽压力
- * https://cli.vuejs.org/zh/guide/html-and-static-assets.html#prefetch
- * 而且预渲染时生成的prefetch标签是modern版本的,低版本浏览器是不需要的
- */
- config.plugins.delete('prefetch')
- pageNames.forEach(page => {
- config.plugins.delete(`preload-${page}`)
- config.plugins.delete(`prefetch-${page}`)
- })
- },
- transpileDependencies: [
- /[/\\]node_modules[/\\]webpack-dev-server[/\\]client[/\\]/,
- /[/\\]node_modules[/\\]element-ui[/\\]/,
- /[/\\]node_modules[/\\]resize-detector[/\\]/,
- ],
- // 是否在编译时输出map文件,map文件可以在生产环境提供阅读友好的源码格式而不直接输出源码
- productionSourceMap: true,
- // css配置
- css: {
- extract: process.env.NODE_ENV === 'development' || {
- // filename必须与chunkFilename层级一致,\node_modules\@vue\cli-service\lib\config\css.js 42行
- filename: `${assetsDir}/css/[name]/[name]-[contenthash:8].css`,
- chunkFilename: `${assetsDir}/css/chunk/[id]-[contenthash:8].css`,
- },
- sourceMap: false,
- loaderOptions: {},
- },
- // 所有 webpack-dev-server 的选项都支持
- devServer: {
- // 启动时打开浏览器
- open: true,
- // 打开浏览器时的路径
- openPage: loginPath.startsWith('/') ? loginPath.substr(1) : loginPath,
- // 路径重写规则
- historyApiFallback: {
- disableDotRule: true,
- rewrites: genHistoryApiFallbackRewrites(projectContext, pages),
- },
- // 服务端口
- port: 8888,
- // 设置代理
- proxy: {
- // 将后端请求转发至服务器
- [proxyPath]: {
- target: backServerRoot,
- ws: true,
- changeOrigin: true,
- pathRewrite (path) {
- if (backServerType === 'soa') {
- // 单体应用服务器
- const pathArr = path.split('/')
- // 前端请求/api/whatever/someOtherParts,移除api和之后的部分,不关心此部分内容是什么
- pathArr.splice(1, 2)
- return pathArr.join('/')
- }
- // 微服务仅移除掉匹配路径
- return path.replace(proxyPath, '')
- },
- },
- [soaBackServerContext]: {
- target: backServerRoot,
- changeOrigin: true,
- pathRewrite (path) {
- // jsp页面请求[soaBackServerContext]/someOtherParts,移除上下文
- return path.replace(soaBackServerContext, '')
- },
- },
- },
- },
- // 是否为 Babel 或 TypeScript 使用 thread-loader
- parallel: require('os').cpus().length > 1,
- // 向 PWA 插件传递选项
- pwa: {},
- // 可以用来传递任何第三方插件选项
- pluginOptions: {},
- }
- /**
- * 重写@vue/cli的方法,使得多页面子路由也可以直接触发路由判断
- * @param baseUrl
- * @param pages
- * @returns {*[]}
- */
- function genHistoryApiFallbackRewrites (baseUrl, pages = {}) {
- const path = require('path')
- const multiPageRewrites = Object.keys(pages)
- // sort by length in reversed order to avoid overrides
- // eg. 'page11' should appear in front of 'page1'
- .sort((a, b) => b.length - a.length).map(name => ({
- from: new RegExp(`^${baseUrl}/${name}/`), // 此处被重写
- to: path.posix.join(baseUrl, pages[name].filename || `${name}/index.html`),
- }))
- return [
- ...multiPageRewrites,
- // 平台页面
- ...platformPages.map(name => ({
- from: new RegExp(`^${baseUrl}/${name}/`),
- to: path.posix.join(baseUrl, `${name}/index.html`),
- })),
- // 系统根路径,进管理页面
- {
- from: new RegExp(`^${baseUrl}(/?|/error-.*)$`),
- to: path.posix.join(baseUrl, 'admin'),
- },
- // 服务器根路径,进静态404
- { from: /\//, to: '/common-assets/pages/error/404.html' },
- // 其他,进路由404
- { from: /./, to: path.posix.join(baseUrl, '/admin/404') },
- ]
- }
|