基于webpack4.x项目实战

webpack在前端开发者的世界再熟悉不过了,网上也很多关于webpack的文章,自己也写一下,加深印象

webpack 是js模块打包器,一直在更新,本文是基于webpack4.29.5版本,将来的某一天,发觉本文章的一些配置用不了,那可能是webpack已经更新到更高的版本了

webpack4.0的零配置

安装webpack4webpack-cli,由于webpack4中和webpack-cli抽离了,所以需要分别安装,我们全局安装一个:

npm install webpack webpack-cli -g

搞一个demo试试

mkdir webpack-demo && cd webpack-demo
npm init -y
复制代码

新建一个index.js文件,./src/index.js

--package.json
--src
  -- index.js
复制代码

webpack4支持0配置,默认./src/index.js为入口文件,webpack运行时,会根据mode的值采取不同的默认配置,mode两个可选值:productiondevelopment。没有传mode,会有一个警告

WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
复制代码

解决这个警告,修改package.json部分,传入mode即可

   "scripts": {
      "dev": "webpack --mode development",
      "build": "webpack --mode production"
   }
复制代码

developmentproduction的区别在于一个代码没压缩,一个有压缩和优化,执行 npm run dev,就会生产一个./dist/main.js文件。

如果我们只是想要一个简单的打包功能,使用默认配置就够用了。都不需要创建webpack.config.js

自定义webpack配置

在我们的项目中,使用webpack的默认配置明显是不够用的,还是需要自定义我们的webpack配置

在我们的项目根目录,创建webpack.config.js,webpack配置的概况如下

module.exports = {
    entry: '',               // 入口文件
    output: {},              // 出口文件
    module: {},              // 模块相关配置
    plugins: [],             // 插件相关配置
    resolve: { }             // 解析模块的可选项
    devServer: {},           // 开发服务器相关配置
    devtool: 'inline-source-map',  //开发工具,比如启动source-map
    mode: 'development'      // 模式配置 development/production
}
复制代码

接下来,我们一点点往里面添加内容,以此更好的理解webpack

单入口配置

一些项目中,只有一个入口文件,那么,入口文件和出口文件可以这样配置

const path = require('path');

module.exports = {
    entry: './src/index.js',    
    output: {
        filename: 'main.js',    // 打包后的文件名称
        path: path.resolve(__dirname, 'dist')  // 打包后的目录
    }
}
复制代码

多入口配置

如果有多个入口,可以将entry配置成一个array或者object,如果是array,则是将多个入口文件最终生成一个出口文件,如果是object,则对应生成多个文件,如下: entry为array

const path = require('path');
module.exports = {
    entry: ['./src/a.js', './src/b.js'], 多个入口文件打包为一个js文件
    output: {
        filename: 'main.js', 
        path: path.resolve(__dirname, 'dist')
    }
}
复制代码

entry为object

const path = require('path');
module.exports = {
    entry: {
        a: './src/a.js',
        b: './src/b.js'
    },
    output: {
        filename: '[name].js',      // 打包后为a.js和b.js文件
        path: path.resolve(__dirname, 'dist')
    }
}
复制代码

添加loader

由于webpack只能处理js,当我们需要处理其他非js文件时,我们需要引入对应的loader

1.加载css

我们想从javaScript 模块中 import 一个 CSS 文件,需要在 module 配置中 安装并添加 style-loadercss-loader

npm install style-loader css-loader -D

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    }
};
复制代码

src/css/style.css

body {
    background: red;
}
复制代码

src/index.js

 import './css/style.css';
复制代码

打开index.html,可以看到我们页面的背景色此时为红色。执行编译,css文件将会打包到js文件当中

2.处理图片

加载图片,也需要安装对应的loader npm install file-loader url-loader -D 在css引入背景图片时,需要指定一下相对路径

module: {
    rules: [
        {
            test: /\.(png|svg|jpg|gif)$/, // 加载图片
            use: [{
                loader: 'url-loader',
                options: {
                    limit: 8192,  // 小于8k的图片自动转成base64格式
                    name: 'images/[name].[ext]?[hash]', // 图片打包后存放的目录
                    publicPath: '../'  // css图片引用地址,可修正打包后,css图片引用出错的问题
                }
            }]
        },
    ]
}
复制代码

3.加载字体文件

module: {
    rules: [
        {
            test: /\.(woff|woff2|eot|ttf|otf)$/,
            use: [
            'file-loader'
            ]
        }
    ]
}
    
复制代码

webpack插件

webpack提供了各种各样的插件,下面介绍几种常用的插件,只试过在webpack4.0以上的版本,其他版本没试过,所以如果有报错,那可能是版本不支持

使用webpack插件,我们要写在配置中的plugins里面,在使用插件之前,我们都需要先安装该插件。下面介绍几个常用的插件

1. html-webpack-plugin: 创建 HTML 文件

该插件的作用为:生成html页面,自动引入js文件,自动消除src引入的缓存问题,上线之前压缩。

使用前安装该插件npm install html-webpack-plugin --save -dev;

let HtmlWebpackPlugin = require('html-webpack-plugin');
module: {
    ...
    plugins:[
        new HtmlWebpakPlugin({
            minify:{
                collapseWhitespace: true,   // 折叠空白区域 也就是压缩代码
                removeAttributeQuotes: true // 移除双引号,
            },
            hash:true, //向html引入的src链接后面增加一段hash值,消除缓存
            template:'./src/index.html', // 模板地址
            title: 'webpack' // 标题
        })
    ]
}
复制代码

关于该插件的更多使用方法,可以看这里

2. extract-text-webpack-plugin:拆分单独的css

前面安装的css-loader在js中引入css文件时,打包后的css是和js混合在一起的,如果我们想打包后的css文件时单独的存在,需要引入这个插件。

安装npm i extract-text-webpack-plugin@next -D,其中@next表示可以在webpack4中使用

const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: "css-loader"
                })
            }
        ]
    },
  plugins: [
      new ExtractTextPlugin("css/styles.css"), // 打包后的css文件
  ]
}
复制代码

关于该插件的更多使用方法,可以看这里

3.添加css3前缀

利用postcss中的autoprefixer来为css3自动添加前缀。

安装npm i postcss-loader autoprefixer -D

在根目录下新建postcss.config.js里面写入

module.exports = {
    plugins: [
        require('autoprefixer')
    ]
}
复制代码

webpack.config.js里面,配置postcss-loader

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            }
        ]
    }
}
复制代码

在package.json上,添加需要支持的版本

"browerslist": [
    "last 2 versions",
    "IE 8",
    "UCAndroid"
  ],
复制代码

webpack提供的插件非常多,更多的插件,可以看这里

转义es6

安装npm i -D @babel/core @babel/plugin-transform-runtime @babel/preset-env babel-loader 在webpack.config.js中添加

..略
module:{
    rulse:[
        ...略
        {
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/, // 忽略掉该文件下的js
        }
    ]
}
复制代码

在根目录下,也就是和webpack.config.js同一个目录下,新增.babelrc文件

{
    "presets": ["@babel/preset-env"], 
    "plugins": ["@babel/plugin-transform-runtime"] 
}
复制代码

webpack-dev-server:为webpack添加静态服务器

webpack-dev-server为自动刷新和模块热替换机制,装上它,可以我们的改动可以自动刷新

安装npm install webpack-dev-server -D

修改一下我们的webpack.config.js

var path = require('path');

module.exports = {
  //...
  devServer: {
    contentBase: path.join(__dirname, 'dist'), // 服务器资源的根目录,不写的话,默认为bundle.js
    compress: true, // 服务器资源采用gzip压缩
    port: 9000,  // 运行的端口
    overlay: true  // 出错代码是否显示在html页面上
    hot: true //热加载
  }
};
复制代码

再修改我们的package.json

...
 "scripts": {
    "dev": "webpack-dev-server --mode development --open",
  }
...
复制代码

执行npm run dev,就会自动打开浏览器,监听你的修改了

注意点:webpack-dev-server输出的文件只存在于内存中,不输出真实的文件,也就是说,你启动它,你的dist文件其实是没有生产新的文件的

resolve: 设置模块如何被解析

resolve是webpack自带的,主要作用是设置模块如何被解析 主要介绍几个:

1. resolve.alias 配置别名

resolve.alias配置项通过别名来把原来导入路径映射成一个新的导入路径,例如:

resolve: {
    alias: {
        components: './src/components/'
    }
}
复制代码

这样,我们原来import Dialog from './src/components/dialog'可以缩减为import Dialog from 'components/dialog';

2. resolve.extensions 自动解析扩展,意味着我们导入模块可以省略不写后缀名

resolve: {
    extensions: ['.js', '.json']
}
复制代码

我们用import data from './data'时,webpack就会依次寻找data.js是否存在,不存在继续寻找data.json是否存在,最后寻找data/index.js是否存在

3. resolve.modules 配置 Webpack 去哪些目录下寻找第三方模块

Webpack找第三方模块,默认是只会去node_modules目录下寻找。如果你的项目中,很多模块要引用这个目录下的, 那目录可能很长。打个比方,src/components/test.js要去node_modules下面找dialog组件,那么可能这样写:import '../../../components/dialog',利用resolve.modules优化后:

resolve.modules:['./src/components','node_modules']
复制代码

你可以简单通过 import 'dialog' 导入。

webpack实践

通过以上的介绍,我们大概熟悉了webpack的一些基础配置,下面我们来进入实战

我们配置具有如下功能的webpack配置:

  • 支持es6
  • 支持css3自动前缀
  • less、css单独存为css文件,而不是打包到js中
  • 支持引入图片
  • 支持热跟新项目
  • 支持引用别名以及省略一些文件后缀

我们目录结构如下:

——src
    ├─components
    ├─css
    └─images
    ├─index.js   
——index.html
——.babelrc
——package.json
——postcss.config.js
——webpack.config.js
复制代码

webpack.config.js的配置如下:

const path = require('path');
const htmlWebpackPlugin = require("html-webpack-plugin");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = {
    entry: './src/index.js', // 入口文件
    output: {
        filename: 'main.js',    // 打包后的文件名称
        path: path.resolve(__dirname, 'dist')  // 打包后的目录
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ExtractTextPlugin.extract({ // 拆分单独的css文件
                    fallback: "style-loader",
                    use: ['css-loader', 'postcss-loader'] // 加载css
                })
            },
            // 加载less
            {
                test: /\.less$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: ['css-loader', 'postcss-loader']
                })
            },
            {
                test: /\.(png|svg|jpg|gif)$/, // 加载图片
                use: [{
                    loader: 'url-loader',
                    options: {
                        limit: 8192,  // 小于8k的图片自动转成base64格式
                        name: 'images/[name].[ext]?[hash]', // 图片打包后的目录
                        publicPath: '../'  // css图片引用地址
                    },
                }]
            },
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/, // 加载字体文件
                use: [
                    'file-loader'
                ]
            },
            // 转义es6
            {
                test: /\.js$/,
                loader: 'babel-loader',
                include: /src/,          // 只转化src目录下的js
                exclude: /node_modules/, // 忽略掉node_modules下的js
            }
        ]
    },
    resolve: {
        alias: {
            components: path.resolve(__dirname, 'src/components/') // 别名
        },
        extensions: ['.js', '.json'], // 忽略文件后缀
        modules: ['node_modules']
    },
    plugins: [
        new htmlWebpackPlugin({
            template: "./index.html",
            filename: "index.html",
            inject: true,
            hash: true,
            chunksSortMode: 'none' //如使用webpack4将该配置项设置为'none'
        }),
        new ExtractTextPlugin("css/styles.css"), 
        new OptimizeCssAssetsPlugin({ // 优化css
            cssProcessor: require('cssnano'), //引入cssnano配置压缩选项
            cssProcessorOptions: {
                discardComments: { removeAll: true }
            },
            canPrint: true //是否将插件信息打印到控制台
        })
    ],
    devServer: {
        hot: true,
        contentBase: path.join(__dirname, 'dist'),
        port: 3002,
    },
};
复制代码

代码地址: 这里

后记

目前vue、react都有自己的webpack配置,都已经配好了,直接拿来用即可。不过作为一个优秀的前端,我们也需要知道如何从零开始配置属于自己的webpack.config.js。 后续写一个只需配置一次,可以多个项目公共的webpack配置,敬请期待...




参考文章:

https://blog.csdn.net/qq_16339527/article/details/80641245 https://juejin.im/post/5aa3d2056fb9a028c36868aa https://juejin.im/post/5adea0106fb9a07a9d6ff6de https://juejin.im/post/5b304f1f51882574c72f19b0

猜你喜欢

转载自juejin.im/post/5c7a9f27f265da2dca387dc9