Webpack4构建项目(初级)

2020-08-08  本文已影响0人  罂粟1995

本文学习用webpack4来构建一个vue项目。

1. 初始化项目

新建文件夹,控制台进入文件目录,执行:npm init ,一路回车,会生成一个package.json文件。

2. 全局安装webpack

全局安装:

npm install webpack webpack-cli vue -g 
//如果是mac,需要以管理员身份运行: sudo npm install webpack webpack-cli vue -g

全局安装完成后,执行本地安装命令:

npm install webpack webpack-cli vue vue-loader webpack-dev-server

3. 安装依赖

安装常用依赖包:vue-template-compiler css-loader file-loader style-loader sass-loader url-loader html-webpack-plugin cross-env

npm install vue-template-compiler css-loader file-loader style-loader sass-loader url-loader html-webpack-plugin cross-env

4. 编写webpack配置文件

在目录下创建一个webpack.config.js文件,编写配置:

const path = require('path');//nodejs中的基本包,处理文件路径
const VueLoaderPlugin = require('vue-loader/lib/plugin');//vue-loader需要配合此插件使用
const HtmlWebpackPlugin = require('html-webpack-plugin');

const config = {
    target: 'web',
    entry:path.join(__dirname, 'src/index.js'),
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname,'dist')
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.(gif|jpg|jpeg|png|svg)/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            name: '[name].[ext]'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin()
    ]
}

module.exports = config

5. 区分开发环境和生产环境

package.json中增加两行配置:
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",//生产环境
"dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js",//开发环境

{
  "name": "vuetest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config webpack.config.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "cross-env": "^7.0.2",
    "css-loader": "^4.2.0",
    "extract-text-webpack-plugin": "^3.0.2",
    "file-loader": "^6.0.0",
    "html-webpack-plugin": "^4.3.0",
    "mini-css-extract-plugin": "^0.9.0",
    "postcss-loader": "^3.0.0",
    "sass-loader": "^9.0.2",
    "style-loader": "^1.2.1",
    "url-loader": "^4.1.0",
    "vue": "^2.6.11",
    "vue-loader": "^15.9.3",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12"
  }
}

webpack.config.js:

const path = require('path');//nodejs中的基本包,处理文件路径
const VueLoaderPlugin = require('vue-loader/lib/plugin');//vue-loader需要配合此插件使用
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

const isDev = process.env.NODE_ENV === 'development'

const config = {
    target: 'web',
    entry:path.join(__dirname, 'src/index.js'),
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname,'dist')
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.(gif|jpg|jpeg|png|svg)/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            name: '[name].[ext]'
                        }
                    }
                ]
            }
        ]
    },
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin(),
        //通过配置了DefinePlugin,那么这里面的标识就相当于全局变量,便可以在src目录下直接取到当前环境变量。
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: isDev ? '"development"' : '"production"'
            }
        })
    ]
}

//开发环境
if(isDev){
    config.devtool = '#cheap-module-eval-source-map' // 调试代码时可以看到自己原本的代码,而不是编译后的
    config.devServer = {
        port: 8000,
        host: '0.0.0.0',
        overlay: {
            errors: true // 将webpack编译的错误显示在网页上面
        },
        open: true // 在启用webpack-dev-server时,自动打开浏览器
    }
    config.plugins.push(
        new webpack.HotModuleReplacementPlugin(),//模块热替换
        new webpack.NoEmitOnErrorsPlugin()
    )
}

module.exports = config

6. 编写源文件

新建src目录,在src目录下新建app.vue和index.js。
app.vue:

<template>
   <div>{{text}}</div>
</template>

<script>
export default {
    data() {
       return {
          text: 'abc'
       }
    }
}
</script>

<style>

</style>

index.js:

import Vue from 'vue'
import App from './app.vue'

const root = document.createElement('div');
document.body.appendChild(root);

new Vue({
    render: (h) => h(App)
}).$mount(root)

尝试执行npm run buildnpm run dev查看效果,webpack会自动生成dist目录,将业务代码和类库代码打包成dist目录下的bundle.js文件。

7. 区分开发环境和生产环境的打包配置,在生产环境下,用mini-css-extract-plugin来分离css

通常,webpack将css内容都打包在bundle.js里,插件mini-css-extract-plugin能够将css和js模块分开打包,把css代码从js文件中抽离出来,单独出一个模块。
执行npm install mini-css-extract-plugin
修改webpack.config.js:

const path = require('path');//nodejs中的基本包,处理文件路径
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const miniCssExtractPlugin = require('mini-css-extract-plugin');

const isDev = process.env.NODE_ENV === 'development'

const config = {
    target: 'web',
    entry:path.join(__dirname, 'src/index.js'),
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname,'dist')
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.(gif|jpg|jpeg|png|svg)/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            name: '[name].[ext]'
                        }
                    }
                ]
            }
        ]
    },
    // process.env.NODE_ENV = develpment
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: isDev ? '"development"' : '"production"'
            }
        })
    ]
}

//判断正式环境和开发环境
if (isDev) {
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                'style-loader',
                'css-loader',
                'sass-loader'
            ]
        },
    )
    config.devtool = '#cheap-module-eval-source-map'
    config.devServer = {
        port: 8000,
        host: '0.0.0.0',
        overlay: {
            errors: true,
        },
        hot: true
    }
    config.plugins.push(
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin()
    )
} else {
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                miniCssExtractPlugin.loader,//注意:miniCssExtractPlugin与style-loader冲突,使用miniCssExtractPlugin时不要使用style-loader,否则会报错
               // 'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                miniCssExtractPlugin.loader,//注意:miniCssExtractPlugin与style-loader冲突,使用miniCssExtractPlugin时不要使用style-loader,否则会报错
               // 'style-loader',
                'css-loader',
                'sass-loader'
            ]
        }
    )
    config.plugins.push(
        new miniCssExtractPlugin({
            filename: 'css/[name].[contenthash].css',
            allChunks: true
        }),
    )
}

module.exports = config

8. 区分打包类库代码

bundle.js包含了类库代码和业务代码,每次更新业务代码,都生成新的bundle.js,意味着浏览器中缓存的bundle.js也得更新。
我们期望类库代码能够长时间地在浏览器中缓存,而不必随着业务代码的更新而更新。
使用webpack.optimization.splitChunks分割类库代码,webpack4中可直接使用,无需安装任何插件,修改后的配置文件如下:

const path = require('path');//nodejs中的基本包,处理文件路径
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const miniCssExtractPlugin = require('mini-css-extract-plugin');

const isDev = process.env.NODE_ENV === 'development'

const config = {
    target: 'web',
    entry:path.join(__dirname, 'src/index.js'),
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname,'dist')
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.(gif|jpg|jpeg|png|svg)/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            name: '[name].[ext]'
                        }
                    }
                ]
            }
        ]
    },
    // process.env.NODE_ENV = develpment
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: isDev ? '"development"' : '"production"'
            }
        })
    ]
}

//判断正式环境和开发环境
if (isDev) {
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                'style-loader',
                'css-loader',
                'sass-loader'
            ]
        },
    )
    config.devtool = '#cheap-module-eval-source-map'
    config.devServer = {
        port: 8000,
        host: '0.0.0.0',
        overlay: {
            errors: true,
        },
        hot: true
    }
    config.plugins.push(
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin()
    )
} else {
    config.entry = {
        app:path.join(__dirname, 'src/index.js')
    }
    config.optimization = {
        splitChunks : {
            chunks: 'all',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    },
    config.output.filename = '[name].[chunkhash:8].js'
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                miniCssExtractPlugin.loader,//注意:miniCssExtractPlugin与style-loader冲突,使用miniCssExtractPlugin时不要使用style-loader,否则会报错
                //'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                miniCssExtractPlugin.loader,//注意:miniCssExtractPlugin与style-loader冲突,使用miniCssExtractPlugin时不要使用style-loader,否则会报错
               // 'style-loader',
                'css-loader',
                'sass-loader'
            ]
        }
    )
    config.plugins.push(
        new miniCssExtractPlugin({
            filename: 'css/[name].[contenthash].css',
            allChunks: true
        }),
    )
}

module.exports = config

执行npm run build,dist目录下将生成业务代码app.js文件和类库代码vendors.js。

多次打包,会发现dist目录下生成了多个hash值不同的vendor.js和app.js,这时我们需要安装插件,在每次打包前清除dist目录。

npm install clean-webpack-plugin

webpack.config.js :

const path = require('path');//nodejs中的基本包,处理文件路径
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const miniCssExtractPlugin = require('mini-css-extract-plugin');
// 引入打包时清除 dist 目录的插件,引入时需要用对象{ CleanWebpackPlugin }包裹起来
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

const isDev = process.env.NODE_ENV === 'development'

const config = {
    target: 'web',
    entry:path.join(__dirname, 'src/index.js'),
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname,'dist')
    },
    resolve: {
        alias: {
            '@': path.resolve(__dirname,'./src'),
        }
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.(gif|jpg|jpeg|png|svg)/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            name: '[name].[ext]'
                        }
                    }
                ]
            }
        ]
    },
    // process.env.NODE_ENV = develpment
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: isDev ? '"development"' : '"production"'
            }
        })
    ]
}

//判断正式环境和开发环境
if (isDev) {
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                'style-loader',
                'css-loader',
                'sass-loader'
            ]
        },
    )
    config.devtool = '#cheap-module-eval-source-map'
    config.devServer = {
        port: 8000,
        host: '0.0.0.0',
        overlay: {
            errors: true,
        },
        hot: true
    }
    config.plugins.push(
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin()
    )
} else {
    config.entry = {
        app:path.join(__dirname, 'src/index.js')
    }
    config.optimization = {
        splitChunks : {
            chunks: 'all',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    },
    config.output.filename = '[name].[chunkhash:8].js'
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                miniCssExtractPlugin.loader,
                //'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                miniCssExtractPlugin.loader,
                //'style-loader',
                'css-loader',
                'sass-loader'
            ]
        }
    )
    config.plugins.push(
        new miniCssExtractPlugin({
            filename: 'css/[name].[contenthash].css',
            allChunks: true
        }),
        // 打包时,把 dist 目录下的文件内容先清除
        new CleanWebpackPlugin()
    )
}

module.exports = config

9. 用babel将es6转译为浏览器能够识别的代码

安装相应的包:

npm install -D babel-loader @babel/core @babel/preset-env webpack

创建一个与webpack.config.js同级的文件.babelrc,写入内容:

{
    "presets" : [
       "@babel/preset-env"
    ]
}

webpack.config.js:

const path = require('path');//nodejs中的基本包,处理文件路径
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const miniCssExtractPlugin = require('mini-css-extract-plugin');
// 引入打包时清除 dist 目录的插件,引入时需要用对象{ CleanWebpackPlugin }包裹起来
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

const isDev = process.env.NODE_ENV === 'development'

const config = {
    target: 'web',
    entry:path.join(__dirname, 'src/index.js'),
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname,'dist')
    },
    resolve: {
        alias: {
            '@': path.resolve(__dirname,'./src'),
        }
    },
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            },
            {
                test: /\.(gif|jpg|jpeg|png|svg)/,
                use: [
                    {
                        loader: 'url-loader',
                        options: {
                            limit: 1024,
                            name: '[name].[ext]'
                        }
                    }
                ]
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    },
    // process.env.NODE_ENV = develpment
    plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin(),
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: isDev ? '"development"' : '"production"'
            }
        })
    ]
}

//判断正式环境和开发环境
if (isDev) {
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                'style-loader',
                'css-loader',
                'sass-loader'
            ]
        },
    )
    config.devtool = '#cheap-module-eval-source-map'
    config.devServer = {
        port: 8000,
        host: '0.0.0.0',
        overlay: {
            errors: true,
        },
        hot: true
    }
    config.plugins.push(
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoEmitOnErrorsPlugin()
    )
} else {
    config.entry = {
        app:path.join(__dirname, 'src/index.js')
    }
    config.optimization = {
        splitChunks : {
            chunks: 'all',
            minSize: 30000,
            maxSize: 0,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            name: true,
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        }
    },
    config.output.filename = '[name].[chunkhash:8].js'
    config.module.rules.push(
        {
            test: /\.css$/,
            use: [
                miniCssExtractPlugin.loader,
                //'style-loader',
                'css-loader'
            ]
        },
        {
            test: /\.scss$/,
            use: [
                miniCssExtractPlugin.loader,
                //'style-loader',
                'css-loader',
                'sass-loader'
            ]
        }
    )
    config.plugins.push(
        new miniCssExtractPlugin({
            filename: 'css/[name].[contenthash].css',
            allChunks: true
        }),
        // 打包时,把 dist 目录下的文件内容先清除
        new CleanWebpackPlugin()
    )
}

module.exports = config
上一篇下一篇

猜你喜欢

热点阅读