【webpack系列】从零搭建 webpack4+react 脚手架(四)
经过三个章节的学习,你已经学会搭建了一个基于webpack4的react脚手架。如果要更改配置,比如,你希望把编译后的js文件和css文件等单独放dist下的static目录下,你想想,是不是有点麻烦。你要去浏览webpack的配置文件,找到哪些配置项,然后去更改它,我们希望有个参数配置文件,只要更改参数配置,而无需更改webpack的配置文件。
(1)在根目录创建config文件夹,在config文件夹内新建一个index.js文件,文件内容如下:
'use strict' const path = require('path') module.exports = { dev: { assetsSubDirectory: 'static', assetsPublicPath: '/', }, build: { assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/', } }
我们定义了一部分配置参数,顾名思义,dev属性下的参数配置是针对开发环境,build属性下的参数配置是针对生产环境的。其中,assetsRoot是编译后的文件存放根路径,assetsSubDirectory是资源文件编译后存放的文件夹名称,assetsPublicPath是公共的路径。
(2)修改webpack.base.conf.js,如下
const path = require('path'); const config=require('../config'); const APP_PATH = path.resolve(__dirname, '../app'); module.exports = { entry: { app: './app/index.js', framework: ['react', 'react-dom'], }, output: { path: config.build.assetsRoot, filename: '[name].js', publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, module: { rules: [ { test: /\.js?$/, use: "babel-loader", include: APP_PATH } ] } };
注意:我们在webpack.base.conf.js内配置了filename,那么dev环境下默认使用该配置,可以删除webpack.dev.conf.js内关于output的配置。
(3)路径生成方法:
路径的配置需要由assetsPublicPath和assetsSubDirectory以及具体的子路径组成,我们写一个公共的方法来生成路径。无论是开发环境还是生产环境,我们都可以复用该方法。我们在build文件夹下建立一个utils.js文件。内容如下:
const path = require('path') const config = require('../config') exports.assetsPath = function (_path) { const assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) }
(4)修改webpack.prod.conf.js
在webpack.prod.conf.js页面头部引入
const config=require('../config'); const utils=require('./utils');
修改output属性的内容
output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash:16].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') },
修改plugins内的相关配置:
new CleanWebpackPlugin([config.build.assetsRoot], { allowExternal: true }), //导出css new MiniCssExtractPlugin({ filename: utils.assetsPath('css/[name].[hash].css'), chunkFilename: utils.assetsPath('css/[id].[hash].css'), }),
(5)执行编译命令查看
npm run build
查看编译后的文件是否放在static目录下了。你可以修改参数配置文件,然后试试看吧。
(1)index.html HTML模板的位置
在config.js文件的dev和build参数下都配置index属性:
index: path.resolve(__dirname, '../public/index.html'),
修改HtmlWebpackPlugin内template的值:
webpack.prod.conf.js的相关修改:
new HtmlWebpackPlugin({ template: config.build.index, inject: 'body', minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true }, }),
webpack.dev.conf.js的相关修改:
new HtmlWebpackPlugin({ template: config.dev.index, inject: true }),
(2)增加devServer 的配置
dev环境下,启动的端口号,代理相关,以及是否自动打开浏览器等等推荐可以放在参数配置文件内。
在config.js文件内增加相关配置参数:
proxyTable: {}, host: 'localhost', port: 8080, autoOpenBrowser: true,
修改webpack.dev.conf.js内的相关配置:
devServer: { host: config.dev.host, port: config.dev.port, contentBase: path.join(__dirname, '../public'), compress: true, historyApiFallback: true, hot: true, https: false, noInfo: true, open: config.dev.autoOpenBrowser, proxy: config.dev.proxyTable, }
(1)前期准备,增加部分参数配置
修改config.js内的配置:
const path = require('path') module.exports = { base: { // 是否开启cssModule cssModule: true, // cssModule排除的目录, 其他css库可以放这里 cssModuleExcludePath: /public/ }, dev: { assetsSubDirectory: 'static', assetsPublicPath: '/', index: path.resolve(__dirname, '../public/index.html'), proxyTable: {}, host: 'localhost', port: 8080, autoOpenBrowser: true, // 是否生成sourceMap cssSourceMap: true, }, build: { assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/', index: path.resolve(__dirname, '../public/index.html'), // 是否生成sourceMap productionSourceMap: true, } }
(2) 在utils.js内增加cssLoaders和styleLoaders方法:
exports.cssLoaders = function (options) { options = options || {} let cssLoader = { loader: 'css-loader', options: { importLoaders: 1, sourceMap: options.sourceMap } } if (options.cssModule) { cssLoader.options.modules = true; cssLoader.options.localIdentName = '[local]__[hash:7]'; } const postcssLoader = { loader: 'postcss-loader', options: { sourceMap: options.sourceMap } } function generateLoaders(loader, loaderOptions) { const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] if (loader) { loaders.push({ loader: loader + '-loader', options: Object.assign({}, loaderOptions, { sourceMap: options.sourceMap }) }) } if (options.extract) { return [MiniCssExtractPlugin.loader].concat(loaders) } else { return ['style-loader'].concat(loaders) } } return { css: generateLoaders(), postcss: generateLoaders(), less: generateLoaders('less', { javascriptEnabled: true, indentedSyntax: true }), sass: generateLoaders('sass', { indentedSyntax: true }), scss: generateLoaders('sass'), stylus: generateLoaders('stylus'), styl: generateLoaders('stylus') } } exports.styleLoaders = function (options) { let output = [] const loaders = exports.cssLoaders(options) for (const extension in loaders) { const loader = loaders[extension] let loaderObj = { test: new RegExp('\\.' + extension + '$'), use: loader, } if (options.cssModule) { loaderObj.exclude = options.cssModuleExcludePath; } output.push(loaderObj) } if (options.cssModule) { options.cssModule = false const cssModuleLoaders = exports.cssLoaders(options) for (const extension in cssModuleLoaders) { const cssModuleLoader = cssModuleLoaders[extension] let cssModuleLoaderObj = { test: new RegExp('\\.' + extension + '$'), use: cssModuleLoader, } cssModuleLoaderObj.include = options.cssModuleExcludePath; output.push(cssModuleLoaderObj) } } return output }
(3) 在webpack.prod.conf.js内使用
确认是否引入了utils,如果没有引入,在页面上方增加代码:
const utils=require('./utils');
修改 module内的rules属性:
rules: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true, usePostCSS: true, cssModule:config.base.cssModule, cssModuleExcludePath:config.base.cssModuleExcludePath })
执行编译查看:
npm run build
(4) 在webpack.dev.conf.js内使用
确认是否引入了utils,如果没有引入,在页面上方增加代码:
const utils=require('./utils');
修改 module内的rules属性:
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true, cssModule:config.base.cssModule, cssModuleExcludePath:config.base.cssModuleExcludePath })
执行dev命令试试看
npm run dev
下一节会对 如果对编译后的文件进行gzip压缩,如何让开发环境的控制台输出更加高逼格,如何更好的对编译后的文件进行bundle分析等问题展开讨论。