美文网首页
vue-cli构建项目之webpack配置理解

vue-cli构建项目之webpack配置理解

作者: 稀释1 | 来源:发表于2022-07-31 20:47 被阅读0次

    一、目录结构

    构建项目目录结构如下,不知道怎么构建的可以网上查找一下,不再赘述。

    image

    其中

    build文件夹下面有:build.js、check-versions.js、utils.js、vue-loader.conf.js、webpack.base.conf.js、webpack.dev.conf.js、webpack.prod.conf.js

    config文件夹下面有有:dev.env.js、index.js、prod.env.js

    这些文件,都是关于webpack配置的文件。

    二、首先理解package.json

    {
        "name": "openlayer",      // 模块名称
        "version": "1.0.0",       // 版本
        "description": "A Vue.js project",    // 项目描述
        "author": "zhufengli",    // 作者
        "private": true,      // 私有
        "scripts": {          // 指定执行命令
            "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",     // 执行npm run dev或者npm start的时候就是执行的build文件下面的webpack.dev.conf.js
            "start": "npm run dev",     // 启动项目命令
            "build": "node build/build.js"  // build命令
        },
        "dependencies": {     // 配置依赖
            "vue": "^2.5.2",
            "vue-router": "^3.0.1",
            "vuex": "^3.5.1"
        },
            "devDependencies": {      // dev开发环境配置依赖
            "autoprefixer": "^7.1.2",
            "babel-core": "^6.22.1",
            "babel-helper-vue-jsx-merge-props": "^2.0.3",
        },
        "engines": {  // 指定node和npm版本
            "node": ">= 6.0.0",
            "npm": ">= 3.0.0"
        },
        "browserslist": [ // 支持浏览器配置
            "> 1%",
            "last 2 versions",
            "not ie <= 8"
        ]
    }
    
    

    从package的执行命令配置中可以知道执行的是build文件下的webpack.dev.conf.js

    三、build/webpack.dev.conf.js

    'use strict'
    const utils = require('./utils')
    const webpack = require('webpack')
    const config = require('../config')
    const merge = require('webpack-merge')  // 合并文件作用
    const path = require('path')
    const baseWebpackConfig = require('./webpack.base.conf')
    const CopyWebpackPlugin = require('copy-webpack-plugin')    // 复制插件
    const HtmlWebpackPlugin = require('html-webpack-plugin')    // 配置生成的文件
    const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')   // 友好错误提示插件
    const { CleanWebpackPlugin } = require('clean-webpack-plugin') // 清除文件插件
    const portfinder = require('portfinder')    // 自动获取端口
    
    const HOST = process.env.HOST
    const PORT = process.env.PORT && Number(process.env.PORT)
    
    // 合并了build下面的webpack.base.conf.js
    const devWebpackConfig = merge(baseWebpackConfig, {
        module: {
            // 资源管理配置,处理各种文件类型
            rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
        },
        // 主要是定位错误(用于开发)
        devtool: config.dev.devtool,  // 'cheap-module-eval-source-map'
    
        // 这些devServer选项应在config/index.js中自定义
        devServer: {
            clientLogLevel: 'warning',
            historyApiFallback: {
                rewrites: [
                    { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
                ],
            },
            hot: true, 
            contentBase: false, 
            compress: true,     
            host: HOST || config.dev.host,      // ip地址
            port: PORT || config.dev.port,      // 端口
            open: config.dev.autoOpenBrowser,   // 运行npm run dev成功之后自动打开浏览器窗口
            overlay: config.dev.errorOverlay
              ? { warnings: false, errors: true }
              : false,
            publicPath: config.dev.assetsPublicPath,
            proxy: config.dev.proxyTable,       // 代理
            quiet: true, 
            watchOptions: {
                poll: config.dev.poll,
            }
        },
        // 插件配置
        plugins: [
            new webpack.DefinePlugin({
                'process.env': require('../config/dev.env'),
                'process.env.NODE_ENV': 'pro'
            }),
            new webpack.HotModuleReplacementPlugin(),    // 热更新
            new webpack.NamedModulesPlugin(), 
            new webpack.NoEmitOnErrorsPlugin(),
    
            // 这个打包本来配了两个文件,发现没效果
            new HtmlWebpackPlugin({
                title:"openlayer",//它的title为app,在index.html的title中间中加入<%= %>
                filename: 'index.html',   // 输出的文件名
                template: 'index.html',   // 模板文件
                inject: true,
                minify: {
                    removeComments: true,               // 移除HTML中的注释
                    removeScriptTypeAttributes: true,   // 删除script的类型属性,在h5下面script的type默认值:text/javascript 默认值false
                    removeAttributeQuotes: true,        // 是否移除属性的引号 默认false
                    useShortDoctype: true,              // 使用短的文档类型,默认false
                    decodeEntities: true,
                    collapseWhitespace: true,           // 删除空白符与换行符
                    minifyCSS: true                     // 是否压缩html里的css(使用clean-css进行的压缩) 默认值false
                },
                hash:true,
                chunks:['app']
            }),
            new HtmlWebpackPlugin({
                title:"test",
                filename: 'test.html',
                template: 'test.html',
                hash:true,
                inject:true,
                chunks:['test']
            }),
            new CleanWebpackPlugin({
                root: path.resolve(__dirname, '..'),
                dry: false    // 启用删除文件
            }),
            new CopyWebpackPlugin([
                {
                    from: path.resolve(__dirname, '../static'),
                    to: config.dev.assetsSubDirectory,
                    ignore: ['.*']
                }
            ])
        ]
    })
    
    module.exports = new Promise((resolve, reject) => {
        portfinder.basePort = process.env.PORT || config.dev.port
        portfinder.getPort((err, port) => {
            if (err) {
                reject(err)
            } else {
    
              process.env.PORT = port
              // 将端口添加到devServer配置
              devWebpackConfig.devServer.port = port
    
              // 添加 FriendlyErrorsPlugin
              devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
                compilationSuccessInfo: {
                  messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
                },
                onErrors: config.dev.notifyOnErrors
                ? utils.createNotifierCallback()
                : undefined
              }))
              resolve(devWebpackConfig)
            }
        })
    })
    
    

    通过webpack.dev.conf.js merge合并文件 webpack.base.conf.js 可以知道基础的配置文件都在这里

    四、build/webpack.base.conf.js

    'use strict'
    const path = require('path')
    const utils = require('./utils')
    const config = require('../config')
    const vueLoaderConfig = require('./vue-loader.conf')
    
    function resolve (dir) {
        return path.join(__dirname, '..', dir)
    }
    
    module.exports = {
        context: path.resolve(__dirname, '../'),
        // 入口文件配置
        entry: {  
            app: './src/main.js',  // 入口文件
            test: './src/test.js'
        },
        // 出口文件配置
        output: {
            path: config.build.assetsRoot,  // 输出文件路径
            filename: '[name].js',          // 输出文件名
            publicPath: process.env.NODE_ENV === 'pro'    
          ? config.build.assetsPublicPath
          : config.build.assetsPublicPath   // 公共存放路径
            // 为什么用一样的路劲config.build.assetsPublicPath,下面详解
        },
        resolve: {
            // 扩展文件后缀,这样在引入文件的时候可以忽略后缀名
            // 如 import 'js/index'     js/index.js
            extensions: ['.js', '.vue', '.json'],
    
            // 配置别名
            alias: {   
                'vue$': 'vue/dist/vue.esm.js',
                '@': resolve('src'),
                'src': resolve('src'),
            }
        },
    
        // 文件处理
        module: {
            rules: [
                // vue文件语法处理
                {
                    test: /\.vue$/,
                    loader: 'vue-loader',
                    options: vueLoaderConfig    // (下面详解)
                },
                // 语法处理,会处理成浏览器能够识别的ES5语法
                {
                    test: /\.js$/,
                    loader: 'babel-loader',
                    include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
                },
                // 图片处理
                {
                    test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
                    loader: 'url-loader',
                    options: {
                        limit: 10000,
                        name: utils.assetsPath('img/[name].[hash:7].[ext]')
                    }
                },
                // 文件处理
                {
                    test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
                    loader: 'url-loader',
                    options: {
                        limit: 10000,
                        name: utils.assetsPath('media/[name].[hash:7].[ext]')
                    }
                },
                // 字体处理
                {
                    test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
                    loader: 'url-loader',
                    options: {
                        limit: 10000,
                        name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
                    }
                }
            ]
        },
        node: {
            //防止Webpack注入无用的setImmediate polyfill。
            setImmediate: false,
            // 阻止webpack将模拟注入到Node本机模块
            dgram: 'empty',
            fs: 'empty',
            net: 'empty',
            tls: 'empty',
            child_process: 'empty'
        }
    }
    
    

    五、build/webpack.prod.conf.js

    生产环境配置,暂不深入理解,想要了解的小伙伴可以看下这篇文章 webpack.prod.conf.js文件详解

    六、build/vue-loader.conf.js

    这个文件主要是处理vue文件,主要是sass、less用的比较多,这里需要更加正确理解。

    'use strict'
    const utils = require('./utils')
    const config = require('../config')
    const isProduction = process.env.NODE_ENV === 'production'
    const sourceMapEnabled = isProduction ? config.build.productionSourceMap : config.dev.cssSourceMap
    
    module.exports = {
        // css规则处理,包括sass、less、postcss等
        loaders: utils.cssLoaders({
            sourceMap: sourceMapEnabled,    // 调式作用
            extract: isProduction
        }),
        cssSourceMap: sourceMapEnabled,
        cacheBusting: config.dev.cacheBusting,
    
        // 可以将某些属性转成require调用
        transformToRequire: {
            video: ['src', 'poster'],
            source: 'src',
            img: 'src',
            image: 'xlink:href'
        }
    }
    
    

    七、build/utils.js

    utils 工具文件,主要作用分为

    1. 配置导出路径
    2. 处理各类loader相关配置
    3. 跨平台通知系统
    'use strict'
    const path = require('path')
    const config = require('../config')
    const ExtractTextPlugin = require('extract-text-webpack-plugin')  // 用来将文本从bundle中提取到一个单独的文件中
    const packageConfig = require('../package.json')
    
    // 导出路径
    exports.assetsPath = function (_path) {
        const assetsSubDirectory = process.env.NODE_ENV === 'production' 
        ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory
    
        // 返回一个完整路径的相对根路径
        return path.posix.join(assetsSubDirectory, _path) 
    }
    
    // 导出cssLoaders相关配置
    exports.cssLoaders = function (options) {
        options = options || {}
    
        const cssLoader = {
            loader: 'css-loader',
            options: {
                sourceMap: options.sourceMap
            }
        }
    
        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
                    })
                })
            }
    
            // 指定该选项时提取CSS(生产构建)
            if (options.extract) {
                return ExtractTextPlugin.extract({
                    use: loaders,
                    fallback: 'vue-style-loader'
                })
            } else {
                return ['vue-style-loader'].concat(loaders)
            }
        }
    
        // https://vue-loader.vuejs.org/en/configurations/extract-css.html
        return {
            css: generateLoaders(),             // 对应 vue-style-loader 和 css-loader
            postcss: generateLoaders(),         // 对应 vue-style-loader 和 css-loader
            less: generateLoaders('less'),      // 对应 vue-style-loader 和 less-loader
            sass: generateLoaders('sass', { indentedSyntax: true }),    // 对应 vue-style-loader 和 sass-loader
            scss: generateLoaders('sass'),      // 对应 vue-style-loader 和 sass-loader
            stylus: generateLoaders('stylus'),  // 对应 vue-style-loader 和 stylus-loader
            styl: generateLoaders('stylus')     // 对应 vue-style-loader 和 styl-loader
        }
    }
    
    // 为独立样式文件生成加载程序(.vue之外)
    exports.styleLoaders = function (options) {
        const output = []
    
        // 生成的各种css文件的loader对象
        const loaders = exports.cssLoaders(options)
    
        // 把每个文件的loader提取出来,push到output数组中
        for (const extension in loaders) {
            const loader = loaders[extension]
            output.push({
                test: new RegExp('\\.' + extension + '$'),
                use: loader
            })
        }
    
        return output
    }
    
    // 发送跨平台通知系统
    exports.createNotifierCallback = () => {
        const notifier = require('node-notifier')
    
        // 出现error时触发
        return (severity, errors) => {
            if (severity !== 'error') return
    
            const error = errors[0]
            const filename = error.file && error.file.split('!').pop()
    
            notifier.notify({
                title: packageConfig.name,  // 标题
                message: severity + ': ' + error.name,  // 内容
                subtitle: filename || '',               // 短标题
                icon: path.join(__dirname, 'logo.png')  // 图标
            })
        }
    }
    
    

    关于sass和less,在我们初始化项目的时候并没有默认安装,而在项目中,基本上都有用到,比如写一个.sass文件或者写一段代码

    <style lang="sass" scoped>
    
    </style>
    
    

    可能,就会报错如下

    image

    这时候你就需要安装一下node-sass、sass-loader和scss

    npm install node-sass
    npm install sass-loader
    npm install scss
    
    

    安装完之后你以为万事大吉了,没想到只是从一个坑掉进另一个坑

    image

    这意思是路径错误?查找了一番之后原来是版本过高,package.json里面可以查看版本10.0.1,卸载,重新装一个低版本的7.3.1,这次真的可以了

    image
    npm uninstall sass-loader
    npm install sass-loader@7.3.1
    
    

    七、build/build.js和build/check-versions.js

    暂不深入理解,想要了解的小伙伴可以看下参考文章 vue-cli脚手架中webpack配置基础文件详解

    讲完build文件夹,还有一个config文件夹,这个文件夹主是定义一些变量exports出去给build文件夹下面的文件使用

    八、config/dev.env.js

    开发环境配置

    'use strict'
    module.exports = {
        NODE_ENV: '"dev"'
    }
    
    

    九、config/prod.env.js

    生产环境配置

    'use strict'
    module.exports = {
        NODE_ENV: '"pro"'
    }
    
    

    八、config/index.js

    定义一些开发/打包需要的变量

    'use strict'
    // Template version: 1.3.1
    // see http://vuejs-templates.github.io/webpack for documentation.
    
    const path = require('path')
    
    module.exports = {
        dev: {
            assetsSubDirectory: 'static',
            assetsPublicPath: '/',  // 公共路劲
            proxyTable: {},
    
            // 各种开发服务器设置
            host: 'localhost',  // 可以被process.env.HOST覆盖
            port: 8080,     //端口, 可以被process.env.PORT覆盖,如果正在使用端口,则将确定一个空闲端口
            autoOpenBrowser: false,     // 运行自动打开浏览器
            errorOverlay: true,         // 错误提示
            notifyOnErrors: true,       // 跨平台错误提示
            poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
    
            /**
             *  Source Maps 代码调试BUG、错误等
                https://webpack.js.org/configuration/devtool/#development
             */
            devtool: 'cheap-module-eval-source-map',
            // cheap-module-eval-source-map 开发环境(dev)推荐使用
            // cheap-module-source-map  可以定位生产环境的代码报错
    
            //如果在devtools中调试vue文件时遇到问题,
            //将其设置为false-可能会有所帮助
            // https://vue-loader.vuejs.org/en/options.html#cachebusting
            cacheBusting: true,     // 缓存失效
            cssSourceMap: true
        },
    
        // 打包配置
        build: {
            index: path.resolve(__dirname, '../dist/index.html'), // 编译后生成的文件位置
            assetsRoot: path.resolve(__dirname, '../dist'), // 打包后存放代码位置
            assetsSubDirectory: 'static',   // 静态文件夹(js、css、images)
            assetsPublicPath: './', // 发布根目录
    
            productionSourceMap: true,
            devtool: '#source-map',
            productionGzip: false,
            productionGzipExtensions: ['js', 'css'],
            bundleAnalyzerReport: process.env.npm_config_report
        }
    }
    
    

    十一、项目遇坑记

    为了弄清楚一下项目中使用的插件,以及一些重要的属性,进行的一些属性试验

    1. 关于npm run build 打包编译

    这里有个问题是关于输出公共存放路径 config.build.assetsPublicPath 在我打包完之后打开dist/index.html,页面是空白,然后报错如下

    image

    经过一番查找之后,得到结果是在config/index.js文件下dev和build下面的 assetsPublicPath '/' 修改成 './' ,改完之后发现npm run dev 的时候页面找不到了,所以为了兼容打包和运行,打包的时候统一使用config.build.assetsPublicPath,而dev下面的assetsPublicPath还是改回原来的 '/'。

    image image

    打包好之后的文件在根目录下面会生成一个dist文件夹,里面的结构如下,其中,static主要存放静态资源(css、js、images、fonts等)

    image
    2. 关于devtool: 'cheap-module-eval-source-map'

    为什么需要这个东西,主要的作用是帮助我们精准定位错误信息,如

    image

    我在helloWorld.vue文件调用了print文件里面的consoleLog函数,函数内容我写的很简单

    let consoleLog = () =>{
        console.log('这是正确的打印');
        cosnole.error('这是错误的打印')
    }
    
    export default {
        consoleLog
    }
    
    

    如果没有devtool: '',那么提示的错误信息

    image

    它虽然告诉你cosnole is not defined,但是并没有告诉你在哪个文件哪一行

    但是如果有devtool: 'cheap-module-eval-source-map',他的提示信息可以非常准确定位到文件以及行位置

    image

    devtool相关知识

    3.关于clean-webpack-plugin 清除文件插件

    按着上面插件使用方法,直接报了一个不是构造函数的错误,一脸懵

    image image

    后来去看官方文档引入

    image

    使用的时候查找了很多文章有这类的使用方法

    new CleanWebpackPlugin(['dist'],{
        root: path.resolve(__dirname, '..'),
        dry: false // 启用删除文件
    }),
    
    

    果然不行,这不是就是参数类型传得不对吗

    image

    换成下面这种参数吧,估计这个与版本有关,我的是3.0.0

    new CleanWebpackPlugin({
        root: path.resolve(__dirname, '..'),
        dry: false // 启用删除文件
    }),
    

    链接:https://www.jianshu.com/p/051fcd267316

    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    参考:https://segmentfault.com/a/1190000014804826

    相关文章

      网友评论

          本文标题:vue-cli构建项目之webpack配置理解

          本文链接:https://www.haomeiwen.com/subject/xnxlwrtx.html