Webpack 之常见见招拆招

前端的发展,大体的发展路线能够看黄玄的JavaScript 模块化七日谈。从最初的全局污染式的注入到ES6模块化,打包工具的不断迭代替换。主要的缘由都是由于前端发展愈来愈复杂庞大所致使。css

本篇文章主要是来谈谈 webpack 在咱们平时的开发工做中起到什么做用,以及咱们该如何灵活的应用它来成为咱们的利器。大多数状况下我不会说明怎么使用,由于这样会致使篇幅太多不容易阅览,因此具体的配置仍是得本身阅览官方文档。html

背景

现在的前端百花齐放,再也不像之前那样直接操做 DOM 而后压缩扔到服务器上去。看似没啥问题,可是不断的重复劳动力致使开发效率低下。前端

React、Vue、Angular2。Typescript、Flow、CoffeeScript、ES6。SASS、LESS。分别为前端框架JS超集/JS新标准CSS预处理器。以上的这些没法直接的在浏览器上跑,都须要转换为 ES5/CSS 才能够。(注:ES6 能够在支持 ES6 语法的浏览器上运行,如Chrome)vue

构建工具

不管什么构建工具,它们作的内容都是大同小异:代码转换、文件优化、代码分割、模块合并、自动刷新、代码校验、自动分布。历史上的构建工具都是基于Node.js开发的。有GruntGulpFis3RollupBrowserify 等等。更具体能够参考前端构建:3类13种热门工具的选型参考node

至于它们之间优劣性以及为何选择webpack在网上有不少相关的资料能够参考,在这里就再也不赘述了。react

开始

基础配置

/// webpack.config.js
const path = require('path');
module.exports = {
  // js 执行入口文件
  entry: './main.js',
  output: {
    // 将全部依赖的模块合并输出到一个 bundle.js 文件
    filename: 'bundle.js',
    // 将输出文件都放到 dist 目录下
    path: path.resolve(__dirname, './dist'),
  }
};
复制代码

执行 webpack --config webpack.config.js,则会在dist文件夹生成bundle.js文件,这就是最基本的 webpack 配置。更多配置查看官网webpackwebpack

Loader

Loader 主要是用于将模块代码转换为可在浏览器运行的代码。能够理解为翻译机。如将 Less 转换为 CSS,Typescript 转换为 Javascript 等。git

Plugin

Plugin 主要是扩展 webpack 的功能,加强 webpack 的灵活性。如extract-text-webpack-plugin,能够将包中的文本提取到单独的文件中,从bundle.js提取 css 到单独的文件出来等。web

DevServer

webpack-dev-server,能够帮咱们解决上面没提到可是在开发中遇到的痛点。vue-router

  • 提供 HTTP 服务而不是使用本地文件预览;
  • 监听文件的变化并自动刷新网页,作到实时预览:
  • 支持 Source Map,以方便调试。

见招拆招

交待完 webpack 的基础也是重要的功能以后,咱们从工做中开始,见招拆招,也就是说咱们平时须要作什么,webpack 能帮咱们作什么。

见招 - ES6

ES6的出现引入了新的语法,提升了开发效率。可是目前仍有不少浏览器对其标准支持不全。因此咱们须要将其转换为 ES5 以及对新 API 打 polyfill。才能正常的使用。

拆招 - Babel

Babel 是 JS 编译器,主要功能就是将 ES6 转为 ES5,详看 What is Babel? · Babel。在项目根目录建立.babelrc

{
  // plugins 告诉 Babel 要使用哪些插件,这些插件能够控制如何转换代码 。 
  "plugins": [
    [
      "transform-runtime",
      {
        "polyfill": false
      }
    ],
  ],
  // presets属性告诉 Babel要转换的源码使用了哪些新的语法特性,一个 Presets对一组新语法的特性提供了支持,多个 Presets 能够叠加。
  "presets": [
    [
      // 除此以外,还有往上的标准如 ES2016等 以及 Env,其中 Env 包含ES 标准的最新特性
      "es2015", 
      {
        "modules": false
      }
    ],
    // 社区提出却还未入标准的新特性,有stage0 - stage4,被归入的可能性依次增长
    "stage-2",
    // 特定应用场景语法特性
    "react"
  ]
}
复制代码

在了解 Babel 后,下一步就是配置 Webpack。

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

见招 - Typescript

Typescript 是 Javascript 类型的超集,它能够编译成纯 Javascript。TypeScript—JavaScript的超集

拆招 - Typescript

Typescript 官方提供了能将 Typescript 转换成 JavaScript 的编译器。执行安装npm i -g typescript,而后在根目录新建配置编译选项tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs", // 编译出的代码采用的模块规范
    "target": "es5", // 编译出的代码采用 ES 的哪一个版本
    "sourceMap": true // 输出 Source Map 以方便调试
  },
  "exclude": [
    "node_modules"
  ]
}
复制代码

配置完tsconfig.json,咱们就能够配置 Webpack。

module.exports = {
  ...
  resolve: {
    extensions: ['.ts']
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: 'ts-loader'
      }
    ]
  },
  devtool: 'source-map',
}
复制代码

见招 - SASS/LESS

SASS 和 LESS 都是 CSS 的预处理器,它们都是能够方便的管理代码,抽离样式公共部分,经过逻辑来书写更加灵活的样式代码,从而提升效率。关于他们更多的信息能够Sass: Syntactically Awesome Style SheetsGetting started | Less.js去查看。

拆招 - SASS-LOADER / LESS-LOADER

安装完sass-loaderless-loader以后,直接配置Webpack。

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

其处理流程以下:

  1. 经过 loader 将 sass/less 文件转换为 css 代码,再将其交给 css-loader 处理;
  2. css-loader 会找出 css 代码中导入语句如@importurl(),同时支持 css modules、压缩 css 等功能,而后交给 style-loader 处理;
  3. style-loader 会讲 css 转换为字符串注入 js 代码中。

见招 - React

React 中主要是由于其代码中使用了 JSX 和 Class 特性,所以咱们须要将其转换为浏览器能识别的 JavaScript 代码。

拆招 - Babel

咱们须要依赖 babel-preset-react来完成语法上的转换。因此咱们还须要配置.babelrc,加入 React Preset。

"presets": [
  "react"
]
复制代码

其实这样就能够了。可是咱们有时候会使用 React + Typescript 组合来提升咱们开发效率。在上面咱们提到 Typescript 的开发,咱们此次来修改其配置文件tsconfig.json

{
  "compilerOptions": {
    "jsx": "react" // 开启 JSX,支持 React
  }
}
复制代码

至于 Webpack 的配置,其实不用太多的改动,只须要支持下/\.tsx/后缀文件就行。


见招 - Vue

Vue 没有 React 那样会内置专属语法,但它和 React 同样,都推崇组件化和由数据驱动的思想。话很少说,直接拆招。

拆招 - vue-loader

解析 vue 主要须要 vue-loadervue-template-compilervue-loader 主要事用来解析和转换.vue文件,提取出其中的逻辑代码、样式代码以及 html 模板 template,再分别将它们交给对应的 Loader 去处理,如 template 则就是由 vue-template-compiler 去处理的。

/// webpack
module: {
  rules: [
    {
      test: /\.vue$/,
      use: ['vue-loader'],
    },
  ]
}
复制代码

一样,假如咱们须要 Vue + Typescript 组合呢?从 Vue 2.5 开始,就提供了对TS 的支持。配置 tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "es2015", // 用于使 Tree Shaking 优化生效
    "moduleResolution": "node",
  }
}
复制代码

除此以外还须要在声明文件 vue-shims.d.ts 定义 vue 类型:

declare module "*.vue" {
  import Vue from "vue";
  export default Vue;
}
复制代码

修改 webpack 配置文件。

module: {
  rules: [
    {
      test: /\.ts$/,
      loader: 'ts-loader',
      exclude: /node_modules/,
      options: {
        appendTsSuffixTo: [/\.vue$/],
      }
    }
  ]
}
复制代码

到这里为止,咱们就能够经过 webpack 来进行咱们的开发工做了。可是实际项目中有不少的痛点,例如代码检查,热更新,CDN发布等。咱们不可能每次都手动的来配置,这样太繁琐太浪费时间了。接下来咱们经过 webpack 来优化咱们的开发体验。

见招 - 监听更新

当咱们在开发阶段,确定会在期间不断地修改源码。可是咱们不可能每一次修改就手动编译而后刷新页面,这明显浪费咱们的时间跟精力。因而就有了自动化监听更新,原理就是监听本地源码包括样式,一旦发生变化时,就会自动构建而后刷新浏览器。

拆招 - webpack

经过 webpack 开启监听模式,通常有两种方式:

  • 配置webpack.config.js设置watch: true;
  • 执行 webpack 时,能够带上参数,如 webpack --watch

它的工做原理就是经过 aggregateTimeout 设置等待时间,到该时间时就会去检查编辑后的文件的最后编辑时间从而达到监听的目的。

见招 - 自动刷新浏览器

在上面咱们提到了监听更新,可是更新完后浏览器应该有所表现,否则手动刷新浏览器的行为也是蛮愚蠢的。因此当咱们监听到的文件一旦发生了修改,浏览器就要主动去刷新浏览器。

拆招 - webpack-dev-server

咱们使用 webpack-dev-server 模块启动 webpack 模块时,webpack 模块的监听模式默认会被开启。webpack 模块会在文件发生变化时通知 webpack-dev-server 模块。

经过 webpack-dev-server 启动时,有如下两种方式能够实现自动刷新:

  • webpack-dev-server(默认):向要开发的网页注入代理客户端代码,经过代理客户端去刷新整个页面;
  • webpack-dev-server --inline false:将要开发的网页装进一个 iframe 中,经过刷新 iframe 去看到最新效果。

见招 - 模块热替换

在上面提到的更新后刷新是会刷新整个页面,这样的体验很差。因此 webpack-dev-server 还支持模块热替换,就是在不刷新整个页面的状况下只替换修改的文件,这样不但快捷,并且数据也不会丢失。

拆招 - webpack-dev-server

实现模块热替换也有两种方式:

  • webpack-dev-server-hot
  • HotModuleReplacementPlugin(推荐)

见招 - 检查代码

当咱们的项目愈来愈庞大时,特别是多人协做开发,会致使一个问题就是代码会有多种风格致使可读性降低。所以咱们须要在提交以前执行自动化检查,让项目成员强制遵照统一的代码风格,同时也能够分析出潜在的问题。

拆招 - **lint 及 husky

**lint 这里指的是针对不一样的语言使用不一样的 lint 检查工具。

  • eslint:用来检查 JavaScript,配置 .eslintrc 来添加规则,再结合 eslint-loader 就能够经过 webpack 来执行代码检查;
  • tslint:用来检查 TypeScript,配置 tslint.json 来添加规则,再结合 tslint-loader 就能够经过 webpack 来执行代码检查;
  • stylelint:用来检查样式文件,如 SCSS、Less等,配置.stylelintrc 来添加规则,再结合 stylelint-webpack-plugin 就能够经过 webpack 来执行代码检查;

上面经过整合到 webpack 存在个问题,就是在开发过程当中构建速度会变慢不少。因此咱们建议在提交的时候经过 Git Hook 来执行咱们的代码检查,如huskyhusky 会经过 Npm Script Hook 自动配置好 Git Hook,而后咱们只须要在 package.json 添加 script 脚本,其中 precommitprepush 只须要其中一个就行了,配置以下:

{
  "scripts": {
    // 在执行 git commit 前会执行的脚本 
    "precommit": "npm run lint",
    //在执行 git push 前会执行的脚本 
    "prepush": "lint",
    // 调用 eslint、stylelint 等工具检查代码
    "lint": "eslint && stylelint"
  }
}
复制代码

其余

除了上面这些,咱们可能还须要须要如下的配置: 加载图片 - file-loader:将 JavaScript 和 CSS 中导入图片的路径替换成正确的路径,并同时将其输出到对应位置; - url-loader:将文件的内容通过 base 64 编码后注入JavaScript 或 CSS 中。

加载SVG - raw-loader:能够将文本文件内容读取出来,注入到 JavaScript 或 CSS 中。 - svg-inline-loader:跟 raw-loader 同样,可是增长了对 svg 压缩的功能。

优化

区分环境

区分环境的好处我就很少解释了,这里主要是用到了 webpack自带(当代码出现process时,webpack会将其模块打包进去)的 process 模块。使用方法也很简单 process.env.NODE_ENV 就好了。

压缩代码

上线后咱们除了GZIP对其文件进行压缩,咱们还须要对文件自己进行压缩进而减小网络传输流量和提升网页加载速度。这里的文件压缩就是用到了UglifyJsPlugin 插件。详情配置能够查看官方,须要注意的是,记得区分环境如 source-map 等。

压缩 CSS

压缩 CSS,用一款基于 PostCSS 的压缩工具 cssnanocss-loader已经内置其模块了,只须要开启 css-loaderminimize 选项便可。

CDN加速

这里不是说要经过前端来作 CDN 加速的事,而是当咱们上传静态资源时,静态资源须要经过 CDN 服务提供的 URL 地址去访问,而咱们要作的,就是在生成页面时,将咱们的静态资源替换为CDN的地址。 咱们所说的静态资源主要分为两种,入口 HTML 文件以及 JS、CSS、图片等静态资源。前者的处理方法是存在服务器而非CDN,而且服务器不对其作缓存处理,这样就能够保证每次请求的入口文件都是最新的;后者则会上传 CDN 服务上,作缓存处理。 简单的来讲就是入口 HTML 文件是在每一次请求都是最新的,那么其请求的 静态资源的 Hash 值也有可能会更新,那么只要发生变换,则去请求新的静态资源就好了。

那么问题来了,怎么作才能每次打包新的 HTML 文件时,其请求的静态资源的也会跟随变化呢?webpack 及其插件提供了其功能,分别为:

  • output.publicPath 中设置 Javascript 的地址;
  • css-loader.publicPath 中设置被 CSS 导入的资源的地址;
  • Webplugin.stylePublicPath 中设置 CSS 文件的地址。

提取公共代码

webpack 有个专门用于提取多个 Chunk 中公共部分的插件 CommonsChunkPlugin,用法以下:

const ComrnonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');

new CommonsChunkPlugin({
  // 从 a、b chunk 提取共同的代码模块
  chunks: ['a', 'b'],
  // 将其封装到 common 新 chunk
  name: 'common',
}) 

复制代码

按需加载

在这里只针对 Vue、React 来讲。目前比较流行的作法就是在路由上作处理。

Vue vue-router 经过 vue 的动态组件 & 异步组件 — Vue.js,就能够实现按需加载了,如:

resolve => require(['./Test'], resolve)
复制代码

React react-router 还能够配合 react-loadable,实现路由按需加载,如:

function asyncLoad (loader) {
  return Loadable({ loader });
}
asyncLoad(() => import('./Test'));
复制代码

分析报告

webpack 自带分析功能webpack --profile --json > stats.json,也能够安装可视化分析工具webpack-bundle-analyzer更加直观的观察项目的状况。

最后

本篇的大多内容是阅览完《深刻浅出 webpack》后的总结。之因此想总结,是由于 webpack 的配置给人的感受就是配置麻烦很琐碎。所以就有了这个想法,对知识点的查漏补缺,同时也是一次对知识点的梳理。这篇文章目前主要梳理经常使用的一些配置、插件以及优化。固然这也只是冰山一角,更多的还须要本身去查阅官方文档,不一样版本也会有不一样的差别性。以后遇到问题,我也会持续记录下来。

相关文章
相关标签/搜索