# 简介

按照webpack官网的说法,webpack本质上是javascript应用程序的静态资源打包器(static module bundler),它可以将模块按照依赖和规则打包成符合生产环境部署的前端资源。webpack可以将按模块异步加载按需引用,通过loader的转换还可以将任何资源看作模块,比如css、图片、json、commonjs模块、amd模块、es6模块等。如今webpack的大版本更新到4.x了,让我们看下如何使用。

# 1.安装使用

新建一个文件夹,依次执行:

npm init
npm i webpack webpack-cli webpack-server -D

即可开始使用webpack。在文件夹中新建文件夹 srcdist 和文件webpack.config.js,其中 src 为打包的源目录用来存放我们要打包的文件,dist 为输出目录,webpack.config.js 是我们配置webpack打包相关参数的文件。

  • 项目目录
  • dist
  • node_modules
  • src
    • index.js
  • package-lock.json
  • package.json
  • webpack.config.js
  • webpack.config.js
//webpack.config.js
const path = require('path')

module.exports = {
  // 打包模式,可选development和production
  mode: 'development',
  // 配置入口文件
  entry: './src/index.js',
  // 配置输出文件
  output: {
    // 输出路径
    path: path.resolve(__dirname, './dist'),
    // 输出文件名称
    filename: 'build.js'
  },
  // 模块,可以使用各种loader,比如css转换,图片压缩等
  module: {},
  // 插件,用于生成模板和其它功能
  plugins: [],
  // 可配置本地的webpack开发服务功能
  devServer: {}
}

编写好webpack.config.js之后就使用命令npx webpack开始执行打包了。webpack的打包机制,只能以js文件为入口,如果你需要打包图片,css,字体,svg等资源,必须使用es6或commonjs等模块加载规范在入口文件中引入,否则是没法进行打包构建的。

  • module和一些loader的使用

module可以使用loader处理引入的模块,支持的模块类型有CoffeeScript,TypeScript,ES5/6,sass,less,stylus。模块的依赖方式有:

# 1.ES2015 import 语句
# 2. CommonJS require() 语句
# 3. AMD definerequire 语句
# 4. css/sass/less 文件中的 @import 语句
# 5. 样式(url(...))或 HTML 文件(<img src=...>)中的图片链接(image url)

下面我用一些loader来介绍module的用法,注意使用loader之前需要使用npm安装对应的loader(npm i xxx-loader -D)

// webpack.config.js
module.exports = {
  mode: '',
  entry: '',
  output: {},
  module: {
    rules: [
      {
        test: /\.css$/, // 匹配css文件模块
        use: ['style-loader', 'css-loader'] // 使用对应的loader处理
      },
      {
        test: /\.(png|gif|jpe?g|svg)$/, // 匹配图片文件
        use: [{
          loader: 'file-loader',
          options: {
            name: '[path][name].[ext]?[hash]',
            // 会输出类似下面这样的结果
            // path/to/file.png?e43b20c069c4a01867c31e98cbce33c9
          }
        }]
      },
      {
        test: /\.(html)$/, // 用来匹配html文件模块(html需要通过插件引入?),可以将html标签中引入的图片资源进行打包
        use: [{
          loader: 'html-loader',
          options: {
            attrs: ['<tag>:<attribute>'] // <tag>为引入图片的标签名,<attribute>为引入图片的属性名
          }
        }]
      }
    ]
  },
  plugins: [],
  devServer: {}
}

通过上面例子中几个loader的使用,大致的介绍了模块的作用和用法。如果没有你需要的功能,可以去webpack官网 (opens new window)上去寻找。

另外还有一个图片打包url-loader,需要注意它和file-loader的区别。url-loader可以说是file-loader的进一步封装,可以将图片转换为base64格式内联在代码中,这样可以减少图片加载的http请求。

  • plugins插件的使用

plugins也是webpack的重要功能之一,借助插件我们可以实现loader无法完成的工作。要使用插件之前,同样需要使用npm安装(npm i xxx -D),并且在webpack.config.js中要引用该插件。下面我简单介绍几个插件的用法,想要更完整的资料可以访问webpack插件 (opens new window)

// webpack.config.js
const path = require('path')
// 使用插件之前需要先加载对应的plugin
const CleanWebpackPlugin = require('clear-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports = {
  mode: '',
  entry: {
    a: './src/a.js', // 可以引入多个入口文件
    b: './src/b.js'
  },
  output: {
    path: path.resolve(__dirname + '/dist/'),
    filename: '[name].[hash:8].js' // 输出多个结果,文件名带hash值
  },
  module: {},
  plugins: [
    // 每次构建完成后先清理一遍dist目录
    new CleanWebpackPlugin(['dist']),
    // 生成html文件到输入目录
    new HtmlWebpackPlugin({
      // 可以以src目录下的html源文件为模板
      template: './src/index.html',
      // 在目标目录下生成目标文件
      filename: './dist/index.html',
      chunks: ['a', 'b'] // 这个参数配合entry可以将打包的模块以<script></script>的形式加载到html文件中
    }),
    // 该插件可以将源目录中的文件直接复制到目标目录中
    new CopyWebpackplugin([{
      from: './src/*.css', // 选择源目录下的所有css文件
      flatten: true // 选择拷贝文件还是包括文件夹,默认是false
    }])
  ],
  devServer: {}
}
  • devServer

devServer可以在本地搭建一个webpack构建服务环境

// webpack.config.js
module.exports = {
  mode: '',
  entry: {},
  output: {},
  module: {},
  plugins: [],
  devServer: {
    contentBase: '/dist', // 服务的内容目录
    port: 4396, // 搭建在本地的服务的端口号
    compress: true, // 服务开启gzip压缩
    open: true // 自动打开本地浏览器
  }
}

同时将项目目录下的package.json改写一下。

// package.json
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "start": "webpack-dev-server", // 开启本地的webpack开发服务环境
  "build": "webpack" // 执行打包
}

之后我们就可以执行npm run startnpm run webpack开启服务,执行打包。

# 2.多文件入口打包

多文件打包的本质就是在入口entry添加多个打包入口,在上面介绍plugins的HtmlWebpackPlugins时就用到了两个入口。我们自然而然的想到有多少个入口文件,就在entry里面添加几个入口不就好了。这样虽然可以实现多文件打包,但是每次我们新加一个入口都要手动添加,非常麻烦。所以我们想办法匹配获取到源目录下的所有的入口文件,然后添加到entry中即可。

function getEntry () {
  let globPath = 'src/**/*.html' // 匹配src目录下的所有文件夹中的html文件
  // (\/|\\\\) 这种写法是为了兼容 windows和 mac系统目录路径的不同写法
  let pathDir = 'src(\/|\\\\)(.*?)(\/|\\\\)' // 路径为src目录下的所有文件夹
  let files = glob.sync(globPath)
  let dirname, entries = []
  for (let i = 0; i < files.length; i++) {
    dirname = path.dirname(files[i])
    entries.push(dirname.replace(new RegExp('^' + pathDir), '$2').replace('src/', ''))
  }
  return entries
}

function addEntry () {
  let entryObj = {}
  getEntry().forEach(item => {
    entryObj[item] = resolve(__dirname, 'src', item, 'index.js')
  })
  return entryObj
}

通过上面两个方法我们就能获取到src目录下的所有入口文件。下面我们看webpack.config.js如何修改

// ...
{
  // entry: resolve(__dirname, "src/home/index.js")
  // 改为
  entry: addEntry()
  //...
}
// ...

getEntry().forEach(pathname => {
  let conf = {
    filename: pathname.replace('src/', '') + '.html',
    template: path.join(__dirname, 'src', pathname, 'index.html'),
    chunks: Array.call([], pathname)
  }
  webpackconfig.plugins.push(new HtmlWebpackPlugin(conf))
})

这样我们就能够实现自动的匹配所有入口文件和要生成的html模板文件,同时在HtmlWebpackPlugin插件使用时,也自动添加了多个模板入口,在目标目录下生成多个html文件。

上面只是大致介绍了多入口的思路,具体代码可以看我实现的一个demo多页面打包 (opens new window)

# 参考文章

webpack文档 (opens new window)

webpack4.x入门配置 (opens new window)

webpack多入口文件页面打包配置 (opens new window)

webpack前端多页项目工程 (opens new window)


作者简介: 宫晨光,人和未来 (opens new window)大数据前端工程师。