Webpack 快速上手(下)

栏目: JavaScript · 发布时间: 5年前

内容简介:作者 | 朱士奇杏仁前端开发工程师,代码洁癖症早期,关注前端技术。由于文章篇幅较长,为了更好的阅读体验,本文分为上、中、下三篇:

作者 | 朱士奇

杏仁前端开发工程师,代码洁癖症早期,关注前端技术。

由于文章篇幅较长,为了更好的阅读体验,本文分为上、中、下三篇:

  • 上篇介绍了什么是 webpack,为什么需要 webpack,webpack 的文件输入和输出

  • 中篇介绍了 webpack 在输入和输出这段中间所做的事情,也就是 loader 和 plugins

  • 下篇介绍了 webpack 的优化,以及在开发环境和生产环境的不同用法

用过上面两篇较长的篇幅,我们终于了解了 webpack 的整体工作流程。在最后一篇,将会介绍一些零碎但也很重要的知识点。

tree-shaking

tree-shaking 这个技术在 webpack2 就已经被添加进来了,作用是在打包过程中,将模块内没有使用的代码删除,从而减小打包后的文件体积。

这个单词表面的意思是,有一棵小树,你去抖动这棵树,那么树上多余没用的树叶就会掉落,那在代码中具体是什么样子呢。假设我们现在将一些常用的方法都封装在了 util.js 这个文件中:

// util.js 
function add(...args) {
  return args.reduce((prev, currrent, index) => {
    return prev + currrent;
  }, 0);
}

function multiply(...args) {
  return args.reduce((prev, currrent, index) => {
    return prev * currrent;
  }, 1);
}

export {
  add,
  multiply
}

然后我们在 index.js 中需要用到 add 这方法:

// index.js
import { add } from './util.js';

add(1, 2);
add(1, 2, 3);

这样打包后的代码是不含有 multiply 这个函数的,这就是 tree-shaking 的作用。

splitChunk

现在我们再回顾一下 webpack 打包过程

  1. 以 entry 为起点,将所依赖的模块组织成一个树形的结构

  2. 通过不同的 loader 对不同的文件进行编译

  3. 使用 plugins 对文件打包后的文件进行特定的操作

  4. 根据 output 将打包后的文件输出到指定的位置

如果只有一个入口文件,最终也只会打包出一个文件(下文用 chunk   表示,每打包出的一个文件就叫一个 chunk)(排除动态加载的情况( import() ))。这里有一个很明显的缺陷,就是将所有的模块打包成一个文件,打包后的体积一定会很大。同时,如果我们使用了 chunkhash 做文件缓存的话,每次项目修改的时候,无论修改哪个文件,即使是修改了一个换行,chunkhash 的值都会发生改变,那么每次改动上线之后,用户都要重新加载这个巨大的文件,这样用户体验非常糟糕。如果你说我不做文件缓存,那么由于浏览器缓存的原因,用户首次加载的文件会被缓存到本地,下次即使你更新了代码,用户执行的还是首次加载的文件,这样老板会找你聊天的。

为了解决这个问题,我们可以考虑设置多个入口文件,就像在介绍 entry 的例子代码中那样:

// webpack.config.js
module.exports = {
  entry: {
    login: 'src/login.js',
    logout: 'src/logout.js',
  }
}

通过这样的配置,我们就可以将 login.js 和 logout.js 打包成两个文件,而且修改其中一个文件不会影响到另一个的 chunkhash。看起来好像已经解决了上面的问题,但是我们再结合实际的项目深入的分析一下,我们通常会在项目中引入一些类库,比如常见的 lodash ,假设 login.js 和 logout.js 中都用到了 lodash ,这就需要在这个两个文件中显式的 import _ from 'lodash';   这样一来,打包出来的两个文件都包含了 lodash ,这就属于重复引用了,另外如果我们的项目是单页应用,理应只有一个入口,在需要的时候再去加载 login.js 或 logout.js 的代码。

所以我们要解决我们一开始的问题,应该从下面两个点出发:

  1. 分离代码中公共的部分,打包成一个或多个chunk

  2. 将不需要立刻执行的代码分离出来,打包成多个 chunk ,然后通过动态加载的加载这些chunk

针对第一点,我们可以使用 webpack 提供的 SplitChunksPlugin 插件,这个插件和上面介绍的 minimize 一样,需要在 optimization.splitChunks   中配置。在 production 模式下 webpack 会默认做一下代码分离的工作,但是没多大的卵用,所以还是需要我们自己动手配置。

第一步先将来自 node_modules 中的包分离出来,因为这些都是项目所依赖的第三方库,我们是不会改动的(除非升级版本),这些可以做通过 chunkhash   做长期缓存,我们把这写代码打包为   chunk-vendors

// webpack.config.js
module.exports = {
  entry,
  output: {
    path: './dist',
    filename: '[name].[chunkhash:8].js' // 只取chunkhash的前8位
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          name: `chunk-vendors`,
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          chunks: 'initial'
        }
      }
    }
  }
}

在上面的配置中,我们用 cacheGroups   将 node_modules 的代码全部分离出来。 cacheGroups 直译成中文就是缓存组,其实就是存放分离代码块规则的对象,第一个对象的 key 值是 vendors ,这个 key 值没什么用,主要还是看对应的 val 。

  • name:分离后打包出的文件名称。我们设置为 chunk-vendors ,那么打包出来的文件就叫 chunk-vendors.js 。因为在 output.filename 设置了 chunkhash:8,所以最终打包出的文件名称是 chunk-vendors.ac96737b.js 。后面的一串字符就是 chunkhash 的前8位。前面介绍过 chunkhash 是每一个打包出来的文件的 hash ,只要文件的内容没有改变,这个值就不会发生变化,所以只要不对我们依赖的包进行版本升级,或者增加新的包,这个值就不会变动,因此可以用这个办法进行长期缓存。

  • test:用于匹配的文件位置, test: /[\\/]node_modules[\\/]/  表示所有来自 node_modules 下面的代码,可以填写具体的路

  • priority:权重,这个值还是很重要的,webpack 会优先分离权重高的 cacheGroups 。

  • chunks:作用范围,可以设置为 async 表示对异步模块起作用, initial 表示对初始模块起作用, all 则表示对所有模块起作用。

如果打包出的 chunk-vendors 体积很大,而且包含一些经常升级的依赖,那么我们可以继续做拆分

// webpack.config.js
module.exports = {
  entry,
  output: {
    path: './dist',
    filename: '[name].[chunkhash:8].js'
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        vendors: {
          name: `chunk-vendors`,
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          chunks: 'initial'
        },
        vue: {
          name: 'chuank-vue',
          test: /[\\/]node_modules[\\/]vue[\\/]/,
          priority: 10,
          chunks: 'initial'
        }
      }
    }
  }
}

这样我就将 vue 分离成单独一个 chunk 了,不仅减小了 chunk-vendors 的体积 ,当我们升级 vue 版本的时候,也不会影响 chunk-vendors 的 chunkhash 。 注意:不要忘了设置 priority 。

除了将 node_modules 中的类库分离出来,我们自己写的代码中也有些公共的部分,比如在讲 tree-shaking 提到了 util.js ,作为一个 工具 方法,跟定会在项目中好多处用到,那么我们也可以将会这个公共代码分离出来:

// webpack.config.js
module.exports = {
  entry,
  output: {
    path: './dist',
    filename: '[name].[chunkhash:8].js'
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: `chunk-common`,
          minChunks: 2,
          priority: -20,
          chunks: 'initial',
          reuseExistingChunk: true
        }
      }
    }
  }
}

在上面的配置中,我们把被依赖超过两次( minChunks: 2 )的 chunk 都分离到了 chunk-common.f4786e34.js 中。

在解决了对公共代码的分离,下一步即使处理动态加载的代码,之一部分相对简单一些,就像在介绍 babel 时提到的那样,通过 import()   来切分动态加载的代码。

webpack 在将我们的代码打包后,也会生成一些在运行时所必须的代码,这些代码默认会打包进主文件中,我们也可以将它分离出来单独打包成一个文件,这需要在 optimization.runtimeChunk  中单独配置:

// webpack.config.js
module.exports = {
  entry,
  output,
  optimization: {
    runtimeChunk: {
      name: 'manifest'
    }
  }
}

这样就可以将运行时的代码也分里出来,打包为 manifest.js。

其实代码拆分是需要反复尝试的,一般情况下我们只会将 node_modules 里的包分离成一份( chunk-vendors.js ), 业务中公共的代码分离成一分( chunk-common.js ),剩下的都放在了主模块(main.js) 和动态加载的 chunk 中了。但是由于项目的不同,这种方式未必是最好的,所以这需要我们反复的去尝试一各种分离的方式,为了让我们对打包后的代码有更为直观的认识,我们可以借助  webpack-bundle-analyzer   来帮我们很直观的看到打包后每一个 chunk 的大小。

Webpack 快速上手(下)

webpack-dev-server

在上面的介绍中,都是面向打包的,也就是说我们默认代码是无误可以直接打包上线运行,当然这是不可能滴,实际开中需要配合 Google 和 fuck 来 debug 代码,如果用上面的方法来 debug 我相信不管是谁,都会想砸电脑的,因为每次 debug 都要重新的打包,然后再想办法再本地启动一个web服务,用来托管我们打包出的静态文件。那么 webpack 可不可以帮我做到这两点呢:

  1. 监听文件变化,自动重新编译

  2. 创建一个web服务,用来托管打包后的静态文件,方便本地调试

为了解决上面两点,webpack 提供了 webpack-dev-server 这个包,它可以轻松的帮助我们实现上面两功能,这个包需要单独安装一下

npm i webpack-dev-server -D

然后在 npm script   中添加一行 :

// package.json
{
  "scripts": {
    "dev": "webpack-dev-server --mode development",
    "build": "webpack --mode production"
  }
}

这时候在命令行中执行 npm run dev   ,便会在本地启动一个Web服务,当命令行中出现   Compiled successfully   便表示服务启动成功,然后打开浏览器,输入 localhost:8080   便可以直接访问项目了。当源代码发生变化时,便会自动重新编译,然后刷新浏览器。

webpack-dev-server 同样也提供了一些配置选项,可以在配置文件的 devServer 中进行配置:

// webpack.config.js
module.exports = {
  entry,
  output,
  devServer: {
    port: 8080, // 设置端口为8080,默认就是8080
    open: true, // 编译完成后自动打开浏览器
    historyApiFallback: true, // 如果你的项目使用了 HTML5 history API ,开启此项可以将所有的跳转将指向index.html
  }
}

这些配置也可以以参数的形式添加在加命令行后面,但是有的配置只能以参数的形式使用,比如我们想查看编译的进度,就需要加上 --progress   :

// package.json
{
  "scripts": {
    "dev": "webpack-dev-server --mode development --progress",
    "build": "webpack --mode production"
  }
}

学会了如何使用,在简单的介绍一下 webpack-dev-server 的工作原理,webpack-dev-server 是一个基于 express 封装的 Web 服务,当我们在执行 webpack-dev-server 时候,虽然可以看到打包之后的运行效果,但是实际上并没有生成打包后的文件,这是因为 webpack-dev-server 将打包后的内容放在了内存中,当某一个源代码文件发生变更的时候,它也不会重新的再将所有的文件打包一遍,而是只更新了一部分文件,这样的好处是可以加快重新编译的速度,加大程度的减少了开发模式下的编译时间。

讲到这里,你可能也意识到了,如果是开发模式下,有许多事情都不需要做。比如不需要设置 output ,不需要对代码压缩,不需要分离 css 和 js 等等,如果省去这些工作,首次编译的速度又会有大幅度的提升,这是一个优化点,会在后面讲到。

HMR

HMR (hot module replace) 模块热替换,在不刷新页面的情况下更新代码。

在引入了 webpack-dev-server 之后,我们可以做到监听源代码变化,然后刷新浏览器及时看到修改效果。但是在前端开发中,每一步操作往往都伴随着状态和 dom 的变化,比如我们开发一个定外卖的网站,此时正在调试购物车功能,先加了一份煲仔饭,为了满减,再加一份荷包蛋,但是这时候后出现了bug,加了荷包蛋还是没有满减,原来是计算满减的方法写错了,修复这个bug之后,我们发现页面刷新了,回到最开始的样子,于是又要从选择店铺开始在走一遍流程。那可不可以在修复计算满减的方法之后,不要刷新页面也能看到正确的效果呢?这就是 HMR 实现的功能了。

开启 HMR 需要将 devServer.hot 设置为 true ,然后在 plugins 中添加 HotModuleReplacementPlugin 插件,该插件是 webpack 自带的一个插件:

// webpack.config.js
module.exports = {
  entry,
  output,
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ]
  devServer: {
    hot: true,
    /* 其他配置 */
    /* ... */
  }
}

还有一种更简便的方法来开启 HRM ,那就是在命令行中添加参数 --hot   ,然后在执行   npm run dev   的时候也会自动添加 HotModuleReplacementPlugin 插件。

现在我们在 webpack 中开启了 HMR 功能,webpack 可以将老模块替换为编译后的新模块,但是从浏览器层面考虑,浏览拿到新模块之后,并不知道要做什么处理,就像我们前面举的例子中提到,在修改计算满减方法之后,我们希望重新执行一遍这个方法,很明显这个需求不太现实,浏览名没那么聪明。所有这就需要我们显式的用代码来告诉浏览器来做哪些事情。

我们可以在项目代码中通过 module.hot   来判断是否启用了 HMR ,通过 module.hot.accept   来处理模块更新后的要做的事情,现在假设我们的项目入口文件是 index.js ,还有一个 util.js 里面封装了 add 方法:

project
├── src 
│   ├── index.js
│   ├── util.js
│   └── index.html
└── webpack.config.js
// util.js
function add(...args) {
  return args.reduce((prev, currrent, index) => {
    return prev + currrent;
  }, 0);
}
export { add }

然后我们在 index.js 中导入 add 方法,并且将计算结果显示在页面上:

// index.js
import { add } from './util.js';
const h2 = document.createElement('h2');
h2.innerHTML = add('1', '2');

document.body.appendChild(h2);

将项目跑起来之后,发现 add 方法计算的结果错了,经排查发现原来 add 方法忽略了对 string 类型的转换,只要修改一下 util.js 中的 add 函数就好了:

// util.js
function add(...args) {
  return args.reduce((prev, currrent, index) => {
    return prev + currrent * 1;
  }, 0);
}

export { add }

这时候可以发现,页面中虽然显示了正确的结果,但是页面刷新了,而我们希望的是在页面不刷新的情况下显示正确结果,这时候就要在 index.js 添加热更新后需要执行的代码了:

// index.js
import { add } from './util.js';

const h2 = document.createElement('h2');
h2.innerHTML = add('1', '2');

document.body.appendChild(h2);

if (module.hot) {
  module.hot.accept('./util.js', () => {
    h2.innerHTML = add('1', '2');
  });
}

这样再去修改 add 方法的时候,h2 显示的内容会发生变化,但是页面却不会刷新,这才是我们想要的热更新。

讲到这里你可能已经发现,实现一个完美的热更新,难点不是在 webpack 的配置,而是在我们的项目代码中,我们要针对所有需要热更新的模块加上热更新之后的回调( module.hot.accept  ),不过社区中已经提供了一些 loader 使 HMR 与各种框架平滑地进行交互 https://webpack.js.org/guides/hot-module-replacement/#other-code-and-frameworks ,

如果需要样式热更新的话,我们需要判断当前的环境变量是否为 development ,然后将 MiniCssExtractPlugin.loader 换成 style-loader ,因为 MiniCssExtractPlugin 还不支持 HMR :

// webpack.config.js
const styleLoader = process.env.NODE_ENV === 'development' ? 'style-loader' : MiniCssExtractPlugin.loader;

module.exports = {
  entry,
  output,
  module: {
    rules: [{
      test: /\.css$/,
      use: [styleLoader, 'css-loader']
    }]
  }
}

content-base

在 plugins 这一章节中,提到了 copy-webpack-plugin   这个插件,它是用来将一些静态资源拷贝到打包后的目录,但是在开发环境下,我们是通过 webpack-dev-server 创建一个 Web 服务,它的根目录默认是配置文件所在的目录,所以在开发模式下,如果需要请求一些静态资源,那么我们就需要设置一下   contentBase  

假设我们的静态资源放在了项目根目录下的 static 文件夹下面,而且配置文件 webpack.config.js 也放在了项目根目录下,那么我么就可以将 devServer.contentBase  设置为   static  

// webpack.config.js
module.exports = {
  entry,
  output,
  devServer: {
    contentBase: 'static'
  }
}

假设 static 下面有一个图片 logo.png ,我们就可以通过 localhost:8080/logo.png 来访问这张图片了。

public-path

在介绍 output.publicPath   的时候提到,这个值并不会影响打包后输出的文件路径,他只是设置在线上运行的时候,所请求的资源路径,当我们在 webpack-dev-server 这个 Web 服务下调试我们的代码的时候,可能也会出现和类型的情况,这时候就需要设置一下 devServer.publicPath 了。它 output.publicPath 的区别的是 一个作用于线上环境,一个作用于我们调试的开发环境。

proxy

在开发过程,经常需要调用后端提供的接口,一般情况会把接口部署在测试环境,比如 http://test.api.com 然后我们在项目中通过 ajax 的方式去调用。由于同源策略,我们在开发的时候通过 webpack-dev-server 启动的 Web 服务的域是 localhost:8080 ,很明显跨域了,接口无法调用了。这个时候有两种办法解决,一是在测试上环境上配置 cors ,将我们的 localhost 加入允许跨域的名单;二是我们在本地利用 node 去请求这个接口,然后再将请求内容发送给前端,在整个过程中 node 扮演的角色就是一个可靠的跑腿子,你去把请求交给它,它把请求送给测试环境,测试环境把响应交给它,它再把响应送到你这边。

Webpack 快速上手(下)

在 webpack-dev-server 集成了中间件 (http-proxy-middleware)[https://github.com/chimurai/http-proxy-middleware] 可以很轻松的完成接口转发,比如我们想将所有的以 /api 开头的请求都转发到 http://test.api.com 只要在 devServer.proxy   像下面这样配置即可:

// webpack.config.js
module.exports = {
  entry,
  output,
  devServer: {
    proxy: {
      '/api': 'http://test.api.com'
    }
  }
}

devServer.proxy  暴露出的配置项和 (http-proxy-middleware)[https://github.com/chimurai/http-proxy-middleware] 的配置项完全一样,具体可以点击链接查看。

mock

后端已经写好的接口我们我们可以用转发的方式调用,而对于还没有写好的接口我们可以通过 mock 的方式来调用,这样可以解决因为接口调用通而导致我们开发不畅的问题。因为 webpacl-dev-server 是基于 express 封装了,并且将 express 的实例暴露在了 devServer.before    devServer.after   这两个配置项下面,所以我们完全可以将后端没有写好的接口在 devServer.before 通过 express 去 mock 。假设我们现在需要调用   /api/user/creation   这个接口来创建用户,我们可以这样 mock

// webpack.config.js
module.exports = {
  entry,
  output,
  devServer: {
    befor(app) {
      app.post('/api/user/creation', (req, res) => {
        // some code
        res.json({success: true});
      });
    }
  }
}

如果你需要 mock 的接口后端,那你完全可以像写 express 那样去写接口,当然有些常用的中间件需要我们自己去安装。

其它

source-maps

webpack 打包压缩之后的代码可读性几乎为零,同时也不方便调试,这时候可以通过设置 devtool   选项来帮助我们在开发环境调试,具体效果是:在 chrome 中(其它高级浏览器同样支持)打开控制台,我们可以在   Sources   中看到一个以   webpack:// 开头的资源,里面的内容和我们编写代码大致相同(这取决于   devtool   的值)。

Webpack 快速上手(下)

由于 devtool 会影响打包的速度和打包后的代码质量,所以在生产环境的构建中,不建议开启此项(默认为 none ),只要在开环境设置为   eval-source-map   即可。其它配置和打包速度可以参考   官网  

alias

当项目的目录结构越来越深,模块变得越来越多的时候,模块间的引用会变得很混乱,时常会看到下面这样的代码:

import ComponentA from '../../../../../components/a.component.js';
import ServiceA from '../../../../../service/a.service.js';

有没有想骂人的冲动?这时候可以使用 webpack 的 alias   选项来解决这个问题,配置文件的内容如下:

// webpack.config.js
module.exports = {
  entry,
  output,
  resolve: {
    alias: {
      '@': path.resolve(__dirname, "src"),
      'components': path.resolve(__dirname, "src/components"),
      'services': path.resolve(__dirname, "src/services"),
    }
  }
}

上面的配置表示为 srcsrc/componentssrc/services   分别设置一个别名,我们就可以在代码中用   @   表示相对路径   src   而不必再使用   ../../   一层一层的向上查找了。假设我们现在的项目结构是下面这样子:

project
├── src
│   ├── components
│   └── services
└── webpack.config.js

这样我们可以在任意文件夹下的代码内使用 @   来表示根目录   src/ ,使用 components   来表示路径   src/components/   ,所以上面例子中的代码可以在简化为:

import ComponentA from '@/components/a.component.js';
import ServiceA from 'services/a.service.js';

这样配置之后,webpack 在打包编译的时候能识别简化之后的路径,但是编辑器却未必能识别,这又给我们开发带来了一些困扰,如果你是 vscode   用户的话,这个问题可以很好的解决。只要在项目的根目录添加一份配置文件   jsconfig.json   即可,配置文件的内容如下:

{
  "compilerOptions": {
    "baseUrl": ".",   // 根目录
    "paths": {
      "@/*": [ "./src/*" ],
      "components/*": [ "./src/components/*" ],
      "services/*": [ "./src/services/*" ],
    }
  }
}

这个配置文件和 webpack 是没有关系的,它是给 vscode   用的,想请可以查看这里:https://code.visualstudio.com/docs/languages/jsconfig

extensions

在原生的 JavaScript 中,使用 import   加载一个模块是可以不用写文件的扩展名的,nodejs 中的 require 也是一样,就像这样: import ModuleA from 'a' ,现在有了 loader 我们也希望   import   其它类型文件的时候也不写扩展名,比如

import styles from '@/styles/common';
import html from '@/tpl/login';

只需在 webpack 中配置 extensions 即可,具体代码如下:

// webpack.config.js
module.exports = {
  entry,
  output,
  resolve: {
    extensions: ['.js', '.json', '.css', '.html']
  }
}

该选项的值是一个数组,默认值为 ['.js', '.json']   ,当我们手动配置之后,默认值会被覆盖,所以为了不影响之前的写法,要在配置中将   .js    .json   也加上。

个人建议不要配置此项,尽量把文件的扩展名写全,这样不仅可以知道引入的文件是什么类型,而且在打包的时候速度也相对快一些。

externals

开发一个 Web 项目肯定会用到第三方的类库比如 jQuerylodash   等,有人会选从 npm 下载,有人会选择从 cdn 加载。这两种方式使用起来都很简单:

  • 从 npm 下载的包只需在用到的时候 import   就行了: import _ from 'lodash'

  • 从 cdn 加载类库只要在 html 页面通过 script   引入之后(注意引用顺序),便可以在任何地方使用

但是从 cdn 引入的资源在开发过程有一个很不好的地方:既然已经是模块化开发了,突然冒出一个全局变量会让人觉得很莫名其妙,而且这个变量也不能类型提示。

那可不可以这样子呢:

  1. 从 cdn 加载第三方类库(速度快)

  2. 在代码中依然使用 import   的方式来引入资源(代码模块清晰)

  3. 打包的时候排除从cdn加载的资源(减小打包后的代码体积)

答案是可以的,配置一下 externals 就可以轻松实现,以 jQuery   为例,具体代码如下:

// webpack.config.js
module.exports = {
  entry,
  output,
  externals: {
    jquery: 'jQuery'
  }
}

在代码中就可以这样使用 jQuery   :

import $ from 'jquery';
$(() => {
  console.log('hello jQuery');
});

而且打包的时候会自动的把 jquery   排除掉。

从上面的配置中可以看出, externals  是一个对象,它的 key ( jquery ) 对应的是代码中引入的包名,也就是   from   后面的字符串,它的 val ( jQuery ) 就是暴露在全局的变量名,jQuery 暴露在全局的变量名为   jQuery    $   ,所以这里换成   $   同样是可以的。

所以上面的代码可以理解为是下面这种写法:

const $ = window.jQuery;
$(() => {
  console.log('hello jQuery');
});

如果想对这个选项有更多的理解,可以参考这里:https://github.com/zhengweikeng/blog/issues/10

分离配置文件

上面的配置中,有的是适用于生产环境的,有的是适用于开发环境的,所以我们要将配置文件做一下分离。在项目中创建   build   文件夹,用来存放我们的构建脚本,在 build 中创建   webpack.common.js   我们可以将一些通用的配置写在这里面,比如 entry、output、loader 等等。然后我们在创建   webpack.prod.js  webpack.dev.js   两份配置文件,分别用来编写打包和开发是的脚本,已经在 webpack.common.js   中写好的配置,就不需要在写了。然后我们利用   webpack-merge   将通用的配置分别和 dev、prod 的配置合并:

// build/webpack.prod.js
const merge = require('webpack-merge'); 
const webpackCommonConfig = require('./webpack.common');
module.exports = merge(webpackCommonConfig, {
/** 针对打包到生产环境的配置 */
});

最后再利用 npm script   设置不同的脚本

{
  "scripts": {
    "dev": "webpack-dev-server --mode development --color --progress --config  build/webpack.dev.js",
    "build": "node build/build.js"
  }
}

这里我已经写好一份可以直接使用的配置,大家可以参考一下 webpack-workbench https://github.com/onlymisaky/webpack-workbench

全文完

以下文章您可能也会感兴趣:

我们正在招聘 Java 工程师,欢迎有兴趣的同学投递简历到 rd-hr@xingren.com 。

Webpack 快速上手(下)

杏仁技术站

长按左侧二维码关注我们,这里有一群热血青年期待着与您相会。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户至上:用户研究方法与实践(原书第2版)

用户至上:用户研究方法与实践(原书第2版)

凯茜·巴克斯特 / 王兰、杨雪、苏寅 / 机械工业出版社 / 2017-5-1 / 99

《UI/UE系列丛书 用户至上:用户研究方法与实践(原书第2版)》是用户研究方法指南,谷歌用户体验研究员十几年工作经验结晶,从理论到实战,包含完整的实操案例,是设计以人为中心产品的实用手册。 《UI/UE系列丛书 用户至上:用户研究方法与实践(原书第2版)》包含五个部分共15章。入门篇包括第1~5章:介绍用户体验入门,如何理解目标用户,道德与法律问题,如何搭建研究设施,如何选择用户体验研究方......一起来看看 《用户至上:用户研究方法与实践(原书第2版)》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具