zoukankan      html  css  js  c++  java
  • 【webpack系列】从零搭建 webpack4+react 脚手架(四)

    经过三个章节的学习,你已经学会搭建了一个基于webpack4的react脚手架。如果要更改配置,比如,你希望把编译后的js文件和css文件等单独放dist下的static目录下,你想想,是不是有点麻烦。你要去浏览webpack的配置文件,找到哪些配置项,然后去更改它,我们希望有个参数配置文件,只要更改参数配置,而无需更改webpack的配置文件。

    1 添加参数配置文件
    (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目录下了。你可以修改参数配置文件,然后试试看吧。

    2 更多的配置化
     
    接下去我们会把更多的参数和变量进行配置化。
    (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,
        }
    更多的css loader加载器
     
    我们在loader中配置了less加载器,如果我们要支持其他呢,比如sass,scss等等,显然,我们需要在rules数组增加配置。其实,这里可以写成公共的生成方法。并且我们把部分参数变成可配置。比如:是否开启cssModule等。
    (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分析等问题展开讨论。

  • 相关阅读:
    Python笔记 #17# Pandas: Merge
    MVC相关资料收集
    Python笔记 #16# Pandas: Operations
    Least slack time scheduling
    Python笔记 #15# Pandas: Missing Data
    Python笔记 #14# Pandas: Selection
    C++中const引用的是对象的时候只能调用该对象的f()const方法
    模板与泛型编程
    c++中的单例模式
    C/C++异常处理机制
  • 原文地址:https://www.cnblogs.com/nianzhilian/p/webpack.html
Copyright © 2011-2022 走看看