Webpack从入门到发布一个NPM包(一)

没头脑

作者 没头脑

创建时间 2021-03-09

更新时间 2021-05-25

阅读 219

评论 0

Webpack是Javascrpit程序的一个静态模型捆绑器。当webpack处理你的应用程序时,它在内部建立了一个依赖关系图,映射了项目中依赖的每个模块,并生成多个捆绑包。
在模块化设计中,开发者将程序拆散成离散的功能块,称之为模块。每个模块都有自己独立的作用域,方便验证、调试和测试。编写良好的模块,可以提供更好的抽象和封装,这样在应用程序中的每个模块都有一致的设计原则和明确的目的。

一、前言

webpack也用了一段时间了,在这里总结一下webpack的知识点。这个系列主要分为一下三个部分

  1. 在本地使用webpack搭建一个项目
  2. 测试本地项目,并发布一个NPM包
  3. 使用GITHUB的Action,自动发布NPM包

二、准备工作

  • 安装 noode
  • 新建一个文件夹,作为项目的根目录,以blog1997为例
    然后在命令行(CMD)中,进入项目根目录,初始化项目
    npm init
    
    之后,根据提示,输入相关信息
    This utility will walk you through creating a package.json file.
    It only covers the most common items, and tries to guess sensible defaults.
    _
    See `npm help init` for definitive documentation on these fields
    and exactly what they do.
    _
    Use `npm install <pkg>` afterwards to install a package and
    save it as a dependency in the package.json file.
    _
    Press ^C at any time to quit.
    package name: (blog1997)                      // 项目名称,默认是文件夹名称,回车即可
    version: (1.0.0)                              // 项目的版本号,默认1.0.0,回车即可
    description:                                  // 项目的描述,默认是空字符串,回车即可
    entry point: (index.js)                       // NPM包的入口文件,会在下一章介绍,回车即可
    test command:                                 // 测试命令,回车即可
    git repository:                               // github仓库的地址,回车即可
    keywords:                                     // 项目的关键字,回车即可
    author:                                       // 作者信息,回车即可
    license: (ISC)                      
    
    然会在项目的根目录下,会创建一个package.json文件,在这里,你可以随意修改项目的相关配置。
    {
    "name": "blog1997",  // 对应之前输入的项目名称
    "version": "1.0.0",  // 对应之前输入的version:(1.0.0)
    "description": "",   // 对应之前输入的description
    "main": "index.js",  // 对应之前输入的entry point
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",        // 对应之前输入的author
    "license": "ISC"
    }
    
  • 在项目根目录中创建一个文件夹src,并创建index.js文件

    三、安装webapck

    npm i --save-dev webpack webpack-cli webpack-dev-server
    
    目前使用的版本是webpack ^5.18.0, webpack-cli ^4.4.0, webpack-dev-server ^3.11.2
  • webpack-cli 为开发者提供了一些灵活的命令,加速开发者自定义webpack项目
  • webpack-dev-server 提供了一个实时重载的开发服务器,但只能在开发过程中使用
  • --save-dev表示实在开发模式中使用的包,npm会将包的信息放在package.json文件中的devDependencies属性中,默认值是--save,包的信息会存放在dependencies属性中。
    然后配置webpack,在项目的根目录中,创建一个文件webpack.config.js(名字可以自定义)
    const path = require("path");
    module.exports = {
      mode: 'development',                         // 设置为开发模式, 值:development|production
      entry: "./src/index.js",                     // 项目模块的入口(索引)文件
      output: {                                    // 输出打包模块的配置
          path: path.resolve(__dirname, "dist"),   // 表示项目根目录下的dist文件夹
          filename: "bundle.js"
      },
      devServer: {
          port: 8081          // 可以自定义服务器端口
      }
    }
    

    四、安装SASS

    npm install --save-dev style-loader css-loader sass-loader sass
    
  • sass 是Dart sass的发行版。提供了一些Node.js API和一些命令行命令。
  • sass-loader 将scss或sass文件编译成css
  • css-loader 将css解析到JavaScript代码中,并处理其中的相关依赖
  • style-loader 将css注入到DOM中的Style标签里
    然后配置webpack.config.js
    module.exports = {
      entry: "./src/index.js",
      module: {
          rules: [{
              test: /\.s?[ac]ss$/,         // 匹配css、scss和sass文件的正则
              use: [{
                  loader: 'style-loader'
              }, {
                  loader: 'css-loader'
              }, {
                  loader: 'sass-loader'
              }]
          }],
      },
    }
    

    五、安装babel

    npm install --save-dev @babel/preset-env babel-loader @babel/core
    
    目前使用的版本是:@babel/core ^7.12.10,@babel/preset-env,^7.12.11,babel-loader,^8.2.2
    配置一下babel,在项目根目录中创建文件 .babel.json
    {
    "presets": [
      [
        "@babel/preset-env",
        {
          "targets": {
            "ie": "10",
            "chrome": "67"
          },
          "useBuiltIns": "usage",
          "corejs": "3.6.5"
        }
      ]
    ]
    }
    
    useBuiltIns: "entry" | "usage" | false,默认是false,这个选项用于配置 @babel/preset-env是怎么处理 polyfills
    entry: 会根据targets环境的不同,将 import "core-js/stable;"**和 **import "regenerator-runtime/runtime" 声明替换为特定的import
    usage: 在每个文件中为polyfill添加特定的导入。相同的polyfill只会添加一次。
    false:不自动添加polyfill 查看更多@babel/preset-env的 配置
    配置webapck.config.js
    module.exports = {
      ...
      module: {
          rules: [{
              test: /\.?js$/,                              // 匹配js文件的正则表达式
              exclude: /(node_modules|bower_components)/,  // 不对node_modules和bower_components文件夹下的js文件进行处理
              use: {
                  loader: 'babel-loader',
                  options: {
                      presets: ['@babel/preset-env']
                  }
              }
          }
    }
    

    六、html-webpack-plugin

    之前安装的babel和sass都属于webpack的loader范畴,用于转换某些特定类型的模块。而插件执行的任务更加广泛,包括:打包优化,资源管理,注入环境变量。
    npm install html-webpack-plugin --save-dev
    
    目前使用的版本是5.2.0。然后在项目根目录中新建一个文件app.html
    <!DOCTYPE html>
    <html lang="zh">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title><%= htmlWebpackPlugin.options.title %></title>
    </head>
    <body></body>
    </html>
    

**<%= htmlWebpackPlugin.options.title %>**会读取webpack.config.js配置文件中的Title属性。

const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
    ...
    plugins: [
        new HtmlWebpackPlugin({
            title: 'Blog1997',   // 标题
            template: 'app.html' // 模板文件路径
        })
    ]
}

修改package.json

{
  "name": "blog1997",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack serve --config webpack.config.js"
  }
  ...
}

运行 npm run dev 就可以在本地打开开发服务器了,我们之前创建的src/index.js会被webapck打包,并添加到模板中。如下图所示 webpack-dev-server

七、file-loader

npm install --save-dev file-loader

file-loader可以帮助你打包一些文件,如JPG、PNG和TTF等。
配置webpack.config.js

module.exports = {
    ...
    module: {
      rules: [
       {
         test: /\.(png|svg|jpg|gif)$/,  // 匹配文件的正则表达式
         use: [
           {
            loader: 'file-loader?name=./img/[name].[ext]'
           }
         ]
       }
      ]
    }
  }

查看更多关于file-loader的 配置

八、分割JS代码

在中型或大型项目中,前端的代码量也是非常大的。如果将所有的JS代码打包在一个文件中,文件的加载时非常慢的,用户的初次体验也不是很友好。所以,我们在这种情况下,需要分割代码,将代码打包在不同的文件中,按需加载。

npm install @babel/plugin-syntax-dynamic-import --save-dev

修改 .babel.json 配置

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "ie": "10",
          "chrome": "67",
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.5"
      }
    ]
  ],
  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

修改 webapck.config.js 配置

const path = require("path");
module.exports = {
    mode: 'development',
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "[name].bundle.js"
    }
}

修改代码

// chat.js
export default {
    say () {
       //  ...
    }
}

// main.js
button.onclick = () => {
    import(/* webpackChunkName: 'dynamic' */ './chat.js').then(chat=> {
        chat.default.say()
    })
}

九、clean-webpack-plugin

在使用webpack-server服务或打包之前,清空dist文件夹

npm i --save-dev clean-webpack-plugin

修改 webpack.config.js

const path = require("path");
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
    mode: 'development',
    ...
    plugins: [
        new CleanWebpackPlugin({
            cleanStaleWebpackAssets: true
        })
    ]
}

十、uglify js Webpack plugin

用于压缩JS代码,并去掉注释部分

npm install uglifyjs-webpack-plugin --save-dev

配置 webpack.config.js

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
    mode: 'development',
    ...
    optimization: {
        minimizer: [new UglifyJsPlugin({
            uglifyOptions: {
                output: {
                    comments: false,
                    beautify: false
                },
                compress: true,
                warning: false
            }
        })]
    }
}

十一、webpack-merge

webpack分为开发环境和编译环境,有时候我们需要根据环境的需求,对webpack进行不同的配置。这时,我们就可以使用webpack-merge提取出配置中的公共部分。
首先在项目根目录下(也可以创建一个文件夹)创建三个文件 webpack.common.jswebpack.dev.jswebpack.prod.js webpack.common.js内容如下

const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
    // 入口js文件的位置
    entry: "./src/index.js",
    output: {
        path: path.resolve(__dirname, '../dist/'),
        filename: "[name].bundle.js"
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: "babel-loader",
                    options: {
                        presets: ["@babel/preset-env"]
                    }
                }
            },
            {
                test: /\.(sa|sc|c)ss$/i,
                use: [
                    {
                        loader: 'style-loader',
                    },
                    {
                        loader: 'css-loader',
                    }, {
                        loader: 'sass-loader'
                    }
                ]
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: [{
                    loader: 'file-loader?name=./img/[name].[ext]'
                }]
            }
        ]
    },
    plugins: [
        new CleanWebpackPlugin({
            cleanStaleWebpackAssets: true
        })
    ],
    optimization: {
        // 压缩js文件的相关设置
        minimizer: [new UglifyJsPlugin({
            uglifyOptions: {
                output: {
                    comments: false,
                    beautify: false
                },
                compress: true,
                warning: false
            }
        })],
        splitChunks: {
            chunks: 'all',
        }
    },
    // 定义变量
    resolve: {
        extensions: ['.js'],
        alias: {
            '~': path.resolve(__dirname, '../src/') // 将~ 定义为项目根目录
        }
    }
}

webpack.dev.js内容如下

const merge   = require('webpack-merge')
const common  = require('./webpack.common.js')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = merge(common, {
  entry: "./src/index.js",
  mode: 'development',
  devServer: {
    hot: true,
    port: 8088,
    historyApiFallback: true
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    // HTML模板的相关设置
    new HtmlWebpackPlugin({
      title: 'blog1997',
      template: 'app.html'
    })
  ]
})

webpack.prod.js内容如下

const merge = require('webpack-merge')
const common = require('./webpack.common.js')

module.exports = merge(common, {
  mode: 'production'
})

最后修改一下 package.json

{
  "scripts": {
    "dev": "webpack-dev-server --config ./config/webpack.dev.js",
    "build": "webpack --config ./config/webpack.prod.js"
  }
}

十二、webpack-bundle-analyzer

使用交互式可缩放树图,可视化输出文件的大小。

npm i --save-dev webpack-bundle-analyzer

配置 webpack.prod.js

const merge = require('webpack-merge')
const common = require('./webpack.common.js')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = merge(common, {
  mode: 'production',
  plugins: [
    new BundleAnalyzerPlugin({
      generateStatsFile: true,
      analyzerMode: 'static'
    }),
  ]
})

当你在运行完 npm run build 之后,浏览器会自动弹出如下页面

十三、其他小技巧

手动指定loader

import styleContent from 'file-loader!extract-loader!css-loader!./mathquill.css'

就先总结到这吧,更多关于webpack的详细配置以及使用,请参阅 webpack官网

提 交
暂无评论