这是别眨眼学前端的第 003 篇文章,别眨眼系列文章的主旨在于能够快速的回顾某个库/框架或是某种编程语言的基本知识点,于我而言作用类似于详细一点的复习大纲,用于快速回顾记忆,标题命名来源于 苹果 的视频 Don't blink

这一篇我们来快速回顾一下 Webpack,本文是基于 Webpack — The Confusing Parts 学习编写而成,可能会有描述不够清楚的地方,可自行参考原文

其余别眨眼系列文章,可以查看以下链接,

Webpack 是在 React 或 Redux 应用中最常见的模块管理工具,然而第一眼看 Webpack 的配置文件时,却感觉非常难懂,因为 Webpack 使用了自创的语法以及拥有独特的核心理念,下面我们会对 Webpack 的核心理念和基本语法进行快速回顾,

Webpack 的核心理念

Webpack 拥有两条核心理念,

  1. 一切皆为模块,不单单 JS 文件可以是“模块”,其余任何 (CSS, HTML, 图像) 文件皆可成为模块。因此你可以这样 require('myJSfile.js') 或这样 require('myCSSfile.css') 的引入我们需要的任何东西,这也意味着我们能够将任何文件分割成可管理的片段,并进行复用等,

  2. 只在我们需要的时候加载需要的东西 (Load only “what” you need and “when” you need),通常的模块管理会将所有模块打包至一个单独的文件,bundle.js,但是在实际的应用中,可能这个文件会非常的大!所以 Webpack 拥有很多特性来支持你拆分你的代码 (split your code) 并生成多个 bundle 文件,并且可以异步的加载所需的不同部分

接下来我们来看一下 Webpack 的一些基本语法,也是容易混淆的一些部分,

开发环境 Vs 生产环境 (Development Vs Production)

在 Webpack 众多特性中,一些适用于开发环境的 (Development-only) ,一些适用于生产环境 (Production-only),一些是二者皆可,

// example

output: {
  // Dev
  publicPath: '/'
  // Production
  publicPath: 'http://someCDN.com'
},

plugins: {
  // Dev
  new webpack.HotModuleReplacementPlugin(),
  // Production
  new AppCachePlugin({
    exclude: ['.htaccess']
  }),
  new webpack.optimize.UglifyJsPlugin()
}

通常来讲,很多项目会在不同的环境下使用不同的 Webpack 特性,因此往往会配置两个配置文件

'scritps': {
  // 生产环境
  'build': 'webpack --config webpack.config.prod.js',

  // 开发环境
  'dev': 'webpack-dev-server'
}

webpack CLI Vs webpack-dev-server

Webpack 拥有两种交互接口,

  1. Webpack CLI 工具 -- 默认的操作接口,随着 Webpack 默认一起安装,

  2. webpack-dev-server 工具 -- 一个基于 Node.js 的服务器,必须单独安装

Webpack-dev-server (更加适用于开发环境)

Webpack-dev-server 默认使用 8080 端口,它能够提供比如 “实时更新 (Live Reloding)” 或者 ”热替换 (Hot Module Replacement)” 等功能,

// 安装
npm install webpack-dev-server --save

// 在终端中使用
webpack-dev-server --inline-hot

// 在 `package.json` 中使用
'scripts': {
  'start': 'webpack-dev-server -inline --hot',
}

// 然后执行该脚本
npm start

// 在浏览器中访问
http://loacalhost:808

当然你可以使用两种方式去配置 wbpack-dev-server ,

  1. webpack.config.js 中使用 devServer 对象来配置,

  2. 直接在命令行中输入

// 通过命令行
webpack-dev-server --hot --inline

// 通过 `webpack.config.js`
devServer: {
  inline: true,
  hot: true
}

hot Vs inline

inline 选项为整个页面提供了 "实时更新" 功能,而 hot 则提供了 “热替换” 功能,用于尝试仅更新改变的组件,而非整个页面。当我们同时开启两个功能的时候,当源文件发生改变,webpack-dev-server 首先会尝试热替换,当热替换无法工作的时候,才会刷新整个页面

'entry' -- 字符串 Vs 数组 Vs 对象

Entry 告知 Webpack 根模块或是入口文件在哪里,它的值可以是字符串,数组或者对象,

// string
entry: '.publish/src/index.js'

// array
entry: ['.publish/src/index.js']

// object
entry: {'.publish/src/index.js'}

如果只有单一的入口文件,上述三种写法的结果是一样的,

entry -- 数组

当我们想要加入几个不相互依赖的文件的时候,即可使用数组格式,

比如,当我希望给我的页面加入 Google 统计,

{
  entry: ['./public/src/index.js', './public/src/googleAnalytics.js'],
  output: {
    path: '/dist',
    filename: 'bundle.js'
  }
}

当我们这样配置的时候,Webpack 会先打包出 bundle.js ,然后会将 Google 统计的代码插入在 bundle.js 的最后,

entry -- 对象

当我们想要编写一个真正的多页面应用的时候,我们可能会拥有多个 HTML 文件,比如 index.html 或是 profile.html,我们可以使用对象的方式来配置 Webpack 来告知我们需要生成多个 bundle 文件,

下属例子中,我们将会生成两个 JS 文件,

{
  entry: {
    'indexEntry': './public/src/index.js',
    'profileEntry': './public/src/profile.js'
  },

  output: {
    path: '/dist',
    filename: '[name].js' // indexEntry.js and profileEntry.js
  }
}

当然我们可以将上述方法组合使用,

{
  entry: {
    'vendor': ['jquery', 'analytics.js', 'optimizely.js'],
    'index': './public/src/index.js',
    'profile': './public/src/profile.js'
  },

  output: {
    path: '/dist',
    filename: '[name].js' // vendor.js, index.js and profile.js
  }
}

output -- path Vs publicPath

output 告知 Webpack 在哪里存储输出文件,其中有两个属性会引起混淆,pathpublicPath

path 属性仅仅是告知 Webpack 在哪里存储输出文件,而 publicPath 则是在生产环境编译时给一些插件所引用的静态文件生成 URLs 的公共地址,或是你的静态文件没有存储在本地而是在 CDN 行,

// Development
{
  entry: __dirname + '/app/main.js',
  output: {
    // path: 指明在哪存储 `bundle.js`
    path: __dirname + '/public',
    // 在开发环境中不要使用 `publicPath` ,除非你的静态资源,比如图片或是 CSS 文件
    // 并没有存储在本带,而是在 CDN 上
    // publicPath: 'http://mycdn.com/'
    filename: 'bundle.js'
  }
}

// Production
{
  entry: __dirname + '/app/main.js',
  output: {
    path: __dirname + '/public',
    // publicPath: 被众多插件使用,比如 url-loader, file-loader 等,
    // 为应用的静态文件生成公共地址,
    // 比如:
    // .image {
    //   background-image: url('./test.png');
    // }
    // 会被编译成,
    // .image {
    //   background-image: url('http://mycdn.com.test.png');
    // }
    publicPath: 'http://mycdn.com',
    filename: 'bundle.js'
  }
}

Loaders 和链式调用

Loader 是额外安装的 node 模块,用于将引入的文件编译成为浏览器可使用的文件等,

比如,我们常使用 babel-loader 来转换 ES6 的语法至 ES5,

loaders: [{
  test: /\.js$/,    // 遍历所有文件,如果后缀名为 `.js` 则使用 loader
  exclude: /node_modules/,  // 不会遍历 Node_modules 文件夹
  loader: 'babel' // 使用 babel-loader
}]

链式调用

我们可以使用链式调用的方式来使用多个 Loader,其语法为使用 ! 连接每一个 Loader,链式的执行顺序为 从右至左,

如下述例子,当我们拥有一个名为 "myCssfile.css" ,我们想要读取并将其内容插入进一个 <style> 标签中并直接插入在我们的 HTML 页面中,我们将使用到两个 loader: css-loaderstyle-loader

module: {
  loaders: [{
    test: /\.css$/,
    loader: 'style!css'
  }]
}

Loaders 自身是可以配置的

通常来讲,我们有两种方式配置 Loader,一种是使用 URL 参数的形式,一种是使用 query 字段,

{
  test: /\.png$/,
  loader: 'url-loader?limit=1024'
}

{
  test: /.\png$/,
  loader: 'url-loader',
  query: {limit: 1024}
}

插件 (Plugins)

插件同样是额外安装的 node 模块,作用于已经打包好的文件,

比如,UglifyJsPlugin 会压缩混淆打包后的 bundle.js

或是 extract-text-webpack-plugin 插件是在其内部调用 css-loaderstyle-loader 来获取所有引用的 CSS 文件并最终打包至一个单独的 style.css 以供引用,

var ETP = require('extract-text-webpack-plugin');

module: {
  loaders: [
    {
      test: /\.css$/,
      loader: ETP.extract('style-loader', 'css-loader')
    }
  ]
},

plugins: [
  new ExtractTextPlugin('style.css')
]

至此,别眨眼看 Webpack 完结啦,

同时如果文章中有任何错误,欢迎大家指出,好的文章需要你的支持,谢谢


awpsawps12
63 声望4 粉丝

« 上一篇
别眨眼看 React